diff --git a/.env.example b/.env.example
index 5dc2622..3954b6b 100644
--- a/.env.example
+++ b/.env.example
@@ -1,163 +1,16 @@
-###############################################
-############## LLM API SELECTION ##############
-###############################################
-LLM_PROVIDER=openai
-
-OPEN_AI_KEY=sk-proj-
-LANGCHAIN_TRACING_V2=true
-LANGCHAIN_PROJECT=langgraph_tutorial
-LANGCHAIN_ENDPOINT=https://api.smith.langchain.com
-LANGCHAIN_API_KEY=lsv2_
-
-
-
-# LLM_PROVIDER=openai
-# OPEN_AI_KEY=sk-proj-----
-OPEN_AI_LLM_MODEL=gpt-4.1
-
-# LLM_PROVIDER=gemini
-# GEMINI_API_KEY=
-# GEMINI_LLM_MODEL=gemini-2.0-flash-lite
-
-# LLM_PROVIDER=azure
-# AZURE_OPENAI_LLM_ENDPOINT=https://-------.openai.azure.com/
-# AZURE_OPENAI_LLM_KEY=-
-# AZURE_OPENAI_LLM_MODEL=gpt4o
-# AZURE_OPENAI_LLM_API_VERSION=2024-07-01-preview
-
-# LLM_PROVIDER=ollama
-# OLLAMA_LLM_BASE_URL=
-# OLLAMA_LLM_MODEL=
-
-# LLM_PROVIDER=huggingface
-# HUGGING_FACE_LLM_REPO_ID=
-# HUGGING_FACE_LLM_ENDPOINT=
-# HUGGING_FACE_LLM_API_TOKEN=
-
-# LLM_PROVIDER=bedrock
-# AWS_BEDROCK_LLM_ACCESS_KEY_ID=
-# AWS_BEDROCK_LLM_SECRET_ACCESS_KEY=
-# AWS_BEDROCK_LLM_REGION=us-west-2
-# AWS_BEDROCK_LLM_ENDPOINT_URL=https://bedrock.us-west-2.amazonaws.com
-# AWS_BEDROCK_LLM_MODEL=anthropic.claude-3-5-sonnet-20241022-v2:0\
-
-###############################################
-########### Embedding API SElECTION ###########
-###############################################
-# Only used if you are using an LLM that does not natively support embedding (openai or Azure)
-EMBEDDING_PROVIDER='openai'
-OPEN_AI_EMBEDDING_MODEL='text-embedding-ada-002'
-
-# EMBEDDING_PROVIDER=azure
-# AZURE_OPENAI_EMBEDDING_ENDPOINT=https://-------.openai.azure.com/openai/deployments
-# AZURE_OPENAI_EMBEDDING_KEY=-
-# AZURE_OPENAI_EMBEDDING_MODEL='textembeddingada002' # This is the "deployment" on Azure you want to use for embeddings. Not the base model. Valid base model is text-embedding-ada-002
-# AZURE_OPENAI_EMBEDDING_API_VERSION=2023-09-15-preview
-
-# EMBEDDING_PROVIDER='ollama'
-# EMBEDDING_BASE_PATH='http://host.docker.internal:11434'
-# EMBEDDING_MODEL='nomic-embed-text:latest'
-# EMBEDDING_MODEL_MAX_CHUNK_LENGTH=8192
-
-# EMBEDDING_PROVIDER='bedrock'
-# AWS_BEDROCK_EMBEDDING_ACCESS_KEY_ID=--
-# AWS_BEDROCK_EMBEDDING_SECRET_ACCESS_KEY=-/-+-+-
-# AWS_BEDROCK_EMBEDDING_REGION=us-west-2
-# AWS_BEDROCK_EMBEDDING_MODEL=amazon.titan-embed-text-v2:0
-
-# EMBEDDING_PROVIDER='gemini'
-# GEMINI_EMBEDDING_API_KEY=
-# EMBEDDING_MODEL='text-embedding-004'
-
-# EMBEDDING_PROVIDER='huggingface'
-# HUGGING_FACE_EMBEDDING_REPO_ID=
-# HUGGING_FACE_EMBEDDING_MODEL=
-# HUGGING_FACE_EMBEDDING_API_TOKEN=
-
-DATAHUB_SERVER = 'http://localhost:8080'
-
-
-###############################################
-######## Database Connector SELECTION #########
-###############################################
-
-# clickhouse
-DB_TYPE=clickhouse
-CLICKHOUSE_HOST=localhost
-CLICKHOUSE_PORT=9001
-CLICKHOUSE_USER=clickhouse
-CLICKHOUSE_PASSWORD=clickhouse
-CLICKHOUSE_DATABASE=default
-
-# databricks
-# DB_TYPE=databricks
-# DATABRICKS_HOST=_
-# DATABRICKS_HTTP_PATH=_
-# DATABRICKS_ACCESS_TOKEN=_
-
-# duckdb
-# DB_TYPE=duckdb
-# DUCKDB_PATH=./data/duckdb.db
-
-# mariadb
-# DB_TYPE=mariadb
-# MARIADB_HOST=_
-# MARIADB_PORT=3306
-# MARIADB_USER=_
-# MARIADB_PASSWORD=_
-# MARIADB_DATABASE=_
-
-# mysql
-# DB_TYPE=mysql
-# MYSQL_HOST=_
-# MYSQL_PORT=3306
-# MYSQL_USER=_
-# MYSQL_PASSWORD=_
-# MYSQL_DATABASE=_
-
-# oracle
-# DB_TYPE=oracle
-# ORACLE_HOST=_
-# ORACLE_PORT=1521
-# ORACLE_USER=_
-# ORACLE_PASSWORD=_
-# ORACLE_DATABASE=_
-# ORACLE_SERVICE_NAME=_
-
-# postgresql
-# DB_TYPE=postgresql
-# POSTGRESQL_HOST=_
-# POSTGRESQL_PORT=5432
-# POSTGRESQL_USER=_
-# POSTGRESQL_PASSWORD=_
-# POSTGRESQL_DATABASE=_
-
-# snowflake
-# DB_TYPE=snowflake
-# SNOWFLAKE_USER=_
-# SNOWFLAKE_PASSWORD=_
-# SNOWFLAKE_ACCOUNT=_
-
-# sqlite
-# DB_TYPE=sqlite
-# SQLITE_PATH=./data/sqlite.db
-
-
-# pgvector 설정 (VECTORDB_TYPE=pgvector일 때 사용)
-PGVECTOR_HOST=localhost
-PGVECTOR_PORT=5432
-PGVECTOR_USER=postgres
-PGVECTOR_PASSWORD=postgres
-PGVECTOR_DATABASE=postgres
-PGVECTOR_COLLECTION=table_info_db
-
-# VectorDB 설정
-VECTORDB_TYPE=faiss # faiss 또는 pgvector
-
-
-# TRINO_HOST=localhost
-# TRINO_PORT=8080
-# TRINO_USER=admin
-# TRINO_PASSWORD=password
-# TRINO_CATALOG=delta
-# TRINO_SCHEMA=default
+# Lang2SQL v4.1 — copy to .env and fill in. Only DISCORD_BOT_TOKEN is required.
+
+# Discord bot token from https://discord.com/developers/applications (Bot tab).
+# Required to run `lang2sql-bot`; the bot exits with a clear error if it's unset.
+DISCORD_BOT_TOKEN=
+
+# OpenAI API key. Optional: when set, the agent uses gpt-4.1-mini. When unset,
+# it falls back to the offline FakeLLM (deterministic canned tool cycles — fine
+# for a smoke run, not for real answers).
+OPENAI_API_KEY=
+
+# Fernet key used to encrypt stored secrets (DSNs / API keys) at rest. Optional:
+# if unset, a key is auto-generated and persisted in the SQLite kv table. Set it
+# in production so secrets decrypt across restarts and machines. Generate one:
+# python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
+LANG2SQL_SECRET_KEY=
diff --git a/README.md b/README.md
index 1d32a93..9bdb698 100644
--- a/README.md
+++ b/README.md
@@ -8,308 +8,158 @@
-
-
-
우리는 함께 코드와 아이디어를 나누며 더 나은 데이터 환경을 만들기 위한 오픈소스 여정을 떠납니다. 🌍💡
-
- "모두가 더 가치 있는 일에 집중할 수 있기를 바랍니다."
-
-
---
-## 🚀 Lang2SQL이란?
+> **A document-learning, read-only SQL analytics agent.**
+> Feed it your company's docs → it learns your business context → it keeps a
+> *separate* set of definitions per team → it answers questions over an
+> incomplete database → it remembers every definition and conversation.
-Lang2SQL은 자연어를 SQL로 변환하는 오픈소스 도구입니다. **define-by-run** 철학에 기반한 모듈러 아키텍처로, 복잡한 데이터베이스 스키마에 대한 깊은 지식 없이도 효율적인 SQL 쿼리를 생성할 수 있도록 도와줍니다.
+This is the **v4.1 rebuild** (see [`docs/discord_first_redesign_v4_1.md`](docs/discord_first_redesign_v4_1.md)).
+Where most text-to-SQL projects compete on *"generate better SQL,"* Lang2SQL
+competes on everything *around* the query: business-context learning, per-team
+semantics, robustness to messy databases, and memory. **Discord is the Phase 1
+interface, not the identity** — Slack/Web are adapters on the same core.
-### 🎯 주요 기능
+---
-- **🗣️ 자연어를 SQL로 변환**: 일상 언어를 정확한 SQL 쿼리로 변환
-- **📊 스마트 테이블 발견**: BM25 키워드 검색과 벡터 유사도 검색을 결합한 하이브리드 검색
-- **🔌 Port 기반 확장**: LLM, DB, 임베딩, 벡터스토어를 Protocol 인터페이스로 자유롭게 교체
-- **🛠️ 웹 인터페이스**: Streamlit 기반 대화형 앱 (쿼리 생성 + 챗봇)
-- **📈 시각화**: SQL 쿼리 결과를 차트와 그래프로 시각화
-- **🗄️ 유연한 VectorDB**: InMemory, FAISS(로컬), pgvector(PostgreSQL) 중 선택
+## The four pillars
-### 🤔 해결하는 문제
+| Pillar | What it is |
+|---|---|
+| **① Business-context learning** | Documents are the source of truth. Drop in a doc → the agent extracts metric/dimension/rule candidates → you confirm → they land in the semantic layer. |
+| **② Two-axis robustness** | **(2a) DB robustness** — works even when columns lack descriptions (auto-enrichment, v1.5). **(2b) Semantic robustness** — teams hold *different* definitions of the same term without conflict. This axis is the product/research identity. |
+| **③ Hermes memory** | Conversations, facts, and preferences persist instead of resetting each session. |
+| **④ Multi-interface** | Phase 1 Discord today; Slack/Web are future adapters. No platform lock-in. |
-새로운 데이터팀 구성원들이 자주 직면하는 문제들:
-- 🤯 "테이블이 너무 많아! 어디서부터 시작하지?"
-- 🧐 "이 JOIN이 맞나요?"
-- 🐌 "이 쿼리 성능이 괜찮을까요?"
-- 😰 "어떻게 의미있는 인사이트를 추출하지?"
+## Extensibility — outlets and appliances (콘센트/가전)
-**Lang2SQL은 다음을 제공하여 이를 해결합니다:**
-- ✅ 자연어 입력 → 테이블 추천
-- ✅ 적절한 컬럼 조합으로 자동 SQL 생성
-- ✅ 모범 사례 기반 성능 최적화
+V1 ships the **simplest single implementation** of each extension point, but the
+**abstraction (port) is already in place**, so v1.5/v2 add a new implementation
+*without touching existing code*. Like a wall outlet: the V1 socket has one LED
+bulb plugged in, but because the socket is standard, you later plug in a fan or a
+smart light without rewiring the wall.
----
+Four ★ extension patterns sit behind `core/ports/`:
-## 📦 설치 방법
+| ★ | Pattern | Port | Grows by |
+|---|---|---|---|
+| ① | **Safety pipeline** | `ports/safety.py` | adding one layer class to the line (zero `run_sql` changes) |
+| ② | **Memory service** | `ports/memory.py` | swapping any of 3 axes — Store / Recall / Extractor — independently |
+| ③ | **Ingestion pipeline** | `ports/ingestion.py` | a Source × Extractor matrix |
+| ④ | **Semantic federation** | `ports/semantic_scope.py` | git-like per-team scope branches |
-### 빠른 설치
+Everything outside `tenancy/concierge.py` depends only on these Protocols, so the
+concrete classes (OpenAI, Postgres, SQLite) are swappable at the seams.
-```bash
-# pip
-pip install lang2sql
+---
-# uv (권장)
-uv venv --python 3.11
-source .venv/bin/activate
-uv add lang2sql
-```
+## Quickstart
-### 소스에서 설치
+Requires Python ≥ 3.10 and [uv](https://docs.astral.sh/uv/).
```bash
-git clone https://github.com/CausalInferenceLab/lang2sql.git
-cd lang2sql
-
-# uv (권장)
-uv venv --python 3.11
-source .venv/bin/activate
-uv sync
-
-# pip
-python -m venv .venv
-source .venv/bin/activate
-pip install -e .
+uv sync # create .venv and install deps
```
----
+### 1. Run the offline demo (no token, no database)
-## ⚡ 빠른 시작 (Python API)
-
-```python
-from lang2sql import BaselineNL2SQL
-from lang2sql.integrations.db import SQLAlchemyDB
-from lang2sql.integrations.llm import OpenAILLM
-
-catalog = [
- {
- "name": "orders",
- "description": "고객 주문 정보",
- "columns": {
- "order_id": "주문 고유 ID",
- "customer_id": "고객 ID",
- "order_date": "주문 일시",
- "amount": "주문 금액",
- "status": "주문 상태",
- },
- },
- {
- "name": "customers",
- "description": "고객 마스터",
- "columns": {
- "customer_id": "고객 ID",
- "name": "고객 이름",
- "grade": "고객 등급: bronze / silver / gold",
- },
- },
-]
-
-pipeline = BaselineNL2SQL(
- catalog=catalog,
- llm=OpenAILLM(model="gpt-4o-mini"),
- db=SQLAlchemyDB("sqlite:///sample.db"),
- db_dialect="sqlite",
-)
-
-rows = pipeline.run("지난달 주문 건수를 알려줘")
-print(rows)
+```bash
+.venv/bin/python bench/ecommerce_demo.py
```
-### 파이프라인 선택 가이드
-
-| 파이프라인 | 검색 방식 | 적합한 상황 |
-|---|---|---|
-| `BaselineNL2SQL` | BM25 키워드 | 빠른 시작, 카탈로그 규모 소~중간 |
-| `HybridNL2SQL` | BM25 + Vector | 검색 품질 우선, 비즈니스 문서 활용 |
-| `EnrichedNL2SQL` | BM25 + Vector + Gate | 운영 환경, 부적합 질문 필터링 필요 |
-
-더 자세한 내용은 [튜토리얼](docs/tutorials/01-quickstart.md)을 참고하세요.
-
----
-
-## 🛠️ 사용법
+Shows the federation money-shot (one term, two team definitions, no conflict) and
+the safety gate (DROP/INSERT blocked, SELECT passes). See [`bench/README.md`](bench/README.md).
-### CLI
+### 2. Run the CLI (developer driver)
```bash
-# Streamlit 웹 인터페이스 실행
-lang2sql run-streamlit
-lang2sql run-streamlit -p 8888 # 포트 지정
-
-# 자연어 쿼리 실행
-lang2sql query "지난달 주문 건수를 집계해줘"
-
-# enriched 플로우 (질문 검증 + 프로파일링 + 컨텍스트 보강)
-lang2sql query "이번 달 순매출 합계" --flow enriched --dialect sqlite --top-n 5
-
-# QuestionGate 비활성화 (enriched 전용)
-lang2sql query "이번 달 순매출 합계" --flow enriched --no-gate
+.venv/bin/lang2sql "list the tables"
```
-**CLI 옵션:**
-
-| 옵션 | 기본값 | 설명 |
-|------|--------|------|
-| `--flow` | `baseline` | `baseline` 또는 `enriched` |
-| `--dialect` | `None` | SQL 방언 (sqlite, postgresql, mysql, bigquery, duckdb) |
-| `--top-n` | `5` | 검색할 최대 테이블 수 |
-| `--no-gate` | `False` | QuestionGate 비활성화 (enriched 전용) |
-
-> CLI는 `.env` 파일의 환경변수(`LLM_PROVIDER`, `DB_TYPE` 등)를 읽어 동작합니다. `.env.example`을 참고하세요.
-
-### Streamlit 웹 UI
-
-Streamlit 앱은 멀티 페이지 구조입니다:
-
-- **🏠 홈** — 프로젝트 소개
-- **🔍 Lang2SQL** — 자연어 쿼리 → SQL 생성 및 실행
-- **🤖 ChatBot** — 멀티턴 대화 기반 SQL 생성
-- **⚙️ 설정** — LLM, DB, 임베딩, 데이터 소스 설정
+The CLI assembles a real `HarnessContext` and runs one turn through the agent
+loop. With `OPENAI_API_KEY` set it calls `gpt-4.1-mini`; otherwise it uses the
+offline `FakeLLM`.
-### 처음 시작하기
+### 3. Run the Discord bot
-튜토리얼은 난이도 순서로 구성되어 있습니다.
-
-| 번호 | 문서 | 내용 |
-|------|------|------|
-| 01 | [빠른 시작](docs/tutorials/01-quickstart.md) | 5분 안에 NL2SQL 실행 |
-| 02 | [Baseline 파이프라인](docs/tutorials/02-baseline.md) | 실제 DB 연결, DB Explorer, Factory 함수 |
-| 03 | [벡터 검색](docs/tutorials/03-vector-search.md) | FAISS/pgvector 인덱싱 |
-| 04 | [하이브리드 검색](docs/tutorials/04-hybrid.md) | BM25 + Vector, EnrichedNL2SQL |
-| 05 | [고급](docs/tutorials/05-advanced.md) | 수동 조합, 커스텀 어댑터, Port 레퍼런스, Hook |
-
----
-
-## 🏗️ 아키텍처
-
-Lang2SQL v2는 **define-by-run** 철학을 따르는 모듈러 아키텍처입니다. 파이프라인 제어는 프레임워크 DSL이 아닌 순수 Python 코드로 작성합니다.
-
-```
-src/lang2sql/
-├── core/ # 외부 의존성 0% — 절대 third-party import 금지
-│ ├── base.py # BaseComponent, BaseFlow
-│ ├── catalog.py # CatalogEntry, TextDocument, IndexedChunk
-│ ├── ports.py # LLMPort, DBPort, EmbeddingPort 등 Protocol 인터페이스
-│ ├── hooks.py # TraceHook, MemoryHook (관측성)
-│ └── exceptions.py
-│
-├── components/ # 재사용 가능한 부품
-│ ├── retrieval/ # KeywordRetriever, VectorRetriever, HybridRetriever
-│ ├── generation/ # SQLGenerator (dialect별 프롬프트)
-│ ├── execution/ # SQLExecutor
-│ ├── gate/ # QuestionGate, TableSuitabilityEvaluator
-│ ├── enrichment/ # QuestionProfiler, ContextEnricher
-│ └── loaders/ # MarkdownLoader, PlainTextLoader, DirectoryLoader
-│
-├── flows/ # 프리셋 파이프라인
-│ ├── nl2sql.py # BaselineNL2SQL (Keyword → SQL → Execute)
-│ ├── hybrid.py # HybridNL2SQL (BM25 + Vector)
-│ └── enriched_nl2sql.py # EnrichedNL2SQL (Gate + Profile + Enrich)
-│
-└── integrations/ # 외부 의존성 구현체
- ├── llm/ # OpenAI, Anthropic, Azure, Gemini, Bedrock, Ollama, HuggingFace
- ├── embedding/ # OpenAI, Azure, Gemini, Bedrock, Ollama, HuggingFace
- ├── db/ # SQLAlchemyDB, SQLAlchemyExplorer
- ├── vectorstore/ # InMemory, FAISS, pgvector
- ├── catalog/ # DataHubCatalogLoader
- ├── loaders/ # PDFLoader
- └── chunking/ # SemanticChunker
+```bash
+export DISCORD_BOT_TOKEN=... # required
+export OPENAI_API_KEY=... # optional; offline FakeLLM if unset
+export LANG2SQL_SECRET_KEY=... # optional; Fernet key for secret encryption
+.venv/bin/lang2sql-bot
```
-### 설계 원칙
-
-- **`core/`는 외부 의존성 0%**: 모든 third-party 연동은 `integrations/`에서 구현
-- **Port Protocol**: `LLMPort`, `DBPort`, `EmbeddingPort` 등 메서드 시그니처만 맞추면 어떤 구현체든 연결 가능
-- **Hook 기반 관측성**: `MemoryHook`으로 컴포넌트별 실행 시간, 에러를 추적
+The bot exits loudly if `DISCORD_BOT_TOKEN` is unset. Full setup and hosting:
+[`docs/DEPLOY.md`](docs/DEPLOY.md). Copy [`.env.example`](.env.example) to start.
---
-## 🧑💻 지원 환경
+## What V1 does / does NOT do yet (honesty section)
+
+**Does:**
+- 3-scope semantic federation (guild / channel / thread) with most-specific-wins
+ resolution; `define_metric` writes to the current scope.
+- Safety pipeline with the V1 layers (whitelist + timeout), gating every query.
+- Agent loop with six tools: `run_sql`, `explore_schema`, `define_metric`,
+ `ingest_doc`, `remember`, `ask_user`.
+- Memory service (in-memory store + inject-all recall + manual `/remember`).
+- Discord frontend (bot, commands, session router, render).
+- Encrypted-at-rest secrets (Fernet) and SQLite-backed persistence.
+
+**Does NOT yet:**
+- **Execute against a real database.** `PostgresExplorer` is a **V1 stub** with
+ canned `orders`/`users` schema and sample rows; real psycopg execution is v1.5.
+- **Reason without a key.** Without `OPENAI_API_KEY`, the `FakeLLM` returns
+ deterministic canned tool cycles — useful for wiring tests, not for answers.
+- DB metadata auto-enrichment, AST-precise SQL validation, function blocklists,
+ cost gating, `/semantic diff` / `/semantic promote`, keyword/vector recall,
+ automatic fact extraction, URL/Notion ingestion — all scoped to v1.5+.
+- Persist across restarts by default: the V1 `SqliteStore` defaults to in-memory;
+ point it at a file for durability.
-### LLM
-
-| 프로바이더 | 환경변수 `LLM_PROVIDER` |
-|---|---|
-| OpenAI | `openai` |
-| Anthropic | `anthropic` |
-| Azure OpenAI | `azure` |
-| Google Gemini | `gemini` |
-| AWS Bedrock | `bedrock` |
-| Ollama | `ollama` |
-| HuggingFace | `huggingface` |
-
-### 데이터베이스
+---
-SQLite, PostgreSQL, MySQL, MariaDB, DuckDB, ClickHouse, Snowflake, Oracle, BigQuery, Trino, Hive, Crate
+## Roadmap at a glance
-### VectorDB
+| Area | V1 | V1.5 | V2 | V2.5 |
+|---|---|---|---|---|
+| **Safety** | whitelist + timeout | + AST validation, function blocklist, auto LIMIT, **metadata enrichment**, rate limit | + cost gate (EXPLAIN), per-engine pipelines | — |
+| **Memory** | in-memory + inject-all + manual | SQLite store + keyword recall + auto-extract | + vector recall + conflict resolution | PostgreSQL + hybrid recall + confidence |
+| **Ingestion** | file upload + LLM extract | + URL fetch + DDL parsing | + Notion/Confluence + hybrid | + GitHub/Drive + chunked RAG |
+| **Federation** | 3-scope resolution, `/semantic show` | `/semantic diff`, `/semantic promote`, conflict alerts | git sync (semantic-as-code) | branch fork/merge UI, per-scope audit |
+| **Interface** | Discord | (Anthropic/NIM eval) | Slack | Web |
-| 백엔드 | 영속성 | 권장 규모 |
-|---|---|---|
-| `InMemoryVectorStore` | 없음 | < 50k chunks |
-| `FAISSVectorStore` | 로컬 파일 | < 500k chunks |
-| `PGVectorStore` | PostgreSQL | 500k+ chunks |
+See [`docs/discord_first_redesign_v4_1.md`](docs/discord_first_redesign_v4_1.md)
+for the full architecture write-up.
---
## 🤝 기여하기
-커뮤니티의 기여를 환영합니다!
-
-### 🔧 개발 환경 설정
-
```bash
-# 1. 저장소 포크 & 클론
-git clone https://github.com/YOUR_USERNAME/lang2sql.git
+git clone https://github.com/CausalInferenceLab/lang2sql.git
cd lang2sql
-
-# 2. 의존성 설치
-uv venv --python 3.11
-source .venv/bin/activate
-uv sync --group dev
-
-# 3. 브랜치 생성 (네이밍: feature/이슈번호-작업내용)
-git checkout -b feature/42-amazing-feature
-
-# 4. 작업 후 커밋 & PR
-git commit -m "feat: add amazing feature"
-git push origin feature/42-amazing-feature
+uv sync
+.venv/bin/pytest -q # 12 safety regressions + full suite must pass
```
-### 📋 개발 가이드라인
-
-- `pre-commit run --all-files`로 포맷팅 확인
-- 새로운 기능에 대한 테스트 작성 (`tests/test__.py`)
-- PR은 `master` 브랜치 대상, 최소 2명 리뷰어 승인 필요
-- 커밋 메시지: `feat:`, `fix:`, `docs:`, `refactor:` 등 prefix 사용
-
-### 🐛 이슈 신고
-
-버그를 발견했거나 기능 요청이 있으신가요? 다음 정보와 함께 [이슈를 열어주세요](https://github.com/CausalInferenceLab/lang2sql/issues):
-- 문제/기능에 대한 명확한 설명
-- 재현 단계 (버그의 경우)
-- 예상 동작 vs 실제 동작
-- 환경 세부사항
+- 새 기능에는 테스트 작성 (`tests/test_.py`)
+- PR은 `master` 브랜치 대상, 커밋 메시지에 `feat:` / `fix:` / `docs:` prefix 사용
+- 버그/기능 요청은 [이슈](https://github.com/CausalInferenceLab/lang2sql/issues)로
---
-## 🎓 학습 자료
+## 🙏 감사의 말 / License
-- [모두를 위한 게임 데이터 검색 시스템 / if(kakaoAI)2024](https://www.youtube.com/watch?v=8-GerpWVMis&ab_channel=kakaotech)
-- [AI 데이터 분석가 '물어보새' 등장 – 1부. RAG와 Text-To-SQL 활용](https://techblog.woowahan.com/18144/)
-- [테디노트 LangGraph](https://wikidocs.net/233785)
-- [DataHub 문서](https://datahubproject.io/)
-- [Vanna.ai](https://github.com/vanna-ai/vanna)
+Lang2SQL은 **가짜연구소의 인과추론팀**에서 개발 중인 프로젝트입니다. Licensed under
+the [MIT License](https://opensource.org/licenses/MIT). 커뮤니티: [Discord](https://discord.gg/EPurkHVtp2).
---
@@ -332,44 +182,6 @@ git push origin feature/42-amazing-feature
| **AI Engineer** | 심세원 |  | LLM, RAG, Multi-Agent |
| **Business Analyst** | 서희진 |  | LLM, Data Analysis |
-## 🚀 배포 및 릴리스
-
-### 자동 배포(GitHub Actions)
-
-`v*` 태그 푸시 시 `.github/workflows/pypi-release.yml`이 실행되어 PyPI에 배포됩니다.
-
-```bash
-# 1) 버전 업데이트 (version.py)
-git add version.py
-git commit -m "chore: bump version to X.Y.Z"
-
-# 2) 태그 생성/푸시
-git tag vX.Y.Z
-git push origin HEAD
-git push origin vX.Y.Z
-```
-
-사전 준비: GitHub Secrets에 `PYPI_API_TOKEN` 등록
-
-### 수동 빌드
-
-```bash
-uv build
-UV_PUBLISH_TOKEN=$PYPI_API_TOKEN uv publish --token $UV_PUBLISH_TOKEN
-```
-
----
-
-## 🙏 감사의 말
-
-Lang2SQL은 **가짜연구소의 인과추론팀**에서 개발중인 프로젝트입니다.
-
----
-
-## 📄 라이선스
-
-- This project is licensed under the [MIT License](https://opensource.org/licenses/MIT).
-
---
## 🌍 가짜연구소 소개
@@ -378,8 +190,7 @@ Lang2SQL은 **가짜연구소의 인과추론팀**에서 개발중인 프로젝
전 세계 5,000명 이상의 연구자들과 함께, 우리는 AI 지식의 민주화와 열린 협업을 통한 혁신 촉진에 전념하고 있습니다.
-**우리 커뮤니티에 참여하세요:**
-- 💬 [Discord](https://discord.gg/EPurkHVtp2)
+**커뮤니티**: 💬 [Discord](https://discord.gg/EPurkHVtp2)
---
@@ -388,10 +199,3 @@ Lang2SQL은 **가짜연구소의 인과추론팀**에서 개발중인 프로젝
-
----
-
-
-
⭐ 이 저장소가 도움이 되셨다면 스타를 눌러주세요!
-
"우리는 함께 코드와 아이디어를 나누며 더 나은 데이터 환경을 만들기 위한 오픈소스 여정을 떠납니다. 🌍💡"
-
diff --git a/bench/README.md b/bench/README.md
new file mode 100644
index 0000000..926c4e8
--- /dev/null
+++ b/bench/README.md
@@ -0,0 +1,39 @@
+# bench/ — runnable demo
+
+A self-contained walkthrough of Lang2SQL V1's headline value, with **no Discord
+token and no live database**. It runs the real shipped modules against the
+canned Postgres stub and the offline `FakeLLM`.
+
+## Run
+
+```bash
+.venv/bin/python bench/ecommerce_demo.py
+```
+
+No environment variables are needed. (If `OPENAI_API_KEY` happens to be set,
+Section 0 will route through OpenAI instead of the FakeLLM — everything else is
+offline.)
+
+## What each section proves
+
+| Section | What it shows | Maps to |
+|---|---|---|
+| **0 — assembled harness** | `ContextConcierge` wires the FakeLLM + canned `PostgresExplorer` + six tools into a `HarnessContext`, and `agent_loop` drives one full LLM→tool→LLM cycle. A *wiring* proof, not an intelligence proof. | harness + concierge |
+| **1 — define metrics** | Three e-commerce metrics (`total_revenue`, `aov`, `paid_orders`) are written to the current channel's scope and read back from the effective layer. | ★① context learning |
+| **2 — federation (the money shot)** | `active_user` is defined as *"30-day login"* in `#marketing` and *"paid subscriber"* in `#finance`. Each channel resolves its **own** definition by walking its scope chain — same term, two live meanings, **zero conflict**. | **★④ semantic federation** |
+| **3 — safety gate** | `DROP`, `INSERT`, and a CTE-hidden `INSERT` are all **blocked fail-closed**; a plain `SELECT ... GROUP BY` **passes**. The read-only guarantee is enforced before any SQL would reach a database. | **★① safety pipeline** |
+
+The two headline pillars demonstrated are **★① safety** (Sections 0 + 3) and
+**★④ federation** (Sections 1 + 2).
+
+## Honesty notes
+
+- The `PostgresExplorer` is a **V1 stub**: it returns canned `orders`/`users`
+ schema and sample rows and does not connect to a real database. Real psycopg
+ execution lands in v1.5.
+- The `FakeLLM` is a deterministic stub for offline runs; it does not reason. It
+ blindly calls the first available tool, which is why Section 0's turn ends at
+ the safety gate rather than counting orders. With a real key it would call
+ `gpt-4.1-mini`.
+- Federation state in Sections 1–2 lives in an in-memory `SemanticStore`; the
+ SQLite-backed persistent store is wired through the concierge for the bot.
diff --git a/bench/ecommerce_demo.py b/bench/ecommerce_demo.py
new file mode 100644
index 0000000..b910854
--- /dev/null
+++ b/bench/ecommerce_demo.py
@@ -0,0 +1,192 @@
+#!/usr/bin/env python3
+"""E-commerce demo — Lang2SQL v4.1 headline value, no Discord and no real DB.
+
+Run: .venv/bin/python bench/ecommerce_demo.py
+
+This is the study-group demo. It exercises the *real* V1 code paths
+(``ContextConcierge`` + scope resolver + the canned Postgres explorer + the
+offline ``FakeLLM``) to show three things end-to-end without a token or a live
+database:
+
+ Section 1 — define three e-commerce metrics in a channel and read them back.
+ Section 2 — ★④ semantic federation: the *same* term ``active_user`` carries
+ two different definitions in #marketing vs #finance, with no
+ conflict. Each channel resolves its own effective layer.
+ Section 3 — ★① safety pipeline: DROP / INSERT are blocked fail-closed while a
+ plain SELECT passes.
+
+Everything printed is produced by running the shipped modules, not hard-coded.
+"""
+
+from __future__ import annotations
+
+import asyncio
+
+from lang2sql.core.identity import Identity
+from lang2sql.core.ports.safety import SafetyContext, Verdict
+from lang2sql.harness.loop import agent_loop
+from lang2sql.safety.pipeline import SafetyPipeline
+from lang2sql.semantic.types import Metric
+from lang2sql.tenancy.concierge import ContextConcierge
+from lang2sql.tenancy.scope_resolver import ScopeResolver
+
+# Stable IDs for the demo guild and its two channels.
+GUILD = "acme-shop"
+CH_MARKETING = "marketing"
+CH_FINANCE = "finance"
+
+
+def _hr(title: str) -> None:
+ print("\n" + "=" * 68)
+ print(title)
+ print("=" * 68)
+
+
+def _marketing_identity() -> Identity:
+ return Identity(user_id="dana", guild_id=GUILD, channel_id=CH_MARKETING)
+
+
+def _finance_identity() -> Identity:
+ return Identity(user_id="evan", guild_id=GUILD, channel_id=CH_FINANCE)
+
+
+async def section_0_harness(concierge: ContextConcierge) -> None:
+ """Drive one full agent turn through the assembled harness (offline).
+
+ This is the *wiring* proof, not an intelligence proof: ``ContextConcierge``
+ picks the offline FakeLLM (no OPENAI_API_KEY), starts a session, and wires
+ the canned Postgres explorer + six tools into a ``HarnessContext`` that
+ ``agent_loop`` drives LLM → tool → LLM to a final answer. No network, no
+ real database.
+
+ The FakeLLM is a deterministic stub: it blindly calls the first tool
+ (``run_sql``) with placeholder args, so its turn ends up *demonstrating the
+ safety gate* rather than answering the question. With OPENAI_API_KEY set,
+ the same loop calls gpt-4.1-mini instead — zero other code changes.
+ """
+ _hr("SECTION 0 — assembled harness runs one turn (ContextConcierge + FakeLLM)")
+
+ ident = _marketing_identity()
+ ctx = await concierge.build_context(ident)
+ print(f"LLM in use: {type(ctx.llm).__name__} (offline, no key required)")
+ print(f"Explorer: {type(ctx.explorer).__name__} (canned orders/users tables)")
+ print(f"Tools wired: {', '.join(s.name for s in ctx.tools.specs())}\n")
+
+ final = await agent_loop(ctx, "How many orders do we have?")
+ print("user> How many orders do we have?")
+ print(f"loop> {final}")
+ print("\n The loop completed a real LLM→tool→LLM cycle. The stub's blind")
+ print(" run_sql call was caught by the safety gate, which is exactly the")
+ print(" ★① behaviour Section 3 isolates.")
+
+
+async def section_1_define_metrics(resolver: ScopeResolver) -> None:
+ """Define three e-commerce metrics in #marketing and read them back."""
+ _hr("SECTION 1 — define three metrics (★① business-context learning)")
+
+ ident = _marketing_identity()
+ scope = ident.default_write_scope() # current channel by default
+ print(f"Writing to default scope for this channel: {scope}\n")
+
+ metrics = [
+ Metric("total_revenue", "SUM(orders.amount) WHERE status != 'cancelled'"),
+ Metric("aov", "total_revenue / COUNT(DISTINCT orders.id)"),
+ Metric("paid_orders", "COUNT(*) FROM orders WHERE status = 'paid'"),
+ ]
+ for m in metrics:
+ await resolver.define(scope, m)
+ print(f" defined {m.name:>14} = {m.definition}")
+
+ layer = await resolver.effective_layer(ident)
+ print(f"\nEffective layer for #{CH_MARKETING} now holds "
+ f"{len(layer.entries)} definition(s):")
+ print(layer.render())
+
+
+async def section_2_federation(resolver: ScopeResolver) -> None:
+ """Same term, two channels, two definitions — no conflict (★④)."""
+ _hr("SECTION 2 — semantic federation: one term, two definitions (★④)")
+
+ # #marketing defines active_user one way ...
+ mkt = _marketing_identity()
+ await resolver.define(
+ mkt.default_write_scope(),
+ Metric("active_user", "user with a login event in the last 30 days"),
+ )
+ # ... and #finance defines the SAME name a different way.
+ fin = _finance_identity()
+ await resolver.define(
+ fin.default_write_scope(),
+ Metric("active_user", "user with an active paid subscription"),
+ )
+
+ print("Defined 'active_user' independently in two channels.\n")
+ print("Now resolving the *effective* definition each channel sees")
+ print("by walking its scope chain (most specific scope wins):\n")
+
+ mkt_layer = await resolver.effective_layer(mkt)
+ fin_layer = await resolver.effective_layer(fin)
+ mkt_def = mkt_layer.lookup("active_user")
+ fin_def = fin_layer.lookup("active_user")
+
+ print(f" #{CH_MARKETING:<10} active_user → {mkt_def.definition}")
+ print(f" #{CH_FINANCE:<10} active_user → {fin_def.definition}")
+
+ assert mkt_def.definition != fin_def.definition
+ print("\n ✅ Same term, two live definitions, zero conflict.")
+ print(" Each channel is its own branch in the federation tree;")
+ print(" neither overwrote the other. (Wren's single MDL cannot do this.)")
+
+ # Show the scope chain that produced the marketing answer.
+ chain = " → ".join(str(s) for s in mkt.scope_chain())
+ print(f"\n #{CH_MARKETING} resolution order: {chain}")
+ print(" Lookup stops at the first scope that defines the name (CHANNEL),")
+ print(" so the GUILD/BUILTIN branches never get a chance to disagree.")
+
+
+def section_3_safety(pipeline: SafetyPipeline) -> None:
+ """DROP/INSERT blocked fail-closed; SELECT passes (★①)."""
+ _hr("SECTION 3 — safety pipeline gates every query (★①)")
+
+ layers = " → ".join(layer.name for layer in pipeline.layers)
+ print(f"Pipeline layers in order: {layers}\n")
+
+ cases = [
+ ("DROP TABLE users", "must BLOCK"),
+ ("INSERT INTO orders VALUES (99, 10.0, 'paid', now())", "must BLOCK"),
+ ("WITH x AS (INSERT INTO t VALUES (1)) SELECT * FROM x", "must BLOCK (CTE)"),
+ ("SELECT status, COUNT(*) FROM orders GROUP BY status", "must PASS"),
+ ]
+ ctx = SafetyContext()
+ for sql, expectation in cases:
+ decision = pipeline.evaluate(sql, ctx)
+ passed = decision.verdict is Verdict.PASS
+ mark = "✅ PASS " if passed else "🚫 BLOCK"
+ reason = f" ({decision.reason} @ {decision.layer})" if decision.reason else ""
+ print(f" {mark} {expectation:<16} {sql}{reason}")
+
+ print("\n Read-only is enforced before any SQL reaches the database:")
+ print(" the whitelist layer is fail-closed, so anything it cannot prove")
+ print(" is a single SELECT/WITH is rejected.")
+
+
+async def main() -> None:
+ print("Lang2SQL v4.1 — e-commerce demo (offline: FakeLLM, canned PG, in-memory)")
+
+ # One shared resolver so federation state persists across sections 1 and 2.
+ resolver = ScopeResolver()
+ pipeline = SafetyPipeline()
+ concierge = ContextConcierge()
+
+ await section_0_harness(concierge)
+ await section_1_define_metrics(resolver)
+ await section_2_federation(resolver)
+ section_3_safety(pipeline)
+
+ _hr("DONE")
+ print("Sections 1–2 exercise ★④ federation; section 3 exercises ★① safety.")
+ print("No network, no token, no live database were used.")
+
+
+if __name__ == "__main__":
+ asyncio.run(main())
diff --git a/cli/README.md b/cli/README.md
deleted file mode 100644
index 012d1eb..0000000
--- a/cli/README.md
+++ /dev/null
@@ -1,270 +0,0 @@
-# CLI 모듈
-
-Lang2SQL 프로젝트의 CLI(Command Line Interface) 모듈입니다. 자연어 질문을 SQL 쿼리로 변환하고, Streamlit 웹 애플리케이션을 실행하는 기능을 제공합니다.
-
-## 디렉토리 구조
-
-```
-cli/
-├── __init__.py # CLI 진입점 및 메인 그룹 정의
-├── __pycache__/ # Python 캐시 디렉토리
-├── commands/ # CLI 명령어 정의 모듈
-│ ├── __pycache__/
-│ ├── quary.py # 자연어 질문을 SQL로 변환하는 명령어
-│ ├── run_streamlit.py # Streamlit 실행 명령어
-│ └── README.md # Commands 모듈 문서
-├── core/ # CLI 핵심 기능 모듈
-│ ├── __pycache__/
-│ ├── environment.py # 환경 변수 초기화 모듈
-│ ├── streamlit_runner.py # Streamlit 실행 유틸리티
-│ └── README.md # Core 모듈 문서
-├── utils/ # CLI 유틸리티 모듈
-│ ├── __pycache__/
-│ ├── env_loader.py # 환경 변수 로드 유틸리티
-│ ├── logger.py # 로깅 설정 유틸리티
-│ └── README.md # Utils 모듈 문서
-└── README.md # 이 파일
-```
-
-## 모듈 개요
-
-### `__init__.py`
-
-CLI 프로그램의 진입점입니다. Click 프레임워크를 사용하여 명령어 그룹과 옵션을 정의합니다.
-
-**주요 구성 요소:**
-
-#### CLI 그룹 정의
-- **함수**: `cli(ctx, datahub_server, run_streamlit, port, env_file_path, prompt_dir_path, vectordb_type, vectordb_location)`
-- **데코레이터**: `@click.group()`
-- **역할**: Lang2SQL CLI의 최상위 명령어 그룹
-
-#### 주요 옵션
-
-1. **`--version` / `-v`**: 버전 정보 출력
-2. **`--datahub_server`** (Deprecated): DataHub GMS URL 설정 - 더 이상 사용되지 않음
-3. **`--run-streamlit`**: CLI 실행 시 Streamlit 애플리케이션을 바로 실행하는 플래그
-4. **`-p, --port`**: Streamlit 서버 포트 번호 (기본값: 8501)
-5. **`--env-file-path`**: 환경 변수를 로드할 .env 파일 경로
-6. **`--prompt-dir-path`**: 프롬프트 템플릿 디렉토리 경로
-7. **`--vectordb-type`** (Deprecated): VectorDB 타입 - 더 이상 사용되지 않음
-8. **`--vectordb-location`** (Deprecated): VectorDB 위치 - 더 이상 사용되지 않음
-
-#### 주요 동작
-
-1. **환경 변수 초기화**: `initialize_environment()` 호출로 환경 변수 설정
-2. **Deprecated 옵션 경고**: 사용되지 않는 옵션 사용 시 경고 메시지 출력
-3. **Streamlit 자동 실행**: `--run-streamlit` 플래그가 설정된 경우 Streamlit 실행
-
-#### 등록된 명령어
-
-- `run-streamlit`: Streamlit 애플리케이션 실행 (`cli/commands/run_streamlit.py`)
-- `query`: 자연어 질문을 SQL 쿼리로 변환 (`cli/commands/quary.py`)
-
-#### Import 관계
-
-**Import하는 모듈:**
-- `cli.commands.quary.query_command`: query 명령어
-- `cli.commands.run_streamlit.run_streamlit_cli_command`: run-streamlit 명령어
-- `cli.core.environment.initialize_environment`: 환경 변수 초기화
-- `cli.core.streamlit_runner.run_streamlit_command`: Streamlit 실행 함수
-- `cli.utils.logger.configure_logging`: 로깅 설정
-- `version.__version__`: 버전 정보
-
-**사용 위치:**
-- CLI 진입점: `pyproject.toml`의 `[project.scripts]` 섹션에 정의됨
- ```toml
- [project.scripts]
- lang2sql = "cli.__init__:cli"
- ```
-- 사용 방법: 패키지 설치 후 `lang2sql` 명령어로 실행
- ```bash
- lang2sql --help
- lang2sql --version
- lang2sql --run-streamlit
- lang2sql query "질문"
- lang2sql run-streamlit
- ```
-
-#### 코드 예시
-
-```python
-from cli import cli
-
-# CLI 그룹 자동 등록
-# lang2sql 명령어로 실행 가능
-```
-
-### 하위 모듈
-
-#### `commands/` 모듈
-
-CLI 명령어를 정의하는 모듈입니다. 자연어 질문을 SQL로 변환하는 `query` 명령어와 Streamlit을 실행하는 `run-streamlit` 명령어를 제공합니다.
-
-자세한 내용은 [`commands/README.md`](commands/README.md)를 참고하세요.
-
-**주요 파일:**
-- `quary.py`: `query_command` - 자연어를 SQL로 변환하는 명령어
-- `run_streamlit.py`: `run_streamlit_cli_command` - Streamlit 실행 명령어
-
-**사용 위치:**
-- `cli/__init__.py` (10-11, 116-117번 라인): CLI 그룹에 명령어 등록
- ```python
- from cli.commands.quary import query_command
- from cli.commands.run_streamlit import run_streamlit_cli_command
-
- cli.add_command(run_streamlit_cli_command)
- cli.add_command(query_command)
- ```
-
-#### `core/` 모듈
-
-CLI의 핵심 기능을 제공하는 모듈입니다. 환경 변수 초기화와 Streamlit 실행 기능을 담당합니다.
-
-자세한 내용은 [`core/README.md`](core/README.md)를 참고하세요.
-
-**주요 파일:**
-- `environment.py`: `initialize_environment` - 환경 변수 초기화 함수
-- `streamlit_runner.py`: `run_streamlit_command` - Streamlit 실행 함수
-
-**사용 위치:**
-- `cli/__init__.py` (12-13, 85, 113번 라인):
- ```python
- from cli.core.environment import initialize_environment
- from cli.core.streamlit_runner import run_streamlit_command
-
- # 환경 변수 초기화
- initialize_environment(
- env_file_path=env_file_path,
- prompt_dir_path=prompt_dir_path
- )
-
- # Streamlit 실행
- if run_streamlit:
- run_streamlit_command(port)
- ```
-
-#### `utils/` 모듈
-
-CLI 애플리케이션에서 사용되는 유틸리티 함수들을 제공하는 모듈입니다.
-
-자세한 내용은 [`utils/README.md`](utils/README.md)를 참고하세요.
-
-**주요 파일:**
-- `env_loader.py`: 환경 변수 로드 및 설정 함수들
- - `load_env`: .env 파일 로드
- - `set_prompt_dir`: 프롬프트 디렉토리 설정
- - `set_vectordb`: VectorDB 설정
-- `logger.py`: `configure_logging` - CLI 전용 로깅 설정
-
-**사용 위치:**
-- `cli/__init__.py` (14, 18번 라인): 로깅 설정
- ```python
- from cli.utils.logger import configure_logging
-
- logger = configure_logging()
- ```
-- `cli/core/environment.py`: 환경 변수 로드
-- `cli/core/streamlit_runner.py`: 로깅 설정
-- `cli/commands/quary.py`: 로깅 설정
-- `cli/commands/run_streamlit.py`: 로깅 설정
-
-## CLI 사용 방법
-
-### 기본 사용법
-
-```bash
-# 도움말 보기
-lang2sql --help
-
-# 버전 확인
-lang2sql --version
-
-# Streamlit 바로 실행
-lang2sql --run-streamlit
-
-# 환경 변수 파일 지정하여 실행
-lang2sql --env-file-path /path/to/.env --run-streamlit
-```
-
-### 명령어
-
-#### `query` 명령어
-
-자연어 질문을 SQL 쿼리로 변환합니다.
-
-```bash
-# 기본 사용
-lang2sql query "고객 데이터를 기반으로 유니크한 유저 수를 카운트하는 쿼리"
-
-# 옵션과 함께 사용
-lang2sql query "질문" \
- --database-env clickhouse \
- --retriever-name 기본 \
- --top-n 5 \
- --device cpu \
- --use-enriched-graph \
- --vectordb-type faiss \
- --vectordb-location ./dev/table_info_db
-```
-
-#### `run-streamlit` 명령어
-
-Streamlit 웹 애플리케이션을 실행합니다.
-
-```bash
-# 기본 포트(8501)로 실행
-lang2sql run-streamlit
-
-# 특정 포트로 실행
-lang2sql run-streamlit -p 9000
-```
-
-자세한 사용법은 [`commands/README.md`](commands/README.md)를 참고하세요.
-
-## 의존성
-
-### 내부 의존성
-
-- `cli.commands.*`: CLI 명령어 정의
-- `cli.core.*`: 핵심 기능 모듈
-- `cli.utils.*`: 유틸리티 함수
-- `version`: 버전 정보
-
-### 외부 의존성
-
-- `click`: CLI 프레임워크
-- `subprocess`: Streamlit 프로세스 실행 (core 모듈)
-- `python-dotenv`: 환경 변수 파일 로드 (utils 모듈)
-- `streamlit`: Streamlit 웹 애플리케이션 프레임워크
-
-## 모듈 간 관계도
-
-```
-cli/__init__.py (진입점)
-├── commands/
-│ ├── quary.py → engine.query_executor
-│ └── run_streamlit.py → core/streamlit_runner.py
-├── core/
-│ ├── environment.py → utils/env_loader.py
-│ └── streamlit_runner.py → utils/logger.py
-└── utils/
- ├── env_loader.py (독립적)
- └── logger.py (독립적)
-```
-
-## 주요 특징
-
-1. **모듈화된 구조**: 명령어, 핵심 기능, 유틸리티가 명확히 분리됨
-2. **확장 가능성**: 새로운 명령어를 쉽게 추가할 수 있는 구조
-3. **환경 관리**: 일관된 환경 변수 초기화 보장
-4. **UI 중심 설계**: VectorDB 및 DataHub 설정은 UI에서 관리
-5. **로깅 지원**: 모든 모듈에서 일관된 로깅 사용
-6. **Deprecated 옵션 처리**: 사용되지 않는 옵션에 대한 명확한 경고
-
-## 참고 문서
-
-- [`commands/README.md`](commands/README.md): 명령어 모듈 상세 문서
-- [`core/README.md`](core/README.md): 핵심 기능 모듈 상세 문서
-- [`utils/README.md`](utils/README.md): 유틸리티 모듈 상세 문서
-
diff --git a/cli/__init__.py b/cli/__init__.py
deleted file mode 100644
index a917ae9..0000000
--- a/cli/__init__.py
+++ /dev/null
@@ -1,117 +0,0 @@
-"""Lang2SQL CLI 프로그램입니다.
-이 프로그램은 환경 초기화와 Streamlit 실행을 제공합니다.
-
-주의: --datahub_server 옵션은 더 이상 사용되지 않습니다(deprecated).
-DataHub 설정은 UI의 설정 > 데이터 소스 탭에서 관리하세요.
-"""
-
-import click
-
-from cli.commands.quary import query_command
-from cli.commands.run_streamlit import run_streamlit_cli_command
-from cli.core.environment import initialize_environment
-from cli.core.streamlit_runner import run_streamlit_command
-from cli.utils.logger import configure_logging
-
-from version import __version__
-
-logger = configure_logging()
-
-
-# pylint: disable=redefined-outer-name,broad-exception-caught
-@click.group()
-@click.version_option(version=__version__)
-@click.pass_context
-@click.option(
- "--datahub_server",
- default=None,
- help=("[Deprecated] DataHub GMS URL. 이제는 UI 설정 > 데이터 소스에서 관리하세요."),
-)
-@click.option(
- "--run-streamlit",
- is_flag=True,
- help=(
- "이 옵션을 지정하면 CLI 실행 시 Streamlit 애플리케이션을 바로 실행합니다. "
- "별도의 명령어 입력 없이 웹 인터페이스를 띄우고 싶을 때 사용합니다."
- ),
-)
-@click.option(
- "-p",
- "--port",
- type=int,
- default=8501,
- help=(
- "Streamlit 서버가 바인딩될 포트 번호를 지정합니다. "
- "기본 포트는 8501이며, 포트 충돌을 피하거나 여러 인스턴스를 실행할 때 변경할 수 있습니다."
- ),
-)
-@click.option(
- "--env-file-path",
- type=click.Path(exists=True, file_okay=True, dir_okay=False, readable=True),
- help="환경 변수를 로드할 .env 파일의 경로를 지정합니다. 지정하지 않으면 기본 경로를 사용합니다.",
-)
-@click.option(
- "--prompt-dir-path",
- type=click.Path(exists=True, file_okay=False, dir_okay=True, readable=True),
- help="프롬프트 템플릿(.md 파일)이 저장된 디렉토리 경로를 지정합니다. 지정하지 않으면 기본 경로를 사용합니다.",
-)
-@click.option(
- "--vectordb-type",
- default=None,
- help="[Deprecated] VectorDB 타입. 이제는 UI 설정 > 데이터 소스에서 관리하세요.",
-)
-@click.option(
- "--vectordb-location",
- default=None,
- help="[Deprecated] VectorDB 위치. 이제는 UI 설정 > 데이터 소스에서 관리하세요.",
-)
-def cli(
- ctx: click.Context,
- datahub_server: str | None,
- run_streamlit: bool,
- port: int,
- env_file_path: str | None = None,
- prompt_dir_path: str | None = None,
- vectordb_type: str | None = None,
- vectordb_location: str | None = None,
-) -> None:
- """Lang2SQL CLI 엔트리포인트.
-
- - 환경 변수 및 VectorDB 설정 초기화
- - 필요 시 Streamlit 애플리케이션 실행
- """
-
- try:
- initialize_environment(
- env_file_path=env_file_path, prompt_dir_path=prompt_dir_path
- )
- except Exception:
- logger.error("Initialization failed.", exc_info=True)
- ctx.exit(1)
-
- logger.info(
- "Initialization started: run_streamlit = %s, port = %d",
- run_streamlit,
- port,
- )
-
- # Deprecated 안내: CLI에서 DataHub 설정은 더 이상 처리하지 않습니다
- if datahub_server:
- click.secho(
- "[Deprecated] --datahub_server 옵션은 더 이상 사용되지 않습니다. 설정 > 데이터 소스 탭에서 설정하세요.",
- fg="yellow",
- )
-
- # Deprecated 안내: CLI에서 VectorDB 설정은 더 이상 처리하지 않습니다
- if vectordb_type or vectordb_location:
- click.secho(
- "[Deprecated] --vectordb-type/--vectordb-location 옵션은 더 이상 사용되지 않습니다. 설정 > 데이터 소스 탭에서 설정하세요.",
- fg="yellow",
- )
-
- if run_streamlit:
- run_streamlit_command(port)
-
-
-cli.add_command(run_streamlit_cli_command)
-cli.add_command(query_command)
diff --git a/cli/commands/README.md b/cli/commands/README.md
deleted file mode 100644
index 520e8d6..0000000
--- a/cli/commands/README.md
+++ /dev/null
@@ -1,232 +0,0 @@
-# CLI Commands 모듈
-
-Lang2SQL CLI 애플리케이션에서 사용되는 명령어 정의 모듈입니다.
-
-이 모듈은 사용자가 CLI를 통해 Lang2SQL 기능을 사용할 수 있도록 다양한 명령어를 제공합니다.
-
-## 디렉토리 구조
-
-```
-cli/commands/
-├── __pycache__/ # Python 캐시 디렉토리
-├── quary.py # 자연어 질문을 SQL로 변환하는 명령어
-├── run_streamlit.py # Streamlit 실행 명령어
-└── README.md # 이 파일
-```
-
-## 모듈 개요
-
-### `quary.py`
-- **위치**: `cli/commands/quary.py`
-- **설명**: 자연어 질문을 SQL 쿼리로 변환하는 CLI 명령어 정의 모듈
-- **주요 기능**:
- - 사용자가 입력한 자연어 질문을 SQL 쿼리로 변환
- - 다양한 옵션을 통한 쿼리 실행 파라미터 설정
- - SQL 쿼리 결과 출력
-
-#### 주요 함수
-
-##### `query_command()`
-자연어 질문을 SQL 쿼리로 변환하여 출력합니다.
-
-**매개변수:**
-- `question` (str): SQL로 변환할 자연어 질문
-- `database_env` (str, optional): 사용할 데이터베이스 환경 (기본값: "clickhouse")
-- `retriever_name` (str, optional): 테이블 검색기 이름 (기본값: "기본")
-- `top_n` (int, optional): 검색된 상위 테이블 수 제한 (기본값: 5)
-- `device` (str, optional): LLM 실행에 사용할 디바이스 (기본값: "cpu")
-- `use_enriched_graph` (bool, optional): 확장된 그래프(프로파일 추출 + 컨텍스트 보강) 사용 여부
-- `vectordb_type` (str, optional): 사용할 벡터 데이터베이스 타입 ("faiss" 또는 "pgvector", 기본값: "faiss")
-- `vectordb_location` (str, optional): VectorDB 위치 설정
- - FAISS: 디렉토리 경로 (예: ./my_vectordb)
- - pgvector: 연결 문자열 (예: postgresql://user:pass@host:port/db)
- - 기본값: FAISS는 './dev/table_info_db', pgvector는 환경변수 사용
-
-**동작 방식:**
-1. 환경 변수 설정 (VECTORDB_TYPE, VECTORDB_LOCATION)
-2. `engine.query_executor.execute_query()` 호출하여 쿼리 실행
-3. `extract_sql_from_result()`로 SQL 추출
-4. 추출 실패 시 전체 generated_query 출력
-5. 성공 시 SQL 쿼리 출력
-
-**사용 예제:**
-```bash
-# 기본 사용
-lang2sql query "고객 데이터를 기반으로 유니크한 유저 수를 카운트하는 쿼리"
-
-# 옵션 사용
-lang2sql query "고객 데이터를 기반으로 유니크한 유저 수를 카운트하는 쿼리" \
- --database-env clickhouse \
- --retriever-name 기본 \
- --top-n 5 \
- --device cpu \
- --use-enriched-graph
-
-# VectorDB 지정
-lang2sql query "고객 데이터를 기반으로 유니크한 유저 수를 카운트하는 쿼리" \
- --vectordb-type pgvector \
- --vectordb-location postgresql://user:pass@localhost:5432/db
-```
-
-### `run_streamlit.py`
-- **위치**: `cli/commands/run_streamlit.py`
-- **설명**: Streamlit 실행 CLI 명령어 모듈
-- **주요 기능**:
- - CLI 명령어로 Streamlit 애플리케이션 실행
- - 포트 번호 지정 가능
-
-#### 주요 함수
-
-##### `run_streamlit_cli_command()`
-CLI 명령어로 Streamlit 애플리케이션을 실행합니다.
-
-**매개변수:**
-- `port` (int, optional): Streamlit 서버가 바인딩될 포트 번호 (기본값: 8501)
-
-**동작 방식:**
-1. 로깅을 통해 실행 시작 로그 기록
-2. `cli.core.streamlit_runner.run_streamlit_command()` 호출하여 Streamlit 실행
-
-**사용 예제:**
-```bash
-# 기본 포트(8501)로 실행
-lang2sql run-streamlit
-
-# 사용자 지정 포트로 실행
-lang2sql run-streamlit -p 9000
-
-# 또는
-lang2sql run-streamlit --port 9000
-```
-
-## 의존성
-
-### 내부 모듈
-- `cli.utils.logger.configure_logging`: CLI 전용 로깅 유틸리티
-- `cli.core.streamlit_runner.run_streamlit_command`: Streamlit 실행 유틸리티
-- `engine.query_executor.execute_query`: 쿼리 실행 공용 함수
-- `engine.query_executor.extract_sql_from_result`: SQL 추출 함수
-
-### 외부 라이브러리
-- `click`: CLI 프레임워크
-- `os`: 환경 변수 설정을 위한 표준 라이브러리
-
-## 사용 위치
-
-### 1. CLI 메인 모듈 (`cli/__init__.py`)
-CLI 메인 모듈에서 두 명령어를 등록합니다.
-
-```python
-from cli.commands.quary import query_command
-from cli.commands.run_streamlit import run_streamlit_cli_command
-
-# CLI 그룹에 명령어 추가
-cli.add_command(run_streamlit_cli_command)
-cli.add_command(query_command)
-```
-
-## CLI 사용 방법
-
-### `query` 명령어
-
-자연어 질문을 SQL 쿼리로 변환하는 명령어입니다.
-
-**기본 사용법:**
-```bash
-lang2sql query "<자연어 질문>"
-```
-
-**모든 옵션:**
-```bash
-lang2sql query "<자연어 질문>" \
- --database-env <데이터베이스 환경> \
- --retriever-name <검색기 이름> \
- --top-n <테이블 수> \
- --device \
- --use-enriched-graph \
- --vectordb-type \
- --vectordb-location <경로 또는 연결 문자열>
-```
-
-**실제 사용 예:**
-```bash
-# 간단한 쿼리
-lang2sql query "사용자 수를 확인하는 쿼리"
-
-# 옵션을 사용한 쿼리
-lang2sql query "고객 데이터를 기반으로 유니크한 유저 수를 카운트하는 쿼리" \
- --database-env clickhouse \
- --top-n 10 \
- --use-enriched-graph
-```
-
-### `run-streamlit` 명령어
-
-Streamlit 웹 인터페이스를 실행하는 명령어입니다.
-
-**기본 사용법:**
-```bash
-lang2sql run-streamlit
-```
-
-**포트 지정:**
-```bash
-lang2sql run-streamlit -p 9000
-```
-
-**실제 사용 예:**
-```bash
-# 기본 포트로 실행
-lang2sql run-streamlit
-
-# 다른 포트로 실행
-lang2sql run-streamlit --port 9000
-
-# 브라우저에서 http://localhost:9000 접속
-```
-
-## 워크플로우
-
-### `query` 명령어 워크플로우
-1. 사용자가 자연어 질문과 옵션을 CLI로 입력
-2. VectorDB 타입과 위치 환경 변수 설정
-3. `engine.query_executor.execute_query()` 호출
-4. Lang2SQL 파이프라인 실행
-5. SQL 추출 및 출력
-
-### `run-streamlit` 명령어 워크플로우
-1. 사용자가 명령어 실행
-2. `cli.core.streamlit_runner.run_streamlit_command()` 호출
-3. Streamlit 서브프로세스 실행
-4. 웹 브라우저에서 접속 가능
-
-## 환경 변수
-
-### `query` 명령어에서 설정되는 환경 변수
-- `VECTORDB_TYPE`: 벡터 데이터베이스 타입 ("faiss" 또는 "pgvector")
-- `VECTORDB_LOCATION`: 벡터 데이터베이스 위치 (FAISS: 디렉토리 경로, pgvector: 연결 문자열)
-
-## 로깅
-
-모든 명령어는 `cli.utils.logger.configure_logging()`을 사용하여 로그를 기록합니다:
-
-- `query` 명령어: 쿼리 처리 중 오류 발생 시 에러 로그
-- `run-streamlit` 명령어: 실행 시작 로그
-
-## 에러 처리
-
-### `query` 명령어
-- `Exception` 발생 시 에러 로그 기록 및 예외 재발생
-- SQL 추출 실패 시 전체 generated_query 출력 시도
-
-### `run-streamlit` 명령어
-- `subprocess.CalledProcessError` 발생 시 에러 로그 기록 및 예외 재발생
-
-## 주의사항
-
-1. `query` 명령어는 SQL 쿼리만 출력하며 결과는 실행하지 않습니다
-2. `vectordb_location`을 지정하지 않으면 기본값 또는 환경변수 사용
-3. `run-streamlit` 명령어는 서브프로세스로 Streamlit을 실행하므로 프로세스가 종료될 때까지 대기합니다
-4. 기본 포트(8501)가 이미 사용 중이면 포트 변경 필요
-5. `use_enriched_graph` 옵션 사용 시 더 많은 리소스 소모 가능
-
diff --git a/cli/commands/quary.py b/cli/commands/quary.py
deleted file mode 100644
index 5a6e652..0000000
--- a/cli/commands/quary.py
+++ /dev/null
@@ -1,89 +0,0 @@
-"""자연어 질문을 SQL 쿼리로 변환하는 CLI 명령어 정의 모듈.
-
-이 모듈은 사용자가 입력한 자연어 질문을 SQL 쿼리로 변환하여 출력하는
-`query` CLI 명령어를 제공합니다.
-"""
-
-import click
-
-from cli.utils.logger import configure_logging
-
-logger = configure_logging()
-
-
-@click.command(name="query")
-@click.argument("question", type=str)
-@click.option(
- "--flow",
- type=click.Choice(["baseline", "enriched"]),
- default="baseline",
- help="사용할 플로우 (기본값: baseline)",
-)
-@click.option(
- "--top-n",
- type=int,
- default=5,
- help="검색된 상위 테이블 수 제한 (기본값: 5)",
-)
-@click.option(
- "--dialect",
- default=None,
- help="SQL 방언 (예: sqlite, postgresql, mysql, bigquery, duckdb)",
-)
-@click.option(
- "--no-gate",
- is_flag=True,
- help="QuestionGate 비활성화 (enriched 플로우 전용)",
-)
-def query_command(
- question: str,
- flow: str,
- top_n: int,
- dialect: str,
- no_gate: bool,
-) -> None:
- """자연어 질문을 SQL 쿼리로 변환하여 실행 결과를 출력합니다.
-
- 환경변수(LLM_PROVIDER, EMBEDDING_PROVIDER, DB_TYPE 등)로 설정을 제어합니다.
- """
- try:
- from lang2sql.factory import (
- build_db_from_env,
- build_embedding_from_env,
- build_llm_from_env,
- )
- from lang2sql.flows import BaselineNL2SQL, EnrichedNL2SQL
-
- llm = build_llm_from_env()
- db = build_db_from_env()
-
- if flow == "baseline":
- pipeline = BaselineNL2SQL(
- catalog=[],
- llm=llm,
- db=db,
- db_dialect=dialect,
- )
- else:
- embedding = build_embedding_from_env()
- pipeline = EnrichedNL2SQL(
- catalog=[],
- llm=llm,
- db=db,
- embedding=embedding,
- db_dialect=dialect,
- gate_enabled=not no_gate,
- top_n=top_n,
- )
-
- rows = pipeline.run(question)
- if rows:
- import json
-
- print(json.dumps(rows, ensure_ascii=False, indent=2))
- else:
- print("(결과 없음)")
-
- except Exception as e:
- logger.error("쿼리 처리 중 오류 발생: %s", e)
- raise
diff --git a/cli/commands/run_streamlit.py b/cli/commands/run_streamlit.py
deleted file mode 100644
index 21aff02..0000000
--- a/cli/commands/run_streamlit.py
+++ /dev/null
@@ -1,29 +0,0 @@
-"""Streamlit 실행 CLI 명령어 모듈."""
-
-import click
-
-from cli.core.streamlit_runner import run_streamlit_command
-from cli.utils.logger import configure_logging
-
-logger = configure_logging()
-
-
-@click.command(name="run-streamlit")
-@click.option(
- "-p",
- "--port",
- type=int,
- default=8501,
- help=(
- "Streamlit 애플리케이션이 바인딩될 포트 번호를 지정합니다. "
- "기본 포트는 8501이며, 필요 시 다른 포트를 설정할 수 있습니다."
- ),
-)
-def run_streamlit_cli_command(port: int) -> None:
- """CLI 명령어로 Streamlit 애플리케이션을 실행합니다.
-
- Args:
- port (int): Streamlit 서버가 바인딩될 포트 번호. 기본값은 8501.
- """
- logger.info("Executing 'run-streamlit' command on port %d...", port)
- run_streamlit_command(port)
diff --git a/cli/core/README.md b/cli/core/README.md
deleted file mode 100644
index 925c522..0000000
--- a/cli/core/README.md
+++ /dev/null
@@ -1,147 +0,0 @@
-# CLI Core 모듈
-
-Lang2SQL CLI의 핵심 기능을 제공하는 모듈입니다.
-
-## 디렉토리 구조
-
-```
-cli/core/
-├── environment.py # 환경 변수 초기화 모듈
-└── streamlit_runner.py # Streamlit 실행 유틸리티 모듈
-```
-
-## 모듈 설명
-
-### 1. environment.py
-
-환경 변수 초기화를 담당하는 모듈입니다. VectorDB 설정은 UI에서 관리합니다.
-
-#### 주요 기능
-
-- `initialize_environment()`: 환경 변수를 초기화하는 함수
-
-#### 함수 상세
-
-##### `initialize_environment(env_file_path, prompt_dir_path)`
-
-환경 변수를 초기화합니다. VectorDB 설정은 UI에서 관리합니다.
-
-**매개변수:**
-- `env_file_path` (Optional[str]): 로드할 .env 파일 경로. None이면 기본값 사용.
-- `prompt_dir_path` (Optional[str]): 프롬프트 템플릿 디렉토리 경로. None이면 설정하지 않음.
-
-**예외:**
-- `Exception`: 초기화 과정에서 오류가 발생한 경우.
-
-**내부 동작:**
-- `cli.utils.env_loader.load_env()`: .env 파일을 로드합니다.
-- `cli.utils.env_loader.set_prompt_dir()`: 프롬프트 템플릿 디렉토리 경로를 환경 변수로 설정합니다.
-
-#### 사용 예시
-
-```python
-from cli.core.environment import initialize_environment
-
-# 기본 경로로 초기화
-initialize_environment(env_file_path=None, prompt_dir_path=None)
-
-# 사용자 정의 경로로 초기화
-initialize_environment(
- env_file_path="/path/to/.env",
- prompt_dir_path="/path/to/prompts"
-)
-```
-
-#### import 및 사용 위치
-
-이 모듈의 `initialize_environment` 함수는 다음과 같이 사용됩니다:
-
-- **`cli/__init__.py`** (85-90번째 줄): CLI 진입점에서 환경 초기화 시 호출
- ```python
- from cli.core.environment import initialize_environment
-
- initialize_environment(
- env_file_path=env_file_path,
- prompt_dir_path=prompt_dir_path
- )
- ```
-
-### 2. streamlit_runner.py
-
-Streamlit 애플리케이션 실행을 담당하는 유틸리티 모듈입니다.
-
-#### 주요 기능
-
-- `run_streamlit_command()`: 지정된 포트에서 Streamlit 애플리케이션을 실행하는 함수
-
-#### 함수 상세
-
-##### `run_streamlit_command(port)`
-
-지정된 포트에서 Streamlit 애플리케이션을 실행합니다.
-
-**매개변수:**
-- `port` (int): 바인딩할 포트 번호.
-
-**예외:**
-- `subprocess.CalledProcessError`: 실행 실패 시 발생.
-
-**내부 동작:**
-- `subprocess.run()`을 사용하여 `streamlit run` 명령을 실행합니다.
-- 실행 대상: `interface/streamlit_app.py`
-- 서버 주소: `0.0.0.0`
-- 포트: 사용자 지정 값 (기본값: 8501)
-- 로깅: `cli.utils.logger.configure_logging()`을 통해 로그 출력
-
-#### 사용 예시
-
-```python
-from cli.core.streamlit_runner import run_streamlit_command
-
-# 기본 포트(8501)로 실행
-run_streamlit_command(port=8501)
-
-# 사용자 정의 포트로 실행
-run_streamlit_command(port=8080)
-```
-
-#### import 및 사용 위치
-
-이 모듈의 `run_streamlit_command` 함수는 다음과 같이 사용됩니다:
-
-1. **`cli/__init__.py`** (113번째 줄): CLI의 `--run-streamlit` 옵션이 활성화된 경우 호출
- ```python
- from cli.core.streamlit_runner import run_streamlit_command
-
- if run_streamlit:
- run_streamlit_command(port)
- ```
-
-2. **`cli/commands/run_streamlit.py`** (29번째 줄): `run-streamlit` CLI 명령 실행 시 호출
- ```python
- from cli.core.streamlit_runner import run_streamlit_command
-
- @click.command(name="run-streamlit")
- def run_streamlit_cli_command(port: int):
- logger.info("Executing 'run-streamlit' command on port %d...", port)
- run_streamlit_command(port)
- ```
-
-## 의존성
-
-### 내부 의존성
-
-- `cli.utils.env_loader`: 환경 변수 로드 및 프롬프트 디렉토리 설정
-- `cli.utils.logger`: 로깅 설정
-
-### 외부 의존성
-
-- `subprocess`: 프로세스 실행 (streamlit_runner.py)
-
-## 주요 특징
-
-1. **환경 관리**: CLI 진입점에서 일관된 환경 변수 초기화 보장
-2. **UI 중심 설계**: VectorDB 설정은 UI에서 관리하여 사용자 편의성 향상
-3. **유연한 실행**: 다양한 포트에서 Streamlit 애플리케이션 실행 지원
-4. **로깅 지원**: 실행 상태 및 오류 추적 가능
-
diff --git a/cli/core/environment.py b/cli/core/environment.py
deleted file mode 100644
index d2c957a..0000000
--- a/cli/core/environment.py
+++ /dev/null
@@ -1,23 +0,0 @@
-"""환경 변수 초기화 모듈 (VectorDB 설정은 UI에서 관리)."""
-
-from typing import Optional
-
-from cli.utils.env_loader import load_env, set_prompt_dir
-
-
-def initialize_environment(
- *,
- env_file_path: Optional[str],
- prompt_dir_path: Optional[str],
-) -> None:
- """환경 변수를 초기화합니다. VectorDB 설정은 UI에서 관리합니다.
-
- Args:
- env_file_path (Optional[str]): 로드할 .env 파일 경로. None이면 기본값 사용.
- prompt_dir_path (Optional[str]): 프롬프트 템플릿 디렉토리 경로. None이면 설정하지 않음.
-
- Raises:
- Exception: 초기화 과정에서 오류가 발생한 경우.
- """
- load_env(env_file_path=env_file_path)
- set_prompt_dir(prompt_dir_path=prompt_dir_path)
diff --git a/cli/core/streamlit_runner.py b/cli/core/streamlit_runner.py
deleted file mode 100644
index 8437bf3..0000000
--- a/cli/core/streamlit_runner.py
+++ /dev/null
@@ -1,36 +0,0 @@
-"""Streamlit 실행 유틸리티 모듈."""
-
-import subprocess
-
-from cli.utils.logger import configure_logging
-
-logger = configure_logging()
-
-
-def run_streamlit_command(port: int) -> None:
- """지정된 포트에서 Streamlit 애플리케이션을 실행합니다.
-
- Args:
- port (int): 바인딩할 포트 번호.
-
- Raises:
- subprocess.CalledProcessError: 실행 실패 시 발생.
- """
- logger.info("Starting Streamlit application on port %d...", port)
-
- try:
- subprocess.run(
- [
- "streamlit",
- "run",
- "interface/streamlit_app.py",
- "--server.address=0.0.0.0",
- "--server.port",
- str(port),
- ],
- check=True,
- )
- logger.info("Streamlit application started successfully.")
- except subprocess.CalledProcessError as e:
- logger.error("Failed to start Streamlit application: %s", e)
- raise
diff --git a/cli/utils/README.md b/cli/utils/README.md
deleted file mode 100644
index d6a3012..0000000
--- a/cli/utils/README.md
+++ /dev/null
@@ -1,187 +0,0 @@
-# CLI Utils 모듈
-
-CLI 애플리케이션에서 사용되는 유틸리티 함수들을 제공하는 모듈입니다.
-
-## 디렉토리 구조
-
-```
-cli/utils/
-├── __pycache__/
-├── env_loader.py
-├── logger.py
-└── README.md
-```
-
-## 파일 목록 및 설명
-
-### env_loader.py
-
-환경 변수 유틸리티 모듈입니다. `.env` 파일 로드, 프롬프트 디렉토리 설정, VectorDB 타입 및 위치 설정을 제공합니다.
-
-**주요 함수:**
-
-#### `load_env(env_file_path: Optional[str] = None) -> None`
-환경 변수 파일(.env)을 로드합니다.
-
-**파라미터:**
-- `env_file_path` (Optional[str]): .env 파일 경로. None이면 기본 경로 사용.
-
-**동작:**
-- 지정된 경로의 `.env` 파일을 로드하거나, 경로가 없으면 기본 경로의 `.env` 파일을 로드합니다.
-- 성공/실패 메시지를 컬러로 출력합니다.
-- 로드 실패 시 예외를 발생시킵니다.
-
-**사용 예시:**
-```python
-from cli.utils.env_loader import load_env
-
-# 기본 .env 파일 로드
-load_env()
-
-# 특정 경로의 .env 파일 로드
-load_env(env_file_path="/path/to/.env")
-```
-
-#### `set_prompt_dir(prompt_dir_path: Optional[str]) -> None`
-프롬프트 템플릿 디렉토리 경로를 환경 변수로 설정합니다.
-
-**파라미터:**
-- `prompt_dir_path` (Optional[str]): 디렉토리 경로. None이면 설정하지 않음.
-
-**환경 변수:**
-- `PROMPT_TEMPLATES_DIR`: 설정된 프롬프트 디렉토리 경로
-
-**Raises:**
-- `ValueError`: 경로가 유효하지 않을 경우
-
-**사용 예시:**
-```python
-from cli.utils.env_loader import set_prompt_dir
-
-set_prompt_dir(prompt_dir_path="/path/to/prompt/templates")
-```
-
-#### `set_vectordb(vectordb_type: str, vectordb_location: Optional[str] = None) -> None`
-VectorDB 타입과 위치를 환경 변수로 설정합니다.
-
-**파라미터:**
-- `vectordb_type` (str): VectorDB 타입 ("faiss" 또는 "pgvector")
-- `vectordb_location` (Optional[str]): 경로 또는 연결 URL
-
-**환경 변수:**
-- `VECTORDB_TYPE`: 설정된 VectorDB 타입
-- `VECTORDB_LOCATION`: 설정된 VectorDB 경로 또는 연결 URL (지정된 경우)
-
-**Raises:**
-- `ValueError`: 잘못된 타입이나 경로/URL일 경우
-
-**사용 예시:**
-```python
-from cli.utils.env_loader import set_vectordb
-
-# FAISS 설정
-set_vectordb(vectordb_type="faiss", vectordb_location="/path/to/faiss/db")
-
-# pgvector 설정
-set_vectordb(
- vectordb_type="pgvector",
- vectordb_location="postgresql://user:pass@host:port/db"
-)
-```
-
-**사용처:**
-- `cli/core/environment.py` (5번 라인): `load_env`, `set_prompt_dir` 함수를 import하여 사용
- - `initialize_environment` 함수에서 환경 변수 초기화 시 사용
- ```python
- from cli.utils.env_loader import load_env, set_prompt_dir
-
- def initialize_environment(
- *,
- env_file_path: Optional[str],
- prompt_dir_path: Optional[str],
- ) -> None:
- load_env(env_file_path=env_file_path)
- set_prompt_dir(prompt_dir_path=prompt_dir_path)
- ```
-
-### logger.py
-
-CLI 전용 로깅 유틸리티 모듈입니다. 로깅 설정을 구성하고 기본 로거 인스턴스를 반환합니다.
-
-**주요 함수:**
-
-#### `configure_logging(level: int = logging.INFO) -> logging.Logger`
-로깅을 설정하고 기본 로거를 반환합니다.
-
-**파라미터:**
-- `level` (int, optional): 로깅 레벨. 기본값은 `logging.INFO`.
-
-**반환값:**
-- `logging.Logger`: 설정된 로거 인스턴스. 로거 이름은 "cli"입니다.
-
-**로깅 설정:**
-- 레벨: 지정된 레벨 (기본값: `INFO`)
-- 포맷: `%(asctime)s [%(levelname)s] %(message)s`
-- 날짜 포맷: `%Y-%m-%d %H:%M:%S`
-
-**사용 예시:**
-```python
-from cli.utils.logger import configure_logging
-
-# 기본 설정으로 로거 생성
-logger = configure_logging()
-
-# DEBUG 레벨로 로거 생성
-logger = configure_logging(level=logging.DEBUG)
-
-# 로깅 사용
-logger.info("Information message")
-logger.error("Error message")
-```
-
-**사용처:**
-
-1. **`cli/__init__.py`** (14번 라인)
- - CLI 진입점에서 로거 초기화
- ```python
- from cli.utils.logger import configure_logging
-
- logger = configure_logging()
- ```
- - 사용 위치: 18번 라인에서 로거 인스턴스 생성, 89번, 92번 라인에서 에러 및 정보 로깅
-
-2. **`cli/commands/quary.py`** (11번 라인)
- - query 명령어 실행 시 로거 초기화
- ```python
- from cli.utils.logger import configure_logging
-
- logger = configure_logging()
- ```
- - 사용 위치: 13번 라인에서 로거 인스턴스 생성, 112번 라인에서 에러 로깅
-
-3. **`cli/core/streamlit_runner.py`** (5번 라인)
- - Streamlit 실행 모듈에서 로거 초기화
- ```python
- from cli.utils.logger import configure_logging
-
- logger = configure_logging()
- ```
- - 사용 위치: 7번 라인에서 로거 인스턴스 생성, 19번, 33번, 35번 라인에서 정보 및 에러 로깅
-
-4. **`cli/commands/run_streamlit.py`** (6번 라인)
- - run-streamlit 명령어 실행 시 로거 초기화
- ```python
- from cli.utils.logger import configure_logging
-
- logger = configure_logging()
- ```
- - 사용 위치: 8번 라인에서 로거 인스턴스 생성, 28번 라인에서 정보 로깅
-
-## 의존성
-
-- `click`: CLI 출력 및 메시지 표시
-- `dotenv`: `.env` 파일 로드
-- `logging`: 로깅 기능 (Python 표준 라이브러리)
-- `pathlib.Path`: 경로 처리
-- `os`: 환경 변수 설정
-
diff --git a/cli/utils/env_loader.py b/cli/utils/env_loader.py
deleted file mode 100644
index 768fdce..0000000
--- a/cli/utils/env_loader.py
+++ /dev/null
@@ -1,103 +0,0 @@
-"""환경 변수 유틸리티 모듈.
-
-.env 파일 로드, 프롬프트 디렉토리 설정,
-VectorDB 타입 및 위치 설정을 제공합니다.
-"""
-
-import os
-from pathlib import Path
-from typing import Optional
-
-import click
-import dotenv
-
-
-def load_env(
- *,
- env_file_path: Optional[str] = None,
-) -> None:
- """환경 변수 파일(.env)을 로드합니다.
-
- Args:
- env_file_path (Optional[str]): .env 파일 경로. None이면 기본 경로 사용.
- """
- try:
- if env_file_path:
- loaded = dotenv.load_dotenv(env_file_path, override=True)
- if loaded:
- click.secho(f".env 파일 로드 성공: {env_file_path}", fg="green")
- else:
- click.secho(f".env 파일을 찾을 수 없음: {env_file_path}", fg="yellow")
- else:
- dotenv.load_dotenv(override=True)
- click.secho("기본 .env 파일 로드 시도", fg="blue")
- except Exception as e:
- click.secho(f".env 파일 로드 중 오류 발생: {e}", fg="red")
- raise
-
-
-def set_prompt_dir(
- *,
- prompt_dir_path: Optional[str],
-) -> None:
- """프롬프트 템플릿 디렉토리 경로를 설정합니다.
-
- Args:
- prompt_dir_path (Optional[str]): 디렉토리 경로. None이면 설정하지 않음.
-
- Raises:
- ValueError: 경로가 유효하지 않을 경우.
- """
- if not prompt_dir_path:
- click.secho(
- "프롬프트 디렉토리 경로가 지정되지 않아 설정을 건너뜁니다.", fg="yellow"
- )
- return
-
- path_obj = Path(prompt_dir_path)
- if not path_obj.exists() or not path_obj.is_dir():
- click.secho(f"유효하지 않은 디렉토리 경로: {prompt_dir_path}", fg="red")
- raise ValueError(f"Invalid prompt directory path: {prompt_dir_path}")
-
- os.environ["PROMPT_TEMPLATES_DIR"] = str(path_obj.resolve())
- click.secho(f"프롬프트 디렉토리 환경변수 설정됨: {path_obj.resolve()}", fg="green")
-
-
-def set_vectordb(
- *,
- vectordb_type: str,
- vectordb_location: Optional[str] = None,
-) -> None:
- """VectorDB 타입과 위치를 설정합니다.
-
- Args:
- vectordb_type (str): VectorDB 타입 ("faiss" 또는 "pgvector").
- vectordb_location (Optional[str]): 경로 또는 연결 URL.
-
- Raises:
- ValueError: 잘못된 타입이나 경로/URL일 경우.
- """
-
- if vectordb_type not in ("faiss", "pgvector"):
- raise ValueError(f"지원하지 않는 VectorDB 타입: {vectordb_type}")
-
- os.environ["VECTORDB_TYPE"] = vectordb_type
- click.secho(f"VectorDB 타입 설정됨: {vectordb_type}", fg="green")
-
- if vectordb_location:
- if vectordb_type == "faiss":
- path = Path(vectordb_location)
- if not path.exists() or not path.is_dir():
- raise ValueError(
- f"유효하지 않은 FAISS 디렉토리 경로: {vectordb_location}"
- )
- elif vectordb_type == "pgvector":
- if not vectordb_location.startswith("postgresql://"):
- raise ValueError(
- f"pgvector URL은 'postgresql://'로 시작해야 합니다: {vectordb_location}"
- )
-
- os.environ["VECTORDB_LOCATION"] = vectordb_location
- click.secho(f"VectorDB 경로 설정됨: {vectordb_location}", fg="green")
- else:
- click.secho("VectorDB 경로가 지정되지 않아 기본값을 사용합니다.", fg="yellow")
diff --git a/cli/utils/logger.py b/cli/utils/logger.py
deleted file mode 100644
index cfebc9b..0000000
--- a/cli/utils/logger.py
+++ /dev/null
@@ -1,20 +0,0 @@
-"""CLI 전용 로깅 유틸리티 모듈."""
-
-import logging
-
-
-def configure_logging(level: int = logging.INFO) -> logging.Logger:
- """로깅을 설정하고 기본 로거를 반환합니다.
-
- Args:
- level (int, optional): 로깅 레벨. 기본값은 logging.INFO.
-
- Returns:
- logging.Logger: 설정된 로거 인스턴스.
- """
- logging.basicConfig(
- level=level,
- format="%(asctime)s [%(levelname)s] %(message)s",
- datefmt="%Y-%m-%d %H:%M:%S",
- )
- return logging.getLogger("cli")
diff --git a/docs/DEPLOY.md b/docs/DEPLOY.md
new file mode 100644
index 0000000..ce6a7e5
--- /dev/null
+++ b/docs/DEPLOY.md
@@ -0,0 +1,101 @@
+# Deploying the Lang2SQL Discord bot
+
+This guide covers running the **Phase 1 Discord frontend** (`lang2sql-bot`). Be
+honest with yourself about scope first: see [§What's stub](#whats-stub-be-honest).
+
+---
+
+## 1. Environment variables
+
+| Variable | Required | Purpose |
+|---|---|---|
+| `DISCORD_BOT_TOKEN` | **yes** | Bot token from the Discord developer portal. The bot raises a clear error and exits if this is unset. |
+| `OPENAI_API_KEY` | no | When set, the agent uses OpenAI `gpt-4.1-mini`. When unset, it falls back to the **offline `FakeLLM`** (deterministic canned tool cycles — fine for a smoke run, not for real answers). |
+| `LANG2SQL_SECRET_KEY` | no | A urlsafe-base64 Fernet key used to encrypt stored secrets (DSNs/API keys) at rest. If unset, a key is auto-generated and persisted in the SQLite kv table — self-contained but only as private as the DB file. **Set this in production** so secrets decrypt across restarts and machines. |
+
+Generate a Fernet key:
+
+```bash
+.venv/bin/python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
+```
+
+Copy [`.env.example`](../.env.example) to `.env` and fill it in. (The bot reads
+from the process environment; use your hosting platform's secrets mechanism or a
+tool like `direnv`/`dotenv` to export them.)
+
+---
+
+## 2. Create the Discord application and bot
+
+1. Go to the [Discord Developer Portal](https://discord.com/developers/applications) → **New Application**.
+2. **Bot** tab → **Add Bot** → **Reset Token** → copy it into `DISCORD_BOT_TOKEN`.
+3. **Privileged Gateway Intents** → enable **MESSAGE CONTENT INTENT** (the bot
+ reads message text to answer @mentions and thread replies).
+4. **OAuth2 → URL Generator**:
+ - Scopes: `bot`
+ - Bot permissions: **Send Messages**, **Read Message History**, **Attach
+ Files** (for CSV results), **Create Public Threads**, **Send Messages in
+ Threads**.
+5. Open the generated invite URL and add the bot to your test guild.
+
+Then run it:
+
+```bash
+export DISCORD_BOT_TOKEN=...
+.venv/bin/lang2sql-bot
+```
+
+The bot connects to the gateway and serves. Mention it in a channel or DM it.
+
+---
+
+## 3. Hosting options (free tiers)
+
+Per the v4.1 plan (§4.1), V1 targets a free always-on host:
+
+### Oracle Cloud Always Free
+- Provision an **Always Free** ARM (Ampere A1) or AMD micro VM.
+- Install uv, clone the repo, `uv sync`.
+- Export the env vars and run `lang2sql-bot` under a process supervisor
+ (`systemd` unit or `tmux`/`screen` for a quick trial).
+
+### fly.io (free allowance)
+- A tiny `fly.toml` running `lang2sql-bot` as the process; the bot is a
+ long-lived gateway client, not an HTTP server, so no exposed ports are needed.
+- Set `DISCORD_BOT_TOKEN`, `OPENAI_API_KEY`, `LANG2SQL_SECRET_KEY` with
+ `fly secrets set`.
+
+A minimal `systemd` unit:
+
+```ini
+[Service]
+WorkingDirectory=/opt/lang2sql
+ExecStart=/opt/lang2sql/.venv/bin/lang2sql-bot
+Environment=DISCORD_BOT_TOKEN=...
+Environment=OPENAI_API_KEY=...
+Environment=LANG2SQL_SECRET_KEY=...
+Restart=on-failure
+```
+
+---
+
+## 4. Persistence
+
+The V1 `SqliteStore` **defaults to `:memory:`**, so audit/session/secret state is
+lost on restart. For a real deployment, construct the store with a file path so it
+survives restarts and back it up alongside `LANG2SQL_SECRET_KEY` (you need both to
+decrypt stored secrets).
+
+---
+
+## What's stub (be honest)
+
+- **No real database execution.** `PostgresExplorer` returns canned
+ `orders`/`users` schema and sample rows. `run_sql` enforces the safety gate and
+ goes through the executor, but there is no live psycopg connection in V1 — real
+ execution is v1.5. `/connect` stores a DSN (encrypted) but it is not yet used to
+ open a connection.
+- **No reasoning without `OPENAI_API_KEY`.** The offline `FakeLLM` produces
+ deterministic tool cycles, not real answers.
+- **No rate limiting** in V1 — keep deployments to small trial guilds so token
+ spend stays bounded (rate limit + per-user token caps are v1.5).
diff --git a/infra/__init__.py b/infra/__init__.py
deleted file mode 100644
index db0a75d..0000000
--- a/infra/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-"""인프라 계층 패키지 (DB, 모니터링 등)"""
diff --git a/infra/monitoring/README.md b/infra/monitoring/README.md
deleted file mode 100644
index f14d82a..0000000
--- a/infra/monitoring/README.md
+++ /dev/null
@@ -1,148 +0,0 @@
-# infra/monitoring 패키지
-
-서버 상태 확인 및 헬스 체크 기능을 제공하는 모니터링 패키지입니다.
-
-## 디렉토리 구조
-
-```
-infra/monitoring/
-├── __init__.py
-├── __pycache__/
-└── check_server.py
-```
-
-## 파일 설명
-
-### `__init__.py`
-
-모니터링/헬스체크 패키지의 초기화 파일입니다.
-
-**내용:**
-- 패키지 문서화 문자열: "모니터링/헬스체크 패키지"
-
-**역할:**
-- `infra.monitoring` 패키지를 Python 패키지로 인식시키는 초기화 파일
-
----
-
-### `check_server.py`
-
-서버 상태 확인 및 연결 관련 기능을 제공하는 유틸리티 클래스입니다.
-
-**주요 구성 요소:**
-
-1. **HTTP 기반 서버 헬스 체크**
- - `/health` 엔드포인트를 통한 서버 상태 확인
- - 향후 서버 연결 또는 상태 점검 기능 확장 가능한 구조
-
-2. **예외 처리 및 로깅**
- - 요청 실패, 타임아웃, 연결 오류 등의 다양한 예외 상황 처리
- - 로깅을 통해 상세한 실패 원인 기록
- - 결과를 boolean 값으로 반환
-
-**주요 클래스:**
-
-#### `CheckServer`
-
-서버의 상태를 확인하거나 연결을 테스트하는 유틸리티 메서드를 제공하는 클래스입니다.
-
-현재는 GMS 서버의 `/health` 엔드포인트에 대한 헬스 체크 기능을 포함하고 있으며, 향후에는 다양한 서버 연결 확인 및 상태 점검 기능이 추가될 수 있도록 확장 가능한 구조로 설계되었습니다.
-
-**메서드:**
-
-- `is_gms_server_healthy(*, url: str) -> bool` (정적 메서드):
- - 지정된 GMS 서버의 `/health` 엔드포인트에 요청을 보내 상태를 확인합니다.
- - Parameters:
- - `url` (str): 헬스 체크를 수행할 GMS 서버의 기본 URL (예: "http://localhost:8080")
- - Returns:
- - `bool`: 서버가 정상적으로 응답하면 `True`, 예외 발생 시 `False`
- - 기능:
- - 서버 URL과 `/health` 경로를 결합하여 헬스 체크 엔드포인트 생성
- - 3초 타임아웃으로 GET 요청 수행
- - HTTP 200 응답 시 `True` 반환
- - 다음 예외 상황 처리:
- - `ConnectTimeout`, `ReadTimeout`: 타임아웃 오류 로깅
- - `ConnectionError`: 연결 실패 로깅
- - `HTTPError`: HTTP 오류 로깅
- - `RequestException`: 기타 요청 예외 로깅
- - 예외 발생 시 `False` 반환
-
-**의존성:**
-- `requests`: HTTP 요청 수행
-- `urllib.parse.urljoin`: URL 경로 결합
-- `logging`: 로깅 기능
-
-**사용 예시:**
-
-```python
-from infra.monitoring.check_server import CheckServer
-
-# GMS 서버 헬스 체크
-is_healthy = CheckServer.is_gms_server_healthy(url="http://localhost:8080")
-
-if is_healthy:
- print("서버가 정상입니다.")
-else:
- print("서버 연결에 문제가 있습니다.")
-```
-
-## Import 및 사용 현황
-
-### 사용 위치
-
-**`interface/app_pages/settings_sections/data_source_section.py`**
-
-이 모듈에서 `CheckServer` 클래스를 import하여 사용합니다.
-
-**Import:**
-```python
-from infra.monitoring.check_server import CheckServer
-```
-
-**사용 방법:**
-
-1. **DataHub 편집 시 헬스 체크** (117번째 줄)
- ```python
- if st.button("헬스 체크", key="dh_edit_health"):
- ok = CheckServer.is_gms_server_healthy(url=new_url)
- st.session_state["datahub_last_health"] = bool(ok)
- if ok:
- st.success("GMS 서버가 정상입니다.")
- else:
- st.error("GMS 서버 헬스 체크 실패. URL과 네트워크를 확인하세요.")
- ```
-
-2. **DataHub 추가 시 헬스 체크** (160번째 줄)
- ```python
- if st.button("헬스 체크", key="dh_health_new"):
- ok = CheckServer.is_gms_server_healthy(url=dh_url)
- st.session_state["datahub_last_health"] = bool(ok)
- if ok:
- st.success("GMS 서버가 정상입니다.")
- else:
- st.error("GMS 서버 헬스 체크 실패. URL과 네트워크를 확인하세요.")
- ```
-
-**사용 목적:**
-- Streamlit UI에서 DataHub 서버 설정 시, 사용자가 입력한 URL이 유효한지 확인
-- 헬스 체크 결과를 세션 상태에 저장하여 상태 배너에 표시
-- 서버 연결 성공/실패에 따라 사용자에게 적절한 피드백 제공
-
-## 로깅
-
-모듈은 Python의 `logging` 모듈을 사용하여 다음 정보를 로깅합니다:
-- 서버가 정상일 때: INFO 레벨로 성공 메시지
-- 타임아웃 발생 시: ERROR 레벨로 타임아웃 오류 메시지
-- 연결 실패 시: ERROR 레벨로 연결 오류 메시지
-- HTTP 오류 발생 시: ERROR 레벨로 HTTP 오류 메시지
-- 기타 요청 예외 발생 시: ERROR 레벨로 예외 정보 로깅
-
-로깅 레벨은 `INFO`로 설정되어 있으며, 타임스탬프와 로그 레벨 정보가 포함됩니다.
-
-**로깅 포맷:**
-```
-%(asctime)s [%(levelname)s] %(message)s
-```
-
-날짜 형식: `%Y-%m-%d %H:%M:%S`
-
diff --git a/infra/monitoring/__init__.py b/infra/monitoring/__init__.py
deleted file mode 100644
index 20aff86..0000000
--- a/infra/monitoring/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-"""모니터링/헬스체크 패키지"""
diff --git a/infra/monitoring/check_server.py b/infra/monitoring/check_server.py
deleted file mode 100644
index 1cc652d..0000000
--- a/infra/monitoring/check_server.py
+++ /dev/null
@@ -1,74 +0,0 @@
-"""
-서버 상태 확인 및 연결 관련 기능을 제공하는 유틸리티 클래스입니다.
-
-이 모듈은 HTTP 기반의 서버에 대해 다음과 같은 기능을 제공합니다:
-- `/health` 엔드포인트를 통한 서버 헬스 체크
-- 향후 서버 연결 또는 상태 점검과 관련된 기능 추가 예정
-
-각 기능은 요청 실패, 타임아웃, 연결 오류 등의 다양한 예외 상황을 포괄적으로 처리하며,
-로깅을 통해 상세한 실패 원인을 기록하고 결과를 boolean 또는 적절한 형태로 반환합니다.
-"""
-
-import logging
-from urllib.parse import urljoin
-
-import requests
-
-logging.basicConfig(
- level=logging.INFO,
- format="%(asctime)s [%(levelname)s] %(message)s",
- datefmt="%Y-%m-%d %H:%M:%S",
-)
-logger = logging.getLogger(__name__)
-
-
-class CheckServer:
- """
- 서버의 상태를 확인하거나 연결을 테스트하는 유틸리티 메서드를 제공하는 클래스입니다.
-
- 현재는 GMS 서버의 `/health` 엔드포인트에 대한 헬스 체크 기능을 포함하고 있으며,
- 향후에는 다양한 서버 연결 확인 및 상태 점검 기능이 추가될 수 있도록 확장 가능한 구조로 설계되었습니다.
- 모든 기능은 네트워크 오류 및 서버 응답 상태에 따라 예외를 로깅하며, 호출자가 결과를 판단할 수 있도록 boolean 값을 반환합니다.
- """
-
- @staticmethod
- def is_gms_server_healthy(*, url: str) -> bool:
- """
- 지정된 GMS 서버의 `/health` 엔드포인트에 요청을 보내 상태를 확인합니다.
-
- 서버가 HTTP 200 응답을 반환하면 True를 반환하며,
- 요청 실패, 타임아웃, 연결 오류 등의 예외 발생 시 False를 반환하고,
- 로깅을 통해 상세한 에러 정보를 출력합니다.
-
- Args:
- url (str): 헬스 체크를 수행할 GMS 서버의 기본 URL (예: "http://localhost:8080")
-
- Returns:
- bool: 서버가 정상적으로 응답하면 True, 예외 발생 시 False
- """
-
- health_url = urljoin(url, "/health")
-
- try:
- response = requests.get(
- health_url,
- timeout=3,
- )
- response.raise_for_status()
- logger.info("GMS server is healthy: %s", url)
- return True
- except (
- requests.exceptions.ConnectTimeout,
- requests.exceptions.ReadTimeout,
- ) as e:
- logger.error(
- "Timeout while connecting to GMS server: %s | %s", health_url, e
- )
- except requests.exceptions.ConnectionError as e:
- logger.error("Failed to connect to GMS server: %s | %s", health_url, e)
- except requests.exceptions.HTTPError as e:
- logger.error("GMS server returned HTTP error: %s | %s", health_url, e)
- except requests.exceptions.RequestException as e:
- logger.exception("Unexpected request error to GMS server: %s", health_url)
-
- return False
diff --git a/infra/observability/README.md b/infra/observability/README.md
deleted file mode 100644
index 85f6a2e..0000000
--- a/infra/observability/README.md
+++ /dev/null
@@ -1,98 +0,0 @@
-# observability
-
-LLM 응답 메시지에서 토큰 사용량을 집계하고 관찰(observability)하기 위한 유틸리티 모듈을 제공하는 디렉토리입니다.
-
-## 디렉토리 구조
-
-```
-observability/
-├── __pycache__/
-└── token_usage.py
-```
-
-## 파일 설명
-
-### token_usage.py
-
-LLM 응답 메시지에서 토큰 사용량을 집계하기 위한 유틸리티 모듈입니다.
-
-#### 주요 내용
-
-- **TokenUtils 클래스**: LLM 토큰 사용량 집계 유틸리티 클래스
- - `get_token_usage_summary()`: 메시지 데이터에서 input/output/total 토큰 사용량을 각각 집계하는 정적 메서드
- - `usage_metadata` 필드를 기반으로 입력 토큰, 출력 토큰, 총 토큰 사용량을 계산
- - Streamlit, LangChain 등 LLM 응답을 다루는 애플리케이션에서 비용 분석, 사용량 추적 등에 활용 가능
-
-#### 반환 형식
-
-```python
-{
- "input_tokens": int,
- "output_tokens": int,
- "total_tokens": int
-}
-```
-
-## 사용 방법
-
-### Import
-
-이 모듈은 다음과 같이 import되어 사용됩니다:
-
-```python
-from infra.observability.token_usage import TokenUtils
-```
-
-### 실제 사용 예시
-
-#### 1. interface/core/result_renderer.py
-
-`TokenUtils`는 Lang2SQL 결과 표시 모듈에서 토큰 사용량을 계산하고 Streamlit UI에 표시하는 데 사용됩니다.
-
-```75:85:interface/core/result_renderer.py
- if should_show("show_token_usage"):
- st.markdown("---")
- token_summary = TokenUtils.get_token_usage_summary(data=res["messages"])
- st.write("**토큰 사용량:**")
- st.markdown(
- f"""
- - Input tokens: `{token_summary['input_tokens']}`
- - Output tokens: `{token_summary['output_tokens']}`
- - Total tokens: `{token_summary['total_tokens']}`
- """
- )
-```
-
-**사용 컨텍스트**:
-- `display_result()` 함수 내에서 LLM 실행 결과(`res`)의 `messages` 리스트를 전달받아 토큰 사용량을 집계
-- Streamlit UI에서 토큰 사용량 정보를 마크다운 형식으로 표시
-- 사용자가 설정에서 토큰 사용량 표시 옵션(`show_token_usage`)을 활성화한 경우에만 표시
-
-**입력 데이터 형식**:
-- `data` 파라미터는 각 항목이 `usage_metadata` 속성을 포함할 수 있는 객체 리스트입니다
-- 예: LangChain의 `AIMessage` 객체 리스트
-
-#### 사용 패턴
-
-```python
-# 기본 사용법
-token_summary = TokenUtils.get_token_usage_summary(data=messages)
-
-# 반환된 딕셔너리 접근
-input_tokens = token_summary["input_tokens"]
-output_tokens = token_summary["output_tokens"]
-total_tokens = token_summary["total_tokens"]
-```
-
-## 로깅
-
-이 모듈은 Python 표준 `logging` 모듈을 사용하여 토큰 사용량 정보를 기록합니다:
-
-- **DEBUG 레벨**: 각 메시지별 토큰 사용량 상세 정보
-- **INFO 레벨**: 전체 토큰 사용량 요약 정보
-
-## 참고사항
-
-- `usage_metadata` 필드가 없는 객체는 토큰 사용량이 0으로 처리됩니다
-- 각 메시지의 토큰 사용량은 누적되어 최종 합계를 반환합니다
-
diff --git a/infra/observability/token_usage.py b/infra/observability/token_usage.py
deleted file mode 100644
index 301e5a5..0000000
--- a/infra/observability/token_usage.py
+++ /dev/null
@@ -1,93 +0,0 @@
-"""
-token_usage.py
-
-LLM 응답 메시지에서 토큰 사용량을 집계하기 위한 유틸리티 모듈입니다.
-
-이 모듈은 LLM의 `usage_metadata` 필드를 기반으로 입력 토큰, 출력 토큰, 총 토큰 사용량을 계산하는 기능을 제공합니다.
-Streamlit, LangChain 등 LLM 응답을 다루는 애플리케이션에서 비용 분석, 사용량 추적 등에 활용할 수 있습니다.
-"""
-
-import logging
-from typing import Any, List
-
-logging.basicConfig(
- level=logging.INFO,
- format="%(asctime)s [%(levelname)s] %(message)s",
- datefmt="%Y-%m-%d %H:%M:%S",
-)
-logger = logging.getLogger(__name__)
-
-
-class TokenUtils:
- """
- LLM 토큰 사용량 집계 유틸리티 클래스입니다.
-
- 이 클래스는 LLM 응답 메시지 리스트에서 usage_metadata 필드를 추출하여
- input_tokens, output_tokens, total_tokens의 합계를 계산합니다.
-
- 예를 들어, LangChain 또는 OpenAI API 응답 메시지 객체의 토큰 사용 정보를 분석하고자 할 때
- 활용할 수 있습니다.
-
- 사용 예:
- >>> from infra.observability.token_usage import TokenUtils
- >>> summary = TokenUtils.get_token_usage_summary(messages)
- >>> print(summary["total_tokens"])
-
- 반환 형식:
- {
- "input_tokens": int,
- "output_tokens": int,
- "total_tokens": int,
- }
- """
-
- @staticmethod
- def get_token_usage_summary(*, data: List[Any]) -> dict:
- """
- 메시지 데이터에서 input/output/total 토큰 사용량을 각각 집계합니다.
-
- Args:
- data (List[Any]): 각 항목이 usage_metadata를 포함할 수 있는 객체 리스트.
-
- Returns:
- dict: {
- "input_tokens": int,
- "output_tokens": int,
- "total_tokens": int
- }
- """
-
- input_tokens = 0
- output_tokens = 0
- total_tokens = 0
-
- for idx, item in enumerate(data):
- token_usage = getattr(item, "usage_metadata", {})
- in_tok = token_usage.get("input_tokens", 0)
- out_tok = token_usage.get("output_tokens", 0)
- total_tok = token_usage.get("total_tokens", 0)
-
- logger.debug(
- "Message[%d] → input=%d, output=%d, total=%d",
- idx,
- in_tok,
- out_tok,
- total_tok,
- )
-
- input_tokens += in_tok
- output_tokens += out_tok
- total_tokens += total_tok
-
- logger.info(
- "Token usage summary → input: %d, output: %d, total: %d",
- input_tokens,
- output_tokens,
- total_tokens,
- )
-
- return {
- "input_tokens": input_tokens,
- "output_tokens": output_tokens,
- "total_tokens": total_tokens,
- }
diff --git a/interface/README.md b/interface/README.md
deleted file mode 100644
index d90fc49..0000000
--- a/interface/README.md
+++ /dev/null
@@ -1,742 +0,0 @@
-# interface
-
-Streamlit 기반 Lang2SQL 데이터 분석 도구의 사용자 인터페이스 레이어입니다.
-
-## 디렉토리 구조
-
-```
-interface/
-├── app_pages/ # Streamlit 페이지들
-│ ├── __init__.py
-│ ├── chatbot.py # AI ChatBot 페이지
-│ ├── graph_builder.py # LangGraph 구성 페이지
-│ ├── home.py # 홈 페이지
-│ ├── lang2sql.py # Lang2SQL 메인 페이지
-│ ├── settings.py # 설정 페이지 (탭 구성)
-│ ├── sidebar_components/ # 사이드바 UI 컴포넌트
-│ │ ├── __init__.py
-│ │ ├── chatbot_session_controller.py
-│ │ ├── data_source_selector.py
-│ │ ├── db_selector.py
-│ │ ├── embedding_selector.py
-│ │ ├── llm_selector.py
-│ │ └── README.md
-│ └── settings_sections/ # 설정 페이지 섹션들
-│ ├── __init__.py
-│ ├── data_source_section.py
-│ ├── db_section.py
-│ ├── llm_section.py
-│ └── README.md
-├── core/ # 핵심 인터페이스 로직
-│ ├── config/ # 설정 관리
-│ │ ├── __init__.py
-│ │ ├── models.py # 설정 데이터 모델
-│ │ ├── paths.py # 파일 경로 관리
-│ │ ├── persist.py # 디스크 저장/로드
-│ │ ├── registry_data_sources.py
-│ │ ├── registry_db.py
-│ │ ├── registry_llm.py
-│ │ └── settings.py # 설정 업데이트 API
-│ ├── dialects.py # SQL 다이얼렉트 프리셋
-│ ├── lang2sql_runner.py # Lang2SQL 실행 래퍼
-│ ├── result_renderer.py # 결과 시각화
-│ └── session_utils.py # 세션 관리
-├── pages_config.py # 페이지 구성
-└── streamlit_app.py # 메인 진입점
-```
-
-## 아키텍처 개요
-
-```
-┌─────────────────────────────────────────────────────────────────┐
-│ streamlit_app.py │
-│ (메인 진입점, 페이지 네비게이션) │
-└────────────────────────────┬────────────────────────────────────┘
- │
- ┌────────┴─────────┐
- │ pages_config.py │
- │ (페이지 설정) │
- └────────┬─────────┘
- │
- ┌────────────────────┼────────────────────┐
- │ │ │
- ▼ ▼ ▼
-┌───────────────┐ ┌───────────────┐ ┌───────────────┐
-│ home.py │ │ lang2sql.py │ │ chatbot.py │
-│ (홈/소개) │ │ (메인 기능) │ │ (AI 채팅) │
-└───────────────┘ └───────┬───────┘ └───────────────┘
- │ │
- │ ┌────────┴────────┐
- │ │ │
- ▼ ▼ ▼
-┌───────────────┐ ┌───────────────┐ ┌───────────────┐
-│graph_builder.py│ │ settings.py │ │sidebar_components│
-│ (그래프 구성) │ │ (설정) │ │ (사이드바) │
-└───────────────┘ └───────┬───────┘ └───────┬───────┘
- │ │
- ▼ ▼
- ┌────────────────┐ ┌────────────────┐
- │settings_sections│ │ core/config/ │
- │ (설정 UI) │ │ (설정 관리) │
- └────────────────┘ └────────┬───────┘
- │
- ▼
- ┌──────────────────┐
- │ core/ │
- │ - dialects.py │
- │ - lang2sql_runner│
- │ - result_renderer│
- │ - session_utils │
- └──────────────────┘
-```
-
-## 주요 컴포넌트
-
-### 1. streamlit_app.py
-
-Streamlit 애플리케이션의 메인 진입점입니다.
-
-**역할:**
-- 애플리케이션 전역 설정 초기화 (제목, 아이콘, 레이아웃)
-- 페이지 네비게이션 구성 및 실행
-
-**주요 함수:**
-- `configure_app()`: Streamlit 전역 설정
-- `main()`: 애플리케이션 진입점
-
-**사용 예시:**
-```python
-# streamlit run interface/streamlit_app.py
-```
-
-**의존성:**
-- `interface.pages_config.PAGES`: 페이지 목록
-
----
-
-### 2. pages_config.py
-
-Streamlit 페이지 구성 정의 모듈입니다.
-
-**역할:**
-- 각 페이지의 경로와 제목 정의
-- 네비게이션에 사용되는 페이지 리스트 제공
-
-**내용:**
-- `PAGES`: 5개 페이지 정의
- - 홈 (home.py)
- - Lang2SQL (lang2sql.py)
- - 그래프 빌더 (graph_builder.py)
- - ChatBot (chatbot.py)
- - 설정 (settings.py)
-
-**사용처:**
-- `streamlit_app.py` (line 9, 37)
-
----
-
-### 3. app_pages/
-
-Streamlit 애플리케이션의 각 페이지 모듈들입니다.
-
-#### 3.1. home.py
-
-홈 페이지로 Lang2SQL 도구 소개 및 사용 방법 안내를 제공합니다.
-
-**주요 내용:**
-- 도구 소개 및 환영 메시지
-- 사용 방법 가이드
-- 주요 기능 링크 안내
-
-**사용처:**
-- `pages_config.py` (line 18)
-
----
-
-#### 3.2. lang2sql.py
-
-Lang2SQL 메인 페이지로 자연어 질의를 SQL 쿼리로 변환하고 결과를 시각화합니다.
-
-```
-┌──────────────────────────────────────────────────────────────┐
-│ 🔍 Lang2SQL │
-├──────────────────────────────────────────────────────────────┤
-│ │
-│ ┌──────────────────┐ ┌───────────────────────┐ │
-│ │ Sidebar │ │ Main Area │ │
-│ │ │ │ │ │
-│ │ ┌──────────────┐ │ │ ┌───────────────────┐ │ │
-│ │ │Data Source │ │ │ │쿼리 입력 영역 │ │ │
-│ │ │Selector │ │ │ │ │ │ │
-│ │ └──────────────┘ │ │ │"고객 데이터를..." │ │ │
-│ │ ──────────────── │ │ └───────────────────┘ │ │
-│ │ ┌──────────────┐ │ │ │ │
-│ │ │LLM Selector │ │ │ ┌───────────────────┐ │ │
-│ │ └──────────────┘ │ │ │DB 선택 및 관리 │ │ │
-│ │ ──────────────── │ │ └───────────────────┘ │ │
-│ │ ┌──────────────┐ │ │ │ │
-│ │ │Embedding Sel.│ │ │ [검색기 유형] │ │
-│ │ └──────────────┘ │ │ [Top-N 개수] │ │
-│ │ ──────────────── │ │ [모델 실행 장치] │ │
-│ │ ┌──────────────┐ │ │ │ │
-│ │ │DB Selector │ │ │ [쿼리 실행] │ │
-│ │ └──────────────┘ │ │ │ │
-│ │ ──────────────── │ │ ┌───────────────────┐ │ │
-│ │ Output Settings │ │ │ 결과 표시 영역 │ │ │
-│ │ □ Token Usage │ │ │ - SQL 쿼리 │ │ │
-│ │ □ Result Desc │ │ │ - 결과 테이블 │ │ │
-│ │ □ Show SQL │ │ │ - 차트 │ │ │
-│ │ ... │ │ └───────────────────┘ │ │
-│ │ ──────────────── │ │ │ │
-│ │ 워크플로우 선택 │ └───────────────────────┘ │
-│ │ ○ 기본 │ │
-│ │ ○ 확장 │ │
-│ └──────────────────┘ │
-└──────────────────────────────────────────────────────────────┘
-```
-
-**주요 기능:**
-- 사이드바 설정: 데이터 소스, LLM, Embedding, DB 선택
-- 출력 옵션 설정 (토큰 사용량, SQL 표시, 차트 등)
-- 워크플로우 선택 (기본/확장)
-- 자연어 질의 입력 및 SQL 변환 실행
-- DB 다이얼렉트 선택 및 편집
-- 검색기 유형 및 Top-N 설정
-- 결과 시각화 (테이블, 차트)
-
-**주요 함수:**
-- 페이지 레벨에서 직접 실행되는 Streamlit UI 코드
-
-**사용 모듈:**
-- `interface.core.dialects`: 다이얼렉트 프리셋
-- `interface.core.lang2sql_runner`: Lang2SQL 실행
-- `interface.core.result_renderer`: 결과 시각화
-- `interface.core.session_utils`: 그래프 초기화
-- `interface.app_pages.sidebar_components`: 사이드바 컴포넌트
-
-**의존성:**
-- `engine.query_executor.execute_query`: 실제 쿼리 실행
-
----
-
-#### 3.3. graph_builder.py
-
-LangGraph 워크플로우를 구성하고 세션에 적용하는 페이지입니다.
-
-```
-┌──────────────────────────────────────────────────────────────┐
-│ LangGraph 구성 UI │
-├──────────────────────────────────────────────────────────────┤
-│ │
-│ ┌────────────────────────────────────────────────────────┐ │
-│ │ 프리셋 선택: ○ 기본 ○ 확장 ○ 커스텀 │ │
-│ └────────────────────────────────────────────────────────┘ │
-│ │
-│ ┌────────────────────────────────────────────────────────┐ │
-│ │ 커스텀 옵션: │ │
-│ │ ☑ PROFILE_EXTRACTION 포함 │ │
-│ │ ☑ CONTEXT_ENRICHMENT 포함 │ │
-│ │ ☑ QUERY_MAKER 포함 │ │
-│ └────────────────────────────────────────────────────────┘ │
-│ │
-│ ┌────────────────────────────────────────────────────────┐ │
-│ │ GET_TABLE_INFO 설정: │ │
-│ │ - 테이블 검색기: [벡터 검색 (기본) ▼] │ │
-│ │ - 검색할 테이블 정보 개수: [====●====] 5 │ │
-│ │ - 모델 실행 장치: [cpu ▼] │ │
-│ └────────────────────────────────────────────────────────┘ │
-│ │
-│ ┌────────────────────────────────────────────────────────┐ │
-│ │ 실행 순서: │ │
-│ │ GET_TABLE_INFO → PROFILE_EXTRACTION → ... │ │
-│ └────────────────────────────────────────────────────────┘ │
-│ │
-│ ┌────────────────────────────────────────────────────────┐ │
-│ │ 그래프 생성: │ │
-│ │ ℹ️ 그래프가 세션에 적용되었습니다. │ │
-│ │ [세션 그래프 새로고침] │ │
-│ └────────────────────────────────────────────────────────┘ │
-│ │
-│ ┌────────────────────────────────────────────────────────┐ │
-│ │ 현재 세션 그래프 설정 (expander) │ │
-│ └────────────────────────────────────────────────────────┘ │
-└──────────────────────────────────────────────────────────────┘
-```
-
-**주요 기능:**
-- 프리셋 선택: 기본, 확장, 커스텀
-- 노드 시퀀스 구성 (GET_TABLE_INFO → PROFILE → CONTEXT → QUERY_MAKER)
-- GET_TABLE_INFO 설정 (검색기, Top-N, 장치)
-- 그래프 세션에 적용 및 새로고침
-- 현재 그래프 설정 확인
-
-**주요 함수:**
-- `build_selected_sequence()`: 프리셋에 따른 노드 시퀀스 생성
-- `build_state_graph()`: StateGraph 빌더 생성
-- `render_sequence()`: 시퀀스를 문자열로 변환
-
-**사용 모듈:**
-- `utils.llm.graph_utils.base`: 노드 정의 및 그래프 유틸
-
----
-
-#### 3.4. chatbot.py
-
-AI ChatBot 페이지로 LangGraph 기반 대화형 인터페이스를 제공합니다.
-
-```
-┌──────────────────────────────────────────────────────────────┐
-│ 🤖 AI ChatBot │
-├───────────────────────────┬──────────────────────────────────┤
-│ Sidebar │ Chat Area │
-│ │ │
-│ ┌───────────────────────┐ │ ┌─────────────────────────────┐ │
-│ │Data Source Selector │ │ │ 안녕하세요! 무엇을... 🤖 │ │
-│ └───────────────────────┘ │ └─────────────────────────────┘ │
-│ ───────────────────────── │ │
-│ ┌───────────────────────┐ │ ┌─────────────────────────────┐ │
-│ │LLM Selector │ │ │ 사용자: 데이터베이스 테이블... │ │
-│ └───────────────────────┘ │ └─────────────────────────────┘ │
-│ ───────────────────────── │ │
-│ ┌───────────────────────┐ │ ┌─────────────────────────────┐ │
-│ │Embedding Selector │ │ │ Assistant: 관련 테이블은... │ │
-│ └───────────────────────┘ │ │ 🤖 모델: gpt-4o-mini │ │
-│ ───────────────────────── │ └─────────────────────────────┘ │
-│ ┌───────────────────────┐ │ │
-│ │DB Selector │ │ ┌─────────────────────────────┐ │
-│ └───────────────────────┘ │ │ 사용자: [입력 중...] │ │
-│ ───────────────────────── │ └─────────────────────────────┘ │
-│ 🤖 ChatBot 설정 │ │
-│ ┌───────────────────────┐ │ │
-│ │ Thread ID: uuid... │ │ │
-│ │ [새 세션 시작] │ │ │
-│ └───────────────────────┘ │ │
-│ ───────────────────────── │ │
-│ 대화 기록: │ │
-│ - Thread ID │ │
-│ - 메시지 목록 (JSON) │ │
-└───────────────────────────┴─────────────────────────────────┘
-```
-
-**주요 기능:**
-- OpenAI 기반 대화형 AI 채팅
-- 사이드바 설정: 데이터 소스, LLM, Embedding, DB
-- Thread ID 기반 세션 관리
-- 대화 기록 표시 및 관리
-- 신규 세션 시작 기능
-
-**주요 함수:**
-- `initialize_session_state()`: 세션 상태 초기화 및 ChatBot 인스턴스 생성
-
-**사용 모듈:**
-- `utils.llm.chatbot.ChatBot`: ChatBot 핵심 로직
-- `interface.app_pages.sidebar_components`: 사이드바 컴포넌트
-- `interface.core.config.load_config`: 설정 로드
-
-**제약사항:**
-- 현재 OpenAI만 지원
-- OpenAI API 키 필수
-
----
-
-#### 3.5. settings.py
-
-설정 페이지로 데이터 소스, LLM, DB 설정을 탭으로 관리합니다.
-
-```
-┌──────────────────────────────────────────────────────────────┐
-│ ⚙️ 설정 │
-├──────────────────────────────────────────────────────────────┤
-│ │
-│ ┌──────────┬──────────┬──────────┐ │
-│ │데이터 소스 │ LLM │ DB │ │
-│ └──────────┴──────────┴──────────┘ │
-│ │
-│ [선택된 탭 내용 표시] │
-│ │
-└──────────────────────────────────────────────────────────────┘
-```
-
-**주요 기능:**
-- 3개 탭으로 구성: 데이터 소스, LLM, DB
-- 각 탭은 settings_sections 모듈 사용
-- 세션 우선 설정 반영
-
-**사용 모듈:**
-- `interface.app_pages.settings_sections`: 각 설정 섹션
-
----
-
-#### 3.6. sidebar_components/
-
-사이드바에서 사용되는 설정 선택 컴포넌트들입니다.
-
-**모듈 구조:**
-- `data_source_selector.py`: 데이터 소스 선택 (DataHub/VectorDB)
-- `llm_selector.py`: LLM 프로파일 선택
-- `embedding_selector.py`: Embedding 프로파일 선택
-- `db_selector.py`: DB 프로파일 선택
-- `chatbot_session_controller.py`: ChatBot 세션 관리
-
-**사용처:**
-- `lang2sql.py`: 메인 페이지 사이드바
-- `chatbot.py`: ChatBot 페이지 사이드바
-
-**자세한 내용:** `sidebar_components/README.md` 참고
-
----
-
-#### 3.7. settings_sections/
-
-설정 페이지의 각 섹션 UI 모듈들입니다.
-
-**모듈 구조:**
-- `data_source_section.py`: DataHub/VectorDB 관리
-- `llm_section.py`: LLM 및 Embedding 설정
-- `db_section.py`: DB 연결 설정
-
-**사용처:**
-- `settings.py`: 설정 페이지 탭 내용
-
-**자세한 내용:** `settings_sections/README.md` 참고
-
----
-
-### 4. core/
-
-핵심 인터페이스 로직을 담당하는 모듈들입니다.
-
-#### 4.1. dialects.py
-
-SQL 다이얼렉트 프리셋 정의 및 관리 모듈입니다.
-
-**주요 내용:**
-- `DialectOption`: SQL 엔진 특성 데이터클래스
- - `name`: 엔진 표시 이름
- - `supports_ilike`: ILIKE 지원 여부
- - `hints`: 자주 쓰이는 함수 목록
-- `PRESET_DIALECTS`: 9개 SQL 엔진 프리셋
- - PostgreSQL, ClickHouse, Trino, Snowflake, Redshift
- - BigQuery, MSSQL, Oracle, DuckDB
-
-**사용처:**
-- `lang2sql.py` (line 19, 85-124): DB 선택 및 편집 UI
-
----
-
-#### 4.2. lang2sql_runner.py
-
-Lang2SQL 실행 래퍼 모듈입니다.
-
-**주요 함수:**
-- `run_lang2sql()`: 자연어 질의를 SQL로 변환 후 실행
-
-**파라미터:**
-- `query`: 자연어 질문
-- `database_env`: 데이터베이스 환경 이름
-- `retriever_name`: 검색기 유형
-- `top_n`: 검색할 테이블 정보 개수
-- `device`: 모델 실행 장치
-
-**사용처:**
-- `lang2sql.py` (line 139-145): 쿼리 실행
-
-**의존성:**
-- `engine.query_executor.execute_query`: 실제 실행 로직
-
----
-
-#### 4.3. result_renderer.py
-
-Lang2SQL 실행 결과 시각화 모듈입니다.
-
-**주요 함수:**
-- `display_result()`: Streamlit UI로 결과 출력
-
-**표시 항목:**
-- Question Gate 결과
-- 문서 적합성 평가
-- 토큰 사용량
-- SQL 쿼리 및 해석
-- 결과 설명
-- 재해석된 질문
-- 참고 테이블 목록
-- 쿼리 실행 결과 테이블
-- 결과 차트 (Plotly)
-
-**사용 모듈:**
-- `infra.observability.token_usage.TokenUtils`: 토큰 사용량
-- `utils.databases.DatabaseFactory`: DB 커넥터
-- `utils.llm.llm_response_parser.LLMResponseParser`: SQL 파싱
-- `utils.visualization.display_chart.DisplayChart`: 차트 생성
-
-**사용처:**
-- `lang2sql.py` (line 146): 결과 표시
-
----
-
-#### 4.4. session_utils.py
-
-Streamlit 세션 상태에서 그래프 빌더를 초기화하는 모듈입니다.
-
-**주요 함수:**
-- `init_graph()`: 그래프 초기화 및 세션 상태 갱신
-
-**파라미터:**
-- `use_enriched`: 확장 그래프 사용 여부
-
-**반환값:**
-- 그래프 유형 문자열 ("확장된" 또는 "기본")
-
-**사용처:**
-- `lang2sql.py` (line 72, 76): 그래프 초기화
-
-**의존성:**
-- `utils.llm.graph_utils.enriched_graph`: 확장 그래프
-- `utils.llm.graph_utils.basic_graph`: 기본 그래프
-
----
-
-#### 4.5. config/
-
-설정 관리 패키지로 데이터 소스, DB, LLM, Embedding 설정을 관리합니다.
-
-**아키텍처:**
-
-```
-config/
-├── models.py # 데이터 모델 정의
-│ ├── Config # 전역 설정
-│ ├── DataHubSource # DataHub 소스
-│ ├── VectorDBSource # VectorDB 소스
-│ ├── DataSourcesRegistry # 데이터 소스 레지스트리
-│ ├── DBConnectionProfile # DB 프로파일
-│ ├── DBConnectionsRegistry # DB 레지스트리
-│ ├── LLMProfile # LLM 프로파일
-│ ├── LLMRegistry # LLM 레지스트리
-│ ├── EmbeddingProfile # Embedding 프로파일
-│ └── EmbeddingRegistry # Embedding 레지스트리
-│
-├── paths.py # 파일 경로 관리
-│ ├── get_registry_file_path() # 데이터 소스 레지스트리 경로
-│ ├── get_db_registry_file_path() # DB 레지스트리 경로
-│ ├── get_llm_registry_file_path() # LLM 레지스트리 경로
-│ └── get_embedding_registry_file_path() # Embedding 레지스트리 경로
-│
-├── persist.py # 디스크 저장/로드
-│ ├── save_registry_to_disk() # 데이터 소스 저장
-│ ├── load_registry_from_disk() # 데이터 소스 로드
-│ ├── save_db_registry_to_disk() # DB 저장
-│ ├── load_db_registry_from_disk() # DB 로드
-│ ├── save_llm_registry_to_disk() # LLM 저장
-│ ├── load_llm_registry_from_disk() # LLM 로드
-│ ├── save_embedding_registry_to_disk() # Embedding 저장
-│ └── load_embedding_registry_from_disk() # Embedding 로드
-│
-├── settings.py # 설정 업데이트 API
-│ ├── load_config() # 설정 로드
-│ ├── update_datahub_server() # DataHub 서버 업데이트
-│ ├── update_data_source_mode() # 데이터 소스 모드 업데이트
-│ ├── update_vectordb_settings() # VectorDB 설정 업데이트
-│ ├── update_llm_settings() # LLM 설정 업데이트
-│ ├── update_embedding_settings() # Embedding 설정 업데이트
-│ └── update_db_settings() # DB 설정 업데이트
-│
-├── registry_data_sources.py # 데이터 소스 레지스트리 관리
-│ ├── get_data_sources_registry() # 레지스트리 조회
-│ ├── add_datahub_source() # DataHub 추가
-│ ├── update_datahub_source() # DataHub 업데이트
-│ ├── delete_datahub_source() # DataHub 삭제
-│ ├── add_vectordb_source() # VectorDB 추가
-│ ├── update_vectordb_source() # VectorDB 업데이트
-│ └── delete_vectordb_source() # VectorDB 삭제
-│
-├── registry_db.py # DB 레지스트리 관리
-│ ├── get_db_connections_registry() # 레지스트리 조회
-│ ├── add_db_connection() # DB 추가
-│ ├── update_db_connection() # DB 업데이트
-│ └── delete_db_connection() # DB 삭제
-│
-├── registry_llm.py # LLM/Embedding 레지스트리 관리
-│ ├── get_llm_registry() # LLM 레지스트리 조회
-│ ├── save_llm_profile() # LLM 프로파일 저장
-│ ├── get_embedding_registry() # Embedding 레지스트리 조회
-│ └── save_embedding_profile() # Embedding 프로파일 저장
-│
-└── __init__.py # 패키지 공개 API
-```
-
-**설정 우선순위:**
-1. 세션 상태 (`st.session_state`)
-2. 환경 변수 (`os.getenv`)
-3. 기본값
-
-**저장 위치:**
-- 기본: `./config/` 디렉토리
-- 환경 변수로 오버라이드 가능:
- - `LANG2SQL_REGISTRY_PATH`
- - `LANG2SQL_DB_REGISTRY_PATH`
- - `LANG2SQL_LLM_REGISTRY_PATH`
- - `LANG2SQL_EMBEDDING_REGISTRY_PATH`
-
-**사용 예시:**
-```python
-from interface.core.config import load_config, get_data_sources_registry
-
-# 설정 로드
-config = load_config()
-
-# 레지스트리 조회
-registry = get_data_sources_registry()
-
-# DataHub 추가
-from interface.core.config import add_datahub_source
-add_datahub_source(
- name="Production",
- url="http://datahub.prod:8080",
- faiss_path="./prod/faiss",
- note="프로덕션 DataHub"
-)
-```
-
----
-
-## 전체 데이터 흐름
-
-```
-┌────────────────────────────────────────────────────────────┐
-│ 사용자 입력 │
-│ (자연어 질의, 설정 변경 등) │
-└──────────────────────┬─────────────────────────────────────┘
- │
- ▼
-┌────────────────────────────────────────────────────────────┐
-│ Streamlit UI Layer │
-│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
-│ │ app_pages/ │ │ core/ │ │ config/ │ │
-│ │ - lang2sql │ │ - runner │ │ - settings │ │
-│ │ - chatbot │ │ - renderer │ │ - registry │ │
-│ │ - settings │ │ - dialects │ │ - persist │ │
-│ └─────────────┘ └─────────────┘ └─────────────┘ │
-└──────────────────┬─────────────────────────────────────────┘
- │
- ▼
-┌────────────────────────────────────────────────────────────┐
-│ Business Logic Layer │
-│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
-│ │ engine/ │ │ utils/llm/ │ │ utils/data/ │ │
-│ │ - executor │ │ - graph │ │ - databases │ │
-│ │ │ │ - factory │ │ - hub │ │
-│ └─────────────┘ └─────────────┘ └─────────────┘ │
-└──────────────────┬─────────────────────────────────────────┘
- │
- ▼
-┌────────────────────────────────────────────────────────────┐
-│ Data & External Services │
-│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
-│ │ DataHub │ │ Database │ │ LLM APIs │ │
-│ │ VectorDB │ │ (DBs) │ │ (OpenAI, │ │
-│ │ │ │ │ │ AWS, etc) │ │
-│ └─────────────┘ └─────────────┘ └─────────────┘ │
-└────────────────────────────────────────────────────────────┘
- │
- ▼
-┌────────────────────────────────────────────────────────────┐
-│ 결과 반환 │
-│ (SQL, 테이블, 차트, 설명 등) │
-└────────────────────────────────────────────────────────────┘
-```
-
-## 주요 설정 파일
-
-설정은 JSON 파일로 `./config/` 디렉토리에 저장됩니다.
-
-- `data_sources.json`: DataHub/VectorDB 레지스트리
-- `db_connections.json`: DB 프로파일 레지스트리
-- `llm_profiles.json`: LLM 프로파일 레지스트리
-- `embedding_profiles.json`: Embedding 프로파일 레지스트리
-
-**예시: data_sources.json**
-```json
-{
- "datahub": [
- {
- "name": "Local DataHub",
- "url": "http://localhost:8080",
- "faiss_path": "./dev/table_info_db",
- "note": "로컬 개발 환경"
- }
- ],
- "vectordb": [
- {
- "name": "Local FAISS",
- "type": "faiss",
- "location": "./dev/faiss_db",
- "collection_prefix": null,
- "note": "로컬 벡터 DB"
- }
- ]
-}
-```
-
-## 사용 방법
-
-### 애플리케이션 실행
-
-```bash
-# 메인 애플리케이션 실행
-streamlit run interface/streamlit_app.py
-
-# 또는
-python -m interface.streamlit_app
-```
-
-### 설정 관리
-
-```python
-from interface.core.config import (
- load_config,
- get_data_sources_registry,
- add_datahub_source,
- update_db_settings,
-)
-
-# 설정 로드
-config = load_config()
-
-# DataHub 추가
-add_datahub_source(
- name="Production",
- url="http://datahub.prod:8080",
- faiss_path="./prod/faiss"
-)
-
-# DB 설정 업데이트
-update_db_settings(
- db_type="postgresql",
- values={"host": "localhost", "port": "5432", "user": "admin"},
- secrets={"password": "secret"}
-)
-```
-
-## 의존성
-
-**주요 패키지:**
-- `streamlit`: UI 프레임워크
-- `pandas`: 데이터 처리
-- `plotly`: 차트 시각화
-- `langchain_core`: LLM 메시지 처리
-
-**프로젝트 내부 모듈:**
-- `engine.query_executor`: 쿼리 실행
-- `utils.llm`: LLM 관련 유틸리티
-- `utils.data`: 데이터 관련 유틸리티
-- `utils.databases`: 데이터베이스 유틸리티
-- `infra.observability`: 모니터링 및 관찰성
-
-## 참고 문서
-
-- `app_pages/sidebar_components/README.md`: 사이드바 컴포넌트 상세
-- `app_pages/settings_sections/README.md`: 설정 섹션 상세
-- `README_UPDATE_CONDITION.md`: 문서 업데이트 가이드
-
diff --git a/interface/app_pages/chatbot.py b/interface/app_pages/chatbot.py
deleted file mode 100644
index 8c460fd..0000000
--- a/interface/app_pages/chatbot.py
+++ /dev/null
@@ -1,157 +0,0 @@
-"""
-AI ChatBot 페이지
-LangGraph와 OpenAI를 활용한 대화형 인터페이스
-"""
-
-import os
-import streamlit as st
-
-from utils.llm.chatbot import ChatBot
-from interface.app_pages.sidebar_components import (
- render_sidebar_data_source_selector,
- render_sidebar_llm_selector,
- render_sidebar_embedding_selector,
- render_sidebar_db_selector,
- render_sidebar_chatbot_session_controller,
-)
-from interface.core.config import load_config
-
-
-def initialize_session_state():
- """세션 상태 초기화 함수
-
- Streamlit의 session_state를 사용하여 앱의 상태를 유지합니다.
- LLM 설정을 sidebar의 llm_selector에서 선택한 값으로부터 가져옵니다.
- """
- # 채팅 메시지 기록 저장 (자동으로 시작)
- if "chatbot_messages" not in st.session_state:
- st.session_state.chatbot_messages = []
-
- # LLM 공급자 확인 (현재 ChatBot은 OpenAI만 지원)
- llm_provider = (
- st.session_state.get("LLM_PROVIDER") or os.getenv("LLM_PROVIDER") or "openai"
- ).lower()
-
- if llm_provider != "openai":
- st.error(
- f"⚠️ ChatBot은 현재 OpenAI만 지원합니다. 설정 > LLM에서 OpenAI 프로파일을 선택하거나 LLM_PROVIDER를 'openai'로 설정해주세요."
- )
- st.stop()
-
- # OpenAI API 키 확인
- openai_api_key = st.session_state.get("OPEN_AI_KEY") or os.getenv("OPEN_AI_KEY")
-
- if not openai_api_key:
- st.error(
- "⚠️ OpenAI API 키가 설정되지 않았습니다. 설정 > LLM에서 OpenAI API 키를 입력하거나, 사이드바에서 LLM 프로파일을 적용해주세요."
- )
- st.stop()
-
- # 사용할 모델명 가져오기 (llm_selector에서 설정한 값)
- model_name = (
- st.session_state.get("OPEN_AI_LLM_MODEL")
- or os.getenv("OPEN_AI_LLM_MODEL")
- or "gpt-4o-mini"
- )
-
- # DataHub 서버 URL 가져오기 (config에서 로드)
- config = load_config()
- gms_server = config.datahub_server
-
- # ChatBot 인스턴스 생성 또는 모델 업데이트
- if "chatbot_instance" not in st.session_state:
- st.session_state.chatbot_instance = ChatBot(
- openai_api_key, model_name=model_name, gms_server=gms_server
- )
- else:
- # 기존 인스턴스가 있는 경우, 모델이나 API 키, gms_server가 변경되었는지 확인
- existing_bot = st.session_state.chatbot_instance
- if (
- existing_bot.model_name != model_name
- or existing_bot.openai_api_key != openai_api_key
- or existing_bot.gms_server != gms_server
- ):
- st.session_state.chatbot_instance = ChatBot(
- openai_api_key, model_name=model_name, gms_server=gms_server
- )
-
-
-# 세션 상태 초기화 실행
-initialize_session_state()
-
-# 페이지 제목
-st.title("🤖 AI ChatBot")
-
-st.markdown("""
- LangGraph 기반 AI ChatBot과 대화를 나눌 수 있습니다.
- - 데이터베이스 테이블 정보 검색
- - 용어집 조회
- - 쿼리 예제 조회
- - 대화를 통해 질문 구체화
- """)
-
-# 설정 로드
-config = load_config()
-
-# 사이드바 UI 구성 (lang2sql.py와 동일한 구조)
-render_sidebar_data_source_selector(config)
-st.sidebar.divider()
-render_sidebar_llm_selector()
-st.sidebar.divider()
-render_sidebar_embedding_selector()
-st.sidebar.divider()
-render_sidebar_db_selector()
-st.sidebar.divider()
-
-# ChatBot 전용 설정
-with st.sidebar:
- st.markdown("### 🤖 ChatBot 설정")
- st.divider()
- thread_id = render_sidebar_chatbot_session_controller()
-
-
-# 첫 메시지가 없으면 환영 메시지 추가
-if not st.session_state.chatbot_messages:
- hello_message = "안녕하세요! 무엇을 도와드릴까요? 🤖"
- st.session_state.chatbot_messages = [
- {"role": "assistant", "content": hello_message}
- ]
-
-# 저장된 모든 메시지를 순서대로 표시
-for message in st.session_state.chatbot_messages:
- with st.chat_message(message["role"]):
- st.markdown(message["content"])
-
-# 사용자 입력 처리
-if prompt := st.chat_input("메시지를 입력하세요"):
- # 사용자 메시지를 기록에 추가
- st.session_state.chatbot_messages.append({"role": "user", "content": prompt})
- with st.chat_message("user"):
- st.markdown(prompt)
-
- # AI 응답 생성 및 표시
- with st.chat_message("assistant"):
- try:
- # ChatBot을 통해 응답 생성
- response = st.session_state.chatbot_instance.chat(prompt, thread_id)
-
- # 응답 내용 추출
- response_content = response["messages"][-1].content
-
- # 모델 정보 표시
- model_name = st.session_state.chatbot_instance.model_name
- st.caption(f"🤖 모델: {model_name}")
-
- # 응답 표시
- st.markdown(response_content)
-
- # AI 응답을 기록에 추가
- st.session_state.chatbot_messages.append(
- {"role": "assistant", "content": response_content}
- )
- except Exception as e:
- error_msg = f"오류가 발생했습니다: {str(e)}"
- st.error(error_msg)
- st.session_state.chatbot_messages.append(
- {"role": "assistant", "content": error_msg}
- )
diff --git a/interface/app_pages/home.py b/interface/app_pages/home.py
deleted file mode 100644
index ef7644b..0000000
--- a/interface/app_pages/home.py
+++ /dev/null
@@ -1,25 +0,0 @@
-"""
-홈 페이지 모듈.
-
-Lang2SQL 데이터 분석 도구의 소개와 사용 방법을 안내합니다.
-"""
-
-import streamlit as st
-
-st.title("🏠 홈")
-
-st.markdown("""
- ### Lang2SQL 데이터 분석 도구에 오신 것을 환영합니다 🎉
-
- 이 도구는 자연어로 작성한 질문을 SQL 쿼리로 변환하고,
- 데이터베이스를 조회하여 결과를 **표와 차트**로 시각화합니다.
-
- ---
- #### 사용 방법
- 1. 왼쪽 메뉴에서 원하는 기능 페이지를 선택하세요.
- 2. **🔍 Lang2SQL**: 자연어 → SQL 변환 및 결과 분석
- 3. **📊 그래프 빌더**: LangGraph 실행 순서를 프리셋/커스텀으로 구성하고 세션에 적용
- 4. **⚙️ 설정**: 데이터 소스, LLM, DB 연결 등 환경 설정
- """)
-
-st.info("왼쪽 메뉴에서 기능 페이지를 선택해 시작하세요 🚀")
diff --git a/interface/app_pages/lang2sql.py b/interface/app_pages/lang2sql.py
deleted file mode 100644
index 214e480..0000000
--- a/interface/app_pages/lang2sql.py
+++ /dev/null
@@ -1,78 +0,0 @@
-"""
-Lang2SQL Streamlit 애플리케이션.
-
-자연어 질의를 SQL 쿼리로 변환하고 실행 결과를 시각화하는 인터페이스를 제공합니다.
-
-주요 기능:
- - 사용자 질의를 SQL 쿼리로 변환 후 실행
- - SQL 방언(dialect) 선택 지원
- - 쿼리 실행 결과를 표로 시각화
- - Baseline / Enriched 워크플로우 선택
-"""
-
-import pandas as pd
-import streamlit as st
-
-from interface.core.config import load_config
-from interface.core.lang2sql_runner import run_lang2sql
-from interface.app_pages.sidebar_components import (
- render_sidebar_data_source_selector,
- render_sidebar_db_selector,
- render_sidebar_embedding_selector,
- render_sidebar_llm_selector,
-)
-
-TITLE = "Lang2SQL"
-DEFAULT_QUERY = "고객 데이터를 기반으로 유니크한 유저 수를 카운트하는 쿼리"
-
-DIALECT_OPTIONS = ["default", "sqlite", "postgresql", "mysql", "bigquery", "duckdb"]
-
-st.title(TITLE)
-
-config = load_config()
-
-render_sidebar_data_source_selector(config)
-st.sidebar.divider()
-render_sidebar_llm_selector()
-st.sidebar.divider()
-render_sidebar_embedding_selector()
-st.sidebar.divider()
-render_sidebar_db_selector()
-st.sidebar.divider()
-
-st.sidebar.markdown("### 워크플로우 선택")
-use_enriched = st.sidebar.checkbox(
- "프로파일 추출 & 컨텍스트 보강 워크플로우 사용 (Enriched)", value=False
-)
-
-# 쿼리 입력
-user_query = st.text_area("쿼리를 입력하세요:", value=DEFAULT_QUERY)
-
-# 설정
-col1, col2 = st.columns(2)
-with col1:
- user_dialect = st.selectbox("SQL 방언(Dialect):", options=DIALECT_OPTIONS, index=0)
-with col2:
- user_top_n = st.slider(
- "검색할 테이블 정보 개수:", min_value=1, max_value=20, value=5
- )
-
-if st.button("쿼리 실행"):
- with st.spinner("쿼리 실행 중..."):
- res = run_lang2sql(
- query=user_query,
- db_dialect=user_dialect if user_dialect != "default" else None,
- top_n=user_top_n,
- use_enriched=use_enriched,
- )
-
- if res.get("error"):
- st.error(f"오류 발생: {res['error']}")
- else:
- rows = res.get("rows", [])
- if rows:
- st.success(f"{len(rows)}개 행 반환됨.")
- st.markdown("**쿼리 실행 결과:**")
- st.dataframe(pd.DataFrame(rows))
- else:
- st.info("쿼리 실행 결과가 없습니다.")
diff --git a/interface/app_pages/settings.py b/interface/app_pages/settings.py
deleted file mode 100644
index 72d05c7..0000000
--- a/interface/app_pages/settings.py
+++ /dev/null
@@ -1,30 +0,0 @@
-"""
-Settings 페이지 – 섹션 기반 UI
-"""
-
-import streamlit as st
-
-from interface.core.config import load_config
-from interface.app_pages.settings_sections.data_source_section import (
- render_data_source_section,
-)
-from interface.app_pages.settings_sections.llm_section import render_llm_section
-from interface.app_pages.settings_sections.db_section import render_db_section
-
-st.title("⚙️ 설정")
-
-config = load_config()
-
-tabs = st.tabs(["데이터 소스", "LLM", "DB"])
-
-with tabs[0]:
- render_data_source_section(config)
-
-with tabs[1]:
- render_llm_section(config)
-
-with tabs[2]:
- render_db_section()
-
-st.divider()
-st.caption("민감 정보는 로그에 기록되지 않으며, 이 설정은 현재 세션에 우선 반영됩니다.")
diff --git a/interface/app_pages/settings_sections/README.md b/interface/app_pages/settings_sections/README.md
deleted file mode 100644
index c81b768..0000000
--- a/interface/app_pages/settings_sections/README.md
+++ /dev/null
@@ -1,185 +0,0 @@
-# settings_sections
-
-설정 페이지의 각 섹션을 렌더링하는 모듈들입니다.
-
-## 디렉토리 구조
-
-```
-settings_sections/
-├── __init__.py
-├── data_source_section.py
-├── db_section.py
-└── llm_section.py
-```
-
-## 파일 목록 및 설명
-
-### `__init__.py`
-
-네임스페이스 패키지 초기화 파일로, 패키지에서 export되는 모듈 목록을 정의합니다.
-
-**내보내는 모듈:**
-- `data_source_section`
-- `llm_section`
-- `db_section`
-
-### `data_source_section.py`
-
-데이터 소스 설정을 관리하는 UI 섹션을 제공합니다.
-
-**주요 기능:**
-- DataHub 또는 VectorDB 중 하나를 선택하여 데이터 소스 모드 설정
-- DataHub 서버 관리:
- - 등록된 DataHub 목록 조회 및 표시
- - 새로운 DataHub 추가 (이름, URL, FAISS 저장 경로, 메모)
- - 기존 DataHub 편집 및 삭제
- - GMS 서버 헬스 체크 기능
-- VectorDB 관리:
- - 등록된 VectorDB 목록 조회 및 표시 (FAISS, pgvector 지원)
- - 새로운 VectorDB 추가 (이름, 타입, 위치, 컬렉션 접두사, 메모)
- - 기존 VectorDB 편집 및 삭제
- - 설정 검증 기능
-
-**주요 함수:**
-- `render_data_source_section(config: Config | None = None) -> None`
- - 데이터 소스 설정 섹션을 Streamlit UI로 렌더링
- - `config` 파라미터가 없으면 내부에서 `load_config()`를 호출하여 로드
-
-**의존성:**
-- `interface.core.config`: Config 관리, 데이터 소스 레지스트리 조작
-- `infra.monitoring.check_server.CheckServer`: GMS 서버 헬스 체크
-
-**상태 표시:**
-- 현재 선택된 데이터 소스 모드에 따라 상태 배너 표시
-- DataHub: 헬스 체크 결과에 따른 성공/경고/정보 메시지
-- VectorDB: 설정 완전성에 따른 성공/경고 메시지
-
-### `db_section.py`
-
-데이터베이스 연결 설정을 관리하는 UI 섹션을 제공합니다.
-
-**주요 기능:**
-- 다양한 DB 타입 지원:
- - PostgreSQL, MySQL, MariaDB, Oracle, ClickHouse
- - DuckDB, SQLite
- - Databricks, Snowflake, Trino
-- DB 프로파일 관리:
- - 등록된 DB 프로파일 목록 조회 및 표시
- - 새로운 DB 프로파일 추가
- - 기존 DB 프로파일 편집 및 삭제
-- DB 타입별 필드 동적 처리:
- - 기본 필드: Host, Port, User, Database (또는 Path for DuckDB/SQLite)
- - 추가 필드: Oracle(Service Name), Databricks(HTTP Path, Catalog, Schema), Snowflake(Account, Warehouse, Schema), Trino(HTTP Scheme, Catalog, Schema)
- - 비밀 필드: Password 또는 Access Token (타입별 상이)
-- 환경 변수 기반 자동 채우기 지원
-- 연결 테스트 기능 (SELECT 1 쿼리 실행)
-- 설정 검증 및 세션 적용 기능
-
-**주요 함수:**
-- `render_db_section() -> None`
- - DB 연결 설정 섹션을 Streamlit UI로 렌더링
-
-**의존성:**
-- `interface.core.config`: DB 연결 레지스트리 조작
-- `utils.databases.DatabaseFactory`: DB 커넥터 생성 및 연결 테스트
-- `utils.databases.factory.load_config_from_env`: 환경 변수에서 설정 로드
-
-**헬퍼 함수:**
-- `_non_secret_fields(db_type: str) -> list[tuple[str, str]]`: DB 타입별 기본 필드 정의
-- `_extra_non_secret_fields(db_type: str) -> list[tuple[str, str]]`: DB 타입별 추가 필드 정의
-- `_secret_fields(db_type: str) -> list[tuple[str, str]]`: DB 타입별 비밀 필드 정의
-- `_prefill_from_env(db_type: str, key: str) -> str`: 환경 변수에서 기본값 로드
-
-### `llm_section.py`
-
-LLM 및 Embedding 설정을 관리하는 UI 섹션을 제공합니다.
-
-**주요 기능:**
-- LLM 공급자 지원:
- - OpenAI, Azure OpenAI, AWS Bedrock, Gemini, Ollama, Hugging Face
-- Embedding 공급자 지원 (동일한 공급자 목록)
-- 공급자별 필드 동적 처리:
- - OpenAI: Model, API Key
- - Azure: Endpoint, Deployment(Model), API Version, API Key
- - Bedrock: Model, Access Key ID, Secret Access Key, Region
- - Gemini: Model, API Key (embedding만)
- - Ollama: Model, Base URL
- - Hugging Face: Endpoint URL, Repo ID, Model, API Token (또는 Embedding: Model, Repo ID, API Token)
-- 프로파일 저장 기능:
- - LLM 프로파일 저장 (비밀키 제외 옵션)
- - Embedding 프로파일 저장 (시크릿 포함)
-- 저장된 프로파일 목록 조회
-- 환경 변수 및 세션 상태 기반 자동 채우기
-
-**주요 함수:**
-- `render_llm_section(config: Config | None = None) -> None`
- - LLM 및 Embedding 설정 섹션을 Streamlit UI로 렌더링
- - 2개 컬럼으로 나뉘어 Chat LLM과 Embeddings를 각각 설정
- - `config` 파라미터가 없으면 내부에서 `load_config()`를 호출하거나 None 처리
-
-**의존성:**
-- `interface.core.config`: LLM/Embedding 설정 및 프로파일 관리
-
-**헬퍼 함수:**
-- `_llm_fields(provider: str) -> list[tuple[str, str, bool]]`: LLM 공급자별 필드 정의 (label, env_key, is_secret)
-- `_embedding_fields(provider: str) -> list[tuple[str, str, bool]]`: Embedding 공급자별 필드 정의
-
-## 사용 방법
-
-이 모듈들은 `interface.app_pages.settings.py`에서 import되어 사용됩니다.
-
-### Import 예시
-
-```python
-from interface.app_pages.settings_sections.data_source_section import (
- render_data_source_section,
-)
-from interface.app_pages.settings_sections.llm_section import render_llm_section
-from interface.app_pages.settings_sections.db_section import render_db_section
-```
-
-### 사용 예시
-
-`settings.py`에서의 사용:
-
-```python
-from interface.core.config import load_config
-
-config = load_config()
-
-tabs = st.tabs(["데이터 소스", "LLM", "DB"])
-
-with tabs[0]:
- render_data_source_section(config)
-
-with tabs[1]:
- render_llm_section(config)
-
-with tabs[2]:
- render_db_section()
-```
-
-### 함수 시그니처
-
-#### `render_data_source_section(config: Config | None = None) -> None`
-- **매개변수:**
- - `config` (Config | None): 설정 객체. None이면 내부에서 `load_config()` 호출
-- **반환값:** None (Streamlit UI 직접 렌더링)
-
-#### `render_db_section() -> None`
-- **매개변수:** 없음
-- **반환값:** None (Streamlit UI 직접 렌더링)
-
-#### `render_llm_section(config: Config | None = None) -> None`
-- **매개변수:**
- - `config` (Config | None): 설정 객체. None이면 내부에서 `load_config()` 호출하거나 None 처리
-- **반환값:** None (Streamlit UI 직접 렌더링)
-
-## 공통 특징
-
-- 모든 섹션은 Streamlit을 사용하여 UI를 렌더링합니다.
-- 설정 변경 시 `st.rerun()`을 호출하여 UI를 새로고침합니다.
-- 에러 발생 시 `st.error()`를 사용하여 사용자에게 오류 메시지를 표시합니다.
-- 성공적인 작업 완료 시 `st.success()`를 사용하여 확인 메시지를 표시합니다.
-- 민감한 정보(비밀번호, API 키 등)는 `type="password"`를 사용하여 마스킹 처리합니다.
-
diff --git a/interface/app_pages/settings_sections/__init__.py b/interface/app_pages/settings_sections/__init__.py
deleted file mode 100644
index 53cae96..0000000
--- a/interface/app_pages/settings_sections/__init__.py
+++ /dev/null
@@ -1,7 +0,0 @@
-# Namespace package for settings page sections
-
-__all__ = [
- "data_source_section",
- "llm_section",
- "db_section",
-]
diff --git a/interface/app_pages/settings_sections/data_source_section.py b/interface/app_pages/settings_sections/data_source_section.py
deleted file mode 100644
index 2f63881..0000000
--- a/interface/app_pages/settings_sections/data_source_section.py
+++ /dev/null
@@ -1,321 +0,0 @@
-import streamlit as st
-from interface.core.config import (
- Config,
- load_config,
- update_vectordb_settings,
- update_data_source_mode,
- get_data_sources_registry,
- add_datahub_source,
- update_datahub_source,
- delete_datahub_source,
- add_vectordb_source,
- update_vectordb_source,
- delete_vectordb_source,
-)
-from infra.monitoring.check_server import CheckServer
-
-
-def _render_status_banner(config: Config) -> None:
- mode = config.data_source_mode
- ready_msgs = []
-
- if mode == "datahub":
- last_health = st.session_state.get("datahub_last_health")
- if last_health is True:
- st.success(f"데이터 소스 준비됨: DataHub ({config.datahub_server})")
- elif last_health is False:
- st.warning(
- "DataHub 헬스 체크 실패. URL을 확인하거나 VectorDB로 전환하세요."
- )
- else:
- st.info("DataHub 상태 미검증 – 헬스 체크 버튼으로 확인하세요.")
- elif mode == "vectordb":
- if config.vectordb_type and (
- (config.vectordb_type == "faiss" and config.vectordb_location)
- or (config.vectordb_type == "pgvector" and config.vectordb_location)
- ):
- st.success(
- f"데이터 소스 준비됨: VectorDB ({config.vectordb_type}, {config.vectordb_location or '기본값'})"
- )
- else:
- st.warning("VectorDB 설정이 불완전합니다. 타입/위치를 확인하세요.")
- else:
- st.info(
- "데이터 소스를 선택해주세요: DataHub 또는 VectorDB 중 하나는 필수입니다."
- )
-
-
-def render_data_source_section(config: Config | None = None) -> None:
- st.subheader("데이터 소스 (필수)")
-
- if config is None:
- config = load_config()
-
- _render_status_banner(config)
-
- # 선택 스위치
- col = st.columns([1, 3])[0]
- with col:
- mode = st.radio(
- "데이터 소스 선택",
- options=["DataHub", "VectorDB"],
- horizontal=True,
- index=(
- 0 if (config.data_source_mode or "datahub").lower() == "datahub" else 1
- ),
- )
- selected = mode.lower()
- update_data_source_mode(config, selected)
-
- st.divider()
-
- registry = get_data_sources_registry()
-
- if selected == "datahub":
- with st.container(border=True):
- st.write("등록된 DataHub")
- for source in list(registry.datahub):
- cols = st.columns([2, 4, 2, 1, 1])
- with cols[0]:
- st.text(source.name)
- with cols[1]:
- st.text(source.url)
- with cols[2]:
- note_val = source.note or ""
- st.caption(note_val)
- with cols[3]:
- if st.button("편집", key=f"edit_dh_{source.name}"):
- st.session_state["edit_dh_name"] = source.name
- with cols[4]:
- if st.button("삭제", type="secondary", key=f"del_dh_{source.name}"):
- delete_datahub_source(name=source.name)
- st.rerun()
-
- # 편집 폼
- edit_dh = st.session_state.get("edit_dh_name")
- if edit_dh:
- st.divider()
- st.write(f"DataHub 편집: {edit_dh}")
- existing = next(
- (s for s in registry.datahub if s.name == edit_dh), None
- )
- if existing:
- new_url = st.text_input(
- "URL", value=existing.url, key="dh_edit_url"
- )
- new_faiss = st.text_input(
- "FAISS 저장 경로(선택)",
- value=existing.faiss_path or "",
- key="dh_edit_faiss",
- )
- new_note = st.text_input(
- "메모", value=existing.note or "", key="dh_edit_note"
- )
- cols = st.columns([1, 1, 2])
- with cols[0]:
- if st.button("헬스 체크", key="dh_edit_health"):
- ok = CheckServer.is_gms_server_healthy(url=new_url)
- st.session_state["datahub_last_health"] = bool(ok)
- if ok:
- st.success("GMS 서버가 정상입니다.")
- else:
- st.error(
- "GMS 서버 헬스 체크 실패. URL과 네트워크를 확인하세요."
- )
- with cols[1]:
- if st.button("저장", key="dh_edit_save"):
- try:
- update_datahub_source(
- name=edit_dh,
- url=new_url,
- faiss_path=(new_faiss or None),
- note=(new_note or None),
- )
- st.success("저장되었습니다.")
- st.session_state.pop("edit_dh_name", None)
- st.rerun()
- except Exception as e:
- st.error(f"저장 실패: {e}")
- with cols[2]:
- if st.button("취소", key="dh_edit_cancel"):
- st.session_state.pop("edit_dh_name", None)
- st.rerun()
-
- st.divider()
- st.write("DataHub 추가")
- dh_name = st.text_input("이름", key="dh_name")
- dh_url = st.text_input(
- "URL", key="dh_url", placeholder="http://localhost:8080"
- )
- dh_faiss = st.text_input(
- "FAISS 저장 경로(선택)",
- key="dh_faiss",
- placeholder="예: ./dev/table_info_db",
- )
- dh_note = st.text_input("메모", key="dh_note", placeholder="선택")
-
- cols = st.columns([1, 1, 2])
- with cols[0]:
- if st.button("헬스 체크", key="dh_health_new"):
- ok = CheckServer.is_gms_server_healthy(url=dh_url)
- st.session_state["datahub_last_health"] = bool(ok)
- if ok:
- st.success("GMS 서버가 정상입니다.")
- else:
- st.error(
- "GMS 서버 헬스 체크 실패. URL과 네트워크를 확인하세요."
- )
- with cols[1]:
- if st.button("추가", key="dh_add"):
- try:
- if not dh_name or not dh_url:
- st.warning("이름과 URL을 입력하세요.")
- else:
- add_datahub_source(
- name=dh_name,
- url=dh_url,
- faiss_path=(dh_faiss or None),
- note=dh_note or None,
- )
- st.success("추가되었습니다.")
- st.rerun()
- except Exception as e:
- st.error(f"추가 실패: {e}")
-
- else: # VectorDB
- with st.container(border=True):
- st.write("등록된 VectorDB")
- for source in list(registry.vectordb):
- cols = st.columns([2, 2, 4, 2, 1, 1])
- with cols[0]:
- st.text(source.name)
- with cols[1]:
- st.text(source.type)
- with cols[2]:
- st.text(source.location)
- with cols[3]:
- st.caption(source.collection_prefix or "-")
- with cols[4]:
- if st.button("편집", key=f"edit_vdb_{source.name}"):
- st.session_state["edit_vdb_name"] = source.name
- with cols[5]:
- if st.button(
- "삭제", type="secondary", key=f"del_vdb_{source.name}"
- ):
- delete_vectordb_source(name=source.name)
- st.rerun()
-
- # 편집 폼
- edit_vdb = st.session_state.get("edit_vdb_name")
- if edit_vdb:
- st.divider()
- st.write(f"VectorDB 편집: {edit_vdb}")
- existing = next(
- (s for s in registry.vectordb if s.name == edit_vdb), None
- )
- if existing:
- new_type = st.selectbox(
- "타입",
- options=["faiss", "pgvector"],
- index=(0 if existing.type == "faiss" else 1),
- key="vdb_edit_type",
- )
- new_loc_placeholder = (
- "FAISS 디렉토리 경로 (예: ./dev/table_info_db)"
- if new_type == "faiss"
- else "pgvector 연결 문자열 (postgresql://user:pass@host:port/db)"
- )
- new_location = st.text_input(
- "위치",
- value=existing.location,
- key="vdb_edit_location",
- placeholder=new_loc_placeholder,
- )
- new_prefix = st.text_input(
- "컬렉션 접두사(선택)",
- value=existing.collection_prefix or "",
- key="vdb_edit_prefix",
- )
- new_note = st.text_input(
- "메모(선택)", value=existing.note or "", key="vdb_edit_note"
- )
- cols = st.columns([1, 1, 2])
- with cols[0]:
- if st.button("검증", key="vdb_edit_validate"):
- try:
- update_vectordb_settings(
- config,
- vectordb_type=new_type,
- vectordb_location=new_location,
- )
- st.success("설정이 유효합니다.")
- except Exception as e:
- st.error(f"검증 실패: {e}")
- with cols[1]:
- if st.button("저장", key="vdb_edit_save"):
- try:
- update_vectordb_source(
- name=edit_vdb,
- vtype=new_type,
- location=new_location,
- collection_prefix=(new_prefix or None),
- note=(new_note or None),
- )
- st.success("저장되었습니다.")
- st.session_state.pop("edit_vdb_name", None)
- st.rerun()
- except Exception as e:
- st.error(f"저장 실패: {e}")
- with cols[2]:
- if st.button("취소", key="vdb_edit_cancel"):
- st.session_state.pop("edit_vdb_name", None)
- st.rerun()
-
- st.divider()
- st.write("VectorDB 추가")
- vdb_name = st.text_input("이름", key="vdb_name")
- vdb_type = st.selectbox(
- "타입", options=["faiss", "pgvector"], key="vdb_type"
- )
- vdb_loc_placeholder = (
- "FAISS 디렉토리 경로 (예: ./dev/table_info_db)"
- if vdb_type == "faiss"
- else "pgvector 연결 문자열 (postgresql://user:pass@host:port/db)"
- )
- vdb_location = st.text_input(
- "위치", key="vdb_location", placeholder=vdb_loc_placeholder
- )
- vdb_prefix = st.text_input(
- "컬렉션 접두사(선택)", key="vdb_prefix", placeholder="예: app1_"
- )
- vdb_note = st.text_input("메모(선택)", key="vdb_note")
-
- cols = st.columns([1, 1, 2])
- with cols[0]:
- if st.button("검증", key="vdb_validate_new"):
- try:
- update_vectordb_settings(
- config,
- vectordb_type=vdb_type,
- vectordb_location=vdb_location,
- )
- st.success("설정이 유효합니다.")
- except Exception as e:
- st.error(f"검증 실패: {e}")
- with cols[1]:
- if st.button("추가", key="vdb_add"):
- try:
- if not vdb_name or not vdb_type or not vdb_location:
- st.warning("이름/타입/위치를 입력하세요.")
- else:
- add_vectordb_source(
- name=vdb_name,
- vtype=vdb_type,
- location=vdb_location,
- collection_prefix=(vdb_prefix or None),
- note=(vdb_note or None),
- )
- st.success("추가되었습니다.")
- st.rerun()
- except Exception as e:
- st.error(f"추가 실패: {e}")
diff --git a/interface/app_pages/settings_sections/db_section.py b/interface/app_pages/settings_sections/db_section.py
deleted file mode 100644
index 2ebeec5..0000000
--- a/interface/app_pages/settings_sections/db_section.py
+++ /dev/null
@@ -1,300 +0,0 @@
-import os
-
-import streamlit as st
-
-from interface.core.config import (
- add_db_connection,
- delete_db_connection,
- get_db_connections_registry,
- update_db_connection,
- update_db_settings,
-)
-from utils.databases import DatabaseFactory
-from utils.databases.factory import load_config_from_env
-
-DB_TYPES = [
- "postgresql",
- "mysql",
- "mariadb",
- "oracle",
- "clickhouse",
- "duckdb",
- "sqlite",
- "databricks",
- "snowflake",
- "trino",
-]
-
-
-def _non_secret_fields(db_type: str) -> list[tuple[str, str]]:
- t = (db_type or "").lower()
- # label, key (values dict)
- if t in {"duckdb", "sqlite"}:
- return [("Path", "path")]
- base = [
- ("Host", "host"),
- ("Port", "port"),
- ("User", "user"),
- ("Database", "database"),
- ]
- return base
-
-
-def _extra_non_secret_fields(db_type: str) -> list[tuple[str, str]]:
- t = (db_type or "").lower()
- if t == "oracle":
- return [("Service Name", "service_name")]
- if t == "databricks":
- return [
- ("HTTP Path", "http_path"),
- ("Catalog(옵션)", "catalog"),
- ("Schema(옵션)", "schema"),
- ]
- if t == "snowflake":
- return [
- ("Account", "account"),
- ("Warehouse(옵션)", "warehouse"),
- ("Schema(옵션)", "schema"),
- ]
- if t == "trino":
- return [
- ("HTTP Scheme(http/https)", "http_scheme"),
- ("Catalog", "catalog"),
- ("Schema", "schema"),
- ]
- return []
-
-
-def _secret_fields(db_type: str) -> list[tuple[str, str]]:
- t = (db_type or "").lower()
- if t in {"postgresql", "mysql", "mariadb", "oracle", "clickhouse", "trino"}:
- return [("Password", "password")]
- if t == "databricks":
- return [("Access Token", "access_token")]
- if t == "snowflake":
- return [("Password", "password")]
- # duckdb/sqlite는 비밀번호 필요 없음
- return []
-
-
-def _prefill_from_env(db_type: str, key: str) -> str:
- # Normalize to PREFIX_KEY
- prefix = (db_type or "").upper()
- if key == "path":
- # duckdb/sqlite: PATH 로 통일 저장
- return os.getenv(f"{prefix}_PATH", "")
- return os.getenv(f"{prefix}_{key.upper()}", "")
-
-
-def render_db_section() -> None:
- st.subheader("DB 연결")
-
- registry = get_db_connections_registry()
-
- # 목록 렌더링
- with st.container(border=True):
- st.write("등록된 DB 프로파일")
- for profile in list(registry.connections):
- cols = st.columns([2, 2, 3, 2, 1, 1])
- with cols[0]:
- st.text(profile.name)
- with cols[1]:
- st.text(profile.type)
- with cols[2]:
- host_or_path = profile.host or (profile.extra or {}).get("path") or "-"
- st.caption(host_or_path)
- with cols[3]:
- st.caption(profile.note or "")
- with cols[4]:
- if st.button("편집", key=f"edit_db_{profile.name}"):
- st.session_state["edit_db_name"] = profile.name
- with cols[5]:
- if st.button("삭제", type="secondary", key=f"del_db_{profile.name}"):
- delete_db_connection(name=profile.name)
- st.rerun()
-
- # 편집 폼
- edit_name = st.session_state.get("edit_db_name")
- if edit_name:
- st.divider()
- st.write(f"DB 프로파일 편집: {edit_name}")
- existing = next((c for c in registry.connections if c.name == edit_name), None)
- if existing:
- new_type = st.selectbox(
- "타입",
- options=DB_TYPES,
- index=max(
- 0, DB_TYPES.index(existing.type) if existing.type in DB_TYPES else 0
- ),
- key="db_edit_type",
- )
-
- values: dict[str, object] = {}
- # 기본 필드
- for label, k in _non_secret_fields(new_type):
- default_val = (
- existing.extra.get("path")
- if k == "path" and existing.extra
- else getattr(existing, k, "")
- )
- v = st.text_input(
- label, value=str(default_val or ""), key=f"db_edit_val_{k}"
- )
- if v != "":
- values[k] = v
-
- # 추가 필드(Non-secret)
- extra_vals: dict[str, str] = {}
- for label, k in _extra_non_secret_fields(new_type):
- default_val = (existing.extra or {}).get(k, "")
- v = st.text_input(
- label, value=str(default_val or ""), key=f"db_edit_extra_{k}"
- )
- if v != "":
- extra_vals[k] = v
- if extra_vals:
- values["extra"] = extra_vals
-
- # 시크릿 필드 (JSON 저장 허용)
- secrets: dict[str, str] = {}
- for label, k in _secret_fields(new_type):
- # 기존 저장값 우선: existing.password or existing.extra
- default_secret = ""
- if k == "password":
- default_secret = getattr(
- existing, "password", None
- ) or _prefill_from_env(new_type, k)
- else:
- default_secret = (existing.extra or {}).get(
- k, ""
- ) or _prefill_from_env(new_type, k)
- sv = st.text_input(
- label,
- value=str(default_secret or ""),
- type="password",
- key=f"db_edit_secret_{k}",
- )
- if sv != "":
- secrets[k] = sv
-
- cols = st.columns([1, 1, 2])
- with cols[0]:
- if st.button("적용(세션)", key="db_edit_apply"):
- try:
- update_db_settings(
- db_type=new_type, values=values, secrets=secrets
- )
- st.success("환경/세션에 적용되었습니다.")
- except Exception as e:
- st.error(f"적용 실패: {e}")
- with cols[1]:
- if st.button("저장", key="db_edit_save"):
- try:
- update_db_connection(
- name=edit_name,
- db_type=new_type,
- host=str(values.get("host") or "") or None,
- port=(
- int(values.get("port"))
- if str(values.get("port") or "").isdigit()
- else None
- ),
- user=str(values.get("user") or "") or None,
- password=(secrets.get("password") or None),
- database=str(values.get("database") or "") or None,
- extra=values.get("extra"),
- note=existing.note,
- )
- st.success("저장되었습니다.")
- st.session_state.pop("edit_db_name", None)
- st.rerun()
- except Exception as e:
- st.error(f"저장 실패: {e}")
- with cols[2]:
- if st.button("연결 테스트", key="db_edit_test"):
- try:
- # 먼저 적용하여 env에 반영
- update_db_settings(
- db_type=new_type, values=values, secrets=secrets
- )
-
- connector = DatabaseFactory.get_connector(db_type=new_type)
- # 간단한 SELECT 1 테스트 (DB마다 상이할 수 있음)
- test_sql = (
- "SELECT 1"
- if new_type not in {"oracle", "snowflake", "trino"}
- else {
- "oracle": "SELECT 1 FROM dual",
- "snowflake": "SELECT 1",
- "trino": "SELECT 1",
- }[new_type]
- )
- df = connector.run_sql(test_sql)
- st.success(f"연결 성공. 결과 행 수: {len(df)}")
- connector.close()
- except Exception as e:
- st.error(f"연결 테스트 실패: {e}")
-
- st.divider()
- # 추가 폼
- st.write("DB 프로파일 추가")
- name = st.text_input("이름", key="db_new_name")
- db_type = st.selectbox("타입", options=DB_TYPES, key="db_new_type")
-
- values_new: dict[str, object] = {}
- for label, k in _non_secret_fields(db_type):
- v = st.text_input(label, key=f"db_new_val_{k}")
- if v != "":
- values_new[k] = v
-
- extra_new: dict[str, str] = {}
- for label, k in _extra_non_secret_fields(db_type):
- v = st.text_input(label, key=f"db_new_extra_{k}")
- if v != "":
- extra_new[k] = v
- if extra_new:
- values_new["extra"] = extra_new
-
- secrets_new: dict[str, str] = {}
- for label, k in _secret_fields(db_type):
- sv = st.text_input(label, key=f"db_new_secret_{k}")
- if sv != "":
- secrets_new[k] = sv
-
- cols2 = st.columns([1, 1, 2])
- with cols2[0]:
- if st.button("검증", key="db_new_validate"):
- try:
- update_db_settings(
- db_type=db_type, values=values_new, secrets=secrets_new
- )
- # load back config for the type to ensure required fields presence
- _ = load_config_from_env(db_type.upper())
- st.success("형식 검증 완료.")
- except Exception as e:
- st.error(f"검증 실패: {e}")
- with cols2[1]:
- if st.button("추가", key="db_new_add"):
- try:
- if not name:
- st.warning("이름을 입력하세요.")
- else:
- add_db_connection(
- name=name,
- db_type=db_type,
- host=str(values_new.get("host") or "") or None,
- port=(
- int(values_new.get("port"))
- if str(values_new.get("port") or "").isdigit()
- else None
- ),
- user=str(values_new.get("user") or "") or None,
- password=(secrets_new.get("password") or None),
- database=str(values_new.get("database") or "") or None,
- extra=values_new.get("extra"),
- note=None,
- )
- st.success("추가되었습니다.")
- st.rerun()
- except Exception as e:
- st.error(f"추가 실패: {e}")
diff --git a/interface/app_pages/settings_sections/llm_section.py b/interface/app_pages/settings_sections/llm_section.py
deleted file mode 100644
index 2473012..0000000
--- a/interface/app_pages/settings_sections/llm_section.py
+++ /dev/null
@@ -1,273 +0,0 @@
-import os
-import streamlit as st
-
-from interface.core.config import (
- update_llm_settings,
- update_embedding_settings,
- Config,
- load_config,
- save_llm_profile,
- get_llm_registry,
- save_embedding_profile,
- get_embedding_registry,
-)
-
-LLM_PROVIDERS = [
- "openai",
- "azure",
- "bedrock",
- "gemini",
- "ollama",
- "huggingface",
-]
-
-
-def _llm_fields(provider: str) -> list[tuple[str, str, bool]]:
- """Return list of (label, env_key, is_secret) for LLM provider."""
- p = provider.lower()
- if p == "openai":
- return [
- ("Model", "OPEN_AI_LLM_MODEL", False),
- ("API Key", "OPEN_AI_KEY", True),
- ]
- if p == "azure":
- return [
- ("Endpoint", "AZURE_OPENAI_LLM_ENDPOINT", False),
- ("Deployment(Model)", "AZURE_OPENAI_LLM_MODEL", False),
- ("API Version", "AZURE_OPENAI_LLM_API_VERSION", False),
- ("API Key", "AZURE_OPENAI_LLM_KEY", True),
- ]
- if p == "bedrock":
- return [
- ("Model", "AWS_BEDROCK_LLM_MODEL", False),
- ("Access Key ID", "AWS_BEDROCK_LLM_ACCESS_KEY_ID", True),
- ("Secret Access Key", "AWS_BEDROCK_LLM_SECRET_ACCESS_KEY", True),
- ("Region", "AWS_BEDROCK_LLM_REGION", False),
- ]
- if p == "gemini":
- return [
- ("Model", "GEMINI_LLM_MODEL", False),
- # ChatGoogleGenerativeAI uses GOOGLE_API_KEY at process level, but factory currently reads only model
- ]
- if p == "ollama":
- return [
- ("Model", "OLLAMA_LLM_MODEL", False),
- ("Base URL", "OLLAMA_LLM_BASE_URL", False),
- ]
- if p == "huggingface":
- return [
- ("Endpoint URL", "HUGGING_FACE_LLM_ENDPOINT", False),
- ("Repo ID", "HUGGING_FACE_LLM_REPO_ID", False),
- ("Model", "HUGGING_FACE_LLM_MODEL", False),
- ("API Token", "HUGGING_FACE_LLM_API_TOKEN", True),
- ]
- return []
-
-
-def _embedding_fields(provider: str) -> list[tuple[str, str, bool]]:
- p = provider.lower()
- if p == "openai":
- return [
- ("Model", "OPEN_AI_EMBEDDING_MODEL", False),
- ("API Key", "OPEN_AI_KEY", True),
- ]
- if p == "azure":
- return [
- ("Endpoint", "AZURE_OPENAI_EMBEDDING_ENDPOINT", False),
- ("Deployment(Model)", "AZURE_OPENAI_EMBEDDING_MODEL", False),
- ("API Version", "AZURE_OPENAI_EMBEDDING_API_VERSION", False),
- ("API Key", "AZURE_OPENAI_EMBEDDING_KEY", True),
- ]
- if p == "bedrock":
- return [
- ("Model", "AWS_BEDROCK_EMBEDDING_MODEL", False),
- ("Access Key ID", "AWS_BEDROCK_EMBEDDING_ACCESS_KEY_ID", True),
- ("Secret Access Key", "AWS_BEDROCK_EMBEDDING_SECRET_ACCESS_KEY", True),
- ("Region", "AWS_BEDROCK_EMBEDDING_REGION", False),
- ]
- if p == "gemini":
- return [
- ("Model", "GEMINI_EMBEDDING_MODEL", False),
- ("API Key", "GEMINI_EMBEDDING_KEY", True),
- ]
- if p == "ollama":
- return [
- ("Model", "OLLAMA_EMBEDDING_MODEL", False),
- ("Base URL", "OLLAMA_EMBEDDING_BASE_URL", False),
- ]
- if p == "huggingface":
- return [
- ("Model", "HUGGING_FACE_EMBEDDING_MODEL", False),
- ("Repo ID", "HUGGING_FACE_EMBEDDING_REPO_ID", False),
- ("API Token", "HUGGING_FACE_EMBEDDING_API_TOKEN", True),
- ]
- return []
-
-
-def render_llm_section(config: Config | None = None) -> None:
- st.subheader("LLM 설정")
-
- if config is None:
- try:
- config = load_config()
- except Exception:
- config = None # UI 일관성을 위한 옵셔널 처리
-
- llm_col, emb_col = st.columns(2)
-
- with llm_col:
- st.markdown("**Chat LLM**")
- default_llm_provider = (
- (
- st.session_state.get("LLM_PROVIDER")
- or os.getenv("LLM_PROVIDER")
- or "openai"
- )
- ).lower()
- try:
- default_llm_index = LLM_PROVIDERS.index(default_llm_provider)
- except ValueError:
- default_llm_index = 0
- provider = st.selectbox(
- "공급자",
- options=LLM_PROVIDERS,
- index=default_llm_index,
- key="llm_provider",
- )
- fields = _llm_fields(provider)
- values: dict[str, str | None] = {}
- non_secret_values: dict[str, str | None] = {}
- for label, env_key, is_secret in fields:
- prefill = st.session_state.get(env_key) or os.getenv(env_key) or ""
- if is_secret:
- values[env_key] = st.text_input(
- label, value=prefill, type="password", key=f"llm_{env_key}"
- )
- else:
- v = st.text_input(label, value=prefill, key=f"llm_{env_key}")
- values[env_key] = v
- non_secret_values[env_key] = v
-
- # 메시지 영역
- llm_msg = st.empty()
-
- st.markdown("**프로파일 저장 (비밀키 제외)**")
- with st.form("llm_profile_save_form"):
- prof_cols = st.columns([2, 2])
- with prof_cols[0]:
- profile_name = st.text_input("프로파일 이름", key="llm_profile_name")
- with prof_cols[1]:
- profile_note = st.text_input("메모(선택)", key="llm_profile_note")
-
- submitted = st.form_submit_button("프로파일 저장")
- if submitted:
- try:
- if not profile_name:
- llm_msg.warning("프로파일 이름을 입력하세요.")
- else:
- # 1) 환경/세션에 즉시 적용 (저장된 모든 값 사용: 사용자 요청)
- update_llm_settings(provider=provider, values=values)
- # 2) 디스크에 프로파일 저장 (비밀키 포함)
- save_llm_profile(
- name=profile_name,
- provider=provider,
- values=values,
- note=(profile_note or None),
- )
- llm_msg.success("프로파일이 저장 및 적용되었습니다.")
- except Exception as e:
- llm_msg.error(f"프로파일 저장 실패: {e}")
-
- # 저장된 프로파일 미리보기
- reg = get_llm_registry()
- if reg.profiles:
- with st.expander("저장된 LLM 프로파일", expanded=False):
- for p in reg.profiles:
- if p.fields:
- pairs = [
- f"{k}={p.fields.get(k, '')}"
- for k in sorted(p.fields.keys())
- ]
- fields_text = ", ".join(pairs)
- else:
- fields_text = "-"
- note_text = f" | note: {p.note}" if getattr(p, "note", None) else ""
- st.caption(f"- {p.name} ({p.provider}) | {fields_text}{note_text}")
-
- with emb_col:
- st.markdown("**Embeddings**")
- default_emb_provider = (
- (
- st.session_state.get("EMBEDDING_PROVIDER")
- or os.getenv("EMBEDDING_PROVIDER")
- or "openai"
- )
- ).lower()
- try:
- default_emb_index = LLM_PROVIDERS.index(default_emb_provider)
- except ValueError:
- default_emb_index = 0
- e_provider = st.selectbox(
- "공급자",
- options=LLM_PROVIDERS,
- index=default_emb_index,
- key="embedding_provider",
- )
- e_fields = _embedding_fields(e_provider)
- e_values: dict[str, str | None] = {}
- for label, env_key, is_secret in e_fields:
- prefill = st.session_state.get(env_key) or os.getenv(env_key) or ""
- if is_secret:
- e_values[env_key] = st.text_input(
- label, value=prefill, type="password", key=f"emb_{env_key}"
- )
- else:
- e_values[env_key] = st.text_input(
- label, value=prefill, key=f"emb_{env_key}"
- )
-
- # 메시지 영역: 버튼 컬럼 밖(섹션 폭)
- emb_msg = st.empty()
-
- st.markdown("**Embeddings 프로파일 저장 (시크릿 포함)**")
- with st.form("embedding_profile_save_form"):
- e_prof_cols = st.columns([2, 2])
- with e_prof_cols[0]:
- e_profile_name = st.text_input(
- "프로파일 이름", key="embedding_profile_name"
- )
- with e_prof_cols[1]:
- e_profile_note = st.text_input(
- "메모(선택)", key="embedding_profile_note"
- )
-
- e_submitted = st.form_submit_button("프로파일 저장")
- if e_submitted:
- try:
- if not e_profile_name:
- emb_msg.warning("프로파일 이름을 입력하세요.")
- else:
- update_embedding_settings(provider=e_provider, values=e_values)
- save_embedding_profile(
- name=e_profile_name,
- provider=e_provider,
- values=e_values,
- note=(e_profile_note or None),
- )
- emb_msg.success("Embeddings 프로파일이 저장 및 적용되었습니다.")
- except Exception as e:
- emb_msg.error(f"프로파일 저장 실패: {e}")
- e_reg = get_embedding_registry()
- if e_reg.profiles:
- with st.expander("저장된 Embeddings 프로파일", expanded=False):
- for p in e_reg.profiles:
- if p.fields:
- pairs = [
- f"{k}={p.fields.get(k, '')}"
- for k in sorted(p.fields.keys())
- ]
- fields_text = ", ".join(pairs)
- else:
- fields_text = "-"
- note_text = f" | note: {p.note}" if getattr(p, "note", None) else ""
- st.caption(f"- {p.name} ({p.provider}) | {fields_text}{note_text}")
diff --git a/interface/app_pages/sidebar_components/README.md b/interface/app_pages/sidebar_components/README.md
deleted file mode 100644
index dab4f4e..0000000
--- a/interface/app_pages/sidebar_components/README.md
+++ /dev/null
@@ -1,297 +0,0 @@
-# sidebar_components
-
-사이드바 UI 컴포넌트 모듈. Streamlit 애플리케이션의 사이드바에서 사용되는 설정 선택 및 관리 컴포넌트들을 제공합니다.
-
-## 디렉토리 구조
-
-```
-sidebar_components/
-├── __init__.py
-├── chatbot_session_controller.py
-├── data_source_selector.py
-├── db_selector.py
-├── embedding_selector.py
-└── llm_selector.py
-```
-
-## 파일 설명
-
-### `__init__.py`
-
-모든 사이드바 컴포넌트 함수들을 모듈에서 export합니다.
-
-**Export되는 함수:**
-- `render_sidebar_data_source_selector`: 데이터 소스 선택기 렌더링
-- `render_sidebar_llm_selector`: LLM 선택기 렌더링
-- `render_sidebar_embedding_selector`: Embeddings 선택기 렌더링
-- `render_sidebar_db_selector`: DB 연결 선택기 렌더링
-- `render_sidebar_chatbot_session_controller`: ChatBot 세션 컨트롤러 렌더링
-
-**사용 예시:**
-```python
-from interface.app_pages.sidebar_components import (
- render_sidebar_data_source_selector,
- render_sidebar_llm_selector,
- render_sidebar_embedding_selector,
- render_sidebar_db_selector,
- render_sidebar_chatbot_session_controller,
-)
-```
-
----
-
-### `chatbot_session_controller.py`
-
-ChatBot 세션 관리 및 대화 기록 표시를 위한 사이드바 컴포넌트입니다.
-
-**주요 기능:**
-- 세션 ID 자동 생성 및 관리 (`chatbot_thread_id`)
-- 새 세션 시작 버튼
-- 대화 기록 표시 (JSON 형식)
-- 최근 메시지 미리보기 (최근 3개)
-
-**함수:**
-```python
-def render_sidebar_chatbot_session_controller() -> str
-```
-
-**반환값:**
-- `str`: 현재 thread_id
-
-**사용 예시:**
-```python
-from interface.app_pages.sidebar_components import render_sidebar_chatbot_session_controller
-
-thread_id = render_sidebar_chatbot_session_controller()
-```
-
-**의존성:**
-- `streamlit`: UI 렌더링
-- `uuid`: 세션 ID 생성
-- `st.session_state`: 세션 상태 관리
- - `chatbot_thread_id`: 현재 세션 ID
- - `chatbot_messages`: 대화 기록 리스트
-
-**사용처:**
-- `/home/dwlee/Lang2SQL/interface/app_pages/chatbot.py` (line 112)
-
----
-
-### `data_source_selector.py`
-
-데이터 소스 선택 컴포넌트입니다. DataHub 또는 VectorDB 중 하나를 선택하고 설정을 적용할 수 있습니다.
-
-**주요 기능:**
-- DataHub/VectorDB 모드 선택 (라디오 버튼)
-- DataHub 인스턴스 선택 및 적용
-- VectorDB 인스턴스 선택 및 적용
-- FAISS 경로 자동 적용 (DataHub 선택 시)
-
-**함수:**
-```python
-def render_sidebar_data_source_selector(config=None) -> None
-```
-
-**매개변수:**
-- `config` (optional): 설정 객체. None인 경우 내부에서 `load_config()`로 로드합니다.
-
-**사용 예시:**
-```python
-from interface.app_pages.sidebar_components import render_sidebar_data_source_selector
-from interface.core.config import load_config
-
-config = load_config()
-render_sidebar_data_source_selector(config)
-```
-
-**의존성:**
-- `streamlit`: UI 렌더링
-- `interface.core.config`:
- - `load_config()`: 설정 로드
- - `get_data_sources_registry()`: 데이터 소스 레지스트리 조회
- - `update_datahub_server()`: DataHub 서버 설정 업데이트
- - `update_vectordb_settings()`: VectorDB 설정 업데이트
- - `update_data_source_mode()`: 데이터 소스 모드 업데이트
-
-**사용처:**
-- `/home/dwlee/Lang2SQL/interface/app_pages/chatbot.py` (line 99)
-- `/home/dwlee/Lang2SQL/interface/app_pages/lang2sql.py` (line 50)
-
----
-
-### `db_selector.py`
-
-데이터베이스 연결 프로파일 선택 컴포넌트입니다.
-
-**주요 기능:**
-- 등록된 DB 프로파일 목록 표시
-- 프로파일 선택 및 적용
-- 세션 또는 환경 변수의 DB_TYPE과 일치하는 프로파일 자동 선택
-
-**함수:**
-```python
-def render_sidebar_db_selector() -> None
-```
-
-**사용 예시:**
-```python
-from interface.app_pages.sidebar_components import render_sidebar_db_selector
-
-render_sidebar_db_selector()
-```
-
-**의존성:**
-- `streamlit`: UI 렌더링
-- `os`: 환경 변수 조회
-- `interface.core.config`:
- - `get_db_connections_registry()`: DB 연결 레지스트리 조회
- - `update_db_settings()`: DB 설정 업데이트
-- `st.session_state.get("DB_TYPE")`: 세션의 DB 타입 확인
-- `os.getenv("DB_TYPE")`: 환경 변수에서 DB 타입 확인
-
-**사용처:**
-- `/home/dwlee/Lang2SQL/interface/app_pages/chatbot.py` (line 105)
-- `/home/dwlee/Lang2SQL/interface/app_pages/lang2sql.py` (line 56)
-
----
-
-### `embedding_selector.py`
-
-Embeddings 프로파일 선택 컴포넌트입니다.
-
-**주요 기능:**
-- 등록된 Embeddings 프로파일 목록 표시
-- 프로파일 선택 및 적용
-- 프로파일이 없는 경우 공급자 직접 선택 (fallback 모드)
-- 지원 공급자: openai, azure, bedrock, gemini, ollama, huggingface
-
-**함수:**
-```python
-def render_sidebar_embedding_selector() -> None
-```
-
-**사용 예시:**
-```python
-from interface.app_pages.sidebar_components import render_sidebar_embedding_selector
-
-render_sidebar_embedding_selector()
-```
-
-**의존성:**
-- `streamlit`: UI 렌더링
-- `os`: 환경 변수 조회
-- `interface.core.config`:
- - `get_embedding_registry()`: Embeddings 레지스트리 조회
- - `update_embedding_settings()`: Embeddings 설정 업데이트
-- `st.session_state.get("EMBEDDING_PROVIDER")`: 세션의 Embeddings 공급자 확인
-- `os.getenv("EMBEDDING_PROVIDER")`: 환경 변수에서 Embeddings 공급자 확인
-
-**사용처:**
-- `/home/dwlee/Lang2SQL/interface/app_pages/chatbot.py` (line 103)
-- `/home/dwlee/Lang2SQL/interface/app_pages/lang2sql.py` (line 54)
-
----
-
-### `llm_selector.py`
-
-LLM 프로파일 선택 컴포넌트입니다.
-
-**주요 기능:**
-- 등록된 LLM 프로파일 목록 표시
-- 프로파일 선택 및 적용
-- 프로파일이 없는 경우 공급자 직접 선택 (fallback 모드)
-- 지원 공급자: openai, azure, bedrock, gemini, ollama, huggingface
-
-**함수:**
-```python
-def render_sidebar_llm_selector() -> None
-```
-
-**사용 예시:**
-```python
-from interface.app_pages.sidebar_components import render_sidebar_llm_selector
-
-render_sidebar_llm_selector()
-```
-
-**의존성:**
-- `streamlit`: UI 렌더링
-- `os`: 환경 변수 조회
-- `interface.core.config`:
- - `get_llm_registry()`: LLM 레지스트리 조회
- - `update_llm_settings()`: LLM 설정 업데이트
-- `st.session_state.get("LLM_PROVIDER")`: 세션의 LLM 공급자 확인
-- `os.getenv("LLM_PROVIDER")`: 환경 변수에서 LLM 공급자 확인
-
-**사용처:**
-- `/home/dwlee/Lang2SQL/interface/app_pages/chatbot.py` (line 101)
-- `/home/dwlee/Lang2SQL/interface/app_pages/lang2sql.py` (line 52)
-
----
-
-## 전체 사용 예시
-
-### chatbot.py에서의 사용
-
-```python
-from interface.app_pages.sidebar_components import (
- render_sidebar_data_source_selector,
- render_sidebar_llm_selector,
- render_sidebar_embedding_selector,
- render_sidebar_db_selector,
- render_sidebar_chatbot_session_controller,
-)
-from interface.core.config import load_config
-
-config = load_config()
-
-# 사이드바 UI 구성
-render_sidebar_data_source_selector(config)
-st.sidebar.divider()
-render_sidebar_llm_selector()
-st.sidebar.divider()
-render_sidebar_embedding_selector()
-st.sidebar.divider()
-render_sidebar_db_selector()
-st.sidebar.divider()
-
-# ChatBot 전용 설정
-with st.sidebar:
- st.markdown("### 🤖 ChatBot 설정")
- st.divider()
- thread_id = render_sidebar_chatbot_session_controller()
-```
-
-### lang2sql.py에서의 사용
-
-```python
-from interface.app_pages.sidebar_components import (
- render_sidebar_data_source_selector,
- render_sidebar_llm_selector,
- render_sidebar_embedding_selector,
- render_sidebar_db_selector,
-)
-from interface.core.config import load_config
-
-config = load_config()
-
-render_sidebar_data_source_selector(config)
-st.sidebar.divider()
-render_sidebar_llm_selector()
-st.sidebar.divider()
-render_sidebar_embedding_selector()
-st.sidebar.divider()
-render_sidebar_db_selector()
-st.sidebar.divider()
-```
-
-## 공통 패턴
-
-모든 컴포넌트는 다음과 같은 공통 패턴을 따릅니다:
-
-1. **레지스트리 기반**: 각 컴포넌트는 해당 설정의 레지스트리(`get_*_registry()`)에서 프로파일/인스턴스 목록을 가져옵니다.
-2. **Fallback 지원**: 프로파일이 없는 경우 기본 공급자 선택 옵션을 제공합니다.
-3. **세션/환경 변수 통합**: 현재 세션 상태 또는 환경 변수와 일치하는 항목을 자동으로 선택합니다.
-4. **설정 업데이트**: 선택한 항목을 적용하면 `update_*_settings()` 함수를 통해 설정을 업데이트합니다.
-5. **에러 처리**: 적용 실패 시 `st.sidebar.error()`로 에러 메시지를 표시합니다.
-
diff --git a/interface/app_pages/sidebar_components/__init__.py b/interface/app_pages/sidebar_components/__init__.py
deleted file mode 100644
index 47026dc..0000000
--- a/interface/app_pages/sidebar_components/__init__.py
+++ /dev/null
@@ -1,13 +0,0 @@
-from .data_source_selector import render_sidebar_data_source_selector
-from .llm_selector import render_sidebar_llm_selector
-from .embedding_selector import render_sidebar_embedding_selector
-from .db_selector import render_sidebar_db_selector
-from .chatbot_session_controller import render_sidebar_chatbot_session_controller
-
-__all__ = [
- "render_sidebar_data_source_selector",
- "render_sidebar_llm_selector",
- "render_sidebar_embedding_selector",
- "render_sidebar_db_selector",
- "render_sidebar_chatbot_session_controller",
-]
diff --git a/interface/app_pages/sidebar_components/chatbot_session_controller.py b/interface/app_pages/sidebar_components/chatbot_session_controller.py
deleted file mode 100644
index f4e774a..0000000
--- a/interface/app_pages/sidebar_components/chatbot_session_controller.py
+++ /dev/null
@@ -1,63 +0,0 @@
-"""ChatBot 세션 제어를 위한 사이드바 컴포넌트"""
-
-import streamlit as st
-import uuid
-
-
-def render_sidebar_chatbot_session_controller() -> str:
- """ChatBot 세션 관리 및 대화 기록 표시 (사이드바 전용)
-
- Returns:
- str: 현재 thread_id
- """
- # 세션 ID 자동 생성 (처음 방문 시에만)
- if "chatbot_thread_id" not in st.session_state:
- st.session_state.chatbot_thread_id = str(uuid.uuid4())[:8] # 8자리 짧은 ID
-
- thread_id = st.session_state.chatbot_thread_id
-
- # 세션 관리 섹션
- st.markdown("### 📋 세션 관리")
-
- # 세션 정보 표시
- st.markdown(f"**현재 세션:** `{thread_id}`")
- st.caption("대화 기록을 구분하는 고유 ID입니다.")
-
- # 새 세션 시작 버튼
- if st.button(
- "🔄 새 세션 시작",
- use_container_width=True,
- help="새로운 대화 세션을 시작합니다.",
- ):
- st.session_state.chatbot_thread_id = str(uuid.uuid4())[:8]
- st.session_state.chatbot_messages = []
- st.rerun()
-
- # 대화 기록 섹션
- if st.session_state.get("chatbot_messages"):
- st.divider()
- st.markdown("### 💬 대화 기록")
-
- # 메시지 개수 표시
- message_count = len(st.session_state.chatbot_messages)
- st.caption(f"총 {message_count}개의 메시지")
-
- # 대화 기록 표시 (접힌 상태)
- with st.expander("📄 전체 기록 보기 (JSON)", expanded=False):
- st.json(st.session_state.chatbot_messages)
-
- # 최근 메시지 미리보기
- if message_count > 0:
- with st.expander("👀 최근 메시지 미리보기", expanded=False):
- recent_messages = st.session_state.chatbot_messages[-3:] # 최근 3개
- for msg in recent_messages:
- role_icon = "👤" if msg["role"] == "user" else "🤖"
- role_text = "사용자" if msg["role"] == "user" else "AI"
- content_preview = (
- msg["content"][:50] + "..."
- if len(msg["content"]) > 50
- else msg["content"]
- )
- st.caption(f"{role_icon} {role_text}: {content_preview}")
-
- return thread_id
diff --git a/interface/app_pages/sidebar_components/data_source_selector.py b/interface/app_pages/sidebar_components/data_source_selector.py
deleted file mode 100644
index b32cf3f..0000000
--- a/interface/app_pages/sidebar_components/data_source_selector.py
+++ /dev/null
@@ -1,80 +0,0 @@
-import streamlit as st
-
-from interface.core.config import (
- load_config,
- get_data_sources_registry,
- update_datahub_server,
- update_vectordb_settings,
- update_data_source_mode,
-)
-
-
-def render_sidebar_data_source_selector(config=None) -> None:
- if config is None:
- config = load_config()
-
- registry = get_data_sources_registry()
-
- st.sidebar.markdown("### 데이터 소스")
-
- mode_index = 0 if (config.data_source_mode or "datahub").lower() == "datahub" else 1
- selected_mode = st.sidebar.radio(
- "소스 종류", options=["DataHub", "VectorDB"], index=mode_index, horizontal=True
- )
-
- if selected_mode == "DataHub":
- datahub_names = [s.name for s in registry.datahub]
- if not datahub_names:
- st.sidebar.warning(
- "등록된 DataHub가 없습니다. 설정 > 데이터 소스에서 추가하세요."
- )
- return
- dh_name = st.sidebar.selectbox(
- "DataHub 인스턴스", options=datahub_names, key="sidebar_dh_select"
- )
- if st.sidebar.button("소스 적용", key="sidebar_apply_dh"):
- selected = next((s for s in registry.datahub if s.name == dh_name), None)
- if selected is None:
- st.sidebar.error("선택한 DataHub를 찾을 수 없습니다.")
- return
- try:
- update_datahub_server(config, selected.url)
- # DataHub 선택 시, FAISS 경로가 정의되어 있으면 기본 VectorDB 로케이션으로도 반영
- if selected.faiss_path:
- try:
- update_vectordb_settings(
- config,
- vectordb_type="faiss",
- vectordb_location=selected.faiss_path,
- )
- except Exception as e:
- st.sidebar.warning(f"FAISS 경로 적용 경고: {e}")
- update_data_source_mode(config, "datahub")
- st.sidebar.success(f"DataHub 적용됨: {selected.name}")
- except Exception as e:
- st.sidebar.error(f"적용 실패: {e}")
- else:
- vdb_names = [s.name for s in registry.vectordb]
- if not vdb_names:
- st.sidebar.warning(
- "등록된 VectorDB가 없습니다. 설정 > 데이터 소스에서 추가하세요."
- )
- return
- vdb_name = st.sidebar.selectbox(
- "VectorDB 인스턴스", options=vdb_names, key="sidebar_vdb_select"
- )
- if st.sidebar.button("소스 적용", key="sidebar_apply_vdb"):
- selected = next((s for s in registry.vectordb if s.name == vdb_name), None)
- if selected is None:
- st.sidebar.error("선택한 VectorDB를 찾을 수 없습니다.")
- return
- try:
- update_vectordb_settings(
- config,
- vectordb_type=selected.type,
- vectordb_location=selected.location,
- )
- update_data_source_mode(config, "vectordb")
- st.sidebar.success(f"VectorDB 적용됨: {selected.name}")
- except Exception as e:
- st.sidebar.error(f"적용 실패: {e}")
diff --git a/interface/app_pages/sidebar_components/db_selector.py b/interface/app_pages/sidebar_components/db_selector.py
deleted file mode 100644
index 4f9bff0..0000000
--- a/interface/app_pages/sidebar_components/db_selector.py
+++ /dev/null
@@ -1,48 +0,0 @@
-import os
-import streamlit as st
-
-from interface.core.config import get_db_connections_registry, update_db_settings
-
-
-def render_sidebar_db_selector() -> None:
- st.sidebar.markdown("### DB 연결")
-
- registry = get_db_connections_registry()
- names = [c.name for c in registry.connections]
- if not names:
- st.sidebar.warning("등록된 DB 프로파일이 없습니다. 설정 > DB에서 추가하세요.")
- return
-
- # 기본 선택: 세션 또는 ENV의 DB_TYPE과 일치하는 첫 프로파일
- current_type = (
- st.session_state.get("DB_TYPE") or os.getenv("DB_TYPE") or ""
- ).lower()
- default_index = 0
- if current_type:
- for idx, c in enumerate(registry.connections):
- if c.type == current_type:
- default_index = idx
- break
-
- sel_name = st.sidebar.selectbox(
- "프로파일", options=names, index=default_index, key="sidebar_db_profile"
- )
- selected = next((c for c in registry.connections if c.name == sel_name), None)
- if selected is None:
- st.sidebar.error("선택한 프로파일을 찾을 수 없습니다.")
- return
-
- if st.sidebar.button("적용", key="sidebar_apply_db"):
- try:
- values = {
- "host": selected.host,
- "port": selected.port,
- "user": selected.user,
- "password": selected.password,
- "database": selected.database,
- "extra": selected.extra,
- }
- update_db_settings(db_type=selected.type, values=values, secrets={})
- st.sidebar.success(f"DB 적용됨: {selected.name}")
- except Exception as e:
- st.sidebar.error(f"적용 실패: {e}")
diff --git a/interface/app_pages/sidebar_components/embedding_selector.py b/interface/app_pages/sidebar_components/embedding_selector.py
deleted file mode 100644
index c01f50c..0000000
--- a/interface/app_pages/sidebar_components/embedding_selector.py
+++ /dev/null
@@ -1,81 +0,0 @@
-import os
-import streamlit as st
-
-from interface.core.config import (
- update_embedding_settings,
- get_embedding_registry,
-)
-
-
-def render_sidebar_embedding_selector() -> None:
- st.sidebar.markdown("### Embeddings 선택")
-
- e_reg = get_embedding_registry()
- if not e_reg.profiles:
- st.sidebar.info(
- "저장된 Embeddings 프로파일이 없습니다. 설정 > LLM에서 저장하세요."
- )
- # fallback: 간단 공급자 선택 유지
- default_emb = (
- (
- st.session_state.get("EMBEDDING_PROVIDER")
- or os.getenv("EMBEDDING_PROVIDER")
- or "openai"
- )
- ).lower()
- selected = st.sidebar.selectbox(
- "Embeddings 공급자",
- options=["openai", "azure", "bedrock", "gemini", "ollama", "huggingface"],
- index=(
- ["openai", "azure", "bedrock", "gemini", "ollama", "huggingface"].index(
- default_emb
- )
- if default_emb
- in {"openai", "azure", "bedrock", "gemini", "ollama", "huggingface"}
- else 0
- ),
- key="sidebar_embedding_provider_fallback",
- )
- if selected != default_emb:
- try:
- update_embedding_settings(provider=selected, values={})
- st.sidebar.success(
- f"Embeddings 공급자가 '{selected}'로 변경되었습니다."
- )
- except Exception as e:
- st.sidebar.error(f"Embeddings 공급자 변경 실패: {e}")
- return
-
- e_names = [p.name for p in e_reg.profiles]
- current_emb_provider = (
- st.session_state.get("EMBEDDING_PROVIDER")
- or os.getenv("EMBEDDING_PROVIDER")
- or ""
- ).lower()
- e_default_index = 0
- if current_emb_provider:
- for idx, p in enumerate(e_reg.profiles):
- if p.provider == current_emb_provider:
- e_default_index = idx
- break
-
- e_sel_name = st.sidebar.selectbox(
- "Embeddings 프로파일",
- options=e_names,
- index=e_default_index,
- key="sidebar_embedding_profile",
- )
-
- e_selected = next((p for p in e_reg.profiles if p.name == e_sel_name), None)
- if e_selected is None:
- st.sidebar.error("선택한 Embeddings 프로파일을 찾을 수 없습니다.")
- return
-
- if st.sidebar.button("Embeddings 적용", key="sidebar_apply_embedding_profile"):
- try:
- update_embedding_settings(
- provider=e_selected.provider, values=e_selected.fields
- )
- st.sidebar.success(f"Embeddings 프로파일 적용됨: {e_selected.name}")
- except Exception as e:
- st.sidebar.error(f"Embeddings 프로파일 적용 실패: {e}")
diff --git a/interface/app_pages/sidebar_components/llm_selector.py b/interface/app_pages/sidebar_components/llm_selector.py
deleted file mode 100644
index b8260e0..0000000
--- a/interface/app_pages/sidebar_components/llm_selector.py
+++ /dev/null
@@ -1,77 +0,0 @@
-import os
-import streamlit as st
-
-from interface.core.config import (
- update_llm_settings,
- get_llm_registry,
-)
-
-
-def render_sidebar_llm_selector() -> None:
- st.sidebar.markdown("### LLM 선택")
-
- reg = get_llm_registry()
- if not reg.profiles:
- st.sidebar.info(
- "저장된 LLM 프로파일이 없습니다. 설정 > LLM에서 프로파일을 저장하세요."
- )
- # 기존 방식 fallback
- default_llm = (
- (
- st.session_state.get("LLM_PROVIDER")
- or os.getenv("LLM_PROVIDER")
- or "openai"
- )
- ).lower()
- selected_provider = st.sidebar.selectbox(
- "LLM 공급자",
- options=["openai", "azure", "bedrock", "gemini", "ollama", "huggingface"],
- index=(
- ["openai", "azure", "bedrock", "gemini", "ollama", "huggingface"].index(
- default_llm
- )
- if default_llm
- in {"openai", "azure", "bedrock", "gemini", "ollama", "huggingface"}
- else 0
- ),
- key="sidebar_llm_provider_fallback",
- )
- if selected_provider != default_llm:
- try:
- update_llm_settings(provider=selected_provider, values={})
- st.sidebar.success(
- f"LLM 공급자가 '{selected_provider}'로 변경되었습니다."
- )
- except Exception as e:
- st.sidebar.error(f"LLM 공급자 변경 실패: {e}")
- return
-
- names = [p.name for p in reg.profiles]
- # 기본 선택: 세션의 LLM_PROVIDER와 같은 provider를 가진 첫 프로파일
- current_provider = (
- st.session_state.get("LLM_PROVIDER") or os.getenv("LLM_PROVIDER") or ""
- ).lower()
- default_index = 0
- if current_provider:
- for idx, p in enumerate(reg.profiles):
- if p.provider == current_provider:
- default_index = idx
- break
-
- sel_name = st.sidebar.selectbox(
- "LLM 프로파일", options=names, index=default_index, key="sidebar_llm_profile"
- )
- selected = next((p for p in reg.profiles if p.name == sel_name), None)
- if selected is None:
- st.sidebar.error("선택한 LLM 프로파일을 찾을 수 없습니다.")
- return
-
- if st.sidebar.button("적용", key="sidebar_apply_llm_profile"):
- try:
- # provider 설정 + 프로파일의 비민감 필드만 적용
- update_llm_settings(provider=selected.provider, values=selected.fields)
- st.sidebar.success(f"LLM 프로파일 적용됨: {selected.name}")
- except Exception as e:
- st.sidebar.error(f"LLM 프로파일 적용 실패: {e}")
-
- # Embeddings 관련 UI는 embedding_selector.py에서 처리
diff --git a/interface/core/config/__init__.py b/interface/core/config/__init__.py
deleted file mode 100644
index e316c17..0000000
--- a/interface/core/config/__init__.py
+++ /dev/null
@@ -1,123 +0,0 @@
-"""config 패키지의 공개 API를 재노출하여 기존 import 호환성을 유지합니다.
-모델, 경로/지속성, 레지스트리, 설정 업데이트 유틸을 한 곳에서 제공합니다.
-"""
-
-from .models import (
- Config,
- DataHubSource,
- VectorDBSource,
- DataSourcesRegistry,
- DBConnectionProfile,
- DBConnectionsRegistry,
- LLMProfile,
- LLMRegistry,
- EmbeddingProfile,
- EmbeddingRegistry,
-)
-
-from .settings import (
- load_config,
- update_datahub_server,
- update_data_source_mode,
- update_vectordb_settings,
- update_llm_settings,
- update_embedding_settings,
- update_db_settings,
-)
-
-from .registry_data_sources import (
- get_data_sources_registry,
- add_datahub_source,
- update_datahub_source,
- delete_datahub_source,
- add_vectordb_source,
- update_vectordb_source,
- delete_vectordb_source,
-)
-
-from .registry_db import (
- get_db_connections_registry,
- add_db_connection,
- update_db_connection,
- delete_db_connection,
-)
-
-from .registry_llm import (
- get_llm_registry,
- save_llm_profile,
- get_embedding_registry,
- save_embedding_profile,
-)
-
-from .paths import (
- get_registry_file_path,
- get_db_registry_file_path,
- get_llm_registry_file_path,
- get_embedding_registry_file_path,
- ensure_parent_dir,
-)
-
-from .persist import (
- save_registry_to_disk,
- save_db_registry_to_disk,
- save_llm_registry_to_disk,
- save_embedding_registry_to_disk,
- load_registry_from_disk,
- load_db_registry_from_disk,
- load_llm_registry_from_disk,
- load_embedding_registry_from_disk,
-)
-
-__all__ = [
- # Models
- "Config",
- "DataHubSource",
- "VectorDBSource",
- "DataSourcesRegistry",
- "DBConnectionProfile",
- "DBConnectionsRegistry",
- "LLMProfile",
- "LLMRegistry",
- "EmbeddingProfile",
- "EmbeddingRegistry",
- # Settings APIs
- "load_config",
- "update_datahub_server",
- "update_data_source_mode",
- "update_vectordb_settings",
- "update_llm_settings",
- "update_embedding_settings",
- "update_db_settings",
- # Registries - data sources
- "get_data_sources_registry",
- "add_datahub_source",
- "update_datahub_source",
- "delete_datahub_source",
- "add_vectordb_source",
- "update_vectordb_source",
- "delete_vectordb_source",
- # Registries - db connections
- "get_db_connections_registry",
- "add_db_connection",
- "update_db_connection",
- "delete_db_connection",
- # Registries - llm/embedding
- "get_llm_registry",
- "save_llm_profile",
- "get_embedding_registry",
- "save_embedding_profile",
- # Persistence helpers and paths (for backward compatibility)
- "get_registry_file_path",
- "get_db_registry_file_path",
- "get_llm_registry_file_path",
- "get_embedding_registry_file_path",
- "ensure_parent_dir",
- "save_registry_to_disk",
- "save_db_registry_to_disk",
- "save_llm_registry_to_disk",
- "save_embedding_registry_to_disk",
- "load_registry_from_disk",
- "load_db_registry_from_disk",
- "load_llm_registry_from_disk",
- "load_embedding_registry_from_disk",
-]
diff --git a/interface/core/config/models.py b/interface/core/config/models.py
deleted file mode 100644
index 9ec02af..0000000
--- a/interface/core/config/models.py
+++ /dev/null
@@ -1,83 +0,0 @@
-"""설정 및 각 레지스트리에서 사용하는 데이터 모델(dataclass) 정의 모듈입니다."""
-
-from dataclasses import dataclass, field
-from typing import List, Optional, Any, Dict
-
-
-@dataclass
-class Config:
- datahub_server: str
- vectordb_type: str
- vectordb_location: str
- data_source_mode: str | None = None # "datahub" | "vectordb" | None
-
-
-@dataclass
-class DataHubSource:
- name: str
- url: str
- faiss_path: Optional[str] = None
- note: Optional[str] = None
-
-
-@dataclass
-class VectorDBSource:
- name: str
- type: str # 'faiss' | 'pgvector'
- location: str
- collection_prefix: Optional[str] = None
- note: Optional[str] = None
-
-
-@dataclass
-class DataSourcesRegistry:
- datahub: List[DataHubSource] = field(default_factory=list)
- vectordb: List[VectorDBSource] = field(default_factory=list)
-
-
-@dataclass
-class DBConnectionProfile:
- name: str
- type: str # 'postgresql' | 'mysql' | 'mariadb' | 'oracle' | 'clickhouse' | 'duckdb' | 'sqlite' | 'databricks' | 'snowflake' | 'trino'
- host: Optional[str] = None
- port: Optional[int] = None
- user: Optional[str] = None
- password: Optional[str] = None
- database: Optional[str] = None
- extra: Optional[Dict[str, Any]] = None # non-secret
- note: Optional[str] = None
-
-
-@dataclass
-class DBConnectionsRegistry:
- connections: List[DBConnectionProfile] = field(default_factory=list)
-
-
-@dataclass
-class LLMProfile:
- name: str
- provider: (
- str # 'openai' | 'azure' | 'bedrock' | 'gemini' | 'ollama' | 'huggingface'
- )
- fields: Dict[str, str] = field(default_factory=dict) # includes secrets
- note: Optional[str] = None
-
-
-@dataclass
-class LLMRegistry:
- profiles: List[LLMProfile] = field(default_factory=list)
-
-
-@dataclass
-class EmbeddingProfile:
- name: str
- provider: (
- str # 'openai' | 'azure' | 'bedrock' | 'gemini' | 'ollama' | 'huggingface'
- )
- fields: Dict[str, str] = field(default_factory=dict)
- note: Optional[str] = None
-
-
-@dataclass
-class EmbeddingRegistry:
- profiles: List[EmbeddingProfile] = field(default_factory=list)
diff --git a/interface/core/config/paths.py b/interface/core/config/paths.py
deleted file mode 100644
index 668f052..0000000
--- a/interface/core/config/paths.py
+++ /dev/null
@@ -1,38 +0,0 @@
-"""레지스트리 파일 경로 계산 및 상위 디렉토리 생성 유틸리티를 제공합니다."""
-
-import os
-from pathlib import Path
-
-
-def get_registry_file_path() -> Path:
- # Allow override via env var, else default to ./config/data_sources.json
- override = os.getenv("LANG2SQL_REGISTRY_PATH")
- if override:
- return Path(override).expanduser().resolve()
- return Path(os.getcwd()) / "config" / "data_sources.json"
-
-
-def get_db_registry_file_path() -> Path:
- # Allow override via env var, else default to ./config/db_connections.json
- override = os.getenv("LANG2SQL_DB_REGISTRY_PATH")
- if override:
- return Path(override).expanduser().resolve()
- return Path(os.getcwd()) / "config" / "db_connections.json"
-
-
-def get_llm_registry_file_path() -> Path:
- override = os.getenv("LANG2SQL_LLM_REGISTRY_PATH")
- if override:
- return Path(override).expanduser().resolve()
- return Path(os.getcwd()) / "config" / "llm_profiles.json"
-
-
-def get_embedding_registry_file_path() -> Path:
- override = os.getenv("LANG2SQL_EMBEDDING_REGISTRY_PATH")
- if override:
- return Path(override).expanduser().resolve()
- return Path(os.getcwd()) / "config" / "embedding_profiles.json"
-
-
-def ensure_parent_dir(path: Path) -> None:
- path.parent.mkdir(parents=True, exist_ok=True)
diff --git a/interface/core/config/persist.py b/interface/core/config/persist.py
deleted file mode 100644
index 81ba144..0000000
--- a/interface/core/config/persist.py
+++ /dev/null
@@ -1,205 +0,0 @@
-"""레지스트리 직렬화/역직렬화와 디스크 저장/로드 로직을 제공합니다."""
-
-import json
-from dataclasses import asdict
-from pathlib import Path
-from typing import Any, Dict, List
-
-from .models import (
- DataSourcesRegistry,
- DataHubSource,
- VectorDBSource,
- DBConnectionsRegistry,
- DBConnectionProfile,
- LLMRegistry,
- LLMProfile,
- EmbeddingRegistry,
- EmbeddingProfile,
-)
-from .paths import (
- get_registry_file_path,
- get_db_registry_file_path,
- get_llm_registry_file_path,
- get_embedding_registry_file_path,
- ensure_parent_dir,
-)
-
-
-def save_registry_to_disk(registry: DataSourcesRegistry) -> None:
- path = get_registry_file_path()
- ensure_parent_dir(path)
- payload = asdict(registry)
- with path.open("w", encoding="utf-8") as f:
- json.dump(payload, f, ensure_ascii=False, indent=2)
-
-
-def save_db_registry_to_disk(registry: DBConnectionsRegistry) -> None:
- path = get_db_registry_file_path()
- ensure_parent_dir(path)
- payload = asdict(registry)
- with path.open("w", encoding="utf-8") as f:
- json.dump(payload, f, ensure_ascii=False, indent=2)
-
-
-def save_llm_registry_to_disk(registry: LLMRegistry) -> None:
- path = get_llm_registry_file_path()
- ensure_parent_dir(path)
- payload = asdict(registry)
- with path.open("w", encoding="utf-8") as f:
- json.dump(payload, f, ensure_ascii=False, indent=2)
-
-
-def save_embedding_registry_to_disk(registry: EmbeddingRegistry) -> None:
- path = get_embedding_registry_file_path()
- ensure_parent_dir(path)
- payload = asdict(registry)
- with path.open("w", encoding="utf-8") as f:
- json.dump(payload, f, ensure_ascii=False, indent=2)
-
-
-def _parse_datahub_list(items: List[Dict[str, Any]]) -> List[DataHubSource]:
- parsed: List[DataHubSource] = []
- for item in items or []:
- name = str(item.get("name", "")).strip()
- url = str(item.get("url", "")).strip()
- faiss_path = item.get("faiss_path")
- note = item.get("note")
- if not name or not url:
- continue
- parsed.append(
- DataHubSource(name=name, url=url, faiss_path=faiss_path, note=note)
- )
- return parsed
-
-
-def _parse_vectordb_list(items: List[Dict[str, Any]]) -> List[VectorDBSource]:
- parsed: List[VectorDBSource] = []
- for item in items or []:
- name = str(item.get("name", "")).strip()
- vtype = str(item.get("type", "")).strip().lower()
- location = str(item.get("location", "")).strip()
- if not name or not vtype or not location:
- continue
- collection_prefix = item.get("collection_prefix")
- note = item.get("note")
- parsed.append(
- VectorDBSource(
- name=name,
- type=vtype,
- location=location,
- collection_prefix=collection_prefix,
- note=note,
- )
- )
- return parsed
-
-
-def load_registry_from_disk() -> DataSourcesRegistry:
- path = get_registry_file_path()
- if not path.exists():
- return DataSourcesRegistry()
- with path.open("r", encoding="utf-8") as f:
- data: Dict[str, Any] = json.load(f)
- return DataSourcesRegistry(
- datahub=_parse_datahub_list(data.get("datahub", [])),
- vectordb=_parse_vectordb_list(data.get("vectordb", [])),
- )
-
-
-def _parse_db_conn_list(items: List[Dict[str, Any]]) -> List[DBConnectionProfile]:
- parsed: List[DBConnectionProfile] = []
- for item in items or []:
- name = str(item.get("name", "")).strip()
- db_type = str(item.get("type", "")).strip().lower()
- if not name or not db_type:
- continue
- host = item.get("host")
- port = item.get("port")
- try:
- port = int(port) if port is not None else None
- except Exception:
- port = None
- user = item.get("user")
- password = item.get("password")
- database = item.get("database")
- extra = item.get("extra") or None
- note = item.get("note") or None
- parsed.append(
- DBConnectionProfile(
- name=name,
- type=db_type,
- host=host,
- port=port,
- user=user,
- password=password,
- database=database,
- extra=extra,
- note=note,
- )
- )
- return parsed
-
-
-def load_db_registry_from_disk() -> DBConnectionsRegistry:
- path = get_db_registry_file_path()
- if not path.exists():
- return DBConnectionsRegistry()
- with path.open("r", encoding="utf-8") as f:
- data: Dict[str, Any] = json.load(f)
- return DBConnectionsRegistry(
- connections=_parse_db_conn_list(data.get("connections", []))
- )
-
-
-def _parse_llm_profiles(items: List[Dict[str, Any]]) -> List[LLMProfile]:
- parsed: List[LLMProfile] = []
- for item in items or []:
- name = str(item.get("name", "")).strip()
- provider = str(item.get("provider", "")).strip().lower()
- if not name or not provider:
- continue
- fields = item.get("fields") or {}
- note = item.get("note") or None
- if not isinstance(fields, dict):
- fields = {}
- parsed.append(
- LLMProfile(name=name, provider=provider, fields=fields, note=note)
- )
- return parsed
-
-
-def load_llm_registry_from_disk() -> LLMRegistry:
- path = get_llm_registry_file_path()
- if not path.exists():
- return LLMRegistry()
- with path.open("r", encoding="utf-8") as f:
- data: Dict[str, Any] = json.load(f)
- return LLMRegistry(profiles=_parse_llm_profiles(data.get("profiles", [])))
-
-
-def _parse_embedding_profiles(items: List[Dict[str, Any]]) -> List[EmbeddingProfile]:
- parsed: List[EmbeddingProfile] = []
- for item in items or []:
- name = str(item.get("name", "")).strip()
- provider = str(item.get("provider", "")).strip().lower()
- if not name or not provider:
- continue
- fields = item.get("fields") or {}
- note = item.get("note") or None
- if not isinstance(fields, dict):
- fields = {}
- parsed.append(
- EmbeddingProfile(name=name, provider=provider, fields=fields, note=note)
- )
- return parsed
-
-
-def load_embedding_registry_from_disk() -> EmbeddingRegistry:
- path = get_embedding_registry_file_path()
- if not path.exists():
- return EmbeddingRegistry()
- with path.open("r", encoding="utf-8") as f:
- data: Dict[str, Any] = json.load(f)
- return EmbeddingRegistry(
- profiles=_parse_embedding_profiles(data.get("profiles", []))
- )
diff --git a/interface/core/config/registry_data_sources.py b/interface/core/config/registry_data_sources.py
deleted file mode 100644
index 8e9b646..0000000
--- a/interface/core/config/registry_data_sources.py
+++ /dev/null
@@ -1,130 +0,0 @@
-"""DataHub/VectorDB 소스 레지스트리를 세션+디스크에 관리하는 모듈입니다.
-get/add/update/delete 연산과 Streamlit 세션 연동을 제공합니다.
-"""
-
-from typing import Optional
-
-try:
- import streamlit as st # type: ignore
-except Exception: # pragma: no cover
- st = None # type: ignore
-
-from .models import DataSourcesRegistry, DataHubSource, VectorDBSource
-from .persist import (
- load_registry_from_disk,
- save_registry_to_disk,
-)
-
-
-def get_data_sources_registry() -> DataSourcesRegistry:
- if st is not None and "data_sources_registry" in st.session_state:
- reg = st.session_state["data_sources_registry"]
- return reg # stored as DataSourcesRegistry
- # Try load from disk
- try:
- registry = load_registry_from_disk()
- except Exception:
- registry = DataSourcesRegistry()
- if st is not None:
- st.session_state["data_sources_registry"] = registry
- return registry
-
-
-def _save_registry(registry: DataSourcesRegistry) -> None:
- if st is not None:
- st.session_state["data_sources_registry"] = registry
- try:
- save_registry_to_disk(registry)
- except Exception:
- # fail-soft; UI will still have session copy
- pass
-
-
-def add_datahub_source(
- *, name: str, url: str, faiss_path: Optional[str] = None, note: Optional[str] = None
-) -> None:
- registry = get_data_sources_registry()
- if any(s.name == name for s in registry.datahub):
- raise ValueError(f"이미 존재하는 DataHub 이름입니다: {name}")
- registry.datahub.append(
- DataHubSource(name=name, url=url, faiss_path=faiss_path, note=note)
- )
- _save_registry(registry)
-
-
-def update_datahub_source(
- *, name: str, url: str, faiss_path: Optional[str], note: Optional[str]
-) -> None:
- registry = get_data_sources_registry()
- for idx, s in enumerate(registry.datahub):
- if s.name == name:
- registry.datahub[idx] = DataHubSource(
- name=name, url=url, faiss_path=faiss_path, note=note
- )
- _save_registry(registry)
- return
- raise ValueError(f"존재하지 않는 DataHub 이름입니다: {name}")
-
-
-def delete_datahub_source(*, name: str) -> None:
- registry = get_data_sources_registry()
- registry.datahub = [s for s in registry.datahub if s.name != name]
- _save_registry(registry)
-
-
-def add_vectordb_source(
- *,
- name: str,
- vtype: str,
- location: str,
- collection_prefix: Optional[str] = None,
- note: Optional[str] = None,
-) -> None:
- vtype = (vtype or "").lower()
- if vtype not in ("faiss", "pgvector"):
- raise ValueError("VectorDB 타입은 'faiss' 또는 'pgvector'여야 합니다")
- registry = get_data_sources_registry()
- if any(s.name == name for s in registry.vectordb):
- raise ValueError(f"이미 존재하는 VectorDB 이름입니다: {name}")
- registry.vectordb.append(
- VectorDBSource(
- name=name,
- type=vtype,
- location=location,
- collection_prefix=collection_prefix,
- note=note,
- )
- )
- _save_registry(registry)
-
-
-def update_vectordb_source(
- *,
- name: str,
- vtype: str,
- location: str,
- collection_prefix: Optional[str],
- note: Optional[str],
-) -> None:
- vtype = (vtype or "").lower()
- if vtype not in ("faiss", "pgvector"):
- raise ValueError("VectorDB 타입은 'faiss' 또는 'pgvector'여야 합니다")
- registry = get_data_sources_registry()
- for idx, s in enumerate(registry.vectordb):
- if s.name == name:
- registry.vectordb[idx] = VectorDBSource(
- name=name,
- type=vtype,
- location=location,
- collection_prefix=collection_prefix,
- note=note,
- )
- _save_registry(registry)
- return
- raise ValueError(f"존재하지 않는 VectorDB 이름입니다: {name}")
-
-
-def delete_vectordb_source(*, name: str) -> None:
- registry = get_data_sources_registry()
- registry.vectordb = [s for s in registry.vectordb if s.name != name]
- _save_registry(registry)
diff --git a/interface/core/config/registry_db.py b/interface/core/config/registry_db.py
deleted file mode 100644
index b58d5e7..0000000
--- a/interface/core/config/registry_db.py
+++ /dev/null
@@ -1,106 +0,0 @@
-"""DB 연결 프로파일 레지스트리를 세션+디스크에 관리하는 모듈입니다.
-get/add/update/delete 연산과 Streamlit 세션 연동을 제공합니다.
-"""
-
-from typing import Any, Dict, Optional
-
-try:
- import streamlit as st # type: ignore
-except Exception: # pragma: no cover
- st = None # type: ignore
-
-from .models import DBConnectionsRegistry, DBConnectionProfile
-from .persist import load_db_registry_from_disk, save_db_registry_to_disk
-
-
-def get_db_connections_registry() -> DBConnectionsRegistry:
- if st is not None and "db_connections_registry" in st.session_state:
- reg = st.session_state["db_connections_registry"]
- return reg # stored as DBConnectionsRegistry
- try:
- registry = load_db_registry_from_disk()
- except Exception:
- registry = DBConnectionsRegistry()
- if st is not None:
- st.session_state["db_connections_registry"] = registry
- return registry
-
-
-def _save_db_registry(registry: DBConnectionsRegistry) -> None:
- if st is not None:
- st.session_state["db_connections_registry"] = registry
- try:
- save_db_registry_to_disk(registry)
- except Exception:
- # fail-soft; UI will still have session copy
- pass
-
-
-def add_db_connection(
- *,
- name: str,
- db_type: str,
- host: Optional[str] = None,
- port: Optional[int] = None,
- user: Optional[str] = None,
- password: Optional[str] = None,
- database: Optional[str] = None,
- extra: Optional[Dict[str, Any]] = None,
- note: Optional[str] = None,
-) -> None:
- db_type_norm = (db_type or "").lower()
- registry = get_db_connections_registry()
- if any(c.name == name for c in registry.connections):
- raise ValueError(f"이미 존재하는 DB 프로파일 이름입니다: {name}")
- registry.connections.append(
- DBConnectionProfile(
- name=name,
- type=db_type_norm,
- host=host,
- port=port,
- user=user,
- password=password,
- database=database,
- extra=extra or None,
- note=note or None,
- )
- )
- _save_db_registry(registry)
-
-
-def update_db_connection(
- *,
- name: str,
- db_type: str,
- host: Optional[str],
- port: Optional[int],
- user: Optional[str],
- password: Optional[str],
- database: Optional[str],
- extra: Optional[Dict[str, Any]],
- note: Optional[str],
-) -> None:
- db_type_norm = (db_type or "").lower()
- registry = get_db_connections_registry()
- for idx, c in enumerate(registry.connections):
- if c.name == name:
- registry.connections[idx] = DBConnectionProfile(
- name=name,
- type=db_type_norm,
- host=host,
- port=port,
- user=user,
- password=password,
- database=database,
- extra=extra or None,
- note=note or None,
- )
- _save_db_registry(registry)
- return
- raise ValueError(f"존재하지 않는 DB 프로파일 이름입니다: {name}")
-
-
-def delete_db_connection(*, name: str) -> None:
- registry = get_db_connections_registry()
- registry.connections = [c for c in registry.connections if c.name != name]
- _save_db_registry(registry)
diff --git a/interface/core/config/registry_llm.py b/interface/core/config/registry_llm.py
deleted file mode 100644
index 7f4c17a..0000000
--- a/interface/core/config/registry_llm.py
+++ /dev/null
@@ -1,114 +0,0 @@
-"""LLM/Embedding 프로파일 레지스트리를 세션+디스크에 관리하는 모듈입니다.
-프로파일 저장(upsert)과 Streamlit 세션 연동을 제공합니다.
-"""
-
-try:
- import streamlit as st # type: ignore
-except Exception: # pragma: no cover
- st = None # type: ignore
-
-from .models import (
- LLMRegistry,
- LLMProfile,
- EmbeddingRegistry,
- EmbeddingProfile,
-)
-from .persist import (
- load_llm_registry_from_disk,
- save_llm_registry_to_disk,
- load_embedding_registry_from_disk,
- save_embedding_registry_to_disk,
-)
-
-
-def get_llm_registry() -> LLMRegistry:
- if st is not None and "llm_registry" in st.session_state:
- return st.session_state["llm_registry"]
- try:
- registry = load_llm_registry_from_disk()
- except Exception:
- registry = LLMRegistry()
- if st is not None:
- st.session_state["llm_registry"] = registry
- return registry
-
-
-def _save_llm_registry(registry: LLMRegistry) -> None:
- if st is not None:
- st.session_state["llm_registry"] = registry
- try:
- save_llm_registry_to_disk(registry)
- except Exception:
- pass
-
-
-def get_embedding_registry() -> EmbeddingRegistry:
- if st is not None and "embedding_registry" in st.session_state:
- return st.session_state["embedding_registry"]
- try:
- registry = load_embedding_registry_from_disk()
- except Exception:
- registry = EmbeddingRegistry()
- if st is not None:
- st.session_state["embedding_registry"] = registry
- return registry
-
-
-def _save_embedding_registry(registry: EmbeddingRegistry) -> None:
- if st is not None:
- st.session_state["embedding_registry"] = registry
- try:
- save_embedding_registry_to_disk(registry)
- except Exception:
- pass
-
-
-def save_llm_profile(
- *, name: str, provider: str, values: dict[str, str | None], note: str | None = None
-) -> None:
- provider_norm = (provider or "").lower()
- stored_fields: dict[str, str] = {}
- for k, v in (values or {}).items():
- if v is None:
- continue
- stored_fields[k] = str(v)
-
- reg = get_llm_registry()
- # upsert by name
- for idx, p in enumerate(reg.profiles):
- if p.name == name:
- reg.profiles[idx] = LLMProfile(
- name=name, provider=provider_norm, fields=stored_fields, note=note
- )
- _save_llm_registry(reg)
- return
- reg.profiles.append(
- LLMProfile(name=name, provider=provider_norm, fields=stored_fields, note=note)
- )
- _save_llm_registry(reg)
-
-
-def save_embedding_profile(
- *, name: str, provider: str, values: dict[str, str | None], note: str | None = None
-) -> None:
- provider_norm = (provider or "").lower()
- stored_fields: dict[str, str] = {}
- for k, v in (values or {}).items():
- if v is None:
- continue
- stored_fields[k] = str(v)
-
- reg = get_embedding_registry()
- for idx, p in enumerate(reg.profiles):
- if p.name == name:
- reg.profiles[idx] = EmbeddingProfile(
- name=name, provider=provider_norm, fields=stored_fields, note=note
- )
- _save_embedding_registry(reg)
- return
- reg.profiles.append(
- EmbeddingProfile(
- name=name, provider=provider_norm, fields=stored_fields, note=note
- )
- )
- _save_embedding_registry(reg)
diff --git a/interface/core/config/settings.py b/interface/core/config/settings.py
deleted file mode 100644
index 9b4eeb2..0000000
--- a/interface/core/config/settings.py
+++ /dev/null
@@ -1,250 +0,0 @@
-"""런타임 설정 로딩/업데이트 및 세션/환경 변수 반영, 입력값 검증 유틸 포함.
-DataHub/VectorDB/DB/LLM/Embedding 관련 설정 업데이트를 제공합니다.
-"""
-
-import os
-from pathlib import Path
-from typing import Any, Dict, Optional
-
-try:
- import streamlit as st # type: ignore
-except Exception: # pragma: no cover - streamlit may not be present in non-UI contexts
- st = None # type: ignore
-
-from utils.llm.tools import set_gms_server
-
-from .models import Config
-
-DEFAULT_DATAHUB_SERVER = "http://localhost:8080"
-DEFAULT_VECTORDB_TYPE = os.getenv("VECTORDB_TYPE", "faiss").lower()
-DEFAULT_VECTORDB_LOCATION = os.getenv("VECTORDB_LOCATION", "")
-
-
-def _get_session_value(key: str) -> str | None:
- if st is None:
- return None
- try:
- if key in st.session_state and st.session_state[key]:
- return str(st.session_state[key])
- except Exception:
- return None
- return None
-
-
-def load_config() -> Config:
- """Load configuration with priority: session_state > environment > defaults."""
- datahub = _get_session_value("datahub_server") or os.getenv(
- "DATAHUB_SERVER", DEFAULT_DATAHUB_SERVER
- )
- mode = _get_session_value("data_source_mode")
-
- vectordb_type = _get_session_value("vectordb_type") or os.getenv(
- "VECTORDB_TYPE", DEFAULT_VECTORDB_TYPE
- )
- vectordb_location = _get_session_value("vectordb_location") or os.getenv(
- "VECTORDB_LOCATION", DEFAULT_VECTORDB_LOCATION
- )
-
- return Config(
- datahub_server=datahub,
- vectordb_type=vectordb_type.lower() if vectordb_type else DEFAULT_VECTORDB_TYPE,
- vectordb_location=vectordb_location,
- data_source_mode=mode,
- )
-
-
-def update_datahub_server(config: Config, new_url: str) -> None:
- """Update DataHub server URL across runtime config, env-aware clients, and session."""
- if not new_url:
- return
- config.datahub_server = new_url
-
- # Propagate to underlying tooling/clients
- try:
- set_gms_server(new_url)
- except Exception:
- # Fail-soft: UI should surface errors from callers if needed
- pass
-
- # Reflect into session state for immediate UI reuse
- if st is not None:
- try:
- st.session_state["datahub_server"] = new_url
- except Exception:
- pass
-
-
-def update_data_source_mode(config: Config, mode: str | None) -> None:
- """Persist user's data source selection (datahub | vectordb)."""
- config.data_source_mode = mode
- if st is not None:
- try:
- st.session_state["data_source_mode"] = mode
- except Exception:
- pass
-
-
-def _put_env(key: str, value: str | None) -> None:
- if value is None:
- return
- os.environ[key] = value
-
-
-def _put_session(key: str, value: str | None) -> None:
- if st is None:
- return
- try:
- st.session_state[key] = value
- except Exception:
- pass
-
-
-def update_db_settings(
- *,
- db_type: str,
- values: Dict[str, Any] | None,
- secrets: Dict[str, Optional[str]] | None = None,
-) -> None:
- """Update DB settings into process env and session.
-
- Only non-sensitive values should be passed in values and may come from registry.
- Secrets (e.g., PASSWORD, ACCESS_TOKEN) are applied to env/session but never persisted to disk.
- """
- db_type_norm = (db_type or "").lower()
- if not db_type_norm:
- raise ValueError("DB 타입이 비어 있습니다.")
-
- # Core selector
- _put_env("DB_TYPE", db_type_norm)
- _put_session("DB_TYPE", db_type_norm)
-
- prefix = db_type_norm.upper()
-
- base_keys = ["HOST", "PORT", "USER", "DATABASE"]
- for base_key in base_keys:
- vk = base_key.lower()
- v = (values or {}).get(vk)
- if v is None:
- continue
- _put_env(f"{prefix}_{base_key}", str(v))
- _put_session(f"{prefix}_{base_key}", str(v))
-
- # Extras (non-secret)
- extra = (values or {}).get("extra") or {}
- if isinstance(extra, dict):
- for k, v in extra.items():
- if v is None:
- continue
- _put_env(f"{prefix}_{str(k).upper()}", str(v))
- _put_session(f"{prefix}_{str(k).upper()}", str(v))
-
- # Secrets (applied to env+session, never persisted)
- for sk, sv in (secrets or {}).items():
- if sv is None:
- continue
- key_up = str(sk).upper()
- _put_env(f"{prefix}_{key_up}", str(sv))
- _put_session(f"{prefix}_{key_up}", str(sv))
-
-
-def update_vectordb_settings(
- config: Config, *, vectordb_type: str, vectordb_location: str | None
-) -> None:
- """Validate and update VectorDB settings into env and session.
-
- Basic validation rules follow CLI's behavior:
- - vectordb_type must be 'faiss' or 'pgvector'
- - if type == 'faiss' and location provided: must be an existing directory
- - if type == 'pgvector' and location provided: must start with 'postgresql://'
- """
- vtype = (vectordb_type or "").lower()
- if vtype not in ("faiss", "pgvector"):
- raise ValueError(f"지원하지 않는 VectorDB 타입: {vectordb_type}")
-
- vloc = vectordb_location or ""
- if vloc:
- if vtype == "faiss":
- path = Path(vloc)
- # 신규 경로 허용: 존재하면 디렉토리인지 확인, 없으면 상위 디렉토리 생성
- if path.exists() and not path.is_dir():
- raise ValueError(
- f"유효하지 않은 FAISS 디렉토리 경로(파일 경로임): {vloc}"
- )
- if not path.exists():
- try:
- path.mkdir(parents=True, exist_ok=True)
- except Exception as e:
- raise ValueError(f"FAISS 경로 생성 실패: {vloc} | {e}")
- elif vtype == "pgvector":
- if not vloc.startswith("postgresql://"):
- raise ValueError("pgvector URL은 'postgresql://'로 시작해야 합니다")
-
- # Persist to runtime config
- config.vectordb_type = vtype
- config.vectordb_location = vloc
-
- # Reflect to process env for downstream modules
- os.environ["VECTORDB_TYPE"] = vtype
- if vloc:
- os.environ["VECTORDB_LOCATION"] = vloc
-
- # Reflect to session state for UI
- if st is not None:
- try:
- st.session_state["vectordb_type"] = vtype
- st.session_state["vectordb_location"] = vloc
- except Exception:
- pass
-
-
-def update_llm_settings(*, provider: str, values: dict[str, str | None]) -> None:
- """Update chat LLM settings from UI into process env and session.
-
- This function mirrors the environment-variable based configuration consumed by
- utils.llm.core.factory.get_llm(). Only sets provided keys; missing values are left as-is.
- """
- provider_norm = (provider or "").lower()
- if provider_norm not in {
- "openai",
- "azure",
- "bedrock",
- "gemini",
- "ollama",
- "huggingface",
- }:
- raise ValueError(f"지원하지 않는 LLM 공급자: {provider}")
-
- # Core selector
- _put_env("LLM_PROVIDER", provider_norm)
- _put_session("LLM_PROVIDER", provider_norm)
-
- # Provider-specific fields (keys exactly as factory expects)
- for k, v in (values or {}).items():
- if v is not None:
- _put_env(k, v)
- _put_session(k, v)
-
-
-def update_embedding_settings(*, provider: str, values: dict[str, str | None]) -> None:
- """Update Embeddings settings from UI into process env and session.
-
- Mirrors env vars consumed by utils.llm.core.factory.get_embeddings().
- """
- provider_norm = (provider or "").lower()
- if provider_norm not in {
- "openai",
- "azure",
- "bedrock",
- "gemini",
- "ollama",
- "huggingface",
- }:
- raise ValueError(f"지원하지 않는 Embedding 공급자: {provider}")
-
- _put_env("EMBEDDING_PROVIDER", provider_norm)
- _put_session("EMBEDDING_PROVIDER", provider_norm)
-
- for k, v in (values or {}).items():
- if v is not None:
- _put_env(k, v)
- _put_session(k, v)
diff --git a/interface/core/dialects.py b/interface/core/dialects.py
deleted file mode 100644
index ea1463d..0000000
--- a/interface/core/dialects.py
+++ /dev/null
@@ -1,139 +0,0 @@
-"""
-다이얼렉트 프리셋과 옵션 정의 모듈.
-
-이 모듈은 다음을 제공합니다:
-
-- DialectOption: 각 SQL 엔진 특성 데이터클래스
- - name: 엔진 표시 이름 (예: "PostgreSQL", "ClickHouse")
- - supports_ilike: 대소문자 무시 비교(ILIKE) 지원 여부
- - hints: 자주 쓰이는/효과적인 함수의 간결 목록 + 짧은 메모
- - 예: ["DATE_TRUNC (날짜 절단)", "STRING_AGG (문자 집계)"]
-
-- PRESET_DIALECTS: 대표 SQL 엔진들의 기본 프리셋 모음
- - PostgreSQL, ClickHouse, Trino, Snowflake, Redshift, BigQuery, MSSQL, Oracle, DuckDB
-
-주 사용처:
-- Streamlit UI에서 프리셋 선택 및 커스텀 다이얼렉트 입력의 기준 데이터
-- Lang2SQL 파이프라인에서 프롬프트/키워드 힌트 구성
-
-주의:
-- hints는 프롬프트 가이드용이며 실행 보장을 의미하지 않습니다.
-- 실제 문법/함수 지원은 엔진 버전 및 설정에 따라 달라질 수 있습니다.
-"""
-
-from __future__ import annotations
-
-from dataclasses import asdict, dataclass, field
-from typing import Dict, List
-
-
-@dataclass
-class DialectOption:
- name: str
- supports_ilike: bool = False
- hints: List[str] = field(default_factory=list)
-
- def to_dict(self) -> Dict:
- return asdict(self)
-
- @staticmethod
- def from_dict(data: Dict) -> "DialectOption":
- return DialectOption(
- name=data.get("name", "Custom"),
- supports_ilike=bool(data.get("supports_ilike", False)),
- hints=list(data.get("hints", data.get("keyword_hints", []))),
- )
-
-
-PRESET_DIALECTS: Dict[str, DialectOption] = {
- "PostgreSQL": DialectOption(
- name="PostgreSQL",
- supports_ilike=True,
- hints=[
- "COALESCE (널 대체)",
- "DATE_TRUNC (날짜 절단)",
- "STRING_AGG (문자 집계)",
- "GENERATE_SERIES (시퀀스 생성)",
- ],
- ),
- "ClickHouse": DialectOption(
- name="ClickHouse",
- supports_ilike=False,
- hints=[
- "toDate (날짜 변환)",
- "dateDiff (날짜 차이)",
- "arrayJoin (배열 펼치기)",
- "groupArray (배열 집계)",
- ],
- ),
- "Trino": DialectOption(
- name="Trino",
- supports_ilike=False,
- hints=[
- "date_trunc (날짜 절단)",
- "try_cast (안전 변환)",
- "coalesce (널 대체)",
- "regexp_like (정규식 매칭)",
- ],
- ),
- "Snowflake": DialectOption(
- name="Snowflake",
- supports_ilike=True,
- hints=[
- "IFF (조건 분기)",
- "TO_DATE (날짜 변환)",
- "DATE_TRUNC (날짜 절단)",
- "LISTAGG (문자 집계)",
- ],
- ),
- "Redshift": DialectOption(
- name="Redshift",
- supports_ilike=True,
- hints=[
- "COALESCE (널 대체)",
- "DATE_TRUNC (날짜 절단)",
- "LISTAGG (문자 집계)",
- "REGEXP_REPLACE (정규식 치환)",
- ],
- ),
- "BigQuery": DialectOption(
- name="BigQuery",
- supports_ilike=False,
- hints=[
- "SAFE_CAST (안전 변환)",
- "DATE_TRUNC (날짜 절단)",
- "ARRAY_AGG (배열 집계)",
- "REGEXP_CONTAINS (정규식 포함)",
- ],
- ),
- "MSSQL": DialectOption(
- name="MSSQL",
- supports_ilike=False,
- hints=[
- "ISNULL (널 대체)",
- "DATEADD (날짜 가감)",
- "CONVERT (형 변환)",
- "STRING_AGG (문자 집계)",
- ],
- ),
- "Oracle": DialectOption(
- name="Oracle",
- supports_ilike=False,
- hints=[
- "NVL (널 대체)",
- "TO_DATE (날짜 변환)",
- "TRUNC (날짜 절단)",
- "LISTAGG (문자 집계)",
- ],
- ),
- "DuckDB": DialectOption(
- name="DuckDB",
- supports_ilike=True,
- hints=[
- "date_trunc (날짜 절단)",
- "string_agg (문자 집계)",
- "coalesce (널 대체)",
- "regexp_replace (정규식 치환)",
- ],
- ),
-}
diff --git a/interface/core/lang2sql_runner.py b/interface/core/lang2sql_runner.py
deleted file mode 100644
index 08ecdc0..0000000
--- a/interface/core/lang2sql_runner.py
+++ /dev/null
@@ -1,72 +0,0 @@
-"""
-Lang2SQL 실행 모듈.
-
-이 모듈은 자연어로 입력된 질문을 SQL 쿼리로 변환하고,
-지정된 데이터베이스 환경에서 실행하는 함수(`run_lang2sql`)를 제공합니다.
-내부적으로 v2 플로우(BaselineNL2SQL / EnrichedNL2SQL)를 사용한다.
-"""
-
-from __future__ import annotations
-
-from typing import Any
-
-
-def run_lang2sql(
- query: str,
- db_dialect: str | None = None,
- top_n: int = 5,
- use_enriched: bool = False,
- catalog: list | None = None,
-) -> dict[str, Any]:
- """Lang2SQL 실행 함수.
-
- 주어진 자연어 질문을 SQL 쿼리로 변환하고 데이터베이스에서 실행한다.
- LLM, Embedding, DB는 환경변수(LLM_PROVIDER, EMBEDDING_PROVIDER, DB_TYPE 등)로
- 자동 설정된다.
-
- Args:
- query: 사용자 입력 자연어 질문.
- db_dialect: SQL 방언 힌트 (None이면 default 프롬프트 사용).
- top_n: 검색할 상위 테이블 수.
- use_enriched: True이면 EnrichedNL2SQL 플로우 사용.
- catalog: CatalogEntry 목록. None이면 빈 카탈로그로 실행.
-
- Returns:
- dict: {"rows": list[dict], "sql": str, "error": str | None}
- """
- from lang2sql.factory import (
- build_db_from_env,
- build_embedding_from_env,
- build_llm_from_env,
- )
- from lang2sql.flows import BaselineNL2SQL, EnrichedNL2SQL
-
- catalog = catalog or []
-
- try:
- llm = build_llm_from_env()
- db = build_db_from_env()
-
- if use_enriched:
- embedding = build_embedding_from_env()
- pipeline = EnrichedNL2SQL(
- catalog=catalog,
- llm=llm,
- db=db,
- embedding=embedding,
- db_dialect=db_dialect,
- top_n=top_n,
- )
- else:
- pipeline = BaselineNL2SQL(
- catalog=catalog,
- llm=llm,
- db=db,
- db_dialect=db_dialect,
- )
-
- rows = pipeline.run(query)
- return {"rows": rows, "error": None}
-
- except Exception as exc:
- return {"rows": [], "error": str(exc)}
diff --git a/interface/core/provider_factory.py b/interface/core/provider_factory.py
deleted file mode 100644
index 72da835..0000000
--- a/interface/core/provider_factory.py
+++ /dev/null
@@ -1,141 +0,0 @@
-"""Settings UI에서 선택된 프로파일을 LLMPort/EmbeddingPort로 변환하는 팩토리.
-
-LLMProfile과 EmbeddingProfile(config/models.py)을 받아
-lang2sql.integrations의 구현체를 반환한다.
-"""
-
-from __future__ import annotations
-
-from typing import TYPE_CHECKING
-
-if TYPE_CHECKING:
- from lang2sql.core.ports import EmbeddingPort, LLMPort
- from interface.core.config.models import EmbeddingProfile, LLMProfile
-
-
-def build_llm(profile: "LLMProfile") -> "LLMPort":
- """LLMProfile → LLMPort 변환. Settings UI에서 호출한다."""
- f = profile.fields
- provider = profile.provider.lower()
-
- if provider == "openai":
- from lang2sql.integrations.llm.openai_ import OpenAILLM
-
- return OpenAILLM(
- model=f.get("model", "gpt-4o"),
- api_key=f.get("api_key"),
- )
-
- if provider == "anthropic":
- from lang2sql.integrations.llm.anthropic_ import AnthropicLLM
-
- return AnthropicLLM(
- model=f.get("model", "claude-sonnet-4-6"),
- api_key=f.get("api_key"),
- )
-
- if provider == "azure":
- from lang2sql.integrations.llm.azure_ import AzureOpenAILLM
-
- return AzureOpenAILLM(
- azure_deployment=f["azure_deployment"],
- azure_endpoint=f["azure_endpoint"],
- api_version=f.get("api_version", "2023-07-01-preview"),
- api_key=f.get("api_key"),
- )
-
- if provider == "gemini":
- from lang2sql.integrations.llm.gemini_ import GeminiLLM
-
- return GeminiLLM(
- model=f.get("model", "gemini-2.0-flash-lite"),
- api_key=f.get("api_key"),
- )
-
- if provider == "bedrock":
- from lang2sql.integrations.llm.bedrock_ import BedrockLLM
-
- return BedrockLLM(
- model=f["model"],
- aws_access_key_id=f.get("aws_access_key_id"),
- aws_secret_access_key=f.get("aws_secret_access_key"),
- region_name=f.get("region_name", "us-east-1"),
- )
-
- if provider == "ollama":
- from lang2sql.integrations.llm.ollama_ import OllamaLLM
-
- return OllamaLLM(
- model=f["model"],
- base_url=f.get("base_url", "http://localhost:11434"),
- )
-
- if provider == "huggingface":
- from lang2sql.integrations.llm.huggingface_ import HuggingFaceLLM
-
- return HuggingFaceLLM(
- repo_id=f.get("repo_id"),
- endpoint_url=f.get("endpoint_url"),
- api_token=f.get("api_token"),
- )
-
- raise ValueError(f"Unknown LLM provider: {provider!r}")
-
-
-def build_embedding(profile: "EmbeddingProfile") -> "EmbeddingPort":
- """EmbeddingProfile → EmbeddingPort 변환. Settings UI에서 호출한다."""
- f = profile.fields
- provider = profile.provider.lower()
-
- if provider == "openai":
- from lang2sql.integrations.embedding.openai_ import OpenAIEmbedding
-
- return OpenAIEmbedding(
- model=f.get("model", "text-embedding-3-small"),
- api_key=f.get("api_key"),
- )
-
- if provider == "azure":
- from lang2sql.integrations.embedding.azure_ import AzureOpenAIEmbedding
-
- return AzureOpenAIEmbedding(
- azure_deployment=f["azure_deployment"],
- azure_endpoint=f["azure_endpoint"],
- api_version=f.get("api_version", "2023-09-15-preview"),
- api_key=f.get("api_key"),
- )
-
- if provider == "ollama":
- from lang2sql.integrations.embedding.ollama_ import OllamaEmbedding
-
- return OllamaEmbedding(
- model=f.get("model", "nomic-embed-text"),
- base_url=f.get("base_url", "http://localhost:11434"),
- )
-
- if provider == "bedrock":
- from lang2sql.integrations.embedding.bedrock_ import BedrockEmbedding
-
- return BedrockEmbedding(
- model_id=f.get("model_id", "amazon.titan-embed-text-v2:0"),
- aws_access_key_id=f.get("aws_access_key_id"),
- aws_secret_access_key=f.get("aws_secret_access_key"),
- region_name=f.get("region_name", "us-east-1"),
- )
-
- if provider == "gemini":
- from lang2sql.integrations.embedding.gemini_ import GeminiEmbedding
-
- return GeminiEmbedding(
- model=f.get("model", "models/embedding-001"),
- api_key=f.get("api_key"),
- )
-
- if provider == "huggingface":
- from lang2sql.integrations.embedding.huggingface_ import HuggingFaceEmbedding
-
- return HuggingFaceEmbedding(
- model=f.get("model", f.get("repo_id", "")),
- )
-
- raise ValueError(f"Unknown embedding provider: {provider!r}")
diff --git a/interface/core/result_renderer.py b/interface/core/result_renderer.py
deleted file mode 100644
index 69e3950..0000000
--- a/interface/core/result_renderer.py
+++ /dev/null
@@ -1,209 +0,0 @@
-"""
-Lang2SQL 결과 표시 모듈.
-
-이 모듈은 LLM이 생성한 SQL 쿼리 및 결과 데이터를
-Streamlit UI를 통해 다양한 형태(쿼리, 표, 차트, 설명 등)로 표시합니다.
-토큰 사용량, 문서 적합성 평가, 재해석된 질문 등도 함께 확인할 수 있습니다.
-"""
-
-import pandas as pd
-import streamlit as st
-from langchain_core.messages import AIMessage
-
-from infra.observability.token_usage import TokenUtils
-from utils.databases import DatabaseFactory
-from utils.llm.llm_response_parser import LLMResponseParser
-from utils.visualization.display_chart import DisplayChart
-
-
-def display_result(res: dict) -> None:
- """Lang2SQL 실행 결과를 Streamlit UI로 출력합니다.
-
- Args:
- res (dict): Lang2SQL 실행 결과를 담은 딕셔너리.
- - generated_query (AIMessage | str): LLM이 생성한 SQL 쿼리
- - messages (list): LLM 입력/출력 메시지 목록
- - question_gate_result (dict, optional): 질문 게이트 결과
- - document_suitability (dict, optional): 문서 적합성 평가 결과
- - searched_tables (list, optional): 검색된 테이블 목록
-
- 표시 항목:
- - SQL 쿼리 및 실행 결과
- - 결과 설명 및 재해석된 질문
- - 문서 적합성 평가 및 질문 게이트 결과
- - 토큰 사용량 요약
- - 쿼리 결과 표 및 차트
- """
-
- def should_show(_key: str) -> bool:
- return st.session_state.get(_key, True)
-
- has_query = bool(res.get("generated_query"))
- show_sql_section = has_query and should_show("show_sql")
- show_result_desc = has_query and should_show("show_result_description")
- show_reinterpreted = has_query and should_show("show_question_reinterpreted_by_ai")
- show_gate_result = should_show("show_question_gate_result")
- show_doc_suitability = should_show("show_document_suitability")
- show_table_section = has_query and should_show("show_table")
- show_chart_section = has_query and should_show("show_chart")
-
- if show_gate_result and ("question_gate_result" in res):
- st.markdown("---")
- st.markdown("**Question Gate 결과:**")
- st.json(res.get("question_gate_result", {}))
-
- if show_doc_suitability and ("document_suitability" in res):
- st.markdown("---")
- st.markdown("**문서 적합성 평가:**")
- ds = res.get("document_suitability")
- if isinstance(ds, dict) and ds:
- rows = [
- {
- "table": t,
- "score": float(info.get("score", -1)),
- "matched_columns": ", ".join(info.get("matched_columns", [])),
- "missing_entities": ", ".join(info.get("missing_entities", [])),
- "reason": info.get("reason", ""),
- }
- for t, info in ds.items()
- if isinstance(info, dict)
- ]
- st.dataframe(rows, use_container_width=True)
- else:
- st.info("문서 적합성 평가 결과가 비어 있습니다.")
-
- if should_show("show_token_usage"):
- st.markdown("---")
- token_summary = TokenUtils.get_token_usage_summary(data=res["messages"])
- st.write("**토큰 사용량:**")
- st.markdown(f"""
- - Input tokens: `{token_summary['input_tokens']}`
- - Output tokens: `{token_summary['output_tokens']}`
- - Total tokens: `{token_summary['total_tokens']}`
- """)
-
- if show_sql_section:
- st.markdown("---")
- generated_query = res.get("generated_query")
- if generated_query:
- query_text = (
- generated_query.content
- if isinstance(generated_query, AIMessage)
- else str(generated_query)
- )
- try:
- sql = LLMResponseParser.extract_sql(query_text)
- st.markdown("**생성된 SQL 쿼리:**")
- st.code(sql, language="sql")
- except ValueError:
- st.warning("SQL 블록을 추출할 수 없습니다.")
- st.text(query_text)
- interpretation = LLMResponseParser.extract_interpretation(query_text)
- if interpretation:
- st.markdown("**결과 해석:**")
- st.code(interpretation)
- else:
- st.warning("쿼리 텍스트가 문자열이 아닙니다.")
- st.text(str(query_text))
-
- if show_result_desc and res.get("messages"):
- st.markdown("---")
- st.markdown("**결과 설명:**")
- result_message = res["messages"][-1].content
-
- if isinstance(result_message, str):
- try:
- sql = LLMResponseParser.extract_sql(result_message)
- st.code(sql, language="sql")
- except ValueError:
- st.warning("SQL 블록을 추출할 수 없습니다.")
- st.text(result_message)
-
- interpretation = LLMResponseParser.extract_interpretation(result_message)
- if interpretation:
- st.code(interpretation, language="plaintext")
- else:
- st.warning("결과 메시지가 문자열이 아닙니다.")
- st.text(str(result_message))
-
- if show_reinterpreted and res.get("messages"):
- st.markdown("---")
- st.markdown("**AI가 재해석한 사용자 질문:**")
- try:
- if len(res["messages"]) > 1:
- candidate = res["messages"][-2]
- question_text = (
- candidate.content
- if hasattr(candidate, "content")
- else str(candidate)
- )
- else:
- question_text = res["messages"][0].content
- except Exception:
- question_text = str(res["messages"][0].content)
- st.code(question_text)
-
- if should_show("show_referenced_tables"):
- st.markdown("---")
- st.markdown("**참고한 테이블 목록:**")
- st.write(res.get("searched_tables", []))
-
- if not has_query:
- st.info("QUERY_MAKER 없이 실행되었습니다. 검색된 테이블 정보만 표시합니다.")
-
- if show_table_section or show_chart_section:
- database = DatabaseFactory.get_connector()
- df = pd.DataFrame()
- try:
- sql_raw = (
- res["generated_query"].content
- if isinstance(res["generated_query"], AIMessage)
- else str(res["generated_query"])
- )
- if isinstance(sql_raw, str):
- sql = LLMResponseParser.extract_sql(sql_raw)
- df = database.run_sql(sql)
- else:
- st.error("SQL 원본이 문자열이 아닙니다.")
- except Exception as e:
- st.markdown("---")
- st.error(f"쿼리 실행 중 오류 발생: {e}")
- df = pd.DataFrame()
-
- if not df.empty and show_table_section:
- st.markdown("---")
- st.markdown("**쿼리 실행 결과:**")
- try:
- st.dataframe(df.head(10) if len(df) > 10 else df)
- except Exception as e:
- st.error(f"결과 테이블 생성 중 오류 발생: {e}")
-
- if df is not None and show_chart_section:
- st.markdown("---")
- try:
- st.markdown("**쿼리 결과 시각화:**")
- try:
- if len(res["messages"]) > 1:
- candidate = res["messages"][-2]
- chart_question = (
- candidate.content
- if hasattr(candidate, "content")
- else str(candidate)
- )
- else:
- chart_question = res["messages"][0].content
- except Exception:
- chart_question = str(res["messages"][0].content)
-
- display_code = DisplayChart(
- question=chart_question,
- sql=sql,
- df_metadata=f"Running df.dtypes gives:\n{df.dtypes}",
- )
- # plotly_code 변수도 따로 보관할 필요 없이 바로 그려도 됩니다
- fig = display_code.get_plotly_figure(
- plotly_code=display_code.generate_plotly_code(), df=df
- )
- st.plotly_chart(fig)
- except Exception as e:
- st.error(f"차트 생성 중 오류 발생: {e}")
diff --git a/interface/core/session_utils.py b/interface/core/session_utils.py
deleted file mode 100644
index fbabb2b..0000000
--- a/interface/core/session_utils.py
+++ /dev/null
@@ -1,16 +0,0 @@
-"""Streamlit 세션 상태 유틸리티 모듈."""
-
-
-def init_pipeline(use_enriched: bool) -> str:
- """파이프라인 타입을 세션 상태에 기록한다.
-
- Args:
- use_enriched: True이면 EnrichedNL2SQL, False이면 BaselineNL2SQL.
-
- Returns:
- str: "확장된" 또는 "기본".
- """
- import streamlit as st
-
- st.session_state["use_enriched"] = use_enriched
- return "확장된" if use_enriched else "기본"
diff --git a/interface/pages_config.py b/interface/pages_config.py
deleted file mode 100644
index 362f855..0000000
--- a/interface/pages_config.py
+++ /dev/null
@@ -1,22 +0,0 @@
-"""
-Streamlit 애플리케이션 페이지 설정 모듈.
-
-각 페이지의 경로와 제목을 정의하여 내비게이션에 사용합니다.
-
-Attributes:
- PAGES (list): Streamlit Page 객체 리스트.
- - 홈 페이지
- - Lang2SQL 페이지
- - 그래프 빌더 페이지
- - ChatBot 페이지
- - 설정 페이지
-"""
-
-import streamlit as st
-
-PAGES = [
- st.Page("app_pages/home.py", title="🏠 홈"),
- st.Page("app_pages/lang2sql.py", title="🔍 Lang2SQL"),
- st.Page("app_pages/chatbot.py", title="🤖 ChatBot"),
- st.Page("app_pages/settings.py", title="⚙️ 설정"),
-]
diff --git a/interface/streamlit_app.py b/interface/streamlit_app.py
deleted file mode 100644
index 8e74d8e..0000000
--- a/interface/streamlit_app.py
+++ /dev/null
@@ -1,42 +0,0 @@
-"""
-Streamlit 애플리케이션 메인 실행 모듈.
-
-Lang2SQL 데이터 분석 도구의 내비게이션을 초기화하고 실행합니다.
-"""
-
-import streamlit as st
-
-from interface.pages_config import PAGES
-
-
-def configure_app() -> None:
- """앱 전역 설정 초기화.
-
- Streamlit 애플리케이션의 제목, 아이콘, 레이아웃, 사이드바 상태를 설정합니다.
-
- Returns:
- None
- """
- st.set_page_config(
- page_title="Lang2SQL 데이터 분석 도구",
- page_icon="🔎",
- layout="wide",
- initial_sidebar_state="expanded",
- )
-
-
-def main() -> None:
- """애플리케이션 진입점.
-
- 전역 설정을 초기화하고, 정의된 페이지 내비게이션을 실행합니다.
-
- Returns:
- None
- """
- configure_app()
- pg = st.navigation(PAGES)
- pg.run()
-
-
-if __name__ == "__main__":
- main()
diff --git a/prompt/template_loader.py b/prompt/template_loader.py
deleted file mode 100644
index 1e3a554..0000000
--- a/prompt/template_loader.py
+++ /dev/null
@@ -1,20 +0,0 @@
-"""
-이 모듈은 프롬프트 템플릿을 로드하는 기능을 제공합니다.
-- 프롬프트 템플릿은 마크다운 파일로 관리되고 있으며, 환경변수에서 템플릿 디렉토리를 가져오거나, 없으면 현재 파일 위치 기준으로 설정합니다.
-"""
-
-import os
-
-
-def get_prompt_template(prompt_name: str) -> str:
- # 환경변수에서 템플릿 디렉토리를 가져오거나, 없으면 현재 파일 위치 기준으로 설정
- templates_dir = os.environ.get("PROMPT_TEMPLATES_DIR", os.path.dirname(__file__))
-
- try:
- template_path = os.path.join(templates_dir, f"{prompt_name}.md")
- with open(template_path, "r", encoding="utf-8") as f:
- template = f.read()
- except FileNotFoundError:
- raise FileNotFoundError(f"경고: '{prompt_name}.md' 파일을 찾을 수 없습니다.")
-
- return template
diff --git a/pyproject.toml b/pyproject.toml
index 62dd941..77062ec 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -4,82 +4,39 @@ build-backend = "hatchling.build"
[project]
name = "lang2sql"
-dynamic = ["version"]
-description = "Lang2SQL - Query Generator for Data Warehouse"
+version = "0.4.0.dev0"
+description = "Lang2SQL — document-learning, read-only SQL analytics agent (v4.1 rebuild)"
readme = "README.md"
-requires-python = ">=3.9"
+requires-python = ">=3.10"
authors = [
- { name = "ehddnr301", email = "dy95032@gmail.com" },
+ { name = "brain-crew", email = "ryan@brain-crew.com" },
]
dependencies = [
- "langgraph==0.2.62",
- "datahub==0.999.1",
- "langchain==0.3.14",
- "langchain-community==0.3.14",
- "openai==1.59.8",
- "langchain-openai==0.3.0",
- "streamlit==1.41.1",
- "python-dotenv==1.0.1",
- "faiss-cpu==1.10.0",
- "transformers==4.51.2",
- "langchain-aws>=0.2.21,<0.3.0",
- "langchain-google-genai>=2.1.3,<3.0.0",
- "langchain-ollama>=0.3.2,<0.4.0",
- "langchain-huggingface>=0.1.2,<0.2.0",
- "clickhouse_driver>=0.2.9,<0.3.0",
- "plotly",
- "matplotlib",
- "ipython",
- "kaleido",
- "numpy<2.0",
- "snowflake-connector-python>=3.15.0,<4.0.0",
- "databricks-sql-connector>=4.0.3,<5.0.0",
- "oracledb>=3.1.0,<4.0.0",
- "mysql-connector-python>=9.3.0,<10.0.0",
- "duckdb>=1.2.2,<2.0.0",
- "psycopg2-binary>=2.9.10,<3.0.0",
- "psycopg[binary]>=3.2,<4.0",
- "pyodbc>=5.1.0,<6.0.0",
- "crate>=0.29.0,<1.0.0",
- "pyhive>=0.6.6,<1.0.0",
- "google-cloud-bigquery>=3.20.1,<4.0.0",
- "pgvector==0.3.6",
- "langchain-postgres==0.0.15",
- "trino>=0.329.0,<1.0.0",
- "anthropic>=0.20.0",
- "sqlalchemy>=2.0",
+ "discord.py>=2.3,<3.0", # Phase 1 frontend transport
+ "cryptography>=42.0", # EncryptedSecrets at-rest encryption
]
+[project.optional-dependencies]
+# Real outbound adapters (V1 ships urllib OpenAI + stub PG; these enable v1.5 swaps)
+postgres = ["psycopg[binary]>=3.2,<4.0"]
+
[project.scripts]
-lang2sql = "cli.__init__:cli"
+lang2sql = "lang2sql.frontends.cli.app:main"
+lang2sql-bot = "lang2sql.frontends.discord.bot:run"
[project.urls]
Homepage = "https://github.com/CausalInferenceLab/Lang2SQL"
Repository = "https://github.com/CausalInferenceLab/Lang2SQL"
-[tool.hatch.version]
-path = "version.py"
-
-[tool.hatch.build]
-include = [
- "version.py",
- "prompt/*.md",
- "src/lang2sql/**",
-]
-
[tool.hatch.build.targets.wheel]
-packages = [
- "cli",
- "interface",
- "engine",
- "infra",
- "prompt",
- "utils",
- "src/lang2sql"
-]
+packages = ["src/lang2sql"]
[tool.uv]
dev-dependencies = [
- "pre_commit==4.1.0",
"pytest>=8.3.5",
+ "pytest-asyncio>=0.24",
]
+
+[tool.pytest.ini_options]
+pythonpath = ["src"]
+testpaths = ["tests"]
diff --git a/src/lang2sql/__init__.py b/src/lang2sql/__init__.py
index 66811de..02ec0f7 100644
--- a/src/lang2sql/__init__.py
+++ b/src/lang2sql/__init__.py
@@ -1,155 +1,3 @@
-from .factory import (
- build_db_from_env,
- build_embedding_from_env,
- build_explorer_from_url,
- build_llm_from_env,
-)
-from .components.enrichment.context_enricher import ContextEnricher
-from .components.enrichment.question_profiler import QuestionProfiler
-from .components.execution.sql_executor import SQLExecutor
-from .components.gate.question_gate import QuestionGate
-from .components.gate.table_suitability import TableSuitabilityEvaluator
-from .components.generation.sql_generator import SQLGenerator
-from .components.loaders.directory_ import DirectoryLoader
-from .components.loaders.markdown_ import MarkdownLoader
-from .components.loaders.plaintext_ import PlainTextLoader
-from .components.retrieval.chunker import (
- CatalogChunker,
- DocumentChunkerPort,
- RecursiveCharacterChunker,
-)
-from .components.retrieval.hybrid import HybridRetriever
-from .components.retrieval.keyword import KeywordRetriever
-from .components.retrieval.vector import VectorRetriever
-from .core.catalog import (
- CatalogEntry,
- GateResult,
- IndexedChunk,
- QuestionProfile,
- RetrievalResult,
- TableScore,
- TextDocument,
-)
-from .core.exceptions import ComponentError, IntegrationMissingError, Lang2SQLError
-from .core.hooks import MemoryHook, NullHook, TraceHook
-from .core.ports import (
- CatalogLoaderPort,
- DBExplorerPort,
- DBPort,
- DocumentLoaderPort,
- EmbeddingPort,
- LLMPort,
- VectorStorePort,
-)
-from .integrations.db.sqlalchemy_ import SQLAlchemyExplorer
-from .flows.enriched_nl2sql import EnrichedNL2SQL
-from .flows.hybrid import HybridNL2SQL
-from .flows.nl2sql import BaselineNL2SQL
-from .integrations.embedding.azure_ import AzureOpenAIEmbedding
-from .integrations.embedding.bedrock_ import BedrockEmbedding
-from .integrations.embedding.gemini_ import GeminiEmbedding
-from .integrations.embedding.huggingface_ import HuggingFaceEmbedding
-from .integrations.embedding.ollama_ import OllamaEmbedding
-from .integrations.llm.azure_ import AzureOpenAILLM
-from .integrations.llm.bedrock_ import BedrockLLM
-from .integrations.llm.gemini_ import GeminiLLM
-from .integrations.llm.huggingface_ import HuggingFaceLLM
-from .integrations.llm.ollama_ import OllamaLLM
+"""Lang2SQL — document-learning analytics agent (v4.1 rebuild)."""
-__all__ = [
- # Data types
- "CatalogEntry",
- "TextDocument",
- "IndexedChunk",
- "RetrievalResult",
- # Domain types (Phase 4)
- "GateResult",
- "QuestionProfile",
- "TableScore",
- # Ports (protocols)
- "LLMPort",
- "DBPort",
- "DBExplorerPort",
- "EmbeddingPort",
- "VectorStorePort",
- "DocumentLoaderPort",
- "CatalogLoaderPort",
- # Components — retrieval
- "KeywordRetriever",
- "VectorRetriever",
- "HybridRetriever",
- "DocumentChunkerPort",
- "CatalogChunker",
- "RecursiveCharacterChunker",
- # Components — generation & execution
- "SQLGenerator",
- "SQLExecutor",
- # Components — gate (Phase 4)
- "QuestionGate",
- "TableSuitabilityEvaluator",
- # Components — enrichment (Phase 4)
- "QuestionProfiler",
- "ContextEnricher",
- # Components — loaders
- "MarkdownLoader",
- "PlainTextLoader",
- "DirectoryLoader",
- # Flows
- "BaselineNL2SQL",
- "HybridNL2SQL",
- "EnrichedNL2SQL",
- # Hooks
- "TraceHook",
- "MemoryHook",
- "NullHook",
- # Exceptions
- "Lang2SQLError",
- "ComponentError",
- "IntegrationMissingError",
- # Vector store backends
- "FAISSVectorStore",
- "PGVectorStore",
- # LLM integrations (Phase 1)
- "AzureOpenAILLM",
- "BedrockLLM",
- "GeminiLLM",
- "HuggingFaceLLM",
- "OllamaLLM",
- # Embedding integrations (Phase 2)
- "AzureOpenAIEmbedding",
- "BedrockEmbedding",
- "GeminiEmbedding",
- "HuggingFaceEmbedding",
- "OllamaEmbedding",
- # Catalog integrations (Phase 3)
- "DataHubCatalogLoader",
- # DB Explorer (Phase A1)
- "SQLAlchemyExplorer",
- # Factory (Phase 6)
- "build_llm_from_env",
- "build_embedding_from_env",
- "build_db_from_env",
- "build_explorer_from_url",
-]
-
-# ---------------------------------------------------------------------------
-# Lazy imports (PEP 562) — optional dependencies that have import side-effects
-# (e.g. faiss prints INFO logs on import) or are rarely needed at startup.
-# ---------------------------------------------------------------------------
-_LAZY_IMPORTS: dict[str, tuple[str, str]] = {
- "DataHubCatalogLoader": (".integrations.catalog.datahub_", "DataHubCatalogLoader"),
- "FAISSVectorStore": (".integrations.vectorstore.faiss_", "FAISSVectorStore"),
- "PGVectorStore": (".integrations.vectorstore.pgvector_", "PGVectorStore"),
-}
-
-
-def __getattr__(name: str):
- if name in _LAZY_IMPORTS:
- module_path, attr = _LAZY_IMPORTS[name]
- import importlib
-
- obj = getattr(importlib.import_module(module_path, package=__name__), attr)
- # Cache in module globals so subsequent accesses skip __getattr__
- globals()[name] = obj
- return obj
- raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
+__version__ = "0.4.0.dev0"
diff --git a/src/lang2sql/adapters/__init__.py b/src/lang2sql/adapters/__init__.py
new file mode 100644
index 0000000..85d7b8a
--- /dev/null
+++ b/src/lang2sql/adapters/__init__.py
@@ -0,0 +1,14 @@
+"""Outbound adapters — concrete impls of the ``core.ports`` Protocols (v4.1 §2.1).
+
+stdlib only in V1: OpenAI via ``urllib``, storage via ``sqlite3``, the Postgres
+explorer a canned stub until psycopg lands in v1.5.
+"""
+
+from __future__ import annotations
+
+from .db.postgres_explorer import PostgresExplorer
+from .llm.fake import FakeLLM
+from .llm.openai_ import OpenAILLM
+from .storage.sqlite_store import SqliteStore
+
+__all__ = ["FakeLLM", "OpenAILLM", "PostgresExplorer", "SqliteStore"]
diff --git a/src/lang2sql/adapters/db/__init__.py b/src/lang2sql/adapters/db/__init__.py
new file mode 100644
index 0000000..99cfe06
--- /dev/null
+++ b/src/lang2sql/adapters/db/__init__.py
@@ -0,0 +1,7 @@
+"""DB adapters — :class:`ExplorerPort` impls."""
+
+from __future__ import annotations
+
+from .postgres_explorer import PostgresExplorer
+
+__all__ = ["PostgresExplorer"]
diff --git a/src/lang2sql/adapters/db/postgres_explorer.py b/src/lang2sql/adapters/db/postgres_explorer.py
new file mode 100644
index 0000000..1aaa33f
--- /dev/null
+++ b/src/lang2sql/adapters/db/postgres_explorer.py
@@ -0,0 +1,84 @@
+"""PostgresExplorer — V1 stub; real psycopg-backed explorer lands in v1.5.
+
+psycopg is not stdlib, so V1 does not connect to anything. The ctor stores the
+DSN for the future real impl, and the read methods return small canned schema
+data so the agent loop and schema-aware tools can be exercised end-to-end.
+"""
+
+from __future__ import annotations
+
+from ...core.ports.explorer import Column, Table
+
+# Canned catalog — two tables a demo guild would plausibly have.
+_TABLES: dict[str, Table] = {
+ "public.orders": Table(
+ name="orders",
+ schema="public",
+ description="One row per customer order.",
+ columns=[
+ Column("id", "integer", nullable=False, description="Primary key."),
+ Column("amount", "numeric", nullable=False, description="Order total."),
+ Column("status", "text", description="pending | paid | shipped | cancelled."),
+ Column("created_at", "timestamptz", nullable=False),
+ ],
+ ),
+ "public.users": Table(
+ name="users",
+ schema="public",
+ description="Registered users.",
+ columns=[
+ Column("id", "integer", nullable=False, description="Primary key."),
+ Column("email", "text", nullable=False),
+ Column("created_at", "timestamptz", nullable=False),
+ ],
+ ),
+}
+
+_SAMPLES: dict[str, list[dict]] = {
+ "public.orders": [
+ {"id": 1, "amount": 49.90, "status": "paid", "created_at": "2026-05-01T10:00:00Z"},
+ {"id": 2, "amount": 12.00, "status": "pending", "created_at": "2026-05-02T14:30:00Z"},
+ ],
+ "public.users": [
+ {"id": 1, "email": "alice@example.com", "created_at": "2026-04-20T08:00:00Z"},
+ {"id": 2, "email": "bob@example.com", "created_at": "2026-04-21T09:15:00Z"},
+ ],
+}
+
+
+def _resolve_key(name: str) -> str:
+ """Map a bare or qualified table name onto a canned-catalog key."""
+ if name in _TABLES:
+ return name
+ return f"public.{name}"
+
+
+class PostgresExplorer:
+ """Read-only schema introspection — canned data in V1."""
+
+ def __init__(self, dsn: str) -> None:
+ self.dsn = dsn # stored for the v1.5 psycopg impl; no connection in V1.
+
+ async def list_tables(self) -> list[Table]:
+ return list(_TABLES.values())
+
+ async def describe_table(self, name: str) -> Table:
+ key = _resolve_key(name)
+ table = _TABLES.get(key)
+ if table is None:
+ raise KeyError(f"unknown table: {name}")
+ return table
+
+ async def sample_rows(self, name: str, limit: int = 5) -> list[dict]:
+ key = _resolve_key(name)
+ return list(_SAMPLES.get(key, []))[:limit]
+
+ async def execute(self, sql: str, limit: int = 1000) -> list[dict]:
+ # V1 stub: no real query engine. Echo canned rows that match whichever
+ # known table the SQL mentions, else a generic single-row result.
+ lowered = sql.lower()
+ for key, rows in _SAMPLES.items():
+ table = key.split(".", 1)[-1]
+ if table in lowered:
+ return list(rows)[:limit]
+ return [{"result": 1}][:limit]
diff --git a/src/lang2sql/adapters/llm/__init__.py b/src/lang2sql/adapters/llm/__init__.py
new file mode 100644
index 0000000..bcf0ff0
--- /dev/null
+++ b/src/lang2sql/adapters/llm/__init__.py
@@ -0,0 +1,8 @@
+"""LLM adapters — :class:`LLMPort` impls."""
+
+from __future__ import annotations
+
+from .fake import FakeLLM
+from .openai_ import OpenAILLM
+
+__all__ = ["FakeLLM", "OpenAILLM"]
diff --git a/src/lang2sql/adapters/llm/fake.py b/src/lang2sql/adapters/llm/fake.py
new file mode 100644
index 0000000..59dd7a0
--- /dev/null
+++ b/src/lang2sql/adapters/llm/fake.py
@@ -0,0 +1,60 @@
+"""FakeLLM — a scripted LLMPort for skeleton validation (no network).
+
+It drives one full tool cycle so the agent loop can be exercised end-to-end
+before the real OpenAI adapter exists: on the first turn it requests the first
+available tool; once it sees a tool result it produces a final answer quoting
+that result. Replaced by ``adapters/llm/openai_.py`` in Week 2.
+"""
+
+from __future__ import annotations
+
+import json
+from typing import Sequence
+
+from ...core.types import Completion, Message, Role, ToolCall, ToolSpec
+
+
+class FakeLLM:
+ """Deterministic stand-in implementing :class:`LLMPort`."""
+
+ def __init__(self) -> None:
+ self._counter = 0
+
+ async def complete(
+ self,
+ messages: Sequence[Message],
+ tools: Sequence[ToolSpec] = (),
+ ) -> Completion:
+ last = messages[-1] if messages else None
+
+ # Saw a tool result → wrap up.
+ if last is not None and last.role == Role.TOOL:
+ return Completion(
+ content=f"Done. Tool reported: {last.content}",
+ finish_reason="stop",
+ )
+
+ # First pass with a tool available → call it.
+ if tools:
+ self._counter += 1
+ spec = tools[0]
+ return Completion(
+ tool_calls=[
+ ToolCall(
+ id=f"call_{self._counter}",
+ name=spec.name,
+ arguments=json.loads(_demo_args(spec)),
+ )
+ ],
+ finish_reason="tool_calls",
+ )
+
+ # No tools at all → just answer.
+ return Completion(content="(no tools available) Hello from FakeLLM.", finish_reason="stop")
+
+
+def _demo_args(spec: ToolSpec) -> str:
+ """Best-effort sample args from a tool's JSON-Schema, as a JSON string."""
+ props = (spec.parameters or {}).get("properties", {})
+ sample = {name: "demo" for name in props}
+ return json.dumps(sample)
diff --git a/src/lang2sql/adapters/llm/openai_.py b/src/lang2sql/adapters/llm/openai_.py
new file mode 100644
index 0000000..74b5b02
--- /dev/null
+++ b/src/lang2sql/adapters/llm/openai_.py
@@ -0,0 +1,147 @@
+"""OpenAILLM — the V1 :class:`LLMPort`, talking OpenAI chat/completions.
+
+stdlib only: HTTP via :mod:`urllib.request`, JSON via :mod:`json`. No openai
+SDK. The adapter's whole job is translation — core :class:`Message`/:class:`ToolSpec`
+in, OpenAI wire dict out, OpenAI response back into a core :class:`Completion`.
+The loop never sees an OpenAI shape.
+
+Construction is offline-safe: a missing key only bites when :meth:`complete` is
+actually called, so importing/wiring this in a no-key environment is fine.
+"""
+
+from __future__ import annotations
+
+import json
+import os
+import urllib.error
+import urllib.request
+from typing import Any, Sequence
+
+from ...core.types import Completion, Message, Role, ToolCall, ToolSpec
+
+_DEFAULT_URL = "https://api.openai.com/v1/chat/completions"
+
+
+class OpenAILLM:
+ """Tool-calling chat completion backed by OpenAI's REST API."""
+
+ def __init__(
+ self,
+ model: str = "gpt-4.1-mini",
+ api_key: str | None = None,
+ *,
+ base_url: str = _DEFAULT_URL,
+ timeout: float = 60.0,
+ ) -> None:
+ self.model = model
+ # Resolve lazily-ish: read env now, but tolerate absence until complete().
+ self._api_key = api_key if api_key is not None else os.environ.get("OPENAI_API_KEY")
+ self._base_url = base_url
+ self._timeout = timeout
+
+ async def complete(
+ self,
+ messages: Sequence[Message],
+ tools: Sequence[ToolSpec] = (),
+ ) -> Completion:
+ if not self._api_key:
+ raise RuntimeError("OPENAI_API_KEY not set")
+
+ payload: dict[str, Any] = {
+ "model": self.model,
+ "messages": [_encode_message(m) for m in messages],
+ }
+ if tools:
+ payload["tools"] = [_encode_tool(t) for t in tools]
+
+ raw = self._post(payload)
+ return _decode_completion(raw)
+
+ def _post(self, payload: dict[str, Any]) -> dict[str, Any]:
+ body = json.dumps(payload).encode("utf-8")
+ req = urllib.request.Request(
+ self._base_url,
+ data=body,
+ method="POST",
+ headers={
+ "Authorization": f"Bearer {self._api_key}",
+ "Content-Type": "application/json",
+ },
+ )
+ try:
+ with urllib.request.urlopen(req, timeout=self._timeout) as resp:
+ text = resp.read().decode("utf-8")
+ except urllib.error.HTTPError as exc:
+ detail = exc.read().decode("utf-8", "replace") if exc.fp else ""
+ raise RuntimeError(f"OpenAI HTTP {exc.code}: {detail}") from exc
+ except urllib.error.URLError as exc:
+ raise RuntimeError(f"OpenAI request failed: {exc.reason}") from exc
+
+ try:
+ return json.loads(text)
+ except (ValueError, TypeError) as exc:
+ raise RuntimeError(f"OpenAI returned non-JSON response: {text[:200]!r}") from exc
+
+
+def _encode_message(m: Message) -> dict[str, Any]:
+ """Core :class:`Message` → an OpenAI chat message dict."""
+ out: dict[str, Any] = {"role": m.role.value}
+ # OpenAI wants content present (may be null when only tool_calls are set).
+ out["content"] = m.content or None
+ if m.role == Role.TOOL:
+ out["tool_call_id"] = m.tool_call_id
+ if m.name:
+ out["name"] = m.name
+ if m.tool_calls:
+ out["tool_calls"] = [
+ {
+ "id": tc.id,
+ "type": "function",
+ "function": {
+ "name": tc.name,
+ "arguments": json.dumps(tc.arguments),
+ },
+ }
+ for tc in m.tool_calls
+ ]
+ return out
+
+
+def _encode_tool(t: ToolSpec) -> dict[str, Any]:
+ """Core :class:`ToolSpec` → an OpenAI tool dict."""
+ return {
+ "type": "function",
+ "function": {
+ "name": t.name,
+ "description": t.description,
+ "parameters": t.parameters or {"type": "object", "properties": {}},
+ },
+ }
+
+
+def _decode_completion(raw: dict[str, Any]) -> Completion:
+ """OpenAI response JSON → core :class:`Completion`."""
+ try:
+ choice = raw["choices"][0]
+ msg = choice["message"]
+ except (KeyError, IndexError, TypeError) as exc:
+ raise RuntimeError(f"unexpected OpenAI response shape: {raw!r}") from exc
+
+ tool_calls: list[ToolCall] = []
+ for tc in msg.get("tool_calls") or []:
+ fn = tc.get("function", {})
+ raw_args = fn.get("arguments", "") or "{}"
+ try:
+ args = json.loads(raw_args)
+ except (ValueError, TypeError):
+ # Model emitted malformed JSON args; surface raw so the tool can complain.
+ args = {"__raw__": raw_args}
+ tool_calls.append(
+ ToolCall(id=tc.get("id", ""), name=fn.get("name", ""), arguments=args)
+ )
+
+ return Completion(
+ content=msg.get("content") or "",
+ tool_calls=tool_calls,
+ finish_reason=choice.get("finish_reason"),
+ )
diff --git a/src/lang2sql/adapters/storage/__init__.py b/src/lang2sql/adapters/storage/__init__.py
new file mode 100644
index 0000000..e2c6269
--- /dev/null
+++ b/src/lang2sql/adapters/storage/__init__.py
@@ -0,0 +1,7 @@
+"""Storage adapters — :class:`AuditPort` + :class:`SessionStorePort` impls."""
+
+from __future__ import annotations
+
+from .sqlite_store import SqliteStore
+
+__all__ = ["SqliteStore"]
diff --git a/src/lang2sql/adapters/storage/sqlite_semantic.py b/src/lang2sql/adapters/storage/sqlite_semantic.py
new file mode 100644
index 0000000..8ee941c
--- /dev/null
+++ b/src/lang2sql/adapters/storage/sqlite_semantic.py
@@ -0,0 +1,104 @@
+"""SqliteSemanticStore — durable backing for the semantic layer (★④).
+
+Drop-in replacement for the in-memory :class:`~lang2sql.semantic.store.SemanticStore`:
+same ``add(scope, entry)`` / ``entries_at(scope)`` surface, but rows live in a
+stdlib :mod:`sqlite3` table so definitions survive a process restart. Wiring
+``ScopeResolver(store=SqliteSemanticStore(path))`` is all federation needs to
+become persistent — the resolver and merge logic stay untouched.
+
+Like the rest of V1's sqlite usage this is synchronous and inline; the store
+itself exposes the plain ``add``/``entries_at`` shape (no ``async``), matching
+:class:`SemanticStore` so it substitutes cleanly under :class:`ScopeResolver`.
+"""
+
+from __future__ import annotations
+
+import sqlite3
+
+from ...core.identity import Scope
+from ...semantic.types import SemanticEntry, SemanticKind
+
+
+class SqliteSemanticStore:
+ """Per-scope semantic entries persisted in a single sqlite table.
+
+ The ``(scope, name)`` pair is the primary key, mirroring the in-memory
+ store's "one entry per name within a scope" rule: re-adding a name at the
+ same scope replaces the prior definition.
+ """
+
+ def __init__(self, path: str = ":memory:") -> None:
+ self.path = path
+ self._conn = sqlite3.connect(path, check_same_thread=False)
+ self._conn.row_factory = sqlite3.Row
+ self._create_tables()
+
+ def _create_tables(self) -> None:
+ self._conn.executescript(
+ """
+ CREATE TABLE IF NOT EXISTS semantic_entries (
+ scope TEXT NOT NULL,
+ kind TEXT NOT NULL,
+ name TEXT NOT NULL,
+ definition TEXT NOT NULL,
+ applies_to TEXT NOT NULL DEFAULT '',
+ source_id TEXT NOT NULL DEFAULT '',
+ created_by TEXT NOT NULL DEFAULT '',
+ created_at TEXT NOT NULL DEFAULT '',
+ PRIMARY KEY (scope, name)
+ );
+ """
+ )
+ self._conn.commit()
+
+ def close(self) -> None:
+ self._conn.close()
+
+ def add(self, scope: Scope, entry: SemanticEntry) -> None:
+ """Store ``entry`` at ``scope``, replacing a same-named entry there."""
+ self._conn.execute(
+ "INSERT INTO semantic_entries "
+ "(scope, kind, name, definition, applies_to, source_id, created_by, created_at) "
+ "VALUES (?, ?, ?, ?, ?, ?, ?, ?) "
+ "ON CONFLICT(scope, name) DO UPDATE SET "
+ "kind = excluded.kind, definition = excluded.definition, "
+ "applies_to = excluded.applies_to, source_id = excluded.source_id, "
+ "created_by = excluded.created_by, created_at = excluded.created_at",
+ (
+ str(scope),
+ entry.kind.value,
+ entry.name,
+ entry.definition,
+ entry.applies_to,
+ entry.source_id,
+ entry.created_by,
+ entry.created_at,
+ ),
+ )
+ self._conn.commit()
+
+ def entries_at(self, scope: Scope) -> list[SemanticEntry]:
+ """Entries authored exactly at ``scope`` (no inheritance)."""
+ rows = self._conn.execute(
+ "SELECT kind, name, definition, applies_to, source_id, created_by, created_at "
+ "FROM semantic_entries WHERE scope = ? ORDER BY rowid",
+ (str(scope),),
+ ).fetchall()
+ return [_row_to_entry(r) for r in rows]
+
+
+def _row_to_entry(row: sqlite3.Row) -> SemanticEntry:
+ """Reconstruct a :class:`SemanticEntry` from a stored row.
+
+ ``created_at`` is passed through verbatim so the persisted timestamp is
+ preserved rather than regenerated by ``__post_init__``.
+ """
+ return SemanticEntry(
+ kind=SemanticKind(row["kind"]),
+ name=row["name"],
+ definition=row["definition"],
+ applies_to=row["applies_to"],
+ source_id=row["source_id"],
+ created_by=row["created_by"],
+ created_at=row["created_at"],
+ )
diff --git a/src/lang2sql/adapters/storage/sqlite_store.py b/src/lang2sql/adapters/storage/sqlite_store.py
new file mode 100644
index 0000000..99ce42d
--- /dev/null
+++ b/src/lang2sql/adapters/storage/sqlite_store.py
@@ -0,0 +1,187 @@
+"""SqliteStore — the real V1 persistence backend (stdlib :mod:`sqlite3`).
+
+One store, three roles:
+
+* :class:`AuditPort` — append-only ``audit`` table behind ``/audit me``.
+* :class:`SessionStorePort` — serialize/restore a :class:`Session` as JSON.
+* a generic key-value table the secrets adapter (tenancy) wraps.
+
+sqlite is synchronous; V1 just runs the calls inline inside the async methods,
+which is fine for the expected load. The connection uses
+``check_same_thread=False`` so it tolerates being touched from the event-loop
+thread pool.
+"""
+
+from __future__ import annotations
+
+import json
+import sqlite3
+import time
+from typing import Any
+
+from ...core.identity import Identity
+from ...core.ports.audit import AuditEvent
+from ...core.types import Message, Role, ToolCall
+from ...harness.session import Session
+
+
+class SqliteStore:
+ """Append-only audit + session + kv storage on one sqlite connection."""
+
+ def __init__(self, path: str = ":memory:") -> None:
+ self.path = path
+ self._conn = sqlite3.connect(path, check_same_thread=False)
+ self._conn.row_factory = sqlite3.Row
+ self._create_tables()
+
+ def _create_tables(self) -> None:
+ self._conn.executescript(
+ """
+ CREATE TABLE IF NOT EXISTS audit (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ actor TEXT NOT NULL,
+ action TEXT NOT NULL,
+ scope TEXT NOT NULL,
+ detail TEXT NOT NULL,
+ ts REAL NOT NULL
+ );
+ CREATE TABLE IF NOT EXISTS sessions (
+ key TEXT PRIMARY KEY,
+ data TEXT NOT NULL
+ );
+ CREATE TABLE IF NOT EXISTS kv (
+ scope TEXT NOT NULL,
+ key TEXT NOT NULL,
+ value TEXT NOT NULL,
+ PRIMARY KEY (scope, key)
+ );
+ """
+ )
+ self._conn.commit()
+
+ def close(self) -> None:
+ self._conn.close()
+
+ # -- AuditPort -------------------------------------------------------
+
+ async def record(self, event: AuditEvent) -> None:
+ ts = event.ts or time.time()
+ self._conn.execute(
+ "INSERT INTO audit (actor, action, scope, detail, ts) VALUES (?, ?, ?, ?, ?)",
+ (event.actor, event.action, event.scope, json.dumps(event.detail), ts),
+ )
+ self._conn.commit()
+
+ async def query(self, actor: str, limit: int = 20) -> list[AuditEvent]:
+ rows = self._conn.execute(
+ "SELECT actor, action, scope, detail, ts FROM audit "
+ "WHERE actor = ? ORDER BY id DESC LIMIT ?",
+ (actor, limit),
+ ).fetchall()
+ return [
+ AuditEvent(
+ actor=r["actor"],
+ action=r["action"],
+ scope=r["scope"],
+ detail=json.loads(r["detail"]),
+ ts=r["ts"],
+ )
+ for r in rows
+ ]
+
+ # -- SessionStorePort ------------------------------------------------
+
+ async def load(self, key: str) -> Session | None:
+ row = self._conn.execute(
+ "SELECT data FROM sessions WHERE key = ?", (key,)
+ ).fetchone()
+ if row is None:
+ return None
+ return _deserialize_session(json.loads(row["data"]))
+
+ async def save(self, key: str, session: Session) -> None:
+ data = json.dumps(_serialize_session(session))
+ self._conn.execute(
+ "INSERT INTO sessions (key, data) VALUES (?, ?) "
+ "ON CONFLICT(key) DO UPDATE SET data = excluded.data",
+ (key, data),
+ )
+ self._conn.commit()
+
+ # -- generic key-value (wrapped by the secrets adapter) --------------
+
+ def kv_get(self, scope: str, key: str) -> str | None:
+ row = self._conn.execute(
+ "SELECT value FROM kv WHERE scope = ? AND key = ?", (scope, key)
+ ).fetchone()
+ return row["value"] if row else None
+
+ def kv_set(self, scope: str, key: str, value: str) -> None:
+ self._conn.execute(
+ "INSERT INTO kv (scope, key, value) VALUES (?, ?, ?) "
+ "ON CONFLICT(scope, key) DO UPDATE SET value = excluded.value",
+ (scope, key, value),
+ )
+ self._conn.commit()
+
+ def kv_delete(self, scope: str, key: str) -> None:
+ self._conn.execute(
+ "DELETE FROM kv WHERE scope = ? AND key = ?", (scope, key)
+ )
+ self._conn.commit()
+
+
+# -- Session (de)serialization ------------------------------------------
+
+
+def _serialize_session(session: Session) -> dict[str, Any]:
+ ident = session.identity
+ return {
+ "identity": {
+ "user_id": ident.user_id,
+ "guild_id": ident.guild_id,
+ "channel_id": ident.channel_id,
+ "thread_id": ident.thread_id,
+ "is_admin": ident.is_admin,
+ },
+ "transcript": [_serialize_message(m) for m in session.transcript],
+ }
+
+
+def _deserialize_session(data: dict[str, Any]) -> Session:
+ ident_data = data["identity"]
+ identity = Identity(
+ user_id=ident_data["user_id"],
+ guild_id=ident_data.get("guild_id"),
+ channel_id=ident_data.get("channel_id"),
+ thread_id=ident_data.get("thread_id"),
+ is_admin=ident_data.get("is_admin", False),
+ )
+ transcript = [_deserialize_message(m) for m in data.get("transcript", [])]
+ return Session(identity=identity, transcript=transcript)
+
+
+def _serialize_message(m: Message) -> dict[str, Any]:
+ return {
+ "role": m.role.value,
+ "content": m.content,
+ "tool_calls": [
+ {"id": tc.id, "name": tc.name, "arguments": tc.arguments}
+ for tc in m.tool_calls
+ ],
+ "tool_call_id": m.tool_call_id,
+ "name": m.name,
+ }
+
+
+def _deserialize_message(data: dict[str, Any]) -> Message:
+ return Message(
+ role=Role(data["role"]),
+ content=data.get("content", ""),
+ tool_calls=[
+ ToolCall(id=tc["id"], name=tc["name"], arguments=tc.get("arguments", {}))
+ for tc in data.get("tool_calls", [])
+ ],
+ tool_call_id=data.get("tool_call_id"),
+ name=data.get("name"),
+ )
diff --git a/src/lang2sql/components/enrichment/__init__.py b/src/lang2sql/components/enrichment/__init__.py
deleted file mode 100644
index 3abd0d1..0000000
--- a/src/lang2sql/components/enrichment/__init__.py
+++ /dev/null
@@ -1,4 +0,0 @@
-from .context_enricher import ContextEnricher
-from .question_profiler import QuestionProfiler
-
-__all__ = ["ContextEnricher", "QuestionProfiler"]
diff --git a/src/lang2sql/components/enrichment/context_enricher.py b/src/lang2sql/components/enrichment/context_enricher.py
deleted file mode 100644
index 47012f9..0000000
--- a/src/lang2sql/components/enrichment/context_enricher.py
+++ /dev/null
@@ -1,57 +0,0 @@
-from __future__ import annotations
-
-import dataclasses
-import json
-from pathlib import Path
-from typing import Optional
-
-from ...core.base import BaseComponent
-from ...core.catalog import CatalogEntry, QuestionProfile
-from ...core.hooks import TraceHook
-from ...core.ports import LLMPort
-
-_PROMPT_PATH = Path(__file__).parent / "prompts" / "context_enricher.md"
-
-
-def _load_prompt() -> str:
- return _PROMPT_PATH.read_text(encoding="utf-8").strip()
-
-
-class ContextEnricher(BaseComponent):
- """질문 프로파일 + 스키마 메타데이터로 질문을 보강한다."""
-
- def __init__(
- self,
- *,
- llm: LLMPort,
- name: Optional[str] = None,
- hook: Optional[TraceHook] = None,
- ) -> None:
- super().__init__(name=name or "ContextEnricher", hook=hook)
- self._llm = llm
- self._system_prompt = _load_prompt()
-
- def _run(
- self,
- query: str,
- schemas: list[CatalogEntry],
- profile: QuestionProfile,
- ) -> str:
- profiles_json = json.dumps(dataclasses.asdict(profile), ensure_ascii=False)
-
- tables_map: dict[str, dict] = {
- entry.get("name", ""): {
- "description": entry.get("description", ""),
- "columns": entry.get("columns", {}),
- }
- for entry in schemas
- }
- tables_json = json.dumps(tables_map, ensure_ascii=False)
-
- user_content = (
- self._system_prompt.replace("{profiles}", profiles_json)
- .replace("{related_tables}", tables_json)
- .replace("{refined_question}", query)
- )
- messages = [{"role": "user", "content": user_content}]
- return self._llm.invoke(messages).strip()
diff --git a/src/lang2sql/components/enrichment/prompts/context_enricher.md b/src/lang2sql/components/enrichment/prompts/context_enricher.md
deleted file mode 100644
index a148dd9..0000000
--- a/src/lang2sql/components/enrichment/prompts/context_enricher.md
+++ /dev/null
@@ -1,22 +0,0 @@
-# Role
-
-You are a smart assistant that takes a user question and enriches it using:
-1. Question profiles: {profiles}
-2. Table metadata (names, columns, descriptions):
- {related_tables}
-
-# Tasks
-
-- Correct any wrong terms by matching them to actual column names.
-- If the question is time-series or aggregation, add explicit hints (e.g., "over the last 30 days").
-- If needed, map natural language terms to actual column values (e.g., '미국' → 'USA' for country_code).
-- Output the enriched question only.
-
-# Input
-
-Refined question:
-{refined_question}
-
-# Notes
-
-Using the refined version for enrichment, but keep the original intent in mind.
diff --git a/src/lang2sql/components/enrichment/prompts/question_profiler.md b/src/lang2sql/components/enrichment/prompts/question_profiler.md
deleted file mode 100644
index 806cedc..0000000
--- a/src/lang2sql/components/enrichment/prompts/question_profiler.md
+++ /dev/null
@@ -1,28 +0,0 @@
-# Role
-
-You are an assistant that analyzes a user question and extracts the following profiles as JSON:
-- is_timeseries (boolean)
-- is_aggregation (boolean)
-- has_filter (boolean)
-- is_grouped (boolean)
-- has_ranking (boolean)
-- has_temporal_comparison (boolean)
-- intent_type (one of: trend, lookup, comparison, distribution)
-
-# Input
-
-Question:
-{question}
-
-# Output
-
-The output must be a valid JSON matching the schema below (no extra keys):
-{{
- "is_timeseries": boolean,
- "is_aggregation": boolean,
- "has_filter": boolean,
- "is_grouped": boolean,
- "has_ranking": boolean,
- "has_temporal_comparison": boolean,
- "intent_type": string
-}}
diff --git a/src/lang2sql/components/enrichment/question_profiler.py b/src/lang2sql/components/enrichment/question_profiler.py
deleted file mode 100644
index f0dc5c8..0000000
--- a/src/lang2sql/components/enrichment/question_profiler.py
+++ /dev/null
@@ -1,61 +0,0 @@
-from __future__ import annotations
-
-import json
-import re
-from pathlib import Path
-from typing import Optional
-
-from ...core.base import BaseComponent
-from ...core.catalog import QuestionProfile
-from ...core.hooks import TraceHook
-from ...core.ports import LLMPort
-
-_PROMPT_PATH = Path(__file__).parent / "prompts" / "question_profiler.md"
-
-_VALID_INTENT_TYPES = {"trend", "lookup", "comparison", "distribution"}
-
-
-def _load_prompt() -> str:
- return _PROMPT_PATH.read_text(encoding="utf-8").strip()
-
-
-def _parse_json(text: str) -> dict:
- match = re.search(r"```(?:json)?\s*(.*?)```", text, re.DOTALL | re.IGNORECASE)
- if match:
- text = match.group(1).strip()
- return json.loads(text)
-
-
-class QuestionProfiler(BaseComponent):
- """질문에서 구조화된 특성(시계열, 집계, 필터 등)을 추출한다."""
-
- def __init__(
- self,
- *,
- llm: LLMPort,
- name: Optional[str] = None,
- hook: Optional[TraceHook] = None,
- ) -> None:
- super().__init__(name=name or "QuestionProfiler", hook=hook)
- self._llm = llm
- self._system_prompt = _load_prompt()
-
- def _run(self, query: str) -> QuestionProfile:
- user_content = self._system_prompt.replace("{question}", query)
- messages = [{"role": "user", "content": user_content}]
- response = self._llm.invoke(messages)
- data = _parse_json(response)
-
- intent_type = str(data.get("intent_type", "lookup"))
- if intent_type not in _VALID_INTENT_TYPES:
- intent_type = "lookup"
-
- return QuestionProfile(
- is_timeseries=bool(data.get("is_timeseries", False)),
- is_aggregation=bool(data.get("is_aggregation", False)),
- has_filter=bool(data.get("has_filter", False)),
- is_grouped=bool(data.get("is_grouped", False)),
- has_ranking=bool(data.get("has_ranking", False)),
- has_temporal_comparison=bool(data.get("has_temporal_comparison", False)),
- intent_type=intent_type,
- )
diff --git a/src/lang2sql/components/execution/__init__.py b/src/lang2sql/components/execution/__init__.py
deleted file mode 100644
index a6f15b9..0000000
--- a/src/lang2sql/components/execution/__init__.py
+++ /dev/null
@@ -1,3 +0,0 @@
-from .sql_executor import SQLExecutor
-
-__all__ = ["SQLExecutor"]
diff --git a/src/lang2sql/components/execution/sql_executor.py b/src/lang2sql/components/execution/sql_executor.py
deleted file mode 100644
index f7c9a28..0000000
--- a/src/lang2sql/components/execution/sql_executor.py
+++ /dev/null
@@ -1,27 +0,0 @@
-from __future__ import annotations
-
-from typing import Any, Optional
-
-from ...core.base import BaseComponent
-from ...core.exceptions import ComponentError
-from ...core.hooks import TraceHook
-from ...core.ports import DBPort
-
-
-class SQLExecutor(BaseComponent):
- """Executes a SQL string and returns rows as a list of dicts."""
-
- def __init__(
- self,
- *,
- db: DBPort,
- name: Optional[str] = None,
- hook: Optional[TraceHook] = None,
- ) -> None:
- super().__init__(name=name or "SQLExecutor", hook=hook)
- self._db = db
-
- def _run(self, sql: str) -> list[dict[str, Any]]:
- if not sql or not sql.strip():
- raise ComponentError(self.name, "sql must not be empty.")
- return self._db.execute(sql)
diff --git a/src/lang2sql/components/gate/__init__.py b/src/lang2sql/components/gate/__init__.py
deleted file mode 100644
index 0fc026c..0000000
--- a/src/lang2sql/components/gate/__init__.py
+++ /dev/null
@@ -1,4 +0,0 @@
-from .question_gate import QuestionGate
-from .table_suitability import TableSuitabilityEvaluator
-
-__all__ = ["QuestionGate", "TableSuitabilityEvaluator"]
diff --git a/src/lang2sql/components/gate/prompts/question_gate.md b/src/lang2sql/components/gate/prompts/question_gate.md
deleted file mode 100644
index 2e08150..0000000
--- a/src/lang2sql/components/gate/prompts/question_gate.md
+++ /dev/null
@@ -1,25 +0,0 @@
-당신은 데이터 분석 도우미입니다. 아래 사용자 질문이 SQL로 답변 가능한지 판별하고, 구조화된 결과를 반환하세요.
-
-요건:
-- suitable: 질문이 SQL로 답변 가능한지 여부(Boolean)
-- reason: 한 줄 설명(어떤 보완이 필요한지 요약)
-- missing_entities: 기간, 대상 엔터티, 측정값 등 누락된 핵심 요소 리스트(없으면 빈 리스트)
-- requires_data_science: 통계/ML 분석이 필요한지 여부(Boolean)
-
-언어/출력 형식:
-- 모든 텍스트 값은 한국어로 작성하세요. (reason는 한국어 문장, missing_entities 항목은 한국어 명사구)
-- Boolean 값은 JSON의 true/false로 표기하세요.
-
-주의:
-- 데이터 분석 맥락에서 SQL 집계/필터/조인으로 해결 가능한지 판단합니다.
-- 정책/운영/가이드/설치/권한/오류 해결 등은 SQL 부적합으로 간주합니다.
-
-입력: {question}
-
-출력은 반드시 아래 JSON 스키마로만 반환하세요:
-{{
- "suitable": boolean,
- "reason": string,
- "missing_entities": string[],
- "requires_data_science": boolean
-}}
diff --git a/src/lang2sql/components/gate/prompts/table_suitability.md b/src/lang2sql/components/gate/prompts/table_suitability.md
deleted file mode 100644
index 23b0e53..0000000
--- a/src/lang2sql/components/gate/prompts/table_suitability.md
+++ /dev/null
@@ -1,47 +0,0 @@
-## 문서 적합성 평가 프롬프트 (Table Search 재랭킹)
-
-당신은 데이터 카탈로그 평가자입니다. 주어진 사용자 질문과 검색 결과(테이블 → 칼럼 설명 맵)를 바탕으로, 각 테이블이 질문에 얼마나 적합한지 0~1 사이의 실수 점수로 평가하세요.
-
-### 입력
-- **question**: {question}
-- **tables**: {tables}
-
-### 과업
-1. **핵심 신호 추출**: 질문에서 엔터티/지표/시간/필터/그룹화 단서를 추출합니다.
-2. **테이블별 점수화**: 각 테이블의 칼럼·설명과의 연관성으로 적합도를 점수화합니다(0~1, 소수 셋째 자리 반올림).
-3. **근거와 보완점 제시**: 매칭된 칼럼과 부족한 요소(엔터티/지표/기간 등)를 한국어로 설명합니다.
-4. **정렬**: 결과를 점수 내림차순으로 정렬해 반환합니다.
-
-### 평가 규칙(가이드)
-- **0.90~1.00**: 필요한 엔터티, 기간/시간 컬럼, 핵심 지표/측정 칼럼이 모두 존재. 직접 조회/집계만으로 답 가능.
-- **0.60~0.89**: 주요 신호 매칭, 일부 보완(기간/그룹 키/보조 칼럼) 필요. 조인 없이 근사 가능.
-- **0.30~0.59**: 일부만 매칭. 외부 컨텍스트나 조인 없이는 부정확/제한적.
-- **0.00~0.29**: 연관성 낮음. 스키마/도메인 불일치 또는 정책/운영성 테이블.
-
-### 주의
-- 칼럼 이름/설명에 실제로 존재하지 않는 항목을 매칭하지 마세요(환각 금지).
-- 시간 요구(특정 날짜/기간)가 있으면 timestamp/date/created_at 등 시간 계열 키를 중시하세요.
-- 엔티티 키(예: id, user_id, product_id)의 존재 여부를 가산점으로 반영하세요.
-- 키 이름은 정확히 입력 맵의 키만 사용하세요(자유 추측 금지).
-
-### 언어/출력 형식
-- 모든 텍스트 값은 한국어로 작성하세요.
-- 결과는 반드시 아래 JSON 스키마로만 반환하세요(추가/누락 키 금지).
-
-### 출력(JSON 스키마)
-{{
- "results": [
- {{
- "table_name": string,
- "score": number,
- "reason": string,
- "matched_columns": string[],
- "missing_entities": string[]
- }}
- ]
-}}
-
-### 검증 규칙
-- score는 [0, 1] 범위로 클램핑하고 소수 셋째 자리까지 반올림하세요.
-- matched_columns는 해당 테이블 객체의 실제 키만 포함하세요(단, table_description 제외).
-- reason 및 missing_entities는 한국어로 작성하세요.
diff --git a/src/lang2sql/components/gate/question_gate.py b/src/lang2sql/components/gate/question_gate.py
deleted file mode 100644
index 6d559eb..0000000
--- a/src/lang2sql/components/gate/question_gate.py
+++ /dev/null
@@ -1,55 +0,0 @@
-from __future__ import annotations
-
-import json
-import re
-from pathlib import Path
-from typing import Optional
-
-from ...core.base import BaseComponent
-from ...core.catalog import GateResult
-from ...core.hooks import TraceHook
-from ...core.ports import LLMPort
-
-_PROMPT_PATH = Path(__file__).parent / "prompts" / "question_gate.md"
-
-
-def _load_prompt() -> str:
- return _PROMPT_PATH.read_text(encoding="utf-8").strip()
-
-
-def _parse_json(text: str) -> dict:
- """LLM 응답에서 JSON을 추출한다. 마크다운 코드블록을 자동으로 제거한다."""
- # ```json ... ``` 또는 ``` ... ``` 블록 제거
- match = re.search(r"```(?:json)?\s*(.*?)```", text, re.DOTALL | re.IGNORECASE)
- if match:
- text = match.group(1).strip()
- return json.loads(text)
-
-
-class QuestionGate(BaseComponent):
- """질문이 SQL로 답변 가능한지 판별한다."""
-
- def __init__(
- self,
- *,
- llm: LLMPort,
- name: Optional[str] = None,
- hook: Optional[TraceHook] = None,
- ) -> None:
- super().__init__(name=name or "QuestionGate", hook=hook)
- self._llm = llm
- self._system_prompt = _load_prompt()
-
- def _run(self, query: str) -> GateResult:
- user_content = self._system_prompt.replace("{question}", query)
- messages = [
- {"role": "user", "content": user_content},
- ]
- response = self._llm.invoke(messages)
- data = _parse_json(response)
- return GateResult(
- suitable=bool(data.get("suitable", True)),
- reason=str(data.get("reason", "")),
- missing_entities=list(data.get("missing_entities", [])),
- requires_data_science=bool(data.get("requires_data_science", False)),
- )
diff --git a/src/lang2sql/components/gate/table_suitability.py b/src/lang2sql/components/gate/table_suitability.py
deleted file mode 100644
index 4837805..0000000
--- a/src/lang2sql/components/gate/table_suitability.py
+++ /dev/null
@@ -1,78 +0,0 @@
-from __future__ import annotations
-
-import json
-import re
-from pathlib import Path
-from typing import Optional
-
-from ...core.base import BaseComponent
-from ...core.catalog import CatalogEntry, TableScore
-from ...core.hooks import TraceHook
-from ...core.ports import LLMPort
-
-_PROMPT_PATH = Path(__file__).parent / "prompts" / "table_suitability.md"
-
-
-def _load_prompt() -> str:
- return _PROMPT_PATH.read_text(encoding="utf-8").strip()
-
-
-def _parse_json(text: str) -> dict:
- match = re.search(r"```(?:json)?\s*(.*?)```", text, re.DOTALL | re.IGNORECASE)
- if match:
- text = match.group(1).strip()
- return json.loads(text)
-
-
-class TableSuitabilityEvaluator(BaseComponent):
- """검색된 테이블을 질문 관련도순으로 필터링한다."""
-
- def __init__(
- self,
- *,
- llm: LLMPort,
- threshold: float = 0.3,
- name: Optional[str] = None,
- hook: Optional[TraceHook] = None,
- ) -> None:
- super().__init__(name=name or "TableSuitabilityEvaluator", hook=hook)
- self._llm = llm
- self._threshold = threshold
- self._system_prompt = _load_prompt()
-
- def _run(self, query: str, schemas: list[CatalogEntry]) -> list[CatalogEntry]:
- # 테이블을 {table_name: {col: desc, ...}} 구조로 직렬화
- tables_map: dict[str, dict] = {}
- for entry in schemas:
- name = entry.get("name", "")
- cols = entry.get("columns", {})
- desc = entry.get("description", "")
- tables_map[name] = {"table_description": desc, **cols}
-
- tables_json = json.dumps(tables_map, ensure_ascii=False)
- user_content = self._system_prompt.replace("{question}", query).replace(
- "{tables}", tables_json
- )
- messages = [{"role": "user", "content": user_content}]
- response = self._llm.invoke(messages)
- data = _parse_json(response)
-
- results: list[TableScore] = [
- TableScore(
- table_name=r["table_name"],
- score=float(r.get("score", 0.0)),
- reason=str(r.get("reason", "")),
- matched_columns=list(r.get("matched_columns", [])),
- missing_entities=list(r.get("missing_entities", [])),
- )
- for r in data.get("results", [])
- ]
-
- # threshold 이상인 테이블만 score 내림차순으로 필터
- passing = {r.table_name for r in results if r.score >= self._threshold}
- filtered = [e for e in schemas if e.get("name", "") in passing]
-
- # score 내림차순 정렬
- score_map = {r.table_name: r.score for r in results}
- filtered.sort(key=lambda e: score_map.get(e.get("name", ""), 0.0), reverse=True)
- return filtered
diff --git a/src/lang2sql/components/generation/__init__.py b/src/lang2sql/components/generation/__init__.py
deleted file mode 100644
index c2a86fe..0000000
--- a/src/lang2sql/components/generation/__init__.py
+++ /dev/null
@@ -1,3 +0,0 @@
-from .sql_generator import SQLGenerator
-
-__all__ = ["SQLGenerator"]
diff --git a/src/lang2sql/components/generation/prompts/bigquery.md b/src/lang2sql/components/generation/prompts/bigquery.md
deleted file mode 100644
index 0e86d58..0000000
--- a/src/lang2sql/components/generation/prompts/bigquery.md
+++ /dev/null
@@ -1,16 +0,0 @@
-You are a Google BigQuery SQL expert. Generate a SQL query that runs correctly on BigQuery.
-
-BigQuery rules:
-- Use DATE_TRUNC(col, MONTH) for month truncation
-- Use EXTRACT(MONTH FROM col) for month extraction
-- Use CURRENT_DATE() for today, CURRENT_TIMESTAMP() for now
-- Use DATE_SUB(CURRENT_DATE(), INTERVAL 1 MONTH) for last month
-- Use FORMAT_DATE('%Y-%m', col) for date formatting
-- Use TIMESTAMP_TRUNC for timestamp operations
-- Qualify table names with project and dataset when applicable: `project.dataset.table`
-
-Rules:
-- Return ONLY the SQL query inside a ```sql ... ``` code block
-- Do not include any explanation
-- Use only the tables and columns provided in the schemas
-- Generate SELECT queries only (no INSERT, UPDATE, DELETE, DROP, ALTER)
diff --git a/src/lang2sql/components/generation/prompts/default.md b/src/lang2sql/components/generation/prompts/default.md
deleted file mode 100644
index a1a17df..0000000
--- a/src/lang2sql/components/generation/prompts/default.md
+++ /dev/null
@@ -1,7 +0,0 @@
-You are a SQL expert. Given a natural language question and relevant table schemas, write a single SQL query that answers the question.
-
-Rules:
-- Return ONLY the SQL query inside a ```sql ... ``` code block
-- Do not include any explanation
-- Use only the tables and columns provided in the schemas
-- Generate SELECT queries only (no INSERT, UPDATE, DELETE, DROP, ALTER)
diff --git a/src/lang2sql/components/generation/prompts/duckdb.md b/src/lang2sql/components/generation/prompts/duckdb.md
deleted file mode 100644
index ac22f88..0000000
--- a/src/lang2sql/components/generation/prompts/duckdb.md
+++ /dev/null
@@ -1,14 +0,0 @@
-You are a DuckDB SQL expert. Generate a SQL query that runs correctly on DuckDB.
-
-DuckDB date/time rules:
-- Use DATE_TRUNC('month', col) for month truncation
-- Use EXTRACT(MONTH FROM col) for month extraction
-- Use NOW() or CURRENT_DATE for current datetime/date
-- Use NOW() - INTERVAL '1 month' for last month
-- Use strftime(col, '%Y-%m') for date formatting (DuckDB: column first, format second)
-
-Rules:
-- Return ONLY the SQL query inside a ```sql ... ``` code block
-- Do not include any explanation
-- Use only the tables and columns provided in the schemas
-- Generate SELECT queries only (no INSERT, UPDATE, DELETE, DROP, ALTER)
diff --git a/src/lang2sql/components/generation/prompts/mysql.md b/src/lang2sql/components/generation/prompts/mysql.md
deleted file mode 100644
index dc4024a..0000000
--- a/src/lang2sql/components/generation/prompts/mysql.md
+++ /dev/null
@@ -1,14 +0,0 @@
-You are a MySQL SQL expert. Generate a SQL query that runs correctly on MySQL.
-
-MySQL date/time rules:
-- Use MONTH(col), YEAR(col), DAY(col) for date part extraction
-- Use NOW() or CURDATE() for current datetime/date
-- Use DATE_FORMAT(col, '%Y-%m') for date formatting
-- Use DATE_SUB(NOW(), INTERVAL 1 MONTH) for last month
-- Use DATEDIFF(date1, date2) for date differences
-
-Rules:
-- Return ONLY the SQL query inside a ```sql ... ``` code block
-- Do not include any explanation
-- Use only the tables and columns provided in the schemas
-- Generate SELECT queries only (no INSERT, UPDATE, DELETE, DROP, ALTER)
diff --git a/src/lang2sql/components/generation/prompts/postgresql.md b/src/lang2sql/components/generation/prompts/postgresql.md
deleted file mode 100644
index e99e27e..0000000
--- a/src/lang2sql/components/generation/prompts/postgresql.md
+++ /dev/null
@@ -1,14 +0,0 @@
-You are a PostgreSQL SQL expert. Generate a SQL query that runs correctly on PostgreSQL.
-
-PostgreSQL date/time rules:
-- Use DATE_TRUNC('month', col) to truncate to month
-- Use EXTRACT(MONTH FROM col) or DATE_PART('month', col) for month extraction
-- Use NOW() or CURRENT_TIMESTAMP for current datetime, CURRENT_DATE for today
-- Use NOW() - INTERVAL '1 month' for last month
-- Use TO_CHAR(col, 'YYYY-MM') for date formatting
-
-Rules:
-- Return ONLY the SQL query inside a ```sql ... ``` code block
-- Do not include any explanation
-- Use only the tables and columns provided in the schemas
-- Generate SELECT queries only (no INSERT, UPDATE, DELETE, DROP, ALTER)
diff --git a/src/lang2sql/components/generation/prompts/sqlite.md b/src/lang2sql/components/generation/prompts/sqlite.md
deleted file mode 100644
index 8899b84..0000000
--- a/src/lang2sql/components/generation/prompts/sqlite.md
+++ /dev/null
@@ -1,14 +0,0 @@
-You are a SQLite SQL expert. Generate a SQL query that runs correctly on SQLite.
-
-SQLite date/time rules (CRITICAL — SQLite does NOT support MySQL/PostgreSQL date functions):
-- Use strftime('%Y-%m', col) = strftime('%Y-%m', 'now') for current month comparison
-- Use strftime('%Y', col) = strftime('%Y', 'now') for current year comparison
-- Use DATE('now') for today, DATE('now', '-1 month') for last month
-- Do NOT use MONTH(), YEAR(), DAY(), DATE_FORMAT(), NOW(), CURDATE(), DATEDIFF()
-- For date arithmetic use DATE(col, '+N days') or DATE(col, '-N months')
-
-Rules:
-- Return ONLY the SQL query inside a ```sql ... ``` code block
-- Do not include any explanation
-- Use only the tables and columns provided in the schemas
-- Generate SELECT queries only (no INSERT, UPDATE, DELETE, DROP, ALTER)
diff --git a/src/lang2sql/components/generation/sql_generator.py b/src/lang2sql/components/generation/sql_generator.py
deleted file mode 100644
index efc7bb3..0000000
--- a/src/lang2sql/components/generation/sql_generator.py
+++ /dev/null
@@ -1,105 +0,0 @@
-from __future__ import annotations
-
-import re
-from pathlib import Path
-from typing import Optional
-
-from ...core.base import BaseComponent
-from ...core.catalog import CatalogEntry
-from ...core.exceptions import ComponentError
-from ...core.hooks import TraceHook
-from ...core.ports import LLMPort
-
-_PROMPT_DIR = Path(__file__).parent / "prompts"
-
-_SUPPORTED_DIALECTS = {"default", "sqlite", "postgresql", "mysql", "bigquery", "duckdb"}
-
-
-def _load_prompt(dialect: str) -> str:
- path = _PROMPT_DIR / f"{dialect}.md"
- if not path.exists():
- raise ValueError(
- f"Unsupported dialect: {dialect!r}. "
- f"Available: {sorted(_SUPPORTED_DIALECTS)}"
- )
- return path.read_text(encoding="utf-8").strip()
-
-
-class SQLGenerator(BaseComponent):
- """Generates a SQL string from a natural language query and schema context.
-
- System prompt priority (highest to lowest):
- 1. ``system_prompt`` — explicit string passed by the caller
- 2. ``db_dialect`` — loads the matching ``prompts/{dialect}.md``
- 3. default — loads ``prompts/default.md``
- """
-
- def __init__(
- self,
- *,
- llm: LLMPort,
- db_dialect: Optional[str] = None,
- system_prompt: Optional[str] = None,
- name: Optional[str] = None,
- hook: Optional[TraceHook] = None,
- ) -> None:
- super().__init__(name=name or "SQLGenerator", hook=hook)
- self._llm = llm
-
- if system_prompt is not None:
- self._system_prompt = system_prompt
- elif db_dialect is not None:
- self._system_prompt = _load_prompt(db_dialect)
- else:
- self._system_prompt = _load_prompt("default")
-
- def _run(
- self,
- query: str,
- schemas: list[CatalogEntry],
- context: Optional[list[str]] = None,
- ) -> str:
- schema_text = self._build_context(schemas)
- user_parts: list[str] = []
- if context:
- user_parts.append("Business Context:\n" + "\n\n".join(context))
- user_parts.append(f"Schemas:\n{schema_text}\n\nQuestion: {query}")
- user_content = "\n\n".join(user_parts)
- messages = [
- {"role": "system", "content": self._system_prompt},
- {"role": "user", "content": user_content},
- ]
- response = self._llm.invoke(messages)
- sql = self._extract_sql(response)
- if not sql:
- raise ComponentError(
- self.name,
- "LLM response did not contain a ```sql ... ``` code block.",
- )
- return sql
-
- def _build_context(self, schemas: list[CatalogEntry]) -> str:
- parts: list[str] = []
- for entry in schemas:
- name = entry.get("name", "(unnamed)")
- description = entry.get("description", "")
- columns = entry.get("columns", {})
-
- lines = [f"Table: {name}"]
- if description:
- lines.append(f" Description: {description}")
- if columns:
- lines.append(" Columns:")
- for col, col_desc in columns.items():
- lines.append(f" - {col}: {col_desc}")
- parts.append("\n".join(lines))
- return "\n\n".join(parts)
-
- @staticmethod
- def _extract_sql(text: str) -> str:
- match = re.search(r"```sql\s*(.*?)```", text, re.DOTALL | re.IGNORECASE)
- if not match:
- return ""
- sql = match.group(1).strip()
- sql = sql.rstrip(";").rstrip()
- return sql
diff --git a/src/lang2sql/components/loaders/__init__.py b/src/lang2sql/components/loaders/__init__.py
deleted file mode 100644
index 80ae315..0000000
--- a/src/lang2sql/components/loaders/__init__.py
+++ /dev/null
@@ -1,9 +0,0 @@
-from .directory_ import DirectoryLoader
-from .markdown_ import MarkdownLoader
-from .plaintext_ import PlainTextLoader
-
-__all__ = [
- "MarkdownLoader",
- "PlainTextLoader",
- "DirectoryLoader",
-]
diff --git a/src/lang2sql/components/loaders/directory_.py b/src/lang2sql/components/loaders/directory_.py
deleted file mode 100644
index 48cfe3a..0000000
--- a/src/lang2sql/components/loaders/directory_.py
+++ /dev/null
@@ -1,61 +0,0 @@
-from __future__ import annotations
-
-import warnings
-from pathlib import Path
-
-from ...core.catalog import TextDocument
-from ...core.ports import DocumentLoaderPort
-from .markdown_ import MarkdownLoader
-from .plaintext_ import PlainTextLoader
-
-
-class DirectoryLoader:
- """
- Recursively loads a directory by dispatching each file to the loader
- registered for its extension.
-
- Default mapping::
-
- .md → MarkdownLoader
- .txt → PlainTextLoader
-
- Custom loaders can be added or override defaults::
-
- from lang2sql.integrations.loaders import PDFLoader
-
- docs = DirectoryLoader(
- "docs/",
- loaders={".md": MarkdownLoader(), ".pdf": PDFLoader()},
- ).load()
-
- Args:
- path: Directory path to load.
- loaders: Mapping of lowercase extension → DocumentLoaderPort.
- Defaults to ``{".md": MarkdownLoader(), ".txt": PlainTextLoader()}``.
- """
-
- def __init__(
- self,
- path: str,
- loaders: dict[str, DocumentLoaderPort] | None = None,
- ) -> None:
- self._path = Path(path)
- self._loaders: dict[str, DocumentLoaderPort] = loaders or {
- ".md": MarkdownLoader(),
- ".txt": PlainTextLoader(),
- }
-
- def load(self) -> list[TextDocument]:
- """Recursively walk the directory and load all files with a registered extension."""
- docs: list[TextDocument] = []
- for file in sorted(self._path.rglob("*")):
- if not file.is_file():
- continue
- loader = self._loaders.get(file.suffix.lower())
- if loader is None:
- continue
- try:
- docs.extend(loader.load(str(file)))
- except Exception as e:
- warnings.warn(f"Failed to load {file}: {e}", stacklevel=2)
- return docs
diff --git a/src/lang2sql/components/loaders/markdown_.py b/src/lang2sql/components/loaders/markdown_.py
deleted file mode 100644
index eb0488d..0000000
--- a/src/lang2sql/components/loaders/markdown_.py
+++ /dev/null
@@ -1,42 +0,0 @@
-from __future__ import annotations
-
-from pathlib import Path
-
-from ...core.catalog import TextDocument
-from ...core.ports import DocumentLoaderPort
-
-
-class MarkdownLoader(DocumentLoaderPort):
- """
- Markdown file(s) (.md) → list[TextDocument].
-
- Standard library only. No external dependencies.
-
- - Single file: ``load("docs/revenue.md")`` → [TextDocument]
- - Directory: ``load("docs/")`` → [TextDocument, ...] (recursive)
-
- The first ``# heading`` becomes ``title``; the full file text becomes ``content``.
- Falls back to the filename stem when no heading is found.
- """
-
- def load(self, path: str) -> list[TextDocument]:
- p = Path(path)
- if p.is_dir():
- return [doc for f in sorted(p.rglob("*.md")) for doc in self._load_file(f)]
- return self._load_file(p)
-
- def _load_file(self, path: Path) -> list[TextDocument]:
- content = path.read_text(encoding="utf-8")
- title = ""
- for line in content.splitlines():
- if line.startswith("# "):
- title = line[2:].strip()
- break
- return [
- TextDocument(
- id=path.stem,
- title=title or path.stem,
- content=content,
- source=str(path),
- )
- ]
diff --git a/src/lang2sql/components/loaders/plaintext_.py b/src/lang2sql/components/loaders/plaintext_.py
deleted file mode 100644
index 4337133..0000000
--- a/src/lang2sql/components/loaders/plaintext_.py
+++ /dev/null
@@ -1,46 +0,0 @@
-from __future__ import annotations
-
-from pathlib import Path
-
-from ...core.catalog import TextDocument
-from ...core.ports import DocumentLoaderPort
-
-
-class PlainTextLoader(DocumentLoaderPort):
- """
- Plain text file(s) (.txt, etc.) → list[TextDocument].
-
- Standard library only. No external dependencies.
-
- - Single file: ``load("notes.txt")`` → [TextDocument]
- - Directory: ``load("data/")`` → [TextDocument, ...] (recursive)
-
- ``title`` = filename stem, ``content`` = full file text.
-
- Args:
- extensions: File extensions to load. Default ``[".txt"]``.
- """
-
- def __init__(self, extensions: list[str] | None = None) -> None:
- self._extensions = extensions or [".txt"]
-
- def load(self, path: str) -> list[TextDocument]:
- p = Path(path)
- if p.is_dir():
- docs: list[TextDocument] = []
- for ext in self._extensions:
- for f in sorted(p.rglob(f"*{ext}")):
- docs.extend(self._load_file(f))
- return docs
- return self._load_file(p)
-
- def _load_file(self, path: Path) -> list[TextDocument]:
- content = path.read_text(encoding="utf-8")
- return [
- TextDocument(
- id=path.stem,
- title=path.stem,
- content=content,
- source=str(path),
- )
- ]
diff --git a/src/lang2sql/components/retrieval/__init__.py b/src/lang2sql/components/retrieval/__init__.py
deleted file mode 100644
index 997cc7c..0000000
--- a/src/lang2sql/components/retrieval/__init__.py
+++ /dev/null
@@ -1,18 +0,0 @@
-from .chunker import CatalogChunker, DocumentChunkerPort, RecursiveCharacterChunker
-from .hybrid import HybridRetriever
-from .keyword import KeywordRetriever
-from .vector import VectorRetriever
-from ...core.catalog import CatalogEntry, IndexedChunk, RetrievalResult, TextDocument
-
-__all__ = [
- "KeywordRetriever",
- "VectorRetriever",
- "HybridRetriever",
- "DocumentChunkerPort",
- "CatalogChunker",
- "RecursiveCharacterChunker",
- "CatalogEntry",
- "TextDocument",
- "IndexedChunk",
- "RetrievalResult",
-]
diff --git a/src/lang2sql/components/retrieval/_bm25.py b/src/lang2sql/components/retrieval/_bm25.py
deleted file mode 100644
index d3e87c0..0000000
--- a/src/lang2sql/components/retrieval/_bm25.py
+++ /dev/null
@@ -1,130 +0,0 @@
-"""
-Internal BM25 index — stdlib only (math, collections).
-
-BM25 parameters:
- k1 = 1.5 (term frequency saturation)
- b = 0.75 (document length normalization)
-
-Tokenization: text.lower().split() (whitespace, no external deps)
-"""
-
-from __future__ import annotations
-
-import math
-from collections import Counter
-from typing import Any
-
-_K1 = 1.5
-_B = 0.75
-
-
-def _tokenize(text: str) -> list[str]:
- return text.lower().split()
-
-
-def _extract_text(value: Any) -> list[str]:
- """Recursively extract text tokens from any value (str, list, dict, other)."""
- if isinstance(value, str):
- return [value]
- if isinstance(value, list):
- result: list[str] = []
- for item in value:
- result.extend(_extract_text(item))
- return result
- if isinstance(value, dict):
- result = []
- for k, v in value.items():
- result.append(str(k))
- result.extend(_extract_text(v))
- return result
- return [str(value)]
-
-
-def _entry_to_text(entry: dict[str, Any], index_fields: list[str]) -> str:
- """
- Convert a catalog dict entry into a single text string for indexing.
-
- Handles:
- - str fields → joined as-is
- - dict fields → "key value key value ..." (for columns: {col_name: col_desc})
- - list fields → each element extracted recursively
- - other types → str(value)
- """
- parts: list[str] = []
- for field in index_fields:
- value = entry.get(field)
- if value is None:
- continue
- parts.extend(_extract_text(value))
- return " ".join(parts)
-
-
-class _BM25Index:
- """
- In-memory BM25 index over a list[dict] catalog.
-
- Usage:
- index = _BM25Index(catalog, index_fields=["name", "description", "columns"])
- scores = index.score("주문 테이블") # list[float], one per catalog entry
- """
-
- def __init__(
- self,
- catalog: list[dict[str, Any]],
- index_fields: list[str],
- ) -> None:
- self._catalog = catalog
- self._n = len(catalog)
-
- # Tokenize each document
- self._docs: list[list[str]] = [
- _tokenize(_entry_to_text(entry, index_fields)) for entry in catalog
- ]
-
- # Term frequencies per document
- self._tfs: list[Counter[str]] = [Counter(doc) for doc in self._docs]
-
- # Document lengths
- doc_lengths = [len(doc) for doc in self._docs]
- self._avgdl: float = sum(doc_lengths) / self._n if self._n > 0 else 0.0
-
- # Inverted index: term → set of doc indices that contain it
- self._df: Counter[str] = Counter()
- for tf in self._tfs:
- for term in tf:
- self._df[term] += 1
-
- def score(self, query: str) -> list[float]:
- """
- Return a BM25 score for each catalog entry.
-
- Args:
- query: Natural language query string.
-
- Returns:
- List of float scores, one per catalog entry, in original order.
- """
- if self._n == 0:
- return []
-
- query_terms = _tokenize(query)
- scores = [0.0] * self._n
-
- for term in query_terms:
- df_t = self._df.get(term, 0)
- if df_t == 0:
- continue
-
- # IDF — smoothed to avoid log(0)
- idf = math.log((self._n - df_t + 0.5) / (df_t + 0.5) + 1)
-
- for i, tf in enumerate(self._tfs):
- tf_t = tf.get(term, 0)
- if tf_t == 0:
- continue
-
- dl = len(self._docs[i])
- denom = tf_t + _K1 * (1 - _B + _B * dl / self._avgdl)
- scores[i] += idf * (tf_t * (_K1 + 1)) / denom
-
- return scores
diff --git a/src/lang2sql/components/retrieval/chunker.py b/src/lang2sql/components/retrieval/chunker.py
deleted file mode 100644
index 93c1df2..0000000
--- a/src/lang2sql/components/retrieval/chunker.py
+++ /dev/null
@@ -1,206 +0,0 @@
-from __future__ import annotations
-
-from typing import Protocol, runtime_checkable
-
-from ...core.catalog import CatalogEntry, IndexedChunk, TextDocument
-
-
-@runtime_checkable
-class DocumentChunkerPort(Protocol):
- """
- Interface for TextDocument → list[IndexedChunk] conversion.
-
- Default implementation: RecursiveCharacterChunker
- Advanced implementation: SemanticChunker (integrations/chunking/semantic_.py)
- Custom implementation: any class satisfying this Protocol can be passed as splitter.
-
- Example (wrapping LangChain)::
-
- class LangChainChunkerAdapter:
- def __init__(self, lc_splitter):
- self._splitter = lc_splitter
-
- def chunk(self, doc: TextDocument) -> list[IndexedChunk]:
- texts = self._splitter.split_text(doc["content"])
- return [
- IndexedChunk(
- chunk_id=f"{doc['id']}__{i}", text=t,
- source_type="document", source_id=doc["id"],
- chunk_index=i, metadata={"title": doc.get("title", "")},
- )
- for i, t in enumerate(texts)
- ]
-
- retriever = VectorRetriever.from_sources(..., splitter=LangChainChunkerAdapter(...))
- """
-
- def chunk(self, doc: TextDocument) -> list[IndexedChunk]: ...
-
-
-class CatalogChunker:
- """
- Converts a CatalogEntry into a list of IndexedChunks.
-
- Solves the problem where a table with 100+ columns loses column-level
- semantics when represented as a single vector.
- Chunk 0 is a table header summary; subsequent chunks are column groups.
- Each chunk's metadata preserves the full CatalogEntry so VectorRetriever
- can reconstruct it on retrieval.
-
- Args:
- max_columns_per_chunk: Maximum columns per column-group chunk. Default 20.
- """
-
- def __init__(self, max_columns_per_chunk: int = 20) -> None:
- self._max_cols = max_columns_per_chunk
-
- def split(self, catalog: list[CatalogEntry]) -> list[IndexedChunk]:
- """LangChain-style batch split: list input → list output."""
- return [c for entry in catalog for c in self.chunk(entry)]
-
- def chunk(self, entry: CatalogEntry) -> list[IndexedChunk]:
- name = entry.get("name", "")
- description = entry.get("description", "")
- columns = entry.get("columns", {})
- chunks: list[IndexedChunk] = []
-
- # Chunk 0: table header
- chunks.append(
- IndexedChunk(
- chunk_id=f"{name}__0",
- text=f"{name}: {description}".strip(),
- source_type="catalog",
- source_id=name,
- chunk_index=0,
- metadata=dict(entry), # preserve full CatalogEntry for reconstruction
- )
- )
-
- # Chunk 1+: column groups
- col_items = list(columns.items())
- for i, start in enumerate(range(0, len(col_items), self._max_cols)):
- group = col_items[start : start + self._max_cols]
- col_text = " ".join(f"{k} {v}" for k, v in group)
- chunks.append(
- IndexedChunk(
- chunk_id=f"{name}__col_{i + 1}",
- text=f"{name} columns: {col_text}",
- source_type="catalog",
- source_id=name,
- chunk_index=i + 1,
- metadata=dict(
- entry
- ), # preserve full CatalogEntry in every column chunk
- )
- )
-
- return chunks
-
-
-class RecursiveCharacterChunker(DocumentChunkerPort):
- """
- Hierarchical separator-based document chunker. No external dependencies.
-
- Separator priority: ["\\n\\n", "\\n", ". ", " ", ""]
- — tries paragraph → line → sentence → word boundaries in order.
- Character-count-based so it works for both Korean and English
- (unlike str.split() which assumes whitespace-delimited words).
-
- For higher chunking quality, use SemanticChunker (integrations/chunking/semantic_.py).
-
- Args:
- chunk_size: Maximum characters per chunk. Default 1000.
- chunk_overlap: Overlap characters between consecutive chunks. Default 100.
- separators: Separator priority list. None uses the default list above.
- """
-
- _DEFAULT_SEPARATORS = ["\n\n", "\n", ". ", " ", ""]
-
- def __init__(
- self,
- chunk_size: int = 1000,
- chunk_overlap: int = 100,
- separators: list[str] | None = None,
- ) -> None:
- if chunk_overlap >= chunk_size:
- raise ValueError(
- f"chunk_overlap ({chunk_overlap}) must be less than chunk_size ({chunk_size})"
- )
- self._chunk_size = chunk_size
- self._chunk_overlap = chunk_overlap
- self._separators = separators or self._DEFAULT_SEPARATORS
-
- def split(self, docs: list[TextDocument]) -> list[IndexedChunk]:
- """LangChain-style batch split: list input → list output."""
- return [c for doc in docs for c in self.chunk(doc)]
-
- def chunk(self, doc: TextDocument) -> list[IndexedChunk]:
- content = doc.get("content", "")
- if not content:
- return []
-
- raw_chunks = self._split(content, self._separators)
- title = doc.get("title", "")
- doc_id = doc.get("id", "")
-
- return [
- IndexedChunk(
- chunk_id=f"{doc_id}__{i}",
- text=f"{title}: {text}" if title else text,
- source_type="document",
- source_id=doc_id,
- chunk_index=i,
- metadata={
- "id": doc_id,
- "title": title,
- "source": doc.get("source", ""),
- },
- )
- for i, text in enumerate(raw_chunks)
- ]
-
- def _split(self, text: str, separators: list[str]) -> list[str]:
- """Recursively try separators until all chunks fit within chunk_size."""
- if not separators:
- return [text] if text else []
- chunks: list[str] = []
- separator = separators[-1] # fallback: character-level split
-
- for sep in separators:
- if sep and sep in text:
- separator = sep
- break
-
- parts = text.split(separator) if separator else list(text)
- current = ""
-
- for part in parts:
- candidate = (
- (current + separator + part).lstrip(separator) if current else part
- )
- if len(candidate) <= self._chunk_size:
- current = candidate
- else:
- if current:
- chunks.append(current)
- # part itself exceeds chunk_size → recurse with finer separators
- if len(part) > self._chunk_size and len(separators) > 1:
- chunks.extend(self._split(part, separators[1:]))
- current = ""
- else:
- current = part
-
- if current:
- chunks.append(current)
-
- if self._chunk_overlap > 0 and len(chunks) > 1:
- chunks = self._apply_overlap(chunks)
-
- return chunks
-
- def _apply_overlap(self, chunks: list[str]) -> list[str]:
- overlapped = [chunks[0]]
- for i in range(1, len(chunks)):
- prev_tail = chunks[i - 1][-self._chunk_overlap :]
- overlapped.append(prev_tail + chunks[i])
- return overlapped
diff --git a/src/lang2sql/components/retrieval/hybrid.py b/src/lang2sql/components/retrieval/hybrid.py
deleted file mode 100644
index 5a3df77..0000000
--- a/src/lang2sql/components/retrieval/hybrid.py
+++ /dev/null
@@ -1,112 +0,0 @@
-from __future__ import annotations
-
-from typing import Optional
-
-from ...core.base import BaseComponent
-from ...core.catalog import CatalogEntry, RetrievalResult, TextDocument
-from ...core.hooks import TraceHook
-from ...core.ports import EmbeddingPort
-from .chunker import DocumentChunkerPort
-from .keyword import KeywordRetriever
-from .vector import VectorRetriever
-
-
-class HybridRetriever(BaseComponent):
- """
- BM25 + vector hybrid retriever. Merges results with Reciprocal Rank Fusion (RRF).
-
- RRF algorithm::
-
- RRF_score(table) = Σ 1/(k + rank_i) for each ranker i
- k = 60 (default, recommended by the RRF paper)
-
- Over-fetches ``top_n * 2`` candidates from each retriever, merges via RRF,
- and returns the final ``top_n``.
- Context is taken from VectorRetriever only (BM25 has no document context).
-
- Args:
- catalog: List of CatalogEntry dicts.
- embedding: EmbeddingPort implementation.
- documents: Optional list of business documents to index.
- splitter: Chunking strategy for documents (default: RecursiveCharacterChunker).
- top_n: Maximum number of schemas to return. Default 5.
- rrf_k: RRF smoothing constant. Default 60.
- score_threshold: Minimum vector similarity score. Default 0.0.
- name: Component name for tracing.
- hook: TraceHook for observability.
-
- Usage::
-
- retriever = HybridRetriever(
- catalog=[{"name": "orders", "description": "...", "columns": {...}}],
- embedding=OpenAIEmbedding(model="text-embedding-3-small"),
- )
- result = retriever("How many orders last month?") # RetrievalResult
- """
-
- def __init__(
- self,
- *,
- catalog: list[CatalogEntry],
- embedding: EmbeddingPort,
- documents: Optional[list[TextDocument]] = None,
- splitter: Optional[DocumentChunkerPort] = None,
- top_n: int = 5,
- rrf_k: int = 60,
- score_threshold: float = 0.0,
- name: Optional[str] = None,
- hook: Optional[TraceHook] = None,
- ) -> None:
- super().__init__(name=name or "HybridRetriever", hook=hook)
- fetch = top_n * 2
- self._keyword = KeywordRetriever(catalog=catalog, top_n=fetch)
- self._vector = VectorRetriever.from_sources(
- catalog=catalog,
- embedding=embedding,
- documents=documents,
- splitter=splitter,
- top_n=fetch,
- score_threshold=score_threshold,
- )
- self._top_n = top_n
- self._rrf_k = rrf_k
-
- def _run(self, query: str) -> RetrievalResult:
- """
- Args:
- query: Natural language search query.
-
- Returns:
- RetrievalResult:
- .schemas — top_n schemas after RRF merge
- .context — business document context from VectorRetriever
- """
- keyword_schemas = self._keyword(query) # list[CatalogEntry]
- vector_result = self._vector(query) # RetrievalResult
-
- merged = self._rrf_merge(keyword_schemas, vector_result.schemas)
- return RetrievalResult(schemas=merged, context=vector_result.context)
-
- def _rrf_merge(
- self,
- keyword_schemas: list[CatalogEntry],
- vector_schemas: list[CatalogEntry],
- ) -> list[CatalogEntry]:
- """Merge results from both retrievers via RRF and return top_n entries."""
- k = self._rrf_k
- scores: dict[str, float] = {}
- entries: dict[str, CatalogEntry] = {}
-
- for rank, entry in enumerate(keyword_schemas, start=1):
- name = entry["name"]
- scores[name] = scores.get(name, 0.0) + 1.0 / (k + rank)
- entries[name] = entry
-
- for rank, entry in enumerate(vector_schemas, start=1):
- name = entry["name"]
- scores[name] = scores.get(name, 0.0) + 1.0 / (k + rank)
- if name not in entries:
- entries[name] = entry
-
- sorted_names = sorted(scores, key=lambda n: scores[n], reverse=True)
- return [entries[n] for n in sorted_names[: self._top_n]]
diff --git a/src/lang2sql/components/retrieval/keyword.py b/src/lang2sql/components/retrieval/keyword.py
deleted file mode 100644
index bf29166..0000000
--- a/src/lang2sql/components/retrieval/keyword.py
+++ /dev/null
@@ -1,81 +0,0 @@
-from __future__ import annotations
-
-from typing import Optional
-
-from ...core.base import BaseComponent
-from ...core.catalog import CatalogEntry
-from ...core.hooks import TraceHook
-from ._bm25 import _BM25Index
-
-_DEFAULT_INDEX_FIELDS = ["name", "description", "columns"]
-
-
-class KeywordRetriever(BaseComponent):
- """
- BM25-based keyword retriever over a table catalog.
-
- Indexes catalog entries at init time (in-memory).
- On each call, scores entries against the given query and returns
- the top-N matches as a ranked list.
-
- Args:
- catalog: List of table dicts. Each dict should have at minimum
- ``name`` (str) and ``description`` (str).
- Optional keys: ``columns`` (dict[str, str]), ``meta`` (dict).
- top_n: Maximum number of results to return. Defaults to 5.
- index_fields: Fields to index. Defaults to ["name", "description", "columns"].
- Pass a custom list to replace the default (complete override).
- name: Component name for tracing. Defaults to "KeywordRetriever".
- hook: Optional TraceHook for observability.
-
- Example::
-
- retriever = KeywordRetriever(catalog=[
- {"name": "orders", "description": "주문 정보 테이블"},
- ])
- results = retriever("주문 조회")
- print(results) # [{"name": "orders", ...}]
- """
-
- def __init__(
- self,
- *,
- catalog: list[dict],
- top_n: int = 5,
- index_fields: Optional[list[str]] = None,
- name: Optional[str] = None,
- hook: Optional[TraceHook] = None,
- ) -> None:
- super().__init__(name=name or "KeywordRetriever", hook=hook)
- self._catalog = catalog
- self._top_n = top_n
- self._index_fields = (
- index_fields if index_fields is not None else _DEFAULT_INDEX_FIELDS
- )
- self._index = _BM25Index(catalog, self._index_fields)
-
- def _run(self, query: str) -> list[CatalogEntry]:
- """
- Search the catalog with BM25 and return top-N matching entries.
-
- Args:
- query: Natural language search query.
-
- Returns:
- Ranked list of matching catalog entries (BM25 score descending).
- Empty list if no match or catalog is empty.
- """
- if not self._catalog:
- return []
-
- scores = self._index.score(query)
-
- # Pair each catalog entry with its score, sort descending
- ranked = sorted(
- zip(scores, self._catalog),
- key=lambda x: x[0],
- reverse=True,
- )
-
- # Return up to top_n entries that have a positive score
- return [entry for score, entry in ranked[: self._top_n] if score > 0.0]
diff --git a/src/lang2sql/components/retrieval/vector.py b/src/lang2sql/components/retrieval/vector.py
deleted file mode 100644
index c104590..0000000
--- a/src/lang2sql/components/retrieval/vector.py
+++ /dev/null
@@ -1,283 +0,0 @@
-from __future__ import annotations
-
-from typing import Optional
-
-from ...core.base import BaseComponent
-from ...core.catalog import CatalogEntry, IndexedChunk, RetrievalResult, TextDocument
-from ...core.hooks import TraceHook
-from ...core.ports import EmbeddingPort, VectorStorePort
-from .chunker import CatalogChunker, DocumentChunkerPort
-
-
-class VectorRetriever(BaseComponent):
- """
- Catalog + business document retrieval via vector similarity.
-
- RetrievalResult.schemas is deduplicated by source table — multiple chunks
- from the same table produce only one CatalogEntry in the result.
-
- Two construction patterns:
-
- 1. One-touch factory (quick start):
- retriever = VectorRetriever.from_sources(catalog=..., embedding=...)
- retriever.add(RecursiveCharacterChunker().split(more_docs)) # incremental
-
- 2. Explicit pipeline (full control, LangChain-style):
- chunks = (
- CatalogChunker().split(catalog) +
- RecursiveCharacterChunker().split(docs)
- )
- retriever = VectorRetriever.from_chunks(chunks, embedding=embedding, top_n=5)
- retriever.add(RecursiveCharacterChunker().split(new_docs)) # incremental
-
- Args:
- vectorstore: VectorStorePort implementation.
- embedding: EmbeddingPort implementation.
- registry: dict[chunk_id, IndexedChunk] mapping.
- top_n: Maximum schemas and context items to return. Default 5.
- score_threshold: Chunks with score <= this value are excluded. Default 0.0.
- name: Component name for tracing.
- hook: TraceHook for observability.
- """
-
- def __init__(
- self,
- *,
- vectorstore: VectorStorePort,
- embedding: EmbeddingPort,
- registry: dict,
- top_n: int = 5,
- score_threshold: float = 0.0,
- name: Optional[str] = None,
- hook: Optional[TraceHook] = None,
- ) -> None:
- super().__init__(name=name or "VectorRetriever", hook=hook)
- self._vectorstore = vectorstore
- self._embedding = embedding
- self._registry = registry
- self._top_n = top_n
- self._score_threshold = score_threshold
-
- @classmethod
- def from_chunks(
- cls,
- chunks: list[IndexedChunk],
- *,
- embedding: EmbeddingPort,
- vectorstore: Optional[VectorStorePort] = None,
- top_n: int = 5,
- score_threshold: float = 0.0,
- name: Optional[str] = None,
- hook: Optional[TraceHook] = None,
- ) -> "VectorRetriever":
- """
- LangChain-style factory: build from pre-split chunks.
-
- Embeds and stores the given chunks; no splitting is performed here.
- Use chunker.split(docs) before calling this method.
-
- Args:
- chunks: Pre-split list[IndexedChunk] (e.g. from CatalogChunker.split()).
- embedding: EmbeddingPort implementation.
- vectorstore: Defaults to InMemoryVectorStore.
- top_n: Maximum schemas and context items to return. Default 5.
- score_threshold: Score cutoff. Default 0.0.
- """
- from ...integrations.vectorstore.inmemory_ import InMemoryVectorStore
-
- store = vectorstore or InMemoryVectorStore()
- registry: dict = {}
- if chunks:
- ids = [c["chunk_id"] for c in chunks]
- texts = [c["text"] for c in chunks]
- vectors = embedding.embed_texts(texts)
- store.upsert(ids, vectors)
- registry.update({c["chunk_id"]: c for c in chunks})
-
- return cls(
- vectorstore=store,
- embedding=embedding,
- registry=registry,
- top_n=top_n,
- score_threshold=score_threshold,
- name=name,
- hook=hook,
- )
-
- @classmethod
- def from_sources(
- cls,
- *,
- catalog: list[CatalogEntry],
- embedding: EmbeddingPort,
- documents: Optional[list[TextDocument]] = None,
- splitter: Optional[DocumentChunkerPort] = None,
- vectorstore: Optional[VectorStorePort] = None,
- top_n: int = 5,
- score_threshold: float = 0.0,
- name: Optional[str] = None,
- hook: Optional[TraceHook] = None,
- ) -> "VectorRetriever":
- """
- One-touch factory: chunk, embed, and index in a single call.
-
- Internally calls from_chunks() after splitting catalog and documents.
- For incremental addition after construction, use retriever.add(chunks).
-
- Args:
- catalog: List of CatalogEntry dicts to index.
- embedding: EmbeddingPort implementation.
- documents: Optional list of TextDocument to index alongside catalog.
- splitter: Chunker for documents. Defaults to RecursiveCharacterChunker.
- Pass SemanticChunker(embedding=...) for higher quality.
- vectorstore: Defaults to InMemoryVectorStore.
- top_n: Maximum schemas and context items to return. Default 5.
- score_threshold: Score cutoff. Default 0.0.
- """
- from .chunker import RecursiveCharacterChunker
-
- _splitter = splitter or RecursiveCharacterChunker()
- chunks = CatalogChunker().split(catalog)
- if documents:
- chunks = chunks + _splitter.split(documents)
-
- return cls.from_chunks(
- chunks,
- embedding=embedding,
- vectorstore=vectorstore,
- top_n=top_n,
- score_threshold=score_threshold,
- name=name,
- hook=hook,
- )
-
- def add(self, chunks: list[IndexedChunk]) -> None:
- """
- Add pre-split chunks to the index incrementally.
-
- Use chunker.split(docs) before calling this method.
-
- Args:
- chunks: list[IndexedChunk] from chunker.split().
- """
- if not chunks:
- return
- ids = [c["chunk_id"] for c in chunks]
- texts = [c["text"] for c in chunks]
- vectors = self._embedding.embed_texts(texts)
- self._vectorstore.upsert(ids, vectors)
- self._registry.update({c["chunk_id"]: c for c in chunks})
-
- # ── Persistence ──────────────────────────────────────────────────
-
- def save(self, path: str) -> None:
- """벡터 인덱스와 registry를 path에 저장.
-
- FAISSVectorStore처럼 save()를 지원하는 store에서만 동작한다.
- InMemoryVectorStore 등 save()가 없는 store는 NotImplementedError.
-
- 저장 파일:
- {path} — FAISSVectorStore 벡터 인덱스
- {path}.meta — chunk_id 순서 목록 (FAISSVectorStore 내부)
- {path}.registry — registry JSON
- """
- import json
- import pathlib
-
- save_fn = getattr(self._vectorstore, "save", None)
- if save_fn is None:
- raise NotImplementedError(
- f"{type(self._vectorstore).__name__} does not support save(). "
- "Use FAISSVectorStore for file-based persistence."
- )
- save_fn(path)
- pathlib.Path(path + ".registry").write_text(
- json.dumps(self._registry), encoding="utf-8"
- )
-
- @classmethod
- def load(
- cls,
- path: str,
- *,
- vectorstore: VectorStorePort,
- embedding: EmbeddingPort,
- top_n: int = 5,
- score_threshold: float = 0.0,
- name: Optional[str] = None,
- hook: Optional[TraceHook] = None,
- ) -> "VectorRetriever":
- """저장된 registry를 복원해 VectorRetriever를 반환.
-
- 벡터 인덱스 복원은 호출자가 직접 수행한 뒤 vectorstore로 전달한다.
- 이렇게 하면 VectorRetriever가 특정 store 구현체에 의존하지 않는다.
-
- Args:
- path: save() 시 사용한 경로 (registry 파일 위치 기준).
- vectorstore: 이미 로드된 VectorStorePort 구현체.
- embedding: EmbeddingPort 구현체.
- top_n: 최대 반환 스키마/컨텍스트 수. 기본 5.
- score_threshold: 이 점수 이하는 결과에서 제외. 기본 0.0.
-
- Example:
- store = FAISSVectorStore.load(path)
- retriever = VectorRetriever.load(path, vectorstore=store, embedding=emb)
- """
- import json
- import pathlib
-
- registry_path = pathlib.Path(path + ".registry")
- if not registry_path.exists():
- raise FileNotFoundError(f"Registry file not found: {registry_path}")
-
- registry = json.loads(registry_path.read_text(encoding="utf-8"))
- return cls(
- vectorstore=vectorstore,
- embedding=embedding,
- registry=registry,
- top_n=top_n,
- score_threshold=score_threshold,
- name=name,
- hook=hook,
- )
-
- # ── Core retrieval ────────────────────────────────────────────────
-
- def _run(self, query: str) -> RetrievalResult:
- """
- Args:
- query: Natural language search query.
-
- Returns:
- RetrievalResult:
- .schemas — relevant CatalogEntry list (deduplicated, at most top_n)
- .context — relevant business document chunk texts (at most top_n)
- """
- if not self._registry:
- return RetrievalResult(schemas=[], context=[])
-
- query_vector = self._embedding.embed_query(query)
- # over-fetch by 3x so deduplication still yields top_n catalog entries
- raw = self._vectorstore.search(query_vector, k=self._top_n * 3)
-
- seen_tables: dict[str, CatalogEntry] = {} # source_id → CatalogEntry (dedup)
- context: list[str] = []
-
- for chunk_id, score in raw:
- if score <= self._score_threshold:
- continue
- chunk = self._registry.get(chunk_id)
- if chunk is None:
- continue
-
- if chunk["source_type"] == "catalog":
- src = chunk["source_id"]
- if src not in seen_tables:
- seen_tables[src] = chunk["metadata"] # full CatalogEntry
- elif chunk["source_type"] == "document":
- context.append(chunk["text"])
-
- return RetrievalResult(
- schemas=list(seen_tables.values())[: self._top_n],
- context=context[: self._top_n],
- )
diff --git a/src/lang2sql/core/__init__.py b/src/lang2sql/core/__init__.py
index e69de29..4aabff6 100644
--- a/src/lang2sql/core/__init__.py
+++ b/src/lang2sql/core/__init__.py
@@ -0,0 +1,16 @@
+"""Pure core — types, identity, and ports. No I/O, sits at the import root."""
+
+from .identity import Identity, Scope, ScopeLevel
+from .types import (
+ Completion,
+ Message,
+ Role,
+ ToolCall,
+ ToolResult,
+ ToolSpec,
+)
+
+__all__ = [
+ "Identity", "Scope", "ScopeLevel",
+ "Completion", "Message", "Role", "ToolCall", "ToolResult", "ToolSpec",
+]
diff --git a/src/lang2sql/core/base.py b/src/lang2sql/core/base.py
deleted file mode 100644
index 7a3c0d8..0000000
--- a/src/lang2sql/core/base.py
+++ /dev/null
@@ -1,171 +0,0 @@
-from __future__ import annotations
-
-from abc import ABC, abstractmethod
-from typing import Any, Optional
-
-from .exceptions import ComponentError, Lang2SQLError
-from .hooks import Event, NullHook, TraceHook, ms, now, summarize
-
-
-class BaseComponent(ABC):
- """
- Base class for all components.
-
- Design goals:
- - Components are plain callables (define-by-run friendly).
- - No enforced global state schema.
- - Hooks provide observability without requiring a graph engine.
-
- Public entry point is ``run()``. ``__call__`` is a convenience alias so
- components can be used as plain callables (e.g. in SequentialFlow steps).
- Subclasses implement ``_run()``.
- """
-
- def __init__(
- self, name: Optional[str] = None, hook: Optional[TraceHook] = None
- ) -> None:
- self.name: str = name or self.__class__.__name__
- self.hook: TraceHook = hook or NullHook()
-
- def __call__(self, *args: Any, **kwargs: Any) -> Any:
- return self.run(*args, **kwargs)
-
- def run(self, *args: Any, **kwargs: Any) -> Any:
- t0 = now()
- self.hook.on_event(
- Event(
- name="component.run",
- component=self.name,
- phase="start",
- ts=t0,
- input_summary=f"args={summarize(args)} kwargs={summarize(kwargs)}",
- )
- )
-
- try:
- out = self._run(*args, **kwargs)
-
- t1 = now()
- self.hook.on_event(
- Event(
- name="component.run",
- component=self.name,
- phase="end",
- ts=t1,
- duration_ms=ms(t0, t1),
- output_summary=summarize(out),
- )
- )
- return out
-
- except Lang2SQLError as e:
- # Preserve domain-level errors (IntegrationMissingError, ValidationError, etc.).
- t1 = now()
- self.hook.on_event(
- Event(
- name="component.run",
- component=self.name,
- phase="error",
- ts=t1,
- duration_ms=ms(t0, t1),
- error=f"{type(e).__name__}: {e}",
- )
- )
- raise
-
- except Exception as e:
- # Wrap non-domain errors into ComponentError.
- t1 = now()
- self.hook.on_event(
- Event(
- name="component.run",
- component=self.name,
- phase="error",
- ts=t1,
- duration_ms=ms(t0, t1),
- error=f"{type(e).__name__}: {e}",
- )
- )
- raise ComponentError(
- self.name,
- f"component failed ({type(e).__name__}: {e})",
- cause=e,
- ) from e
-
- @abstractmethod
- def _run(self, *args: Any, **kwargs: Any) -> Any:
- raise NotImplementedError
-
-
-class BaseFlow(ABC):
- """
- Base class for flows.
-
- Define-by-run:
- - Users write control-flow in pure Python (if/for/while).
- - We provide parts + presets, not a graph engine.
-
- Public entry point is ``run()``. ``__call__`` is a convenience alias.
- Subclasses implement ``_run()``.
- """
-
- def __init__(
- self, name: Optional[str] = None, hook: Optional[TraceHook] = None
- ) -> None:
- self.name: str = name or self.__class__.__name__
- self.hook: TraceHook = hook or NullHook()
-
- def __call__(self, *args: Any, **kwargs: Any) -> Any:
- return self.run(*args, **kwargs)
-
- def run(self, *args: Any, **kwargs: Any) -> Any:
- t0 = now()
- self.hook.on_event(
- Event(name="flow.run", component=self.name, phase="start", ts=t0)
- )
-
- try:
- out = self._run(*args, **kwargs)
- t1 = now()
- self.hook.on_event(
- Event(
- name="flow.run",
- component=self.name,
- phase="end",
- ts=t1,
- duration_ms=ms(t0, t1),
- )
- )
- return out
-
- except Lang2SQLError as e:
- t1 = now()
- self.hook.on_event(
- Event(
- name="flow.run",
- component=self.name,
- phase="error",
- ts=t1,
- duration_ms=ms(t0, t1),
- error=f"{type(e).__name__}: {e}",
- )
- )
- raise
-
- except Exception as e:
- t1 = now()
- self.hook.on_event(
- Event(
- name="flow.run",
- component=self.name,
- phase="error",
- ts=t1,
- duration_ms=ms(t0, t1),
- error=f"{type(e).__name__}: {e}",
- )
- )
- raise
-
- @abstractmethod
- def _run(self, *args: Any, **kwargs: Any) -> Any:
- raise NotImplementedError
diff --git a/src/lang2sql/core/catalog.py b/src/lang2sql/core/catalog.py
deleted file mode 100644
index 56413d0..0000000
--- a/src/lang2sql/core/catalog.py
+++ /dev/null
@@ -1,73 +0,0 @@
-from __future__ import annotations
-
-from dataclasses import dataclass, field
-from typing import TypedDict
-
-
-class CatalogEntry(TypedDict, total=False):
- name: str
- description: str
- columns: dict[str, str]
-
-
-class TextDocument(TypedDict, total=False):
- """Business document — data dictionaries, business rules, FAQ, etc."""
-
- id: str # Unique identifier (required)
- title: str # Document title (required)
- content: str # Full body text (required)
- source: str # File path, URL, etc. (optional)
- metadata: dict # Free-form additional info (optional)
-
-
-class IndexedChunk(TypedDict):
- """Minimum indexing unit stored in the vector store. Shared by catalog and document chunks."""
-
- chunk_id: str # e.g. "orders__0", "orders__col_1", "bizrule__2"
- text: str # Text to embed
- source_type: str # "catalog" | "document"
- source_id: str # Table name (catalog) or doc id (document)
- chunk_index: int # Position within the source (0-based)
- metadata: dict # catalog → full CatalogEntry / document → document meta
-
-
-@dataclass
-class RetrievalResult:
- """Return value of VectorRetriever — schema list + domain context."""
-
- schemas: list[CatalogEntry] = field(default_factory=list)
- context: list[str] = field(default_factory=list)
-
-
-@dataclass
-class GateResult:
- """QuestionGate 컴포넌트의 반환 타입."""
-
- suitable: bool
- reason: str
- missing_entities: list[str] = field(default_factory=list)
- requires_data_science: bool = False
-
-
-@dataclass
-class QuestionProfile:
- """질문 특성 프로파일."""
-
- is_timeseries: bool = False
- is_aggregation: bool = False
- has_filter: bool = False
- is_grouped: bool = False
- has_ranking: bool = False
- has_temporal_comparison: bool = False
- intent_type: str = "lookup" # trend | lookup | comparison | distribution
-
-
-@dataclass
-class TableScore:
- """개별 테이블의 적합도 평가 결과."""
-
- table_name: str
- score: float
- reason: str
- matched_columns: list[str] = field(default_factory=list)
- missing_entities: list[str] = field(default_factory=list)
diff --git a/src/lang2sql/core/context.py b/src/lang2sql/core/context.py
deleted file mode 100644
index 7d3a0f5..0000000
--- a/src/lang2sql/core/context.py
+++ /dev/null
@@ -1,182 +0,0 @@
-# lang2sql/core/context.py
-from __future__ import annotations
-
-from dataclasses import dataclass, field
-from typing import Any, Optional
-from collections.abc import MutableMapping
-
-
-@dataclass(init=False)
-class RunContext:
- """
- A minimal state carrier for define-by-run pipelines.
-
- Internal storage is generic:
- - inputs: user inputs (e.g., query)
- - artifacts: intermediate artifacts (e.g., schema candidates, prompt context)
- - outputs: final outputs (e.g., sql, validation)
- - error: structured error information (optional)
- - metadata: logs/traces/history (optional)
-
- Public UX can be domain-friendly via alias properties:
- - .query, .schema, .sql, .validation, etc.
- """
-
- inputs: dict[str, Any] = field(default_factory=dict)
- artifacts: dict[str, Any] = field(default_factory=dict)
- outputs: dict[str, Any] = field(default_factory=dict)
- error: Optional[dict[str, Any]] = None
- metadata: dict[str, Any] = field(default_factory=dict)
-
- def __init__(self, query: Optional[str] = None, **kwargs: Any) -> None:
- # Keep storage generic and always initialized.
- self.inputs = {}
- self.artifacts = {}
- self.outputs = {}
- self.error = None
- self.metadata = {}
-
- if query is not None:
- self.inputs["query"] = query
-
- # Allow lightweight initialization: RunContext(foo=..., bar=...)
- # Store unknown fields in metadata to avoid schema lock-in.
- if kwargs:
- self.metadata.update(kwargs)
-
- # -------------------------
- # Domain-friendly aliases
- # -------------------------
-
- @property
- def query(self) -> str:
- """Return the user question/query."""
- v = self.inputs.get("query", "")
- if v is None:
- return ""
- if isinstance(v, str):
- return v
- # Be forgiving: keep pipeline running, but avoid crashing on accidental types.
- return str(v)
-
- @query.setter
- def query(self, value: str) -> None:
- if not isinstance(value, str):
- raise TypeError(f"RunContext.query must be str, got {type(value).__name__}")
- self.inputs["query"] = value
-
- @property
- def schema(self) -> MutableMapping[str, Any]:
- """
- Return the schema artifact mapping.
-
- Typical keys (convention):
- - catalog: full schema catalog (list/provider)
- - selected: top-k table candidates
- - context: final context text used for prompting
- """
- v = self.artifacts.get("schema")
- if v is None:
- v = {}
- self.artifacts["schema"] = v
- return v
-
- if isinstance(v, MutableMapping):
- return v
-
- # If someone wrote a non-mapping value, replace it to keep conventions stable.
- v = {}
- self.artifacts["schema"] = v
- return v
-
- @property
- def sql(self) -> str:
- """Return the final SQL string."""
- v = self.outputs.get("sql", "")
- if v is None:
- return ""
- if isinstance(v, str):
- return v
- return str(v)
-
- @sql.setter
- def sql(self, value: str) -> None:
- if not isinstance(value, str):
- raise TypeError(f"RunContext.sql must be str, got {type(value).__name__}")
- self.outputs["sql"] = value
-
- # sql changes invalidate validation (validation is derived from sql)
- # self.outputs.pop("validation", None)
-
- @property
- def validation(self) -> Any:
- """Return validation result object, if present."""
- return self.outputs.get("validation")
-
- @validation.setter
- def validation(self, value: Any) -> None:
- self.outputs["validation"] = value
-
- # Optional convenience aliases (recommended for discoverability)
-
- @property
- def schema_catalog(self) -> Any:
- """Alias for schema['catalog']."""
- return self.schema.get("catalog")
-
- @schema_catalog.setter
- def schema_catalog(self, value: Any) -> None:
- self.schema["catalog"] = value
-
- @property
- def schema_selected(self) -> Any:
- """Alias for schema['selected']."""
- return self.schema.get("selected")
-
- @schema_selected.setter
- def schema_selected(self, value: Any) -> None:
- self.schema["selected"] = value
-
- @property
- def schema_context(self) -> str:
- """Alias for schema['context']."""
- v = self.schema.get("context", "")
- if v is None:
- return ""
- if isinstance(v, str):
- return v
- return str(v)
-
- @schema_context.setter
- def schema_context(self, value: str) -> None:
- if not isinstance(value, str):
- raise TypeError(
- f"RunContext.schema_context must be str, got {type(value).__name__}"
- )
- self.schema["context"] = value
-
- # -------------------------
- # Small utilities
- # -------------------------
-
- def push_meta(self, key: str, value: Any) -> None:
- """
- Append a value into metadata[key] list.
-
- Example:
- run.push_meta("sql_drafts", sql)
- run.push_meta("events", event)
- """
- arr = self.metadata.setdefault(key, [])
- if not isinstance(arr, list):
- raise TypeError(f"metadata['{key}'] exists but is not a list")
- arr.append(value)
-
- def get_meta_list(self, key: str) -> list[Any]:
- """Return metadata[key] as a list (empty list if missing)."""
- v = self.metadata.get(key)
- if v is None:
- return []
- if isinstance(v, list):
- return v
- return [v]
diff --git a/src/lang2sql/core/exceptions.py b/src/lang2sql/core/exceptions.py
deleted file mode 100644
index c4e8556..0000000
--- a/src/lang2sql/core/exceptions.py
+++ /dev/null
@@ -1,39 +0,0 @@
-# lang2sql/core/exceptions.py
-from __future__ import annotations
-
-
-class Lang2SQLError(Exception):
- """Base error for lang2sql."""
-
-
-class IntegrationMissingError(Lang2SQLError):
- def __init__(
- self, integration: str, extra: str | None = None, hint: str | None = None
- ):
- self.integration = integration
- self.extra = extra
- self.hint = hint
-
- msg = f"Missing optional integration: {integration}."
- if extra:
- msg += f" Install with: pip install 'lang2sql[{extra}]'"
- if hint:
- msg += f" ({hint})"
- super().__init__(msg)
-
-
-class ComponentError(Lang2SQLError):
- def __init__(self, component: str, message: str, *, cause: Exception | None = None):
- self.component = component
- self.cause = cause
- super().__init__(f"[{component}] {message}")
-
-
-class ValidationError(Lang2SQLError):
- pass
-
-
-class ContractError(Lang2SQLError):
- """Raised when a component violates a required call/return contract."""
-
- pass
diff --git a/src/lang2sql/core/hooks.py b/src/lang2sql/core/hooks.py
deleted file mode 100644
index f4c8428..0000000
--- a/src/lang2sql/core/hooks.py
+++ /dev/null
@@ -1,64 +0,0 @@
-# lang2sql/core/hooks.py
-from __future__ import annotations
-
-from dataclasses import dataclass, field
-from typing import Any, Protocol, Optional, Literal
-import time
-
-
-@dataclass
-class Event:
- name: str # e.g., "component.run"
- component: str # e.g., "KeywordTableRetriever"
- phase: Literal["start", "end", "error"]
- ts: float
- duration_ms: Optional[float] = None
-
- # human-friendly summaries (debug only)
- input_summary: Optional[str] = None
- output_summary: Optional[str] = None
- error: Optional[str] = None
-
- # structured payload (for UI / filtering / tests)
- data: dict[str, Any] = field(default_factory=dict)
-
-
-class TraceHook(Protocol):
- def on_event(self, event: Event) -> None: ...
-
-
-class NullHook(TraceHook):
- def on_event(self, event: Event) -> None:
- return
-
-
-class MemoryHook(TraceHook):
- def __init__(self) -> None:
- self.events: list[Event] = []
-
- def on_event(self, event: Event) -> None:
- self.events.append(event)
-
- def clear(self) -> None:
- self.events.clear()
-
- def snapshot(self) -> list[Event]:
- return list(self.events)
-
-
-def now() -> float:
- return time.time()
-
-
-def ms(start: float, end: float) -> float:
- return (end - start) * 1000.0
-
-
-def summarize(x: Any, max_len: int = 240) -> str:
- try:
- s = repr(x)
- except Exception:
- s = f""
- if len(s) > max_len:
- s = s[: max_len - 3] + "..."
- return s
diff --git a/src/lang2sql/core/identity.py b/src/lang2sql/core/identity.py
new file mode 100644
index 0000000..f527991
--- /dev/null
+++ b/src/lang2sql/core/identity.py
@@ -0,0 +1,94 @@
+"""Who is asking, and in which semantic scope.
+
+Federation (★④) resolves a metric definition by walking *most specific →
+least specific* scope. :class:`Identity` is the frontend-agnostic carrier of
+that scope chain: Discord fills it from guild/channel/thread IDs, the CLI from
+flags, a future Slack adapter from workspace/channel — but the harness only
+ever sees this shape.
+"""
+
+from __future__ import annotations
+
+from dataclasses import dataclass
+from enum import Enum
+
+
+class ScopeLevel(str, Enum):
+ """Federation scope levels, ordered narrow → wide.
+
+ ``THREAD`` overrides ``CHANNEL`` overrides ``GUILD``; ``BUILTIN`` is the
+ empty system default that every guild inherits from.
+ """
+
+ THREAD = "thread"
+ CHANNEL = "channel"
+ GUILD = "guild"
+ BUILTIN = "builtin"
+
+
+@dataclass(frozen=True)
+class Scope:
+ """A single addressable point in the federation tree."""
+
+ level: ScopeLevel
+ key: str # e.g. guild id, channel id, thread id; "" for BUILTIN
+
+ def __str__(self) -> str:
+ return f"{self.level.value}:{self.key}" if self.key else self.level.value
+
+
+@dataclass(frozen=True)
+class Identity:
+ """Frontend-agnostic identity + scope coordinates for one request.
+
+ ``guild_id``/``channel_id``/``thread_id`` are optional so the same type
+ serves a DM (guild only), a channel mention, or a thread reply.
+ """
+
+ user_id: str
+ guild_id: str | None = None
+ channel_id: str | None = None
+ thread_id: str | None = None
+ is_admin: bool = False
+
+ def session_key(self) -> str:
+ """Stable key for persisting/looking up this conversation's session.
+
+ The thread (or channel, or DM) is the unit of conversation, so the key
+ is the narrowest container that exists.
+ """
+ if self.thread_id:
+ return f"thread:{self.thread_id}"
+ if self.channel_id:
+ return f"channel:{self.channel_id}"
+ if self.guild_id:
+ return f"guild:{self.guild_id}:{self.user_id}"
+ return f"dm:{self.user_id}"
+
+ def scope_chain(self) -> list[Scope]:
+ """Scopes from most specific to least, for federation resolution.
+
+ ``define_metric`` writes to ``scope_chain()[0]`` by default; lookup
+ reads down the chain and stops at the first definition found.
+ """
+ chain: list[Scope] = []
+ if self.thread_id:
+ chain.append(Scope(ScopeLevel.THREAD, self.thread_id))
+ if self.channel_id:
+ chain.append(Scope(ScopeLevel.CHANNEL, self.channel_id))
+ if self.guild_id:
+ chain.append(Scope(ScopeLevel.GUILD, self.guild_id))
+ chain.append(Scope(ScopeLevel.BUILTIN, ""))
+ return chain
+
+ def default_write_scope(self) -> Scope:
+ """Where a new definition lands when the user gives no ``--scope``.
+
+ Per v4.1 §3.5 the default is the current channel; a DM falls back to a
+ per-user pseudo-scope so personal definitions don't leak.
+ """
+ if self.channel_id:
+ return Scope(ScopeLevel.CHANNEL, self.channel_id)
+ if self.guild_id:
+ return Scope(ScopeLevel.GUILD, self.guild_id)
+ return Scope(ScopeLevel.CHANNEL, f"dm:{self.user_id}")
diff --git a/src/lang2sql/core/ports.py b/src/lang2sql/core/ports.py
deleted file mode 100644
index d1bf462..0000000
--- a/src/lang2sql/core/ports.py
+++ /dev/null
@@ -1,85 +0,0 @@
-from __future__ import annotations
-
-from typing import Any, Protocol, runtime_checkable
-
-from .catalog import CatalogEntry, TextDocument
-
-
-class LLMPort(Protocol):
- """Abstracts LLM backends (Anthropic, OpenAI, etc.)."""
-
- def invoke(self, messages: list[dict[str, str]]) -> str: ...
-
-
-class DBPort(Protocol):
- """Abstracts database backends (SQLAlchemy, etc.)."""
-
- def execute(self, sql: str) -> list[dict[str, Any]]: ...
-
-
-class EmbeddingPort(Protocol):
- """Abstracts embedding backends (OpenAI, Azure, Bedrock, etc.)."""
-
- def embed_query(self, text: str) -> list[float]: ...
-
- def embed_texts(self, texts: list[str]) -> list[list[float]]: ...
-
-
-class VectorStorePort(Protocol):
- """Abstracts vector store backends (InMemory, FAISS, pgvector, etc.)."""
-
- def search(self, vector: list[float], k: int) -> list[tuple[str, float]]:
- """
- Return the k nearest vectors.
-
- Returns:
- List of (chunk_id, score) sorted by score descending.
- Score range: [-1, 1] (cosine similarity).
- """
- ...
-
- def upsert(self, ids: list[str], vectors: list[list[float]]) -> None:
- """
- Store or update vectors by chunk_id.
-
- Implementations must merge incoming entries into existing ones —
- calling upsert twice must not lose entries from the first call.
-
- Args:
- ids: List of chunk_ids.
- vectors: Corresponding embedding vectors (len(ids) == len(vectors)).
- """
- ...
-
-
-@runtime_checkable
-class DocumentLoaderPort(Protocol):
- """Converts a file path or directory to list[TextDocument]."""
-
- def load(self, path: str) -> list[TextDocument]: ...
-
-
-class CatalogLoaderPort(Protocol):
- """Abstracts catalog loading from external sources (DataHub, file, database, etc.)."""
-
- def load(self) -> list[CatalogEntry]: ...
-
-
-class DBExplorerPort(Protocol):
- """DB 에이전틱 탐색 인터페이스. Agent가 DB를 직접 탐색할 때 사용.
-
- 메서드 선정 원칙:
- - DDL에 이미 있는 정보(컬럼 목록, FK, PK)는 별도 메서드 없음
- - 통계/집계는 execute_read_only()로 직접 질의
- - 관계 추론은 LLM에 위임 (휴리스틱 제거)
- """
-
- def list_tables(self, schema: str | None = None) -> list[str]: ...
-
- def get_ddl(self, table: str, *, schema: str | None = None) -> str: ...
-
- def sample_data(
- self, table: str, *, limit: int = 5, schema: str | None = None
- ) -> list[dict]: ...
-
- def execute_read_only(self, sql: str) -> list[dict]: ...
diff --git a/src/lang2sql/core/ports/__init__.py b/src/lang2sql/core/ports/__init__.py
new file mode 100644
index 0000000..9928dea
--- /dev/null
+++ b/src/lang2sql/core/ports/__init__.py
@@ -0,0 +1,44 @@
+"""Abstract ports — the standardised "wall sockets" of the system.
+
+Every outbound dependency and every pluggable strategy is one Protocol here.
+V1 ships the simplest implementation of each; later versions add adapters
+without the harness or tools changing.
+"""
+
+from .audit import AuditEvent, AuditPort
+from .explorer import Column, ExplorerPort, Table
+from .frontend import FrontendPort, InboundMessage, OutboundMessage
+from .ingestion import (
+ CandidateKind,
+ DocExtractorPort,
+ Document,
+ SemanticCandidate,
+ SourcePort,
+)
+from .llm import LLMPort
+from .memory import ExtractorPort, Fact, RecallPort, StorePort
+from .safety import (
+ SafetyContext,
+ SafetyDecision,
+ SafetyLayerPort,
+ SafetyPipelinePort,
+ Verdict,
+)
+from .secrets import SecretsPort
+from .semantic_scope import ScopeResolverPort
+from .session_store import SessionStorePort
+from .tool import ToolPort
+
+__all__ = [
+ "AuditEvent", "AuditPort",
+ "Column", "ExplorerPort", "Table",
+ "FrontendPort", "InboundMessage", "OutboundMessage",
+ "CandidateKind", "DocExtractorPort", "Document", "SemanticCandidate", "SourcePort",
+ "LLMPort",
+ "ExtractorPort", "Fact", "RecallPort", "StorePort",
+ "SafetyContext", "SafetyDecision", "SafetyLayerPort", "SafetyPipelinePort", "Verdict",
+ "SecretsPort",
+ "ScopeResolverPort",
+ "SessionStorePort",
+ "ToolPort",
+]
diff --git a/src/lang2sql/core/ports/audit.py b/src/lang2sql/core/ports/audit.py
new file mode 100644
index 0000000..63940d8
--- /dev/null
+++ b/src/lang2sql/core/ports/audit.py
@@ -0,0 +1,29 @@
+"""Audit port — append-only record of what the agent did.
+
+Backs ``/audit me``. V1 is a plain append-only SQLite table (no hash chain —
+that's V2). Every SQL execution, definition change, and ingestion lands here.
+"""
+
+from __future__ import annotations
+
+from dataclasses import dataclass, field
+from typing import Any, Protocol, runtime_checkable
+
+
+@dataclass
+class AuditEvent:
+ actor: str # user_id
+ action: str # "run_sql" | "define_metric" | "ingest" | ...
+ scope: str # session/scope key
+ detail: dict[str, Any] = field(default_factory=dict)
+ ts: float = 0.0 # epoch seconds; filled by the store if 0
+
+
+@runtime_checkable
+class AuditPort(Protocol):
+ async def record(self, event: AuditEvent) -> None:
+ ...
+
+ async def query(self, actor: str, limit: int = 20) -> list[AuditEvent]:
+ """Recent events for one actor, newest first."""
+ ...
diff --git a/src/lang2sql/core/ports/explorer.py b/src/lang2sql/core/ports/explorer.py
new file mode 100644
index 0000000..d9776ae
--- /dev/null
+++ b/src/lang2sql/core/ports/explorer.py
@@ -0,0 +1,54 @@
+"""DB explorer port — read-only schema introspection.
+
+The agent uses this to discover tables/columns before writing SQL. V1 backs it
+with a PostgreSQL adapter; the contract is dialect-neutral so BigQuery et al.
+slot in later.
+"""
+
+from __future__ import annotations
+
+from dataclasses import dataclass, field
+from typing import Protocol, runtime_checkable
+
+
+@dataclass
+class Column:
+ name: str
+ type: str
+ nullable: bool = True
+ description: str = "" # may be auto-enriched (v1.5 metadata layer)
+
+
+@dataclass
+class Table:
+ name: str
+ schema: str = "public"
+ columns: list[Column] = field(default_factory=list)
+ description: str = ""
+
+ @property
+ def qualified(self) -> str:
+ return f"{self.schema}.{self.name}" if self.schema else self.name
+
+
+@runtime_checkable
+class ExplorerPort(Protocol):
+ """Introspect a connected database, read-only."""
+
+ async def list_tables(self) -> list[Table]:
+ """Tables visible to the connection (columns may be unpopulated)."""
+ ...
+
+ async def describe_table(self, name: str) -> Table:
+ """Full column detail for one table."""
+ ...
+
+ async def sample_rows(self, name: str, limit: int = 5) -> list[dict]:
+ """A few rows to give the model a feel for the data."""
+ ...
+
+ async def execute(self, sql: str, limit: int = 1000) -> list[dict]:
+ """Run a read-only query (already cleared by the safety pipeline) and
+ return up to ``limit`` rows. The ``run_sql`` tool calls this only after
+ a PASS verdict; the adapter must never see un-gated SQL."""
+ ...
diff --git a/src/lang2sql/core/ports/frontend.py b/src/lang2sql/core/ports/frontend.py
new file mode 100644
index 0000000..c56df5e
--- /dev/null
+++ b/src/lang2sql/core/ports/frontend.py
@@ -0,0 +1,45 @@
+"""Frontend port — the Phase boundary (★ multi-interface).
+
+Discord (Phase 1), Slack (Phase 2), Web (Phase 3) and the dev CLI all implement
+this. The harness never imports a frontend; frontends drive the harness. Adding
+Slack later is "implement this Protocol" with zero core changes.
+"""
+
+from __future__ import annotations
+
+from dataclasses import dataclass, field
+from typing import Protocol, runtime_checkable
+
+from ..identity import Identity
+
+
+@dataclass
+class InboundMessage:
+ """Normalised user input from any frontend."""
+
+ identity: Identity
+ text: str
+ attachments: list[bytes] = field(default_factory=list)
+ attachment_names: list[str] = field(default_factory=list)
+
+
+@dataclass
+class OutboundMessage:
+ """Normalised agent output a frontend renders natively."""
+
+ text: str
+ file_bytes: bytes | None = None # e.g. CSV when result > 50 rows
+ file_name: str | None = None
+
+
+@runtime_checkable
+class FrontendPort(Protocol):
+ """Translate between a chat platform and the harness."""
+
+ async def receive(self) -> InboundMessage:
+ """Block until the next user message arrives."""
+ ...
+
+ async def send(self, identity: Identity, message: OutboundMessage) -> None:
+ """Deliver agent output back to the originating conversation."""
+ ...
diff --git a/src/lang2sql/core/ports/ingestion.py b/src/lang2sql/core/ports/ingestion.py
new file mode 100644
index 0000000..38f37fe
--- /dev/null
+++ b/src/lang2sql/core/ports/ingestion.py
@@ -0,0 +1,55 @@
+"""Ingestion ports — document → semantic candidates (★③).
+
+A Source × Extractor matrix: one new Source automatically pairs with every
+Extractor. V1 = file upload × LLM extraction; v1.5 adds URL source + DDL
+parser, etc. Extracted candidates are shown for user confirm before landing in
+the semantic layer (documents are the source of truth).
+"""
+
+from __future__ import annotations
+
+from dataclasses import dataclass, field
+from enum import Enum
+from typing import Protocol, runtime_checkable
+
+
+@dataclass
+class Document:
+ """Raw material pulled from some source."""
+
+ name: str
+ text: str
+ source_id: str = "" # preserved on resulting semantic entries
+
+
+class CandidateKind(str, Enum):
+ METRIC = "metric"
+ DIMENSION = "dimension"
+ RULE = "rule"
+
+
+@dataclass
+class SemanticCandidate:
+ """A proposed definition awaiting user confirmation."""
+
+ kind: CandidateKind
+ name: str
+ definition: str
+ applies_to: str = ""
+ source_id: str = ""
+
+
+@runtime_checkable
+class SourcePort(Protocol):
+ """Where a document comes from (file/URL/Notion/…). Axis 1."""
+
+ async def fetch(self, ref: str, blob: bytes | None = None) -> Document:
+ ...
+
+
+@runtime_checkable
+class DocExtractorPort(Protocol):
+ """How definitions are pulled from a document (LLM/DDL/…). Axis 2."""
+
+ async def extract(self, doc: Document) -> list[SemanticCandidate]:
+ ...
diff --git a/src/lang2sql/core/ports/llm.py b/src/lang2sql/core/ports/llm.py
new file mode 100644
index 0000000..6623880
--- /dev/null
+++ b/src/lang2sql/core/ports/llm.py
@@ -0,0 +1,25 @@
+"""LLM port — the one outbound call the agent loop makes per turn.
+
+V1 wires a single OpenAI ``gpt-4.1-mini`` adapter behind this. Because the loop
+depends only on this Protocol, swapping in Anthropic/NIM later (v1.5+) is an
+adapter add with zero loop changes.
+"""
+
+from __future__ import annotations
+
+from typing import Protocol, Sequence, runtime_checkable
+
+from ..types import Completion, Message, ToolSpec
+
+
+@runtime_checkable
+class LLMPort(Protocol):
+ """Tool-calling chat completion."""
+
+ async def complete(
+ self,
+ messages: Sequence[Message],
+ tools: Sequence[ToolSpec] = (),
+ ) -> Completion:
+ """Run one completion. May return tool calls, a final answer, or both."""
+ ...
diff --git a/src/lang2sql/core/ports/memory.py b/src/lang2sql/core/ports/memory.py
new file mode 100644
index 0000000..14ffbc6
--- /dev/null
+++ b/src/lang2sql/core/ports/memory.py
@@ -0,0 +1,56 @@
+"""Memory ports — Hermes memory split into 3 independent axes (★②).
+
+Store (where), Recall (what to fetch), Extractor (how new facts are made) each
+evolve on their own. V1 = in-memory dict + inject-all + manual ``/remember``;
+v1.5 swaps Store→SQLite or Recall→keyword as a single adapter add.
+"""
+
+from __future__ import annotations
+
+from dataclasses import dataclass, field
+from typing import Protocol, Sequence, runtime_checkable
+
+from ..types import Message
+
+
+@dataclass
+class Fact:
+ """A remembered statement, scoped to a user/conversation."""
+
+ id: str
+ owner: str # user_id or scope key
+ text: str
+ source: str = "manual" # "manual" (/remember) | "auto" (v1.5 extractor)
+ ts: float = 0.0
+
+
+@runtime_checkable
+class StorePort(Protocol):
+ """Where facts live. Axis 1."""
+
+ async def add(self, fact: Fact) -> None: ...
+
+ async def all(self, owner: str) -> list[Fact]: ...
+
+
+@runtime_checkable
+class RecallPort(Protocol):
+ """Which facts to surface for the current question. Axis 2.
+
+ V1 returns everything; v1.5 filters by keyword, v2 by vector similarity.
+ """
+
+ async def recall(self, owner: str, query: str, store: StorePort) -> list[Fact]:
+ ...
+
+
+@runtime_checkable
+class ExtractorPort(Protocol):
+ """How new facts get created. Axis 3.
+
+ V1 only the explicit ``/remember`` path produces facts (so ``extract``
+ yields nothing); v1.5 mines the transcript with an LLM.
+ """
+
+ async def extract(self, owner: str, transcript: Sequence[Message]) -> list[Fact]:
+ ...
diff --git a/src/lang2sql/core/ports/safety.py b/src/lang2sql/core/ports/safety.py
new file mode 100644
index 0000000..8b69d11
--- /dev/null
+++ b/src/lang2sql/core/ports/safety.py
@@ -0,0 +1,60 @@
+"""Safety port — the ★① pipeline that gates every SQL execution.
+
+Airport-security model: SQL passes a *line* of layers; each returns pass /
+block / needs-confirmation / rewrite. New checks (v1.5 AST validation, function
+blocklist, metadata enrichment) are "one class + slot it in the line" with zero
+``run_sql`` changes.
+"""
+
+from __future__ import annotations
+
+from dataclasses import dataclass, field
+from enum import Enum
+from typing import Protocol, Sequence, runtime_checkable
+
+
+class Verdict(str, Enum):
+ PASS = "pass"
+ BLOCK = "block"
+ CONFIRM = "confirm" # ask the user before proceeding
+ REWRITE = "rewrite" # layer rewrote the SQL (e.g. attach LIMIT)
+
+
+@dataclass
+class SafetyDecision:
+ verdict: Verdict
+ sql: str # possibly rewritten
+ reason: str = ""
+ layer: str = "" # which layer decided
+ confirm_prompt: str = "" # populated when verdict is CONFIRM
+
+
+@dataclass
+class SafetyContext:
+ """Knobs a layer reads (timeout, row cap). Grows over versions."""
+
+ timeout_seconds: int = 30
+ row_limit: int = 1000
+ extras: dict = field(default_factory=dict)
+
+
+@runtime_checkable
+class SafetyLayerPort(Protocol):
+ """One check in the line. Pure: SQL in, decision out."""
+
+ @property
+ def name(self) -> str: ...
+
+ def check(self, sql: str, ctx: SafetyContext) -> SafetyDecision:
+ ...
+
+
+@runtime_checkable
+class SafetyPipelinePort(Protocol):
+ """Runs layers in order; first non-PASS short-circuits."""
+
+ def evaluate(self, sql: str, ctx: SafetyContext) -> SafetyDecision:
+ ...
+
+ @property
+ def layers(self) -> Sequence[SafetyLayerPort]: ...
diff --git a/src/lang2sql/core/ports/secrets.py b/src/lang2sql/core/ports/secrets.py
new file mode 100644
index 0000000..92c7fda
--- /dev/null
+++ b/src/lang2sql/core/ports/secrets.py
@@ -0,0 +1,24 @@
+"""Secrets port — per-scope encrypted credential storage.
+
+Holds DB connection strings / API keys a guild registers via ``/connect``. V1
+backs it with an encrypted SQLite store; the boundary lets a KMS-backed impl
+drop in later without touching callers.
+"""
+
+from __future__ import annotations
+
+from typing import Protocol, runtime_checkable
+
+
+@runtime_checkable
+class SecretsPort(Protocol):
+ async def get(self, scope: str, key: str) -> str | None:
+ """Decrypt and return one secret, or ``None`` if unset."""
+ ...
+
+ async def set(self, scope: str, key: str, value: str) -> None:
+ """Encrypt and persist one secret under ``scope``."""
+ ...
+
+ async def delete(self, scope: str, key: str) -> None:
+ ...
diff --git a/src/lang2sql/core/ports/semantic_scope.py b/src/lang2sql/core/ports/semantic_scope.py
new file mode 100644
index 0000000..71c746c
--- /dev/null
+++ b/src/lang2sql/core/ports/semantic_scope.py
@@ -0,0 +1,34 @@
+"""Semantic scope port — git-like federation resolution (★④).
+
+The same term ("active user") can mean different things per team. Definitions
+are stored against a :class:`Scope`; resolution walks ``Identity.scope_chain``
+narrow→wide and the first hit wins. No conflicts — each scope lives in its own
+branch. V1 = guild/channel/thread auto-resolution + ``/semantic show``.
+"""
+
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Protocol, runtime_checkable
+
+from ..identity import Identity, Scope
+
+if TYPE_CHECKING:
+ from ...semantic.types import SemanticEntry
+ from ...semantic.layer import SemanticLayer
+
+
+@runtime_checkable
+class ScopeResolverPort(Protocol):
+ """Resolve the effective semantic layer for an identity's scope chain."""
+
+ async def effective_layer(self, identity: Identity) -> "SemanticLayer":
+ """Merge scopes narrow→wide so the most specific definition wins."""
+ ...
+
+ async def define(self, scope: Scope, entry: "SemanticEntry") -> None:
+ """Persist one definition at an explicit scope."""
+ ...
+
+ async def entries_at(self, scope: Scope) -> "list[SemanticEntry]":
+ """Definitions stored exactly at ``scope`` (no inheritance)."""
+ ...
diff --git a/src/lang2sql/core/ports/session_store.py b/src/lang2sql/core/ports/session_store.py
new file mode 100644
index 0000000..5a3a763
--- /dev/null
+++ b/src/lang2sql/core/ports/session_store.py
@@ -0,0 +1,22 @@
+"""Session store port — persist/restore a conversation by session key.
+
+This is what makes the bot *not* stateless (tiebreaker #4, context
+preservation). V1 persists to SQLite keyed by :meth:`Identity.session_key`.
+"""
+
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Protocol, runtime_checkable
+
+if TYPE_CHECKING:
+ from ...harness.session import Session
+
+
+@runtime_checkable
+class SessionStorePort(Protocol):
+ async def load(self, key: str) -> "Session | None":
+ """Restore a saved session, or ``None`` for a fresh conversation."""
+ ...
+
+ async def save(self, key: str, session: "Session") -> None:
+ ...
diff --git a/src/lang2sql/core/ports/tool.py b/src/lang2sql/core/ports/tool.py
new file mode 100644
index 0000000..34d58ac
--- /dev/null
+++ b/src/lang2sql/core/ports/tool.py
@@ -0,0 +1,29 @@
+"""Tool port — a single capability the agent can invoke.
+
+Tools are ctx-aware: ``run`` receives the live :class:`HarnessContext` so a
+tool can reach the explorer, semantic layer, safety pipeline, etc. without
+globals. The harness advertises ``spec`` to the LLM and dispatches by ``name``.
+"""
+
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Any, Protocol, runtime_checkable
+
+from ..types import ToolResult, ToolSpec
+
+if TYPE_CHECKING: # avoid import cycle: harness imports core, not vice versa
+ from ...harness.context import HarnessContext
+
+
+@runtime_checkable
+class ToolPort(Protocol):
+ """One invocable capability (run_sql, explore_schema, define_metric, …)."""
+
+ @property
+ def spec(self) -> ToolSpec:
+ """Name + description + JSON-Schema params advertised to the model."""
+ ...
+
+ async def run(self, args: dict[str, Any], ctx: "HarnessContext") -> ToolResult:
+ """Execute with model-supplied ``args`` against the live context."""
+ ...
diff --git a/src/lang2sql/core/types.py b/src/lang2sql/core/types.py
new file mode 100644
index 0000000..4637585
--- /dev/null
+++ b/src/lang2sql/core/types.py
@@ -0,0 +1,85 @@
+"""Pure domain types shared across the whole system.
+
+These carry no behaviour and no I/O — they are the vocabulary every layer
+(harness, tools, frontends, adapters) speaks. Keeping them dependency-free is
+what lets the ``core`` package sit at the bottom of the import graph.
+"""
+
+from __future__ import annotations
+
+from dataclasses import dataclass, field
+from enum import Enum
+from typing import Any
+
+
+class Role(str, Enum):
+ """Author of a conversation message."""
+
+ SYSTEM = "system"
+ USER = "user"
+ ASSISTANT = "assistant"
+ TOOL = "tool"
+
+
+@dataclass
+class ToolCall:
+ """A model's request to invoke one tool.
+
+ ``arguments`` is the already-parsed JSON object the model emitted; adapters
+ are responsible for turning provider-specific payloads into this shape.
+ """
+
+ id: str
+ name: str
+ arguments: dict[str, Any] = field(default_factory=dict)
+
+
+@dataclass
+class ToolResult:
+ """Outcome of running a :class:`ToolCall`, fed back to the model."""
+
+ call_id: str
+ content: str
+ is_error: bool = False
+
+
+@dataclass
+class Message:
+ """One entry in a conversation transcript.
+
+ A single assistant turn may both say something (``content``) and request
+ tools (``tool_calls``). Tool-role messages carry ``tool_call_id`` to bind
+ their result back to the originating call.
+ """
+
+ role: Role
+ content: str = ""
+ tool_calls: list[ToolCall] = field(default_factory=list)
+ tool_call_id: str | None = None
+ name: str | None = None
+
+
+@dataclass
+class ToolSpec:
+ """Catalog entry the harness advertises to the LLM.
+
+ ``parameters`` is a JSON-Schema object describing the tool's arguments,
+ matching what tool-calling LLM APIs expect.
+ """
+
+ name: str
+ description: str
+ parameters: dict[str, Any] = field(default_factory=dict)
+
+
+@dataclass
+class Completion:
+ """What an :class:`~lang2sql.core.ports.llm.LLMPort` returns for one call.
+
+ Either ``content`` (final answer) or ``tool_calls`` (the model wants to act)
+ or both. The agent loop branches on whether ``tool_calls`` is non-empty.
+ """
+
+ content: str = ""
+ tool_calls: list[ToolCall] = field(default_factory=list)
+ finish_reason: str | None = None
diff --git a/src/lang2sql/factory.py b/src/lang2sql/factory.py
deleted file mode 100644
index bd05b41..0000000
--- a/src/lang2sql/factory.py
+++ /dev/null
@@ -1,236 +0,0 @@
-"""환경변수 기반 LLM/Embedding/DB 인스턴스 팩토리.
-
-레거시 utils/llm/core/factory.py를 LangChain 없이 재구현한 것.
-CLI와 Streamlit UI 양쪽에서 사용한다.
-"""
-
-from __future__ import annotations
-
-import os
-
-from .core.ports import DBExplorerPort, DBPort, EmbeddingPort, LLMPort
-
-
-def build_llm_from_env() -> LLMPort:
- """환경변수 LLM_PROVIDER에 따라 적절한 LLMPort 인스턴스를 생성한다."""
- provider = os.getenv("LLM_PROVIDER", "openai").lower()
-
- if provider == "openai":
- from .integrations.llm.openai_ import OpenAILLM
-
- return OpenAILLM(
- model=os.getenv("OPEN_AI_LLM_MODEL", "gpt-4o"),
- api_key=os.getenv("OPEN_AI_KEY"),
- )
-
- if provider == "anthropic":
- from .integrations.llm.anthropic_ import AnthropicLLM
-
- return AnthropicLLM(
- model=os.getenv("ANTHROPIC_LLM_MODEL", "claude-sonnet-4-6"),
- api_key=os.getenv("ANTHROPIC_API_KEY"),
- )
-
- if provider == "azure":
- from .integrations.llm.azure_ import AzureOpenAILLM
-
- return AzureOpenAILLM(
- azure_deployment=os.environ["AZURE_OPENAI_LLM_MODEL"],
- azure_endpoint=os.environ["AZURE_OPENAI_LLM_ENDPOINT"],
- api_version=os.getenv("AZURE_OPENAI_LLM_API_VERSION", "2023-07-01-preview"),
- api_key=os.getenv("AZURE_OPENAI_LLM_KEY"),
- )
-
- if provider == "gemini":
- from .integrations.llm.gemini_ import GeminiLLM
-
- return GeminiLLM(
- model=os.getenv("GEMINI_LLM_MODEL", "gemini-2.0-flash-lite"),
- api_key=os.getenv("GEMINI_API_KEY"),
- )
-
- if provider == "bedrock":
- from .integrations.llm.bedrock_ import BedrockLLM
-
- return BedrockLLM(
- model=os.environ["AWS_BEDROCK_LLM_MODEL"],
- aws_access_key_id=os.getenv("AWS_BEDROCK_LLM_ACCESS_KEY_ID"),
- aws_secret_access_key=os.getenv("AWS_BEDROCK_LLM_SECRET_ACCESS_KEY"),
- region_name=os.getenv("AWS_BEDROCK_LLM_REGION", "us-east-1"),
- )
-
- if provider == "ollama":
- from .integrations.llm.ollama_ import OllamaLLM
-
- return OllamaLLM(
- model=os.environ["OLLAMA_LLM_MODEL"],
- base_url=os.getenv("OLLAMA_LLM_BASE_URL", "http://localhost:11434"),
- )
-
- if provider == "huggingface":
- from .integrations.llm.huggingface_ import HuggingFaceLLM
-
- return HuggingFaceLLM(
- repo_id=os.getenv("HUGGING_FACE_LLM_REPO_ID"),
- endpoint_url=os.getenv("HUGGING_FACE_LLM_ENDPOINT"),
- api_token=os.getenv("HUGGING_FACE_LLM_API_TOKEN"),
- )
-
- raise ValueError(
- f"Unknown LLM_PROVIDER: {provider!r}. "
- "Valid values: openai, anthropic, azure, gemini, bedrock, ollama, huggingface"
- )
-
-
-def build_embedding_from_env() -> EmbeddingPort:
- """환경변수 EMBEDDING_PROVIDER에 따라 EmbeddingPort 인스턴스를 생성한다."""
- provider = os.getenv("EMBEDDING_PROVIDER", "openai").lower().strip("'\"")
-
- if provider == "openai":
- from .integrations.embedding.openai_ import OpenAIEmbedding
-
- return OpenAIEmbedding(
- model=os.getenv("OPEN_AI_EMBEDDING_MODEL", "text-embedding-3-small"),
- api_key=os.getenv("OPEN_AI_KEY"),
- )
-
- if provider == "azure":
- from .integrations.embedding.azure_ import AzureOpenAIEmbedding
-
- return AzureOpenAIEmbedding(
- azure_deployment=os.environ["AZURE_OPENAI_EMBEDDING_MODEL"],
- azure_endpoint=os.environ["AZURE_OPENAI_EMBEDDING_ENDPOINT"],
- api_version=os.getenv(
- "AZURE_OPENAI_EMBEDDING_API_VERSION", "2023-09-15-preview"
- ),
- api_key=os.getenv("AZURE_OPENAI_EMBEDDING_KEY"),
- )
-
- if provider == "ollama":
- from .integrations.embedding.ollama_ import OllamaEmbedding
-
- return OllamaEmbedding(
- model=os.getenv(
- "EMBEDDING_MODEL",
- os.getenv("OLLAMA_EMBEDDING_MODEL", "nomic-embed-text"),
- ),
- base_url=os.getenv(
- "EMBEDDING_BASE_PATH",
- os.getenv("OLLAMA_EMBEDDING_BASE_URL", "http://localhost:11434"),
- ),
- )
-
- if provider == "bedrock":
- from .integrations.embedding.bedrock_ import BedrockEmbedding
-
- return BedrockEmbedding(
- model_id=os.getenv(
- "AWS_BEDROCK_EMBEDDING_MODEL", "amazon.titan-embed-text-v2:0"
- ),
- aws_access_key_id=os.getenv("AWS_BEDROCK_EMBEDDING_ACCESS_KEY_ID"),
- aws_secret_access_key=os.getenv("AWS_BEDROCK_EMBEDDING_SECRET_ACCESS_KEY"),
- region_name=os.getenv("AWS_BEDROCK_EMBEDDING_REGION", "us-east-1"),
- )
-
- if provider == "gemini":
- from .integrations.embedding.gemini_ import GeminiEmbedding
-
- return GeminiEmbedding(
- model=os.getenv("EMBEDDING_MODEL", "models/embedding-001"),
- api_key=os.getenv("GEMINI_EMBEDDING_API_KEY"),
- )
-
- if provider == "huggingface":
- from .integrations.embedding.huggingface_ import HuggingFaceEmbedding
-
- return HuggingFaceEmbedding(
- model=os.getenv(
- "HUGGING_FACE_EMBEDDING_MODEL",
- os.getenv("HUGGING_FACE_EMBEDDING_REPO_ID", ""),
- )
- )
-
- raise ValueError(
- f"Unknown EMBEDDING_PROVIDER: {provider!r}. "
- "Valid values: openai, azure, ollama, bedrock, gemini, huggingface"
- )
-
-
-def build_explorer_from_url(url: str, *, schema: str | None = None) -> "DBExplorerPort":
- """DB URL로 SQLAlchemyExplorer 생성."""
- from .integrations.db.sqlalchemy_ import SQLAlchemyExplorer
-
- return SQLAlchemyExplorer(url, schema=schema)
-
-
-def build_db_from_env(database_env: str = "") -> DBPort:
- """환경변수에서 DB URL을 구성하고 SQLAlchemyDB를 반환한다.
-
- DB_TYPE 환경변수에 따라 적절한 SQLAlchemy 연결 URL을 구성한다.
- """
- from .integrations.db.sqlalchemy_ import SQLAlchemyDB
-
- db_type = os.getenv("DB_TYPE", "sqlite").lower()
- url = _build_db_url(db_type)
- return SQLAlchemyDB(url)
-
-
-def _build_db_url(db_type: str) -> str:
- if db_type == "sqlite":
- path = os.getenv("SQLITE_PATH", "./data/sqlite.db")
- return f"sqlite:///{path}"
-
- if db_type == "postgresql":
- host = os.getenv("POSTGRESQL_HOST", "localhost")
- port = os.getenv("POSTGRESQL_PORT", "5432")
- user = os.getenv("POSTGRESQL_USER", "postgres")
- password = os.getenv("POSTGRESQL_PASSWORD", "")
- database = os.getenv("POSTGRESQL_DATABASE", "postgres")
- return f"postgresql://{user}:{password}@{host}:{port}/{database}"
-
- if db_type == "mysql":
- host = os.getenv("MYSQL_HOST", "localhost")
- port = os.getenv("MYSQL_PORT", "3306")
- user = os.getenv("MYSQL_USER", "root")
- password = os.getenv("MYSQL_PASSWORD", "")
- database = os.getenv("MYSQL_DATABASE", "")
- return f"mysql+pymysql://{user}:{password}@{host}:{port}/{database}"
-
- if db_type == "mariadb":
- host = os.getenv("MARIADB_HOST", "localhost")
- port = os.getenv("MARIADB_PORT", "3306")
- user = os.getenv("MARIADB_USER", "root")
- password = os.getenv("MARIADB_PASSWORD", "")
- database = os.getenv("MARIADB_DATABASE", "")
- return f"mariadb+pymysql://{user}:{password}@{host}:{port}/{database}"
-
- if db_type == "duckdb":
- path = os.getenv("DUCKDB_PATH", "./data/duckdb.db")
- return f"duckdb:///{path}"
-
- if db_type == "clickhouse":
- host = os.getenv("CLICKHOUSE_HOST", "localhost")
- port = os.getenv("CLICKHOUSE_PORT", "9001")
- user = os.getenv("CLICKHOUSE_USER", "default")
- password = os.getenv("CLICKHOUSE_PASSWORD", "")
- database = os.getenv("CLICKHOUSE_DATABASE", "default")
- return f"clickhouse+native://{user}:{password}@{host}:{port}/{database}"
-
- if db_type == "snowflake":
- user = os.environ["SNOWFLAKE_USER"]
- password = os.environ["SNOWFLAKE_PASSWORD"]
- account = os.environ["SNOWFLAKE_ACCOUNT"]
- return f"snowflake://{user}:{password}@{account}"
-
- if db_type == "oracle":
- host = os.getenv("ORACLE_HOST", "localhost")
- port = os.getenv("ORACLE_PORT", "1521")
- user = os.getenv("ORACLE_USER", "")
- password = os.getenv("ORACLE_PASSWORD", "")
- service = os.getenv("ORACLE_SERVICE_NAME", os.getenv("ORACLE_DATABASE", ""))
- return f"oracle+cx_oracle://{user}:{password}@{host}:{port}/?service_name={service}"
-
- raise ValueError(
- f"Unknown DB_TYPE: {db_type!r}. "
- "Valid values: sqlite, postgresql, mysql, mariadb, duckdb, clickhouse, snowflake, oracle"
- )
diff --git a/src/lang2sql/flows/__init__.py b/src/lang2sql/flows/__init__.py
deleted file mode 100644
index b6aa9ac..0000000
--- a/src/lang2sql/flows/__init__.py
+++ /dev/null
@@ -1,5 +0,0 @@
-from .enriched_nl2sql import EnrichedNL2SQL
-from .hybrid import HybridNL2SQL
-from .nl2sql import BaselineNL2SQL
-
-__all__ = ["BaselineNL2SQL", "EnrichedNL2SQL", "HybridNL2SQL"]
diff --git a/src/lang2sql/flows/baseline.py b/src/lang2sql/flows/baseline.py
deleted file mode 100644
index 43b7dc1..0000000
--- a/src/lang2sql/flows/baseline.py
+++ /dev/null
@@ -1,77 +0,0 @@
-from __future__ import annotations
-
-from typing import Any, Callable, Sequence
-
-from ..core.base import BaseFlow
-
-
-class SequentialFlow(BaseFlow):
- """
- A minimal sequential pipeline runner (define-by-run style).
-
- This flow runs `steps` in order, passing the output of each step
- as the input to the next. Each step is a plain callable — no shared
- state bag is required.
-
- Args:
- steps: Ordered sequence of callables.
- name: Optional name override for tracing/logging.
- hook: Optional TraceHook. If not provided, a NullHook is used by BaseFlow.
-
- Returns:
- The final value after running all steps.
- """
-
- def __init__(
- self,
- *,
- steps: Sequence[Callable[..., Any]],
- name: str | None = None,
- hook=None,
- ) -> None:
- """
- Initialize the flow with an ordered list of steps.
-
- Args:
- steps: Ordered sequence of callables. Must be non-empty.
- name: Optional name override.
- hook: Optional TraceHook used by BaseFlow for flow-level events.
-
- Raises:
- ValueError: If `steps` is empty.
- """
- super().__init__(name=name or "SequentialFlow", hook=hook)
- if not steps:
- raise ValueError("SequentialFlow requires at least one step.")
- self.steps: list[Callable[..., Any]] = list(steps)
-
- def _run(self, value: Any) -> Any:
- """
- Execute the flow by passing `value` through each step in order.
-
- Args:
- value: The initial input value.
-
- Returns:
- The final value after running all configured steps.
- """
- for step in self.steps:
- value = step(value)
- return value
-
-
-def BaselineFlow(*args, **kwargs):
- """
- Deprecated alias for SequentialFlow.
-
- .. deprecated::
- Use :class:`SequentialFlow` instead.
- """
- import warnings
-
- warnings.warn(
- "BaselineFlow is deprecated. Use SequentialFlow instead.",
- DeprecationWarning,
- stacklevel=2,
- )
- return SequentialFlow(*args, **kwargs)
diff --git a/src/lang2sql/flows/enriched_nl2sql.py b/src/lang2sql/flows/enriched_nl2sql.py
deleted file mode 100644
index e01ea4b..0000000
--- a/src/lang2sql/flows/enriched_nl2sql.py
+++ /dev/null
@@ -1,101 +0,0 @@
-from __future__ import annotations
-
-from typing import Optional
-
-from ..components.enrichment.context_enricher import ContextEnricher
-from ..components.enrichment.question_profiler import QuestionProfiler
-from ..components.execution.sql_executor import SQLExecutor
-from ..components.gate.question_gate import QuestionGate
-from ..components.gate.table_suitability import TableSuitabilityEvaluator
-from ..components.generation.sql_generator import SQLGenerator
-from ..components.retrieval.hybrid import HybridRetriever
-from ..core.base import BaseFlow
-from ..core.catalog import TextDocument
-from ..core.exceptions import ContractError
-from ..core.hooks import TraceHook
-from ..core.ports import DBPort, EmbeddingPort, LLMPort
-
-
-class EnrichedNL2SQL(BaseFlow):
- """
- 풀 파이프라인 NL→SQL:
- QuestionGate → HybridRetriever → TableSuitabilityEvaluator
- → QuestionProfiler → ContextEnricher → SQLGenerator → SQLExecutor
-
- 레거시 LangGraph 기반 engine/query_executor.py + graph_utils/enriched_graph.py를 대체한다.
-
- Args:
- catalog: list[CatalogEntry] — 검색 대상 테이블 메타데이터.
- llm: LLMPort — 질문 평가, SQL 생성 등에 사용.
- db: DBPort — SQL 실행 대상 데이터베이스.
- embedding: EmbeddingPort — 벡터 검색용 임베딩 모델.
- documents: Optional list of business documents to index.
- db_dialect: SQL 방언. "sqlite", "postgresql", "mysql", "bigquery", "duckdb", "default".
- gate_enabled: QuestionGate를 활성화할지 여부. Default True.
- top_n: HybridRetriever가 반환할 최대 스키마 수. Default 5.
- hook: TraceHook for observability.
-
- Usage::
-
- pipeline = EnrichedNL2SQL(
- catalog=[{"name": "orders", "description": "...", "columns": {...}}],
- llm=AnthropicLLM(model="claude-sonnet-4-6"),
- db=SQLAlchemyDB("sqlite:///sample.db"),
- embedding=OpenAIEmbedding(model="text-embedding-3-small"),
- db_dialect="sqlite",
- )
- rows = pipeline.run("지난달 주문 건수를 알려줘")
- """
-
- def __init__(
- self,
- *,
- catalog: list[dict],
- llm: LLMPort,
- db: DBPort,
- embedding: EmbeddingPort,
- documents: Optional[list[TextDocument]] = None,
- db_dialect: Optional[str] = None,
- gate_enabled: bool = True,
- top_n: int = 5,
- hook: Optional[TraceHook] = None,
- ) -> None:
- super().__init__(name="EnrichedNL2SQL", hook=hook)
- self._gate = QuestionGate(llm=llm, hook=hook) if gate_enabled else None
- self._retriever = HybridRetriever(
- catalog=catalog,
- embedding=embedding,
- documents=documents,
- top_n=top_n,
- hook=hook,
- )
- self._table_eval = TableSuitabilityEvaluator(llm=llm, hook=hook)
- self._profiler = QuestionProfiler(llm=llm, hook=hook)
- self._enricher = ContextEnricher(llm=llm, hook=hook)
- self._generator = SQLGenerator(llm=llm, db_dialect=db_dialect, hook=hook)
- self._executor = SQLExecutor(db=db, hook=hook)
-
- def _run(self, query: str) -> list[dict]:
- # 1. Gate (선택적): 질문이 SQL 답변 불가능하면 ContractError 발생
- if self._gate is not None:
- gate = self._gate(query)
- if not gate.suitable:
- raise ContractError(f"Query not suitable for SQL: {gate.reason}")
-
- # 2. Retrieval: HybridRetriever → RetrievalResult
- result = self._retriever(query)
-
- # 3. Table filtering: 관련도 낮은 테이블 제거
- schemas = self._table_eval(query, result.schemas)
-
- # 4. Profiling: 질문 특성 추출
- profile = self._profiler(query)
-
- # 5. Context enrichment: 보강된 질문 텍스트 생성
- enriched = self._enricher(query, schemas, profile)
-
- # 6. SQL generation: 보강된 컨텍스트 + 도메인 문서를 함께 전달
- sql = self._generator(query, schemas, context=[enriched] + result.context)
-
- # 7. Execution
- return self._executor(sql)
diff --git a/src/lang2sql/flows/hybrid.py b/src/lang2sql/flows/hybrid.py
deleted file mode 100644
index b30575a..0000000
--- a/src/lang2sql/flows/hybrid.py
+++ /dev/null
@@ -1,73 +0,0 @@
-from __future__ import annotations
-
-from typing import Optional
-
-from ..components.execution.sql_executor import SQLExecutor
-from ..components.generation.sql_generator import SQLGenerator
-from ..components.retrieval.hybrid import HybridRetriever
-from ..core.base import BaseFlow
-from ..core.catalog import TextDocument
-from ..core.hooks import TraceHook
-from ..core.ports import DBPort, EmbeddingPort, LLMPort
-
-
-class HybridNL2SQL(BaseFlow):
- """
- NL→SQL pipeline backed by BM25 + vector hybrid retrieval.
-
- Provides higher retrieval quality than ``BaselineNL2SQL`` with only the
- addition of an ``embedding`` parameter.
-
- Pipeline: HybridRetriever → SQLGenerator → SQLExecutor
-
- Args:
- catalog: List of CatalogEntry dicts.
- llm: LLMPort implementation.
- db: DBPort implementation.
- embedding: EmbeddingPort implementation.
- documents: Optional list of business documents to index.
- db_dialect: SQL dialect. Supported values: ``"sqlite"``, ``"postgresql"``,
- ``"mysql"``, ``"bigquery"``, ``"duckdb"``, ``"default"``
- (or ``None`` for default).
- top_n: Maximum number of schemas to return. Default 5.
- hook: TraceHook for observability.
-
- Usage::
-
- pipeline = HybridNL2SQL(
- catalog=[{"name": "orders", "description": "...", "columns": {...}}],
- llm=AnthropicLLM(model="claude-sonnet-4-6"),
- db=SQLAlchemyDB("sqlite:///sample.db"),
- embedding=OpenAIEmbedding(model="text-embedding-3-small"),
- db_dialect="sqlite",
- )
- rows = pipeline.run("How many orders last month?")
- """
-
- def __init__(
- self,
- *,
- catalog: list[dict],
- llm: LLMPort,
- db: DBPort,
- embedding: EmbeddingPort,
- documents: Optional[list[TextDocument]] = None,
- db_dialect: Optional[str] = None,
- top_n: int = 5,
- hook: Optional[TraceHook] = None,
- ) -> None:
- super().__init__(name="HybridNL2SQL", hook=hook)
- self._retriever = HybridRetriever(
- catalog=catalog,
- embedding=embedding,
- documents=documents,
- top_n=top_n,
- hook=hook,
- )
- self._generator = SQLGenerator(llm=llm, db_dialect=db_dialect, hook=hook)
- self._executor = SQLExecutor(db=db, hook=hook)
-
- def _run(self, query: str) -> list[dict]:
- result = self._retriever(query) # RetrievalResult
- sql = self._generator(query, result.schemas, context=result.context)
- return self._executor(sql)
diff --git a/src/lang2sql/flows/nl2sql.py b/src/lang2sql/flows/nl2sql.py
deleted file mode 100644
index 530798a..0000000
--- a/src/lang2sql/flows/nl2sql.py
+++ /dev/null
@@ -1,50 +0,0 @@
-from __future__ import annotations
-
-from typing import Optional
-
-from ..components.execution.sql_executor import SQLExecutor
-from ..components.generation.sql_generator import SQLGenerator
-from ..components.retrieval.keyword import KeywordRetriever
-from ..core.base import BaseFlow
-from ..core.hooks import TraceHook
-from ..core.ports import DBPort, LLMPort
-
-
-class BaselineNL2SQL(BaseFlow):
- """
- End-to-end NL→SQL pipeline.
-
- Pipeline: KeywordRetriever → SQLGenerator → SQLExecutor
-
- Usage::
-
- pipeline = BaselineNL2SQL(
- catalog=[{"name": "orders", "description": "...", "columns": {...}}],
- llm=AnthropicLLM(model="claude-sonnet-4-6"),
- db=SQLAlchemyDB("sqlite:///sample.db"),
- db_dialect="sqlite",
- )
- rows = pipeline.run("How many orders last month?")
-
- Supported ``db_dialect`` values: ``"sqlite"``, ``"postgresql"``, ``"mysql"``,
- ``"bigquery"``, ``"duckdb"``, ``"default"`` (or ``None`` for default).
- """
-
- def __init__(
- self,
- *,
- catalog: list[dict],
- llm: LLMPort,
- db: DBPort,
- db_dialect: Optional[str] = None,
- hook: Optional[TraceHook] = None,
- ) -> None:
- super().__init__(name="BaselineNL2SQL", hook=hook)
- self._retriever = KeywordRetriever(catalog=catalog, hook=hook)
- self._generator = SQLGenerator(llm=llm, db_dialect=db_dialect, hook=hook)
- self._executor = SQLExecutor(db=db, hook=hook)
-
- def _run(self, query: str) -> list[dict]:
- schemas = self._retriever(query) # list[CatalogEntry]
- sql = self._generator(query, schemas)
- return self._executor(sql)
diff --git a/prompt/__init__.py b/src/lang2sql/frontends/__init__.py
similarity index 100%
rename from prompt/__init__.py
rename to src/lang2sql/frontends/__init__.py
diff --git a/src/lang2sql/components/__init__.py b/src/lang2sql/frontends/cli/__init__.py
similarity index 100%
rename from src/lang2sql/components/__init__.py
rename to src/lang2sql/frontends/cli/__init__.py
diff --git a/src/lang2sql/frontends/cli/app.py b/src/lang2sql/frontends/cli/app.py
new file mode 100644
index 0000000..47a6272
--- /dev/null
+++ b/src/lang2sql/frontends/cli/app.py
@@ -0,0 +1,36 @@
+"""CLI frontend — the developer-facing driver of the harness.
+
+Per v4.1 the CLI is a dev tool (Discord is the Phase 1 product frontend). It
+assembles a real :class:`HarnessContext` via the :class:`ContextConcierge`
+(OpenAI when ``OPENAI_API_KEY`` is set, else the offline FakeLLM) and runs one
+turn through ``agent_loop`` — the harness's end-to-end smoke test.
+
+Run: python -m lang2sql.frontends.cli.app "your question"
+"""
+
+from __future__ import annotations
+
+import asyncio
+import sys
+
+from ...core.identity import Identity
+from ...harness.loop import agent_loop
+from ...tenancy.concierge import ContextConcierge
+
+
+async def _run(user_text: str) -> str:
+ concierge = ContextConcierge()
+ identity = Identity(user_id="cli-user", guild_id="cli", channel_id="cli-main")
+ ctx = await concierge.build_context(identity)
+ return await agent_loop(ctx, user_text)
+
+
+def main(argv: list[str] | None = None) -> int:
+ argv = argv if argv is not None else sys.argv[1:]
+ user_text = " ".join(argv) or "list the tables"
+ print(asyncio.run(_run(user_text)))
+ return 0
+
+
+if __name__ == "__main__":
+ raise SystemExit(main())
diff --git a/src/lang2sql/frontends/discord/__init__.py b/src/lang2sql/frontends/discord/__init__.py
new file mode 100644
index 0000000..f252f93
--- /dev/null
+++ b/src/lang2sql/frontends/discord/__init__.py
@@ -0,0 +1,30 @@
+"""Discord frontend (Phase 1) — the V1 chat interface over the harness.
+
+``bot.py`` is the only module that imports discord.py; ``session_router``,
+``render``, and ``commands`` are pure and unit-testable. Importing this package
+does not import discord.py — ``bot`` is loaded lazily on demand — so the pure
+layer (and its tests) stay free of the gateway dependency.
+"""
+
+from __future__ import annotations
+
+from .commands import CommandHandlers
+from .render import MAX_INLINE_ROWS, render_answer
+from .session_router import (
+ InteractionContext,
+ is_channel,
+ is_dm,
+ is_thread,
+ to_identity,
+)
+
+__all__ = [
+ "CommandHandlers",
+ "render_answer",
+ "MAX_INLINE_ROWS",
+ "InteractionContext",
+ "to_identity",
+ "is_dm",
+ "is_thread",
+ "is_channel",
+]
diff --git a/src/lang2sql/frontends/discord/bot.py b/src/lang2sql/frontends/discord/bot.py
new file mode 100644
index 0000000..c51c8ae
--- /dev/null
+++ b/src/lang2sql/frontends/discord/bot.py
@@ -0,0 +1,182 @@
+"""bot.py — the ONLY discord.py-aware module in the frontend (Phase 1).
+
+It is the thin shell described in v4.1 §2.1: receive a Discord interaction or
+mention, translate it to an :class:`InteractionContext` →
+:class:`Identity` (session_router), call a :class:`CommandHandlers` method, and
+deliver the resulting :class:`OutboundMessage` natively (plain reply, or a
+``discord.File`` upload when render attached a CSV).
+
+Import-safety contract (tested): importing this module must not require a token
+or any network access — only :func:`run` connects to the gateway. So discord.py
+is imported at module load (it's a pure library import), but the client is
+constructed and the token is read only inside :func:`run`.
+"""
+
+from __future__ import annotations
+
+import io
+import os
+
+import discord
+from discord import app_commands
+
+from ...core.ports.frontend import OutboundMessage
+from ...tenancy.concierge import ContextConcierge
+from .commands import CommandHandlers
+from .session_router import InteractionContext, to_identity
+
+TOKEN_ENV = "DISCORD_BOT_TOKEN"
+
+
+def _interaction_context(interaction: discord.Interaction) -> InteractionContext:
+ """Extract frontend-neutral coordinates from a slash-command interaction."""
+ channel = interaction.channel
+ thread_id: str | None = None
+ channel_id: str | None = None
+ if isinstance(channel, discord.Thread):
+ thread_id = str(channel.id)
+ channel_id = str(channel.parent_id) if channel.parent_id else None
+ elif channel is not None:
+ channel_id = str(channel.id)
+
+ is_admin = False
+ perms = getattr(interaction, "permissions", None)
+ if perms is not None:
+ is_admin = bool(perms.administrator)
+
+ return InteractionContext(
+ user_id=str(interaction.user.id),
+ guild_id=str(interaction.guild_id) if interaction.guild_id else None,
+ channel_id=channel_id,
+ thread_id=thread_id,
+ is_admin=is_admin,
+ )
+
+
+def _message_context(message: discord.Message) -> InteractionContext:
+ """Extract coordinates from a plain message (an @mention or thread reply)."""
+ channel = message.channel
+ thread_id: str | None = None
+ channel_id: str | None = None
+ if isinstance(channel, discord.Thread):
+ thread_id = str(channel.id)
+ channel_id = str(channel.parent_id) if channel.parent_id else None
+ elif channel is not None:
+ channel_id = str(channel.id)
+
+ is_admin = False
+ author = message.author
+ guild_perms = getattr(author, "guild_permissions", None)
+ if guild_perms is not None:
+ is_admin = bool(guild_perms.administrator)
+
+ return InteractionContext(
+ user_id=str(author.id),
+ guild_id=str(message.guild.id) if message.guild else None,
+ channel_id=channel_id,
+ thread_id=thread_id,
+ is_admin=is_admin,
+ )
+
+
+def _to_sendable(message: OutboundMessage) -> tuple[str, discord.File | None]:
+ """Turn an :class:`OutboundMessage` into (content, optional file) for send."""
+ if message.file_bytes is not None:
+ file = discord.File(
+ io.BytesIO(message.file_bytes),
+ filename=message.file_name or "result.csv",
+ )
+ return message.text, file
+ return message.text, None
+
+
+class Lang2SQLBot(discord.Client):
+ """Discord client wiring slash commands + @mentions to the harness."""
+
+ def __init__(self, handlers: CommandHandlers) -> None:
+ intents = discord.Intents.default()
+ intents.message_content = True # needed to read @mention text
+ super().__init__(intents=intents)
+ self._handlers = handlers
+ self.tree = app_commands.CommandTree(self)
+ self._register_commands()
+
+ async def setup_hook(self) -> None:
+ # Sync slash commands with Discord on startup.
+ await self.tree.sync()
+
+ def _register_commands(self) -> None:
+ tree = self.tree
+ handlers = self._handlers
+
+ @tree.command(name="connect", description="Store a database connection string")
+ async def connect(interaction: discord.Interaction, dsn: str) -> None:
+ await self._run(interaction, handlers.connect(to_identity(_interaction_context(interaction)), dsn))
+
+ @tree.command(name="ingest", description="Propose definitions from a document")
+ async def ingest(interaction: discord.Interaction, ref: str) -> None:
+ await self._run(interaction, handlers.ingest(to_identity(_interaction_context(interaction)), ref=ref))
+
+ @tree.command(name="define_metric", description='Define a metric: name and "definition"')
+ async def define_metric(
+ interaction: discord.Interaction, name: str, definition: str
+ ) -> None:
+ await self._run(
+ interaction,
+ handlers.define_metric(to_identity(_interaction_context(interaction)), name, definition),
+ )
+
+ @tree.command(name="remember", description="Remember a fact for future turns")
+ async def remember(interaction: discord.Interaction, text: str) -> None:
+ await self._run(interaction, handlers.remember(to_identity(_interaction_context(interaction)), text))
+
+ @tree.command(name="semantic_show", description="Show definitions in effect here")
+ async def semantic_show(interaction: discord.Interaction) -> None:
+ await self._run(interaction, handlers.semantic_show(to_identity(_interaction_context(interaction))))
+
+ @tree.command(name="audit_me", description="Show your recent activity")
+ async def audit_me(interaction: discord.Interaction) -> None:
+ await self._run(interaction, handlers.audit_me(to_identity(_interaction_context(interaction))))
+
+ async def _run(self, interaction: discord.Interaction, coro) -> None:
+ """Await a handler coroutine and reply with its OutboundMessage."""
+ await interaction.response.defer(thinking=True)
+ message = await coro
+ content, file = _to_sendable(message)
+ await interaction.followup.send(content=content or "(empty)", file=file)
+
+ async def on_message(self, message: discord.Message) -> None:
+ """Treat an @mention (or a reply inside a thread) as a free-form query."""
+ if message.author == self.user:
+ return
+ mentioned = self.user is not None and self.user.mentioned_in(message)
+ in_thread = isinstance(message.channel, discord.Thread)
+ if not mentioned and not in_thread:
+ return
+
+ text = message.content
+ if self.user is not None:
+ text = text.replace(self.user.mention, "").strip()
+ if not text:
+ return
+
+ identity = to_identity(_message_context(message))
+ out = await self._handlers.query(identity, text)
+ content, file = _to_sendable(out)
+ await message.channel.send(content=content or "(empty)", file=file)
+
+
+def run() -> None:
+ """Entry point for the ``lang2sql-bot`` script: connect and serve.
+
+ Reads the token from :data:`TOKEN_ENV`; raises a clear error if it's unset
+ so a misconfigured deploy fails loudly rather than hanging on the gateway.
+ """
+ token = os.environ.get(TOKEN_ENV)
+ if not token:
+ raise RuntimeError(
+ f"{TOKEN_ENV} is not set; export your Discord bot token to run the bot."
+ )
+ handlers = CommandHandlers(ContextConcierge())
+ client = Lang2SQLBot(handlers)
+ client.run(token)
diff --git a/src/lang2sql/frontends/discord/commands.py b/src/lang2sql/frontends/discord/commands.py
new file mode 100644
index 0000000..ac426ed
--- /dev/null
+++ b/src/lang2sql/frontends/discord/commands.py
@@ -0,0 +1,143 @@
+"""CommandHandlers — the V1 Discord command surface, free of discord.py.
+
+Each public method takes plain arguments (an :class:`Identity` plus strings) and
+returns an :class:`OutboundMessage`, so the whole command layer is unit-testable
+without a gateway connection. ``bot.py`` is the only module that knows about
+slash commands, embeds, and ``discord.File``; it parses an interaction, picks a
+handler here, and renders the result.
+
+The handlers drive the harness through a :class:`ContextConcierge`: every call
+builds a fresh :class:`HarnessContext` for the identity (restoring its session),
+then either runs the agent loop (``query``) or invokes a single ctx-aware tool /
+port directly (the structured commands). Running the real tools — rather than
+re-implementing their logic — keeps federation scoping, audit logging, and
+memory writes identical to what the agent itself does.
+"""
+
+from __future__ import annotations
+
+from datetime import datetime, timezone
+
+from ...core.identity import Identity
+from ...core.ports.frontend import OutboundMessage
+from ...harness.loop import agent_loop
+from ...tenancy.concierge import ContextConcierge
+from .render import render_answer
+
+
+class CommandHandlers:
+ """Async command methods returning :class:`OutboundMessage` (discord-free)."""
+
+ def __init__(self, concierge: ContextConcierge) -> None:
+ self._concierge = concierge
+
+ async def query(self, identity: Identity, text: str) -> OutboundMessage:
+ """Run a natural-language question through the agent loop, then render.
+
+ The loop mutates the in-context :class:`Session`; we persist it back
+ through the concierge store afterwards so the next message in the same
+ thread/DM continues the conversation (tiebreaker #4).
+ """
+ ctx = await self._concierge.build_context(identity, user_text=text)
+ answer = await agent_loop(ctx, text)
+ await self._concierge.store.save(identity.session_key(), ctx.session)
+ return render_answer(answer)
+
+ async def define_metric(
+ self,
+ identity: Identity,
+ name: str,
+ definition: str,
+ scope: str | None = None,
+ ) -> OutboundMessage:
+ """Register one definition at the current (or requested) scope.
+
+ Delegates to the ctx-aware ``define_metric`` tool, which writes through
+ the :class:`ScopeResolverPort` and records an audit event. ``scope`` is
+ ``"channel"`` (default) or ``"guild"`` per the tool's schema.
+ """
+ ctx = await self._concierge.build_context(identity)
+ args: dict[str, str] = {"name": name, "definition": definition}
+ if scope:
+ args["scope"] = scope
+ result = await ctx.tools.dispatch("define_metric", args, ctx, "cmd:define_metric")
+ return OutboundMessage(text=result.content)
+
+ async def remember(self, identity: Identity, text: str) -> OutboundMessage:
+ """Persist a user fact via the memory service (manual ``/remember``)."""
+ ctx = await self._concierge.build_context(identity)
+ result = await ctx.tools.dispatch("remember", {"text": text}, ctx, "cmd:remember")
+ return OutboundMessage(text=result.content)
+
+ async def semantic_show(self, identity: Identity) -> OutboundMessage:
+ """Show the effective semantic layer for this scope chain."""
+ ctx = await self._concierge.build_context(identity)
+ if ctx.scope_resolver is None:
+ return OutboundMessage(text="Semantic layer unavailable.")
+ layer = await ctx.scope_resolver.effective_layer(identity)
+ rendered = layer.render()
+ if not rendered:
+ return OutboundMessage(text="No definitions apply in this scope yet.")
+ return OutboundMessage(text=f"Definitions in effect here:\n{rendered}")
+
+ async def audit_me(self, identity: Identity) -> OutboundMessage:
+ """List the caller's recent audited actions, newest first."""
+ ctx = await self._concierge.build_context(identity)
+ if ctx.audit is None:
+ return OutboundMessage(text="Audit log unavailable.")
+ events = await ctx.audit.query(identity.user_id)
+ if not events:
+ return OutboundMessage(text="No audited activity for you yet.")
+ lines = ["Your recent activity:"]
+ for event in events:
+ lines.append(f"- {_fmt_ts(event.ts)} {event.action} @ {event.scope}")
+ return OutboundMessage(text="\n".join(lines))
+
+ async def connect(self, identity: Identity, dsn: str) -> OutboundMessage:
+ """V1 stub: stash a DB DSN keyed by guild/DM in the concierge kv store.
+
+ There is no secrets *port* on :class:`HarnessContext` in V1, so this
+ does not yet encrypt or wire the DSN into the explorer — it simply
+ records it (so a later run can pick it up) and acknowledges. The real
+ encrypted-secrets path lands in the tenancy work (task #2); this keeps
+ the command present and documented without overreaching.
+ """
+ dsn = dsn.strip()
+ if not dsn:
+ return OutboundMessage(text="Provide a database connection string.")
+ scope = identity.guild_id or f"dm:{identity.user_id}"
+ self._concierge.store.kv_set(scope, "dsn", dsn)
+ return OutboundMessage(
+ text=(
+ "Connection string saved for this workspace (V1 stub — not yet "
+ "encrypted or live; queries still run against the configured DB)."
+ )
+ )
+
+ async def ingest(
+ self,
+ identity: Identity,
+ ref: str | None = None,
+ content: str | None = None,
+ ) -> OutboundMessage:
+ """Propose semantic definitions extracted from a document.
+
+ Lists the candidates via the ``ingest_doc`` tool; confirmation buttons
+ are ``bot.py``'s job (V1 just surfaces the list, keeping the human in
+ the loop before anything enters the semantic layer).
+ """
+ ctx = await self._concierge.build_context(identity)
+ args: dict[str, str] = {}
+ if ref:
+ args["ref"] = ref
+ if content:
+ args["content"] = content
+ result = await ctx.tools.dispatch("ingest_doc", args, ctx, "cmd:ingest")
+ return OutboundMessage(text=result.content)
+
+
+def _fmt_ts(ts: float) -> str:
+ """Format an epoch timestamp as a short UTC string for audit listings."""
+ if not ts:
+ return "?"
+ return datetime.fromtimestamp(ts, tz=timezone.utc).strftime("%Y-%m-%d %H:%M")
diff --git a/src/lang2sql/frontends/discord/render.py b/src/lang2sql/frontends/discord/render.py
new file mode 100644
index 0000000..f143d2a
--- /dev/null
+++ b/src/lang2sql/frontends/discord/render.py
@@ -0,0 +1,80 @@
+"""render — agent answer → :class:`OutboundMessage` (pure, no discord import).
+
+v4.1 §4.1 sets the response rule: a small result goes back as text, a large one
+(> :data:`MAX_INLINE_ROWS`) is attached as a CSV with a short text summary so a
+Discord message never balloons past what a human skims. Keeping this pure means
+``bot.py`` only has to turn an :class:`OutboundMessage` into a ``discord.File``
+or a plain reply — all the threshold logic is unit-tested here.
+"""
+
+from __future__ import annotations
+
+import csv
+import io
+from collections.abc import Sequence
+from typing import Any
+
+from ...core.ports.frontend import OutboundMessage
+
+# Above this many rows (or text lines) we attach a CSV instead of inlining.
+MAX_INLINE_ROWS = 50
+
+
+def render_answer(
+ text: str,
+ rows: Sequence[Sequence[Any]] | None = None,
+ *,
+ header: Sequence[str] | None = None,
+ file_name: str = "result.csv",
+) -> OutboundMessage:
+ """Render an agent answer, attaching a CSV when it's too big to inline.
+
+ Two oversized shapes trigger an attachment:
+
+ * structured ``rows`` longer than :data:`MAX_INLINE_ROWS` — serialised to
+ CSV (with ``header`` if given) and replaced by a one-line summary; or
+ * a plain ``text`` answer with more than :data:`MAX_INLINE_ROWS` lines —
+ written verbatim into a ``.csv``/text attachment.
+
+ Anything smaller is returned as plain ``text``.
+ """
+ if rows is not None and len(rows) > MAX_INLINE_ROWS:
+ payload = _rows_to_csv(rows, header)
+ summary = f"{len(rows)} rows — attached as {file_name}."
+ if text.strip():
+ summary = f"{text.strip()}\n{summary}"
+ return OutboundMessage(
+ text=summary,
+ file_bytes=payload.encode("utf-8"),
+ file_name=file_name,
+ )
+
+ if rows is not None:
+ # Small structured result: inline as text, CSV-formatted for legibility.
+ body = _rows_to_csv(rows, header).rstrip("\n")
+ text_block = f"{text.strip()}\n{body}" if text.strip() else body
+ return OutboundMessage(text=text_block)
+
+ lines = text.splitlines()
+ if len(lines) > MAX_INLINE_ROWS:
+ summary = f"Result is {len(lines)} lines — attached as {file_name}."
+ return OutboundMessage(
+ text=summary,
+ file_bytes=text.encode("utf-8"),
+ file_name=file_name,
+ )
+
+ return OutboundMessage(text=text)
+
+
+def _rows_to_csv(
+ rows: Sequence[Sequence[Any]], header: Sequence[str] | None
+) -> str:
+ """Serialise ``rows`` (optionally with a ``header``) to a CSV string."""
+ buf = io.StringIO()
+ writer = csv.writer(buf)
+ if header is not None:
+ writer.writerow(list(header))
+ for row in rows:
+ writer.writerow(list(row))
+ return buf.getvalue()
diff --git a/src/lang2sql/frontends/discord/session_router.py b/src/lang2sql/frontends/discord/session_router.py
new file mode 100644
index 0000000..7973c07
--- /dev/null
+++ b/src/lang2sql/frontends/discord/session_router.py
@@ -0,0 +1,71 @@
+"""session_router — interaction coordinates → :class:`Identity` (pure).
+
+This module is deliberately free of ``discord`` imports so the mapping is unit-
+testable without a live gateway. ``bot.py`` extracts the raw ids from a
+discord.py interaction/message and hands them here; everything downstream
+(session key, federation scope chain) follows from the :class:`Identity` these
+functions build.
+
+The three conversation shapes of v4.1 §4.1 map cleanly onto the optional id
+fields of :class:`Identity`:
+
+* **DM** — no ``guild_id``; the user is the unit of conversation.
+* **channel @mention** — ``guild_id`` + ``channel_id``; a reply normally opens
+ a thread, so the bot fills ``thread_id`` once that thread exists.
+* **thread reply** — ``guild_id`` + ``channel_id`` + ``thread_id``; the thread
+ is the narrowest conversation container and drives both the session key and
+ the top of the federation scope chain.
+"""
+
+from __future__ import annotations
+
+from dataclasses import dataclass
+
+from ...core.identity import Identity
+
+
+@dataclass(frozen=True)
+class InteractionContext:
+ """Frontend-neutral snapshot of where an interaction happened.
+
+ ``bot.py`` populates this from a discord.py object; the router turns it into
+ an :class:`Identity`. Ids are strings (Discord snowflakes stringified) or
+ ``None`` when the platform doesn't supply that level — a DM has no guild.
+ """
+
+ user_id: str
+ guild_id: str | None = None
+ channel_id: str | None = None
+ thread_id: str | None = None
+ is_admin: bool = False
+
+
+def to_identity(ctx: InteractionContext) -> Identity:
+ """Map an :class:`InteractionContext` to an :class:`Identity`.
+
+ A pure 1:1 field copy — the semantics (session key, scope chain, default
+ write scope) live on :class:`Identity` itself, so the router stays a thin,
+ obvious translation layer with nothing to get subtly wrong.
+ """
+ return Identity(
+ user_id=ctx.user_id,
+ guild_id=ctx.guild_id,
+ channel_id=ctx.channel_id,
+ thread_id=ctx.thread_id,
+ is_admin=ctx.is_admin,
+ )
+
+
+def is_dm(identity: Identity) -> bool:
+ """True when this conversation is a direct message (no guild)."""
+ return identity.guild_id is None
+
+
+def is_thread(identity: Identity) -> bool:
+ """True when the conversation is anchored to a thread."""
+ return identity.thread_id is not None
+
+
+def is_channel(identity: Identity) -> bool:
+ """True for a guild channel conversation that isn't (yet) in a thread."""
+ return identity.guild_id is not None and identity.thread_id is None
diff --git a/src/lang2sql/integrations/__init__.py b/src/lang2sql/frontends/slack/__init__.py
similarity index 100%
rename from src/lang2sql/integrations/__init__.py
rename to src/lang2sql/frontends/slack/__init__.py
diff --git a/src/lang2sql/frontends/web/__init__.py b/src/lang2sql/frontends/web/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/src/lang2sql/harness/__init__.py b/src/lang2sql/harness/__init__.py
new file mode 100644
index 0000000..fa0166a
--- /dev/null
+++ b/src/lang2sql/harness/__init__.py
@@ -0,0 +1,8 @@
+"""Harness — the assembled agent unit (context, session, loop, tools)."""
+
+from .context import HarnessContext
+from .loop import agent_loop
+from .session import Session
+from .tool_registry import ToolRegistry
+
+__all__ = ["HarnessContext", "agent_loop", "Session", "ToolRegistry"]
diff --git a/src/lang2sql/harness/context.py b/src/lang2sql/harness/context.py
new file mode 100644
index 0000000..1d3431b
--- /dev/null
+++ b/src/lang2sql/harness/context.py
@@ -0,0 +1,33 @@
+"""HarnessContext — the assembled unit handed to every turn and every tool.
+
+v4.1 §2.1 describes the harness as "one bundled thing": LLM + tools + scope-
+aware semantic + session + safety + explorer + audit. The ContextConcierge
+(tenancy) builds one of these per request; the agent loop and ctx-aware tools
+read from it. Optional fields are the pieces a bare CLI smoke-test can omit.
+"""
+
+from __future__ import annotations
+
+from dataclasses import dataclass
+
+from ..core.identity import Identity
+from ..core.ports.audit import AuditPort
+from ..core.ports.explorer import ExplorerPort
+from ..core.ports.llm import LLMPort
+from ..core.ports.safety import SafetyPipelinePort
+from ..core.ports.semantic_scope import ScopeResolverPort
+from .session import Session
+from .tool_registry import ToolRegistry
+
+
+@dataclass
+class HarnessContext:
+ identity: Identity
+ llm: LLMPort
+ tools: ToolRegistry
+ session: Session
+ explorer: ExplorerPort | None = None
+ safety: SafetyPipelinePort | None = None
+ audit: AuditPort | None = None
+ scope_resolver: ScopeResolverPort | None = None
+ max_turns: int = 8
diff --git a/src/lang2sql/harness/loop.py b/src/lang2sql/harness/loop.py
new file mode 100644
index 0000000..4a8e59a
--- /dev/null
+++ b/src/lang2sql/harness/loop.py
@@ -0,0 +1,48 @@
+"""agent_loop — the turn engine.
+
+One call per user message: build the system prompt, then iterate
+LLM → tool calls → LLM until the model returns a final answer (no tool calls)
+or ``max_turns`` is hit. Tool failures come back as tool messages so the model
+can recover rather than crashing the loop.
+"""
+
+from __future__ import annotations
+
+from ..core.types import Message, Role
+from .context import HarnessContext
+from .system_prompt import build_system_prompt
+
+
+async def agent_loop(ctx: HarnessContext, user_text: str) -> str:
+ """Run one user turn to completion; return the final assistant text."""
+ ctx.session.add(Message(role=Role.USER, content=user_text))
+
+ system = await build_system_prompt(ctx)
+ specs = ctx.tools.specs()
+
+ for _ in range(ctx.max_turns):
+ messages = [Message(role=Role.SYSTEM, content=system), *ctx.session.history()]
+ completion = await ctx.llm.complete(messages, specs)
+
+ assistant = Message(
+ role=Role.ASSISTANT,
+ content=completion.content,
+ tool_calls=completion.tool_calls,
+ )
+ ctx.session.add(assistant)
+
+ if not completion.tool_calls:
+ return completion.content
+
+ for call in completion.tool_calls:
+ result = await ctx.tools.dispatch(call.name, call.arguments, ctx, call.id)
+ ctx.session.add(
+ Message(
+ role=Role.TOOL,
+ content=result.content,
+ tool_call_id=result.call_id,
+ name=call.name,
+ )
+ )
+
+ return "(reached max turns without a final answer)"
diff --git a/src/lang2sql/harness/session.py b/src/lang2sql/harness/session.py
new file mode 100644
index 0000000..6eda61f
--- /dev/null
+++ b/src/lang2sql/harness/session.py
@@ -0,0 +1,28 @@
+"""Session — the persistable state of one conversation.
+
+Holds the transcript plus a scratch of facts recalled for the current turn.
+Persisted via :class:`SessionStorePort` keyed by ``Identity.session_key`` so a
+thread picks up where it left off (tiebreaker #4).
+"""
+
+from __future__ import annotations
+
+from dataclasses import dataclass, field
+
+from ..core.identity import Identity
+from ..core.types import Message
+
+
+@dataclass
+class Session:
+ identity: Identity
+ transcript: list[Message] = field(default_factory=list)
+
+ def add(self, message: Message) -> None:
+ self.transcript.append(message)
+
+ def history(self) -> list[Message]:
+ return list(self.transcript)
+
+ def reset(self) -> None:
+ self.transcript.clear()
diff --git a/src/lang2sql/harness/system_prompt.py b/src/lang2sql/harness/system_prompt.py
new file mode 100644
index 0000000..10d2837
--- /dev/null
+++ b/src/lang2sql/harness/system_prompt.py
@@ -0,0 +1,40 @@
+"""Assemble the system prompt for one turn.
+
+Per v4.1 §2.1 the prompt injects: (1) the agent's role/rules, (2) the effective
+semantic layer for the current scope, (3) recalled facts, (4) DB schema. V1
+keeps each section simple; later versions enrich them without changing the
+loop. Sections are suppressed when empty.
+"""
+
+from __future__ import annotations
+
+from .context import HarnessContext
+
+_BASE = """\
+You are Lang2SQL, a read-only data analytics agent.
+
+Rules:
+- Only ever read data. Never modify the database.
+- When you need data, call the run_sql tool with a single SELECT/WITH query.
+- Discover schema with explore_schema before guessing table or column names.
+- Prefer definitions from the semantic layer below over your own assumptions.
+- Answer concisely; show the SQL you ran.
+"""
+
+
+async def build_system_prompt(ctx: HarnessContext) -> str:
+ parts: list[str] = [_BASE]
+
+ if ctx.scope_resolver is not None:
+ layer = await ctx.scope_resolver.effective_layer(ctx.identity)
+ rendered = layer.render() if layer is not None else ""
+ if rendered:
+ parts.append("## Semantic layer (effective for this scope)\n" + rendered)
+
+ if ctx.explorer is not None:
+ tables = await ctx.explorer.list_tables()
+ if tables:
+ names = ", ".join(t.qualified for t in tables)
+ parts.append("## Known tables\n" + names)
+
+ return "\n\n".join(parts)
diff --git a/src/lang2sql/harness/tool_registry.py b/src/lang2sql/harness/tool_registry.py
new file mode 100644
index 0000000..c6add7e
--- /dev/null
+++ b/src/lang2sql/harness/tool_registry.py
@@ -0,0 +1,37 @@
+"""Tool registry — name→tool dispatch and spec catalog for the loop."""
+
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Any
+
+from ..core.ports.tool import ToolPort
+from ..core.types import ToolResult, ToolSpec
+
+if TYPE_CHECKING:
+ from .context import HarnessContext
+
+
+class ToolRegistry:
+ def __init__(self, tools: list[ToolPort] | None = None) -> None:
+ self._tools: dict[str, ToolPort] = {}
+ for tool in tools or []:
+ self.register(tool)
+
+ def register(self, tool: ToolPort) -> None:
+ self._tools[tool.spec.name] = tool
+
+ def specs(self) -> list[ToolSpec]:
+ return [t.spec for t in self._tools.values()]
+
+ async def dispatch(
+ self, name: str, args: dict[str, Any], ctx: "HarnessContext", call_id: str
+ ) -> ToolResult:
+ tool = self._tools.get(name)
+ if tool is None:
+ return ToolResult(call_id=call_id, content=f"unknown tool: {name}", is_error=True)
+ try:
+ result = await tool.run(args, ctx)
+ result.call_id = call_id # tools don't know their call id; stamp it here
+ return result
+ except Exception as exc: # tools must never crash the loop
+ return ToolResult(call_id=call_id, content=f"{type(exc).__name__}: {exc}", is_error=True)
diff --git a/src/lang2sql/ingestion/__init__.py b/src/lang2sql/ingestion/__init__.py
new file mode 100644
index 0000000..136aac4
--- /dev/null
+++ b/src/lang2sql/ingestion/__init__.py
@@ -0,0 +1,18 @@
+"""Ingestion — document → semantic candidates via Source × Extractor (★③).
+
+V1 wires file upload (``FileSource``) × LLM extraction (``LLMExtractor``)
+through ``IngestionPipeline``. See ``core/ports/ingestion.py`` for the axis
+Protocols.
+"""
+
+from __future__ import annotations
+
+from .extractors import LLMExtractor
+from .pipeline import IngestionPipeline
+from .sources import FileSource
+
+__all__ = [
+ "IngestionPipeline",
+ "FileSource",
+ "LLMExtractor",
+]
diff --git a/src/lang2sql/ingestion/extractors/__init__.py b/src/lang2sql/ingestion/extractors/__init__.py
new file mode 100644
index 0000000..2f42701
--- /dev/null
+++ b/src/lang2sql/ingestion/extractors/__init__.py
@@ -0,0 +1,7 @@
+"""Extractor axis adapters."""
+
+from __future__ import annotations
+
+from .llm_extractor import LLMExtractor
+
+__all__ = ["LLMExtractor"]
diff --git a/src/lang2sql/ingestion/extractors/llm_extractor.py b/src/lang2sql/ingestion/extractors/llm_extractor.py
new file mode 100644
index 0000000..e741a12
--- /dev/null
+++ b/src/lang2sql/ingestion/extractors/llm_extractor.py
@@ -0,0 +1,87 @@
+"""LLMExtractor — the V1 Extractor axis (★③).
+
+Asks an LLM to read a document and propose metric/dimension/rule definitions as
+a JSON array. Parsing is deliberately robust: ```json fences are stripped and
+any malformed response degrades to an empty candidate list rather than raising.
+v1.5 adds a DDL parser implementing the same :class:`DocExtractorPort`.
+"""
+
+from __future__ import annotations
+
+import json
+
+from ...core.ports.ingestion import (
+ CandidateKind,
+ Document,
+ SemanticCandidate,
+)
+from ...core.ports.llm import LLMPort
+from ...core.types import Message, Role
+
+_INSTRUCTIONS = (
+ "From this document, extract metric/dimension/rule definitions as a JSON "
+ "array of {kind,name,definition,applies_to}. ``kind`` must be one of "
+ '"metric", "dimension", "rule". Respond with only the JSON array.'
+)
+
+
+class LLMExtractor:
+ """``DocExtractorPort`` that mines candidates with an LLM."""
+
+ def __init__(self, llm: LLMPort) -> None:
+ self._llm = llm
+
+ async def extract(self, doc: Document) -> list[SemanticCandidate]:
+ prompt = f"{_INSTRUCTIONS}\n\nDocument: {doc.name}\n\n{doc.text}"
+ completion = await self._llm.complete([Message(role=Role.USER, content=prompt)])
+ rows = _parse(completion.content)
+ candidates: list[SemanticCandidate] = []
+ for row in rows:
+ candidate = _to_candidate(row, doc.source_id)
+ if candidate is not None:
+ candidates.append(candidate)
+ return candidates
+
+
+def _parse(content: str) -> list:
+ """Parse a JSON array from raw model output; [] on any failure."""
+ text = _strip_fences(content)
+ try:
+ data = json.loads(text)
+ except (ValueError, TypeError):
+ return []
+ return data if isinstance(data, list) else []
+
+
+def _strip_fences(content: str) -> str:
+ """Drop a surrounding ```json ... ``` fence if present."""
+ text = (content or "").strip()
+ if text.startswith("```"):
+ lines = text.splitlines()
+ # Drop opening fence (```json or ```) and a trailing fence if present.
+ lines = lines[1:]
+ if lines and lines[-1].strip().startswith("```"):
+ lines = lines[:-1]
+ text = "\n".join(lines).strip()
+ return text
+
+
+def _to_candidate(row: object, source_id: str) -> SemanticCandidate | None:
+ """Map one parsed dict to a SemanticCandidate; skip if unusable."""
+ if not isinstance(row, dict):
+ return None
+ try:
+ kind = CandidateKind(str(row.get("kind", "")).lower())
+ except ValueError:
+ return None
+ name = row.get("name")
+ definition = row.get("definition")
+ if not name or not definition:
+ return None
+ return SemanticCandidate(
+ kind=kind,
+ name=str(name),
+ definition=str(definition),
+ applies_to=str(row.get("applies_to", "")),
+ source_id=source_id,
+ )
diff --git a/src/lang2sql/ingestion/pipeline.py b/src/lang2sql/ingestion/pipeline.py
new file mode 100644
index 0000000..b30e1bb
--- /dev/null
+++ b/src/lang2sql/ingestion/pipeline.py
@@ -0,0 +1,29 @@
+"""IngestionPipeline — drives the Source × Extractor matrix (★③).
+
+A document is fetched from a Source then handed to an Extractor; the resulting
+candidates are shown for user confirmation before they land in the semantic
+layer (documents stay the source of truth). The pipeline itself is axis-blind:
+any Source pairs with any Extractor.
+"""
+
+from __future__ import annotations
+
+from ..core.ports.ingestion import (
+ DocExtractorPort,
+ SemanticCandidate,
+ SourcePort,
+)
+
+
+class IngestionPipeline:
+ """Fetch a document from ``source`` then extract candidates with ``extractor``."""
+
+ async def ingest(
+ self,
+ source: SourcePort,
+ extractor: DocExtractorPort,
+ ref: str,
+ blob: bytes | None = None,
+ ) -> list[SemanticCandidate]:
+ doc = await source.fetch(ref, blob)
+ return await extractor.extract(doc)
diff --git a/src/lang2sql/ingestion/sources/__init__.py b/src/lang2sql/ingestion/sources/__init__.py
new file mode 100644
index 0000000..21b67bb
--- /dev/null
+++ b/src/lang2sql/ingestion/sources/__init__.py
@@ -0,0 +1,7 @@
+"""Source axis adapters."""
+
+from __future__ import annotations
+
+from .file_source import FileSource
+
+__all__ = ["FileSource"]
diff --git a/src/lang2sql/ingestion/sources/file_source.py b/src/lang2sql/ingestion/sources/file_source.py
new file mode 100644
index 0000000..2bf9f2d
--- /dev/null
+++ b/src/lang2sql/ingestion/sources/file_source.py
@@ -0,0 +1,25 @@
+"""FileSource — the V1 Source axis (★③).
+
+Pulls a document from an uploaded file (MD/PDF/TXT). If the caller already has
+the bytes (a Discord attachment), pass them as ``blob`` and they are decoded as
+utf-8; otherwise the file at ``ref`` is read from disk. v1.5 adds a URL source
+implementing the same :class:`SourcePort`.
+"""
+
+from __future__ import annotations
+
+import os
+
+from ...core.ports.ingestion import Document
+
+
+class FileSource:
+ """``SourcePort`` over a local file or an in-memory blob."""
+
+ async def fetch(self, ref: str, blob: bytes | None = None) -> Document:
+ if blob is not None:
+ text = blob.decode("utf-8")
+ else:
+ with open(ref, "r", encoding="utf-8") as handle:
+ text = handle.read()
+ return Document(name=os.path.basename(ref), text=text, source_id=ref)
diff --git a/src/lang2sql/integrations/catalog/__init__.py b/src/lang2sql/integrations/catalog/__init__.py
deleted file mode 100644
index eaa993e..0000000
--- a/src/lang2sql/integrations/catalog/__init__.py
+++ /dev/null
@@ -1,3 +0,0 @@
-from .datahub_ import DataHubCatalogLoader
-
-__all__ = ["DataHubCatalogLoader"]
diff --git a/src/lang2sql/integrations/catalog/datahub_.py b/src/lang2sql/integrations/catalog/datahub_.py
deleted file mode 100644
index 7ac2e51..0000000
--- a/src/lang2sql/integrations/catalog/datahub_.py
+++ /dev/null
@@ -1,163 +0,0 @@
-from __future__ import annotations
-
-from ...core.catalog import CatalogEntry, TextDocument
-from ...core.exceptions import IntegrationMissingError
-from ...core.ports import CatalogLoaderPort
-
-try:
- import datahub as _datahub # type: ignore[import]
-except ImportError:
- _datahub = None # type: ignore[assignment]
-
-
-class DataHubCatalogLoader(CatalogLoaderPort):
- """DataHub URN → list[CatalogEntry] 변환.
-
- DataHub GMS 서버에서 테이블 메타데이터를 조회하여
- v2 아키텍처의 CatalogEntry 포맷으로 변환한다.
- """
-
- def __init__(
- self,
- gms_server: str = "http://localhost:8080",
- extra_headers: dict | None = None,
- ) -> None:
- if _datahub is None:
- raise IntegrationMissingError(
- "acryl-datahub",
- hint="pip install acryl-datahub",
- )
- # 레거시 DatahubMetadataFetcher를 내부에서 wrapping
- from utils.data.datahub_source import DatahubMetadataFetcher # type: ignore[import]
-
- self._fetcher = DatahubMetadataFetcher(
- gms_server=gms_server,
- extra_headers=extra_headers or {},
- )
-
- def load(self, urns: list[str] | None = None) -> list[CatalogEntry]:
- """DataHub에서 CatalogEntry 목록을 로드한다.
-
- Args:
- urns: 조회할 URN 목록. None이면 전체 URN을 조회한다.
-
- Returns:
- CatalogEntry 목록
- """
- if urns is None:
- urns = list(self._fetcher.get_urns())
-
- entries: list[CatalogEntry] = []
- for urn in urns:
- name = self._fetcher.get_table_name(urn) or ""
- description = self._fetcher.get_table_description(urn) or ""
- raw_cols = self._fetcher.get_column_names_and_descriptions(urn) or []
- columns: dict[str, str] = {
- col["column_name"]: col.get("column_description") or ""
- for col in raw_cols
- }
- entries.append(
- CatalogEntry(name=name, description=description, columns=columns)
- )
- return entries
-
- def load_lineage_documents(
- self,
- urns: list[str] | None = None,
- max_degree: int = 2,
- ) -> list[TextDocument]:
- """DataHub lineage 정보를 TextDocument 목록으로 변환한다.
-
- 내부적으로 build_table_metadata()를 사용하며, 사이클 안전성은
- 하위 레이어에서 보장된다:
- - get_table_lineage(): GraphQL degree 필터로 depth 상한 적용
- - min_degree_lineage(): 테이블별 최소 degree만 유지 (사이클 경로 dedup)
- - build_table_metadata(): 자기 자신(table == current_table) 제외
-
- Args:
- urns: 조회할 URN 목록. None이면 전체 URN을 조회한다.
- max_degree: 포함할 최대 lineage depth. 기본값 2.
-
- Returns:
- TextDocument 목록. lineage 없는 테이블은 제외된다.
-
- Usage::
-
- loader = DataHubCatalogLoader(gms_server="http://localhost:8080")
- pipeline = EnrichedNL2SQL(
- catalog=loader.load(),
- documents=loader.load_lineage_documents(),
- llm=..., db=..., embedding=...,
- )
- """
- if urns is None:
- urns = list(self._fetcher.get_urns())
-
- return [
- doc
- for urn in urns
- if (doc := self._urn_to_lineage_document(urn, max_degree)) is not None
- ]
-
- def _urn_to_lineage_document(
- self, urn: str, max_degree: int
- ) -> TextDocument | None:
- """단일 URN의 lineage를 TextDocument로 변환. lineage 없으면 None 반환."""
- try:
- # build_table_metadata가 upstream/downstream/column lineage를
- # 파싱 및 dedup까지 처리해준다.
- meta = self._fetcher.build_table_metadata(urn, max_degree=max_degree)
- except Exception:
- return None
-
- table_name = meta.get("table_name") or ""
- lineage = meta.get("lineage", {})
- upstream = lineage.get("upstream", [])
- downstream = lineage.get("downstream", [])
- upstream_columns = lineage.get("upstream_columns", [])
-
- if not upstream and not downstream and not upstream_columns:
- return None
-
- return TextDocument(
- id=f"lineage__{table_name}",
- title=f"{table_name} 리니지",
- content=self._format_lineage(
- table_name, upstream, downstream, upstream_columns
- ),
- source="datahub",
- metadata={"urn": urn, "table_name": table_name},
- )
-
- @staticmethod
- def _format_lineage(
- table_name: str,
- upstream: list[dict],
- downstream: list[dict],
- upstream_columns: list[dict],
- ) -> str:
- """lineage 데이터를 자연어 텍스트로 포맷한다."""
- lines: list[str] = [f"테이블: {table_name}", ""]
-
- if upstream:
- lines += ["[Upstream — 이 테이블의 원천 데이터]"]
- lines += [f" - {t['table']} (depth: {t['degree']})" for t in upstream]
- lines.append("")
-
- if downstream:
- lines += ["[Downstream — 이 테이블을 참조하는 테이블]"]
- lines += [f" - {t['table']} (depth: {t['degree']})" for t in downstream]
- lines.append("")
-
- if upstream_columns:
- lines += ["[컬럼 단위 Upstream Lineage]"]
- for dataset in upstream_columns:
- lines.append(f" {dataset.get('upstream_dataset', '')}:")
- lines += [
- f" {col['upstream_column']} → {col['downstream_column']}"
- f" (신뢰도: {col.get('confidence', 1.0):.2f})"
- for col in dataset.get("columns", [])
- ]
- lines.append("")
-
- return "\n".join(lines).strip()
diff --git a/src/lang2sql/integrations/chunking/__init__.py b/src/lang2sql/integrations/chunking/__init__.py
deleted file mode 100644
index f80fad9..0000000
--- a/src/lang2sql/integrations/chunking/__init__.py
+++ /dev/null
@@ -1,3 +0,0 @@
-from .semantic_ import SemanticChunker
-
-__all__ = ["SemanticChunker"]
diff --git a/src/lang2sql/integrations/chunking/semantic_.py b/src/lang2sql/integrations/chunking/semantic_.py
deleted file mode 100644
index 253be97..0000000
--- a/src/lang2sql/integrations/chunking/semantic_.py
+++ /dev/null
@@ -1,123 +0,0 @@
-from __future__ import annotations
-
-from ...core.catalog import IndexedChunk, TextDocument
-from ...core.exceptions import IntegrationMissingError
-from ...core.ports import EmbeddingPort
-from ...components.retrieval.chunker import DocumentChunkerPort
-
-
-class SemanticChunker(DocumentChunkerPort):
- """
- Embedding-based semantic chunker. Optional — explicit opt-in only.
-
- Splits text into sentences, computes cosine similarity between adjacent
- sentences, and treats similarity drop-offs as chunk boundaries.
- Produces more semantically coherent chunks than RecursiveCharacterChunker.
-
- Note: embedding API is called during indexing (not just at query time).
- Consider the cost and latency before choosing this over RecursiveCharacterChunker.
-
- Limitation: sentence splitting uses punctuation and newlines. Languages without
- whitespace word boundaries (e.g. Korean) rely on sentence-ending punctuation
- (., !, ?, 。) or newlines for splits. Accuracy may vary for dense prose.
-
- Args:
- embedding: EmbeddingPort implementation (can be shared with VectorRetriever).
- breakpoint_threshold: Similarity drop threshold for boundary detection. Default 0.3.
- Lower values produce more (smaller) chunks.
- min_chunk_size: Minimum characters per chunk; short chunks are merged
- into the previous one. Default 100.
-
- Example::
-
- from lang2sql.integrations.embedding import OpenAIEmbedding
- from lang2sql.integrations.chunking import SemanticChunker
-
- embedding = OpenAIEmbedding()
- chunker = SemanticChunker(embedding=embedding)
-
- retriever = VectorRetriever.from_sources(..., splitter=chunker)
- """
-
- def __init__(
- self,
- *,
- embedding: EmbeddingPort,
- breakpoint_threshold: float = 0.3,
- min_chunk_size: int = 100,
- ) -> None:
- try:
- import numpy as _np # noqa: F401
- except ImportError:
- raise IntegrationMissingError("numpy", hint="pip install numpy")
- self._embedding = embedding
- self._threshold = breakpoint_threshold
- self._min_size = min_chunk_size
-
- def split(self, docs: list[TextDocument]) -> list[IndexedChunk]:
- """LangChain-style batch split: list input → list output."""
- return [c for doc in docs for c in self.chunk(doc)]
-
- def chunk(self, doc: TextDocument) -> list[IndexedChunk]:
- import numpy as np
-
- content = doc.get("content", "")
- if not content:
- return []
-
- sentences = self._split_sentences(content)
- if len(sentences) <= 1:
- return self._make_chunks(doc, [content])
-
- embeddings = self._embedding.embed_texts(sentences)
- mat = np.array(embeddings, dtype=np.float32)
- norms = np.linalg.norm(mat, axis=1, keepdims=True)
- mat = mat / (norms + 1e-8)
-
- # cosine similarity between adjacent sentences — shape: (n-1,)
- sims = (mat[:-1] * mat[1:]).sum(axis=1)
-
- # positions where similarity drops sharply are chunk boundaries
- boundaries = [0]
- for i, sim in enumerate(sims):
- if sim < (1.0 - self._threshold):
- boundaries.append(i + 1)
- boundaries.append(len(sentences))
-
- raw_chunks: list[str] = []
- for start, end in zip(boundaries, boundaries[1:]):
- chunk_text = " ".join(sentences[start:end])
- if len(chunk_text) < self._min_size and raw_chunks:
- raw_chunks[-1] += (
- " " + chunk_text
- ) # merge short trailing chunk into previous
- else:
- raw_chunks.append(chunk_text)
-
- return self._make_chunks(doc, raw_chunks)
-
- def _split_sentences(self, text: str) -> list[str]:
- """Split on sentence-ending punctuation or newlines. No external tokenizer needed."""
- import re
-
- parts = re.split(r"(?<=[.!?。])\s+|\n+", text.strip())
- return [p.strip() for p in parts if p.strip()]
-
- def _make_chunks(self, doc: TextDocument, texts: list[str]) -> list[IndexedChunk]:
- title = doc.get("title", "")
- doc_id = doc.get("id", "")
- return [
- IndexedChunk(
- chunk_id=f"{doc_id}__{i}",
- text=f"{title}: {text}" if title else text,
- source_type="document",
- source_id=doc_id,
- chunk_index=i,
- metadata={
- "id": doc_id,
- "title": title,
- "source": doc.get("source", ""),
- },
- )
- for i, text in enumerate(texts)
- ]
diff --git a/src/lang2sql/integrations/db/__init__.py b/src/lang2sql/integrations/db/__init__.py
deleted file mode 100644
index 79ae1db..0000000
--- a/src/lang2sql/integrations/db/__init__.py
+++ /dev/null
@@ -1,3 +0,0 @@
-from .sqlalchemy_ import SQLAlchemyDB, SQLAlchemyExplorer
-
-__all__ = ["SQLAlchemyDB", "SQLAlchemyExplorer"]
diff --git a/src/lang2sql/integrations/db/sqlalchemy_.py b/src/lang2sql/integrations/db/sqlalchemy_.py
deleted file mode 100644
index 10f2ea6..0000000
--- a/src/lang2sql/integrations/db/sqlalchemy_.py
+++ /dev/null
@@ -1,140 +0,0 @@
-from __future__ import annotations
-
-from typing import Any
-
-from ...core.exceptions import IntegrationMissingError
-from ...core.ports import DBPort
-
-try:
- from sqlalchemy import create_engine, inspect as sa_inspect, text as sa_text
- from sqlalchemy.engine import Engine
-except ImportError:
- create_engine = None # type: ignore[assignment]
- sa_inspect = None # type: ignore[assignment]
- sa_text = None # type: ignore[assignment]
- Engine = None # type: ignore[assignment,misc]
-
-
-class SQLAlchemyDB(DBPort):
- """DBPort implementation backed by SQLAlchemy 2.x."""
-
- def __init__(self, url: str) -> None:
- if create_engine is None:
- raise IntegrationMissingError(
- "sqlalchemy", extra="sqlalchemy", hint="pip install sqlalchemy"
- )
- self._engine: Engine = create_engine(url)
-
- def execute(self, sql: str) -> list[dict[str, Any]]:
- with self._engine.connect() as conn:
- result = conn.execute(sa_text(sql))
- return [dict(row._mapping) for row in result]
-
-
-_WRITE_PREFIXES = frozenset(
- {
- "INSERT",
- "UPDATE",
- "DELETE",
- "DROP",
- "ALTER",
- "CREATE",
- "TRUNCATE",
- "REPLACE",
- "MERGE",
- }
-)
-
-
-class SQLAlchemyExplorer:
- """DBExplorerPort implementation backed by SQLAlchemy 2.x.
-
- Agent가 DB 스키마를 탐색할 때 사용. DDL + 샘플 데이터를 LLM context에 직접 주입.
- """
-
- def __init__(self, url: str, *, schema: str | None = None) -> None:
- if create_engine is None:
- raise IntegrationMissingError(
- "sqlalchemy", extra="sqlalchemy", hint="pip install sqlalchemy"
- )
- self._engine: Engine = create_engine(url)
- self._schema = schema
-
- @classmethod
- def from_engine(
- cls, engine: "Engine", *, schema: str | None = None
- ) -> "SQLAlchemyExplorer":
- """기존 engine 공유용. 연결 풀 중복 방지."""
- instance = cls.__new__(cls)
- instance._engine = engine
- instance._schema = schema
- return instance
-
- def list_tables(self, schema: str | None = None) -> list[str]:
- """테이블 목록 반환. Agent가 DB 구조 파악 시 첫 번째 호출."""
- insp = sa_inspect(self._engine)
- return insp.get_table_names(schema=schema or self._schema)
-
- def get_ddl(self, table: str, *, schema: str | None = None) -> str:
- """원본 DDL 문자열 반환. 컬럼 정의, PK, FK, 제약조건 모두 포함.
-
- SQLite: sqlite_master에서 원본 그대로 (DEFAULT, 코멘트, 인라인 FK 모두 보존).
- 그 외: SQLAlchemy CreateTable construct로 포괄적 DDL 생성.
- """
- resolved_schema = schema or self._schema
- if self._engine.dialect.name == "sqlite":
- rows = self._execute_safe(
- "SELECT sql FROM sqlite_master WHERE type='table' AND name=:table",
- {"table": table},
- )
- if rows and rows[0].get("sql"):
- return rows[0]["sql"]
-
- from sqlalchemy import MetaData
- from sqlalchemy import Table as SATable
- from sqlalchemy.schema import CreateTable
-
- metadata = MetaData()
- t = SATable(table, metadata, autoload_with=self._engine, schema=resolved_schema)
- return str(CreateTable(t).compile(self._engine))
-
- def sample_data(
- self, table: str, *, limit: int = 5, schema: str | None = None
- ) -> list[dict]:
- """실제 샘플 데이터 반환.
-
- f-string SQL 금지 — SQLAlchemy ORM select()로 identifier quoting 위임.
- dialect별 quoting 차이(PostgreSQL ", MySQL `, SQLite ")를 SQLAlchemy가 처리.
- """
- from sqlalchemy import MetaData, select
- from sqlalchemy import Table as SATable
-
- resolved_schema = schema or self._schema
- metadata = MetaData()
- t = SATable(table, metadata, autoload_with=self._engine, schema=resolved_schema)
- stmt = select(t).limit(limit)
- with self._engine.connect() as conn:
- result = conn.execute(stmt)
- return [dict(row._mapping) for row in result]
-
- def execute_read_only(self, sql: str) -> list[dict]:
- """읽기 전용 SQL 실행.
-
- 두 겹 방어:
- 1. prefix guard — 일반적인 쓰기 구문 빠른 거부 (UX)
- 2. rollback-always — WITH ... DELETE 같은 CTE 우회도 실제 DB 반영 방지
- """
- first_token = sql.strip().upper().split()[0] if sql.strip() else ""
- if first_token in _WRITE_PREFIXES:
- raise ValueError(f"Write operations not allowed: {sql[:50]!r}")
- with self._engine.connect() as conn:
- result = conn.execute(sa_text(sql))
- rows = [dict(row._mapping) for row in result]
- conn.rollback()
- return rows
-
- def _execute_safe(self, sql: str, params: dict | None = None) -> list[dict]:
- """파라미터화 쿼리 실행 (내부용)."""
- with self._engine.connect() as conn:
- result = conn.execute(sa_text(sql), params or {})
- return [dict(row._mapping) for row in result]
diff --git a/src/lang2sql/integrations/embedding/__init__.py b/src/lang2sql/integrations/embedding/__init__.py
deleted file mode 100644
index 3c4d54b..0000000
--- a/src/lang2sql/integrations/embedding/__init__.py
+++ /dev/null
@@ -1,15 +0,0 @@
-from .azure_ import AzureOpenAIEmbedding
-from .bedrock_ import BedrockEmbedding
-from .gemini_ import GeminiEmbedding
-from .huggingface_ import HuggingFaceEmbedding
-from .ollama_ import OllamaEmbedding
-from .openai_ import OpenAIEmbedding
-
-__all__ = [
- "AzureOpenAIEmbedding",
- "BedrockEmbedding",
- "GeminiEmbedding",
- "HuggingFaceEmbedding",
- "OllamaEmbedding",
- "OpenAIEmbedding",
-]
diff --git a/src/lang2sql/integrations/embedding/azure_.py b/src/lang2sql/integrations/embedding/azure_.py
deleted file mode 100644
index 824203e..0000000
--- a/src/lang2sql/integrations/embedding/azure_.py
+++ /dev/null
@@ -1,43 +0,0 @@
-from __future__ import annotations
-
-from ...core.exceptions import IntegrationMissingError
-from ...core.ports import EmbeddingPort
-
-try:
- import openai as _openai
-except ImportError:
- _openai = None # type: ignore[assignment]
-
-
-class AzureOpenAIEmbedding(EmbeddingPort):
- """EmbeddingPort implementation backed by the Azure OpenAI Embeddings API."""
-
- def __init__(
- self,
- *,
- azure_deployment: str,
- azure_endpoint: str,
- api_version: str = "2023-07-01-preview",
- api_key: str | None = None,
- ) -> None:
- if _openai is None:
- raise IntegrationMissingError(
- "openai", hint="pip install openai # or: uv sync"
- )
- self._client = _openai.AzureOpenAI(
- api_key=api_key,
- azure_endpoint=azure_endpoint,
- api_version=api_version,
- )
- self._deployment = azure_deployment
-
- def embed_query(self, text: str) -> list[float]:
- return (
- self._client.embeddings.create(input=text, model=self._deployment)
- .data[0]
- .embedding
- )
-
- def embed_texts(self, texts: list[str]) -> list[list[float]]:
- resp = self._client.embeddings.create(input=texts, model=self._deployment)
- return [item.embedding for item in resp.data]
diff --git a/src/lang2sql/integrations/embedding/bedrock_.py b/src/lang2sql/integrations/embedding/bedrock_.py
deleted file mode 100644
index c0cc69a..0000000
--- a/src/lang2sql/integrations/embedding/bedrock_.py
+++ /dev/null
@@ -1,53 +0,0 @@
-from __future__ import annotations
-
-import json
-
-from ...core.exceptions import IntegrationMissingError
-from ...core.ports import EmbeddingPort
-
-try:
- import boto3 as _boto3 # type: ignore[import]
-except ImportError:
- _boto3 = None # type: ignore[assignment]
-
-
-class BedrockEmbedding(EmbeddingPort):
- """EmbeddingPort implementation backed by AWS Bedrock Embeddings API.
-
- Supports Amazon Titan embedding models (e.g., amazon.titan-embed-text-v1).
- """
-
- def __init__(
- self,
- *,
- model_id: str,
- aws_access_key_id: str | None = None,
- aws_secret_access_key: str | None = None,
- region_name: str = "us-east-1",
- ) -> None:
- if _boto3 is None:
- raise IntegrationMissingError("boto3", hint="pip install boto3")
- self._model_id = model_id
- self._client = _boto3.client(
- "bedrock-runtime",
- aws_access_key_id=aws_access_key_id,
- aws_secret_access_key=aws_secret_access_key,
- region_name=region_name,
- )
-
- def _embed_single(self, text: str) -> list[float]:
- body = json.dumps({"inputText": text})
- resp = self._client.invoke_model(
- modelId=self._model_id,
- body=body,
- contentType="application/json",
- accept="application/json",
- )
- result = json.loads(resp["body"].read())
- return result["embedding"]
-
- def embed_query(self, text: str) -> list[float]:
- return self._embed_single(text)
-
- def embed_texts(self, texts: list[str]) -> list[list[float]]:
- return [self._embed_single(t) for t in texts]
diff --git a/src/lang2sql/integrations/embedding/gemini_.py b/src/lang2sql/integrations/embedding/gemini_.py
deleted file mode 100644
index ee1004f..0000000
--- a/src/lang2sql/integrations/embedding/gemini_.py
+++ /dev/null
@@ -1,46 +0,0 @@
-from __future__ import annotations
-
-from ...core.exceptions import IntegrationMissingError
-from ...core.ports import EmbeddingPort
-
-try:
- import google.generativeai as _genai # type: ignore[import]
-except ImportError:
- _genai = None # type: ignore[assignment]
-
-
-class GeminiEmbedding(EmbeddingPort):
- """EmbeddingPort implementation backed by the Google Gemini Embeddings API."""
-
- def __init__(
- self,
- *,
- model: str = "models/embedding-001",
- api_key: str | None = None,
- ) -> None:
- if _genai is None:
- raise IntegrationMissingError(
- "google-generativeai",
- hint="pip install google-generativeai",
- )
- if api_key:
- _genai.configure(api_key=api_key)
- self._model = model
-
- def embed_query(self, text: str) -> list[float]:
- result = _genai.embed_content(
- model=self._model,
- content=text,
- task_type="retrieval_query",
- )
- return result["embedding"]
-
- def embed_texts(self, texts: list[str]) -> list[list[float]]:
- return [
- _genai.embed_content(
- model=self._model,
- content=t,
- task_type="retrieval_document",
- )["embedding"]
- for t in texts
- ]
diff --git a/src/lang2sql/integrations/embedding/huggingface_.py b/src/lang2sql/integrations/embedding/huggingface_.py
deleted file mode 100644
index b0a584e..0000000
--- a/src/lang2sql/integrations/embedding/huggingface_.py
+++ /dev/null
@@ -1,27 +0,0 @@
-from __future__ import annotations
-
-from ...core.exceptions import IntegrationMissingError
-from ...core.ports import EmbeddingPort
-
-try:
- from sentence_transformers import SentenceTransformer as _SentenceTransformer # type: ignore[import]
-except ImportError:
- _SentenceTransformer = None # type: ignore[assignment]
-
-
-class HuggingFaceEmbedding(EmbeddingPort):
- """EmbeddingPort implementation backed by sentence-transformers."""
-
- def __init__(self, *, model: str) -> None:
- if _SentenceTransformer is None:
- raise IntegrationMissingError(
- "sentence-transformers",
- hint="pip install sentence-transformers",
- )
- self._model = _SentenceTransformer(model)
-
- def embed_query(self, text: str) -> list[float]:
- return self._model.encode(text, convert_to_numpy=True).tolist()
-
- def embed_texts(self, texts: list[str]) -> list[list[float]]:
- return self._model.encode(texts, convert_to_numpy=True).tolist()
diff --git a/src/lang2sql/integrations/embedding/ollama_.py b/src/lang2sql/integrations/embedding/ollama_.py
deleted file mode 100644
index d377c1b..0000000
--- a/src/lang2sql/integrations/embedding/ollama_.py
+++ /dev/null
@@ -1,32 +0,0 @@
-from __future__ import annotations
-
-from ...core.exceptions import IntegrationMissingError
-from ...core.ports import EmbeddingPort
-
-try:
- import ollama as _ollama # type: ignore[import]
-except ImportError:
- _ollama = None # type: ignore[assignment]
-
-
-class OllamaEmbedding(EmbeddingPort):
- """EmbeddingPort implementation backed by the Ollama Embeddings API."""
-
- def __init__(
- self,
- *,
- model: str,
- base_url: str = "http://localhost:11434",
- ) -> None:
- if _ollama is None:
- raise IntegrationMissingError("ollama", hint="pip install ollama")
- self._model = model
- self._client = _ollama.Client(host=base_url)
-
- def embed_query(self, text: str) -> list[float]:
- resp = self._client.embed(model=self._model, input=text)
- return resp.embeddings[0]
-
- def embed_texts(self, texts: list[str]) -> list[list[float]]:
- resp = self._client.embed(model=self._model, input=texts)
- return resp.embeddings
diff --git a/src/lang2sql/integrations/embedding/openai_.py b/src/lang2sql/integrations/embedding/openai_.py
deleted file mode 100644
index 605ee66..0000000
--- a/src/lang2sql/integrations/embedding/openai_.py
+++ /dev/null
@@ -1,32 +0,0 @@
-from __future__ import annotations
-
-from ...core.exceptions import IntegrationMissingError
-from ...core.ports import EmbeddingPort
-
-try:
- import openai as _openai
-except ImportError:
- _openai = None # type: ignore[assignment]
-
-
-class OpenAIEmbedding(EmbeddingPort):
- """EmbeddingPort implementation backed by OpenAI Embeddings API."""
-
- def __init__(
- self, *, model: str = "text-embedding-3-small", api_key: str | None = None
- ) -> None:
- if _openai is None:
- raise IntegrationMissingError("openai", hint="pip install openai")
- self._client = _openai.OpenAI(api_key=api_key)
- self._model = model
-
- def embed_query(self, text: str) -> list[float]:
- return (
- self._client.embeddings.create(input=text, model=self._model)
- .data[0]
- .embedding
- )
-
- def embed_texts(self, texts: list[str]) -> list[list[float]]:
- resp = self._client.embeddings.create(input=texts, model=self._model)
- return [item.embedding for item in resp.data]
diff --git a/src/lang2sql/integrations/llm/__init__.py b/src/lang2sql/integrations/llm/__init__.py
deleted file mode 100644
index 09ecf03..0000000
--- a/src/lang2sql/integrations/llm/__init__.py
+++ /dev/null
@@ -1,17 +0,0 @@
-from .anthropic_ import AnthropicLLM
-from .azure_ import AzureOpenAILLM
-from .bedrock_ import BedrockLLM
-from .gemini_ import GeminiLLM
-from .huggingface_ import HuggingFaceLLM
-from .ollama_ import OllamaLLM
-from .openai_ import OpenAILLM
-
-__all__ = [
- "AnthropicLLM",
- "AzureOpenAILLM",
- "BedrockLLM",
- "GeminiLLM",
- "HuggingFaceLLM",
- "OllamaLLM",
- "OpenAILLM",
-]
diff --git a/src/lang2sql/integrations/llm/anthropic_.py b/src/lang2sql/integrations/llm/anthropic_.py
deleted file mode 100644
index 037cc69..0000000
--- a/src/lang2sql/integrations/llm/anthropic_.py
+++ /dev/null
@@ -1,35 +0,0 @@
-from __future__ import annotations
-
-from ...core.exceptions import IntegrationMissingError
-from ...core.ports import LLMPort
-
-try:
- import anthropic as _anthropic
-except ImportError:
- _anthropic = None # type: ignore[assignment]
-
-
-class AnthropicLLM(LLMPort):
- """LLMPort implementation backed by the Anthropic Messages API."""
-
- def __init__(
- self, *, model: str, api_key: str | None = None, max_tokens: int = 4096
- ) -> None:
- if _anthropic is None:
- raise IntegrationMissingError(
- "anthropic", hint="pip install anthropic # or: uv sync"
- )
- self._client = _anthropic.Anthropic(api_key=api_key)
- self._model = model
- self._max_tokens = max_tokens
-
- def invoke(self, messages: list[dict[str, str]]) -> str:
- system = next((m["content"] for m in messages if m["role"] == "system"), None)
- user_msgs = [m for m in messages if m["role"] != "system"]
- resp = self._client.messages.create(
- model=self._model,
- max_tokens=self._max_tokens,
- system=system or "",
- messages=user_msgs,
- )
- return resp.content[0].text
diff --git a/src/lang2sql/integrations/llm/azure_.py b/src/lang2sql/integrations/llm/azure_.py
deleted file mode 100644
index cc32b9d..0000000
--- a/src/lang2sql/integrations/llm/azure_.py
+++ /dev/null
@@ -1,42 +0,0 @@
-from __future__ import annotations
-
-from ...core.exceptions import IntegrationMissingError
-from ...core.ports import LLMPort
-
-try:
- import openai as _openai
-except ImportError:
- _openai = None # type: ignore[assignment]
-
-
-class AzureOpenAILLM(LLMPort):
- """LLMPort implementation backed by the Azure OpenAI Chat Completions API."""
-
- def __init__(
- self,
- *,
- azure_deployment: str,
- azure_endpoint: str,
- api_version: str = "2023-07-01-preview",
- api_key: str | None = None,
- max_tokens: int = 4096,
- ) -> None:
- if _openai is None:
- raise IntegrationMissingError(
- "openai", hint="pip install openai # or: uv sync"
- )
- self._client = _openai.AzureOpenAI(
- api_key=api_key,
- azure_endpoint=azure_endpoint,
- api_version=api_version,
- )
- self._deployment = azure_deployment
- self._max_tokens = max_tokens
-
- def invoke(self, messages: list[dict[str, str]]) -> str:
- resp = self._client.chat.completions.create(
- model=self._deployment,
- messages=messages, # type: ignore[arg-type]
- max_tokens=self._max_tokens,
- )
- return resp.choices[0].message.content or ""
diff --git a/src/lang2sql/integrations/llm/bedrock_.py b/src/lang2sql/integrations/llm/bedrock_.py
deleted file mode 100644
index 29fadfb..0000000
--- a/src/lang2sql/integrations/llm/bedrock_.py
+++ /dev/null
@@ -1,46 +0,0 @@
-from __future__ import annotations
-
-from ...core.exceptions import IntegrationMissingError
-from ...core.ports import LLMPort
-
-try:
- import boto3 as _boto3 # type: ignore[import]
-except ImportError:
- _boto3 = None # type: ignore[assignment]
-
-
-class BedrockLLM(LLMPort):
- """LLMPort implementation backed by the AWS Bedrock Converse API."""
-
- def __init__(
- self,
- *,
- model: str,
- aws_access_key_id: str | None = None,
- aws_secret_access_key: str | None = None,
- region_name: str = "us-east-1",
- ) -> None:
- if _boto3 is None:
- raise IntegrationMissingError("boto3", hint="pip install boto3")
- self._model = model
- self._client = _boto3.client(
- "bedrock-runtime",
- aws_access_key_id=aws_access_key_id,
- aws_secret_access_key=aws_secret_access_key,
- region_name=region_name,
- )
-
- def invoke(self, messages: list[dict[str, str]]) -> str:
- system_parts = [m["content"] for m in messages if m["role"] == "system"]
- user_msgs = [m for m in messages if m["role"] != "system"]
-
- converse_messages = [
- {"role": m["role"], "content": [{"text": m["content"]}]} for m in user_msgs
- ]
-
- kwargs: dict = {"modelId": self._model, "messages": converse_messages}
- if system_parts:
- kwargs["system"] = [{"text": system_parts[0]}]
-
- resp = self._client.converse(**kwargs)
- return resp["output"]["message"]["content"][0]["text"]
diff --git a/src/lang2sql/integrations/llm/gemini_.py b/src/lang2sql/integrations/llm/gemini_.py
deleted file mode 100644
index 6c70799..0000000
--- a/src/lang2sql/integrations/llm/gemini_.py
+++ /dev/null
@@ -1,46 +0,0 @@
-from __future__ import annotations
-
-from ...core.exceptions import IntegrationMissingError
-from ...core.ports import LLMPort
-
-try:
- import google.generativeai as _genai # type: ignore[import]
-except ImportError:
- _genai = None # type: ignore[assignment]
-
-
-class GeminiLLM(LLMPort):
- """LLMPort implementation backed by the Google Gemini Generative AI API."""
-
- def __init__(
- self,
- *,
- model: str,
- api_key: str | None = None,
- ) -> None:
- if _genai is None:
- raise IntegrationMissingError(
- "google-generativeai",
- hint="pip install google-generativeai",
- )
- if api_key:
- _genai.configure(api_key=api_key)
- self._model_name = model
-
- def invoke(self, messages: list[dict[str, str]]) -> str:
- system_parts = [m["content"] for m in messages if m["role"] == "system"]
- system_instruction = system_parts[0] if system_parts else None
-
- contents = []
- for m in messages:
- if m["role"] == "system":
- continue
- role = "model" if m["role"] == "assistant" else "user"
- contents.append({"role": role, "parts": [m["content"]]})
-
- model = _genai.GenerativeModel(
- model_name=self._model_name,
- system_instruction=system_instruction,
- )
- response = model.generate_content(contents)
- return response.text
diff --git a/src/lang2sql/integrations/llm/huggingface_.py b/src/lang2sql/integrations/llm/huggingface_.py
deleted file mode 100644
index 08cca81..0000000
--- a/src/lang2sql/integrations/llm/huggingface_.py
+++ /dev/null
@@ -1,35 +0,0 @@
-from __future__ import annotations
-
-from ...core.exceptions import IntegrationMissingError
-from ...core.ports import LLMPort
-
-try:
- from huggingface_hub import InferenceClient as _InferenceClient # type: ignore[import]
-except ImportError:
- _InferenceClient = None # type: ignore[assignment]
-
-
-class HuggingFaceLLM(LLMPort):
- """LLMPort implementation backed by the HuggingFace Inference API."""
-
- def __init__(
- self,
- *,
- repo_id: str | None = None,
- endpoint_url: str | None = None,
- api_token: str | None = None,
- ) -> None:
- if _InferenceClient is None:
- raise IntegrationMissingError(
- "huggingface_hub", hint="pip install huggingface_hub"
- )
- if repo_id is None and endpoint_url is None:
- raise ValueError("Either repo_id or endpoint_url must be provided.")
- self._client = _InferenceClient(
- model=endpoint_url or repo_id,
- token=api_token,
- )
-
- def invoke(self, messages: list[dict[str, str]]) -> str:
- resp = self._client.chat_completion(messages=messages) # type: ignore[arg-type]
- return resp.choices[0].message.content or ""
diff --git a/src/lang2sql/integrations/llm/ollama_.py b/src/lang2sql/integrations/llm/ollama_.py
deleted file mode 100644
index 4172df4..0000000
--- a/src/lang2sql/integrations/llm/ollama_.py
+++ /dev/null
@@ -1,31 +0,0 @@
-from __future__ import annotations
-
-from ...core.exceptions import IntegrationMissingError
-from ...core.ports import LLMPort
-
-try:
- import ollama as _ollama # type: ignore[import]
-except ImportError:
- _ollama = None # type: ignore[assignment]
-
-
-class OllamaLLM(LLMPort):
- """LLMPort implementation backed by the Ollama chat API."""
-
- def __init__(
- self,
- *,
- model: str,
- base_url: str = "http://localhost:11434",
- ) -> None:
- if _ollama is None:
- raise IntegrationMissingError("ollama", hint="pip install ollama")
- self._model = model
- self._client = _ollama.Client(host=base_url)
-
- def invoke(self, messages: list[dict[str, str]]) -> str:
- resp = self._client.chat(
- model=self._model,
- messages=messages, # type: ignore[arg-type]
- )
- return resp.message.content
diff --git a/src/lang2sql/integrations/llm/openai_.py b/src/lang2sql/integrations/llm/openai_.py
deleted file mode 100644
index 041a5a4..0000000
--- a/src/lang2sql/integrations/llm/openai_.py
+++ /dev/null
@@ -1,28 +0,0 @@
-from __future__ import annotations
-
-from ...core.exceptions import IntegrationMissingError
-from ...core.ports import LLMPort
-
-try:
- import openai as _openai
-except ImportError:
- _openai = None # type: ignore[assignment]
-
-
-class OpenAILLM(LLMPort):
- """LLMPort implementation backed by the OpenAI Chat Completions API."""
-
- def __init__(self, *, model: str, api_key: str | None = None) -> None:
- if _openai is None:
- raise IntegrationMissingError(
- "openai", hint="pip install openai # or: uv sync"
- )
- self._client = _openai.OpenAI(api_key=api_key)
- self._model = model
-
- def invoke(self, messages: list[dict[str, str]]) -> str:
- resp = self._client.chat.completions.create(
- model=self._model,
- messages=messages, # type: ignore[arg-type]
- )
- return resp.choices[0].message.content or ""
diff --git a/src/lang2sql/integrations/loaders/__init__.py b/src/lang2sql/integrations/loaders/__init__.py
deleted file mode 100644
index c5d095f..0000000
--- a/src/lang2sql/integrations/loaders/__init__.py
+++ /dev/null
@@ -1,3 +0,0 @@
-from .pdf_ import PDFLoader
-
-__all__ = ["PDFLoader"]
diff --git a/src/lang2sql/integrations/loaders/pdf_.py b/src/lang2sql/integrations/loaders/pdf_.py
deleted file mode 100644
index 8d1143d..0000000
--- a/src/lang2sql/integrations/loaders/pdf_.py
+++ /dev/null
@@ -1,58 +0,0 @@
-from __future__ import annotations
-
-from pathlib import Path
-
-from ...core.catalog import TextDocument
-from ...core.exceptions import IntegrationMissingError
-from ...core.ports import DocumentLoaderPort
-
-try:
- import fitz as _fitz
-except ImportError:
- _fitz = None # type: ignore[assignment]
-
-
-class PDFLoader(DocumentLoaderPort):
- """
- PDF file → list[TextDocument].
-
- Requires pymupdf (fitz) as an optional dependency.
- Raises ``IntegrationMissingError`` if not installed.
-
- Produces one TextDocument per page::
-
- id = "{filename}__p{page_number}" (1-indexed)
- title = "{filename} page {page_number}"
- content = extracted text of that page
- source = file path
-
- Usage::
-
- from lang2sql.integrations.loaders import PDFLoader
-
- docs = PDFLoader().load("report.pdf")
-
- Installation::
-
- pip install pymupdf
- """
-
- def load(self, path: str) -> list[TextDocument]:
- if _fitz is None:
- raise IntegrationMissingError(
- "pymupdf",
- hint="pip install pymupdf",
- )
- p = Path(path)
- pdf = _fitz.open(str(p))
- docs: list[TextDocument] = []
- for i, page in enumerate(pdf, start=1):
- docs.append(
- TextDocument(
- id=f"{p.stem}__p{i}",
- title=f"{p.stem} page {i}",
- content=page.get_text(),
- source=str(p),
- )
- )
- return docs
diff --git a/src/lang2sql/integrations/vectorstore/__init__.py b/src/lang2sql/integrations/vectorstore/__init__.py
deleted file mode 100644
index eeab249..0000000
--- a/src/lang2sql/integrations/vectorstore/__init__.py
+++ /dev/null
@@ -1,5 +0,0 @@
-from .faiss_ import FAISSVectorStore
-from .inmemory_ import InMemoryVectorStore
-from .pgvector_ import PGVectorStore
-
-__all__ = ["InMemoryVectorStore", "FAISSVectorStore", "PGVectorStore"]
diff --git a/src/lang2sql/integrations/vectorstore/faiss_.py b/src/lang2sql/integrations/vectorstore/faiss_.py
deleted file mode 100644
index 0e6a12f..0000000
--- a/src/lang2sql/integrations/vectorstore/faiss_.py
+++ /dev/null
@@ -1,104 +0,0 @@
-from __future__ import annotations
-
-import json
-import pathlib
-
-from ...core.exceptions import IntegrationMissingError
-from ...core.ports import VectorStorePort
-
-try:
- import faiss as _faiss
- import numpy as _np
-except ImportError:
- _faiss = None # type: ignore[assignment]
- _np = None # type: ignore[assignment]
-
-
-class FAISSVectorStore(VectorStorePort):
- """
- FAISS-backed vector store with optional file persistence.
-
- Uses IndexFlatIP + L2 normalization for exact cosine similarity.
- Index is lazy-initialized on the first upsert() call.
-
- Known limitation (append-only):
- Upserting the same chunk_id twice creates duplicate FAISS entries.
- To rebuild a clean index, create a new FAISSVectorStore instance
- and run from_chunks() again from scratch.
-
- Args:
- index_path: Optional path for save() / load(). Used as default
- path when save() is called without an explicit argument.
-
- Installation:
- pip install faiss-cpu # CPU-only
- pip install faiss-gpu # GPU variant
- """
-
- def __init__(self, index_path: str | None = None) -> None:
- if _faiss is None or _np is None:
- raise IntegrationMissingError("faiss", hint="pip install faiss-cpu")
- self._index_path = index_path
- self._index: object | None = None # faiss.IndexFlatIP, None until first upsert
- self._ids: list[str] = []
-
- # ── VectorStorePort ──────────────────────────────────────────────
-
- def upsert(self, ids: list[str], vectors: list[list[float]]) -> None:
- """L2-normalize and add vectors. Lazy-creates index on first call."""
- arr = _np.array(vectors, dtype=_np.float32)
- _faiss.normalize_L2(arr) # in-place cosine trick
- if self._index is None:
- self._index = _faiss.IndexFlatIP(arr.shape[1])
- self._index.add(arr)
- self._ids.extend(ids)
-
- def search(self, vector: list[float], k: int) -> list[tuple[str, float]]:
- """Return (chunk_id, cosine_score) for the k nearest vectors."""
- if self._index is None or self._index.ntotal == 0:
- return []
- q = _np.array([vector], dtype=_np.float32)
- _faiss.normalize_L2(q)
- k = min(k, self._index.ntotal)
- scores, positions = self._index.search(q, k)
- return [
- (self._ids[int(pos)], float(scores[0][j]))
- for j, pos in enumerate(positions[0])
- if pos >= 0
- ]
-
- # ── Persistence ──────────────────────────────────────────────────
-
- def save(self, path: str | None = None) -> None:
- """
- Write index to {path} and id list to {path}.meta.
- Falls back to self._index_path when path is None.
- Raises ValueError if no path is available.
- Raises RuntimeError if called before any upsert().
- """
- path = path or self._index_path
- if path is None:
- raise ValueError(
- "No path provided and index_path was not set at construction."
- )
- if self._index is None:
- raise RuntimeError("Cannot save before any upsert() call.")
- pathlib.Path(path).parent.mkdir(parents=True, exist_ok=True)
- _faiss.write_index(self._index, path)
- pathlib.Path(path + ".meta").write_text(json.dumps(self._ids), encoding="utf-8")
-
- @classmethod
- def load(cls, path: str) -> "FAISSVectorStore":
- """
- Load index from {path} and id list from {path}.meta.
- Raises FileNotFoundError if either file is missing.
- """
- if _faiss is None or _np is None:
- raise IntegrationMissingError("faiss", hint="pip install faiss-cpu")
- meta_path = pathlib.Path(path + ".meta")
- if not pathlib.Path(path).exists() or not meta_path.exists():
- raise FileNotFoundError(f"Index files not found: {path}, {path}.meta")
- store = cls(index_path=path)
- store._index = _faiss.read_index(path)
- store._ids = json.loads(meta_path.read_text(encoding="utf-8"))
- return store
diff --git a/src/lang2sql/integrations/vectorstore/inmemory_.py b/src/lang2sql/integrations/vectorstore/inmemory_.py
deleted file mode 100644
index 97a176f..0000000
--- a/src/lang2sql/integrations/vectorstore/inmemory_.py
+++ /dev/null
@@ -1,51 +0,0 @@
-from __future__ import annotations
-
-from ...core.exceptions import IntegrationMissingError
-from ...core.ports import VectorStorePort
-
-try:
- import numpy as _np
-except ImportError:
- _np = None # type: ignore[assignment]
-
-
-class InMemoryVectorStore(VectorStorePort):
- """
- Brute-force cosine similarity vector store backed by numpy.
-
- upsert merges new entries into the existing store (true upsert — no data loss
- across multiple calls). Rebuilds the matrix from a dict on each search call,
- so duplicate chunk_ids are overwritten rather than duplicated.
-
- Handles tens of thousands of vectors without issue. No faiss dependency required.
-
- For larger scale or persistence, use FAISSVectorStore / PGVectorStore (next PR).
- For advanced vector stores (Chroma, Qdrant, etc.), implement VectorStorePort directly.
- """
-
- def __init__(self) -> None:
- if _np is None:
- raise IntegrationMissingError("numpy", hint="pip install numpy")
- self._store: dict[str, list[float]] = {} # chunk_id → vector
-
- def upsert(self, ids: list[str], vectors: list[list[float]]) -> None:
- # Merge into existing store — preserves vectors from previous upsert calls.
- # Duplicate ids overwrite the existing entry (true upsert semantics).
- for id_, vec in zip(ids, vectors):
- self._store[id_] = vec
-
- def search(self, vector: list[float], k: int) -> list[tuple[str, float]]:
- if not self._store:
- return []
-
- ids = list(self._store.keys())
- matrix = _np.array(list(self._store.values()), dtype=_np.float32)
- q = _np.array(vector, dtype=_np.float32)
-
- norms = _np.linalg.norm(matrix, axis=1)
- q_norm = _np.linalg.norm(q)
- sims = matrix @ q / (norms * q_norm + 1e-8)
-
- k = min(k, len(ids))
- top_k = _np.argsort(sims)[::-1][:k]
- return [(ids[int(i)], float(sims[i])) for i in top_k]
diff --git a/src/lang2sql/integrations/vectorstore/pgvector_.py b/src/lang2sql/integrations/vectorstore/pgvector_.py
deleted file mode 100644
index aab745e..0000000
--- a/src/lang2sql/integrations/vectorstore/pgvector_.py
+++ /dev/null
@@ -1,91 +0,0 @@
-from __future__ import annotations
-
-from ...core.exceptions import IntegrationMissingError
-from ...core.ports import VectorStorePort
-
-try:
- import psycopg2 as _psycopg2
- from pgvector.psycopg2 import register_vector as _register_vector
-except ImportError:
- _psycopg2 = None # type: ignore[assignment]
- _register_vector = None # type: ignore[assignment]
-
-
-class PGVectorStore(VectorStorePort):
- """
- PostgreSQL pgvector-backed vector store.
-
- True upsert semantics via ON CONFLICT DO UPDATE — idempotent,
- no duplicates across multiple from_chunks() runs.
- Table is created automatically on first upsert() call.
-
- Args:
- connection: PostgreSQL connection URL.
- e.g. "postgresql://user:pass@localhost:5432/mydb"
- table_name: Name of the vector table. Default "lang2sql_vectors".
-
- Installation:
- pip install psycopg2-binary pgvector
-
- Quick start with Docker:
- docker run -d -e POSTGRES_PASSWORD=postgres \\
- -p 5432:5432 pgvector/pgvector:pg16
- """
-
- def __init__(
- self,
- *,
- connection: str,
- table_name: str = "lang2sql_vectors",
- ) -> None:
- if _psycopg2 is None or _register_vector is None:
- raise IntegrationMissingError(
- "psycopg2", hint="pip install psycopg2-binary pgvector"
- )
- self._conn = _psycopg2.connect(connection)
- _register_vector(self._conn)
- self._table = table_name
- self._ready = False # True after first _ensure_table()
-
- def _ensure_table(self, dim: int) -> None:
- if self._ready:
- return
- with self._conn.cursor() as cur:
- cur.execute("CREATE EXTENSION IF NOT EXISTS vector;")
- cur.execute(
- f"CREATE TABLE IF NOT EXISTS {self._table} "
- f"(id TEXT PRIMARY KEY, embedding vector({dim}));"
- )
- self._conn.commit()
- self._ready = True
-
- # ── VectorStorePort ──────────────────────────────────────────────
-
- def upsert(self, ids: list[str], vectors: list[list[float]]) -> None:
- """Create table if needed, then upsert all (id, vector) pairs."""
- if not vectors:
- return
- self._ensure_table(len(vectors[0]))
- with self._conn.cursor() as cur:
- for id_, vec in zip(ids, vectors):
- cur.execute(
- f"INSERT INTO {self._table} (id, embedding) VALUES (%s, %s) "
- "ON CONFLICT (id) DO UPDATE SET embedding = EXCLUDED.embedding;",
- (id_, vec),
- )
- self._conn.commit()
-
- def search(self, vector: list[float], k: int) -> list[tuple[str, float]]:
- """Return (chunk_id, cosine_score) for the k nearest vectors.
- Returns [] if the table has not been created yet.
- """
- if not self._ready:
- return []
- with self._conn.cursor() as cur:
- cur.execute(
- f"SELECT id, 1 - (embedding <=> %s::vector) AS score "
- f"FROM {self._table} "
- f"ORDER BY embedding <=> %s::vector LIMIT %s;",
- (vector, vector, k),
- )
- return [(row[0], float(row[1])) for row in cur.fetchall()]
diff --git a/src/lang2sql/memory/__init__.py b/src/lang2sql/memory/__init__.py
new file mode 100644
index 0000000..da9c737
--- /dev/null
+++ b/src/lang2sql/memory/__init__.py
@@ -0,0 +1,19 @@
+"""Memory — Hermes 3-axis split (Store / Recall / Extractor) plus service (★②).
+
+V1 wires the simplest combination: in-memory store, inject-all recall, manual
+extractor. See ``core/ports/memory.py`` for the axis Protocols.
+"""
+
+from __future__ import annotations
+
+from .extractors import ManualExtractor
+from .recall import InjectAllRecall
+from .service import MemoryService
+from .stores import InMemoryStore
+
+__all__ = [
+ "MemoryService",
+ "InMemoryStore",
+ "InjectAllRecall",
+ "ManualExtractor",
+]
diff --git a/src/lang2sql/memory/extractors/__init__.py b/src/lang2sql/memory/extractors/__init__.py
new file mode 100644
index 0000000..6630a48
--- /dev/null
+++ b/src/lang2sql/memory/extractors/__init__.py
@@ -0,0 +1,7 @@
+"""Extractor axis strategies."""
+
+from __future__ import annotations
+
+from .manual import ManualExtractor
+
+__all__ = ["ManualExtractor"]
diff --git a/src/lang2sql/memory/extractors/manual.py b/src/lang2sql/memory/extractors/manual.py
new file mode 100644
index 0000000..31a346a
--- /dev/null
+++ b/src/lang2sql/memory/extractors/manual.py
@@ -0,0 +1,20 @@
+"""ManualExtractor — the V1 Extractor axis (★②).
+
+In V1 the only way a fact is born is the explicit ``/remember`` command, so
+automatic extraction from the transcript yields nothing. v1.5 swaps in an
+LLM-backed extractor that mines repeated patterns — same :class:`ExtractorPort`.
+"""
+
+from __future__ import annotations
+
+from typing import Sequence
+
+from ...core.ports.memory import Fact
+from ...core.types import Message
+
+
+class ManualExtractor:
+ """``ExtractorPort`` that never auto-creates facts."""
+
+ async def extract(self, owner: str, transcript: Sequence[Message]) -> list[Fact]:
+ return []
diff --git a/src/lang2sql/memory/recall/__init__.py b/src/lang2sql/memory/recall/__init__.py
new file mode 100644
index 0000000..2a31361
--- /dev/null
+++ b/src/lang2sql/memory/recall/__init__.py
@@ -0,0 +1,7 @@
+"""Recall axis strategies."""
+
+from __future__ import annotations
+
+from .inject_all import InjectAllRecall
+
+__all__ = ["InjectAllRecall"]
diff --git a/src/lang2sql/memory/recall/inject_all.py b/src/lang2sql/memory/recall/inject_all.py
new file mode 100644
index 0000000..3824ab9
--- /dev/null
+++ b/src/lang2sql/memory/recall/inject_all.py
@@ -0,0 +1,17 @@
+"""InjectAllRecall — the V1 Recall axis (★②).
+
+The simplest possible strategy: surface every fact the owner has, ignoring the
+query entirely. Good enough while fact counts are tiny. v1.5 replaces this with
+keyword matching and v2 with vector similarity — same :class:`RecallPort`.
+"""
+
+from __future__ import annotations
+
+from ...core.ports.memory import Fact, StorePort
+
+
+class InjectAllRecall:
+ """``RecallPort`` that returns all of an owner's facts unfiltered."""
+
+ async def recall(self, owner: str, query: str, store: StorePort) -> list[Fact]:
+ return await store.all(owner)
diff --git a/src/lang2sql/memory/service.py b/src/lang2sql/memory/service.py
new file mode 100644
index 0000000..de7e521
--- /dev/null
+++ b/src/lang2sql/memory/service.py
@@ -0,0 +1,53 @@
+"""MemoryService — binds the three memory axes into one usable surface (★②).
+
+Store, Recall and Extractor are injected, so a version bump is an adapter swap
+at the wiring site rather than a change here. The service offers the three
+operations the rest of the system needs: write a fact (``remember``), fetch the
+relevant ones for a turn (``recall``), and render them for the prompt
+(``render``).
+"""
+
+from __future__ import annotations
+
+import time
+import uuid
+
+from ..core.ports.memory import ExtractorPort, Fact, RecallPort, StorePort
+
+
+class MemoryService:
+ """Coordinates the Store / Recall / Extractor axes."""
+
+ def __init__(
+ self,
+ store: StorePort,
+ recall: RecallPort,
+ extractor: ExtractorPort,
+ ) -> None:
+ self._store = store
+ self._recall = recall
+ self._extractor = extractor
+
+ async def remember(self, owner: str, text: str) -> Fact:
+ """Create and persist a manual fact, returning it."""
+ fact = Fact(
+ id=str(uuid.uuid4()),
+ owner=owner,
+ text=text,
+ source="manual",
+ ts=time.time(),
+ )
+ await self._store.add(fact)
+ return fact
+
+ async def recall(self, owner: str, query: str) -> list[Fact]:
+ """Surface the facts the recall strategy deems relevant to ``query``."""
+ return await self._recall.recall(owner, query, self._store)
+
+ def render(self, facts: list[Fact]) -> str:
+ """Render facts as a markdown block for the system prompt ("" if none)."""
+ if not facts:
+ return ""
+ lines = ["## Remembered facts"]
+ lines.extend(f"- {fact.text}" for fact in facts)
+ return "\n".join(lines)
diff --git a/src/lang2sql/memory/stores/__init__.py b/src/lang2sql/memory/stores/__init__.py
new file mode 100644
index 0000000..cd91c34
--- /dev/null
+++ b/src/lang2sql/memory/stores/__init__.py
@@ -0,0 +1,7 @@
+"""Store axis adapters."""
+
+from __future__ import annotations
+
+from .in_memory import InMemoryStore
+
+__all__ = ["InMemoryStore"]
diff --git a/src/lang2sql/memory/stores/in_memory.py b/src/lang2sql/memory/stores/in_memory.py
new file mode 100644
index 0000000..4ec49f8
--- /dev/null
+++ b/src/lang2sql/memory/stores/in_memory.py
@@ -0,0 +1,23 @@
+"""InMemoryStore — the V1 Store axis (★②).
+
+Facts live in a process-local dict keyed by owner. No persistence: a restart
+forgets everything. v1.5 swaps this for a SQLite-backed store implementing the
+same :class:`StorePort`, with zero changes elsewhere.
+"""
+
+from __future__ import annotations
+
+from ...core.ports.memory import Fact, StorePort
+
+
+class InMemoryStore:
+ """``StorePort`` over a ``dict[owner, list[Fact]]``."""
+
+ def __init__(self) -> None:
+ self._facts: dict[str, list[Fact]] = {}
+
+ async def add(self, fact: Fact) -> None:
+ self._facts.setdefault(fact.owner, []).append(fact)
+
+ async def all(self, owner: str) -> list[Fact]:
+ return list(self._facts.get(owner, ()))
diff --git a/src/lang2sql/safety/__init__.py b/src/lang2sql/safety/__init__.py
new file mode 100644
index 0000000..f0cf9f5
--- /dev/null
+++ b/src/lang2sql/safety/__init__.py
@@ -0,0 +1,8 @@
+"""Safety pipeline package — the ★① gate every SQL execution passes."""
+
+from __future__ import annotations
+
+from .layers import TimeoutLayer, WhitelistLayer
+from .pipeline import SafetyPipeline
+
+__all__ = ["SafetyPipeline", "WhitelistLayer", "TimeoutLayer"]
diff --git a/src/lang2sql/safety/layers/__init__.py b/src/lang2sql/safety/layers/__init__.py
new file mode 100644
index 0000000..b311b42
--- /dev/null
+++ b/src/lang2sql/safety/layers/__init__.py
@@ -0,0 +1,8 @@
+"""V1 safety layers."""
+
+from __future__ import annotations
+
+from .timeout import TimeoutLayer
+from .whitelist import WhitelistLayer
+
+__all__ = ["WhitelistLayer", "TimeoutLayer"]
diff --git a/src/lang2sql/safety/layers/timeout.py b/src/lang2sql/safety/layers/timeout.py
new file mode 100644
index 0000000..456fd73
--- /dev/null
+++ b/src/lang2sql/safety/layers/timeout.py
@@ -0,0 +1,34 @@
+"""Timeout layer — execution-config layer, never blocks.
+
+This layer does not inspect the SQL for slow constructs (``pg_sleep`` etc.);
+that is enforced at *run time* by the executor honoring ``ctx.timeout_seconds``.
+Its only job is to guarantee a timeout is set on the context before execution.
+"""
+
+from __future__ import annotations
+
+from ...core.ports.safety import (
+ SafetyContext,
+ SafetyDecision,
+ Verdict,
+)
+
+_DEFAULT_TIMEOUT_SECONDS = 30
+
+
+class TimeoutLayer:
+ """Ensures ``ctx.timeout_seconds`` is set, then PASSes. ``SafetyLayerPort``."""
+
+ def __init__(self, default_seconds: int = _DEFAULT_TIMEOUT_SECONDS) -> None:
+ self._default_seconds = default_seconds
+
+ @property
+ def name(self) -> str:
+ return "timeout"
+
+ def check(self, sql: str, ctx: SafetyContext) -> SafetyDecision:
+ # A non-positive (or unset) timeout would mean "no limit" downstream;
+ # clamp it to the default so a slow query (case #7, pg_sleep) is bounded.
+ if not ctx.timeout_seconds or ctx.timeout_seconds <= 0:
+ ctx.timeout_seconds = self._default_seconds
+ return SafetyDecision(verdict=Verdict.PASS, sql=sql, layer=self.name)
diff --git a/src/lang2sql/safety/layers/whitelist.py b/src/lang2sql/safety/layers/whitelist.py
new file mode 100644
index 0000000..9a6a284
--- /dev/null
+++ b/src/lang2sql/safety/layers/whitelist.py
@@ -0,0 +1,147 @@
+"""Whitelist layer — V1 gate that only lets read-only statements through.
+
+Fail-closed by design: anything we can't confidently classify as a single
+read-only ``SELECT`` / ``WITH`` (or an ``EXPLAIN`` of one) is BLOCKED. This is
+deliberately blunt string parsing — the precise AST validation that catches
+schema-qualified bypasses and the like is V1.5 work (no sqlglot here).
+"""
+
+from __future__ import annotations
+
+import re
+
+from ...core.ports.safety import (
+ SafetyContext,
+ SafetyDecision,
+ Verdict,
+)
+
+# Statement keywords that mutate state. If any of these appears as a *statement*
+# keyword anywhere (including inside a CTE body), we block fail-closed.
+_DML_DDL = frozenset(
+ {
+ "INSERT",
+ "UPDATE",
+ "DELETE",
+ "DROP",
+ "ALTER",
+ "CREATE",
+ "TRUNCATE",
+ "GRANT",
+ "COPY",
+ }
+)
+
+# Statements we allow to *start* a query.
+_ALLOWED_START = ("SELECT", "WITH")
+
+# A line comment runs to end-of-line; a block comment is /* ... */ (non-greedy,
+# spanning newlines).
+_LINE_COMMENT = re.compile(r"--[^\n]*")
+_BLOCK_COMMENT = re.compile(r"/\*.*?\*/", re.DOTALL)
+
+# Word-boundary keyword match, case-insensitive. Used to scan for mutating
+# keywords as standalone words (so "created_at" never matches "CREATE").
+_WORD = re.compile(r"[A-Za-z_][A-Za-z_0-9]*")
+
+
+def _strip_comments(sql: str) -> str:
+ """Remove line and block comments, then collapse surrounding whitespace."""
+ no_block = _BLOCK_COMMENT.sub(" ", sql)
+ no_line = _LINE_COMMENT.sub(" ", no_block)
+ return no_line.strip()
+
+
+def _split_statements(sql: str) -> list[str]:
+ """Split on ``;`` into non-empty trimmed statements.
+
+ String/identifier-quote awareness is V1.5 AST work; for V1 a bare ``;``
+ separator is enough to detect multi-statement payloads fail-closed.
+ """
+ parts = [p.strip() for p in sql.split(";")]
+ return [p for p in parts if p]
+
+
+class WhitelistLayer:
+ """SELECT/WITH-only gate. Implements ``SafetyLayerPort``."""
+
+ @property
+ def name(self) -> str:
+ return "whitelist"
+
+ def check(self, sql: str, ctx: SafetyContext) -> SafetyDecision:
+ cleaned = _strip_comments(sql)
+
+ # Empty / blank (or comment-only) input → cannot parse anything.
+ if not cleaned:
+ return SafetyDecision(
+ verdict=Verdict.BLOCK,
+ sql=sql,
+ reason="parse_error",
+ layer=self.name,
+ )
+
+ statements = _split_statements(cleaned)
+
+ # Multi-statement payloads (e.g. ``; DELETE FROM t; --``) are blocked.
+ if len(statements) != 1:
+ return SafetyDecision(
+ verdict=Verdict.BLOCK,
+ sql=sql,
+ reason="multi_statement",
+ layer=self.name,
+ )
+
+ statement = statements[0]
+
+ # An EXPLAIN wrapper is allowed only when it fronts a read-only query.
+ # Strip a leading EXPLAIN (and EXPLAIN ANALYZE / EXPLAIN VERBOSE etc.)
+ # then re-check the underlying statement's leading keyword.
+ body = statement
+ explain = re.match(r"(?is)^EXPLAIN\b(.*)$", body)
+ if explain is not None:
+ body = explain.group(1).strip()
+ # ``EXPLAIN`` may carry options like ANALYZE / VERBOSE / a paren
+ # option list before the real statement. Drop leading option words
+ # and any "( ... )" option block so we reach the real keyword.
+ paren = re.match(r"(?s)^\((?:[^()]|\([^()]*\))*\)\s*(.*)$", body)
+ if paren is not None:
+ body = paren.group(1).strip()
+ else:
+ # Consume leading bare option words (ANALYZE, VERBOSE, ...).
+ while True:
+ m = re.match(r"(?i)^(ANALYZE|VERBOSE|COSTS|BUFFERS)\b\s*(.*)$", body)
+ if m is None:
+ break
+ body = m.group(2).strip()
+ if not body:
+ return SafetyDecision(
+ verdict=Verdict.BLOCK,
+ sql=sql,
+ reason="parse_error",
+ layer=self.name,
+ )
+
+ # The (possibly EXPLAIN-unwrapped) statement must start with an allowed
+ # read-only keyword.
+ first_word_m = _WORD.match(body)
+ if first_word_m is None or first_word_m.group(0).upper() not in _ALLOWED_START:
+ return SafetyDecision(
+ verdict=Verdict.BLOCK,
+ sql=sql,
+ reason="not_select",
+ layer=self.name,
+ )
+
+ # Fail-closed keyword scan: a mutating keyword appearing anywhere as a
+ # standalone word blocks (catches ``WITH x AS (INSERT ...) SELECT``).
+ for token in _WORD.findall(body):
+ if token.upper() in _DML_DDL:
+ return SafetyDecision(
+ verdict=Verdict.BLOCK,
+ sql=sql,
+ reason=f"dml_keyword:{token.upper()}",
+ layer=self.name,
+ )
+
+ return SafetyDecision(verdict=Verdict.PASS, sql=sql, layer=self.name)
diff --git a/src/lang2sql/safety/pipeline.py b/src/lang2sql/safety/pipeline.py
new file mode 100644
index 0000000..968d498
--- /dev/null
+++ b/src/lang2sql/safety/pipeline.py
@@ -0,0 +1,49 @@
+"""Safety pipeline — runs layers in order, first non-PASS short-circuits.
+
+Airport-security model: SQL walks a *line* of layers. The first layer that
+does not return PASS decides the outcome and the rest are skipped. If every
+layer PASSes, the pipeline returns a PASS carrying the (possibly rewritten)
+SQL forward — so a layer that REWRITEs is itself a non-PASS short-circuit in
+V1, while accumulated rewrites only matter once we have multiple rewriting
+layers (V1.5+).
+"""
+
+from __future__ import annotations
+
+from typing import Sequence
+
+from ..core.ports.safety import (
+ SafetyContext,
+ SafetyDecision,
+ SafetyLayerPort,
+ Verdict,
+)
+from .layers import TimeoutLayer, WhitelistLayer
+
+
+def _default_layers() -> list[SafetyLayerPort]:
+ # Whitelist first (cheap, fail-closed reject), then Timeout (exec config).
+ return [WhitelistLayer(), TimeoutLayer()]
+
+
+class SafetyPipeline:
+ """Ordered safety layers. Implements ``SafetyPipelinePort``."""
+
+ def __init__(self, layers: Sequence[SafetyLayerPort] | None = None) -> None:
+ self._layers: list[SafetyLayerPort] = (
+ list(layers) if layers is not None else _default_layers()
+ )
+
+ @property
+ def layers(self) -> Sequence[SafetyLayerPort]:
+ return self._layers
+
+ def evaluate(self, sql: str, ctx: SafetyContext) -> SafetyDecision:
+ current = sql
+ for layer in self._layers:
+ decision = layer.check(current, ctx)
+ if decision.verdict is not Verdict.PASS:
+ return decision
+ # Carry any rewritten SQL forward to the next layer.
+ current = decision.sql
+ return SafetyDecision(verdict=Verdict.PASS, sql=current, layer="pipeline")
diff --git a/src/lang2sql/semantic/__init__.py b/src/lang2sql/semantic/__init__.py
new file mode 100644
index 0000000..110ae6e
--- /dev/null
+++ b/src/lang2sql/semantic/__init__.py
@@ -0,0 +1,29 @@
+"""Semantic federation layer (★④) — definitions, scoping, composition."""
+
+from __future__ import annotations
+
+from .layer import SemanticLayer
+from .scoped_layer import merge_scoped
+from .sql_composer import SQLComposer
+from .store import SemanticStore
+from .types import (
+ Dimension,
+ Metric,
+ Relationship,
+ Rule,
+ SemanticEntry,
+ SemanticKind,
+)
+
+__all__ = [
+ "SemanticLayer",
+ "merge_scoped",
+ "SQLComposer",
+ "SemanticStore",
+ "SemanticEntry",
+ "SemanticKind",
+ "Metric",
+ "Dimension",
+ "Relationship",
+ "Rule",
+]
diff --git a/src/lang2sql/semantic/layer.py b/src/lang2sql/semantic/layer.py
new file mode 100644
index 0000000..d82e4c7
--- /dev/null
+++ b/src/lang2sql/semantic/layer.py
@@ -0,0 +1,59 @@
+"""The effective semantic layer for one resolved scope (★④).
+
+A :class:`SemanticLayer` is a flat bag of :class:`SemanticEntry` already
+resolved for a particular request — federation merging happens upstream in
+:mod:`lang2sql.semantic.scoped_layer`. The harness calls :meth:`render` to
+splice these definitions into the system prompt so the model prefers them over
+its own assumptions.
+"""
+
+from __future__ import annotations
+
+from .types import SemanticEntry
+
+
+class SemanticLayer:
+ """An ordered collection of definitions keyed by ``name``.
+
+ Insertion order is preserved for stable rendering; :meth:`add` replaces an
+ existing entry of the same ``name`` in place so later authority wins
+ without reordering.
+ """
+
+ def __init__(self, entries: list[SemanticEntry] | None = None) -> None:
+ self._entries: list[SemanticEntry] = list(entries or [])
+
+ @property
+ def entries(self) -> list[SemanticEntry]:
+ return list(self._entries)
+
+ def lookup(self, name: str) -> SemanticEntry | None:
+ """Return the entry named ``name``, or ``None`` if absent."""
+ for entry in self._entries:
+ if entry.name == name:
+ return entry
+ return None
+
+ def add(self, entry: SemanticEntry) -> None:
+ """Add ``entry``, replacing any existing entry with the same name."""
+ for i, existing in enumerate(self._entries):
+ if existing.name == entry.name:
+ self._entries[i] = entry
+ return
+ self._entries.append(entry)
+
+ def render(self) -> str:
+ """Markdown bullet list for system-prompt injection.
+
+ Empty string when there are no entries so the caller can skip the
+ section header entirely.
+ """
+ if not self._entries:
+ return ""
+ lines: list[str] = []
+ for entry in self._entries:
+ scope = f" (applies to {entry.applies_to})" if entry.applies_to else ""
+ lines.append(
+ f"- **{entry.name}** [{entry.kind.value}]: {entry.definition}{scope}"
+ )
+ return "\n".join(lines)
diff --git a/src/lang2sql/semantic/scoped_layer.py b/src/lang2sql/semantic/scoped_layer.py
new file mode 100644
index 0000000..17a1903
--- /dev/null
+++ b/src/lang2sql/semantic/scoped_layer.py
@@ -0,0 +1,33 @@
+"""Pure federation merge — most specific scope wins (★④).
+
+Given each scope's own entries ordered NARROW→WIDE (thread, channel, guild,
+builtin), collapse them into one :class:`SemanticLayer`. For any ``name`` the
+first (narrowest) definition encountered is authoritative; wider scopes only
+contribute names the narrow scopes did not define. No I/O here — the resolver
+gathers the per-scope lists and hands them in.
+"""
+
+from __future__ import annotations
+
+from ..core.identity import Scope
+from .layer import SemanticLayer
+from .types import SemanticEntry
+
+
+def merge_scoped(
+ scoped_entries: list[tuple[Scope, list[SemanticEntry]]],
+) -> SemanticLayer:
+ """Merge ordered ``(scope, entries)`` pairs into one effective layer.
+
+ ``scoped_entries`` must run narrow→wide. The first definition seen for a
+ given ``name`` wins; later (wider) ones for the same name are dropped.
+ """
+ layer = SemanticLayer()
+ seen: set[str] = set()
+ for _scope, entries in scoped_entries:
+ for entry in entries:
+ if entry.name in seen:
+ continue
+ seen.add(entry.name)
+ layer.add(entry)
+ return layer
diff --git a/src/lang2sql/semantic/sql_composer.py b/src/lang2sql/semantic/sql_composer.py
new file mode 100644
index 0000000..0fa928a
--- /dev/null
+++ b/src/lang2sql/semantic/sql_composer.py
@@ -0,0 +1,27 @@
+"""Expand a metric name to its definition (★④, minimal V1).
+
+V1 deliberately does the simplest useful thing: resolve a name against an
+effective :class:`SemanticLayer` and hand back the stored definition string.
+Real composition — substituting nested metric references, stitching dimension
+filters, emitting a SQL fragment — is later work and is flagged inline.
+"""
+
+from __future__ import annotations
+
+from .layer import SemanticLayer
+
+
+class SQLComposer:
+ """Resolve metric/dimension names against a resolved semantic layer."""
+
+ def __init__(self, layer: SemanticLayer) -> None:
+ self._layer = layer
+
+ def expand(self, name: str) -> str | None:
+ """Return the definition string for ``name``, or ``None`` if unknown.
+
+ TODO(v1.5): real composition — recursively expand nested references and
+ emit a SQL fragment rather than the raw stored definition.
+ """
+ entry = self._layer.lookup(name)
+ return entry.definition if entry is not None else None
diff --git a/src/lang2sql/semantic/store.py b/src/lang2sql/semantic/store.py
new file mode 100644
index 0000000..06063d4
--- /dev/null
+++ b/src/lang2sql/semantic/store.py
@@ -0,0 +1,32 @@
+"""In-memory storage of semantic definitions, keyed by scope (★④).
+
+V1 keeps everything in a process-local dict — SQLite persistence is deferred
+(v1.5). The store is intentionally dumb: it knows nothing about inheritance or
+resolution, only "here are the entries authored exactly at this scope".
+Resolution lives in :mod:`lang2sql.tenancy.scope_resolver`.
+"""
+
+from __future__ import annotations
+
+from ..core.identity import Scope
+from .types import SemanticEntry
+
+
+class SemanticStore:
+ """Maps ``str(scope)`` → the entries defined exactly at that scope."""
+
+ def __init__(self) -> None:
+ self._by_scope: dict[str, list[SemanticEntry]] = {}
+
+ def add(self, scope: Scope, entry: SemanticEntry) -> None:
+ """Store ``entry`` at ``scope``, replacing a same-named entry there."""
+ bucket = self._by_scope.setdefault(str(scope), [])
+ for i, existing in enumerate(bucket):
+ if existing.name == entry.name:
+ bucket[i] = entry
+ return
+ bucket.append(entry)
+
+ def entries_at(self, scope: Scope) -> list[SemanticEntry]:
+ """Entries authored exactly at ``scope`` (no inheritance)."""
+ return list(self._by_scope.get(str(scope), []))
diff --git a/src/lang2sql/semantic/types.py b/src/lang2sql/semantic/types.py
new file mode 100644
index 0000000..7001ba3
--- /dev/null
+++ b/src/lang2sql/semantic/types.py
@@ -0,0 +1,71 @@
+"""The stored unit of the semantic layer (★④).
+
+A :class:`SemanticEntry` is one confirmed definition — a metric, dimension,
+relationship, or business rule — that has landed in the semantic layer after
+a user confirmed a :class:`~lang2sql.core.ports.ingestion.SemanticCandidate`
+(or typed it directly via ``/define_metric``). Entries are pure data: they
+carry no I/O and are addressed by ``name`` within a federation scope.
+"""
+
+from __future__ import annotations
+
+from dataclasses import dataclass
+from datetime import datetime, timezone
+from enum import Enum
+
+
+class SemanticKind(str, Enum):
+ """What flavour of definition an entry holds.
+
+ Superset of ``CandidateKind`` — candidates only ever propose METRIC,
+ DIMENSION, or RULE, but the layer can also store RELATIONSHIP entries
+ authored directly (e.g. join keys between tables).
+ """
+
+ METRIC = "metric"
+ DIMENSION = "dimension"
+ RELATIONSHIP = "relationship"
+ RULE = "rule"
+
+
+@dataclass
+class SemanticEntry:
+ """One confirmed definition stored at a single federation scope.
+
+ ``name`` is the lookup key (unique within a scope); ``definition`` is the
+ natural-language or SQL-fragment meaning injected into the system prompt.
+ ``applies_to`` optionally narrows the entry to a table/column. ``source_id``
+ links back to the originating document so the audit trail survives.
+ """
+
+ kind: SemanticKind
+ name: str
+ definition: str
+ applies_to: str = ""
+ source_id: str = ""
+ created_by: str = ""
+ created_at: str = ""
+
+ def __post_init__(self) -> None:
+ if not self.created_at:
+ self.created_at = datetime.now(timezone.utc).isoformat()
+
+
+def Metric(name: str, definition: str, **kwargs: str) -> SemanticEntry:
+ """Convenience builder pinning ``kind`` to METRIC."""
+ return SemanticEntry(SemanticKind.METRIC, name, definition, **kwargs)
+
+
+def Dimension(name: str, definition: str, **kwargs: str) -> SemanticEntry:
+ """Convenience builder pinning ``kind`` to DIMENSION."""
+ return SemanticEntry(SemanticKind.DIMENSION, name, definition, **kwargs)
+
+
+def Relationship(name: str, definition: str, **kwargs: str) -> SemanticEntry:
+ """Convenience builder pinning ``kind`` to RELATIONSHIP."""
+ return SemanticEntry(SemanticKind.RELATIONSHIP, name, definition, **kwargs)
+
+
+def Rule(name: str, definition: str, **kwargs: str) -> SemanticEntry:
+ """Convenience builder pinning ``kind`` to RULE."""
+ return SemanticEntry(SemanticKind.RULE, name, definition, **kwargs)
diff --git a/src/lang2sql/tenancy/__init__.py b/src/lang2sql/tenancy/__init__.py
new file mode 100644
index 0000000..2f339c9
--- /dev/null
+++ b/src/lang2sql/tenancy/__init__.py
@@ -0,0 +1,13 @@
+"""Tenancy — scope resolution, per-scope secrets, and context assembly.
+
+The :class:`ContextConcierge` is the one place concrete semantic/safety/adapter
+classes get wired together into a :class:`~lang2sql.harness.context.HarnessContext`.
+"""
+
+from __future__ import annotations
+
+from .concierge import ContextConcierge
+from .encrypted_secrets import EncryptedSecrets
+from .scope_resolver import ScopeResolver
+
+__all__ = ["ContextConcierge", "EncryptedSecrets", "ScopeResolver"]
diff --git a/src/lang2sql/tenancy/concierge.py b/src/lang2sql/tenancy/concierge.py
new file mode 100644
index 0000000..2050091
--- /dev/null
+++ b/src/lang2sql/tenancy/concierge.py
@@ -0,0 +1,132 @@
+"""ContextConcierge — the assembly point that builds one HarnessContext.
+
+This is the only module allowed to import the concrete semantic/safety/adapter
+classes; everywhere else depends on the ``core.ports`` Protocols. Per request it
+picks an LLM (OpenAI when keyed, else the FakeLLM), restores or starts a
+:class:`Session`, and wires the explorer, safety pipeline, scope resolver, and
+audit store into a :class:`HarnessContext` for the loop.
+
+Dependency-injection friendly: every collaborator can be overridden in the
+ctor so tests (and v1.5 swaps) need no network and no globals.
+"""
+
+from __future__ import annotations
+
+import os
+
+from ..adapters.db.postgres_explorer import PostgresExplorer
+from ..adapters.llm.fake import FakeLLM
+from ..adapters.llm.openai_ import OpenAILLM
+from ..adapters.storage.sqlite_semantic import SqliteSemanticStore
+from ..adapters.storage.sqlite_store import SqliteStore
+from ..core.identity import Identity
+from ..core.ports.audit import AuditPort
+from ..core.ports.explorer import ExplorerPort
+from ..core.ports.llm import LLMPort
+from ..core.ports.safety import SafetyPipelinePort
+from ..core.ports.secrets import SecretsPort
+from ..core.ports.semantic_scope import ScopeResolverPort
+from ..harness.context import HarnessContext
+from ..harness.session import Session
+from ..harness.tool_registry import ToolRegistry
+from ..ingestion import FileSource, IngestionPipeline, LLMExtractor
+from ..memory import InjectAllRecall, InMemoryStore, ManualExtractor, MemoryService
+from ..safety.pipeline import SafetyPipeline
+from ..tools import build_default_tools
+from .encrypted_secrets import EncryptedSecrets
+from .scope_resolver import ScopeResolver
+
+# DSN used for the V1 explorer stub when a scope has registered none yet.
+_DEFAULT_DSN = "postgresql://stub/v1"
+
+
+class ContextConcierge:
+ """Assembles per-request :class:`HarnessContext` from injected ports."""
+
+ def __init__(
+ self,
+ *,
+ path: str = ":memory:",
+ store: SqliteStore | None = None,
+ llm: LLMPort | None = None,
+ explorer: ExplorerPort | None = None,
+ safety: SafetyPipelinePort | None = None,
+ scope_resolver: ScopeResolverPort | None = None,
+ secrets: SecretsPort | None = None,
+ audit: AuditPort | None = None,
+ max_turns: int = 8,
+ ) -> None:
+ # ``path`` drives the default persistence backends; ``:memory:`` keeps
+ # tests isolated, a file path makes sessions/definitions/secrets durable.
+ self._store = store if store is not None else SqliteStore(path)
+ # Audit + session persistence both ride the one sqlite store by default.
+ self._llm = llm if llm is not None else _default_llm()
+ self._explorer = explorer if explorer is not None else PostgresExplorer(_DEFAULT_DSN)
+ self._safety = safety if safety is not None else SafetyPipeline()
+ # Persistent semantic store by default so definitions survive restart.
+ self._scope_resolver = (
+ scope_resolver
+ if scope_resolver is not None
+ else ScopeResolver(SqliteSemanticStore(path))
+ )
+ # Secrets share the session/audit store's kv table (and sqlite file).
+ self._secrets = (
+ secrets if secrets is not None else EncryptedSecrets(self._store)
+ )
+ self._audit = audit if audit is not None else self._store
+ self._max_turns = max_turns
+
+ # V1 memory (in-memory + inject-all + manual) and ingestion (file × LLM).
+ self._memory = MemoryService(InMemoryStore(), InjectAllRecall(), ManualExtractor())
+ self._ingestion = IngestionPipeline()
+ self._source = FileSource()
+ self._extractor = LLMExtractor(self._llm)
+
+ @property
+ def store(self) -> SqliteStore:
+ return self._store
+
+ @property
+ def secrets(self) -> SecretsPort:
+ """Per-scope encrypted credential store (DSNs/API keys via ``/connect``)."""
+ return self._secrets
+
+ @property
+ def scope_resolver(self) -> ScopeResolverPort:
+ """Federation resolver over the (by default persistent) semantic store."""
+ return self._scope_resolver
+
+ async def build_context(
+ self, identity: Identity, user_text: str | None = None
+ ) -> HarnessContext:
+ session = await self._store.load(identity.session_key())
+ if session is None:
+ session = Session(identity=identity)
+
+ tools = ToolRegistry(
+ build_default_tools(
+ memory=self._memory,
+ ingestion=self._ingestion,
+ source=self._source,
+ extractor=self._extractor,
+ )
+ )
+
+ return HarnessContext(
+ identity=identity,
+ llm=self._llm,
+ tools=tools,
+ session=session,
+ explorer=self._explorer,
+ safety=self._safety,
+ audit=self._audit,
+ scope_resolver=self._scope_resolver,
+ max_turns=self._max_turns,
+ )
+
+
+def _default_llm() -> LLMPort:
+ """OpenAI when a key is present, otherwise the offline FakeLLM."""
+ if os.environ.get("OPENAI_API_KEY"):
+ return OpenAILLM()
+ return FakeLLM()
diff --git a/src/lang2sql/tenancy/encrypted_secrets.py b/src/lang2sql/tenancy/encrypted_secrets.py
new file mode 100644
index 0000000..a1362f6
--- /dev/null
+++ b/src/lang2sql/tenancy/encrypted_secrets.py
@@ -0,0 +1,67 @@
+"""EncryptedSecrets — per-scope credential storage over a :class:`SqliteStore` kv.
+
+Real symmetric encryption via :class:`cryptography.fernet.Fernet` (AES-128-CBC +
+HMAC). Each secret value is encrypted to a Fernet token before it lands in the
+kv table, so a stolen database file yields no plaintext DSNs/API keys without
+the key. The :class:`SecretsPort` boundary is unchanged, so callers (concierge,
+``/connect``) need no edits.
+
+Key management:
+
+* If env ``LANG2SQL_SECRET_KEY`` holds a urlsafe-base64 Fernet key, that is the
+ encryption key — point every deployment instance at the same value so secrets
+ decrypt across restarts and across machines.
+* Otherwise a key is generated once and persisted in the kv table under the
+ reserved scope ``__secrets__`` / key ``fernet_key``. This keeps a single
+ sqlite file self-contained (secrets survive restart) but is only as private
+ as the file itself — set ``LANG2SQL_SECRET_KEY`` for real key separation.
+"""
+
+from __future__ import annotations
+
+import os
+
+from cryptography.fernet import Fernet
+
+from ..adapters.storage.sqlite_store import SqliteStore
+
+# Reserved kv location for the auto-generated key (when env key is absent).
+_KEY_SCOPE = "__secrets__"
+_KEY_NAME = "fernet_key"
+_ENV_KEY = "LANG2SQL_SECRET_KEY"
+
+
+def _resolve_key(store: SqliteStore) -> bytes:
+ """Pick the Fernet key: env override, else persisted, else freshly generated."""
+ env_key = os.environ.get(_ENV_KEY)
+ if env_key:
+ return env_key.encode("ascii")
+
+ stored = store.kv_get(_KEY_SCOPE, _KEY_NAME)
+ if stored is not None:
+ return stored.encode("ascii")
+
+ key = Fernet.generate_key()
+ store.kv_set(_KEY_SCOPE, _KEY_NAME, key.decode("ascii"))
+ return key
+
+
+class EncryptedSecrets:
+ """Implements :class:`SecretsPort` on top of a kv-capable store."""
+
+ def __init__(self, store: SqliteStore, *, key: bytes | None = None) -> None:
+ self._store = store
+ self._fernet = Fernet(key if key is not None else _resolve_key(store))
+
+ async def get(self, scope: str, key: str) -> str | None:
+ blob = self._store.kv_get(scope, key)
+ if blob is None:
+ return None
+ return self._fernet.decrypt(blob.encode("ascii")).decode("utf-8")
+
+ async def set(self, scope: str, key: str, value: str) -> None:
+ token = self._fernet.encrypt(value.encode("utf-8")).decode("ascii")
+ self._store.kv_set(scope, key, token)
+
+ async def delete(self, scope: str, key: str) -> None:
+ self._store.kv_delete(scope, key)
diff --git a/src/lang2sql/tenancy/scope_resolver.py b/src/lang2sql/tenancy/scope_resolver.py
new file mode 100644
index 0000000..2093fbf
--- /dev/null
+++ b/src/lang2sql/tenancy/scope_resolver.py
@@ -0,0 +1,42 @@
+"""Federation resolution over a :class:`SemanticStore` (★④).
+
+Implements :class:`~lang2sql.core.ports.semantic_scope.ScopeResolverPort`. The
+store holds raw per-scope definitions; this resolver walks an identity's
+``scope_chain()`` (narrow→wide) and merges them so the most specific definition
+of each ``name`` wins. ``define``/``entries_at`` delegate straight to the store.
+"""
+
+from __future__ import annotations
+
+from ..core.identity import Identity, Scope
+from ..semantic.layer import SemanticLayer
+from ..semantic.scoped_layer import merge_scoped
+from ..semantic.store import SemanticStore
+from ..semantic.types import SemanticEntry
+
+
+class ScopeResolver:
+ """Resolve effective semantic layers, backed by a :class:`SemanticStore`."""
+
+ def __init__(self, store: SemanticStore | None = None) -> None:
+ self._store = store if store is not None else SemanticStore()
+
+ @property
+ def store(self) -> SemanticStore:
+ return self._store
+
+ async def effective_layer(self, identity: Identity) -> SemanticLayer:
+ """Merge ``identity``'s scope chain narrow→wide; most specific wins."""
+ scoped = [
+ (scope, self._store.entries_at(scope))
+ for scope in identity.scope_chain()
+ ]
+ return merge_scoped(scoped)
+
+ async def define(self, scope: Scope, entry: SemanticEntry) -> None:
+ """Persist one definition at an explicit scope."""
+ self._store.add(scope, entry)
+
+ async def entries_at(self, scope: Scope) -> list[SemanticEntry]:
+ """Definitions stored exactly at ``scope`` (no inheritance)."""
+ return self._store.entries_at(scope)
diff --git a/src/lang2sql/tools/__init__.py b/src/lang2sql/tools/__init__.py
new file mode 100644
index 0000000..cc5b398
--- /dev/null
+++ b/src/lang2sql/tools/__init__.py
@@ -0,0 +1,43 @@
+"""Tools — the ctx-aware capabilities the agent can invoke.
+
+``build_default_tools`` assembles the V1 tool set. Tools that need only ports
+read them from the live :class:`HarnessContext`; tools backed by a service
+(remember, ingest_doc) take it by injection here so the wiring stays in one
+place (the ContextConcierge calls this).
+"""
+
+from __future__ import annotations
+
+from ..core.ports.ingestion import DocExtractorPort, SourcePort
+from ..core.ports.tool import ToolPort
+from ..ingestion.pipeline import IngestionPipeline
+from ..memory.service import MemoryService
+from .ask_user import AskUser
+from .define_metric import DefineMetric
+from .explore_schema import ExploreSchema
+from .ingest_doc import IngestDoc
+from .remember import Remember
+from .run_sql import RunSQL
+
+__all__ = [
+ "build_default_tools",
+ "RunSQL", "ExploreSchema", "DefineMetric", "Remember", "AskUser", "IngestDoc",
+]
+
+
+def build_default_tools(
+ *,
+ memory: MemoryService,
+ ingestion: IngestionPipeline,
+ source: SourcePort,
+ extractor: DocExtractorPort,
+) -> list[ToolPort]:
+ """The six V1 tools (v4.1 §4.1)."""
+ return [
+ RunSQL(),
+ ExploreSchema(),
+ DefineMetric(),
+ AskUser(),
+ Remember(memory),
+ IngestDoc(ingestion, source, extractor),
+ ]
diff --git a/src/lang2sql/tools/ask_user.py b/src/lang2sql/tools/ask_user.py
new file mode 100644
index 0000000..683fb6c
--- /dev/null
+++ b/src/lang2sql/tools/ask_user.py
@@ -0,0 +1,37 @@
+"""ask_user — request a clarification from the human.
+
+The agent calls this when a question is ambiguous. V1 returns the question as
+the tool result; the frontend surfaces it and the user's reply arrives as the
+next turn. (A suspend/resume round-trip is a v1.5 frontend concern; the loop
+contract here is simply that the model gets its question echoed back so it
+stops guessing.)
+"""
+
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Any
+
+from ..core.types import ToolResult, ToolSpec
+
+if TYPE_CHECKING:
+ from ..harness.context import HarnessContext
+
+
+class AskUser:
+ @property
+ def spec(self) -> ToolSpec:
+ return ToolSpec(
+ name="ask_user",
+ description="Ask the user a clarifying question when the request is ambiguous.",
+ parameters={
+ "type": "object",
+ "properties": {"question": {"type": "string"}},
+ "required": ["question"],
+ },
+ )
+
+ async def run(self, args: dict[str, Any], ctx: "HarnessContext") -> ToolResult:
+ question = (args.get("question") or "").strip()
+ if not question:
+ return ToolResult(call_id="", content="no question provided", is_error=True)
+ return ToolResult(call_id="", content=f"❓ Awaiting user: {question}")
diff --git a/src/lang2sql/tools/define_metric.py b/src/lang2sql/tools/define_metric.py
new file mode 100644
index 0000000..68dd546
--- /dev/null
+++ b/src/lang2sql/tools/define_metric.py
@@ -0,0 +1,70 @@
+"""define_metric — scope-aware definition writer (★④ federation).
+
+Writes one :class:`SemanticEntry` to a federation scope via the
+:class:`ScopeResolverPort`. With no explicit scope it lands at the identity's
+default write scope (current channel), so ``#marketing`` and ``#finance`` can
+hold different definitions of the same name without conflict.
+"""
+
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Any
+
+from ..core.ports.audit import AuditEvent
+from ..core.identity import Scope, ScopeLevel
+from ..core.types import ToolResult, ToolSpec
+from ..semantic.types import SemanticEntry, SemanticKind
+
+if TYPE_CHECKING:
+ from ..harness.context import HarnessContext
+
+
+class DefineMetric:
+ @property
+ def spec(self) -> ToolSpec:
+ return ToolSpec(
+ name="define_metric",
+ description=(
+ "Define a metric/dimension/rule for the current scope (channel by "
+ "default). Later questions in this scope use this definition."
+ ),
+ parameters={
+ "type": "object",
+ "properties": {
+ "name": {"type": "string"},
+ "definition": {"type": "string"},
+ "kind": {"type": "string", "enum": ["metric", "dimension", "rule"]},
+ "scope": {"type": "string", "enum": ["channel", "guild"],
+ "description": "where to store it; default channel"},
+ },
+ "required": ["name", "definition"],
+ },
+ )
+
+ async def run(self, args: dict[str, Any], ctx: "HarnessContext") -> ToolResult:
+ if ctx.scope_resolver is None:
+ return ToolResult(call_id="", content="semantic layer unavailable", is_error=True)
+
+ name = (args.get("name") or "").strip()
+ definition = (args.get("definition") or "").strip()
+ if not name or not definition:
+ return ToolResult(call_id="", content="name and definition are required", is_error=True)
+
+ kind = SemanticKind(args.get("kind", "metric"))
+ scope = self._resolve_scope(args.get("scope"), ctx)
+ entry = SemanticEntry(kind=kind, name=name, definition=definition,
+ created_by=ctx.identity.user_id)
+ await ctx.scope_resolver.define(scope, entry)
+
+ if ctx.audit is not None:
+ await ctx.audit.record(
+ AuditEvent(actor=ctx.identity.user_id, action="define_metric",
+ scope=str(scope), detail={"name": name, "kind": kind.value})
+ )
+ return ToolResult(call_id="", content=f"✅ {kind.value} '{name}' defined at {scope}.")
+
+ @staticmethod
+ def _resolve_scope(requested: str | None, ctx: "HarnessContext") -> Scope:
+ if requested == "guild" and ctx.identity.guild_id:
+ return Scope(ScopeLevel.GUILD, ctx.identity.guild_id)
+ return ctx.identity.default_write_scope()
diff --git a/src/lang2sql/tools/explore_schema.py b/src/lang2sql/tools/explore_schema.py
new file mode 100644
index 0000000..f5e08bb
--- /dev/null
+++ b/src/lang2sql/tools/explore_schema.py
@@ -0,0 +1,47 @@
+"""explore_schema — let the agent discover tables/columns before writing SQL.
+
+Read-only introspection through the :class:`ExplorerPort`. With no ``table``
+arg it lists tables; with one it returns full column detail.
+"""
+
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Any
+
+from ..core.types import ToolResult, ToolSpec
+
+if TYPE_CHECKING:
+ from ..harness.context import HarnessContext
+
+
+class ExploreSchema:
+ @property
+ def spec(self) -> ToolSpec:
+ return ToolSpec(
+ name="explore_schema",
+ description="List tables, or describe one table's columns. Call before writing SQL.",
+ parameters={
+ "type": "object",
+ "properties": {
+ "table": {"type": "string", "description": "table name to describe; omit to list all tables"},
+ },
+ },
+ )
+
+ async def run(self, args: dict[str, Any], ctx: "HarnessContext") -> ToolResult:
+ if ctx.explorer is None:
+ return ToolResult(call_id="", content="no DB connected (use /connect)", is_error=True)
+
+ table = (args.get("table") or "").strip()
+ if not table:
+ tables = await ctx.explorer.list_tables()
+ names = "\n".join(f"- {t.qualified}" for t in tables) or "(no tables)"
+ return ToolResult(call_id="", content="Tables:\n" + names)
+
+ t = await ctx.explorer.describe_table(table)
+ cols = "\n".join(
+ f"- {c.name}: {c.type}{'' if c.nullable else ' NOT NULL'}"
+ f"{(' — ' + c.description) if c.description else ''}"
+ for c in t.columns
+ ) or "(no columns)"
+ return ToolResult(call_id="", content=f"{t.qualified}\n{cols}")
diff --git a/src/lang2sql/tools/ingest_doc.py b/src/lang2sql/tools/ingest_doc.py
new file mode 100644
index 0000000..e5118a6
--- /dev/null
+++ b/src/lang2sql/tools/ingest_doc.py
@@ -0,0 +1,64 @@
+"""ingest_doc — turn an uploaded document into semantic candidates (★③).
+
+Runs the Source × Extractor pipeline and returns the proposed metric/rule
+definitions for the user to confirm. V1 does NOT auto-register — confirmation
+is a frontend step (Discord buttons in Week 4); this tool surfaces the
+candidates so the human stays in the loop (documents are the source of truth).
+"""
+
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Any
+
+from ..core.ports.ingestion import DocExtractorPort, SourcePort
+from ..core.types import ToolResult, ToolSpec
+from ..ingestion.pipeline import IngestionPipeline
+
+if TYPE_CHECKING:
+ from ..harness.context import HarnessContext
+
+
+class IngestDoc:
+ def __init__(
+ self,
+ pipeline: IngestionPipeline,
+ source: SourcePort,
+ extractor: DocExtractorPort,
+ ) -> None:
+ self._pipeline = pipeline
+ self._source = source
+ self._extractor = extractor
+
+ @property
+ def spec(self) -> ToolSpec:
+ return ToolSpec(
+ name="ingest_doc",
+ description=(
+ "Read a document and propose metric/dimension/rule definitions "
+ "for the user to confirm before they enter the semantic layer."
+ ),
+ parameters={
+ "type": "object",
+ "properties": {
+ "ref": {"type": "string", "description": "document path or identifier"},
+ "content": {"type": "string", "description": "inline document text (alternative to ref)"},
+ },
+ },
+ )
+
+ async def run(self, args: dict[str, Any], ctx: "HarnessContext") -> ToolResult:
+ ref = (args.get("ref") or "inline").strip()
+ content = args.get("content")
+ blob = content.encode("utf-8") if isinstance(content, str) else None
+ if not content and ref == "inline":
+ return ToolResult(call_id="", content="provide a document 'ref' or inline 'content'", is_error=True)
+
+ candidates = await self._pipeline.ingest(self._source, self._extractor, ref, blob)
+ if not candidates:
+ return ToolResult(call_id="", content="No definitions found in the document.")
+
+ lines = ["Proposed definitions (confirm to register):"]
+ for c in candidates:
+ applies = f" [{c.applies_to}]" if c.applies_to else ""
+ lines.append(f"- {c.kind.value.upper()} {c.name}{applies} → {c.definition}")
+ return ToolResult(call_id="", content="\n".join(lines))
diff --git a/src/lang2sql/tools/ping.py b/src/lang2sql/tools/ping.py
new file mode 100644
index 0000000..3ded19c
--- /dev/null
+++ b/src/lang2sql/tools/ping.py
@@ -0,0 +1,34 @@
+"""ping — a trivial ctx-aware tool used to validate the tool dispatch path.
+
+Stands in for real tools (run_sql, explore_schema, …) in Week 1: it proves the
+ToolPort contract, the registry dispatch, and the loop's tool-result handling
+all line up. Removed once the real tools land.
+"""
+
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Any
+
+from ..core.types import ToolResult, ToolSpec
+
+if TYPE_CHECKING:
+ from ..harness.context import HarnessContext
+
+
+class Ping:
+ @property
+ def spec(self) -> ToolSpec:
+ return ToolSpec(
+ name="ping",
+ description="Health-check tool. Echoes back a message.",
+ parameters={
+ "type": "object",
+ "properties": {
+ "message": {"type": "string", "description": "text to echo"}
+ },
+ },
+ )
+
+ async def run(self, args: dict[str, Any], ctx: "HarnessContext") -> ToolResult:
+ msg = args.get("message", "")
+ return ToolResult(call_id="", content=f"pong: {msg!r} (user={ctx.identity.user_id})")
diff --git a/src/lang2sql/tools/remember.py b/src/lang2sql/tools/remember.py
new file mode 100644
index 0000000..c48622f
--- /dev/null
+++ b/src/lang2sql/tools/remember.py
@@ -0,0 +1,47 @@
+"""remember — persist a user fact via the memory service (★②).
+
+V1 is the manual ``/remember`` path: the model (or a slash command) records a
+fact verbatim. Recall/extraction strategies evolve behind MemoryService without
+this tool changing. The service is injected at assembly time.
+"""
+
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Any
+
+from ..core.ports.audit import AuditEvent
+from ..core.types import ToolResult, ToolSpec
+from ..memory.service import MemoryService
+
+if TYPE_CHECKING:
+ from ..harness.context import HarnessContext
+
+
+class Remember:
+ def __init__(self, memory: MemoryService) -> None:
+ self._memory = memory
+
+ @property
+ def spec(self) -> ToolSpec:
+ return ToolSpec(
+ name="remember",
+ description="Persist a fact about the user/data so it informs future turns.",
+ parameters={
+ "type": "object",
+ "properties": {"text": {"type": "string"}},
+ "required": ["text"],
+ },
+ )
+
+ async def run(self, args: dict[str, Any], ctx: "HarnessContext") -> ToolResult:
+ text = (args.get("text") or "").strip()
+ if not text:
+ return ToolResult(call_id="", content="nothing to remember", is_error=True)
+
+ fact = await self._memory.remember(ctx.identity.user_id, text)
+ if ctx.audit is not None:
+ await ctx.audit.record(
+ AuditEvent(actor=ctx.identity.user_id, action="remember",
+ scope=ctx.identity.session_key(), detail={"fact_id": fact.id})
+ )
+ return ToolResult(call_id="", content=f"🧠 Remembered: {text}")
diff --git a/src/lang2sql/tools/run_sql.py b/src/lang2sql/tools/run_sql.py
new file mode 100644
index 0000000..567f265
--- /dev/null
+++ b/src/lang2sql/tools/run_sql.py
@@ -0,0 +1,77 @@
+"""run_sql — execute a read-only query, but only after the safety gate (★①).
+
+The tool never touches the DB directly: it pushes the model's SQL through the
+:class:`SafetyPipelinePort` first. BLOCK/CONFIRM short-circuit with an
+explanation the model can act on; PASS/REWRITE proceed to
+``explorer.execute`` and the (possibly rewritten) SQL is what runs.
+"""
+
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Any
+
+from ..core.ports.audit import AuditEvent
+from ..core.ports.safety import SafetyContext, Verdict
+from ..core.types import ToolResult, ToolSpec
+
+if TYPE_CHECKING:
+ from ..harness.context import HarnessContext
+
+
+class RunSQL:
+ @property
+ def spec(self) -> ToolSpec:
+ return ToolSpec(
+ name="run_sql",
+ description=(
+ "Run a single read-only SQL query (SELECT/WITH only) and return "
+ "rows. Queries are checked by a safety gate before execution."
+ ),
+ parameters={
+ "type": "object",
+ "properties": {
+ "sql": {"type": "string", "description": "a single SELECT or WITH query"},
+ "limit": {"type": "integer", "description": "max rows (default 1000)"},
+ },
+ "required": ["sql"],
+ },
+ )
+
+ async def run(self, args: dict[str, Any], ctx: "HarnessContext") -> ToolResult:
+ sql = (args.get("sql") or "").strip()
+ try:
+ limit = int(args.get("limit") or 1000)
+ except (TypeError, ValueError):
+ limit = 1000 # tolerate a malformed limit from the model
+
+ if ctx.safety is None:
+ return ToolResult(call_id="", content="run_sql unavailable: no safety pipeline wired", is_error=True)
+ if ctx.explorer is None:
+ return ToolResult(call_id="", content="run_sql unavailable: no DB connected (use /connect)", is_error=True)
+
+ decision = ctx.safety.evaluate(sql, SafetyContext(row_limit=limit))
+ if decision.verdict == Verdict.BLOCK:
+ return ToolResult(call_id="", content=f"BLOCKED by {decision.layer}: {decision.reason}", is_error=True)
+ if decision.verdict == Verdict.CONFIRM:
+ return ToolResult(call_id="", content=f"NEEDS CONFIRMATION: {decision.confirm_prompt}")
+
+ rows = await ctx.explorer.execute(decision.sql, limit)
+
+ if ctx.audit is not None:
+ await ctx.audit.record(
+ AuditEvent(actor=ctx.identity.user_id, action="run_sql",
+ scope=ctx.identity.session_key(), detail={"sql": decision.sql})
+ )
+
+ return ToolResult(call_id="", content=_render_rows(decision.sql, rows))
+
+
+def _render_rows(sql: str, rows: list[dict]) -> str:
+ if not rows:
+ return f"(0 rows)\nSQL: {sql}"
+ headers = list(rows[0].keys())
+ lines = [" | ".join(headers), " | ".join("---" for _ in headers)]
+ for r in rows[:50]:
+ lines.append(" | ".join(str(r.get(h, "")) for h in headers))
+ suffix = f"\n… ({len(rows)} rows total)" if len(rows) > 50 else ""
+ return f"{len(rows)} row(s):\n" + "\n".join(lines) + suffix + f"\nSQL: {sql}"
diff --git a/test/test_llm_utils/test_llm_response_parser.py b/test/test_llm_utils/test_llm_response_parser.py
deleted file mode 100644
index ed10acd..0000000
--- a/test/test_llm_utils/test_llm_response_parser.py
+++ /dev/null
@@ -1,107 +0,0 @@
-"""
-LLMResponseParser 클래스의 기능을 테스트하는 단위 테스트 모듈입니다.
-
-주요 테스트 항목:
-- 블록에서 SQL 쿼리 추출 성공/실패
-- <해석> 블록에서 자연어 설명 추출 성공/실패
-- 다양한 입력 형식(들여쓰기, 공백 등)에 대한 정규식 대응 여부 확인
-"""
-
-import unittest
-
-from utils.llm.llm_response_parser import LLMResponseParser
-
-
-class TestLLMResponseParser(unittest.TestCase):
- """
- LLMResponseParser 클래스의 정적 메서드 동작을 검증하는 테스트 케이스입니다.
-
- 각 테스트는 SQL 및 해석 블록 추출 기능이 정상적으로 작동하는지,
- 예외 상황에 올바르게 대응하는지를 검증합니다.
- """
-
- def test_extract_sql_success(self):
- """
- 블록과 ```sql``` 코드 블록이 정상적으로 포함된 문자열에서
- SQL 쿼리가 정확히 추출되는지 확인합니다.
- """
-
- text = """
-
- ```sql
- SELECT * FROM users;
- ````
-
- <해석>
-
- ```plaintext
- 사용자 테이블의 모든 데이터를 조회합니다.
- ```
-
- """
- expected_sql = "SELECT * FROM users;"
- result = LLMResponseParser.extract_sql(text)
- self.assertEqual(result, expected_sql)
-
- def test_extract_sql_missing(self):
- """
- 블록은 존재하지만 코드 블록이 없을 경우,
- ValueError 예외가 발생하는지 확인합니다.
- """
-
- text = " no code block here"
- with self.assertRaises(ValueError):
- LLMResponseParser.extract_sql(text)
-
- def test_extract_interpretation_success(self):
- """
- <해석> 블록과 ```plaintext``` 코드 블록이 포함된 문자열에서
- 해석 텍스트가 정상적으로 추출되는지 확인합니다.
- """
-
- text = """
- ```
-
-
- ```sql
- SELECT * FROM users;
- ```
- <해석>
- ```plaintext
- 사용자 테이블의 모든 데이터를 조회합니다.
- ```
- """
- expected = "사용자 테이블의 모든 데이터를 조회합니다."
- result = LLMResponseParser.extract_interpretation(text)
- self.assertEqual(result, expected)
-
- def test_extract_interpretation_empty(self):
- """
- <해석> 태그는 존재하지만 코드 블록이 없는 경우,
- 빈 문자열을 반환하는지 확인합니다.
- """
-
- text = "<해석> 블록이 없습니다."
- result = LLMResponseParser.extract_interpretation(text)
- self.assertEqual(result, "")
-
- def test_extract_sql_with_leading_whitespace(self):
- """
- 블록이 들여쓰기되어 있는 경우에도 SQL 쿼리를 정확히 추출하는지 확인합니다.
- """
-
- text = """
- ```
-
-
- ```sql
- SELECT id FROM orders;
- ```
- <해석>
- ```plaintext
- 주문 테이블에서 ID 조회
- ```
- """
- expected = "SELECT id FROM orders;"
- result = LLMResponseParser.extract_sql(text)
- self.assertEqual(result, expected.strip())
diff --git a/tests/test_adapters.py b/tests/test_adapters.py
new file mode 100644
index 0000000..33e3096
--- /dev/null
+++ b/tests/test_adapters.py
@@ -0,0 +1,134 @@
+"""Adapter tests — SqliteStore round trips, OpenAILLM offline behaviour, stub.
+
+No network: OpenAI is only checked for construction + the no-key RuntimeError.
+Async port methods are driven with ``asyncio.run``.
+"""
+
+from __future__ import annotations
+
+import asyncio
+import os
+
+from lang2sql.adapters.db.postgres_explorer import PostgresExplorer
+from lang2sql.adapters.llm.openai_ import OpenAILLM
+from lang2sql.adapters.storage.sqlite_store import SqliteStore
+from lang2sql.core.identity import Identity
+from lang2sql.core.ports.audit import AuditEvent
+from lang2sql.core.ports.explorer import ExplorerPort
+from lang2sql.core.types import Message, Role, ToolCall
+from lang2sql.harness.session import Session
+
+
+def test_audit_record_then_query() -> None:
+ store = SqliteStore()
+ asyncio.run(store.record(AuditEvent(actor="u1", action="run_sql", scope="s", detail={"q": "SELECT 1"})))
+ asyncio.run(store.record(AuditEvent(actor="u1", action="define_metric", scope="s", detail={})))
+ asyncio.run(store.record(AuditEvent(actor="other", action="run_sql", scope="s")))
+
+ events = asyncio.run(store.query("u1"))
+ assert len(events) == 2
+ # Newest first.
+ assert events[0].action == "define_metric"
+ assert events[1].action == "run_sql"
+ assert events[1].detail == {"q": "SELECT 1"}
+ assert events[0].ts > 0 # store fills the timestamp
+
+
+def test_session_save_then_load_reconstructs_transcript() -> None:
+ store = SqliteStore()
+ identity = Identity(user_id="u1", guild_id="g", channel_id="c", thread_id="t", is_admin=True)
+ session = Session(identity=identity)
+ session.add(Message(role=Role.USER, content="hi"))
+ session.add(
+ Message(
+ role=Role.ASSISTANT,
+ content="",
+ tool_calls=[ToolCall(id="call_1", name="run_sql", arguments={"q": "SELECT 1"})],
+ )
+ )
+ session.add(Message(role=Role.TOOL, content="ok", tool_call_id="call_1", name="run_sql"))
+
+ key = identity.session_key()
+ asyncio.run(store.save(key, session))
+ loaded = asyncio.run(store.load(key))
+
+ assert loaded is not None
+ assert loaded.identity == identity
+ assert len(loaded.transcript) == 3
+ assert loaded.transcript[0].role == Role.USER
+ tc = loaded.transcript[1].tool_calls[0]
+ assert tc.id == "call_1"
+ assert tc.name == "run_sql"
+ assert tc.arguments == {"q": "SELECT 1"}
+ assert loaded.transcript[2].tool_call_id == "call_1"
+ assert loaded.transcript[2].name == "run_sql"
+
+
+def test_session_load_missing_returns_none() -> None:
+ store = SqliteStore()
+ assert asyncio.run(store.load("nope")) is None
+
+
+def test_kv_set_get_delete() -> None:
+ store = SqliteStore()
+ assert store.kv_get("scope", "k") is None
+ store.kv_set("scope", "k", "v1")
+ assert store.kv_get("scope", "k") == "v1"
+ store.kv_set("scope", "k", "v2") # upsert
+ assert store.kv_get("scope", "k") == "v2"
+ store.kv_delete("scope", "k")
+ assert store.kv_get("scope", "k") is None
+
+
+def test_postgres_explorer_canned_data() -> None:
+ explorer = PostgresExplorer("postgresql://ignored")
+ tables = asyncio.run(explorer.list_tables())
+ names = {t.qualified for t in tables}
+ assert "public.orders" in names
+ assert "public.users" in names
+
+ orders = asyncio.run(explorer.describe_table("orders"))
+ assert {c.name for c in orders.columns} == {"id", "amount", "status", "created_at"}
+
+ rows = asyncio.run(explorer.sample_rows("public.orders", limit=1))
+ assert len(rows) == 1
+ assert "status" in rows[0]
+
+
+def test_postgres_explorer_satisfies_protocol() -> None:
+ explorer = PostgresExplorer("postgresql://ignored")
+ assert isinstance(explorer, ExplorerPort)
+
+
+def test_postgres_explorer_execute() -> None:
+ explorer = PostgresExplorer("postgresql://ignored")
+ order_rows = asyncio.run(explorer.execute("SELECT * FROM orders WHERE status='paid'"))
+ assert order_rows and "amount" in order_rows[0]
+
+ capped = asyncio.run(explorer.execute("select * from orders", limit=1))
+ assert len(capped) == 1
+
+ generic = asyncio.run(explorer.execute("SELECT now()"))
+ assert generic == [{"result": 1}]
+
+
+def test_openai_constructs_offline() -> None:
+ # No network, no key required to construct.
+ llm = OpenAILLM(model="gpt-4.1-mini", api_key=None, base_url="http://localhost:0")
+ assert llm.model == "gpt-4.1-mini"
+
+
+def test_openai_complete_without_key_raises() -> None:
+ saved = os.environ.pop("OPENAI_API_KEY", None)
+ try:
+ llm = OpenAILLM(api_key=None)
+ raised = False
+ try:
+ asyncio.run(llm.complete([Message(role=Role.USER, content="hi")]))
+ except RuntimeError as exc:
+ raised = True
+ assert "OPENAI_API_KEY not set" in str(exc)
+ assert raised
+ finally:
+ if saved is not None:
+ os.environ["OPENAI_API_KEY"] = saved
diff --git a/tests/test_bench_demo.py b/tests/test_bench_demo.py
new file mode 100644
index 0000000..9a2e625
--- /dev/null
+++ b/tests/test_bench_demo.py
@@ -0,0 +1,61 @@
+"""Smoke test: the bench demo runs clean and shows its headline behaviours.
+
+Guards the study-group demo against drift in the modules it exercises
+(ContextConcierge, ScopeResolver, SafetyPipeline). Runs the demo's ``main()``
+in-process and asserts the federation + safety claims it prints are real.
+"""
+
+from __future__ import annotations
+
+import asyncio
+import importlib.util
+from pathlib import Path
+
+import pytest
+
+_DEMO = Path(__file__).resolve().parent.parent / "bench" / "ecommerce_demo.py"
+
+
+def _load_demo():
+ spec = importlib.util.spec_from_file_location("ecommerce_demo", _DEMO)
+ module = importlib.util.module_from_spec(spec)
+ assert spec.loader is not None
+ spec.loader.exec_module(module)
+ return module
+
+
+def test_demo_runs_clean(capsys):
+ demo = _load_demo()
+ asyncio.run(demo.main())
+ out = capsys.readouterr().out
+
+ # ★④ federation: same term, two definitions, no conflict.
+ assert "user with a login event in the last 30 days" in out
+ assert "user with an active paid subscription" in out
+ assert "zero conflict" in out
+
+ # ★① safety: a mutating statement is blocked, a SELECT passes.
+ assert "BLOCK" in out
+ assert "PASS" in out
+
+
+def test_demo_federation_resolves_distinct_definitions():
+ """Reach into the demo's building blocks directly (no printing)."""
+ demo = _load_demo()
+ from lang2sql.semantic.types import Metric
+ from lang2sql.tenancy.scope_resolver import ScopeResolver
+
+ async def _run():
+ resolver = ScopeResolver()
+ mkt = demo._marketing_identity()
+ fin = demo._finance_identity()
+ await resolver.define(mkt.default_write_scope(), Metric("active_user", "30d login"))
+ await resolver.define(fin.default_write_scope(), Metric("active_user", "paid sub"))
+ mkt_layer = await resolver.effective_layer(mkt)
+ fin_layer = await resolver.effective_layer(fin)
+ return mkt_layer.lookup("active_user").definition, fin_layer.lookup("active_user").definition
+
+ mkt_def, fin_def = asyncio.run(_run())
+ assert mkt_def == "30d login"
+ assert fin_def == "paid sub"
+ assert mkt_def != fin_def
diff --git a/tests/test_components_context_enricher.py b/tests/test_components_context_enricher.py
deleted file mode 100644
index 20fd83c..0000000
--- a/tests/test_components_context_enricher.py
+++ /dev/null
@@ -1,80 +0,0 @@
-"""Tests for ContextEnricher component."""
-
-from __future__ import annotations
-
-import pytest
-
-from lang2sql.components.enrichment.context_enricher import ContextEnricher
-from lang2sql.core.catalog import CatalogEntry, QuestionProfile
-from lang2sql.core.hooks import MemoryHook
-
-
-class FakeLLM:
- def __init__(self, response: str = "지난달(2024-03) 주문 건수를 COUNT하는 쿼리"):
- self._response = response
-
- def invoke(self, messages: list[dict]) -> str:
- return self._response
-
-
-def _catalog() -> list[CatalogEntry]:
- return [
- {
- "name": "orders",
- "description": "주문 테이블",
- "columns": {
- "order_id": "주문 ID",
- "amount": "주문 금액",
- "created_at": "생성일",
- },
- }
- ]
-
-
-def _profile(is_aggregation: bool = True) -> QuestionProfile:
- return QuestionProfile(
- is_aggregation=is_aggregation,
- has_filter=True,
- intent_type="lookup",
- )
-
-
-def test_context_enricher_returns_string():
- llm = FakeLLM("enriched question text")
- enricher = ContextEnricher(llm=llm)
- result = enricher.run("주문 수", _catalog(), _profile())
- assert isinstance(result, str)
- assert result == "enriched question text"
-
-
-def test_context_enricher_trims_whitespace():
- llm = FakeLLM(" enriched ")
- enricher = ContextEnricher(llm=llm)
- result = enricher.run("test", _catalog(), _profile())
- assert result == "enriched"
-
-
-def test_context_enricher_emits_hook_events():
- hook = MemoryHook()
- llm = FakeLLM("enriched")
- enricher = ContextEnricher(llm=llm, hook=hook)
- enricher.run("test", _catalog(), _profile())
- phases = [e.phase for e in hook.events]
- assert "start" in phases
- assert "end" in phases
-
-
-def test_context_enricher_passes_profile_to_llm():
- received_messages = []
-
- class CaptureLLM:
- def invoke(self, messages: list[dict]) -> str:
- received_messages.extend(messages)
- return "ok"
-
- profiler = QuestionProfile(is_timeseries=True, intent_type="trend")
- enricher = ContextEnricher(llm=CaptureLLM())
- enricher.run("월별 추이", _catalog(), profiler)
- assert received_messages
- # profile JSON should appear in the message content
- assert "is_timeseries" in received_messages[0]["content"]
diff --git a/tests/test_components_hybrid_retriever.py b/tests/test_components_hybrid_retriever.py
deleted file mode 100644
index 5fb7f9f..0000000
--- a/tests/test_components_hybrid_retriever.py
+++ /dev/null
@@ -1,282 +0,0 @@
-"""
-Tests for HybridRetriever and HybridNL2SQL — 8 cases.
-
-SmartFakeEmbedding maps marker keywords in text to orthogonal unit vectors:
- - "bothdim" → [0.7071, 0.0, 0.7071, 0.0] (found by both BM25 and vector)
- - "kwcommon" → [0.0, 1.0, 0.0, 0.0] (positive BM25, zero cosine with query)
- - "vdimonly" → [0.0, 0.0, 1.0, 0.0] (zero BM25, positive cosine with query)
- - other → [0.0, 0.0, 0.0, 1.0]
-
-Query "BOTHDIM KWCOMMON" embeds to [0.7071, 0, 0.7071, 0] (has "bothdim").
- - both_table (bothdim): cosine 1.0 → in vector ✓ ; BM25 matches "bothdim"+"kwcommon" ✓
- - kwonly_table(kwcommon): cosine 0.0 → NOT in vector ✓ ; BM25 matches "kwcommon" ✓
- - veconly_table(vdimonly): cosine 0.707 → in vector ✓ ; BM25 score 0 → not returned ✓
-"""
-
-from __future__ import annotations
-
-import pytest
-
-from lang2sql.components.retrieval.hybrid import HybridRetriever
-from lang2sql.core.catalog import RetrievalResult
-from lang2sql.core.hooks import MemoryHook
-from lang2sql.flows.hybrid import HybridNL2SQL
-
-# ---------------------------------------------------------------------------
-# Fakes
-# ---------------------------------------------------------------------------
-
-
-class FakeEmbedding:
- """Uniform embedding — all texts and queries map to the same unit vector."""
-
- def embed_query(self, text: str) -> list[float]:
- return [0.5, 0.5, 0.5, 0.5]
-
- def embed_texts(self, texts: list[str]) -> list[list[float]]:
- return [[0.5, 0.5, 0.5, 0.5]] * len(texts)
-
-
-class SmartFakeEmbedding:
- """
- Marker-based deterministic embedding for controlled retrieval testing.
-
- Marker priority (first match wins):
- "bothdim" → [√½, 0, √½, 0] — high cosine with query on dims 0 and 2
- "vdimonly" → [0, 0, 1, 0] — matches query dim 2 only
- "kwcommon" → [0, 1, 0, 0] — orthogonal to query → cosine 0.0 (excluded)
- else → [0, 0, 0, 1] — orthogonal to query → cosine 0.0 (excluded)
-
- Query "BOTHDIM KWCOMMON" has "bothdim" → embeds to [√½, 0, √½, 0].
- """
-
- _SQRT2_INV = 2.0**-0.5 # ≈ 0.7071
-
- def _embed(self, text: str) -> list[float]:
- t = text.lower()
- if "bothdim" in t:
- return [self._SQRT2_INV, 0.0, self._SQRT2_INV, 0.0]
- if "vdimonly" in t:
- return [0.0, 0.0, 1.0, 0.0]
- if "kwcommon" in t:
- return [0.0, 1.0, 0.0, 0.0]
- return [0.0, 0.0, 0.0, 1.0]
-
- def embed_query(self, text: str) -> list[float]:
- return self._embed(text)
-
- def embed_texts(self, texts: list[str]) -> list[list[float]]:
- return [self._embed(t) for t in texts]
-
-
-class FakeLLM:
- def __init__(self, response: str = "```sql\nSELECT COUNT(*) FROM orders\n```"):
- self._response = response
-
- def invoke(self, messages: list[dict]) -> str:
- return self._response
-
-
-class FakeDB:
- def __init__(self, rows: list[dict] | None = None):
- self._rows = rows if rows is not None else [{"count": 1}]
-
- def execute(self, sql: str) -> list[dict]:
- return self._rows
-
-
-# ---------------------------------------------------------------------------
-# Fixtures
-# ---------------------------------------------------------------------------
-
-CATALOG_SIMPLE = [
- {"name": "orders", "description": "order records", "columns": {"id": "pk"}},
- {"name": "customers", "description": "customer data", "columns": {"id": "pk"}},
-]
-
-# Three-table catalog designed for deterministic BM25 vs vector split:
-# - both_table: found by BOTH BM25 ("bothdim"+"kwcommon" in description) AND vector (cosine 1.0)
-# - kwonly_table: found by BM25 only ("kwcommon" matches query token) → vector cosine 0.0
-# - veconly_table:found by vector only (vdimonly → cosine 0.707) → BM25 score 0
-CATALOG_HYBRID = [
- {
- "name": "both_table",
- "description": "BOTHDIM KWCOMMON data",
- "columns": {"id": "pk"},
- },
- {
- "name": "kwonly_table",
- "description": "KWCOMMON unique info",
- "columns": {"id": "pk"},
- },
- {
- "name": "veconly_table",
- "description": "VDIMONLY specific",
- "columns": {"id": "pk"},
- },
-]
-
-DOCS = [
- {
- "id": "revenue_doc",
- "title": "Revenue Rules",
- "content": "Revenue is net sales minus returns.",
- "source": "docs/revenue.md",
- }
-]
-
-# Query that:
-# - BM25-matches "both_table" (has "bothdim" and "kwcommon") and "kwonly_table" (has "kwcommon")
-# - Embeds to [√½, 0, √½, 0] via SmartFakeEmbedding (triggered by "bothdim")
-# - cosine("both_table") = 1.0, cosine("kwonly_table") = 0.0, cosine("veconly_table") = 0.707
-HYBRID_QUERY = "BOTHDIM KWCOMMON"
-
-
-# ---------------------------------------------------------------------------
-# 1. Return type is RetrievalResult
-# ---------------------------------------------------------------------------
-
-
-def test_hybrid_returns_retrieval_result():
- retriever = HybridRetriever(
- catalog=CATALOG_SIMPLE,
- embedding=FakeEmbedding(),
- )
- result = retriever("orders")
- assert isinstance(result, RetrievalResult)
- assert isinstance(result.schemas, list)
- assert isinstance(result.context, list)
-
-
-# ---------------------------------------------------------------------------
-# 2. RRF combines both retrievers — all three match types present
-# ---------------------------------------------------------------------------
-
-
-def test_hybrid_rrf_combines_both_retrievers():
- """keyword-only, vector-only, and both-matched tables must all appear in results."""
- retriever = HybridRetriever(
- catalog=CATALOG_HYBRID,
- embedding=SmartFakeEmbedding(),
- top_n=3,
- )
- result = retriever(HYBRID_QUERY)
- names = {s["name"] for s in result.schemas}
-
- assert "both_table" in names, "both_table (found by both) must be in results"
- assert "kwonly_table" in names, "kwonly_table (BM25-only) must be in results"
- assert "veconly_table" in names, "veconly_table (vector-only) must be in results"
-
-
-# ---------------------------------------------------------------------------
-# 3. Tables found by both retrievers rank higher than single-retriever tables
-# ---------------------------------------------------------------------------
-
-
-def test_hybrid_rrf_ranks_overlap_higher():
- """A table found by both retrievers must rank higher than any single-retriever table."""
- retriever = HybridRetriever(
- catalog=CATALOG_HYBRID,
- embedding=SmartFakeEmbedding(),
- top_n=3,
- )
- result = retriever(HYBRID_QUERY)
- names = [s["name"] for s in result.schemas]
-
- assert names[0] == "both_table", "both_table (highest RRF score) must be rank 1"
- assert names.index("both_table") < names.index("kwonly_table")
- assert names.index("both_table") < names.index("veconly_table")
-
-
-# ---------------------------------------------------------------------------
-# 4. top_n limits schemas count
-# ---------------------------------------------------------------------------
-
-
-def test_hybrid_top_n_limits_schemas():
- retriever = HybridRetriever(
- catalog=CATALOG_HYBRID,
- embedding=SmartFakeEmbedding(),
- top_n=2,
- )
- result = retriever(HYBRID_QUERY)
- assert len(result.schemas) <= 2
-
-
-# ---------------------------------------------------------------------------
-# 5. context comes only from VectorRetriever
-# ---------------------------------------------------------------------------
-
-
-def test_hybrid_context_from_vector():
- """Context must come from VectorRetriever only (document chunk text)."""
- retriever = HybridRetriever(
- catalog=CATALOG_SIMPLE,
- embedding=FakeEmbedding(),
- documents=DOCS,
- top_n=5,
- )
- result = retriever("revenue rules")
-
- assert isinstance(result.context, list)
- assert len(result.context) > 0, "document text must appear in context"
- assert any("Revenue" in c for c in result.context)
-
-
-# ---------------------------------------------------------------------------
-# 6. Hook records start/end events for HybridRetriever itself
-# ---------------------------------------------------------------------------
-
-
-def test_hybrid_hook_events():
- hook = MemoryHook()
- retriever = HybridRetriever(
- catalog=CATALOG_SIMPLE,
- embedding=FakeEmbedding(),
- hook=hook,
- )
- retriever("orders")
-
- hybrid_events = [e for e in hook.snapshot() if e.component == "HybridRetriever"]
- assert any(e.phase == "start" for e in hybrid_events)
- assert any(e.phase == "end" for e in hybrid_events)
- end_event = next(e for e in hybrid_events if e.phase == "end")
- assert end_event.duration_ms is not None
- assert end_event.duration_ms >= 0.0
-
-
-# ---------------------------------------------------------------------------
-# 7. HybridNL2SQL end-to-end pipeline
-# ---------------------------------------------------------------------------
-
-
-def test_hybrid_nl2sql_pipeline():
- """HybridNL2SQL end-to-end with FakeLLM + FakeDB."""
- rows = [{"count": 7}]
- pipeline = HybridNL2SQL(
- catalog=CATALOG_SIMPLE,
- llm=FakeLLM(),
- db=FakeDB(rows),
- embedding=FakeEmbedding(),
- )
- result = pipeline.run("How many orders last month?")
- assert result == rows
-
-
-# ---------------------------------------------------------------------------
-# 8. _rrf_merge deduplication — same table in both → score combined, no duplicate
-# ---------------------------------------------------------------------------
-
-
-def test_rrf_merge_deduplication():
- """Same table in both retrievers → scores combined, no duplicate in results."""
- retriever = HybridRetriever(
- catalog=CATALOG_SIMPLE,
- embedding=FakeEmbedding(),
- )
- entry = {"name": "orders", "description": "order data", "columns": {}}
-
- merged = retriever._rrf_merge([entry], [entry])
-
- assert len(merged) == 1, "duplicate table must appear only once"
- assert merged[0]["name"] == "orders"
diff --git a/tests/test_components_keyword_retriever.py b/tests/test_components_keyword_retriever.py
deleted file mode 100644
index 2af2f3c..0000000
--- a/tests/test_components_keyword_retriever.py
+++ /dev/null
@@ -1,215 +0,0 @@
-"""
-Tests for KeywordRetriever — 14 cases.
-
-Pattern follows test_core_base.py:
-- pytest, inline fixtures, MemoryHook
-"""
-
-import pytest
-
-from lang2sql.components.retrieval import KeywordRetriever
-from lang2sql.core.hooks import MemoryHook
-from lang2sql.flows.baseline import SequentialFlow
-
-# -------------------------
-# Shared test catalog
-# -------------------------
-
-ORDER_TABLE = {
- "name": "order_table",
- "description": "고객 주문 정보를 저장하는 테이블",
- "columns": {"order_id": "주문 고유 ID", "amount": "주문 금액"},
- "meta": {"primary_key": "order_id", "tags": ["finance", "core"]},
-}
-
-USER_TABLE = {
- "name": "user_table",
- "description": "사용자 계정 정보 테이블",
- "columns": {"user_id": "사용자 고유 ID", "email": "이메일"},
- "meta": {"primary_key": "user_id"},
-}
-
-PRODUCT_TABLE = {
- "name": "product_table",
- "description": "상품 목록 및 재고 테이블",
- "columns": {"product_id": "상품 ID", "stock": "재고 수량"},
-}
-
-CATALOG = [ORDER_TABLE, USER_TABLE, PRODUCT_TABLE]
-
-
-# -------------------------
-# Tests
-# -------------------------
-
-
-def test_basic_search_returns_relevant_table():
- """'주문' 질문 → order_table이 top 위치."""
- retriever = KeywordRetriever(catalog=CATALOG)
- results = retriever("주문 정보 조회")
-
- assert results
- assert results[0]["name"] == "order_table"
-
-
-def test_top_n_limits_results():
- """top_n=2 → 최대 2개 반환."""
- retriever = KeywordRetriever(catalog=CATALOG, top_n=2)
- results = retriever("테이블")
-
- assert len(results) <= 2
-
-
-def test_top_n_larger_than_catalog():
- """top_n=10, catalog 3개 → 최대 3개 반환."""
- retriever = KeywordRetriever(catalog=CATALOG, top_n=10)
- results = retriever("테이블")
-
- assert len(results) <= len(CATALOG)
-
-
-def test_zero_results_returns_empty_list():
- """완전히 무관한 query → []."""
- retriever = KeywordRetriever(catalog=CATALOG)
- results = retriever("xyzzy_no_match_token_12345")
-
- assert results == []
-
-
-def test_returns_list_of_dict():
- """결과가 list[dict]인지 확인."""
- retriever = KeywordRetriever(catalog=CATALOG)
- results = retriever("주문")
-
- assert isinstance(results, list)
- assert len(results) > 0
- assert isinstance(results[0], dict)
-
-
-def test_hook_start_end_events():
- """MemoryHook으로 start/end 이벤트 확인."""
- hook = MemoryHook()
- retriever = KeywordRetriever(catalog=CATALOG, hook=hook)
- retriever("주문")
-
- assert len(hook.events) == 2
- assert hook.events[0].name == "component.run"
- assert hook.events[0].phase == "start"
- assert hook.events[1].name == "component.run"
- assert hook.events[1].phase == "end"
- assert hook.events[1].duration_ms is not None
- assert hook.events[1].duration_ms >= 0.0
-
-
-def test_empty_catalog():
- """catalog=[] → []."""
- retriever = KeywordRetriever(catalog=[])
- results = retriever("주문")
-
- assert results == []
-
-
-def test_meta_preserved_in_results():
- """meta 필드가 결과 dict에 그대로 포함되는지 확인."""
- retriever = KeywordRetriever(catalog=CATALOG)
- results = retriever("주문")
-
- assert "meta" in results[0]
- assert results[0]["meta"]["primary_key"] == "order_id"
-
-
-def test_index_fields_meta():
- """index_fields=["description","meta"] → meta 텍스트도 검색에 반영."""
- # finance라는 단어는 meta.tags에만 존재 (name/description/columns에는 없음)
- catalog = [
- {
- "name": "alpha",
- "description": "일반 데이터 저장소",
- "meta": {"tags": ["finance", "core"]},
- },
- {
- "name": "beta",
- "description": "기타 로그 테이블",
- "meta": {"tags": ["logging"]},
- },
- ]
-
- retriever = KeywordRetriever(
- catalog=catalog,
- index_fields=["description", "meta"],
- )
- results = retriever("finance")
-
- assert len(results) > 0
- assert results[0]["name"] == "alpha"
-
-
-def test_result_order_by_relevance():
- """관련도 높은 테이블이 앞에 위치하는지 확인."""
- catalog = [
- {
- "name": "order_summary",
- "description": "주문 요약 주문 집계 주문 통계", # '주문' 3회
- },
- {
- "name": "user_table",
- "description": "사용자 주문 기록", # '주문' 1회
- },
- ]
-
- retriever = KeywordRetriever(catalog=catalog)
- results = retriever("주문")
-
- assert len(results) >= 2
- assert results[0]["name"] == "order_summary"
-
-
-def test_columns_text_indexed():
- """컬럼명/컬럼설명으로 검색 가능한지 확인."""
- catalog = [
- {
- "name": "sales",
- "description": "판매 데이터",
- "columns": {"revenue": "매출액", "region": "지역"},
- },
- {
- "name": "logs",
- "description": "시스템 로그",
- "columns": {"event_type": "이벤트 유형"},
- },
- ]
-
- retriever = KeywordRetriever(catalog=catalog)
- results = retriever("매출액")
-
- assert len(results) > 0
- assert results[0]["name"] == "sales"
-
-
-def test_missing_optional_fields_no_error():
- """columns/meta 없는 entry가 있어도 crash 없음."""
- catalog = [
- {"name": "minimal", "description": "최소 필드만 있는 테이블"},
- {
- "name": "full",
- "description": "전체 필드",
- "columns": {"id": "ID"},
- "meta": {},
- },
- ]
-
- retriever = KeywordRetriever(catalog=catalog)
- results = retriever("테이블")
- assert isinstance(results, list)
-
-
-def test_end_to_end_in_sequential_flow():
- """SequentialFlow(steps=[retriever]).run('...') 가 동작하는지 확인."""
- retriever = KeywordRetriever(catalog=CATALOG)
- flow = SequentialFlow(steps=[retriever])
-
- results = flow.run("주문 내역 확인")
-
- assert isinstance(results, list)
- assert len(results) > 0
- assert results[0]["name"] == "order_table"
diff --git a/tests/test_components_loaders.py b/tests/test_components_loaders.py
deleted file mode 100644
index 5b0e525..0000000
--- a/tests/test_components_loaders.py
+++ /dev/null
@@ -1,141 +0,0 @@
-"""
-Tests for MarkdownLoader, PlainTextLoader, DirectoryLoader, DocumentLoaderPort — 8 cases.
-
-Uses pytest tmp_path fixture to create temporary files in isolation.
-"""
-
-from __future__ import annotations
-
-import pytest
-
-from lang2sql.components.loaders import DirectoryLoader, MarkdownLoader, PlainTextLoader
-from lang2sql.core.ports import DocumentLoaderPort
-
-# ---------------------------------------------------------------------------
-# 1. MarkdownLoader — single file: TextDocument fields are correct
-# ---------------------------------------------------------------------------
-
-
-def test_markdown_loader_single_file(tmp_path):
- md_file = tmp_path / "revenue.md"
- md_file.write_text(
- "# Revenue Definition\n\nRevenue is net sales.", encoding="utf-8"
- )
-
- docs = MarkdownLoader().load(str(md_file))
-
- assert len(docs) == 1
- doc = docs[0]
- assert doc["id"] == "revenue"
- assert doc["title"] == "Revenue Definition"
- assert "Revenue is net sales" in doc["content"]
- assert doc["source"] == str(md_file)
-
-
-# ---------------------------------------------------------------------------
-# 2. MarkdownLoader — directory: returns one doc per .md file
-# ---------------------------------------------------------------------------
-
-
-def test_markdown_loader_directory(tmp_path):
- (tmp_path / "a.md").write_text("# A\ncontent a", encoding="utf-8")
- (tmp_path / "b.md").write_text("# B\ncontent b", encoding="utf-8")
- (tmp_path / "notes.txt").write_text("plain text", encoding="utf-8") # ignored
-
- docs = MarkdownLoader().load(str(tmp_path))
-
- assert len(docs) == 2
- ids = {d["id"] for d in docs}
- assert ids == {"a", "b"}
-
-
-# ---------------------------------------------------------------------------
-# 3. MarkdownLoader — title extracted from first # heading
-# ---------------------------------------------------------------------------
-
-
-def test_markdown_loader_title_from_heading(tmp_path):
- md_file = tmp_path / "doc.md"
- md_file.write_text("Some intro\n# My Title\nBody text.", encoding="utf-8")
-
- docs = MarkdownLoader().load(str(md_file))
-
- # The first # heading (even if not the first line) is used as title
- assert docs[0]["title"] == "My Title"
-
-
-# ---------------------------------------------------------------------------
-# 4. MarkdownLoader — no heading → title falls back to filename stem
-# ---------------------------------------------------------------------------
-
-
-def test_markdown_loader_no_heading(tmp_path):
- md_file = tmp_path / "quarterly_report.md"
- md_file.write_text("Just some content without a heading.", encoding="utf-8")
-
- docs = MarkdownLoader().load(str(md_file))
-
- assert docs[0]["title"] == "quarterly_report"
-
-
-# ---------------------------------------------------------------------------
-# 5. PlainTextLoader — single file: content is correct
-# ---------------------------------------------------------------------------
-
-
-def test_plaintext_loader_single_file(tmp_path):
- txt_file = tmp_path / "notes.txt"
- txt_file.write_text("line one\nline two\n", encoding="utf-8")
-
- docs = PlainTextLoader().load(str(txt_file))
-
- assert len(docs) == 1
- doc = docs[0]
- assert doc["id"] == "notes"
- assert doc["title"] == "notes"
- assert "line one" in doc["content"]
- assert doc["source"] == str(txt_file)
-
-
-# ---------------------------------------------------------------------------
-# 6. DirectoryLoader — dispatches by extension
-# ---------------------------------------------------------------------------
-
-
-def test_directory_loader_dispatches_by_extension(tmp_path):
- (tmp_path / "guide.md").write_text("# Guide\nMarkdown content.", encoding="utf-8")
- (tmp_path / "data.txt").write_text("plain text content", encoding="utf-8")
-
- docs = DirectoryLoader(str(tmp_path)).load()
-
- ids = {d["id"] for d in docs}
- assert "guide" in ids # loaded by MarkdownLoader
- assert "data" in ids # loaded by PlainTextLoader
-
-
-# ---------------------------------------------------------------------------
-# 7. DirectoryLoader — skips unknown extensions
-# ---------------------------------------------------------------------------
-
-
-def test_directory_loader_skips_unknown_extension(tmp_path):
- (tmp_path / "script.py").write_text("print('hello')", encoding="utf-8")
- (tmp_path / "data.csv").write_text("a,b,c\n1,2,3\n", encoding="utf-8")
- (tmp_path / "readme.md").write_text("# Readme\ncontent", encoding="utf-8")
-
- docs = DirectoryLoader(str(tmp_path)).load()
-
- ids = {d["id"] for d in docs}
- assert "readme" in ids # .md is loaded
- assert "script" not in ids # .py is skipped
- assert "data" not in ids # .csv is skipped (not in default loaders)
-
-
-# ---------------------------------------------------------------------------
-# 8. Protocol check — MarkdownLoader and PlainTextLoader satisfy DocumentLoaderPort
-# ---------------------------------------------------------------------------
-
-
-def test_document_loader_port_protocol():
- assert isinstance(MarkdownLoader(), DocumentLoaderPort)
- assert isinstance(PlainTextLoader(), DocumentLoaderPort)
diff --git a/tests/test_components_question_gate.py b/tests/test_components_question_gate.py
deleted file mode 100644
index 21097ec..0000000
--- a/tests/test_components_question_gate.py
+++ /dev/null
@@ -1,89 +0,0 @@
-"""Tests for QuestionGate component."""
-
-from __future__ import annotations
-
-import json
-
-import pytest
-
-from lang2sql.components.gate.question_gate import QuestionGate
-from lang2sql.core.catalog import GateResult
-from lang2sql.core.hooks import MemoryHook
-
-
-class FakeLLM:
- def __init__(self, response: str):
- self._response = response
-
- def invoke(self, messages: list[dict]) -> str:
- return self._response
-
-
-def _gate_json(
- suitable: bool = True,
- reason: str = "SQL로 답변 가능합니다.",
- missing_entities: list | None = None,
- requires_data_science: bool = False,
-) -> str:
- return json.dumps(
- {
- "suitable": suitable,
- "reason": reason,
- "missing_entities": missing_entities or [],
- "requires_data_science": requires_data_science,
- },
- ensure_ascii=False,
- )
-
-
-def test_question_gate_suitable_true():
- llm = FakeLLM(_gate_json(suitable=True, reason="OK"))
- gate = QuestionGate(llm=llm)
- result = gate.run("지난달 주문 수는?")
- assert isinstance(result, GateResult)
- assert result.suitable is True
- assert result.reason == "OK"
-
-
-def test_question_gate_suitable_false():
- llm = FakeLLM(
- _gate_json(
- suitable=False,
- reason="통계 분석이 필요합니다.",
- requires_data_science=True,
- )
- )
- gate = QuestionGate(llm=llm)
- result = gate.run("이상 탐지 모델을 만들어줘")
- assert result.suitable is False
- assert result.requires_data_science is True
-
-
-def test_question_gate_with_missing_entities():
- llm = FakeLLM(
- _gate_json(
- suitable=False,
- missing_entities=["기간", "대상 엔터티"],
- )
- )
- gate = QuestionGate(llm=llm)
- result = gate.run("매출을 보여줘")
- assert "기간" in result.missing_entities
-
-
-def test_question_gate_strips_markdown_json():
- raw = "```json\n" + _gate_json(suitable=True) + "\n```"
- llm = FakeLLM(raw)
- gate = QuestionGate(llm=llm)
- result = gate.run("test")
- assert result.suitable is True
-
-
-def test_question_gate_emits_hook_events():
- hook = MemoryHook()
- llm = FakeLLM(_gate_json())
- gate = QuestionGate(llm=llm, hook=hook)
- gate.run("test query")
- phases = [e.phase for e in hook.events]
- assert "start" in phases
- assert "end" in phases
diff --git a/tests/test_components_question_profiler.py b/tests/test_components_question_profiler.py
deleted file mode 100644
index deb5063..0000000
--- a/tests/test_components_question_profiler.py
+++ /dev/null
@@ -1,75 +0,0 @@
-"""Tests for QuestionProfiler component."""
-
-from __future__ import annotations
-
-import json
-
-import pytest
-
-from lang2sql.components.enrichment.question_profiler import QuestionProfiler
-from lang2sql.core.catalog import QuestionProfile
-from lang2sql.core.hooks import MemoryHook
-
-
-class FakeLLM:
- def __init__(self, response: str):
- self._response = response
-
- def invoke(self, messages: list[dict]) -> str:
- return self._response
-
-
-def _profile_json(**kwargs) -> str:
- defaults = {
- "is_timeseries": False,
- "is_aggregation": True,
- "has_filter": True,
- "is_grouped": False,
- "has_ranking": False,
- "has_temporal_comparison": False,
- "intent_type": "lookup",
- }
- defaults.update(kwargs)
- return json.dumps(defaults)
-
-
-def test_question_profiler_returns_profile():
- llm = FakeLLM(_profile_json(is_aggregation=True, has_filter=True))
- profiler = QuestionProfiler(llm=llm)
- result = profiler.run("지난달 주문 수")
- assert isinstance(result, QuestionProfile)
- assert result.is_aggregation is True
- assert result.has_filter is True
-
-
-def test_question_profiler_timeseries():
- llm = FakeLLM(_profile_json(is_timeseries=True, intent_type="trend"))
- profiler = QuestionProfiler(llm=llm)
- result = profiler.run("월별 매출 추이")
- assert result.is_timeseries is True
- assert result.intent_type == "trend"
-
-
-def test_question_profiler_invalid_intent_type_falls_back_to_lookup():
- llm = FakeLLM(_profile_json(intent_type="invalid_type"))
- profiler = QuestionProfiler(llm=llm)
- result = profiler.run("test")
- assert result.intent_type == "lookup"
-
-
-def test_question_profiler_strips_markdown_json():
- raw = "```json\n" + _profile_json() + "\n```"
- llm = FakeLLM(raw)
- profiler = QuestionProfiler(llm=llm)
- result = profiler.run("test")
- assert isinstance(result, QuestionProfile)
-
-
-def test_question_profiler_emits_hook_events():
- hook = MemoryHook()
- llm = FakeLLM(_profile_json())
- profiler = QuestionProfiler(llm=llm, hook=hook)
- profiler.run("test")
- phases = [e.phase for e in hook.events]
- assert "start" in phases
- assert "end" in phases
diff --git a/tests/test_components_sql_executor.py b/tests/test_components_sql_executor.py
deleted file mode 100644
index 1b97fb8..0000000
--- a/tests/test_components_sql_executor.py
+++ /dev/null
@@ -1,77 +0,0 @@
-"""Tests for SQLExecutor."""
-
-from __future__ import annotations
-
-import pytest
-
-from lang2sql.components.execution.sql_executor import SQLExecutor
-from lang2sql.core.exceptions import ComponentError
-from lang2sql.core.hooks import MemoryHook
-
-# ---------------------------------------------------------------------------
-# Fakes
-# ---------------------------------------------------------------------------
-
-
-class FakeDB:
- def __init__(self, rows: list[dict] | None = None):
- self._rows = rows if rows is not None else [{"count": 1}]
-
- def execute(self, sql: str) -> list[dict]:
- return self._rows
-
-
-# ---------------------------------------------------------------------------
-# Tests
-# ---------------------------------------------------------------------------
-
-
-def test_sql_executor_returns_rows():
- rows = [{"order_id": 1, "amount": 100}]
- executor = SQLExecutor(db=FakeDB(rows))
- result = executor.run("SELECT * FROM orders")
- assert result == rows
-
-
-def test_sql_executor_raises_on_empty_sql():
- executor = SQLExecutor(db=FakeDB())
- with pytest.raises(ComponentError):
- executor.run("")
-
-
-def test_sql_executor_raises_on_whitespace_sql():
- executor = SQLExecutor(db=FakeDB())
- with pytest.raises(ComponentError):
- executor.run(" ")
-
-
-def test_sql_executor_returns_empty_list():
- executor = SQLExecutor(db=FakeDB([]))
- result = executor.run("SELECT * FROM empty_table")
- assert result == []
-
-
-def test_sql_executor_emits_hook_events():
- hook = MemoryHook()
- executor = SQLExecutor(db=FakeDB(), hook=hook)
- executor.run("SELECT 1")
-
- events = hook.snapshot()
- phases = [e.phase for e in events]
- assert "start" in phases
- assert "end" in phases
-
-
-def test_sql_executor_error_event_on_db_failure():
- class BrokenDB:
- def execute(self, sql: str):
- raise RuntimeError("connection refused")
-
- hook = MemoryHook()
- executor = SQLExecutor(db=BrokenDB(), hook=hook)
-
- with pytest.raises(ComponentError):
- executor.run("SELECT 1")
-
- error_events = [e for e in hook.snapshot() if e.phase == "error"]
- assert error_events, "expected an error event"
diff --git a/tests/test_components_sql_generator.py b/tests/test_components_sql_generator.py
deleted file mode 100644
index 84990c0..0000000
--- a/tests/test_components_sql_generator.py
+++ /dev/null
@@ -1,169 +0,0 @@
-"""Tests for SQLGenerator."""
-
-from __future__ import annotations
-
-import pytest
-
-from lang2sql.components.generation.sql_generator import SQLGenerator
-from lang2sql.core.catalog import CatalogEntry
-from lang2sql.core.exceptions import ComponentError
-from lang2sql.core.hooks import MemoryHook
-
-# ---------------------------------------------------------------------------
-# Fakes
-# ---------------------------------------------------------------------------
-
-
-class FakeLLM:
- def __init__(self, response: str = "```sql\nSELECT 1\n```"):
- self._response = response
-
- def invoke(self, messages: list[dict]) -> str:
- return self._response
-
-
-# ---------------------------------------------------------------------------
-# Helpers
-# ---------------------------------------------------------------------------
-
-
-def _catalog() -> list[CatalogEntry]:
- return [
- {
- "name": "orders",
- "description": "Monthly order records",
- "columns": {"order_id": "primary key", "amount": "order amount"},
- }
- ]
-
-
-# ---------------------------------------------------------------------------
-# Tests
-# ---------------------------------------------------------------------------
-
-
-def test_sql_generator_returns_sql_string():
- gen = SQLGenerator(llm=FakeLLM("```sql\nSELECT COUNT(*) FROM orders\n```"))
- result = gen.run("주문 건수", _catalog())
- assert result == "SELECT COUNT(*) FROM orders"
-
-
-def test_sql_generator_strips_trailing_semicolon():
- gen = SQLGenerator(llm=FakeLLM("```sql\nSELECT 1;\n```"))
- result = gen.run("test", _catalog())
- assert not result.endswith(";")
-
-
-def test_sql_generator_raises_on_no_code_block():
- gen = SQLGenerator(llm=FakeLLM("Here is your answer: SELECT 1"))
- with pytest.raises(ComponentError):
- gen.run("test", _catalog())
-
-
-def test_sql_generator_passes_query_and_schema_to_llm():
- received: list[list[dict]] = []
-
- class CaptureLLM:
- def invoke(self, messages):
- received.append(messages)
- return "```sql\nSELECT 1\n```"
-
- gen = SQLGenerator(llm=CaptureLLM())
- gen.run("주문 건수", _catalog())
-
- assert received, "invoke was not called"
- msgs = received[0]
- user_content = next(m["content"] for m in msgs if m["role"] == "user")
- assert "orders" in user_content
- assert "주문 건수" in user_content
-
-
-def test_sql_generator_custom_system_prompt():
- received: list[list[dict]] = []
-
- class CaptureLLM:
- def invoke(self, messages):
- received.append(messages)
- return "```sql\nSELECT 1\n```"
-
- custom_prompt = "You are a DBA."
- gen = SQLGenerator(llm=CaptureLLM(), system_prompt=custom_prompt)
- gen.run("test", _catalog())
-
- system_content = next(m["content"] for m in received[0] if m["role"] == "system")
- assert system_content == custom_prompt
-
-
-def test_sql_generator_emits_hook_events():
- hook = MemoryHook()
- gen = SQLGenerator(llm=FakeLLM(), hook=hook)
- gen.run("test", _catalog())
-
- events = hook.snapshot()
- phases = [e.phase for e in events]
- assert "start" in phases
- assert "end" in phases
-
-
-def test_sql_generator_empty_schemas():
- gen = SQLGenerator(llm=FakeLLM("```sql\nSELECT 1\n```"))
- result = gen.run("test", [])
- assert result == "SELECT 1"
-
-
-# ---------------------------------------------------------------------------
-# db_dialect tests
-# ---------------------------------------------------------------------------
-
-
-def test_sql_generator_db_dialect_loads_sqlite_prompt():
- received: list[list[dict]] = []
-
- class CaptureLLM:
- def invoke(self, messages):
- received.append(messages)
- return "```sql\nSELECT 1\n```"
-
- gen = SQLGenerator(llm=CaptureLLM(), db_dialect="sqlite")
- gen.run("test", _catalog())
-
- system_content = next(m["content"] for m in received[0] if m["role"] == "system")
- assert "SQLite" in system_content
- assert "strftime" in system_content
-
-
-def test_sql_generator_db_dialect_loads_postgresql_prompt():
- received: list[list[dict]] = []
-
- class CaptureLLM:
- def invoke(self, messages):
- received.append(messages)
- return "```sql\nSELECT 1\n```"
-
- gen = SQLGenerator(llm=CaptureLLM(), db_dialect="postgresql")
- gen.run("test", _catalog())
-
- system_content = next(m["content"] for m in received[0] if m["role"] == "system")
- assert "PostgreSQL" in system_content
- assert "DATE_TRUNC" in system_content
-
-
-def test_sql_generator_unsupported_dialect_raises_value_error():
- with pytest.raises(ValueError, match="Unsupported dialect"):
- SQLGenerator(llm=FakeLLM(), db_dialect="oracle")
-
-
-def test_sql_generator_system_prompt_overrides_db_dialect():
- received: list[list[dict]] = []
-
- class CaptureLLM:
- def invoke(self, messages):
- received.append(messages)
- return "```sql\nSELECT 1\n```"
-
- custom = "You are a Snowflake expert."
- gen = SQLGenerator(llm=CaptureLLM(), db_dialect="sqlite", system_prompt=custom)
- gen.run("test", _catalog())
-
- system_content = next(m["content"] for m in received[0] if m["role"] == "system")
- assert system_content == custom
diff --git a/tests/test_components_table_suitability.py b/tests/test_components_table_suitability.py
deleted file mode 100644
index 0a37835..0000000
--- a/tests/test_components_table_suitability.py
+++ /dev/null
@@ -1,129 +0,0 @@
-"""Tests for TableSuitabilityEvaluator component."""
-
-from __future__ import annotations
-
-import json
-
-import pytest
-
-from lang2sql.components.gate.table_suitability import TableSuitabilityEvaluator
-from lang2sql.core.catalog import CatalogEntry
-from lang2sql.core.hooks import MemoryHook
-
-
-class FakeLLM:
- def __init__(self, response: str):
- self._response = response
-
- def invoke(self, messages: list[dict]) -> str:
- return self._response
-
-
-def _catalog() -> list[CatalogEntry]:
- return [
- {
- "name": "orders",
- "description": "주문 테이블",
- "columns": {
- "order_id": "주문 ID",
- "amount": "주문 금액",
- "created_at": "생성일",
- },
- },
- {
- "name": "users",
- "description": "사용자 테이블",
- "columns": {"user_id": "사용자 ID", "name": "이름"},
- },
- ]
-
-
-def _suitability_json(results: list[dict]) -> str:
- return json.dumps({"results": results}, ensure_ascii=False)
-
-
-def test_table_suitability_filters_below_threshold():
- resp = _suitability_json(
- [
- {
- "table_name": "orders",
- "score": 0.9,
- "reason": "핵심 지표 포함",
- "matched_columns": ["amount"],
- "missing_entities": [],
- },
- {
- "table_name": "users",
- "score": 0.1,
- "reason": "관련 없음",
- "matched_columns": [],
- "missing_entities": ["주문 정보"],
- },
- ]
- )
- evaluator = TableSuitabilityEvaluator(llm=FakeLLM(resp), threshold=0.3)
- result = evaluator.run("지난달 주문 금액 합계", _catalog())
- assert len(result) == 1
- assert result[0]["name"] == "orders"
-
-
-def test_table_suitability_sorted_by_score():
- resp = _suitability_json(
- [
- {
- "table_name": "users",
- "score": 0.5,
- "reason": "부분 매칭",
- "matched_columns": [],
- "missing_entities": [],
- },
- {
- "table_name": "orders",
- "score": 0.9,
- "reason": "완전 매칭",
- "matched_columns": ["amount"],
- "missing_entities": [],
- },
- ]
- )
- evaluator = TableSuitabilityEvaluator(llm=FakeLLM(resp), threshold=0.3)
- result = evaluator.run("주문 금액", _catalog())
- assert result[0]["name"] == "orders"
- assert result[1]["name"] == "users"
-
-
-def test_table_suitability_empty_result_when_all_below_threshold():
- resp = _suitability_json(
- [
- {
- "table_name": "orders",
- "score": 0.1,
- "reason": "낮은 관련성",
- "matched_columns": [],
- "missing_entities": [],
- },
- ]
- )
- evaluator = TableSuitabilityEvaluator(llm=FakeLLM(resp), threshold=0.3)
- result = evaluator.run("관련 없는 질문", _catalog())
- assert result == []
-
-
-def test_table_suitability_emits_hook_events():
- hook = MemoryHook()
- resp = _suitability_json(
- [
- {
- "table_name": "orders",
- "score": 0.8,
- "reason": "ok",
- "matched_columns": [],
- "missing_entities": [],
- }
- ]
- )
- evaluator = TableSuitabilityEvaluator(llm=FakeLLM(resp), hook=hook)
- evaluator.run("test", _catalog())
- phases = [e.phase for e in hook.events]
- assert "start" in phases
- assert "end" in phases
diff --git a/tests/test_components_vector_retriever.py b/tests/test_components_vector_retriever.py
deleted file mode 100644
index 07c77cd..0000000
--- a/tests/test_components_vector_retriever.py
+++ /dev/null
@@ -1,583 +0,0 @@
-"""
-Tests for VectorRetriever, CatalogChunker, RecursiveCharacterChunker — 16 cases.
-
-Mock strategy:
-- FakeVectorStore + FakeEmbedding for tests that control search results explicitly.
-- InMemoryVectorStore + FakeEmbedding for tests that verify actual storage/merge behavior
- (tests 10, from_chunks_add_incremental). FakeVectorStore.search() returns pre-configured
- results, so it cannot catch real storage bugs.
-"""
-
-import pytest
-
-from lang2sql.components.retrieval.chunker import (
- CatalogChunker,
- RecursiveCharacterChunker,
-)
-from lang2sql.components.retrieval.vector import VectorRetriever
-from lang2sql.core.catalog import RetrievalResult
-from lang2sql.core.hooks import MemoryHook
-from lang2sql.flows.baseline import SequentialFlow
-from lang2sql.integrations.vectorstore import InMemoryVectorStore
-
-# ---------------------------------------------------------------------------
-# Fakes
-# ---------------------------------------------------------------------------
-
-
-class FakeVectorStore:
- """
- Controlled search results for unit tests.
- search() returns whatever was passed to __init__(results=...).
- upsert() implements merge semantics to match InMemoryVectorStore contract.
- Do NOT use for tests that verify storage correctness — use InMemoryVectorStore instead.
- """
-
- def __init__(self, results=None):
- self._results = results or []
- self.upserted: dict = {}
-
- def search(self, vector, k):
- return self._results[:k]
-
- def upsert(self, ids, vectors):
- # merge semantics — consistent with VectorStorePort contract
- for id_, vec in zip(ids, vectors):
- self.upserted[id_] = vec
-
-
-class FakeEmbedding:
- def embed_query(self, text):
- return [0.0] * 4
-
- def embed_texts(self, texts):
- return [[0.0] * 4] * len(texts)
-
-
-# ---------------------------------------------------------------------------
-# Shared fixtures
-# ---------------------------------------------------------------------------
-
-CATALOG = [
- {
- "name": "orders",
- "description": "Order information table",
- "columns": {"order_id": "Unique order ID", "amount": "Order amount"},
- }
-]
-
-DOCS = [
- {
- "id": "biz_rules",
- "title": "Revenue Definition",
- "content": "Revenue is defined as net sales excluding returns.",
- "source": "docs/biz_rules.md",
- }
-]
-
-
-def _make_catalog_registry():
- """Registry pre-populated with one catalog chunk."""
- return {
- "orders__0": {
- "chunk_id": "orders__0",
- "text": "orders: Order information table",
- "source_type": "catalog",
- "source_id": "orders",
- "chunk_index": 0,
- "metadata": CATALOG[0],
- }
- }
-
-
-def _make_doc_registry():
- """Registry pre-populated with one document chunk."""
- return {
- "biz_rules__0": {
- "chunk_id": "biz_rules__0",
- "text": "Revenue Definition: Revenue is defined as net sales.",
- "source_type": "document",
- "source_id": "biz_rules",
- "chunk_index": 0,
- "metadata": {
- "id": "biz_rules",
- "title": "Revenue Definition",
- "source": "",
- },
- }
- }
-
-
-# ---------------------------------------------------------------------------
-# 1. Catalog chunk deduplication
-# ---------------------------------------------------------------------------
-
-
-def test_catalog_chunk_dedup():
- """Multiple chunks from the same table → only 1 CatalogEntry returned."""
- registry = {
- "orders__0": {
- "chunk_id": "orders__0",
- "text": "orders: Order table",
- "source_type": "catalog",
- "source_id": "orders",
- "chunk_index": 0,
- "metadata": CATALOG[0],
- },
- "orders__col_1": {
- "chunk_id": "orders__col_1",
- "text": "orders columns: order_id amount",
- "source_type": "catalog",
- "source_id": "orders",
- "chunk_index": 1,
- "metadata": CATALOG[0],
- },
- }
- store = FakeVectorStore(results=[("orders__0", 0.9), ("orders__col_1", 0.8)])
- retriever = VectorRetriever(
- vectorstore=store, embedding=FakeEmbedding(), registry=registry
- )
- result = retriever("order amount")
-
- assert len(result.schemas) == 1
- assert result.schemas[0]["name"] == "orders"
-
-
-# ---------------------------------------------------------------------------
-# 2. Document chunk in context
-# ---------------------------------------------------------------------------
-
-
-def test_document_chunk_in_context():
- """Document chunk appears in RetrievalResult.context."""
- registry = _make_doc_registry()
- store = FakeVectorStore(results=[("biz_rules__0", 0.8)])
- retriever = VectorRetriever(
- vectorstore=store, embedding=FakeEmbedding(), registry=registry
- )
- result = retriever("revenue definition")
-
- assert len(result.context) == 1
- assert "Revenue" in result.context[0]
-
-
-# ---------------------------------------------------------------------------
-# 3. Empty registry
-# ---------------------------------------------------------------------------
-
-
-def test_empty_registry_returns_empty_result():
- """Empty registry → empty RetrievalResult."""
- store = FakeVectorStore(results=[("orders__0", 0.9)])
- retriever = VectorRetriever(
- vectorstore=store, embedding=FakeEmbedding(), registry={}
- )
- result = retriever("any query")
-
- assert result.schemas == []
- assert result.context == []
-
-
-# ---------------------------------------------------------------------------
-# 4. Score threshold filtering
-# ---------------------------------------------------------------------------
-
-
-def test_score_threshold_filters_results():
- """Chunks at or below threshold are excluded."""
- registry = _make_catalog_registry()
- store = FakeVectorStore(results=[("orders__0", 0.3)])
- retriever = VectorRetriever(
- vectorstore=store,
- embedding=FakeEmbedding(),
- registry=registry,
- score_threshold=0.3, # score must be strictly greater than threshold
- )
- result = retriever("orders")
-
- assert result.schemas == []
-
-
-# ---------------------------------------------------------------------------
-# 5. top_n limits schemas
-# ---------------------------------------------------------------------------
-
-
-def test_top_n_limits_schemas():
- """schemas capped at top_n."""
- registry = {
- f"table_{i}__0": {
- "chunk_id": f"table_{i}__0",
- "text": f"table_{i}",
- "source_type": "catalog",
- "source_id": f"table_{i}",
- "chunk_index": 0,
- "metadata": {"name": f"table_{i}", "description": "", "columns": {}},
- }
- for i in range(10)
- }
- store = FakeVectorStore(
- results=[(f"table_{i}__0", 0.9 - i * 0.05) for i in range(10)]
- )
- retriever = VectorRetriever(
- vectorstore=store, embedding=FakeEmbedding(), registry=registry, top_n=3
- )
- result = retriever("table")
-
- assert len(result.schemas) <= 3
-
-
-# ---------------------------------------------------------------------------
-# 6. top_n limits context
-# ---------------------------------------------------------------------------
-
-
-def test_top_n_limits_context():
- """context capped at top_n."""
- registry = {
- f"doc_{i}__0": {
- "chunk_id": f"doc_{i}__0",
- "text": f"doc chunk {i}",
- "source_type": "document",
- "source_id": f"doc_{i}",
- "chunk_index": 0,
- "metadata": {"id": f"doc_{i}", "title": "", "source": ""},
- }
- for i in range(10)
- }
- store = FakeVectorStore(
- results=[(f"doc_{i}__0", 0.9 - i * 0.05) for i in range(10)]
- )
- retriever = VectorRetriever(
- vectorstore=store, embedding=FakeEmbedding(), registry=registry, top_n=3
- )
- result = retriever("doc")
-
- assert len(result.context) <= 3
-
-
-# ---------------------------------------------------------------------------
-# 7. Hook events
-# ---------------------------------------------------------------------------
-
-
-def test_hook_start_end_events():
- """MemoryHook records start/end events + duration_ms."""
- hook = MemoryHook()
- store = FakeVectorStore()
- retriever = VectorRetriever(
- vectorstore=store, embedding=FakeEmbedding(), registry={}, hook=hook
- )
- retriever("test query")
-
- assert len(hook.events) == 2
- assert hook.events[0].phase == "start"
- assert hook.events[1].phase == "end"
- assert hook.events[1].duration_ms is not None
- assert hook.events[1].duration_ms >= 0.0
-
-
-# ---------------------------------------------------------------------------
-# 8. from_chunks() — catalog chunks populate registry
-# ---------------------------------------------------------------------------
-
-
-def test_from_chunks_catalog_populates_registry():
- """from_chunks(catalog_chunks) populates registry with catalog source_type."""
- chunks = CatalogChunker().split(CATALOG)
- retriever = VectorRetriever.from_chunks(chunks, embedding=FakeEmbedding())
-
- assert len(retriever._registry) > 0
- for chunk in retriever._registry.values():
- assert chunk["source_type"] == "catalog"
- assert chunk["source_id"] == "orders"
-
-
-# ---------------------------------------------------------------------------
-# 9. from_chunks() — document chunks populate registry
-# ---------------------------------------------------------------------------
-
-
-def test_from_chunks_doc_populates_registry():
- """from_chunks(doc_chunks) populates registry with document source_type."""
- chunks = RecursiveCharacterChunker().split(DOCS)
- retriever = VectorRetriever.from_chunks(chunks, embedding=FakeEmbedding())
-
- assert len(retriever._registry) > 0
- for chunk in retriever._registry.values():
- assert chunk["source_type"] == "document"
- assert chunk["source_id"] == "biz_rules"
-
-
-# ---------------------------------------------------------------------------
-# 10. InMemoryVectorStore merge — catalog survives after doc chunks added
-# ---------------------------------------------------------------------------
-
-
-def test_from_chunks_preserves_catalog_after_doc_run():
- """
- catalog vectors survive when doc chunks are combined.
- Uses InMemoryVectorStore to verify real merge behavior.
- """
- store = InMemoryVectorStore()
- catalog_chunks = CatalogChunker().split(CATALOG)
- retriever = VectorRetriever.from_chunks(
- catalog_chunks, embedding=FakeEmbedding(), vectorstore=store
- )
- catalog_chunk_ids = set(retriever._registry.keys())
- assert len(catalog_chunk_ids) > 0
-
- doc_chunks = RecursiveCharacterChunker().split(DOCS)
- retriever.add(doc_chunks)
-
- for chunk_id in catalog_chunk_ids:
- assert chunk_id in store._store, f"catalog chunk '{chunk_id}' lost after add()"
-
-
-# ---------------------------------------------------------------------------
-# 11. CatalogChunker — column groups
-# ---------------------------------------------------------------------------
-
-
-def test_catalog_chunker_column_groups():
- """25 columns → CatalogChunker (max 20) produces at least 2 chunks beyond header."""
- entry = {
- "name": "big_table",
- "description": "Large table",
- "columns": {f"col_{i}": f"column {i}" for i in range(25)},
- }
- chunker = CatalogChunker(max_columns_per_chunk=20)
- chunks = chunker.chunk(entry)
-
- # chunk 0 = header, chunk 1 = first 20 cols, chunk 2 = remaining 5 cols
- assert len(chunks) >= 3
- assert chunks[0]["chunk_id"] == "big_table__0"
- assert all(c["source_type"] == "catalog" for c in chunks)
- assert all(c["metadata"]["name"] == "big_table" for c in chunks)
-
-
-# ---------------------------------------------------------------------------
-# 12. RecursiveCharacterChunker — respects chunk_size
-# ---------------------------------------------------------------------------
-
-
-def test_recursive_chunker_respects_chunk_size():
- """Text exceeding chunk_size is split into multiple chunks."""
- chunker = RecursiveCharacterChunker(chunk_size=50, chunk_overlap=0)
- doc = {
- "id": "doc1",
- "title": "",
- "content": "A" * 50 + "\n\n" + "B" * 50,
- "source": "",
- }
- chunks = chunker.chunk(doc)
-
- assert len(chunks) >= 2
- for chunk in chunks:
- # title prefix is empty so raw text length should respect chunk_size
- assert len(chunk["text"]) <= 50 + 10 # small tolerance for separator
-
-
-# ---------------------------------------------------------------------------
-# 13. RecursiveCharacterChunker — overlap
-# ---------------------------------------------------------------------------
-
-
-def test_recursive_chunker_overlap():
- """Second chunk contains tail characters of the first chunk."""
- overlap = 20
- chunker = RecursiveCharacterChunker(chunk_size=40, chunk_overlap=overlap)
- long_content = "Hello world this is a test. " * 10
- doc = {"id": "d1", "title": "", "content": long_content, "source": ""}
- chunks = chunker.chunk(doc)
-
- if len(chunks) >= 2:
- tail_of_first = chunks[0]["text"][-overlap:]
- assert tail_of_first in chunks[1]["text"]
-
-
-# ---------------------------------------------------------------------------
-# 14. from_sources() — builds retriever with non-empty registry
-# ---------------------------------------------------------------------------
-
-
-def test_from_sources_builds_retriever():
- """from_sources() returns a VectorRetriever with non-empty registry."""
- retriever = VectorRetriever.from_sources(
- catalog=CATALOG,
- embedding=FakeEmbedding(),
- )
-
- assert isinstance(retriever, VectorRetriever)
- assert len(retriever._registry) > 0
-
-
-# ---------------------------------------------------------------------------
-# 15. from_sources() + add() — incremental indexing
-# ---------------------------------------------------------------------------
-
-
-def test_from_sources_add_incremental():
- """retriever.add(chunks) adds chunks without losing existing catalog chunks."""
- retriever = VectorRetriever.from_sources(
- catalog=CATALOG,
- embedding=FakeEmbedding(),
- )
- initial_ids = set(retriever._registry.keys())
- assert len(initial_ids) > 0
-
- doc_chunks = RecursiveCharacterChunker().split(DOCS)
- retriever.add(doc_chunks)
-
- final_ids = set(retriever._registry.keys())
- # new doc chunks were added
- assert len(final_ids) > len(initial_ids)
- # original catalog chunks are still present
- for chunk_id in initial_ids:
- assert chunk_id in final_ids, f"catalog chunk '{chunk_id}' lost after add()"
-
-
-# ---------------------------------------------------------------------------
-# 16. from_chunks() — empty chunks → empty result
-# ---------------------------------------------------------------------------
-
-
-def test_from_chunks_empty():
- """from_chunks([]) → retriever with empty registry returns empty result."""
- retriever = VectorRetriever.from_chunks([], embedding=FakeEmbedding())
-
- assert retriever._registry == {}
- result = retriever("any query")
- assert result.schemas == []
- assert result.context == []
-
-
-# ---------------------------------------------------------------------------
-# 17. from_chunks() — mixed catalog + doc chunks
-# ---------------------------------------------------------------------------
-
-
-def test_from_chunks_mixed_catalog_and_docs():
- """from_chunks with catalog + doc chunks → both source_types in registry."""
- chunks = CatalogChunker().split(CATALOG) + RecursiveCharacterChunker().split(DOCS)
- retriever = VectorRetriever.from_chunks(chunks, embedding=FakeEmbedding())
-
- source_types = {c["source_type"] for c in retriever._registry.values()}
- assert "catalog" in source_types
- assert "document" in source_types
-
-
-# ---------------------------------------------------------------------------
-# 18. from_chunks() + add() — incremental after from_chunks
-# ---------------------------------------------------------------------------
-
-
-def test_from_chunks_add_incremental():
- """from_chunks() followed by add(more_chunks) preserves original chunks."""
- store = InMemoryVectorStore()
- catalog_chunks = CatalogChunker().split(CATALOG)
- retriever = VectorRetriever.from_chunks(
- catalog_chunks, embedding=FakeEmbedding(), vectorstore=store
- )
- initial_ids = set(retriever._registry.keys())
-
- doc_chunks = RecursiveCharacterChunker().split(DOCS)
- retriever.add(doc_chunks)
-
- final_ids = set(retriever._registry.keys())
- assert len(final_ids) > len(initial_ids)
- for chunk_id in initial_ids:
- assert chunk_id in final_ids, f"chunk '{chunk_id}' lost after add()"
-
-
-# ---------------------------------------------------------------------------
-# 19. CatalogChunker.split() — batch convenience method
-# ---------------------------------------------------------------------------
-
-
-def test_catalog_chunker_split_batch():
- """CatalogChunker.split(catalog) returns same chunks as calling chunk() per entry."""
- chunker = CatalogChunker()
- by_split = chunker.split(CATALOG)
- by_chunk = [c for entry in CATALOG for c in chunker.chunk(entry)]
-
- assert [c["chunk_id"] for c in by_split] == [c["chunk_id"] for c in by_chunk]
-
-
-# ---------------------------------------------------------------------------
-# 20-22. VectorRetriever save / load (FAISS 필요)
-# ---------------------------------------------------------------------------
-
-faiss = pytest.importorskip("faiss", reason="faiss-cpu not installed")
-
-
-class FakeEmbeddingFAISS:
- """FAISS L2 정규화에서 zero-vector 오류가 안 나도록 비영벡터를 반환."""
-
- def _vec(self, text: str) -> list[float]:
- # 텍스트별로 구별 가능한 비영벡터
- h = abs(hash(text)) % 900 + 100
- return [h * 0.001, 1.0, 1.0, 1.0]
-
- def embed_query(self, text: str) -> list[float]:
- return self._vec(text)
-
- def embed_texts(self, texts: list[str]) -> list[list[float]]:
- return [self._vec(t) for t in texts]
-
-
-def test_save_and_load_returns_same_results(tmp_path):
- """save → load 후 동일 쿼리에 동일 스키마가 반환된다."""
- path = str(tmp_path / "catalog")
- embedding = FakeEmbeddingFAISS()
-
- from lang2sql.integrations.vectorstore.faiss_ import FAISSVectorStore
-
- store = FAISSVectorStore(index_path=path + ".faiss")
- chunks = CatalogChunker().split(CATALOG)
- original = VectorRetriever.from_chunks(
- chunks, embedding=embedding, vectorstore=store
- )
- original.save(path)
-
- loaded_store = FAISSVectorStore.load(path)
- loaded = VectorRetriever.load(path, vectorstore=loaded_store, embedding=embedding)
- result = loaded.run("주문 정보")
-
- assert len(result.schemas) > 0
- assert result.schemas[0]["name"] == original.run("주문 정보").schemas[0]["name"]
-
-
-def test_load_registry_intact(tmp_path):
- """load 후 registry의 키·값이 원본과 동일하다."""
- path = str(tmp_path / "catalog")
- embedding = FakeEmbeddingFAISS()
-
- from lang2sql.integrations.vectorstore.faiss_ import FAISSVectorStore
-
- store = FAISSVectorStore(index_path=path + ".faiss")
- chunks = CatalogChunker().split(CATALOG)
- original = VectorRetriever.from_chunks(
- chunks, embedding=embedding, vectorstore=store
- )
- original.save(path)
-
- loaded_store = FAISSVectorStore.load(path)
- loaded = VectorRetriever.load(path, vectorstore=loaded_store, embedding=embedding)
-
- assert set(loaded._registry.keys()) == set(original._registry.keys())
- for chunk_id, chunk in original._registry.items():
- assert loaded._registry[chunk_id]["text"] == chunk["text"]
- assert loaded._registry[chunk_id]["source_id"] == chunk["source_id"]
-
-
-def test_save_raises_for_inmemory():
- """InMemoryVectorStore는 save()를 지원하지 않아 NotImplementedError가 발생한다."""
- embedding = FakeEmbeddingFAISS()
- chunks = CatalogChunker().split(CATALOG)
- retriever = VectorRetriever.from_chunks(
- chunks, embedding=embedding
- ) # InMemory 기본값
-
- with pytest.raises(NotImplementedError, match="does not support save"):
- retriever.save("/tmp/should_not_exist")
diff --git a/tests/test_core_base.py b/tests/test_core_base.py
deleted file mode 100644
index 524fe25..0000000
--- a/tests/test_core_base.py
+++ /dev/null
@@ -1,177 +0,0 @@
-import pytest
-
-from lang2sql.core.base import BaseComponent, BaseFlow
-from lang2sql.core.hooks import MemoryHook
-from lang2sql.core.exceptions import (
- ComponentError,
- ValidationError,
- IntegrationMissingError,
-)
-
-# -------------------------
-# Fixtures: tiny components/flows
-# -------------------------
-
-
-class AddOne(BaseComponent):
- def _run(self, x: int) -> int:
- return x + 1
-
-
-class BoomValueError(BaseComponent):
- def _run(self, x: int) -> int:
- raise ValueError("boom")
-
-
-class BoomDomainError(BaseComponent):
- def _run(self, x: int) -> int:
- raise ValidationError("bad sql")
-
-
-class BoomIntegrationMissing(BaseComponent):
- def _run(self, x: int) -> int:
- raise IntegrationMissingError("faiss", extra="faiss")
-
-
-class FlowOk(BaseFlow):
- def _run(self, x: int) -> int:
- return x * 2
-
-
-class FlowBoomDomain(BaseFlow):
- def _run(self, x: int) -> int:
- raise ValidationError("flow bad")
-
-
-class FlowBoomUnknown(BaseFlow):
- def _run(self, x: int) -> int:
- raise RuntimeError("flow boom")
-
-
-# -------------------------
-# BaseComponent tests
-# -------------------------
-
-
-def test_base_component_emits_start_end_events():
- hook = MemoryHook()
- c = AddOne(hook=hook)
-
- out = c(1)
- assert out == 2
-
- assert len(hook.events) == 2
- assert hook.events[0].name == "component.run"
- assert hook.events[0].phase == "start"
- assert hook.events[1].name == "component.run"
- assert hook.events[1].phase == "end"
- assert hook.events[1].duration_ms is not None
- assert hook.events[1].duration_ms >= 0.0
-
-
-def test_base_component_wraps_non_domain_exception_as_component_error():
- hook = MemoryHook()
- c = BoomValueError(hook=hook)
-
- with pytest.raises(ComponentError) as ei:
- c(1)
-
- # error chain preserved
- assert isinstance(ei.value.cause, ValueError)
- assert "ValueError" in str(ei.value) or "boom" in str(ei.value)
-
- # events: start + error
- assert len(hook.events) == 2
- assert hook.events[0].phase == "start"
- assert hook.events[1].phase == "error"
- assert "ValueError" in (hook.events[1].error or "")
- assert "boom" in (hook.events[1].error or "")
-
-
-def test_base_component_preserves_domain_error_validationerror():
- hook = MemoryHook()
- c = BoomDomainError(hook=hook)
-
- with pytest.raises(ValidationError) as ei:
- c(1)
-
- assert "bad sql" in str(ei.value)
-
- # events: start + error
- assert len(hook.events) == 2
- assert hook.events[0].phase == "start"
- assert hook.events[1].phase == "error"
- assert "ValidationError" in (hook.events[1].error or "")
- assert "bad sql" in (hook.events[1].error or "")
-
-
-def test_base_component_preserves_domain_error_integration_missing():
- hook = MemoryHook()
- c = BoomIntegrationMissing(hook=hook)
-
- with pytest.raises(IntegrationMissingError) as ei:
- c(1)
-
- msg = str(ei.value)
- assert "Missing optional integration: faiss" in msg
- assert "lang2sql[faiss]" in msg
-
- # events: start + error
- assert len(hook.events) == 2
- assert hook.events[0].phase == "start"
- assert hook.events[1].phase == "error"
- assert "IntegrationMissingError" in (hook.events[1].error or "")
- assert "faiss" in (hook.events[1].error or "")
-
-
-# -------------------------
-# BaseFlow tests
-# -------------------------
-
-
-def test_base_flow_emits_start_end_events():
- hook = MemoryHook()
- f = FlowOk(hook=hook)
-
- out = f(3)
- assert out == 6
-
- assert len(hook.events) == 2
- assert hook.events[0].name == "flow.run"
- assert hook.events[0].phase == "start"
- assert hook.events[1].name == "flow.run"
- assert hook.events[1].phase == "end"
- assert hook.events[1].duration_ms is not None
- assert hook.events[1].duration_ms >= 0.0
-
-
-def test_base_flow_preserves_domain_error():
- hook = MemoryHook()
- f = FlowBoomDomain(hook=hook)
-
- with pytest.raises(ValidationError) as ei:
- f(1)
-
- assert "flow bad" in str(ei.value)
-
- assert len(hook.events) == 2
- assert hook.events[0].phase == "start"
- assert hook.events[1].phase == "error"
- assert "ValidationError" in (hook.events[1].error or "")
- assert "flow bad" in (hook.events[1].error or "")
-
-
-def test_base_flow_raises_unknown_error_and_emits_error_event():
- hook = MemoryHook()
- f = FlowBoomUnknown(hook=hook)
-
- with pytest.raises(RuntimeError) as ei:
- f(1)
-
- assert "flow boom" in str(ei.value)
-
- assert len(hook.events) == 2
- assert hook.events[0].phase == "start"
- assert hook.events[1].phase == "error"
- assert "RuntimeError" in (hook.events[1].error or "")
- assert "flow boom" in (hook.events[1].error or "")
diff --git a/tests/test_core_context.py b/tests/test_core_context.py
deleted file mode 100644
index 27bf2bc..0000000
--- a/tests/test_core_context.py
+++ /dev/null
@@ -1,84 +0,0 @@
-import pytest
-
-from lang2sql.core.context import RunContext
-
-
-def test_init_sets_query_and_metadata_kwargs():
- ctx = RunContext(query="hello", user_id="u1", trace_id="t1")
- assert ctx.inputs["query"] == "hello"
- assert ctx.metadata["user_id"] == "u1"
- assert ctx.metadata["trace_id"] == "t1"
-
-
-def test_query_property_default_empty_string():
- ctx = RunContext()
- assert ctx.query == "" # inputs에 query 없으면 ""
-
-
-def test_query_setter_updates_inputs():
- ctx = RunContext()
- ctx.query = "select something"
- assert ctx.inputs["query"] == "select something"
- assert ctx.query == "select something"
-
-
-def test_schema_property_initializes_dict_and_persists_reference():
- ctx = RunContext()
- s = ctx.schema
- assert isinstance(s, dict)
- assert ctx.artifacts["schema"] is s # 같은 객체를 보장(참조 유지)
-
- s["selected"] = ["users", "orders"]
- assert ctx.artifacts["schema"]["selected"] == ["users", "orders"]
-
-
-def test_schema_property_overwrites_non_dict_value():
- ctx = RunContext()
- ctx.artifacts["schema"] = ["not", "a", "dict"]
- s = ctx.schema
- assert isinstance(s, dict)
- assert ctx.artifacts["schema"] == {} # dict가 아니면 {}로 교체됨
-
-
-def test_sql_property_get_set():
- ctx = RunContext()
- assert ctx.sql == ""
- ctx.sql = "SELECT 1;"
- assert ctx.outputs["sql"] == "SELECT 1;"
- assert ctx.sql == "SELECT 1;"
-
-
-def test_validation_property_get_set():
- ctx = RunContext()
- assert ctx.validation is None
- ctx.validation = {"ok": True, "warnings": []}
- assert ctx.outputs["validation"] == {"ok": True, "warnings": []}
- assert ctx.validation == {"ok": True, "warnings": []}
-
-
-def test_push_meta_appends_to_list():
- ctx = RunContext()
- ctx.push_meta("events", {"name": "start"})
- ctx.push_meta("events", {"name": "end"})
- assert ctx.metadata["events"] == [{"name": "start"}, {"name": "end"}]
-
-
-def test_push_meta_raises_when_existing_not_list():
- ctx = RunContext()
- ctx.metadata["events"] = {"name": "oops"} # list가 아닌 값
- with pytest.raises(TypeError):
- ctx.push_meta("events", {"name": "start"})
-
-
-def test_get_meta_list_returns_empty_when_missing():
- ctx = RunContext()
- assert ctx.get_meta_list("missing") == []
-
-
-def test_get_meta_list_returns_list_or_wraps_scalar():
- ctx = RunContext()
- ctx.metadata["items"] = [1, 2, 3]
- assert ctx.get_meta_list("items") == [1, 2, 3]
-
- ctx.metadata["single"] = 42
- assert ctx.get_meta_list("single") == [42]
diff --git a/tests/test_core_exceptions.py b/tests/test_core_exceptions.py
deleted file mode 100644
index b67716e..0000000
--- a/tests/test_core_exceptions.py
+++ /dev/null
@@ -1,40 +0,0 @@
-import pytest
-
-from lang2sql.core.exceptions import (
- IntegrationMissingError,
- ComponentError,
- Lang2SQLError,
-)
-
-
-def test_integration_missing_error_message_includes_extra_hint():
- err = IntegrationMissingError("faiss", extra="faiss")
- msg = str(err)
- assert "Missing optional integration: faiss" in msg
- assert "pip install 'lang2sql[faiss]'" in msg
-
-
-def test_integration_missing_error_message_includes_hint_when_provided():
- err = IntegrationMissingError("openai", extra="openai", hint="Needed for LLM calls")
- msg = str(err)
- assert "Missing optional integration: openai" in msg
- assert "pip install 'lang2sql[openai]'" in msg
- assert "Needed for LLM calls" in msg
-
-
-def test_component_error_wraps_component_name_and_message():
- err = ComponentError("KeywordTableRetriever", "component failed")
- msg = str(err)
- assert msg.startswith("[KeywordTableRetriever]")
- assert "component failed" in msg
-
-
-def test_exceptions_are_subclasses_of_base_error():
- assert issubclass(IntegrationMissingError, Lang2SQLError)
- assert issubclass(ComponentError, Lang2SQLError)
-
-
-def test_component_error_can_chain_cause():
- root = ValueError("boom")
- err = ComponentError("X", "failed", cause=root)
- assert err.cause is root
diff --git a/tests/test_core_hooks.py b/tests/test_core_hooks.py
deleted file mode 100644
index 60cf341..0000000
--- a/tests/test_core_hooks.py
+++ /dev/null
@@ -1,74 +0,0 @@
-from lang2sql.core.hooks import (
- Event,
- MemoryHook,
- NullHook,
- summarize,
- now,
- ms,
-)
-
-
-def test_memory_hook_collects_events():
- hook = MemoryHook()
- e1 = Event(name="x", component="c", phase="start", ts=123.0)
- e2 = Event(name="x", component="c", phase="end", ts=124.0, duration_ms=1.0)
- hook.on_event(e1)
- hook.on_event(e2)
-
- assert len(hook.events) == 2
- assert hook.events[0].phase == "start"
- assert hook.events[1].phase == "end"
-
-
-def test_null_hook_does_not_crash():
- hook = NullHook()
- hook.on_event(
- Event(name="x", component="c", phase="start", ts=0.0)
- ) # should not raise
-
-
-def test_summarize_truncates_long_repr():
- long = "a" * 1000
- s = summarize(long, max_len=50)
- assert len(s) <= 50
- assert s.endswith("...")
-
-
-def test_now_and_ms_work():
- t0 = now()
- t1 = now()
- assert t1 >= t0
- d = ms(t0, t1)
- assert d >= 0.0
-
-
-def test_memory_hook_clear_resets_events():
- hook = MemoryHook()
- hook.on_event(Event(name="x", component="c", phase="start", ts=0.0))
- assert len(hook.events) == 1
- hook.clear()
- assert len(hook.events) == 0
-
-
-def test_memory_hook_snapshot_is_copy():
- hook = MemoryHook()
- hook.on_event(Event(name="x", component="c", phase="start", ts=0.0))
-
- snap = hook.snapshot()
- assert snap is not hook.events
- assert len(snap) == 1
-
- # mutate original after snapshot
- hook.on_event(Event(name="x", component="c", phase="end", ts=1.0))
- assert len(hook.events) == 2
- assert len(snap) == 1 # snapshot should not change
-
-
-class BadRepr:
- def __repr__(self):
- raise RuntimeError("boom")
-
-
-def test_summarize_handles_bad_repr():
- s = summarize(BadRepr(), max_len=50)
- assert "unreprable" in s
diff --git a/tests/test_discord.py b/tests/test_discord.py
new file mode 100644
index 0000000..0fe8fea
--- /dev/null
+++ b/tests/test_discord.py
@@ -0,0 +1,216 @@
+"""Unit tests for the Discord frontend — no live bot, no token, no network.
+
+Covers the three pure modules (session_router mapping, render thresholds,
+CommandHandlers against a real in-memory ContextConcierge) plus the import-
+safety contract that ``bot.py`` loads with no ``DISCORD_BOT_TOKEN`` set.
+
+Async handlers are driven with :func:`asyncio.run` to match the convention in
+the rest of the suite (no pytest-asyncio marker plumbing).
+"""
+
+from __future__ import annotations
+
+import asyncio
+import os
+
+from lang2sql.core.identity import ScopeLevel
+from lang2sql.frontends.discord import (
+ CommandHandlers,
+ InteractionContext,
+ is_channel,
+ is_dm,
+ is_thread,
+ render_answer,
+ to_identity,
+)
+from lang2sql.frontends.discord.render import MAX_INLINE_ROWS
+from lang2sql.tenancy.concierge import ContextConcierge
+
+
+# -- session_router -------------------------------------------------------
+
+
+def test_dm_identity_has_no_guild() -> None:
+ ident = to_identity(InteractionContext(user_id="u1"))
+ assert is_dm(ident)
+ assert not is_channel(ident)
+ assert not is_thread(ident)
+ assert ident.session_key() == "dm:u1"
+
+
+def test_channel_identity_scopes_to_channel() -> None:
+ ident = to_identity(
+ InteractionContext(user_id="u1", guild_id="g1", channel_id="c1")
+ )
+ assert is_channel(ident)
+ assert not is_dm(ident)
+ assert not is_thread(ident)
+ assert ident.session_key() == "channel:c1"
+ # Federation chain runs narrow→wide: channel, guild, builtin.
+ levels = [s.level for s in ident.scope_chain()]
+ assert levels == [ScopeLevel.CHANNEL, ScopeLevel.GUILD, ScopeLevel.BUILTIN]
+
+
+def test_thread_identity_is_narrowest() -> None:
+ ident = to_identity(
+ InteractionContext(user_id="u1", guild_id="g1", channel_id="c1", thread_id="t1")
+ )
+ assert is_thread(ident)
+ assert ident.session_key() == "thread:t1"
+ assert ident.scope_chain()[0].level is ScopeLevel.THREAD
+
+
+def test_admin_flag_propagates() -> None:
+ ident = to_identity(InteractionContext(user_id="u1", guild_id="g1", is_admin=True))
+ assert ident.is_admin is True
+
+
+# -- render ---------------------------------------------------------------
+
+
+def test_render_small_text_is_plain() -> None:
+ msg = render_answer("just a short answer")
+ assert msg.text == "just a short answer"
+ assert msg.file_bytes is None
+ assert msg.file_name is None
+
+
+def test_render_large_rows_attaches_csv() -> None:
+ rows = [[i, f"name{i}"] for i in range(MAX_INLINE_ROWS + 5)]
+ msg = render_answer("Top users:", rows, header=["id", "name"])
+ assert msg.file_bytes is not None
+ assert msg.file_name == "result.csv"
+ assert "55 rows" in msg.text
+ assert "Top users:" in msg.text
+ decoded = msg.file_bytes.decode("utf-8")
+ assert decoded.startswith("id,name")
+ assert "name54" in decoded
+
+
+def test_render_small_rows_inlined() -> None:
+ rows = [[1, "a"], [2, "b"]]
+ msg = render_answer("", rows, header=["id", "name"])
+ assert msg.file_bytes is None
+ assert "id,name" in msg.text
+ assert "1,a" in msg.text
+
+
+def test_render_many_text_lines_attaches() -> None:
+ text = "\n".join(f"line {i}" for i in range(MAX_INLINE_ROWS + 1))
+ msg = render_answer(text)
+ assert msg.file_bytes is not None
+ assert "lines" in msg.text
+
+
+# -- CommandHandlers (real in-memory concierge) ---------------------------
+
+
+def test_define_metric_then_semantic_show() -> None:
+ handlers = CommandHandlers(ContextConcierge())
+ ident = to_identity(
+ InteractionContext(user_id="u1", guild_id="g1", channel_id="c1")
+ )
+
+ async def scenario() -> tuple[str, str]:
+ defined = await handlers.define_metric(ident, "active_user", "logged in within 30 days")
+ shown = await handlers.semantic_show(ident)
+ return defined.text, shown.text
+
+ defined_text, shown_text = asyncio.run(scenario())
+ assert "active_user" in defined_text
+ assert "active_user" in shown_text
+ assert "logged in within 30 days" in shown_text
+
+
+def test_semantic_show_empty_scope() -> None:
+ handlers = CommandHandlers(ContextConcierge())
+ ident = to_identity(InteractionContext(user_id="solo", guild_id="g9", channel_id="c9"))
+ shown = asyncio.run(handlers.semantic_show(ident))
+ assert "No definitions" in shown.text
+
+
+def test_define_metric_is_scope_isolated() -> None:
+ """A channel definition must not leak into a different channel (federation)."""
+ handlers = CommandHandlers(ContextConcierge())
+ marketing = to_identity(InteractionContext(user_id="u1", guild_id="g1", channel_id="mkt"))
+ product = to_identity(InteractionContext(user_id="u1", guild_id="g1", channel_id="prd"))
+
+ async def scenario() -> str:
+ await handlers.define_metric(marketing, "active_user", "30d login")
+ return (await handlers.semantic_show(product)).text
+
+ assert "active_user" not in asyncio.run(scenario())
+
+
+def test_remember_and_audit_me() -> None:
+ handlers = CommandHandlers(ContextConcierge())
+ ident = to_identity(InteractionContext(user_id="u2", guild_id="g1", channel_id="c1"))
+
+ async def scenario() -> tuple[str, str]:
+ remembered = await handlers.remember(ident, "prefers ISO dates")
+ audit = await handlers.audit_me(ident)
+ return remembered.text, audit.text
+
+ remembered_text, audit_text = asyncio.run(scenario())
+ assert "prefers ISO dates" in remembered_text
+ assert "remember" in audit_text
+
+
+def test_audit_me_empty() -> None:
+ handlers = CommandHandlers(ContextConcierge())
+ ident = to_identity(InteractionContext(user_id="never-acted", guild_id="g1", channel_id="c1"))
+ audit = asyncio.run(handlers.audit_me(ident))
+ assert "No audited activity" in audit.text
+
+
+def test_query_returns_outbound_message() -> None:
+ """With the default FakeLLM (no OPENAI key), a query still returns text."""
+ handlers = CommandHandlers(ContextConcierge())
+ ident = to_identity(InteractionContext(user_id="u3", guild_id="g1", channel_id="c1"))
+ out = asyncio.run(handlers.query(ident, "how many users signed up?"))
+ assert isinstance(out.text, str)
+ assert out.text # non-empty
+
+
+def test_query_persists_session() -> None:
+ concierge = ContextConcierge()
+ handlers = CommandHandlers(concierge)
+ ident = to_identity(InteractionContext(user_id="u4", guild_id="g1", channel_id="c1"))
+
+ async def scenario():
+ await handlers.query(ident, "first question")
+ return await concierge.store.load(ident.session_key())
+
+ saved = asyncio.run(scenario())
+ assert saved is not None
+ assert any(m.content == "first question" for m in saved.transcript)
+
+
+def test_connect_stub_acknowledges() -> None:
+ concierge = ContextConcierge()
+ handlers = CommandHandlers(concierge)
+ ident = to_identity(InteractionContext(user_id="u5", guild_id="g1", channel_id="c1"))
+ out = asyncio.run(handlers.connect(ident, "postgresql://localhost/db"))
+ assert "saved" in out.text.lower()
+ assert concierge.store.kv_get("g1", "dsn") == "postgresql://localhost/db"
+
+
+def test_ingest_lists_or_reports() -> None:
+ handlers = CommandHandlers(ContextConcierge())
+ ident = to_identity(InteractionContext(user_id="u6", guild_id="g1", channel_id="c1"))
+ out = asyncio.run(
+ handlers.ingest(ident, content="total_revenue is the sum of order amounts")
+ )
+ assert isinstance(out.text, str)
+ assert out.text
+
+
+# -- import-safety contract -----------------------------------------------
+
+
+def test_bot_imports_without_token() -> None:
+ """Importing bot.py must not require a token or a network connection."""
+ os.environ.pop("DISCORD_BOT_TOKEN", None)
+ import lang2sql.frontends.discord.bot as bot # noqa: F401
+
+ assert hasattr(bot, "run")
diff --git a/tests/test_flows_baseline.py b/tests/test_flows_baseline.py
deleted file mode 100644
index 93df60f..0000000
--- a/tests/test_flows_baseline.py
+++ /dev/null
@@ -1,128 +0,0 @@
-import pytest
-
-from lang2sql.core.base import BaseFlow
-from lang2sql.flows.baseline import BaselineFlow, SequentialFlow
-
-
-def test_requires_at_least_one_step():
- with pytest.raises(ValueError):
- SequentialFlow(steps=[])
-
-
-def test_baseline_flow_emits_deprecation_warning():
- with pytest.warns(DeprecationWarning, match="BaselineFlow is deprecated"):
- flow = BaselineFlow(steps=[lambda x: x])
- assert isinstance(flow, SequentialFlow)
-
-
-def test_run_passes_value_through_single_step():
- flow = SequentialFlow(steps=[lambda x: x + 1])
- assert flow.run(1) == 2
-
-
-def test_run_chains_multiple_steps():
- flow = SequentialFlow(steps=[lambda x: x * 2, lambda x: x + 10])
- assert flow.run(5) == 20 # 5 * 2 = 10, 10 + 10 = 20
-
-
-def test_step_order_is_preserved():
- trace = []
-
- def s1(x):
- trace.append("s1")
- return x
-
- def s2(x):
- trace.append("s2")
- return x
-
- def s3(x):
- trace.append("s3")
- return x
-
- SequentialFlow(steps=[s1, s2, s3]).run("input")
- assert trace == ["s1", "s2", "s3"]
-
-
-def test_user_can_compose_flows_with_python_control_flow():
- default_flow = SequentialFlow(steps=[lambda x: x + "_default"])
- override_flow = SequentialFlow(steps=[lambda x: x + "_override"])
-
- assert default_flow("q") == "q_default"
-
- class CustomFlow(BaseFlow):
- def _run(self, value):
- return override_flow(value)
-
- assert CustomFlow().run("q") == "q_override"
-
-
-# -------------------------
-# Advanced: retry patterns
-# -------------------------
-
-
-def test_custom_flow_fallback_then_revalidate():
- def gen_bad(_query):
- return "DROP TABLE users;"
-
- def validate(sql):
- return "drop " not in sql.lower()
-
- class FixThenRevalidateFlow(BaseFlow):
- def _run(self, query):
- sql = gen_bad(query)
- if not validate(sql):
- sql = "SELECT 1;"
- return sql
-
- assert FixThenRevalidateFlow().run("q") == "SELECT 1;"
-
-
-def test_custom_flow_retry_until_valid():
- attempts = {"count": 0}
-
- def generate(_query):
- attempts["count"] += 1
- return "DROP TABLE users;" if attempts["count"] == 1 else "SELECT 1;"
-
- def validate(sql):
- return "drop " not in sql.lower()
-
- class RetryFlow(BaseFlow):
- def _run(self, query):
- for _ in range(3):
- sql = generate(query)
- if validate(sql):
- return sql
- return sql
-
- result = RetryFlow().run("q")
- assert result == "SELECT 1;"
- assert attempts["count"] >= 2
-
-
-# -------------------------
-# Composition: flow as step
-# -------------------------
-
-
-def test_subflow_can_be_used_as_a_step():
- inner = SequentialFlow(steps=[lambda x: x + 1])
- outer = SequentialFlow(steps=[lambda x: x * 2, inner])
-
- # 3 * 2 = 6, 6 + 1 = 7
- assert outer.run(3) == 7
-
-
-def test_subflow_can_be_conditionally_invoked_in_custom_flow():
- flow_a = SequentialFlow(steps=[lambda x: x + "_a"])
-
- class ConditionalFlow(BaseFlow):
- def _run(self, value):
- if "use_a" in value:
- return flow_a(value)
- return value
-
- assert ConditionalFlow().run("nope") == "nope"
- assert ConditionalFlow().run("please use_a") == "please use_a_a"
diff --git a/tests/test_flows_enriched_nl2sql.py b/tests/test_flows_enriched_nl2sql.py
deleted file mode 100644
index 9683c37..0000000
--- a/tests/test_flows_enriched_nl2sql.py
+++ /dev/null
@@ -1,195 +0,0 @@
-"""Tests for EnrichedNL2SQL flow."""
-
-from __future__ import annotations
-
-import json
-from unittest.mock import MagicMock
-
-import pytest
-
-from lang2sql.core.catalog import CatalogEntry
-from lang2sql.core.exceptions import ContractError
-from lang2sql.core.hooks import MemoryHook
-from lang2sql.flows.enriched_nl2sql import EnrichedNL2SQL
-
-# ---------------------------------------------------------------------------
-# Fakes
-# ---------------------------------------------------------------------------
-
-
-class FakeLLM:
- """Configurable fake LLM that cycles through responses."""
-
- def __init__(self, responses: list[str]):
- self._responses = list(responses)
- self._idx = 0
-
- def invoke(self, messages: list[dict]) -> str:
- resp = self._responses[self._idx % len(self._responses)]
- self._idx += 1
- return resp
-
-
-class FakeDB:
- def execute(self, sql: str) -> list[dict]:
- return [{"count": 42}]
-
-
-class FakeEmbedding:
- def embed_query(self, text: str) -> list[float]:
- return [0.1, 0.2, 0.3]
-
- def embed_texts(self, texts: list[str]) -> list[list[float]]:
- return [[0.1, 0.2, 0.3] for _ in texts]
-
-
-# ---------------------------------------------------------------------------
-# Helpers
-# ---------------------------------------------------------------------------
-
-
-def _gate_json(suitable: bool = True) -> str:
- return json.dumps(
- {
- "suitable": suitable,
- "reason": "ok" if suitable else "not suitable",
- "missing_entities": [],
- "requires_data_science": False,
- }
- )
-
-
-def _suitability_json(table_names: list[str], score: float = 0.9) -> str:
- return json.dumps(
- {
- "results": [
- {
- "table_name": name,
- "score": score,
- "reason": "ok",
- "matched_columns": [],
- "missing_entities": [],
- }
- for name in table_names
- ]
- }
- )
-
-
-def _profile_json() -> str:
- return json.dumps(
- {
- "is_timeseries": False,
- "is_aggregation": True,
- "has_filter": False,
- "is_grouped": False,
- "has_ranking": False,
- "has_temporal_comparison": False,
- "intent_type": "lookup",
- }
- )
-
-
-def _catalog() -> list[CatalogEntry]:
- return [
- {
- "name": "orders",
- "description": "주문 테이블",
- "columns": {"order_id": "ID", "amount": "금액", "created_at": "생성일"},
- }
- ]
-
-
-def _make_pipeline(gate_enabled: bool = True) -> EnrichedNL2SQL:
- # LLM response order:
- # 1. QuestionGate → gate JSON
- # 2. TableSuitabilityEvaluator → suitability JSON
- # 3. QuestionProfiler → profile JSON
- # 4. ContextEnricher → enriched string
- # 5. SQLGenerator → sql block
- llm = FakeLLM(
- [
- _gate_json(suitable=True),
- _suitability_json(["orders"]),
- _profile_json(),
- "지난달 주문 건수를 구합니다",
- "```sql\nSELECT COUNT(*) FROM orders\n```",
- ]
- )
- return EnrichedNL2SQL(
- catalog=_catalog(),
- llm=llm,
- db=FakeDB(),
- embedding=FakeEmbedding(),
- db_dialect="sqlite",
- gate_enabled=gate_enabled,
- )
-
-
-# ---------------------------------------------------------------------------
-# Tests
-# ---------------------------------------------------------------------------
-
-
-def test_enriched_nl2sql_full_pipeline_returns_rows():
- pipeline = _make_pipeline()
- rows = pipeline.run("지난달 주문 건수")
- assert rows == [{"count": 42}]
-
-
-def test_enriched_nl2sql_gate_disabled_skips_gate():
- # With gate disabled, LLM responses shift by one
- llm = FakeLLM(
- [
- _suitability_json(["orders"]),
- _profile_json(),
- "enriched query",
- "```sql\nSELECT COUNT(*) FROM orders\n```",
- ]
- )
- pipeline = EnrichedNL2SQL(
- catalog=_catalog(),
- llm=llm,
- db=FakeDB(),
- embedding=FakeEmbedding(),
- gate_enabled=False,
- )
- rows = pipeline.run("주문 건수")
- assert rows == [{"count": 42}]
-
-
-def test_enriched_nl2sql_gate_raises_contract_error_when_not_suitable():
- llm = FakeLLM([_gate_json(suitable=False)])
- pipeline = EnrichedNL2SQL(
- catalog=_catalog(),
- llm=llm,
- db=FakeDB(),
- embedding=FakeEmbedding(),
- gate_enabled=True,
- )
- with pytest.raises(ContractError):
- pipeline.run("통계 모델을 만들어줘")
-
-
-def test_enriched_nl2sql_emits_hook_events():
- hook = MemoryHook()
- llm = FakeLLM(
- [
- _gate_json(suitable=True),
- _suitability_json(["orders"]),
- _profile_json(),
- "enriched",
- "```sql\nSELECT COUNT(*) FROM orders\n```",
- ]
- )
- pipeline = EnrichedNL2SQL(
- catalog=_catalog(),
- llm=llm,
- db=FakeDB(),
- embedding=FakeEmbedding(),
- gate_enabled=True,
- hook=hook,
- )
- pipeline.run("주문 건수")
- # At minimum, the flow itself emits start/end
- assert len(hook.events) > 0
diff --git a/tests/test_flows_nl2sql.py b/tests/test_flows_nl2sql.py
deleted file mode 100644
index 2573dfa..0000000
--- a/tests/test_flows_nl2sql.py
+++ /dev/null
@@ -1,124 +0,0 @@
-"""End-to-end tests for BaselineNL2SQL."""
-
-from __future__ import annotations
-
-import pytest
-
-from lang2sql.core.exceptions import ComponentError
-from lang2sql.core.hooks import MemoryHook
-from lang2sql.flows.nl2sql import BaselineNL2SQL
-
-# ---------------------------------------------------------------------------
-# Fakes
-# ---------------------------------------------------------------------------
-
-
-class FakeLLM:
- def __init__(self, response: str = "```sql\nSELECT COUNT(*) FROM orders\n```"):
- self._response = response
-
- def invoke(self, messages: list[dict]) -> str:
- return self._response
-
-
-class FakeDB:
- def __init__(self, rows: list[dict] | None = None):
- self._rows = rows if rows is not None else [{"count": 42}]
-
- def execute(self, sql: str) -> list[dict]:
- return self._rows
-
-
-# ---------------------------------------------------------------------------
-# Fixtures
-# ---------------------------------------------------------------------------
-
-CATALOG = [
- {
- "name": "orders",
- "description": "Monthly order records",
- "columns": {"order_id": "primary key", "amount": "order amount"},
- },
- {
- "name": "customers",
- "description": "Customer master data",
- "columns": {"customer_id": "primary key", "name": "customer name"},
- },
-]
-
-
-# ---------------------------------------------------------------------------
-# Tests
-# ---------------------------------------------------------------------------
-
-
-def test_pipeline_e2e_returns_rows():
- pipeline = BaselineNL2SQL(
- catalog=CATALOG,
- llm=FakeLLM(),
- db=FakeDB([{"count": 42}]),
- )
- result = pipeline.run("지난달 주문 건수")
- assert result == [{"count": 42}]
-
-
-def test_pipeline_emits_3_component_start_events():
- hook = MemoryHook()
- pipeline = BaselineNL2SQL(
- catalog=CATALOG,
- llm=FakeLLM(),
- db=FakeDB(),
- hook=hook,
- )
- pipeline.run("주문 건수")
-
- component_starts = [
- e for e in hook.snapshot() if e.name == "component.run" and e.phase == "start"
- ]
- assert len(component_starts) == 3
-
-
-def test_pipeline_propagates_component_error_on_bad_llm_response():
- pipeline = BaselineNL2SQL(
- catalog=CATALOG,
- llm=FakeLLM("No SQL here, just text."),
- db=FakeDB(),
- )
- with pytest.raises(ComponentError):
- pipeline.run("주문 건수")
-
-
-def test_pipeline_hook_records_all_phases():
- hook = MemoryHook()
- pipeline = BaselineNL2SQL(
- catalog=CATALOG,
- llm=FakeLLM(),
- db=FakeDB(),
- hook=hook,
- )
- pipeline.run("test")
-
- events = hook.snapshot()
- component_events = [e for e in events if e.name == "component.run"]
- flow_events = [e for e in events if e.name == "flow.run"]
-
- assert len(component_events) == 6 # 3 components × (start + end)
- assert len(flow_events) == 2 # flow start + flow end
-
-
-def test_pipeline_advanced_usage_manual_composition():
- """고급 사용자 — 직접 컴포넌트 조합."""
- from lang2sql.components.execution.sql_executor import SQLExecutor
- from lang2sql.components.generation.sql_generator import SQLGenerator
- from lang2sql.components.retrieval.keyword import KeywordRetriever
-
- retriever = KeywordRetriever(catalog=CATALOG)
- generator = SQLGenerator(llm=FakeLLM())
- executor = SQLExecutor(db=FakeDB([{"total": 99}]))
-
- query = "주문 건수"
- schemas = retriever(query)
- sql = generator(query, schemas)
- result = executor(sql)
-
- assert result == [{"total": 99}]
diff --git a/tests/test_harness_loop.py b/tests/test_harness_loop.py
new file mode 100644
index 0000000..db73788
--- /dev/null
+++ b/tests/test_harness_loop.py
@@ -0,0 +1,56 @@
+"""Week-1 smoke test: the agent loop completes a full tool cycle.
+
+Locks in the walking-skeleton contract — user turn → tool call → tool result →
+final answer — so later refactors of loop/registry/context don't silently break
+the dispatch path.
+"""
+
+from __future__ import annotations
+
+import asyncio
+
+from lang2sql.adapters.llm.fake import FakeLLM
+from lang2sql.core.identity import Identity
+from lang2sql.core.types import Role
+from lang2sql.harness.context import HarnessContext
+from lang2sql.harness.loop import agent_loop
+from lang2sql.harness.session import Session
+from lang2sql.harness.tool_registry import ToolRegistry
+from lang2sql.tools.ping import Ping
+
+
+def _ctx() -> HarnessContext:
+ identity = Identity(user_id="tester")
+ return HarnessContext(
+ identity=identity,
+ llm=FakeLLM(),
+ tools=ToolRegistry([Ping()]),
+ session=Session(identity=identity),
+ )
+
+
+def test_loop_runs_tool_then_answers():
+ ctx = _ctx()
+ answer = asyncio.run(agent_loop(ctx, "hello"))
+ assert "pong" in answer # tool ran and its result reached the final answer
+
+
+def test_transcript_shape():
+ ctx = _ctx()
+ asyncio.run(agent_loop(ctx, "hello"))
+ roles = [m.role for m in ctx.session.history()]
+ # user → assistant(tool_call) → tool → assistant(final)
+ assert roles == [Role.USER, Role.ASSISTANT, Role.TOOL, Role.ASSISTANT]
+
+
+def test_tool_call_id_is_stamped():
+ ctx = _ctx()
+ asyncio.run(agent_loop(ctx, "hello"))
+ tool_msgs = [m for m in ctx.session.history() if m.role == Role.TOOL]
+ assert tool_msgs and tool_msgs[0].tool_call_id
+
+
+def test_scope_chain_orders_narrow_to_wide():
+ ident = Identity(user_id="u", guild_id="g", channel_id="c", thread_id="t")
+ levels = [s.level.value for s in ident.scope_chain()]
+ assert levels == ["thread", "channel", "guild", "builtin"]
diff --git a/tests/test_ingestion.py b/tests/test_ingestion.py
new file mode 100644
index 0000000..f0d7836
--- /dev/null
+++ b/tests/test_ingestion.py
@@ -0,0 +1,134 @@
+"""Ingestion V1 — LLM extraction and pipeline wiring (★③)."""
+
+from __future__ import annotations
+
+import asyncio
+import os
+import tempfile
+from typing import Sequence
+
+from lang2sql.core.ports.ingestion import CandidateKind, Document
+from lang2sql.core.types import Completion, Message, ToolSpec
+from lang2sql.ingestion import FileSource, IngestionPipeline, LLMExtractor
+
+
+class _ScriptedLLM:
+ """Inline fake LLMPort returning a fixed completion content."""
+
+ def __init__(self, content: str) -> None:
+ self._content = content
+
+ async def complete(
+ self,
+ messages: Sequence[Message],
+ tools: Sequence[ToolSpec] = (),
+ ) -> Completion:
+ return Completion(content=self._content, finish_reason="stop")
+
+
+_JSON_ARRAY = """
+[
+ {"kind": "metric", "name": "total_revenue",
+ "definition": "SUM(orders.amount)", "applies_to": "orders"},
+ {"kind": "rule", "name": "exclude_cancelled",
+ "definition": "status != 'cancelled'", "applies_to": "total_revenue"}
+]
+"""
+
+_FENCED = "```json\n" + _JSON_ARRAY.strip() + "\n```"
+
+
+def test_llm_extractor_maps_json_array_to_candidates() -> None:
+ extractor = LLMExtractor(_ScriptedLLM(_JSON_ARRAY))
+
+ async def run() -> None:
+ doc = Document(name="defs.md", text="...", source_id="doc-1")
+ cands = await extractor.extract(doc)
+ assert [c.name for c in cands] == ["total_revenue", "exclude_cancelled"]
+ assert cands[0].kind == CandidateKind.METRIC
+ assert cands[1].kind == CandidateKind.RULE
+ assert all(c.source_id == "doc-1" for c in cands)
+
+ asyncio.run(run())
+
+
+def test_llm_extractor_strips_json_fences() -> None:
+ extractor = LLMExtractor(_ScriptedLLM(_FENCED))
+
+ async def run() -> None:
+ cands = await extractor.extract(Document(name="d", text="t", source_id="s"))
+ assert len(cands) == 2
+
+ asyncio.run(run())
+
+
+def test_llm_extractor_malformed_content_returns_empty() -> None:
+ async def run() -> None:
+ for content in ["not json at all", "", "{not: valid}", "42", "{}"]:
+ extractor = LLMExtractor(_ScriptedLLM(content))
+ assert await extractor.extract(Document(name="d", text="t")) == []
+
+ asyncio.run(run())
+
+
+def test_llm_extractor_skips_rows_with_bad_kind_or_missing_fields() -> None:
+ content = """
+ [
+ {"kind": "metric", "name": "ok", "definition": "x"},
+ {"kind": "bogus", "name": "bad_kind", "definition": "y"},
+ {"kind": "metric", "name": "", "definition": "z"},
+ {"kind": "metric", "name": "no_def"},
+ "not a dict"
+ ]
+ """
+ extractor = LLMExtractor(_ScriptedLLM(content))
+
+ async def run() -> None:
+ cands = await extractor.extract(Document(name="d", text="t"))
+ assert [c.name for c in cands] == ["ok"]
+
+ asyncio.run(run())
+
+
+def test_file_source_decodes_blob() -> None:
+ source = FileSource()
+
+ async def run() -> None:
+ doc = await source.fetch("path/to/defs.md", blob=b"hello blob")
+ assert doc.name == "defs.md"
+ assert doc.text == "hello blob"
+ assert doc.source_id == "path/to/defs.md"
+
+ asyncio.run(run())
+
+
+def test_file_source_reads_from_disk() -> None:
+ source = FileSource()
+
+ async def run() -> None:
+ fd, path = tempfile.mkstemp(suffix=".txt")
+ try:
+ with os.fdopen(fd, "w", encoding="utf-8") as handle:
+ handle.write("on disk")
+ doc = await source.fetch(path)
+ assert doc.text == "on disk"
+ assert doc.name == os.path.basename(path)
+ finally:
+ os.remove(path)
+
+ asyncio.run(run())
+
+
+def test_pipeline_fetches_then_extracts() -> None:
+ pipeline = IngestionPipeline()
+ source = FileSource()
+ extractor = LLMExtractor(_ScriptedLLM(_JSON_ARRAY))
+
+ async def run() -> None:
+ cands = await pipeline.ingest(
+ source, extractor, ref="defs.md", blob=b"document body"
+ )
+ assert [c.name for c in cands] == ["total_revenue", "exclude_cancelled"]
+ assert all(c.source_id == "defs.md" for c in cands)
+
+ asyncio.run(run())
diff --git a/tests/test_integration.py b/tests/test_integration.py
new file mode 100644
index 0000000..62d2016
--- /dev/null
+++ b/tests/test_integration.py
@@ -0,0 +1,64 @@
+"""Week-2 integration: the ContextConcierge wires a working agent.
+
+Exercises the assembled stack (tools + safety + scope resolver + explorer stub
++ sqlite) without network, using the offline FakeLLM. Locks in that the six V1
+tools are registered and that run_sql/define_metric behave through the live
+HarnessContext.
+"""
+
+from __future__ import annotations
+
+import asyncio
+
+from lang2sql.core.identity import Identity
+from lang2sql.core.ports.safety import SafetyContext, Verdict
+from lang2sql.tenancy.concierge import ContextConcierge
+from lang2sql.tools.define_metric import DefineMetric
+from lang2sql.tools.run_sql import RunSQL
+
+
+def _ctx():
+ concierge = ContextConcierge()
+ ident = Identity(user_id="u1", guild_id="g1", channel_id="c-mkt")
+ return ident, asyncio.run(concierge.build_context(ident))
+
+
+def test_six_v1_tools_registered():
+ _, ctx = _ctx()
+ names = {s.name for s in ctx.tools.specs()}
+ assert names == {"run_sql", "explore_schema", "define_metric", "ask_user", "remember", "ingest_doc"}
+
+
+def test_run_sql_passes_gate_and_returns_rows():
+ _, ctx = _ctx()
+ res = asyncio.run(RunSQL().run({"sql": "SELECT * FROM orders", "limit": 10}, ctx))
+ assert not res.is_error
+ assert "row" in res.content.lower()
+
+
+def test_run_sql_blocks_ddl():
+ _, ctx = _ctx()
+ res = asyncio.run(RunSQL().run({"sql": "DROP TABLE users"}, ctx))
+ assert res.is_error and "BLOCKED" in res.content
+
+
+def test_run_sql_tolerates_bad_limit():
+ _, ctx = _ctx()
+ res = asyncio.run(RunSQL().run({"sql": "SELECT 1", "limit": "demo"}, ctx))
+ assert not res.is_error # malformed limit must not crash the tool
+
+
+def test_define_metric_is_scope_local():
+ ident, ctx = _ctx()
+ asyncio.run(DefineMetric().run({"name": "active_user", "definition": "30d login"}, ctx))
+ layer = asyncio.run(ctx.scope_resolver.effective_layer(ident))
+ assert layer.lookup("active_user") is not None
+ # a different channel does not see it
+ other = Identity(user_id="u1", guild_id="g1", channel_id="c-fin")
+ other_layer = asyncio.run(ctx.scope_resolver.effective_layer(other))
+ assert other_layer.lookup("active_user") is None
+
+
+def test_safety_pipeline_on_context():
+ _, ctx = _ctx()
+ assert ctx.safety.evaluate("SELECT 1", SafetyContext()).verdict == Verdict.PASS
diff --git a/tests/test_integrations_embedding_azure.py b/tests/test_integrations_embedding_azure.py
deleted file mode 100644
index 7e099ad..0000000
--- a/tests/test_integrations_embedding_azure.py
+++ /dev/null
@@ -1,51 +0,0 @@
-"""Tests for AzureOpenAIEmbedding integration."""
-
-from __future__ import annotations
-
-from unittest.mock import MagicMock, patch
-
-import pytest
-
-openai = pytest.importorskip("openai", reason="openai not installed")
-
-from lang2sql.integrations.embedding.azure_ import AzureOpenAIEmbedding
-
-
-def _make_embedding() -> AzureOpenAIEmbedding:
- return AzureOpenAIEmbedding(
- azure_deployment="text-embedding-ada-002",
- azure_endpoint="https://test.openai.azure.com/",
- api_key="test-key",
- )
-
-
-def _mock_embedding_response(vectors: list[list[float]]):
- resp = MagicMock()
- resp.data = [MagicMock(embedding=v) for v in vectors]
- return resp
-
-
-def test_embed_query_returns_vector():
- vec = [0.1, 0.2, 0.3]
- with patch("openai.AzureOpenAI") as MockClient:
- instance = MockClient.return_value
- instance.embeddings.create.return_value = _mock_embedding_response([vec])
-
- emb = _make_embedding()
- emb._client = instance
- result = emb.embed_query("hello")
-
- assert result == vec
-
-
-def test_embed_texts_returns_multiple_vectors():
- vecs = [[0.1, 0.2], [0.3, 0.4]]
- with patch("openai.AzureOpenAI") as MockClient:
- instance = MockClient.return_value
- instance.embeddings.create.return_value = _mock_embedding_response(vecs)
-
- emb = _make_embedding()
- emb._client = instance
- result = emb.embed_texts(["hello", "world"])
-
- assert result == vecs
diff --git a/tests/test_integrations_faiss_vectorstore.py b/tests/test_integrations_faiss_vectorstore.py
deleted file mode 100644
index 0b97fcb..0000000
--- a/tests/test_integrations_faiss_vectorstore.py
+++ /dev/null
@@ -1,107 +0,0 @@
-"""
-FAISSVectorStore integration tests.
-
-All tests are auto-skipped when faiss-cpu is not installed.
-"""
-
-import pytest
-
-faiss = pytest.importorskip("faiss") # skip entire module if not installed
-
-import tempfile
-import os
-
-from lang2sql.integrations.vectorstore.faiss_ import FAISSVectorStore
-
-# ── helpers ──────────────────────────────────────────────────────────────────
-
-
-def _ortho_vectors() -> list[tuple[str, list[float]]]:
- """4 orthogonal unit vectors for deterministic cosine tests."""
- return [
- ("a", [1.0, 0.0, 0.0, 0.0]),
- ("b", [0.0, 1.0, 0.0, 0.0]),
- ("c", [0.0, 0.0, 1.0, 0.0]),
- ("d", [0.0, 0.0, 0.0, 1.0]),
- ]
-
-
-@pytest.fixture
-def store() -> FAISSVectorStore:
- return FAISSVectorStore()
-
-
-def _populate(store: FAISSVectorStore) -> None:
- items = _ortho_vectors()
- ids = [item[0] for item in items]
- vecs = [item[1] for item in items]
- store.upsert(ids, vecs)
-
-
-# ── tests ─────────────────────────────────────────────────────────────────────
-
-
-def test_faiss_upsert_and_search_returns_closest(store):
- """Query vector returns its own id at rank 1."""
- _populate(store)
- results = store.search([1.0, 0.0, 0.0, 0.0], k=1)
- assert len(results) == 1
- assert results[0][0] == "a"
-
-
-def test_faiss_cosine_score_of_identical_vector(store):
- """Identical query → score ≈ 1.0."""
- _populate(store)
- results = store.search([1.0, 0.0, 0.0, 0.0], k=1)
- assert abs(results[0][1] - 1.0) < 1e-5
-
-
-def test_faiss_upsert_merge_preserves_prior_entries(store):
- """Second upsert() call doesn't lose entries from the first."""
- store.upsert(["a"], [[1.0, 0.0, 0.0, 0.0]])
- store.upsert(["b"], [[0.0, 1.0, 0.0, 0.0]])
-
- # "a" should still be retrievable
- results = store.search([1.0, 0.0, 0.0, 0.0], k=2)
- ids = [r[0] for r in results]
- assert "a" in ids
-
-
-def test_faiss_search_respects_k(store):
- """len(results) <= k."""
- _populate(store)
- results = store.search([1.0, 0.0, 0.0, 0.0], k=2)
- assert len(results) <= 2
-
-
-def test_faiss_search_on_empty_store_returns_empty(store):
- """[] before any upsert()."""
- results = store.search([1.0, 0.0, 0.0, 0.0], k=5)
- assert results == []
-
-
-def test_faiss_save_and_load_roundtrip(store, tmp_path):
- """save() → load() → search() returns same results."""
- _populate(store)
- index_path = str(tmp_path / "catalog.faiss")
- store.save(index_path)
-
- loaded = FAISSVectorStore.load(index_path)
- original_results = store.search([1.0, 0.0, 0.0, 0.0], k=1)
- loaded_results = loaded.search([1.0, 0.0, 0.0, 0.0], k=1)
-
- assert loaded_results[0][0] == original_results[0][0]
- assert abs(loaded_results[0][1] - original_results[0][1]) < 1e-5
-
-
-def test_faiss_save_without_path_raises(store):
- """save() with no path and no index_path → ValueError."""
- _populate(store)
- with pytest.raises(ValueError, match="No path provided"):
- store.save()
-
-
-def test_faiss_load_nonexistent_path_raises():
- """load("nonexistent") → FileNotFoundError."""
- with pytest.raises(FileNotFoundError):
- FAISSVectorStore.load("nonexistent_path_that_does_not_exist.faiss")
diff --git a/tests/test_integrations_llm_azure.py b/tests/test_integrations_llm_azure.py
deleted file mode 100644
index 8c1592e..0000000
--- a/tests/test_integrations_llm_azure.py
+++ /dev/null
@@ -1,53 +0,0 @@
-"""Tests for AzureOpenAILLM integration."""
-
-from __future__ import annotations
-
-from unittest.mock import MagicMock, patch
-
-import pytest
-
-openai = pytest.importorskip("openai", reason="openai not installed")
-
-from lang2sql.integrations.llm.azure_ import AzureOpenAILLM
-
-
-def _make_llm() -> AzureOpenAILLM:
- return AzureOpenAILLM(
- azure_deployment="gpt-4o",
- azure_endpoint="https://test.openai.azure.com/",
- api_key="test-key",
- )
-
-
-def test_azure_llm_invoke_returns_string():
- mock_resp = MagicMock()
- mock_resp.choices[0].message.content = "SELECT 1"
-
- with patch("openai.AzureOpenAI") as MockClient:
- instance = MockClient.return_value
- instance.chat.completions.create.return_value = mock_resp
-
- llm = _make_llm()
- llm._client = instance
- result = llm.invoke([{"role": "user", "content": "hello"}])
-
- assert result == "SELECT 1"
-
-
-def test_azure_llm_missing_dependency_raises():
- import sys
-
- with patch.dict(sys.modules, {"openai": None}):
- # Re-import to trigger the ImportError guard
- import importlib
-
- import lang2sql.integrations.llm.azure_ as mod
-
- importlib.reload(mod)
- with pytest.raises(Exception):
- mod.AzureOpenAILLM(
- azure_deployment="x",
- azure_endpoint="https://x.openai.azure.com/",
- )
- # Reload back
- importlib.reload(mod)
diff --git a/tests/test_integrations_llm_ollama.py b/tests/test_integrations_llm_ollama.py
deleted file mode 100644
index 95adf17..0000000
--- a/tests/test_integrations_llm_ollama.py
+++ /dev/null
@@ -1,26 +0,0 @@
-"""Tests for OllamaLLM integration."""
-
-from __future__ import annotations
-
-from unittest.mock import MagicMock, patch
-
-import pytest
-
-ollama = pytest.importorskip("ollama", reason="ollama not installed")
-
-from lang2sql.integrations.llm.ollama_ import OllamaLLM
-
-
-def test_ollama_llm_invoke_returns_string():
- mock_resp = MagicMock()
- mock_resp.message.content = "SELECT 1"
-
- with patch("ollama.Client") as MockClient:
- instance = MockClient.return_value
- instance.chat.return_value = mock_resp
-
- llm = OllamaLLM(model="llama3", base_url="http://localhost:11434")
- llm._client = instance
- result = llm.invoke([{"role": "user", "content": "hello"}])
-
- assert result == "SELECT 1"
diff --git a/tests/test_integrations_pgvector_vectorstore.py b/tests/test_integrations_pgvector_vectorstore.py
deleted file mode 100644
index 789872b..0000000
--- a/tests/test_integrations_pgvector_vectorstore.py
+++ /dev/null
@@ -1,144 +0,0 @@
-"""
-PGVectorStore integration tests.
-
-Requires a live PostgreSQL instance with pgvector installed.
-Skipped when TEST_POSTGRES_URL env variable is not set.
-
-Example:
- TEST_POSTGRES_URL="postgresql://postgres:postgres@localhost:5432/test" \\
- pytest tests/test_integrations_pgvector_vectorstore.py -v
-"""
-
-import os
-import pytest
-from uuid import uuid4
-
-pytestmark = pytest.mark.skipif(
- not os.getenv("TEST_POSTGRES_URL"),
- reason="TEST_POSTGRES_URL not set — skipping pgvector integration tests",
-)
-
-from lang2sql.integrations.vectorstore.pgvector_ import PGVectorStore
-
-# ── helpers ──────────────────────────────────────────────────────────────────
-
-
-def _unique_table() -> str:
- return f"test_{uuid4().hex[:8]}"
-
-
-def _make_store(table_name: str) -> PGVectorStore:
- url = os.environ["TEST_POSTGRES_URL"]
- return PGVectorStore(connection=url, table_name=table_name)
-
-
-def _drop_table(store: PGVectorStore, table_name: str) -> None:
- with store._conn.cursor() as cur:
- cur.execute(f"DROP TABLE IF EXISTS {table_name};")
- store._conn.commit()
-
-
-# ── tests ─────────────────────────────────────────────────────────────────────
-
-
-def test_pgvector_upsert_and_search():
- """Query vector returns its own id."""
- table = _unique_table()
- store = _make_store(table)
- try:
- store.upsert(["a"], [[1.0, 0.0, 0.0, 0.0]])
- results = store.search([1.0, 0.0, 0.0, 0.0], k=1)
- assert len(results) == 1
- assert results[0][0] == "a"
- finally:
- _drop_table(store, table)
- store._conn.close()
-
-
-def test_pgvector_upsert_is_idempotent():
- """Same id upserted twice → exactly one row in DB."""
- table = _unique_table()
- store = _make_store(table)
- try:
- store.upsert(["a"], [[1.0, 0.0, 0.0, 0.0]])
- store.upsert(["a"], [[0.5, 0.5, 0.0, 0.0]]) # overwrite same id
-
- with store._conn.cursor() as cur:
- cur.execute(f"SELECT COUNT(*) FROM {table} WHERE id = 'a';")
- count = cur.fetchone()[0]
- assert count == 1
- finally:
- _drop_table(store, table)
- store._conn.close()
-
-
-def test_pgvector_search_score_in_range():
- """Score ∈ [-1, 1]."""
- table = _unique_table()
- store = _make_store(table)
- try:
- store.upsert(
- ["a", "b", "c"],
- [
- [1.0, 0.0, 0.0, 0.0],
- [0.0, 1.0, 0.0, 0.0],
- [0.0, 0.0, 1.0, 0.0],
- ],
- )
- results = store.search([1.0, 0.0, 0.0, 0.0], k=3)
- for _, score in results:
- assert -1.0 <= score <= 1.0 + 1e-6
- finally:
- _drop_table(store, table)
- store._conn.close()
-
-
-def test_pgvector_search_respects_k():
- """len(results) <= k."""
- table = _unique_table()
- store = _make_store(table)
- try:
- store.upsert(
- ["a", "b", "c", "d"],
- [
- [1.0, 0.0, 0.0, 0.0],
- [0.0, 1.0, 0.0, 0.0],
- [0.0, 0.0, 1.0, 0.0],
- [0.0, 0.0, 0.0, 1.0],
- ],
- )
- results = store.search([1.0, 0.0, 0.0, 0.0], k=2)
- assert len(results) <= 2
- finally:
- _drop_table(store, table)
- store._conn.close()
-
-
-def test_pgvector_table_created_automatically():
- """Table exists in information_schema after first upsert()."""
- table = _unique_table()
- store = _make_store(table)
- try:
- store.upsert(["x"], [[1.0, 0.0]])
- with store._conn.cursor() as cur:
- cur.execute(
- "SELECT COUNT(*) FROM information_schema.tables "
- "WHERE table_name = %s;",
- (table,),
- )
- count = cur.fetchone()[0]
- assert count == 1
- finally:
- _drop_table(store, table)
- store._conn.close()
-
-
-def test_pgvector_search_empty_store_returns_empty():
- """[] before any upsert()."""
- table = _unique_table()
- store = _make_store(table)
- try:
- results = store.search([1.0, 0.0, 0.0, 0.0], k=5)
- assert results == []
- finally:
- store._conn.close()
diff --git a/tests/test_integrations_sqlalchemy_explorer.py b/tests/test_integrations_sqlalchemy_explorer.py
deleted file mode 100644
index 60dd9b2..0000000
--- a/tests/test_integrations_sqlalchemy_explorer.py
+++ /dev/null
@@ -1,149 +0,0 @@
-"""Tests for SQLAlchemyExplorer (Phase A1)."""
-
-from __future__ import annotations
-
-import pytest
-from sqlalchemy import create_engine, text
-
-# ---------------------------------------------------------------------------
-# Fixture: SQLite in-memory DB with FK schema
-# ---------------------------------------------------------------------------
-
-
-@pytest.fixture()
-def engine():
- eng = create_engine("sqlite:///:memory:")
- with eng.connect() as conn:
- conn.execute(text("""
- CREATE TABLE customers (
- id INTEGER PRIMARY KEY,
- name TEXT NOT NULL,
- email TEXT UNIQUE
- )
- """))
- conn.execute(text("""
- CREATE TABLE orders (
- id INTEGER PRIMARY KEY,
- customer_id INTEGER NOT NULL REFERENCES customers(id),
- amount REAL,
- status TEXT DEFAULT 'pending'
- )
- """))
- conn.execute(
- text("INSERT INTO customers VALUES (1, 'Alice', 'alice@example.com')")
- )
- conn.execute(text("INSERT INTO customers VALUES (2, 'Bob', 'bob@example.com')"))
- conn.execute(text("INSERT INTO orders VALUES (1, 1, 99.9, 'shipped')"))
- conn.execute(text("INSERT INTO orders VALUES (2, 2, 42.0, 'pending')"))
- conn.commit()
- return eng
-
-
-@pytest.fixture()
-def explorer(engine):
- from lang2sql.integrations.db.sqlalchemy_ import SQLAlchemyExplorer
-
- return SQLAlchemyExplorer.from_engine(engine)
-
-
-# ---------------------------------------------------------------------------
-# Tests
-# ---------------------------------------------------------------------------
-
-
-def test_list_tables(explorer):
- tables = explorer.list_tables()
- assert set(tables) == {"customers", "orders"}
-
-
-def test_get_ddl_sqlite(explorer):
- ddl = explorer.get_ddl("orders")
- # 원본 DDL에 REFERENCES 절 포함 확인
- assert "REFERENCES" in ddl
- assert "customer_id" in ddl
-
-
-def test_get_ddl_contains_all_columns(explorer):
- ddl = explorer.get_ddl("customers")
- for col in ("id", "name", "email"):
- assert col in ddl
-
-
-def test_sample_data(explorer):
- rows = explorer.sample_data("customers", limit=1)
- assert len(rows) == 1
- assert "name" in rows[0]
- assert "email" in rows[0]
-
-
-def test_sample_data_default_limit(explorer):
- rows = explorer.sample_data("customers")
- # 2행 삽입, limit=5(기본값) → 모두 반환
- assert len(rows) == 2
-
-
-def test_sample_data_empty_table(engine):
- from lang2sql.integrations.db.sqlalchemy_ import SQLAlchemyExplorer
-
- with engine.connect() as conn:
- conn.execute(text("CREATE TABLE empty_tbl (x INTEGER)"))
- conn.commit()
-
- exp = SQLAlchemyExplorer.from_engine(engine)
- assert exp.sample_data("empty_tbl") == []
-
-
-def test_execute_read_only_select(explorer):
- rows = explorer.execute_read_only("SELECT id, name FROM customers ORDER BY id")
- assert len(rows) == 2
- assert rows[0]["name"] == "Alice"
-
-
-def test_execute_read_only_rejects_insert(explorer):
- with pytest.raises(ValueError, match="Write operations not allowed"):
- explorer.execute_read_only(
- "INSERT INTO customers VALUES (3, 'Eve', 'eve@x.com')"
- )
-
-
-def test_execute_read_only_rejects_drop(explorer):
- with pytest.raises(ValueError, match="Write operations not allowed"):
- explorer.execute_read_only("DROP TABLE customers")
-
-
-def test_execute_read_only_rejects_cte_delete(explorer):
- # SQLite는 CTE + DELETE를 지원하지 않으므로 rollback만 검증
- # prefix guard는 통과하지만 실제 변경이 없음을 확인
- initial = explorer.execute_read_only("SELECT COUNT(*) as cnt FROM customers")
- initial_count = initial[0]["cnt"]
-
- # rollback-always 검증: SELECT는 정상 동작, 데이터 변경 없음
- rows = explorer.execute_read_only("SELECT * FROM customers WHERE id = 1")
- assert len(rows) == 1
-
- after = explorer.execute_read_only("SELECT COUNT(*) as cnt FROM customers")
- assert after[0]["cnt"] == initial_count
-
-
-def test_from_engine_shares_data(engine):
- from lang2sql.integrations.db.sqlalchemy_ import SQLAlchemyExplorer
-
- exp1 = SQLAlchemyExplorer.from_engine(engine)
- exp2 = SQLAlchemyExplorer.from_engine(engine)
-
- rows1 = exp1.sample_data("customers")
- rows2 = exp2.sample_data("customers")
- assert rows1 == rows2
-
-
-def test_integration_with_sqlalchemydb(engine):
- from lang2sql.integrations.db.sqlalchemy_ import SQLAlchemyDB, SQLAlchemyExplorer
-
- # SQLAlchemyDB와 같은 engine을 SQLAlchemyExplorer가 공유
- explorer = SQLAlchemyExplorer.from_engine(engine)
-
- tables = explorer.list_tables()
- assert "customers" in tables
-
- ddl = explorer.get_ddl("customers")
- assert "id" in ddl
diff --git a/tests/test_memory.py b/tests/test_memory.py
new file mode 100644
index 0000000..add301b
--- /dev/null
+++ b/tests/test_memory.py
@@ -0,0 +1,77 @@
+"""Memory V1 — service round trip and inject-all behaviour (★②)."""
+
+from __future__ import annotations
+
+import asyncio
+
+from lang2sql.memory import (
+ InMemoryStore,
+ InjectAllRecall,
+ ManualExtractor,
+ MemoryService,
+)
+
+
+def _service() -> MemoryService:
+ return MemoryService(InMemoryStore(), InjectAllRecall(), ManualExtractor())
+
+
+def test_remember_then_recall_round_trip() -> None:
+ svc = _service()
+
+ async def run() -> None:
+ fact = await svc.remember("u1", "revenue excludes cancelled orders")
+ assert fact.id
+ assert fact.source == "manual"
+ assert fact.ts > 0
+
+ facts = await svc.recall("u1", "anything")
+ assert [f.text for f in facts] == ["revenue excludes cancelled orders"]
+
+ asyncio.run(run())
+
+
+def test_inject_all_returns_every_fact_for_owner() -> None:
+ svc = _service()
+
+ async def run() -> None:
+ await svc.remember("u1", "fact a")
+ await svc.remember("u1", "fact b")
+ await svc.remember("u2", "other owner")
+
+ facts = await svc.recall("u1", "ignored query")
+ assert {f.text for f in facts} == {"fact a", "fact b"}
+
+ asyncio.run(run())
+
+
+def test_recall_empty_owner_is_empty_and_renders_blank() -> None:
+ svc = _service()
+
+ async def run() -> None:
+ facts = await svc.recall("nobody", "q")
+ assert facts == []
+ assert svc.render(facts) == ""
+
+ asyncio.run(run())
+
+
+def test_render_produces_markdown_block() -> None:
+ svc = _service()
+
+ async def run() -> None:
+ await svc.remember("u1", "fact a")
+ rendered = svc.render(await svc.recall("u1", "q"))
+ assert "## Remembered facts" in rendered
+ assert "- fact a" in rendered
+
+ asyncio.run(run())
+
+
+def test_manual_extractor_yields_nothing() -> None:
+ extractor = ManualExtractor()
+
+ async def run() -> None:
+ assert await extractor.extract("u1", []) == []
+
+ asyncio.run(run())
diff --git a/tests/test_persistence.py b/tests/test_persistence.py
new file mode 100644
index 0000000..ea8dc79
--- /dev/null
+++ b/tests/test_persistence.py
@@ -0,0 +1,156 @@
+"""Persistence tests — SqliteSemanticStore durability + Fernet secrets (★④ §4.1).
+
+Two guarantees under test: (1) semantic definitions and secrets survive a fresh
+store instance pointed at the same sqlite file, and (2) secrets are stored as
+real Fernet ciphertext, not recoverable without the key.
+"""
+
+from __future__ import annotations
+
+import asyncio
+import os
+
+import pytest
+from cryptography.fernet import Fernet, InvalidToken
+
+from lang2sql.adapters.storage.sqlite_semantic import SqliteSemanticStore
+from lang2sql.adapters.storage.sqlite_store import SqliteStore
+from lang2sql.core.identity import Identity, Scope, ScopeLevel
+from lang2sql.semantic.types import Metric, SemanticKind
+from lang2sql.tenancy.encrypted_secrets import EncryptedSecrets
+from lang2sql.tenancy.scope_resolver import ScopeResolver
+
+
+def test_semantic_store_add_entries_at_round_trip() -> None:
+ store = SqliteSemanticStore()
+ scope = Scope(ScopeLevel.CHANNEL, "c1")
+ store.add(scope, Metric("revenue", "sum of order totals", created_by="u1"))
+
+ entries = store.entries_at(scope)
+ assert len(entries) == 1
+ assert entries[0].name == "revenue"
+ assert entries[0].kind is SemanticKind.METRIC
+ assert entries[0].definition == "sum of order totals"
+ assert entries[0].created_by == "u1"
+ # created_at is preserved, not regenerated.
+ assert entries[0].created_at != ""
+
+ # Re-adding the same name at the same scope replaces it.
+ store.add(scope, Metric("revenue", "net revenue"))
+ entries = store.entries_at(scope)
+ assert len(entries) == 1
+ assert entries[0].definition == "net revenue"
+
+ # A different scope is isolated.
+ assert store.entries_at(Scope(ScopeLevel.CHANNEL, "other")) == []
+
+
+def test_semantic_store_survives_new_instance_on_same_file(tmp_path) -> None:
+ db = str(tmp_path / "semantic.db")
+ scope = Scope(ScopeLevel.GUILD, "g1")
+
+ writer = SqliteSemanticStore(db)
+ writer.add(scope, Metric("aov", "avg order value", source_id="doc-7"))
+ created_at = writer.entries_at(scope)[0].created_at
+ writer.close()
+
+ # A fresh instance on the same path sees the persisted definition.
+ reader = SqliteSemanticStore(db)
+ entries = reader.entries_at(scope)
+ assert len(entries) == 1
+ assert entries[0].name == "aov"
+ assert entries[0].definition == "avg order value"
+ assert entries[0].source_id == "doc-7"
+ assert entries[0].created_at == created_at
+ reader.close()
+
+
+def test_scope_resolver_effective_layer_over_sqlite_store() -> None:
+ store = SqliteSemanticStore()
+ # Narrow (channel) overrides wide (guild) for the same name.
+ store.add(Scope(ScopeLevel.GUILD, "g1"), Metric("revenue", "guild-wide def"))
+ store.add(Scope(ScopeLevel.CHANNEL, "c1"), Metric("revenue", "channel def"))
+ store.add(Scope(ScopeLevel.GUILD, "g1"), Metric("churn", "guild churn"))
+
+ resolver = ScopeResolver(store)
+ identity = Identity(user_id="u1", guild_id="g1", channel_id="c1")
+ layer = asyncio.run(resolver.effective_layer(identity))
+
+ by_name = {e.name: e.definition for e in layer.entries}
+ assert by_name["revenue"] == "channel def" # most specific wins
+ assert by_name["churn"] == "guild churn" # inherited from guild
+
+
+def test_encrypted_secrets_round_trip_and_ciphertext(tmp_path) -> None:
+ # Explicit key so the test is independent of env / generated state.
+ key = Fernet.generate_key()
+ store = SqliteStore()
+ secrets = EncryptedSecrets(store, key=key)
+
+ assert asyncio.run(secrets.get("guild:1", "dsn")) is None
+ asyncio.run(secrets.set("guild:1", "dsn", "postgresql://u:p@host/db"))
+ assert asyncio.run(secrets.get("guild:1", "dsn")) == "postgresql://u:p@host/db"
+
+ # What actually lands in kv is a Fernet token, never the plaintext.
+ blob = store.kv_get("guild:1", "dsn")
+ assert blob is not None
+ assert "postgresql" not in blob
+ assert blob != "postgresql://u:p@host/db"
+ # It decrypts only with the right key.
+ assert Fernet(key).decrypt(blob.encode("ascii")).decode() == "postgresql://u:p@host/db"
+
+ asyncio.run(secrets.delete("guild:1", "dsn"))
+ assert asyncio.run(secrets.get("guild:1", "dsn")) is None
+
+
+def test_encrypted_secrets_wrong_key_fails() -> None:
+ store = SqliteStore()
+ asyncio.run(EncryptedSecrets(store, key=Fernet.generate_key()).set("s", "k", "v"))
+
+ # A different key cannot decrypt the stored token.
+ attacker = EncryptedSecrets(store, key=Fernet.generate_key())
+ with pytest.raises(InvalidToken):
+ asyncio.run(attacker.get("s", "k"))
+
+
+def test_encrypted_secrets_survive_new_instance_via_persisted_key(tmp_path) -> None:
+ db = str(tmp_path / "secrets.db")
+ # No env key -> a key is generated and persisted in the kv table.
+ saved = os.environ.pop("LANG2SQL_SECRET_KEY", None)
+ try:
+ store = SqliteStore(db)
+ asyncio.run(EncryptedSecrets(store).set("guild:1", "dsn", "postgresql://x"))
+ store.close()
+
+ # A fresh store + secrets on the same file reuses the persisted key.
+ reopened = SqliteStore(db)
+ secrets = EncryptedSecrets(reopened)
+ assert asyncio.run(secrets.get("guild:1", "dsn")) == "postgresql://x"
+ reopened.close()
+ finally:
+ if saved is not None:
+ os.environ["LANG2SQL_SECRET_KEY"] = saved
+
+
+def test_concierge_path_persists_definitions_and_secrets(tmp_path) -> None:
+ from lang2sql.adapters.llm.fake import FakeLLM
+ from lang2sql.tenancy.concierge import ContextConcierge
+
+ db = str(tmp_path / "concierge.db")
+ saved = os.environ.pop("LANG2SQL_SECRET_KEY", None)
+ try:
+ c1 = ContextConcierge(path=db, llm=FakeLLM())
+ scope = Scope(ScopeLevel.CHANNEL, "c1")
+ asyncio.run(c1.scope_resolver.define(scope, Metric("revenue", "sum totals")))
+ asyncio.run(c1.secrets.set("guild:1", "dsn", "postgresql://secret"))
+ c1.store.close()
+ c1.scope_resolver.store.close()
+
+ c2 = ContextConcierge(path=db, llm=FakeLLM())
+ assert [e.name for e in asyncio.run(c2.scope_resolver.entries_at(scope))] == [
+ "revenue"
+ ]
+ assert asyncio.run(c2.secrets.get("guild:1", "dsn")) == "postgresql://secret"
+ finally:
+ if saved is not None:
+ os.environ["LANG2SQL_SECRET_KEY"] = saved
diff --git a/tests/test_safety.py b/tests/test_safety.py
new file mode 100644
index 0000000..669f9db
--- /dev/null
+++ b/tests/test_safety.py
@@ -0,0 +1,125 @@
+"""V1 safety pipeline regressions — the 12 CI-gate cases from §4.5.
+
+Gate-time vs exec-time distinction
+----------------------------------
+The Whitelist + Timeout pipeline only decides whether SQL is *allowed to run*.
+It does NOT execute SQL, so anything whose failure surfaces only when the query
+actually runs against the DB is an EXECUTION-time concern and PASSes the gate:
+
+ * #6 SELECT * FROM nonexistent -> PG raises at run time (gate PASSes)
+ * #7 SELECT pg_sleep(60) -> bounded by ctx.timeout_seconds at run time
+ * #8 SELECT * FROM huge_table -> truncated by row_limit at run time
+
+The Timeout layer is itself an exec-config layer: it sets ctx.timeout_seconds
+and PASSes; it never inspects SQL for slow constructs. So #7's protection shows
+up as "PASS + timeout configured", not as a BLOCK.
+
+Only the statement-shape violations (#1-5, #12) BLOCK; #9/#10/#11 PASS.
+"""
+
+from __future__ import annotations
+
+from lang2sql.core.ports.safety import SafetyContext, Verdict
+from lang2sql.safety import SafetyPipeline
+
+
+def _verdict(sql: str) -> Verdict:
+ pipeline = SafetyPipeline()
+ return pipeline.evaluate(sql, SafetyContext()).verdict
+
+
+def _decision(sql: str):
+ pipeline = SafetyPipeline()
+ return pipeline.evaluate(sql, SafetyContext())
+
+
+# --- BLOCK cases (#1-5, #12) -------------------------------------------------
+
+
+def test_case_01_drop_table_blocks():
+ assert _verdict("DROP TABLE users") is Verdict.BLOCK
+
+
+def test_case_02_multi_statement_blocks():
+ assert _verdict("; DELETE FROM t; --") is Verdict.BLOCK
+
+
+def test_case_03_insert_blocks():
+ assert _verdict("INSERT INTO t VALUES (1)") is Verdict.BLOCK
+
+
+def test_case_04_update_blocks():
+ assert _verdict("UPDATE t SET x=1") is Verdict.BLOCK
+
+
+def test_case_05_cte_insert_fail_closed_blocks():
+ # WITH starts the statement but an INSERT keyword lurks in the CTE body.
+ sql = "WITH x AS (INSERT INTO t VALUES (1)) SELECT * FROM x"
+ decision = _decision(sql)
+ assert decision.verdict is Verdict.BLOCK
+ assert "INSERT" in decision.reason
+
+
+def test_case_12_empty_string_parse_error_blocks():
+ decision = _decision("")
+ assert decision.verdict is Verdict.BLOCK
+ assert decision.reason == "parse_error"
+
+
+# --- EXECUTION-time concerns: PASS the gate (#6, #7, #8) ---------------------
+
+
+def test_case_06_nonexistent_table_passes_gate():
+ # Resolution failure is a run-time PG error, not a gate decision.
+ assert _verdict("SELECT * FROM nonexistent") is Verdict.PASS
+
+
+def test_case_07_pg_sleep_passes_gate_with_timeout_configured():
+ sql = "SELECT pg_sleep(60)"
+ ctx = SafetyContext()
+ ctx.timeout_seconds = 0 # simulate "unset" to prove the layer clamps it
+ decision = SafetyPipeline().evaluate(sql, ctx)
+ assert decision.verdict is Verdict.PASS
+ # Timeout layer must have ensured a positive bound for run-time enforcement.
+ assert ctx.timeout_seconds == 30
+
+
+def test_case_08_huge_table_passes_gate():
+ # row_limit truncation happens at execution time, not at the gate.
+ assert _verdict("SELECT * FROM huge_table") is Verdict.PASS
+
+
+# --- PASS cases (#9, #10, #11) -----------------------------------------------
+
+
+def test_case_09_select_one_passes():
+ assert _verdict("SELECT 1") is Verdict.PASS
+
+
+def test_case_10_cte_select_passes():
+ assert _verdict("WITH a AS (SELECT 1) SELECT * FROM a") is Verdict.PASS
+
+
+def test_case_11_explain_select_passes():
+ assert _verdict("EXPLAIN SELECT 1") is Verdict.PASS
+
+
+# --- Extra guards (foreshadow V1.5; verify V1 fail-closed today) -------------
+
+
+def test_explain_analyze_delete_blocks():
+ # §4.5 notes EXPLAIN ANALYZE DELETE as a V1.5 regression; V1's fail-closed
+ # keyword scan + leading-keyword check already blocks it.
+ assert _verdict("EXPLAIN ANALYZE DELETE FROM t") is Verdict.BLOCK
+
+
+def test_default_timeout_set_on_pass():
+ ctx = SafetyContext()
+ SafetyPipeline().evaluate("SELECT 1", ctx)
+ assert ctx.timeout_seconds == 30
+
+
+def test_pipeline_exposes_default_layers():
+ pipeline = SafetyPipeline()
+ names = [layer.name for layer in pipeline.layers]
+ assert names == ["whitelist", "timeout"]
diff --git a/tests/test_semantic.py b/tests/test_semantic.py
new file mode 100644
index 0000000..acf0f0c
--- /dev/null
+++ b/tests/test_semantic.py
@@ -0,0 +1,115 @@
+"""Tests for the semantic federation layer (★④).
+
+Plain functions named ``test_*`` — runnable without pytest via the validation
+one-liner in the spawn brief.
+"""
+
+from __future__ import annotations
+
+import asyncio
+
+from lang2sql.core.identity import Identity, Scope, ScopeLevel
+from lang2sql.semantic import (
+ Metric,
+ SemanticEntry,
+ SemanticKind,
+ SemanticLayer,
+ SemanticStore,
+ merge_scoped,
+)
+from lang2sql.tenancy.scope_resolver import ScopeResolver
+
+
+def test_render_empty_is_blank() -> None:
+ assert SemanticLayer().render() == ""
+
+
+def test_render_lists_entries() -> None:
+ layer = SemanticLayer(
+ [
+ SemanticEntry(SemanticKind.METRIC, "active_user", "30d login"),
+ SemanticEntry(
+ SemanticKind.DIMENSION, "region", "billing country", applies_to="users"
+ ),
+ ]
+ )
+ rendered = layer.render()
+ assert "**active_user** [metric]: 30d login" in rendered
+ assert "**region** [dimension]: billing country (applies to users)" in rendered
+ # one bullet line per entry
+ assert len(rendered.splitlines()) == 2
+ assert all(line.startswith("- ") for line in rendered.splitlines())
+
+
+def test_layer_add_replaces_same_name() -> None:
+ layer = SemanticLayer([SemanticEntry(SemanticKind.METRIC, "rev", "gross")])
+ layer.add(SemanticEntry(SemanticKind.METRIC, "rev", "net"))
+ assert len(layer.entries) == 1
+ assert layer.lookup("rev").definition == "net"
+
+
+def test_scoped_merge_narrow_wins() -> None:
+ """active_user defined at channel must override the guild definition."""
+ guild = Scope(ScopeLevel.GUILD, "g1")
+ channel = Scope(ScopeLevel.CHANNEL, "c1")
+ merged = merge_scoped(
+ [
+ (channel, [Metric(name="active_user", definition="7d core action")]),
+ (guild, [Metric(name="active_user", definition="30d login")]),
+ ]
+ )
+ assert merged.lookup("active_user").definition == "7d core action"
+
+
+def test_scoped_merge_wider_fills_gaps() -> None:
+ guild = Scope(ScopeLevel.GUILD, "g1")
+ channel = Scope(ScopeLevel.CHANNEL, "c1")
+ merged = merge_scoped(
+ [
+ (channel, [Metric(name="active_user", definition="7d")]),
+ (guild, [Metric(name="revenue", definition="net")]),
+ ]
+ )
+ assert merged.lookup("active_user").definition == "7d"
+ assert merged.lookup("revenue").definition == "net"
+
+
+def test_resolver_effective_layer_guild_channel() -> None:
+ store = SemanticStore()
+ resolver = ScopeResolver(store)
+ identity = Identity(user_id="u1", guild_id="g1", channel_id="c1")
+
+ async def scenario() -> SemanticLayer:
+ await resolver.define(
+ Scope(ScopeLevel.GUILD, "g1"),
+ Metric(name="active_user", definition="30d login"),
+ )
+ await resolver.define(
+ Scope(ScopeLevel.GUILD, "g1"),
+ Metric(name="revenue", definition="gross"),
+ )
+ # channel override of active_user only
+ await resolver.define(
+ Scope(ScopeLevel.CHANNEL, "c1"),
+ Metric(name="active_user", definition="7d core action"),
+ )
+ return await resolver.effective_layer(identity)
+
+ layer = asyncio.run(scenario())
+ assert layer.lookup("active_user").definition == "7d core action" # channel wins
+ assert layer.lookup("revenue").definition == "gross" # inherited from guild
+
+
+def test_resolver_entries_at_no_inheritance() -> None:
+ store = SemanticStore()
+ resolver = ScopeResolver(store)
+
+ async def scenario() -> list[SemanticEntry]:
+ await resolver.define(
+ Scope(ScopeLevel.GUILD, "g1"),
+ Metric(name="revenue", definition="gross"),
+ )
+ # channel has nothing of its own
+ return await resolver.entries_at(Scope(ScopeLevel.CHANNEL, "c1"))
+
+ assert asyncio.run(scenario()) == []
diff --git a/tests/test_tenancy.py b/tests/test_tenancy.py
new file mode 100644
index 0000000..be148ac
--- /dev/null
+++ b/tests/test_tenancy.py
@@ -0,0 +1,79 @@
+"""Tenancy tests — EncryptedSecrets round trip + ContextConcierge wiring.
+
+No network: with no OPENAI_API_KEY the concierge falls back to FakeLLM.
+"""
+
+from __future__ import annotations
+
+import asyncio
+import os
+
+from lang2sql.adapters.llm.fake import FakeLLM
+from lang2sql.adapters.storage.sqlite_store import SqliteStore
+from lang2sql.core.identity import Identity
+from lang2sql.core.types import Message, Role
+from lang2sql.harness.context import HarnessContext
+from lang2sql.harness.session import Session
+from lang2sql.tenancy.concierge import ContextConcierge
+from lang2sql.tenancy.encrypted_secrets import EncryptedSecrets
+
+
+def test_encrypted_secrets_round_trip() -> None:
+ store = SqliteStore()
+ secrets = EncryptedSecrets(store)
+
+ assert asyncio.run(secrets.get("guild:1", "dsn")) is None
+ asyncio.run(secrets.set("guild:1", "dsn", "postgresql://u:p@host/db"))
+ assert asyncio.run(secrets.get("guild:1", "dsn")) == "postgresql://u:p@host/db"
+
+ # Stored value is obfuscated, not plaintext.
+ assert store.kv_get("guild:1", "dsn") != "postgresql://u:p@host/db"
+
+ asyncio.run(secrets.set("guild:1", "dsn", "postgresql://new")) # overwrite
+ assert asyncio.run(secrets.get("guild:1", "dsn")) == "postgresql://new"
+
+ asyncio.run(secrets.delete("guild:1", "dsn"))
+ assert asyncio.run(secrets.get("guild:1", "dsn")) is None
+
+
+def test_build_context_populates_llm_and_session() -> None:
+ saved = os.environ.pop("OPENAI_API_KEY", None)
+ try:
+ concierge = ContextConcierge()
+ identity = Identity(user_id="u1", guild_id="g", channel_id="c")
+ ctx = asyncio.run(concierge.build_context(identity, user_text="how many orders?"))
+
+ assert isinstance(ctx, HarnessContext)
+ assert isinstance(ctx.llm, FakeLLM) # no key → fallback
+ assert isinstance(ctx.session, Session)
+ assert ctx.session.identity == identity
+ assert ctx.explorer is not None
+ assert ctx.safety is not None
+ assert ctx.scope_resolver is not None
+ assert ctx.audit is not None
+ finally:
+ if saved is not None:
+ os.environ["OPENAI_API_KEY"] = saved
+
+
+def test_build_context_restores_saved_session() -> None:
+ store = SqliteStore()
+ identity = Identity(user_id="u1", channel_id="c", thread_id="t")
+ prior = Session(identity=identity)
+ prior.add(Message(role=Role.USER, content="earlier turn"))
+ asyncio.run(store.save(identity.session_key(), prior))
+
+ concierge = ContextConcierge(store=store, llm=FakeLLM())
+ ctx = asyncio.run(concierge.build_context(identity))
+
+ assert len(ctx.session.transcript) == 1
+ assert ctx.session.transcript[0].content == "earlier turn"
+
+
+def test_injected_overrides_are_used() -> None:
+ store = SqliteStore()
+ fake = FakeLLM()
+ concierge = ContextConcierge(store=store, llm=fake)
+ ctx = asyncio.run(concierge.build_context(Identity(user_id="u1")))
+ assert ctx.llm is fake
+ assert ctx.audit is store
diff --git a/utils/data/README.md b/utils/data/README.md
deleted file mode 100644
index f9294c4..0000000
--- a/utils/data/README.md
+++ /dev/null
@@ -1,449 +0,0 @@
-# utils/data 패키지
-
-DataHub와의 상호작용을 위한 데이터 관련 유틸리티 패키지입니다.
-
-## 디렉토리 구조
-
-```
-utils/data/
-├── __pycache__/
-├── datahub_services/
-│ ├── __pycache__/
-│ ├── base_client.py # 기본 클라이언트
-│ ├── glossary_service.py # 용어집 서비스
-│ ├── metadata_service.py # 메타데이터 서비스
-│ ├── query_service.py # 쿼리 서비스
-│ ├── __init__.py # 패키지 초기화 및 exports
-│ └── README.md # 서비스 상세 문서
-├── datahub_source.py # 통합 메타데이터 페처
-└── queries.py # GraphQL 쿼리 모음
-```
-
-## 파일별 상세 내용
-
-### `datahub_source.py`
-
-DataHub 메타데이터 페처 - 리팩토링된 버전으로 기존 `DatahubMetadataFetcher`의 모든 기능을 유지하면서 내부적으로는 분리된 서비스 모듈들을 사용합니다.
-
-**클래스:**
-- `DatahubMetadataFetcher`: 기존 인터페이스를 유지하는 통합 페처
-
-**초기화:**
-```python
-from utils.data.datahub_source import DatahubMetadataFetcher
-
-fetcher = DatahubMetadataFetcher(gms_server="http://localhost:8080", extra_headers={})
-```
-
-**주요 메서드:**
-
-#### 메타데이터 관련 메서드
-
-##### `get_urns()`
-필터를 적용하여 데이터셋의 URN 목록을 가져옵니다.
-
-**반환값:**
-- URN 목록
-
-##### `get_table_name(urn)`
-URN에 대한 테이블 이름을 가져옵니다.
-
-**파라미터:**
-- `urn` (str): 데이터셋 URN
-
-**반환값:**
-- `str`: 테이블 이름 (database.table 형태) 또는 None
-
-##### `get_table_description(urn)`
-URN에 대한 테이블 설명을 가져옵니다.
-
-**파라미터:**
-- `urn` (str): 데이터셋 URN
-
-**반환값:**
-- `str`: 테이블 설명 또는 None
-
-##### `get_column_names_and_descriptions(urn)`
-URN에 대한 컬럼 이름 및 설명을 가져옵니다.
-
-**파라미터:**
-- `urn` (str): 데이터셋 URN
-
-**반환값:**
-- `list`: 컬럼 정보 리스트 (각 항목: column_name, column_description, column_type)
-
-##### `get_table_lineage(urn, counts=100, direction="DOWNSTREAM", degree_values=None)`
-URN에 대한 DOWNSTREAM/UPSTREAM lineage entity를 가져옵니다.
-
-**파라미터:**
-- `urn` (str): 데이터셋 URN
-- `counts` (int): 가져올 엔티티 수 (기본값: 100)
-- `direction` (str): 리니지 방향 ("DOWNSTREAM" 또는 "UPSTREAM", 기본값: "DOWNSTREAM")
-- `degree_values` (list): 필터링할 degree 값 리스트 (기본값: ["1", "2"])
-
-**반환값:**
-- `tuple`: (urn, lineage_result)
-
-##### `get_column_lineage(urn)`
-URN에 대한 UPSTREAM lineage의 column source를 가져옵니다.
-
-**파라미터:**
-- `urn` (str): 데이터셋 URN
-
-**반환값:**
-- `dict`: 컬럼 리니지 정보
- - `downstream_dataset`: 다운스트림 데이터셋 이름
- - `lineage_by_upstream_dataset`: 업스트림 데이터셋별 컬럼 매핑
-
-##### `min_degree_lineage(lineage_result)`
-lineage 중 최소 degree만 가져옵니다.
-
-**파라미터:**
-- `lineage_result`: `get_table_lineage`의 반환값
-
-**반환값:**
-- `dict`: 테이블별 최소 degree 매핑
-
-##### `build_table_metadata(urn, max_degree=2, sort_by_degree=True)`
-테이블 단위로 통합 메타데이터를 생성합니다.
-
-**파라미터:**
-- `urn` (str): 데이터셋 URN
-- `max_degree` (int): 최대 degree 제한 (기본값: 2)
-- `sort_by_degree` (bool): degree 기준 정렬 여부 (기본값: True)
-
-**반환값:**
-- `dict`: 통합 메타데이터
- - `table_name`: 테이블 이름
- - `description`: 테이블 설명
- - `columns`: 컬럼 정보 리스트
- - `lineage`: 리니지 정보
-
-##### `get_urn_info(urn)`
-특정 URN에 대한 모든 관련 정보를 가져옵니다.
-
-**파라미터:**
-- `urn` (str): 조회할 데이터셋 URN
-
-**반환값:**
-- `dict`: URN에 대한 전체 메타데이터 정보
-
-##### `_print_urn_details(metadata)`
-URN 메타데이터를 보기 좋게 출력하는 내부 함수입니다.
-
-#### 용어집 관련 메서드
-
-##### `get_root_glossary_nodes()`
-DataHub에서 루트 용어집 노드를 가져옵니다.
-
-**반환값:**
-- `dict`: 루트 용어집 노드 정보
-
-##### `get_glossary_node_by_urn(urn)`
-특정 URN의 용어집 노드 및 그 자식 항목을 가져옵니다.
-
-**파라미터:**
-- `urn` (str): 용어집 노드의 URN
-
-**반환값:**
-- `dict`: 용어집 노드 정보와 자식 항목
-
-##### `get_node_basic_info(node, index)`
-용어집 노드의 기본 정보를 딕셔너리로 반환합니다.
-
-**파라미터:**
-- `node` (dict): 용어집 노드 정보
-- `index` (int): 노드의 인덱스
-
-**반환값:**
-- `dict`: 노드의 기본 정보
-
-##### `get_child_entity_info(entity, index)`
-자식 엔티티(용어 또는 노드)의 정보를 딕셔너리로 반환합니다.
-
-**파라미터:**
-- `entity` (dict): 자식 엔티티 정보
-- `index` (int): 엔티티의 인덱스
-
-**반환값:**
-- `dict`: 엔티티 정보
-
-##### `process_node_details(node)`
-노드의 상세 정보를 처리하고 딕셔너리로 반환합니다.
-
-**파라미터:**
-- `node` (dict): 용어집 노드 정보
-
-**반환값:**
-- `dict`: 노드의 상세 정보
-
-##### `process_glossary_nodes(result)`
-용어집 노드 결과를 처리하고 딕셔너리로 반환합니다.
-
-**파라미터:**
-- `result` (dict): API 응답 결과
-
-**반환값:**
-- `dict`: 처리된 용어집 노드 데이터
-
-##### `get_glossary_data()`
-DataHub에서 전체 용어집 데이터를 가져와 처리합니다.
-
-**반환값:**
-- `dict`: 처리된 용어집 데이터
-
-##### `get_glossary_terms_by_urn(dataset_urn)`
-특정 데이터셋 URN의 glossary terms를 조회합니다.
-
-**파라미터:**
-- `dataset_urn` (str): 데이터셋 URN
-
-**반환값:**
-- `dict`: glossary terms 정보
-
-#### 쿼리 관련 메서드
-
-##### `get_queries(start=0, count=10, query="*", filters=None)`
-DataHub에서 쿼리 목록을 가져옵니다.
-
-**파라미터:**
-- `start` (int): 시작 인덱스 (기본값: 0)
-- `count` (int): 반환할 쿼리 수 (기본값: 10)
-- `query` (str): 필터링에 사용할 쿼리 문자열 (기본값: "*")
-- `filters` (list): 추가 필터 (기본값: None)
-
-**반환값:**
-- `dict`: 쿼리 목록 정보
-
-##### `process_queries(result)`
-쿼리 목록 결과를 처리하고 간소화된 형태로 반환합니다.
-
-**파라미터:**
-- `result` (dict): API 응답 결과
-
-**반환값:**
-- `dict`: 처리된 쿼리 목록 데이터
-
-##### `get_query_data(start=0, count=10, query="*", filters=None)`
-DataHub에서 쿼리 목록을 가져와 처리합니다.
-
-**파라미터:**
-- `start` (int): 시작 인덱스 (기본값: 0)
-- `count` (int): 반환할 쿼리 수 (기본값: 10)
-- `query` (str): 필터링에 사용할 쿼리 문자열 (기본값: "*")
-- `filters` (list): 추가 필터 (기본값: None)
-
-**반환값:**
-- `dict`: 처리된 쿼리 목록 데이터
-
-##### `get_queries_by_urn(dataset_urn)`
-특정 데이터셋 URN과 연관된 쿼리들을 조회합니다.
-
-**파라미터:**
-- `dataset_urn` (str): 데이터셋 URN
-
-**반환값:**
-- `dict`: 연관된 쿼리 목록
-
-**의존성:**
-- `utils.data.datahub_services.base_client.DataHubBaseClient`: 기본 클라이언트
-- `utils.data.datahub_services.metadata_service.MetadataService`: 메타데이터 서비스
-- `utils.data.datahub_services.query_service.QueryService`: 쿼리 서비스
-- `utils.data.datahub_services.glossary_service.GlossaryService`: 용어집 서비스
-
-**사용 예시:**
-```python
-from utils.data.datahub_source import DatahubMetadataFetcher
-
-# 페처 초기화
-fetcher = DatahubMetadataFetcher(gms_server="http://localhost:8080")
-
-# 테이블 메타데이터 조회
-urn = "urn:li:dataset:(urn:li:dataPlatform:postgres,db.schema.table,TABLE)"
-metadata = fetcher.get_urn_info(urn)
-
-# 용어집 조회
-glossary_data = fetcher.get_glossary_data()
-
-# 쿼리 조회
-queries = fetcher.get_query_data(start=0, count=10)
-```
-
-**import 되어 사용되는 위치:**
-- `utils/llm/tools/datahub.py`: `from utils.data.datahub_source import DatahubMetadataFetcher`
-
----
-
-### `queries.py`
-
-DataHub GraphQL 쿼리 모음 파일입니다. DataHub GMS 서버와 통신하기 위한 모든 GraphQL 쿼리 문자열을 포함합니다.
-
-**주요 쿼리:**
-
-#### `ROOT_GLOSSARY_NODES_QUERY`
-루트 용어집 노드를 조회하는 쿼리입니다. 4단계 계층 구조까지 포함합니다.
-
-**사용 위치:**
-- `utils.data.datahub_services.glossary_service.GlossaryService.get_root_glossary_nodes()`
-
-#### `GLOSSARY_NODE_QUERY`
-특정 URN의 용어집 노드 상세 정보를 조회하는 쿼리입니다. 자식 노드, 용어, 소유권, 권한 등의 상세 정보를 포함합니다.
-
-**파라미터:**
-- `urn` (str): 용어집 노드 URN
-
-**사용 위치:**
-- `utils.data.datahub_services.glossary_service.GlossaryService.get_glossary_node_by_urn()`
-
-#### `LIST_QUERIES_QUERY`
-쿼리 목록을 조회하는 쿼리입니다. URN, 이름, 설명, SQL 문, 데이터셋 정보 등을 포함합니다.
-
-**파라미터:**
-- `input` (dict): ListQueriesInput
- - `start` (int): 시작 인덱스
- - `count` (int): 반환할 항목 수
- - `query` (str): 검색 쿼리
- - `filters` (list, optional): 추가 필터
-
-**사용 위치:**
-- `utils.data.datahub_services.query_service.QueryService.get_queries()`
-
-#### `QUERIES_BY_URN_QUERY`
-특정 URN과 연관된 쿼리를 조회하는 단순화된 쿼리입니다.
-
-**파라미터:**
-- `input` (dict): ListQueriesInput
-
-**사용 위치:**
-- `utils.data.datahub_services.query_service.QueryService.get_queries_by_urn()`
-
-#### `GLOSSARY_TERMS_BY_URN_QUERY`
-특정 데이터셋 URN의 glossary terms를 조회하는 쿼리입니다.
-
-**파라미터:**
-- `urn` (str): 데이터셋 URN
-
-**사용 위치:**
-- `utils.data.datahub_services.glossary_service.GlossaryService.get_glossary_terms_by_urn()`
-- `utils.data.datahub_services.query_service.QueryService.get_glossary_terms_by_urn()`
-
-**import 되어 사용되는 위치:**
-- `utils.data.datahub_services.glossary_service`: `from utils.data.queries import ...`
-- `utils.data.datahub_services.query_service`: `from utils.data.queries import ...`
-
----
-
-### `datahub_services/`
-
-DataHub와의 상호작용을 위한 서비스 모듈들을 제공하는 서브패키지입니다.
-
-**주요 구성요소:**
-- `base_client.py`: 기본 연결 및 통신
-- `glossary_service.py`: 용어집 서비스
-- `metadata_service.py`: 메타데이터 서비스
-- `query_service.py`: 쿼리 서비스
-- `__init__.py`: 패키지 초기화 및 exports
-
-상세한 문서는 [datahub_services/README.md](datahub_services/README.md)를 참조하세요.
-
----
-
-## 전체 사용 예시
-
-### 예시 1: 기본 사용법
-
-```python
-from utils.data.datahub_source import DatahubMetadataFetcher
-
-# 페처 초기화
-fetcher = DatahubMetadataFetcher(gms_server="http://localhost:8080")
-
-# 테이블 메타데이터 조회
-urn = "urn:li:dataset:(urn:li:dataPlatform:postgres,db.schema.table,TABLE)"
-metadata = fetcher.get_urn_info(urn)
-
-# 용어집 조회
-glossary_data = fetcher.get_glossary_data()
-
-# 쿼리 조회
-queries = fetcher.get_query_data(start=0, count=10)
-```
-
-### 예시 2: 분리된 서비스 사용
-
-```python
-from utils.data.datahub_services import (
- DataHubBaseClient,
- MetadataService,
- QueryService,
- GlossaryService
-)
-
-# 클라이언트 초기화
-client = DataHubBaseClient(gms_server="http://localhost:8080")
-
-# 서비스 초기화
-metadata_service = MetadataService(client)
-query_service = QueryService(client)
-glossary_service = GlossaryService(client)
-
-# 메타데이터 조회
-urn = "urn:li:dataset:(...)"
-metadata = metadata_service.build_table_metadata(urn)
-
-# 쿼리 조회
-queries = query_service.get_query_data(start=0, count=10)
-
-# 용어집 조회
-glossary_data = glossary_service.get_glossary_data()
-```
-
-### 예시 3: 개별 메서드 사용
-
-```python
-from utils.data.datahub_source import DatahubMetadataFetcher
-
-fetcher = DatahubMetadataFetcher(gms_server="http://localhost:8080")
-
-# 테이블 정보 조회
-urn = "urn:li:dataset:(...)"
-table_name = fetcher.get_table_name(urn)
-description = fetcher.get_table_description(urn)
-columns = fetcher.get_column_names_and_descriptions(urn)
-
-# 리니지 조회
-downstream_lineage = fetcher.get_table_lineage(urn, direction="DOWNSTREAM")
-upstream_lineage = fetcher.get_table_lineage(urn, direction="UPSTREAM")
-column_lineage = fetcher.get_column_lineage(urn)
-
-# 최소 degree 필터링
-min_degree = fetcher.min_degree_lineage(downstream_lineage)
-```
-
----
-
-## 의존성
-
-### 외부 패키지
-- `requests`: HTTP 요청 처리
-- `datahub`: DataHub Python SDK
- - `datahub.emitter.rest_emitter.DatahubRestEmitter`
- - `datahub.ingestion.graph.client.DatahubClientConfig`
- - `datahub.ingestion.graph.client.DataHubGraph`
- - `datahub.metadata.schema_classes`
-
-### 내부 패키지
-- `utils.data.queries`: GraphQL 쿼리 정의 모듈
-- `utils.data.datahub_services.*`: DataHub 서비스 레이어
-
----
-
-## 참고 사항
-
-1. `DatahubMetadataFetcher`는 하위 호환성을 위해 분리된 서비스들을 내부적으로 사용합니다.
-2. 모든 서비스는 `DataHubBaseClient`를 필요로 합니다.
-3. GMS 서버 URL은 초기화 시 유효성 검사가 수행됩니다.
-4. GraphQL 쿼리는 `queries.py`에 중앙 집중식으로 정의되어 있습니다.
-5. 오류 발생 시 dict 형태로 `{"error": True, "message": "..."}` 구조로 반환됩니다.
-6. `utils/llm/tools/datahub.py`에서 `DatahubMetadataFetcher`를 import하여 LLM 도구로 사용됩니다.
-
diff --git a/utils/data/datahub_services/README.md b/utils/data/datahub_services/README.md
deleted file mode 100644
index 203c305..0000000
--- a/utils/data/datahub_services/README.md
+++ /dev/null
@@ -1,527 +0,0 @@
-# DataHub Services 패키지
-
-DataHub와의 상호작용을 위한 서비스 모듈들을 제공하는 패키지입니다.
-
-## 디렉토리 구조
-
-```
-datahub_services/
-├── __init__.py # 패키지 초기화 및 exports
-├── base_client.py # 기본 클라이언트
-├── glossary_service.py # 용어집 서비스
-├── metadata_service.py # 메타데이터 서비스
-└── query_service.py # 쿼리 서비스
-```
-
-## 파일별 상세 내용
-
-### `__init__.py`
-
-패키지의 진입점이며, 주요 서비스 클래스들을 export합니다.
-
-**Export되는 클래스:**
-- `DataHubBaseClient`: 기본 연결 및 통신 클라이언트
-- `MetadataService`: 메타데이터, 리니지, URN 관련 서비스
-- `QueryService`: 쿼리 관련 서비스
-- `GlossaryService`: 용어집 관련 서비스
-
-**사용 방법:**
-```python
-from utils.data.datahub_services import (
- DataHubBaseClient,
- MetadataService,
- QueryService,
- GlossaryService
-)
-
-# 클라이언트 초기화
-client = DataHubBaseClient(gms_server="http://localhost:8080")
-
-# 서비스 초기화
-metadata_service = MetadataService(client)
-query_service = QueryService(client)
-glossary_service = GlossaryService(client)
-```
-
----
-
-### `base_client.py`
-
-DataHub GMS 서버와의 기본 연결 및 통신 기능을 제공하는 클라이언트입니다.
-
-**클래스:**
-- `DataHubBaseClient`
-
-**주요 메서드:**
-
-#### `__init__(self, gms_server="http://localhost:8080", extra_headers={})`
-DataHub 클라이언트를 초기화합니다.
-
-**파라미터:**
-- `gms_server` (str): DataHub GMS 서버 URL
-- `extra_headers` (dict): 추가 HTTP 헤더
-
-**사용 예시:**
-```python
-from utils.data.datahub_services import DataHubBaseClient
-
-client = DataHubBaseClient(gms_server="http://localhost:8080")
-```
-
-#### `_is_valid_gms_server(self, gms_server)`
-GMS 서버 주소의 유효성을 검사합니다.
-
-**파라미터:**
-- `gms_server` (str): 검사할 GMS 서버 URL
-
-**반환값:**
-- `bool`: 서버가 유효한 경우 True
-
-#### `execute_graphql_query(self, query, variables=None)`
-GraphQL 쿼리를 실행합니다.
-
-**파라미터:**
-- `query` (str): GraphQL 쿼리 문자열
-- `variables` (dict, optional): 쿼리 변수
-
-**반환값:**
-- `dict`: GraphQL 응답 또는 오류 정보
-
-**사용 예시:**
-```python
-query = "{ health { status } }"
-result = client.execute_graphql_query(query)
-```
-
-#### `get_datahub_graph(self)`
-DataHub Graph 클라이언트를 반환합니다.
-
-**반환값:**
-- DataHub Graph 객체
-
-#### `get_urns(self)`
-필터를 적용하여 데이터셋의 URN 목록을 가져옵니다.
-
-**반환값:**
-- URN 목록
-
-**의존성:**
-- `requests`: HTTP 요청 처리
-- `datahub.emitter.rest_emitter.DatahubRestEmitter`: DataHub REST emitter
-
----
-
-### `glossary_service.py`
-
-DataHub의 용어집(Glossary) 관련 기능을 제공하는 서비스입니다.
-
-**클래스:**
-- `GlossaryService`
-
-**초기화:**
-```python
-from utils.data.datahub_services import DataHubBaseClient, GlossaryService
-
-client = DataHubBaseClient(gms_server="http://localhost:8080")
-glossary_service = GlossaryService(client)
-```
-
-**주요 메서드:**
-
-#### `get_root_glossary_nodes(self)`
-DataHub에서 루트 용어집 노드를 가져옵니다.
-
-**반환값:**
-- `dict`: 루트 용어집 노드 정보
-
-#### `get_glossary_node_by_urn(self, urn)`
-특정 URN의 용어집 노드 및 그 자식 항목을 가져옵니다.
-
-**파라미터:**
-- `urn` (str): 용어집 노드의 URN
-
-**반환값:**
-- `dict`: 용어집 노드 정보와 자식 항목
-
-#### `get_node_basic_info(self, node, index)`
-용어집 노드의 기본 정보를 딕셔너리로 반환합니다.
-
-**파라미터:**
-- `node` (dict): 용어집 노드 정보
-- `index` (int): 노드의 인덱스
-
-**반환값:**
-- `dict`: 노드의 기본 정보 (index, name, description, child_count 등)
-
-#### `get_child_entity_info(self, entity, index)`
-자식 엔티티(용어 또는 노드)의 정보를 딕셔너리로 반환합니다.
-
-**파라미터:**
-- `entity` (dict): 자식 엔티티 정보
-- `index` (int): 엔티티의 인덱스
-
-**반환값:**
-- `dict`: 엔티티 정보 (index, type, name, description 등)
-
-#### `process_node_details(self, node)`
-노드의 상세 정보를 처리하고 딕셔너리로 반환합니다.
-
-**파라미터:**
-- `node` (dict): 용어집 노드 정보
-
-**반환값:**
-- `dict`: 노드의 상세 정보 (name, children 등)
-
-#### `process_glossary_nodes(self, result)`
-용어집 노드 결과를 처리하고 딕셔너리로 반환합니다.
-
-**파라미터:**
-- `result` (dict): API 응답 결과
-
-**반환값:**
-- `dict`: 처리된 용어집 노드 데이터 (total_nodes, nodes)
-
-#### `get_glossary_data(self)`
-DataHub에서 전체 용어집 데이터를 가져와 처리합니다.
-
-**반환값:**
-- `dict`: 처리된 용어집 데이터 또는 오류 정보
-
-**사용 예시:**
-```python
-result = glossary_service.get_glossary_data()
-print(f"전체 노드 수: {result['total_nodes']}")
-for node in result['nodes']:
- print(f" - {node['name']}: {node.get('description', 'N/A')}")
-```
-
-#### `get_glossary_terms_by_urn(self, dataset_urn)`
-특정 데이터셋 URN의 glossary terms를 조회합니다.
-
-**파라미터:**
-- `dataset_urn` (str): 데이터셋 URN
-
-**반환값:**
-- `dict`: glossary terms 정보
-
-**의존성:**
-- `utils.data.datahub_services.base_client.DataHubBaseClient`: 기본 클라이언트
-- `utils.data.queries`: GraphQL 쿼리 정의
- - `GLOSSARY_NODE_QUERY`
- - `GLOSSARY_TERMS_BY_URN_QUERY`
- - `ROOT_GLOSSARY_NODES_QUERY`
-
----
-
-### `metadata_service.py`
-
-테이블 메타데이터, 리니지, URN 관련 기능을 제공하는 서비스입니다.
-
-**클래스:**
-- `MetadataService`
-
-**초기화:**
-```python
-from utils.data.datahub_services import DataHubBaseClient, MetadataService
-
-client = DataHubBaseClient(gms_server="http://localhost:8080")
-metadata_service = MetadataService(client)
-```
-
-**주요 메서드:**
-
-#### `get_table_name(self, urn)`
-URN에 대한 테이블 이름을 가져옵니다.
-
-**파라미터:**
-- `urn` (str): 데이터셋 URN
-
-**반환값:**
-- `str`: 테이블 이름 (database.table 형태) 또는 None
-
-#### `get_table_description(self, urn)`
-URN에 대한 테이블 설명을 가져옵니다.
-
-**파라미터:**
-- `urn` (str): 데이터셋 URN
-
-**반환값:**
-- `str`: 테이블 설명 또는 None
-
-#### `get_column_names_and_descriptions(self, urn)`
-URN에 대한 컬럼 이름 및 설명을 가져옵니다.
-
-**파라미터:**
-- `urn` (str): 데이터셋 URN
-
-**반환값:**
-- `list`: 컬럼 정보 리스트 (각 항목: column_name, column_description, column_type)
-
-#### `get_table_lineage(self, urn, counts=100, direction="DOWNSTREAM", degree_values=None)`
-URN에 대한 DOWNSTREAM/UPSTREAM lineage entity를 가져옵니다.
-
-**파라미터:**
-- `urn` (str): 데이터셋 URN
-- `counts` (int): 가져올 엔티티 수 (기본값: 100)
-- `direction` (str): 리니지 방향 ("DOWNSTREAM" 또는 "UPSTREAM", 기본값: "DOWNSTREAM")
-- `degree_values` (list): 필터링할 degree 값 리스트 (기본값: ["1", "2"])
-
-**반환값:**
-- `tuple`: (urn, lineage_result)
-
-#### `get_column_lineage(self, urn)`
-URN에 대한 UPSTREAM lineage의 column source를 가져옵니다.
-
-**파라미터:**
-- `urn` (str): 데이터셋 URN
-
-**반환값:**
-- `dict`: 컬럼 리니지 정보
- - `downstream_dataset`: 다운스트림 데이터셋 이름
- - `lineage_by_upstream_dataset`: 업스트림 데이터셋별 컬럼 매핑
- - `upstream_dataset`: 업스트림 데이터셋 이름
- - `columns`: 컬럼 매핑 리스트
- - `upstream_column`: 업스트림 컬럼
- - `downstream_column`: 다운스트림 컬럼
- - `confidence`: 신뢰도
-
-#### `min_degree_lineage(self, lineage_result)`
-lineage 중 최소 degree만 가져옵니다.
-
-**파라미터:**
-- `lineage_result`: `get_table_lineage`의 반환값
-
-**반환값:**
-- `dict`: 테이블별 최소 degree 매핑
-
-#### `build_table_metadata(self, urn, max_degree=2, sort_by_degree=True)`
-테이블 단위로 통합 메타데이터를 생성합니다.
-
-**파라미터:**
-- `urn` (str): 데이터셋 URN
-- `max_degree` (int): 최대 degree 제한 (기본값: 2)
-- `sort_by_degree` (bool): degree 기준 정렬 여부 (기본값: True)
-
-**반환값:**
-- `dict`: 통합 메타데이터
- - `table_name`: 테이블 이름
- - `description`: 테이블 설명
- - `columns`: 컬럼 정보 리스트
- - `lineage`: 리니지 정보
- - `downstream`: 다운스트림 테이블 리스트
- - `upstream`: 업스트림 테이블 리스트
- - `upstream_columns`: 컬럼 레벨 리니지
-
-**사용 예시:**
-```python
-metadata = metadata_service.build_table_metadata(urn)
-print(f"테이블: {metadata['table_name']}")
-print(f"컬럼 수: {len(metadata['columns'])}")
-print(f"다운스트림: {len(metadata['lineage']['downstream'])}개")
-print(f"업스트림: {len(metadata['lineage']['upstream'])}개")
-```
-
-#### `get_urn_info(self, urn)`
-특정 URN에 대한 모든 관련 정보를 가져옵니다.
-
-**파라미터:**
-- `urn` (str): 조회할 데이터셋 URN
-
-**반환값:**
-- `dict`: URN에 대한 전체 메타데이터 정보 또는 오류 정보
-
-**사용 예시:**
-```python
-result = metadata_service.get_urn_info(urn)
-# 콘솔에 자동으로 포맷된 정보 출력 및 반환
-```
-
-**의존성:**
-- `collections.defaultdict`: 딕셔너리 기본값 처리
-- `datahub.ingestion.graph.client`: DataHub Graph 클라이언트
- - `DatahubClientConfig`
- - `DataHubGraph`
-- `datahub.metadata.schema_classes`: DataHub 메타데이터 스키마 클래스
- - `DatasetPropertiesClass`
- - `SchemaMetadataClass`
- - `UpstreamLineageClass`
-- `utils.data.datahub_services.base_client.DataHubBaseClient`: 기본 클라이언트
-
----
-
-### `query_service.py`
-
-DataHub의 쿼리 관련 기능을 제공하는 서비스입니다.
-
-**클래스:**
-- `QueryService`
-
-**초기화:**
-```python
-from utils.data.datahub_services import DataHubBaseClient, QueryService
-
-client = DataHubBaseClient(gms_server="http://localhost:8080")
-query_service = QueryService(client)
-```
-
-**주요 메서드:**
-
-#### `get_queries(self, start=0, count=10, query="*", filters=None)`
-DataHub에서 쿼리 목록을 가져옵니다.
-
-**파라미터:**
-- `start` (int): 시작 인덱스 (기본값: 0)
-- `count` (int): 반환할 쿼리 수 (기본값: 10)
-- `query` (str): 필터링에 사용할 쿼리 문자열 (기본값: "*")
-- `filters` (list): 추가 필터 (기본값: None)
-
-**반환값:**
-- `dict`: 쿼리 목록 정보
-
-#### `process_queries(self, result)`
-쿼리 목록 결과를 처리하고 간소화된 형태로 반환합니다.
-
-**파라미터:**
-- `result` (dict): API 응답 결과
-
-**반환값:**
-- `dict`: 처리된 쿼리 목록 데이터
- - `total_queries`: 전체 쿼리 수
- - `count`: 조회된 쿼리 수
- - `start`: 시작 인덱스
- - `queries`: 쿼리 리스트 (urn, name, description, statement 포함)
-
-#### `get_query_data(self, start=0, count=10, query="*", filters=None)`
-DataHub에서 쿼리 목록을 가져와 처리합니다.
-
-**파라미터:**
-- `start` (int): 시작 인덱스 (기본값: 0)
-- `count` (int): 반환할 쿼리 수 (기본값: 10)
-- `query` (str): 필터링에 사용할 쿼리 문자열 (기본값: "*")
-- `filters` (list): 추가 필터 (기본값: None)
-
-**반환값:**
-- `dict`: 처리된 쿼리 목록 데이터 또는 오류 정보
-
-**사용 예시:**
-```python
-result = query_service.get_query_data(start=0, count=5, query="*")
-print(f"전체 쿼리 수: {result['total_queries']}")
-for idx, q in enumerate(result['queries'], 1):
- print(f"{idx}. {q['name']}: {q.get('description', 'N/A')}")
-```
-
-#### `get_queries_by_urn(self, dataset_urn)`
-특정 데이터셋 URN과 연관된 쿼리들을 조회합니다.
-
-**파라미터:**
-- `dataset_urn` (str): 데이터셋 URN
-
-**반환값:**
-- `dict`: 연관된 쿼리 목록
-
-**주의:** 전체 쿼리를 가져온 후 클라이언트 사이드에서 필터링하는 방식 사용
-
-#### `get_glossary_terms_by_urn(self, dataset_urn)`
-특정 데이터셋 URN의 glossary terms를 조회합니다.
-
-**파라미터:**
-- `dataset_urn` (str): 데이터셋 URN
-
-**반환값:**
-- `dict`: glossary terms 정보
-
-**의존성:**
-- `utils.data.datahub_services.base_client.DataHubBaseClient`: 기본 클라이언트
-- `utils.data.queries`: GraphQL 쿼리 정의
- - `GLOSSARY_TERMS_BY_URN_QUERY`
- - `LIST_QUERIES_QUERY`
- - `QUERIES_BY_URN_QUERY`
-
----
-
-## 전체 사용 예시
-
-### 예시 1: 기본 사용법
-
-```python
-from utils.data.datahub_services import (
- DataHubBaseClient,
- MetadataService,
- QueryService,
- GlossaryService
-)
-
-# 1. 클라이언트 초기화
-gms_server = "http://localhost:8080"
-client = DataHubBaseClient(gms_server=gms_server)
-
-# 2. 서비스 초기화
-metadata_service = MetadataService(client)
-query_service = QueryService(client)
-glossary_service = GlossaryService(client)
-
-# 3. 메타데이터 조회
-urn = "urn:li:dataset:(...)"
-metadata = metadata_service.get_urn_info(urn)
-
-# 4. 쿼리 조회
-queries = query_service.get_query_data(start=0, count=10)
-
-# 5. 용어집 조회
-glossary_data = glossary_service.get_glossary_data()
-```
-
-### 예시 2: 통합 페처 사용 (하위 호환성)
-
-```python
-from utils.data.datahub_source import DatahubMetadataFetcher
-
-# 기존 인터페이스와 동일하게 사용 가능
-fetcher = DatahubMetadataFetcher(gms_server="http://localhost:8080")
-
-# 모든 메서드가 동일하게 동작
-metadata = fetcher.get_urn_info(urn)
-queries = fetcher.get_query_data()
-glossary = fetcher.get_glossary_data()
-```
-
-### 예시 3: 테스트 코드
-
-`test.py` 파일에서 실제 사용 예시를 확인할 수 있습니다:
-
-```python
-from utils.data.datahub_services import DataHubBaseClient, QueryService
-
-client = DataHubBaseClient(gms_server="http://35.222.65.99:8080")
-query_service = QueryService(client)
-
-result = query_service.get_query_data(start=0, count=5, query="*")
-print(json.dumps(result, indent=2, ensure_ascii=False))
-```
-
----
-
-## 의존성
-
-### 외부 패키지
-- `requests`: HTTP 요청 처리
-- `datahub`: DataHub Python SDK
- - `datahub.emitter.rest_emitter.DatahubRestEmitter`
- - `datahub.ingestion.graph.client.DatahubClientConfig`
- - `datahub.ingestion.graph.client.DataHubGraph`
- - `datahub.metadata.schema_classes`
-
-### 내부 패키지
-- `utils.data.queries`: GraphQL 쿼리 정의 모듈
-
----
-
-## 참고 사항
-
-1. 모든 서비스는 `DataHubBaseClient`를 필요로 합니다.
-2. `DatahubMetadataFetcher`는 하위 호환성을 위해 이 서비스들을 내부적으로 사용합니다.
-3. GMS 서버 URL은 초기화 시 유효성 검사가 수행됩니다.
-4. GraphQL 쿼리는 `utils.data.queries` 모듈에 정의되어 있습니다.
-5. 오류 발생 시 dict 형태로 `{"error": True, "message": "..."}` 구조로 반환됩니다.
-
diff --git a/utils/data/datahub_services/__init__.py b/utils/data/datahub_services/__init__.py
deleted file mode 100644
index 2743ef9..0000000
--- a/utils/data/datahub_services/__init__.py
+++ /dev/null
@@ -1,23 +0,0 @@
-"""
-DataHub 유틸리티 패키지
-
-DataHub와의 상호작용을 위한 모듈들을 제공합니다.
-
-주요 구성요소:
-- DataHubBaseClient: 기본 연결 및 통신
-- MetadataService: 메타데이터, 리니지, URN 관련 기능
-- QueryService: 쿼리 관련 기능
-- GlossaryService: 용어집 관련 기능
-"""
-
-from utils.data.datahub_services.base_client import DataHubBaseClient
-from utils.data.datahub_services.glossary_service import GlossaryService
-from utils.data.datahub_services.metadata_service import MetadataService
-from utils.data.datahub_services.query_service import QueryService
-
-__all__ = [
- "DataHubBaseClient",
- "MetadataService",
- "QueryService",
- "GlossaryService",
-]
diff --git a/utils/data/datahub_services/base_client.py b/utils/data/datahub_services/base_client.py
deleted file mode 100644
index 6637b4b..0000000
--- a/utils/data/datahub_services/base_client.py
+++ /dev/null
@@ -1,94 +0,0 @@
-"""
-DataHub 기본 클라이언트 모듈
-
-DataHub GMS 서버와의 기본 연결 및 통신 기능을 제공합니다.
-"""
-
-import requests
-from datahub.emitter.rest_emitter import DatahubRestEmitter
-
-
-class DataHubBaseClient:
- """DataHub 기본 클라이언트 클래스"""
-
- def __init__(self, gms_server="http://localhost:8080", extra_headers={}):
- """
- DataHub 클라이언트 초기화
-
- Args:
- gms_server (str): DataHub GMS 서버 URL
- extra_headers (dict): 추가 HTTP 헤더
- """
- # gms_server 주소 유효성 검사
- if not self._is_valid_gms_server(gms_server):
- raise ValueError(f"유효하지 않은 GMS 서버 주소: {gms_server}")
-
- self.gms_server = gms_server
- self.extra_headers = extra_headers
-
- # DataHub 클라이언트 초기화
- self.emitter = DatahubRestEmitter(
- gms_server=gms_server, extra_headers=extra_headers
- )
- self.datahub_graph = self.emitter.to_graph()
-
- def _is_valid_gms_server(self, gms_server):
- """
- GMS 서버 주소의 유효성을 검사하는 함수
-
- Args:
- gms_server (str): 검사할 GMS 서버 URL
-
- Returns:
- bool: 서버가 유효한 경우 True
- """
- query = {"query": "{ health { status } }"}
- headers = {"Content-Type": "application/json"}
-
- try:
- response = requests.post(
- f"{gms_server}/api/graphql", json=query, headers=headers
- )
- return response.status_code == 200
- except requests.exceptions.RequestException:
- return False
-
- def execute_graphql_query(self, query, variables=None):
- """
- GraphQL 쿼리 실행
-
- Args:
- query (str): GraphQL 쿼리 문자열
- variables (dict, optional): 쿼리 변수
-
- Returns:
- dict: GraphQL 응답
- """
- headers = {"Content-Type": "application/json"}
- payload = {"query": query}
-
- if variables:
- payload["variables"] = variables
-
- response = requests.post(
- f"{self.gms_server}/api/graphql",
- json=payload,
- headers=headers,
- )
-
- if response.status_code == 200:
- return response.json()
- else:
- return {
- "error": True,
- "status_code": response.status_code,
- "message": response.text,
- }
-
- def get_datahub_graph(self):
- """DataHub Graph 클라이언트 반환"""
- return self.datahub_graph
-
- def get_urns(self):
- """필터를 적용하여 데이터셋의 URN 가져오기"""
- return self.datahub_graph.get_urns_by_filter()
diff --git a/utils/data/datahub_services/glossary_service.py b/utils/data/datahub_services/glossary_service.py
deleted file mode 100644
index 36eeec7..0000000
--- a/utils/data/datahub_services/glossary_service.py
+++ /dev/null
@@ -1,194 +0,0 @@
-"""
-DataHub 용어집 서비스 모듈
-
-DataHub의 glossary 관련 기능을 제공합니다.
-"""
-
-from utils.data.datahub_services.base_client import DataHubBaseClient
-from utils.data.queries import (
- GLOSSARY_NODE_QUERY,
- GLOSSARY_TERMS_BY_URN_QUERY,
- ROOT_GLOSSARY_NODES_QUERY,
-)
-
-
-class GlossaryService:
- """용어집 관련 서비스 클래스"""
-
- def __init__(self, client: DataHubBaseClient):
- """
- 용어집 서비스 초기화
-
- Args:
- client (DataHubBaseClient): DataHub 기본 클라이언트
- """
- self.client = client
-
- def get_root_glossary_nodes(self):
- """
- DataHub에서 루트 용어집 노드를 가져오는 함수
-
- Returns:
- dict: 루트 용어집 노드 정보
- """
- return self.client.execute_graphql_query(ROOT_GLOSSARY_NODES_QUERY)
-
- def get_glossary_node_by_urn(self, urn):
- """
- DataHub에서 특정 URN의 용어집 노드 및 그 자식 항목을 가져오는 함수
-
- Args:
- urn (str): 용어집 노드의 URN
-
- Returns:
- dict: 용어집 노드 정보와 자식 항목
- """
- variables = {"urn": urn}
- return self.client.execute_graphql_query(GLOSSARY_NODE_QUERY, variables)
-
- def get_node_basic_info(self, node, index):
- """
- 용어집 노드의 기본 정보를 딕셔너리로 반환하는 함수
-
- Args:
- node (dict): 용어집 노드 정보
- index (int): 노드의 인덱스
-
- Returns:
- dict: 노드의 기본 정보
- """
- result = {"index": index, "name": node["properties"]["name"]}
-
- if node["properties"] and node["properties"].get("description"):
- result["description"] = node["properties"]["description"]
-
- # 자식 노드/용어 관계 정보 수 추가
- if "children" in node and node["children"]["total"] > 0:
- result["child_count"] = node["children"]["total"]
-
- return result
-
- def get_child_entity_info(self, entity, index):
- """
- 자식 엔티티(용어 또는 노드)의 정보를 딕셔너리로 반환하는 함수
-
- Args:
- entity (dict): 자식 엔티티 정보
- index (int): 엔티티의 인덱스
-
- Returns:
- dict: 엔티티 정보
- """
- entity_type = entity["type"]
- result = {"index": index, "type": entity_type}
-
- if entity_type == "GLOSSARY_TERM":
- if "properties" in entity and entity["properties"]:
- result["name"] = entity["properties"].get("name", "N/A")
-
- if (
- "description" in entity["properties"]
- and entity["properties"]["description"]
- ):
- result["description"] = entity["properties"]["description"]
-
- elif entity_type == "GLOSSARY_NODE":
- if "properties" in entity and entity["properties"]:
- result["name"] = entity["properties"].get("name", "N/A")
-
- return result
-
- def process_node_details(self, node):
- """
- 노드의 상세 정보를 처리하고 딕셔너리로 반환하는 함수
-
- Args:
- node (dict): 용어집 노드 정보
-
- Returns:
- dict: 노드의 상세 정보
- """
- node_urn = node["urn"]
- detailed_node = self.get_glossary_node_by_urn(node_urn)
-
- result = {"name": node["properties"]["name"], "children": []}
-
- if (
- detailed_node
- and "data" in detailed_node
- and "glossaryNode" in detailed_node["data"]
- ):
- node_detail = detailed_node["data"]["glossaryNode"]
-
- # 자식 항목 정보 추출
- if "children" in node_detail and node_detail["children"]["total"] > 0:
- relationships = node_detail["children"]["relationships"]
-
- for j, rel in enumerate(relationships, 1):
- entity = rel["entity"]
- result["children"].append(self.get_child_entity_info(entity, j))
-
- return result
-
- def process_glossary_nodes(self, result):
- """
- 용어집 노드 결과를 처리하고 딕셔너리로 반환하는 함수
-
- Args:
- result (dict): API 응답 결과
-
- Returns:
- dict: 처리된 용어집 노드 데이터
- """
- if "error" in result:
- return result
-
- processed_result = {"total_nodes": 0, "nodes": []}
-
- # 노드 목록 추출
- nodes = result["data"]["getRootGlossaryNodes"]["nodes"]
- processed_result["total_nodes"] = len(nodes)
-
- for i, node in enumerate(nodes, 1):
- node_info = self.get_node_basic_info(node, i)
-
- # 자식 노드가 있으면 상세 정보 처리
- if "children" in node and node["children"]["total"] > 0:
- node_details = self.process_node_details(node)
- node_info["details"] = node_details
-
- processed_result["nodes"].append(node_info)
-
- return processed_result
-
- def get_glossary_data(self):
- """
- DataHub에서 전체 용어집 데이터를 가져와 처리하는 함수
-
- Returns:
- dict: 처리된 용어집 데이터
- """
- # DataHub 서버에 연결하여 용어집 노드 가져오기
- result = self.get_root_glossary_nodes()
-
- # 결과 처리
- if result:
- try:
- return self.process_glossary_nodes(result)
- except KeyError as e:
- return {"error": True, "message": f"결과 구조 파싱 중 오류 발생: {e}"}
- else:
- return {"error": True, "message": "용어집 노드를 가져오지 못했습니다."}
-
- def get_glossary_terms_by_urn(self, dataset_urn):
- """
- 특정 데이터셋 URN의 glossary terms를 조회하는 함수
-
- Args:
- dataset_urn (str): 데이터셋 URN
-
- Returns:
- dict: glossary terms 정보
- """
- variables = {"urn": dataset_urn}
- return self.client.execute_graphql_query(GLOSSARY_TERMS_BY_URN_QUERY, variables)
diff --git a/utils/data/datahub_services/metadata_service.py b/utils/data/datahub_services/metadata_service.py
deleted file mode 100644
index 5c5a4f9..0000000
--- a/utils/data/datahub_services/metadata_service.py
+++ /dev/null
@@ -1,317 +0,0 @@
-"""
-DataHub 메타데이터 서비스 모듈
-
-테이블 메타데이터, 리니지, URN 관련 기능을 제공합니다.
-"""
-
-from collections import defaultdict
-
-from datahub.ingestion.graph.client import DatahubClientConfig, DataHubGraph
-from datahub.metadata.schema_classes import (
- DatasetPropertiesClass,
- SchemaMetadataClass,
- UpstreamLineageClass,
-)
-
-from utils.data.datahub_services.base_client import DataHubBaseClient
-
-
-class MetadataService:
- """메타데이터 관련 서비스 클래스"""
-
- def __init__(self, client: DataHubBaseClient):
- """
- 메타데이터 서비스 초기화
-
- Args:
- client (DataHubBaseClient): DataHub 기본 클라이언트
- """
- self.client = client
- self.datahub_graph = client.get_datahub_graph()
- self.gms_server = client.gms_server
-
- def get_table_name(self, urn):
- """URN에 대한 테이블 이름 가져오기"""
- dataset_properties = self.datahub_graph.get_aspect(
- urn, aspect_type=DatasetPropertiesClass
- )
- if dataset_properties:
- database_info = dataset_properties.get("customProperties", {}).get(
- "dbt_unique_id", ""
- )
- if database_info:
- database_info = database_info.split(".")[-2]
- else:
- database_info = ""
- table_info = dataset_properties.get("name", None)
- return database_info + "." + table_info
- return None
-
- def get_table_description(self, urn):
- """URN에 대한 테이블 설명 가져오기"""
- dataset_properties = self.datahub_graph.get_aspect(
- urn, aspect_type=DatasetPropertiesClass
- )
- if dataset_properties:
- return dataset_properties.get("description", None)
- return None
-
- def get_column_names_and_descriptions(self, urn):
- """URN에 대한 컬럼 이름 및 설명 가져오기"""
- schema_metadata = self.datahub_graph.get_aspect(
- urn, aspect_type=SchemaMetadataClass
- )
- columns = []
- if schema_metadata:
- for field in schema_metadata.fields:
- # nativeDataType가 없거나 빈 문자열인 경우 None 처리
- native_type = getattr(field, "nativeDataType", None)
- column_type = (
- native_type if native_type and native_type.strip() else None
- )
-
- columns.append(
- {
- "column_name": field.fieldPath,
- "column_description": field.description,
- "column_type": column_type,
- }
- )
- return columns
-
- def get_table_lineage(
- self,
- urn,
- counts=100,
- direction="DOWNSTREAM",
- degree_values=None,
- ):
- """URN에 대한 DOWNSTREAM/UPSTREAM lineage entity를 counts 만큼 가져오는 함수"""
- if degree_values is None:
- degree_values = ["1", "2"]
-
- graph = DataHubGraph(DatahubClientConfig(server=self.gms_server))
-
- query = """
- query scrollAcrossLineage($input: ScrollAcrossLineageInput!) {
- scrollAcrossLineage(input: $input) {
- searchResults {
- degree
- entity {
- urn
- type
- }
- }
- }
- }
- """
- variables = {
- "input": {
- "query": "*",
- "urn": urn,
- "count": counts,
- "direction": direction,
- "orFilters": [
- {
- "and": [
- {
- "condition": "EQUAL",
- "negated": "false",
- "field": "degree",
- "values": degree_values,
- }
- ]
- }
- ],
- }
- }
-
- result = graph.execute_graphql(query=query, variables=variables)
- return urn, result
-
- def get_column_lineage(self, urn):
- """URN에 대한 UPSTREAM lineage의 column source를 가져오는 함수"""
- # DataHub 연결 및 lineage 가져오기
- graph = DataHubGraph(DatahubClientConfig(server=self.gms_server))
- result = graph.get_aspect(entity_urn=urn, aspect_type=UpstreamLineageClass)
-
- # downstream dataset (URN 테이블명) 파싱
- try:
- down_dataset = urn.split(",")[1]
- table_name = down_dataset.split(".")[1]
- except IndexError:
- # URN이 유효하지 않는 경우
- print(f"[ERROR] Invalid URN format: {urn}")
- return {}
-
- # upstream_dataset별로 column lineage
- upstream_map = defaultdict(list)
-
- if not result:
- return {"downstream_dataset": table_name, "lineage_by_upstream_dataset": []}
-
- for fg in result.fineGrainedLineages or []:
- confidence_score = (
- fg.confidenceScore if fg.confidenceScore is not None else 1.0
- )
- for down in fg.downstreams:
- down_column = down.split(",")[-1].replace(")", "")
- for up in fg.upstreams:
- up_dataset = up.split(",")[1]
- up_dataset = up_dataset.split(".")[1]
- up_column = up.split(",")[-1].replace(")", "")
-
- upstream_map[up_dataset].append(
- {
- "upstream_column": up_column,
- "downstream_column": down_column,
- "confidence": confidence_score,
- }
- )
-
- # 최종 결과 구조 생성
- parsed_lineage = {
- "downstream_dataset": table_name,
- "lineage_by_upstream_dataset": [],
- }
-
- for up_dataset, column_mappings in upstream_map.items():
- parsed_lineage["lineage_by_upstream_dataset"].append(
- {"upstream_dataset": up_dataset, "columns": column_mappings}
- )
-
- return parsed_lineage
-
- def min_degree_lineage(self, lineage_result):
- """lineage 중 최소 degree만 가져오는 함수"""
- table_degrees = {}
- urn, lineage_data = lineage_result
-
- for item in lineage_data["scrollAcrossLineage"]["searchResults"]:
- table = item["entity"]["urn"].split(",")[1]
- table_name = table.split(".")[1]
- degree = item["degree"]
- table_degrees[table_name] = min(
- degree, table_degrees.get(table_name, float("inf"))
- )
-
- return table_degrees
-
- def build_table_metadata(self, urn, max_degree=2, sort_by_degree=True):
- """테이블 단위로 테이블 이름, 설명, 컬럼, 테이블 별 리니지(downstream/upstream), 컬럼 별 리니지(upstream)이 포함된 메타데이터 생성 함수"""
- metadata = {
- "table_name": self.get_table_name(urn),
- "description": self.get_table_description(urn),
- "columns": self.get_column_names_and_descriptions(urn),
- "lineage": {},
- }
-
- def process_lineage(direction):
- # direction : DOWNSTREAM/UPSTREAM 별로 degree가 최소인 lineage를 가져오는 함수
- # 테이블 lineage 가져오기
- lineage_result = self.get_table_lineage(urn, direction=direction)
- table_degrees = self.min_degree_lineage(lineage_result)
- current_table_name = metadata["table_name"]
-
- # degree 필터링
- filtered_lineage = [
- {"table": table, "degree": degree}
- for table, degree in table_degrees.items()
- if degree <= max_degree and table != current_table_name
- ]
-
- # degree 기준 정렬
- if sort_by_degree:
- filtered_lineage.sort(key=lambda x: x["degree"])
-
- return filtered_lineage
-
- # DOWNSTREAM / UPSTREAM 링크 추가
- metadata["lineage"]["downstream"] = process_lineage("DOWNSTREAM")
- metadata["lineage"]["upstream"] = process_lineage("UPSTREAM")
-
- # 컬럼 단위 lineage 추가
- column_lineage = self.get_column_lineage(urn)
- metadata["lineage"]["upstream_columns"] = column_lineage.get(
- "lineage_by_upstream_dataset", []
- )
-
- return metadata
-
- def get_urn_info(self, urn):
- """
- 특정 URN에 대한 모든 관련 정보를 가져오는 함수
-
- Args:
- urn (str): 조회할 데이터셋 URN
-
- Returns:
- dict: URN에 대한 전체 메타데이터 정보
- """
- print(f"\n=== URN 정보 조회: {urn} ===\n")
-
- try:
- # 기본 테이블 메타데이터 가져오기
- metadata = self.build_table_metadata(urn)
-
- # 결과 출력
- self._print_urn_details(metadata)
-
- return metadata
-
- except Exception as e:
- error_msg = f"URN 정보 조회 중 오류 발생: {str(e)}"
- print(error_msg)
- return {"error": True, "message": error_msg}
-
- def _print_urn_details(self, metadata):
- """URN 메타데이터를 보기 좋게 출력하는 내부 함수"""
-
- # 테이블 기본 정보
- print("📋 테이블 정보:")
- print(f" 이름: {metadata.get('table_name', 'N/A')}")
- print(f" 설명: {metadata.get('description', 'N/A')}\n")
-
- # 컬럼 정보
- columns = metadata.get("columns", [])
- if columns:
- print(f"📊 컬럼 정보 ({len(columns)}개):")
- for i, col in enumerate(columns, 1):
- print(f" {i}. {col['column_name']} ({col.get('column_type', 'N/A')})")
- if col.get("column_description"):
- print(f" → {col['column_description']}")
- print()
-
- # 리니지 정보
- lineage = metadata.get("lineage", {})
-
- # Downstream 테이블
- downstream = lineage.get("downstream", [])
- if downstream:
- print(f"⬇️ Downstream 테이블 ({len(downstream)}개):")
- for table in downstream:
- print(f" - {table['table']} (degree: {table['degree']})")
- print()
-
- # Upstream 테이블
- upstream = lineage.get("upstream", [])
- if upstream:
- print(f"⬆️ Upstream 테이블 ({len(upstream)}개):")
- for table in upstream:
- print(f" - {table['table']} (degree: {table['degree']})")
- print()
-
- # 컬럼 레벨 리니지
- upstream_columns = lineage.get("upstream_columns", [])
- if upstream_columns:
- print("🔗 컬럼 레벨 리니지:")
- for upstream_dataset in upstream_columns:
- dataset_name = upstream_dataset["upstream_dataset"]
- columns = upstream_dataset["columns"]
- print(f" 📋 {dataset_name}:")
- for col in columns:
- confidence = col.get("confidence", 1.0)
- print(
- f" {col['upstream_column']} → {col['downstream_column']} (신뢰도: {confidence})"
- )
- print()
diff --git a/utils/data/datahub_services/query_service.py b/utils/data/datahub_services/query_service.py
deleted file mode 100644
index 6622629..0000000
--- a/utils/data/datahub_services/query_service.py
+++ /dev/null
@@ -1,159 +0,0 @@
-"""
-DataHub 쿼리 서비스 모듈
-
-DataHub의 쿼리 관련 기능을 제공합니다.
-"""
-
-from utils.data.datahub_services.base_client import DataHubBaseClient
-from utils.data.queries import (
- GLOSSARY_TERMS_BY_URN_QUERY,
- LIST_QUERIES_QUERY,
- QUERIES_BY_URN_QUERY,
-)
-
-
-class QueryService:
- """쿼리 관련 서비스 클래스"""
-
- def __init__(self, client: DataHubBaseClient):
- """
- 쿼리 서비스 초기화
-
- Args:
- client (DataHubBaseClient): DataHub 기본 클라이언트
- """
- self.client = client
-
- def get_queries(self, start=0, count=10, query="*", filters=None):
- """
- DataHub에서 쿼리 목록을 가져오는 함수
-
- Args:
- start (int): 시작 인덱스 (기본값=0)
- count (int): 반환할 쿼리 수 (기본값=10)
- query (str): 필터링에 사용할 쿼리 문자열 (기본값="*")
- filters (list): 추가 필터 (기본값=None)
-
- Returns:
- dict: 쿼리 목록 정보
- """
- # GraphQL 요청용 입력 변수 준비
- input_params = {"start": start, "count": count, "query": query}
-
- if filters:
- input_params["filters"] = filters
-
- variables = {"input": input_params}
-
- return self.client.execute_graphql_query(LIST_QUERIES_QUERY, variables)
-
- def process_queries(self, result):
- """
- 쿼리 목록 결과를 처리하고 간소화된 형태로 반환하는 함수
-
- Args:
- result (dict): API 응답 결과
-
- Returns:
- dict: 처리된 쿼리 목록 데이터 (urn, name, description, statement만 포함)
- """
- if "error" in result:
- return result
-
- processed_result = {"total_queries": 0, "count": 0, "start": 0, "queries": []}
-
- if "data" in result and "listQueries" in result["data"]:
- list_queries = result["data"]["listQueries"]
- processed_result["total_queries"] = list_queries.get("total", 0)
- processed_result["count"] = list_queries.get("count", 0)
- processed_result["start"] = list_queries.get("start", 0)
-
- for query in list_queries.get("queries", []):
- query_info = {"urn": query.get("urn")}
-
- props = query.get("properties", {})
- query_info["name"] = props.get("name")
- query_info["description"] = props.get("description")
- query_info["statement"] = props.get("statement", {}).get("value")
-
- processed_result["queries"].append(query_info)
-
- return processed_result
-
- def get_query_data(self, start=0, count=10, query="*", filters=None):
- """
- DataHub에서 쿼리 목록을 가져와 처리하는 함수
-
- Args:
- start (int): 시작 인덱스 (기본값=0)
- count (int): 반환할 쿼리 수 (기본값=10)
- query (str): 필터링에 사용할 쿼리 문자열 (기본값="*")
- filters (list): 추가 필터 (기본값=None)
-
- Returns:
- dict: 처리된 쿼리 목록 데이터
- """
- # DataHub 서버에 연결하여 쿼리 목록 가져오기
- result = self.get_queries(start, count, query, filters)
-
- # 결과 처리
- if result:
- try:
- return self.process_queries(result)
- except KeyError as e:
- return {"error": True, "message": f"결과 구조 파싱 중 오류 발생: {e}"}
- else:
- return {"error": True, "message": "쿼리 목록을 가져오지 못했습니다."}
-
- def get_queries_by_urn(self, dataset_urn):
- """
- 특정 데이터셋 URN과 연관된 쿼리들을 조회하는 함수
-
- 전체 쿼리를 가져온 후 클라이언트 사이드에서 필터링하는 방식 사용
-
- Args:
- dataset_urn (str): 데이터셋 URN
-
- Returns:
- dict: 연관된 쿼리 목록
- """
- # 먼저 전체 쿼리 목록을 가져옴
- input_params = {"start": 0, "count": 1000, "query": "*"} # 충분히 큰 수로 설정
-
- variables = {"input": input_params}
- result = self.client.execute_graphql_query(QUERIES_BY_URN_QUERY, variables)
-
- if (
- "error" not in result
- and "data" in result
- and "listQueries" in result["data"]
- ):
- # 클라이언트 사이드에서 특정 URN과 연관된 쿼리만 필터링
- all_queries = result["data"]["listQueries"]["queries"]
- filtered_queries = []
-
- for query in all_queries:
- subjects = query.get("subjects", [])
- for subject in subjects:
- if subject.get("dataset", {}).get("urn") == dataset_urn:
- filtered_queries.append(query)
- break
-
- # 필터링된 결과로 응답 구조 재구성
- result["data"]["listQueries"]["queries"] = filtered_queries
- result["data"]["listQueries"]["count"] = len(filtered_queries)
-
- return result
-
- def get_glossary_terms_by_urn(self, dataset_urn):
- """
- 특정 데이터셋 URN의 glossary terms를 조회하는 함수
-
- Args:
- dataset_urn (str): 데이터셋 URN
-
- Returns:
- dict: glossary terms 정보
- """
- variables = {"urn": dataset_urn}
- return self.client.execute_graphql_query(GLOSSARY_TERMS_BY_URN_QUERY, variables)
diff --git a/utils/data/datahub_source.py b/utils/data/datahub_source.py
deleted file mode 100644
index aeb261a..0000000
--- a/utils/data/datahub_source.py
+++ /dev/null
@@ -1,145 +0,0 @@
-"""
-DataHub 메타데이터 페처 - 리팩토링된 버전
-
-기존 DatahubMetadataFetcher의 모든 기능을 유지하면서
-내부적으로는 분리된 서비스 모듈들을 사용합니다.
-
-기존 코드와의 완벽한 호환성을 보장합니다.
-"""
-
-from utils.data.datahub_services.base_client import DataHubBaseClient
-from utils.data.datahub_services.glossary_service import GlossaryService
-from utils.data.datahub_services.metadata_service import MetadataService
-from utils.data.datahub_services.query_service import QueryService
-
-
-class DatahubMetadataFetcher:
- """
- DataHub 메타데이터 페처 - 기존 인터페이스 유지
-
- 내부적으로는 분리된 서비스들을 사용하지만
- 외부 인터페이스는 기존과 동일하게 유지됩니다.
- """
-
- def __init__(self, gms_server="http://localhost:8080", extra_headers={}):
- """
- DataHub 메타데이터 페처 초기화
-
- Args:
- gms_server (str): DataHub GMS 서버 URL
- extra_headers (dict): 추가 HTTP 헤더
- """
- # 기본 클라이언트 초기화
- self.client = DataHubBaseClient(gms_server, extra_headers)
-
- # 서비스들 초기화
- self.metadata_service = MetadataService(self.client)
- self.query_service = QueryService(self.client)
- self.glossary_service = GlossaryService(self.client)
-
- # 기존 속성들 호환성을 위해 유지
- self.gms_server = gms_server
- self.emitter = self.client.emitter
- self.datahub_graph = self.client.datahub_graph
-
- # === 기존 인터페이스 유지 - 메타데이터 관련 ===
-
- def get_urns(self):
- """필터를 적용하여 데이터셋의 URN 가져오기"""
- return self.client.get_urns()
-
- def get_table_name(self, urn):
- """URN에 대한 테이블 이름 가져오기"""
- return self.metadata_service.get_table_name(urn)
-
- def get_table_description(self, urn):
- """URN에 대한 테이블 설명 가져오기"""
- return self.metadata_service.get_table_description(urn)
-
- def get_column_names_and_descriptions(self, urn):
- """URN에 대한 컬럼 이름 및 설명 가져오기"""
- return self.metadata_service.get_column_names_and_descriptions(urn)
-
- def get_table_lineage(
- self, urn, counts=100, direction="DOWNSTREAM", degree_values=None
- ):
- """URN에 대한 DOWNSTREAM/UPSTREAM lineage entity를 counts 만큼 가져오는 함수"""
- return self.metadata_service.get_table_lineage(
- urn, counts, direction, degree_values
- )
-
- def get_column_lineage(self, urn):
- """URN에 대한 UPSTREAM lineage의 column source를 가져오는 함수"""
- return self.metadata_service.get_column_lineage(urn)
-
- def min_degree_lineage(self, lineage_result):
- """lineage 중 최소 degree만 가져오는 함수"""
- return self.metadata_service.min_degree_lineage(lineage_result)
-
- def build_table_metadata(self, urn, max_degree=2, sort_by_degree=True):
- """테이블 단위로 테이블 이름, 설명, 컬럼, 테이블 별 리니지(downstream/upstream), 컬럼 별 리니지(upstream)이 포함된 메타데이터 생성 함수"""
- return self.metadata_service.build_table_metadata(
- urn, max_degree, sort_by_degree
- )
-
- def get_urn_info(self, urn):
- """특정 URN에 대한 모든 관련 정보를 가져오는 함수"""
- return self.metadata_service.get_urn_info(urn)
-
- def _print_urn_details(self, metadata):
- """URN 메타데이터를 보기 좋게 출력하는 내부 함수"""
- return self.metadata_service._print_urn_details(metadata)
-
- # === 기존 인터페이스 유지 - 용어집 관련 ===
-
- def get_root_glossary_nodes(self):
- """DataHub에서 루트 용어집 노드를 가져오는 함수"""
- return self.glossary_service.get_root_glossary_nodes()
-
- def get_glossary_node_by_urn(self, urn):
- """DataHub에서 특정 URN의 용어집 노드 및 그 자식 항목을 가져오는 함수"""
- return self.glossary_service.get_glossary_node_by_urn(urn)
-
- def get_node_basic_info(self, node, index):
- """용어집 노드의 기본 정보를 딕셔너리로 반환하는 함수"""
- return self.glossary_service.get_node_basic_info(node, index)
-
- def get_child_entity_info(self, entity, index):
- """자식 엔티티(용어 또는 노드)의 정보를 딕셔너리로 반환하는 함수"""
- return self.glossary_service.get_child_entity_info(entity, index)
-
- def process_node_details(self, node):
- """노드의 상세 정보를 처리하고 딕셔너리로 반환하는 함수"""
- return self.glossary_service.process_node_details(node)
-
- def process_glossary_nodes(self, result):
- """용어집 노드 결과를 처리하고 딕셔너리로 반환하는 함수"""
- return self.glossary_service.process_glossary_nodes(result)
-
- def get_glossary_data(self):
- """DataHub에서 전체 용어집 데이터를 가져와 처리하는 함수"""
- return self.glossary_service.get_glossary_data()
-
- def get_queries(self, start=0, count=10, query="*", filters=None):
- """DataHub에서 쿼리 목록을 가져오는 함수"""
- return self.query_service.get_queries(start, count, query, filters)
-
- def process_queries(self, result):
- """쿼리 목록 결과를 처리하고 간소화된 형태로 반환하는 함수"""
- return self.query_service.process_queries(result)
-
- def get_query_data(self, start=0, count=10, query="*", filters=None):
- """DataHub에서 쿼리 목록을 가져와 처리하는 함수"""
- return self.query_service.get_query_data(start, count, query, filters)
-
- def get_queries_by_urn(self, dataset_urn):
- """특정 데이터셋 URN과 연관된 쿼리들을 조회하는 함수"""
- return self.query_service.get_queries_by_urn(dataset_urn)
-
- def get_glossary_terms_by_urn(self, dataset_urn):
- """특정 데이터셋 URN의 glossary terms를 조회하는 함수"""
- return self.glossary_service.get_glossary_terms_by_urn(dataset_urn)
-
- def _is_valid_gms_server(self, gms_server):
- """GMS 서버 주소의 유효성을 검사하는 함수 (하위 호환성)"""
- return self.client._is_valid_gms_server(gms_server)
diff --git a/utils/data/queries.py b/utils/data/queries.py
deleted file mode 100644
index 74a7933..0000000
--- a/utils/data/queries.py
+++ /dev/null
@@ -1,3694 +0,0 @@
-# DataHub GraphQL 쿼리 모음
-
-ROOT_GLOSSARY_NODES_QUERY = """
-query getRootGlossaryNodes {
- getRootGlossaryNodes(input: {start: 0, count: 1000}) {
- count
- start
- total
- nodes {
- ...rootGlossaryNodeWithFourLayers
- __typename
- }
- __typename
- }
-}
-
-fragment rootGlossaryNodeWithFourLayers on GlossaryNode {
- urn
- type
- properties {
- name
- description
- __typename
- }
- displayProperties {
- ...displayPropertiesFields
- __typename
- }
- children: relationships(
- input: {types: ["IsPartOf"], direction: INCOMING, start: 0, count: 500}
- ) {
- total
- relationships {
- type
- entity {
- type
- ... on GlossaryTerm {
- urn
- __typename
- }
- ... on GlossaryNode {
- urn
- children: relationships(
- input: {types: ["IsPartOf"], direction: INCOMING, start: 0, count: 500}
- ) {
- total
- relationships {
- type
- entity {
- type
- ... on GlossaryTerm {
- urn
- __typename
- }
- ... on GlossaryNode {
- urn
- children: relationships(
- input: {types: ["IsPartOf"], direction: INCOMING, start: 0, count: 500}
- ) {
- total
- relationships {
- type
- entity {
- type
- ... on GlossaryTerm {
- urn
- __typename
- }
- ... on GlossaryNode {
- urn
- children: relationships(
- input: {types: ["IsPartOf"], direction: INCOMING, start: 0, count: 500}
- ) {
- total
- relationships {
- type
- entity {
- type
- ... on GlossaryTerm {
- urn
- __typename
- }
- ... on GlossaryNode {
- urn
- __typename
- }
- __typename
- }
- __typename
- }
- __typename
- }
- __typename
- }
- __typename
- }
- __typename
- }
- __typename
- }
- __typename
- }
- __typename
- }
- __typename
- }
- __typename
- }
- __typename
- }
- __typename
- }
- __typename
- }
- __typename
- }
- __typename
-}
-
-fragment displayPropertiesFields on DisplayProperties {
- colorHex
- icon {
- name
- style
- iconLibrary
- __typename
- }
- __typename
-}
-"""
-
-GLOSSARY_NODE_QUERY = """
-query getGlossaryNode($urn: String!) {
- glossaryNode(urn: $urn) {
- urn
- type
- exists
- properties {
- name
- description
- __typename
- }
- ownership {
- ...ownershipFields
- __typename
- }
- parentNodes {
- ...parentNodesFields
- __typename
- }
- privileges {
- ...entityPrivileges
- __typename
- }
- autoRenderAspects: aspects(input: {autoRenderOnly: true}) {
- ...autoRenderAspectFields
- __typename
- }
- structuredProperties {
- properties {
- ...structuredPropertiesFields
- __typename
- }
- __typename
- }
- forms {
- ...formsFields
- __typename
- }
- children: relationships(
- input: {types: ["IsPartOf"], direction: INCOMING, start: 0, count: 10000}
- ) {
- total
- relationships {
- direction
- entity {
- type
- urn
- ... on GlossaryNode {
- ...glossaryNode
- __typename
- }
- ... on GlossaryTerm {
- ...childGlossaryTerm
- __typename
- }
- __typename
- }
- __typename
- }
- __typename
- }
- displayProperties {
- ...displayPropertiesFields
- __typename
- }
- ...notes
- __typename
- }
-}
-
-fragment ownershipFields on Ownership {
- owners {
- owner {
- ... on CorpUser {
- urn
- type
- username
- info {
- active
- displayName
- title
- email
- firstName
- lastName
- fullName
- __typename
- }
- properties {
- active
- displayName
- title
- email
- firstName
- lastName
- fullName
- __typename
- }
- editableProperties {
- displayName
- title
- pictureLink
- email
- __typename
- }
- __typename
- }
- ... on CorpGroup {
- urn
- type
- name
- properties {
- displayName
- email
- __typename
- }
- info {
- displayName
- email
- admins {
- urn
- username
- info {
- active
- displayName
- title
- email
- firstName
- lastName
- fullName
- __typename
- }
- editableInfo {
- pictureLink
- teams
- skills
- __typename
- }
- __typename
- }
- members {
- urn
- username
- info {
- active
- displayName
- title
- email
- firstName
- lastName
- fullName
- __typename
- }
- editableInfo {
- pictureLink
- teams
- skills
- __typename
- }
- __typename
- }
- groups
- __typename
- }
- __typename
- }
- __typename
- }
- type
- ownershipType {
- urn
- type
- info {
- name
- description
- __typename
- }
- status {
- removed
- __typename
- }
- __typename
- }
- associatedUrn
- __typename
- }
- lastModified {
- time
- __typename
- }
- __typename
-}
-
-fragment parentNodesFields on ParentNodesResult {
- count
- nodes {
- urn
- type
- properties {
- name
- __typename
- }
- displayProperties {
- ...displayPropertiesFields
- __typename
- }
- __typename
- }
- __typename
-}
-
-fragment displayPropertiesFields on DisplayProperties {
- colorHex
- icon {
- name
- style
- iconLibrary
- __typename
- }
- __typename
-}
-
-fragment entityPrivileges on EntityPrivileges {
- canEditLineage
- canEditDomains
- canEditDataProducts
- canEditTags
- canEditGlossaryTerms
- canEditDescription
- canEditLinks
- canEditOwners
- canEditAssertions
- canEditIncidents
- canEditDeprecation
- canEditSchemaFieldTags
- canEditSchemaFieldGlossaryTerms
- canEditSchemaFieldDescription
- canEditQueries
- canEditEmbed
- canManageEntity
- canManageChildren
- canEditProperties
- canViewDatasetUsage
- canViewDatasetProfile
- canViewDatasetOperations
- __typename
-}
-
-fragment autoRenderAspectFields on RawAspect {
- aspectName
- payload
- renderSpec {
- displayType
- displayName
- key
- __typename
- }
- __typename
-}
-
-fragment structuredPropertiesFields on StructuredPropertiesEntry {
- structuredProperty {
- exists
- ...structuredPropertyFields
- __typename
- }
- values {
- ... on StringValue {
- stringValue
- __typename
- }
- ... on NumberValue {
- numberValue
- __typename
- }
- __typename
- }
- valueEntities {
- urn
- type
- ...entityDisplayNameFields
- __typename
- }
- associatedUrn
- __typename
-}
-
-fragment structuredPropertyFields on StructuredPropertyEntity {
- urn
- type
- definition {
- displayName
- qualifiedName
- description
- cardinality
- immutable
- valueType {
- urn
- type
- info {
- type
- displayName
- __typename
- }
- __typename
- }
- entityTypes {
- urn
- type
- info {
- type
- __typename
- }
- __typename
- }
- cardinality
- typeQualifier {
- allowedTypes {
- urn
- type
- info {
- type
- displayName
- __typename
- }
- __typename
- }
- __typename
- }
- allowedValues {
- value {
- ... on StringValue {
- stringValue
- __typename
- }
- ... on NumberValue {
- numberValue
- __typename
- }
- __typename
- }
- description
- __typename
- }
- created {
- time
- actor {
- urn
- editableProperties {
- displayName
- pictureLink
- __typename
- }
- ...entityDisplayNameFields
- __typename
- }
- __typename
- }
- lastModified {
- time
- actor {
- urn
- editableProperties {
- displayName
- pictureLink
- __typename
- }
- ...entityDisplayNameFields
- __typename
- }
- __typename
- }
- __typename
- }
- settings {
- isHidden
- showInSearchFilters
- showAsAssetBadge
- showInAssetSummary
- showInColumnsTable
- __typename
- }
- __typename
-}
-
-fragment entityDisplayNameFields on Entity {
- urn
- type
- ... on Dataset {
- name
- properties {
- name
- qualifiedName
- __typename
- }
- __typename
- }
- ... on CorpUser {
- username
- properties {
- displayName
- title
- firstName
- lastName
- fullName
- email
- __typename
- }
- info {
- active
- displayName
- title
- firstName
- lastName
- fullName
- email
- __typename
- }
- __typename
- }
- ... on CorpGroup {
- name
- info {
- displayName
- __typename
- }
- __typename
- }
- ... on Dashboard {
- dashboardId
- properties {
- name
- __typename
- }
- __typename
- }
- ... on Chart {
- chartId
- properties {
- name
- __typename
- }
- __typename
- }
- ... on DataFlow {
- properties {
- name
- __typename
- }
- __typename
- }
- ... on DataJob {
- jobId
- properties {
- name
- __typename
- }
- __typename
- }
- ... on GlossaryTerm {
- name
- hierarchicalName
- properties {
- name
- __typename
- }
- __typename
- }
- ... on GlossaryNode {
- properties {
- name
- description
- __typename
- }
- __typename
- }
- ... on Domain {
- properties {
- name
- __typename
- }
- __typename
- }
- ... on Container {
- properties {
- name
- __typename
- }
- __typename
- }
- ... on MLFeatureTable {
- name
- __typename
- }
- ... on MLFeature {
- name
- __typename
- }
- ... on MLPrimaryKey {
- name
- __typename
- }
- ... on MLModel {
- name
- __typename
- }
- ... on MLModelGroup {
- name
- __typename
- }
- ... on Tag {
- name
- properties {
- name
- colorHex
- __typename
- }
- __typename
- }
- ... on DataPlatform {
- ...nonConflictingPlatformFields
- __typename
- }
- ... on DataProduct {
- properties {
- name
- __typename
- }
- __typename
- }
- ... on DataPlatformInstance {
- instanceId
- __typename
- }
- ... on StructuredPropertyEntity {
- definition {
- displayName
- qualifiedName
- __typename
- }
- __typename
- }
- ... on SchemaFieldEntity {
- fieldPath
- __typename
- }
- ... on OwnershipTypeEntity {
- info {
- name
- __typename
- }
- __typename
- }
- __typename
-}
-
-fragment nonConflictingPlatformFields on DataPlatform {
- urn
- type
- name
- properties {
- displayName
- datasetNameDelimiter
- logoUrl
- __typename
- }
- displayName
- info {
- type
- displayName
- datasetNameDelimiter
- logoUrl
- __typename
- }
- __typename
-}
-
-fragment formsFields on Forms {
- completedForms {
- ...formAssociationFields
- __typename
- }
- incompleteForms {
- ...formAssociationFields
- __typename
- }
- verifications {
- form {
- urn
- __typename
- }
- lastModified {
- time
- actor {
- urn
- type
- ...entityDisplayNameFields
- __typename
- }
- __typename
- }
- __typename
- }
- __typename
-}
-
-fragment formAssociationFields on FormAssociation {
- associatedUrn
- incompletePrompts {
- ...formPromptAssociationFields
- __typename
- }
- completedPrompts {
- ...formPromptAssociationFields
- __typename
- }
- form {
- urn
- type
- info {
- name
- description
- type
- prompts {
- id
- formUrn
- title
- description
- type
- required
- structuredPropertyParams {
- structuredProperty {
- ...structuredPropertyFields
- __typename
- }
- __typename
- }
- __typename
- }
- actors {
- owners
- isAssignedToMe
- __typename
- }
- __typename
- }
- ownership {
- ...ownershipFields
- __typename
- }
- __typename
- }
- __typename
-}
-
-fragment formPromptAssociationFields on FormPromptAssociation {
- id
- lastModified {
- time
- actor {
- urn
- type
- ...entityDisplayNameFields
- __typename
- }
- __typename
- }
- fieldAssociations {
- completedFieldPrompts {
- fieldPath
- lastModified {
- time
- actor {
- urn
- type
- ...entityDisplayNameFields
- __typename
- }
- __typename
- }
- __typename
- }
- __typename
- }
- __typename
-}
-
-fragment glossaryNode on GlossaryNode {
- urn
- type
- properties {
- name
- description
- __typename
- }
- displayProperties {
- ...displayPropertiesFields
- __typename
- }
- children: relationships(
- input: {types: ["IsPartOf"], direction: INCOMING, start: 0, count: 1000}
- ) {
- total
- relationships {
- type
- entity {
- ... on GlossaryTerm {
- urn
- name
- type
- hierarchicalName
- properties {
- name
- description
- __typename
- }
- __typename
- }
- ... on GlossaryNode {
- urn
- __typename
- }
- __typename
- }
- __typename
- }
- __typename
- }
- __typename
-}
-
-fragment childGlossaryTerm on GlossaryTerm {
- urn
- type
- name
- hierarchicalName
- properties {
- name
- description
- __typename
- }
- __typename
-}
-
-fragment notes on Entity {
- notes: relationships(
- input: {types: ["PostTarget"], direction: INCOMING, start: 0, count: 100}
- ) {
- total
- relationships {
- type
- entity {
- ... on Post {
- urn
- type
- postType
- lastModified {
- time
- actor
- __typename
- }
- content {
- contentType
- title
- description
- link
- media {
- type
- location
- __typename
- }
- __typename
- }
- __typename
- }
- __typename
- }
- __typename
- }
- __typename
- }
- __typename
-}
-"""
-
-LIST_QUERIES_QUERY = """
-query listQueries($input: ListQueriesInput!) {
- listQueries(input: $input) {
- start
- total
- count
- queries {
- ...query
- __typename
- }
- __typename
- }
-}
-
-fragment query on QueryEntity {
- urn
- properties {
- name
- description
- source
- statement {
- value
- language
- __typename
- }
- created {
- time
- actor
- __typename
- }
- lastModified {
- time
- actor
- __typename
- }
- origin {
- ...searchResultFields
- __typename
- }
- __typename
- }
- platform {
- ...platformFields
- __typename
- }
- subjects {
- dataset {
- urn
- type
- name
- __typename
- }
- schemaField {
- urn
- type
- fieldPath
- __typename
- }
- __typename
- }
- __typename
-}
-
-fragment searchResultFields on Entity {
- ...searchResultFieldsNoLineage
- ... on EntityWithRelationships {
- upstream: lineage(input: {direction: UPSTREAM, start: 0, count: 100}) {
- total
- filtered
- __typename
- }
- downstream: lineage(input: {direction: DOWNSTREAM, start: 0, count: 100}) {
- total
- filtered
- __typename
- }
- __typename
- }
- __typename
-}
-
-fragment searchResultFieldsNoLineage on Entity {
- ...searchResultsWithoutSchemaField
- ... on SchemaFieldEntity {
- ...entityField
- __typename
- }
- __typename
-}
-
-fragment searchResultsWithoutSchemaField on Entity {
- urn
- type
- ... on Dataset {
- ...nonSiblingsDatasetSearchFields
- siblings {
- isPrimary
- siblings {
- urn
- type
- ... on Dataset {
- ...nonSiblingsDatasetSearchFields
- structuredProperties {
- properties {
- ...structuredPropertiesFields
- __typename
- }
- __typename
- }
- __typename
- }
- __typename
- }
- __typename
- }
- siblingsSearch(input: {query: "*", count: 1}) {
- count
- total
- searchResults {
- entity {
- urn
- type
- ... on Dataset {
- ...nonSiblingsDatasetSearchFields
- siblings {
- isPrimary
- __typename
- }
- structuredProperties {
- properties {
- ...structuredPropertiesFields
- __typename
- }
- __typename
- }
- __typename
- }
- __typename
- }
- __typename
- }
- __typename
- }
- browsePathV2 {
- ...browsePathV2Fields
- __typename
- }
- structuredProperties {
- properties {
- ...structuredPropertiesFields
- __typename
- }
- __typename
- }
- __typename
- }
- ... on DataPlatformInstance {
- ...dataPlatformInstanceFields
- __typename
- }
- ... on Role {
- id
- properties {
- name
- description
- __typename
- }
- __typename
- }
- ... on CorpUser {
- username
- properties {
- active
- displayName
- title
- firstName
- lastName
- fullName
- email
- departmentName
- title
- __typename
- }
- info {
- active
- displayName
- title
- firstName
- lastName
- fullName
- email
- departmentName
- title
- __typename
- }
- editableProperties {
- displayName
- title
- pictureLink
- __typename
- }
- __typename
- }
- ... on CorpGroup {
- name
- info {
- displayName
- description
- __typename
- }
- memberCount: relationships(
- input: {types: ["IsMemberOfGroup", "IsMemberOfNativeGroup"], direction: INCOMING, start: 0, count: 1}
- ) {
- total
- __typename
- }
- __typename
- }
- ... on Dashboard {
- tool
- dashboardId
- properties {
- name
- description
- externalUrl
- access
- lastModified {
- time
- __typename
- }
- __typename
- }
- ownership {
- ...ownershipFields
- __typename
- }
- globalTags {
- ...globalTagsFields
- __typename
- }
- glossaryTerms {
- ...glossaryTerms
- __typename
- }
- editableProperties {
- description
- __typename
- }
- platform {
- ...platformFields
- __typename
- }
- dataPlatformInstance {
- ...dataPlatformInstanceFields
- __typename
- }
- domain {
- ...entityDomain
- __typename
- }
- ...entityDataProduct
- deprecation {
- ...deprecationFields
- __typename
- }
- parentContainers {
- ...parentContainersFields
- __typename
- }
- statsSummary {
- viewCount
- uniqueUserCountLast30Days
- topUsersLast30Days {
- urn
- type
- username
- properties {
- displayName
- firstName
- lastName
- fullName
- __typename
- }
- editableProperties {
- displayName
- pictureLink
- __typename
- }
- __typename
- }
- __typename
- }
- subTypes {
- typeNames
- __typename
- }
- health {
- ...entityHealth
- __typename
- }
- structuredProperties {
- properties {
- ...structuredPropertiesFields
- __typename
- }
- __typename
- }
- browsePathV2 {
- ...browsePathV2Fields
- __typename
- }
- __typename
- }
- ... on Chart {
- chartId
- properties {
- name
- description
- externalUrl
- type
- access
- lastModified {
- time
- __typename
- }
- created {
- time
- __typename
- }
- __typename
- }
- ownership {
- ...ownershipFields
- __typename
- }
- globalTags {
- ...globalTagsFields
- __typename
- }
- glossaryTerms {
- ...glossaryTerms
- __typename
- }
- editableProperties {
- description
- __typename
- }
- platform {
- ...platformFields
- __typename
- }
- dataPlatformInstance {
- ...dataPlatformInstanceFields
- __typename
- }
- domain {
- ...entityDomain
- __typename
- }
- ...entityDataProduct
- deprecation {
- ...deprecationFields
- __typename
- }
- parentContainers {
- ...parentContainersFields
- __typename
- }
- browsePathV2 {
- ...browsePathV2Fields
- __typename
- }
- statsSummary {
- viewCount
- uniqueUserCountLast30Days
- topUsersLast30Days {
- urn
- type
- username
- properties {
- displayName
- firstName
- lastName
- fullName
- __typename
- }
- editableProperties {
- displayName
- pictureLink
- __typename
- }
- __typename
- }
- __typename
- }
- subTypes {
- typeNames
- __typename
- }
- health {
- ...entityHealth
- __typename
- }
- structuredProperties {
- properties {
- ...structuredPropertiesFields
- __typename
- }
- __typename
- }
- __typename
- }
- ... on DataFlow {
- orchestrator
- flowId
- cluster
- properties {
- name
- description
- project
- externalUrl
- __typename
- }
- ownership {
- ...ownershipFields
- __typename
- }
- globalTags {
- ...globalTagsFields
- __typename
- }
- glossaryTerms {
- ...glossaryTerms
- __typename
- }
- editableProperties {
- description
- __typename
- }
- platform {
- ...platformFields
- __typename
- }
- dataPlatformInstance {
- ...dataPlatformInstanceFields
- __typename
- }
- parentContainers {
- ...parentContainersFields
- __typename
- }
- domain {
- ...entityDomain
- __typename
- }
- ...entityDataProduct
- deprecation {
- ...deprecationFields
- __typename
- }
- childJobs: relationships(
- input: {types: ["IsPartOf"], direction: INCOMING, start: 0, count: 100}
- ) {
- total
- __typename
- }
- health {
- ...entityHealth
- __typename
- }
- structuredProperties {
- properties {
- ...structuredPropertiesFields
- __typename
- }
- __typename
- }
- __typename
- }
- ... on DataJob {
- dataFlow {
- ...nonRecursiveDataFlowFields
- __typename
- }
- jobId
- ownership {
- ...ownershipFields
- __typename
- }
- properties {
- name
- description
- externalUrl
- __typename
- }
- globalTags {
- ...globalTagsFields
- __typename
- }
- glossaryTerms {
- ...glossaryTerms
- __typename
- }
- editableProperties {
- description
- __typename
- }
- domain {
- ...entityDomain
- __typename
- }
- ...entityDataProduct
- deprecation {
- ...deprecationFields
- __typename
- }
- dataPlatformInstance {
- ...dataPlatformInstanceFields
- __typename
- }
- parentContainers {
- ...parentContainersFields
- __typename
- }
- subTypes {
- typeNames
- __typename
- }
- lastRun: runs(start: 0, count: 1) {
- count
- start
- total
- runs {
- urn
- type
- created {
- time
- actor
- __typename
- }
- __typename
- }
- __typename
- }
- health {
- ...entityHealth
- __typename
- }
- structuredProperties {
- properties {
- ...structuredPropertiesFields
- __typename
- }
- __typename
- }
- __typename
- }
- ... on GlossaryTerm {
- name
- hierarchicalName
- properties {
- name
- description
- termSource
- sourceRef
- sourceUrl
- rawSchema
- customProperties {
- key
- value
- __typename
- }
- __typename
- }
- deprecation {
- ...deprecationFields
- __typename
- }
- parentNodes {
- ...parentNodesFields
- __typename
- }
- domain {
- ...entityDomain
- __typename
- }
- structuredProperties {
- properties {
- ...structuredPropertiesFields
- __typename
- }
- __typename
- }
- __typename
- }
- ... on GlossaryNode {
- ...glossaryNode
- parentNodes {
- ...parentNodesFields
- __typename
- }
- structuredProperties {
- properties {
- ...structuredPropertiesFields
- __typename
- }
- __typename
- }
- __typename
- }
- ... on Domain {
- properties {
- name
- description
- __typename
- }
- ownership {
- ...ownershipFields
- __typename
- }
- parentDomains {
- ...parentDomainsFields
- __typename
- }
- displayProperties {
- ...displayPropertiesFields
- __typename
- }
- ...domainEntitiesFields
- structuredProperties {
- properties {
- ...structuredPropertiesFields
- __typename
- }
- __typename
- }
- __typename
- }
- ... on Container {
- properties {
- name
- description
- externalUrl
- __typename
- }
- platform {
- ...platformFields
- __typename
- }
- dataPlatformInstance {
- ...dataPlatformInstanceFields
- __typename
- }
- editableProperties {
- description
- __typename
- }
- ownership {
- ...ownershipFields
- __typename
- }
- tags {
- ...globalTagsFields
- __typename
- }
- glossaryTerms {
- ...glossaryTerms
- __typename
- }
- subTypes {
- typeNames
- __typename
- }
- entities(input: {}) {
- total
- __typename
- }
- deprecation {
- ...deprecationFields
- __typename
- }
- parentContainers {
- ...parentContainersFields
- __typename
- }
- structuredProperties {
- properties {
- ...structuredPropertiesFields
- __typename
- }
- __typename
- }
- __typename
- }
- ... on MLFeatureTable {
- name
- description
- featureTableProperties {
- description
- mlFeatures {
- urn
- __typename
- }
- mlPrimaryKeys {
- urn
- __typename
- }
- __typename
- }
- ownership {
- ...ownershipFields
- __typename
- }
- platform {
- ...platformFields
- __typename
- }
- deprecation {
- ...deprecationFields
- __typename
- }
- dataPlatformInstance {
- ...dataPlatformInstanceFields
- __typename
- }
- structuredProperties {
- properties {
- ...structuredPropertiesFields
- __typename
- }
- __typename
- }
- __typename
- }
- ... on MLFeature {
- ...nonRecursiveMLFeature
- structuredProperties {
- properties {
- ...structuredPropertiesFields
- __typename
- }
- __typename
- }
- __typename
- }
- ... on MLPrimaryKey {
- ...nonRecursiveMLPrimaryKey
- structuredProperties {
- properties {
- ...structuredPropertiesFields
- __typename
- }
- __typename
- }
- __typename
- }
- ... on MLModel {
- name
- description
- origin
- ownership {
- ...ownershipFields
- __typename
- }
- platform {
- ...platformFields
- __typename
- }
- deprecation {
- ...deprecationFields
- __typename
- }
- dataPlatformInstance {
- ...dataPlatformInstanceFields
- __typename
- }
- structuredProperties {
- properties {
- ...structuredPropertiesFields
- __typename
- }
- __typename
- }
- properties {
- propertiesName: name
- __typename
- }
- __typename
- }
- ... on MLModelGroup {
- name
- origin
- description
- ownership {
- ...ownershipFields
- __typename
- }
- platform {
- ...platformFields
- __typename
- }
- deprecation {
- ...deprecationFields
- __typename
- }
- dataPlatformInstance {
- ...dataPlatformInstanceFields
- __typename
- }
- structuredProperties {
- properties {
- ...structuredPropertiesFields
- __typename
- }
- __typename
- }
- properties {
- propertiesName: name
- __typename
- }
- __typename
- }
- ... on Tag {
- name
- properties {
- name
- colorHex
- __typename
- }
- description
- __typename
- }
- ... on DataPlatform {
- ...nonConflictingPlatformFields
- __typename
- }
- ... on DataProduct {
- ...dataProductSearchFields
- __typename
- }
- ... on ERModelRelationship {
- urn
- type
- id
- properties {
- ...ermodelrelationPropertiesFields
- __typename
- }
- editableProperties {
- ...ermodelrelationEditablePropertiesFields
- __typename
- }
- ownership {
- ...ownershipFields
- __typename
- }
- tags {
- ...globalTagsFields
- __typename
- }
- glossaryTerms {
- ...glossaryTerms
- __typename
- }
- __typename
- }
- ... on BusinessAttribute {
- ...businessAttributeFields
- __typename
- }
- ... on StructuredPropertyEntity {
- ...structuredPropertyFields
- __typename
- }
- ... on SupportsVersions {
- versionProperties {
- ...versionProperties
- __typename
- }
- __typename
- }
- ... on DataProcessInstance {
- ...dataProcessInstanceFields
- __typename
- }
- ... on DataPlatformInstance {
- ...dataPlatformInstanceFields
- __typename
- }
- __typename
-}
-
-fragment nonSiblingsDatasetSearchFields on Dataset {
- exists
- name
- origin
- uri
- platform {
- ...platformFields
- __typename
- }
- dataPlatformInstance {
- ...dataPlatformInstanceFields
- __typename
- }
- editableProperties {
- name
- description
- __typename
- }
- access {
- ...getAccess
- __typename
- }
- platformNativeType
- properties {
- name
- description
- qualifiedName
- customProperties {
- key
- value
- __typename
- }
- externalUrl
- lastModified {
- time
- actor
- __typename
- }
- __typename
- }
- ownership {
- ...ownershipFields
- __typename
- }
- globalTags {
- ...globalTagsFields
- __typename
- }
- glossaryTerms {
- ...glossaryTerms
- __typename
- }
- subTypes {
- typeNames
- __typename
- }
- domain {
- ...entityDomain
- __typename
- }
- ...entityDataProduct
- parentContainers {
- ...parentContainersFields
- __typename
- }
- deprecation {
- ...deprecationFields
- __typename
- }
- health {
- ...entityHealth
- __typename
- }
- ...datasetStatsFields
- __typename
-}
-
-fragment platformFields on DataPlatform {
- urn
- type
- lastIngested
- name
- properties {
- type
- displayName
- datasetNameDelimiter
- logoUrl
- __typename
- }
- displayName
- info {
- type
- displayName
- datasetNameDelimiter
- logoUrl
- __typename
- }
- __typename
-}
-
-fragment dataPlatformInstanceFields on DataPlatformInstance {
- urn
- type
- platform {
- ...platformFields
- __typename
- }
- instanceId
- __typename
-}
-
-fragment getAccess on Access {
- roles {
- role {
- ...getRolesName
- __typename
- }
- __typename
- }
- __typename
-}
-
-fragment getRolesName on Role {
- urn
- type
- id
- properties {
- name
- description
- type
- __typename
- }
- __typename
-}
-
-fragment ownershipFields on Ownership {
- owners {
- owner {
- ... on CorpUser {
- urn
- type
- username
- info {
- active
- displayName
- title
- email
- firstName
- lastName
- fullName
- __typename
- }
- properties {
- active
- displayName
- title
- email
- firstName
- lastName
- fullName
- __typename
- }
- editableProperties {
- displayName
- title
- pictureLink
- email
- __typename
- }
- __typename
- }
- ... on CorpGroup {
- urn
- type
- name
- properties {
- displayName
- email
- __typename
- }
- info {
- displayName
- email
- admins {
- urn
- username
- info {
- active
- displayName
- title
- email
- firstName
- lastName
- fullName
- __typename
- }
- editableInfo {
- pictureLink
- teams
- skills
- __typename
- }
- __typename
- }
- members {
- urn
- username
- info {
- active
- displayName
- title
- email
- firstName
- lastName
- fullName
- __typename
- }
- editableInfo {
- pictureLink
- teams
- skills
- __typename
- }
- __typename
- }
- groups
- __typename
- }
- __typename
- }
- __typename
- }
- type
- ownershipType {
- urn
- type
- info {
- name
- description
- __typename
- }
- status {
- removed
- __typename
- }
- __typename
- }
- associatedUrn
- __typename
- }
- lastModified {
- time
- __typename
- }
- __typename
-}
-
-fragment globalTagsFields on GlobalTags {
- tags {
- tag {
- urn
- type
- name
- description
- properties {
- name
- colorHex
- __typename
- }
- __typename
- }
- associatedUrn
- __typename
- }
- __typename
-}
-
-fragment glossaryTerms on GlossaryTerms {
- terms {
- term {
- ...glossaryTerm
- __typename
- }
- actor {
- urn
- __typename
- }
- associatedUrn
- __typename
- }
- __typename
-}
-
-fragment glossaryTerm on GlossaryTerm {
- urn
- name
- type
- hierarchicalName
- properties {
- name
- description
- definition
- termSource
- customProperties {
- key
- value
- __typename
- }
- __typename
- }
- ownership {
- ...ownershipFields
- __typename
- }
- parentNodes {
- ...parentNodesFields
- __typename
- }
- __typename
-}
-
-fragment parentNodesFields on ParentNodesResult {
- count
- nodes {
- urn
- type
- properties {
- name
- __typename
- }
- displayProperties {
- ...displayPropertiesFields
- __typename
- }
- __typename
- }
- __typename
-}
-
-fragment displayPropertiesFields on DisplayProperties {
- colorHex
- icon {
- name
- style
- iconLibrary
- __typename
- }
- __typename
-}
-
-fragment entityDomain on DomainAssociation {
- domain {
- urn
- type
- properties {
- name
- description
- __typename
- }
- parentDomains {
- ...parentDomainsFields
- __typename
- }
- ...domainEntitiesFields
- displayProperties {
- ...displayPropertiesFields
- __typename
- }
- __typename
- }
- associatedUrn
- __typename
-}
-
-fragment parentDomainsFields on ParentDomainsResult {
- count
- domains {
- urn
- type
- ... on Domain {
- displayProperties {
- ...displayPropertiesFields
- __typename
- }
- properties {
- name
- description
- __typename
- }
- __typename
- }
- __typename
- }
- __typename
-}
-
-fragment domainEntitiesFields on Domain {
- entities(input: {start: 0, count: 0}) {
- total
- __typename
- }
- dataProducts: entities(
- input: {start: 0, count: 0, filters: [{field: "_entityType", values: "DATA_PRODUCT"}]}
- ) {
- total
- __typename
- }
- children: relationships(
- input: {types: ["IsPartOf"], direction: INCOMING, start: 0, count: 0}
- ) {
- total
- __typename
- }
- __typename
-}
-
-fragment entityDataProduct on Entity {
- dataProduct: relationships(
- input: {types: ["DataProductContains"], direction: INCOMING, start: 0, count: 1}
- ) {
- relationships {
- type
- entity {
- urn
- type
- ... on DataProduct {
- properties {
- name
- description
- __typename
- }
- domain {
- ...entityDomain
- __typename
- }
- __typename
- }
- __typename
- }
- __typename
- }
- __typename
- }
- __typename
-}
-
-fragment parentContainersFields on ParentContainersResult {
- count
- containers {
- ...parentContainerFields
- __typename
- }
- __typename
-}
-
-fragment parentContainerFields on Container {
- urn
- type
- properties {
- name
- __typename
- }
- subTypes {
- typeNames
- __typename
- }
- __typename
-}
-
-fragment deprecationFields on Deprecation {
- actor
- deprecated
- note
- decommissionTime
- actorEntity {
- urn
- type
- ...entityDisplayNameFields
- __typename
- }
- replacement {
- ...entityDisplayNameFields
- ... on Dataset {
- platform {
- ...platformFields
- __typename
- }
- __typename
- }
- __typename
- }
- __typename
-}
-
-fragment entityDisplayNameFields on Entity {
- urn
- type
- ... on Dataset {
- name
- properties {
- name
- qualifiedName
- __typename
- }
- __typename
- }
- ... on CorpUser {
- username
- properties {
- displayName
- title
- firstName
- lastName
- fullName
- email
- __typename
- }
- info {
- active
- displayName
- title
- firstName
- lastName
- fullName
- email
- __typename
- }
- __typename
- }
- ... on CorpGroup {
- name
- info {
- displayName
- __typename
- }
- __typename
- }
- ... on Dashboard {
- dashboardId
- properties {
- name
- __typename
- }
- __typename
- }
- ... on Chart {
- chartId
- properties {
- name
- __typename
- }
- __typename
- }
- ... on DataFlow {
- properties {
- name
- __typename
- }
- __typename
- }
- ... on DataJob {
- jobId
- properties {
- name
- __typename
- }
- __typename
- }
- ... on GlossaryTerm {
- name
- hierarchicalName
- properties {
- name
- __typename
- }
- __typename
- }
- ... on GlossaryNode {
- properties {
- name
- description
- __typename
- }
- __typename
- }
- ... on Domain {
- properties {
- name
- __typename
- }
- __typename
- }
- ... on Container {
- properties {
- name
- __typename
- }
- __typename
- }
- ... on MLFeatureTable {
- name
- __typename
- }
- ... on MLFeature {
- name
- __typename
- }
- ... on MLPrimaryKey {
- name
- __typename
- }
- ... on MLModel {
- name
- __typename
- }
- ... on MLModelGroup {
- name
- __typename
- }
- ... on Tag {
- name
- properties {
- name
- colorHex
- __typename
- }
- __typename
- }
- ... on DataPlatform {
- ...nonConflictingPlatformFields
- __typename
- }
- ... on DataProduct {
- properties {
- name
- __typename
- }
- __typename
- }
- ... on DataPlatformInstance {
- instanceId
- __typename
- }
- ... on StructuredPropertyEntity {
- definition {
- displayName
- qualifiedName
- __typename
- }
- __typename
- }
- ... on SchemaFieldEntity {
- fieldPath
- __typename
- }
- ... on OwnershipTypeEntity {
- info {
- name
- __typename
- }
- __typename
- }
- __typename
-}
-
-fragment nonConflictingPlatformFields on DataPlatform {
- urn
- type
- name
- properties {
- displayName
- datasetNameDelimiter
- logoUrl
- __typename
- }
- displayName
- info {
- type
- displayName
- datasetNameDelimiter
- logoUrl
- __typename
- }
- __typename
-}
-
-fragment entityHealth on Health {
- type
- status
- message
- causes
- __typename
-}
-
-fragment datasetStatsFields on Dataset {
- lastProfile: datasetProfiles(limit: 1) {
- rowCount
- columnCount
- sizeInBytes
- timestampMillis
- __typename
- }
- lastOperation: operations(limit: 1) {
- lastUpdatedTimestamp
- timestampMillis
- __typename
- }
- statsSummary {
- queryCountLast30Days
- uniqueUserCountLast30Days
- topUsersLast30Days {
- urn
- type
- username
- properties {
- displayName
- firstName
- lastName
- fullName
- __typename
- }
- editableProperties {
- displayName
- pictureLink
- __typename
- }
- __typename
- }
- __typename
- }
- __typename
-}
-
-fragment structuredPropertiesFields on StructuredPropertiesEntry {
- structuredProperty {
- exists
- ...structuredPropertyFields
- __typename
- }
- values {
- ... on StringValue {
- stringValue
- __typename
- }
- ... on NumberValue {
- numberValue
- __typename
- }
- __typename
- }
- valueEntities {
- urn
- type
- ...entityDisplayNameFields
- __typename
- }
- associatedUrn
- __typename
-}
-
-fragment structuredPropertyFields on StructuredPropertyEntity {
- urn
- type
- definition {
- displayName
- qualifiedName
- description
- cardinality
- immutable
- valueType {
- urn
- type
- info {
- type
- displayName
- __typename
- }
- __typename
- }
- entityTypes {
- urn
- type
- info {
- type
- __typename
- }
- __typename
- }
- cardinality
- typeQualifier {
- allowedTypes {
- urn
- type
- info {
- type
- displayName
- __typename
- }
- __typename
- }
- __typename
- }
- allowedValues {
- value {
- ... on StringValue {
- stringValue
- __typename
- }
- ... on NumberValue {
- numberValue
- __typename
- }
- __typename
- }
- description
- __typename
- }
- created {
- time
- actor {
- urn
- editableProperties {
- displayName
- pictureLink
- __typename
- }
- ...entityDisplayNameFields
- __typename
- }
- __typename
- }
- lastModified {
- time
- actor {
- urn
- editableProperties {
- displayName
- pictureLink
- __typename
- }
- ...entityDisplayNameFields
- __typename
- }
- __typename
- }
- __typename
- }
- settings {
- isHidden
- showInSearchFilters
- showAsAssetBadge
- showInAssetSummary
- showInColumnsTable
- __typename
- }
- __typename
-}
-
-fragment browsePathV2Fields on BrowsePathV2 {
- path {
- name
- entity {
- urn
- type
- ...entityDisplayNameFields
- __typename
- }
- __typename
- }
- __typename
-}
-
-fragment nonRecursiveDataFlowFields on DataFlow {
- urn
- type
- orchestrator
- flowId
- cluster
- properties {
- name
- description
- project
- externalUrl
- customProperties {
- key
- value
- __typename
- }
- __typename
- }
- editableProperties {
- description
- __typename
- }
- ownership {
- ...ownershipFields
- __typename
- }
- platform {
- ...platformFields
- __typename
- }
- domain {
- ...entityDomain
- __typename
- }
- ...entityDataProduct
- deprecation {
- ...deprecationFields
- __typename
- }
- __typename
-}
-
-fragment glossaryNode on GlossaryNode {
- urn
- type
- properties {
- name
- description
- __typename
- }
- displayProperties {
- ...displayPropertiesFields
- __typename
- }
- children: relationships(
- input: {types: ["IsPartOf"], direction: INCOMING, start: 0, count: 1000}
- ) {
- total
- relationships {
- type
- entity {
- ... on GlossaryTerm {
- urn
- name
- type
- hierarchicalName
- properties {
- name
- description
- __typename
- }
- __typename
- }
- ... on GlossaryNode {
- urn
- __typename
- }
- __typename
- }
- __typename
- }
- __typename
- }
- __typename
-}
-
-fragment nonRecursiveMLFeature on MLFeature {
- urn
- type
- exists
- lastIngested
- name
- featureNamespace
- description
- dataType
- properties {
- description
- dataType
- version {
- versionTag
- __typename
- }
- sources {
- urn
- name
- type
- origin
- description
- uri
- platform {
- ...platformFields
- __typename
- }
- platformNativeType
- __typename
- }
- __typename
- }
- ownership {
- ...ownershipFields
- __typename
- }
- institutionalMemory {
- ...institutionalMemoryFields
- __typename
- }
- status {
- removed
- __typename
- }
- glossaryTerms {
- ...glossaryTerms
- __typename
- }
- domain {
- ...entityDomain
- __typename
- }
- ...entityDataProduct
- tags {
- ...globalTagsFields
- __typename
- }
- editableProperties {
- description
- __typename
- }
- deprecation {
- ...deprecationFields
- __typename
- }
- dataPlatformInstance {
- ...dataPlatformInstanceFields
- __typename
- }
- browsePathV2 {
- ...browsePathV2Fields
- __typename
- }
- featureTables: relationships(
- input: {types: ["Contains"], direction: INCOMING, start: 0, count: 100}
- ) {
- relationships {
- type
- entity {
- ... on MLFeatureTable {
- platform {
- ...platformFields
- __typename
- }
- __typename
- }
- __typename
- }
- __typename
- }
- __typename
- }
- structuredProperties {
- properties {
- ...structuredPropertiesFields
- __typename
- }
- __typename
- }
- __typename
-}
-
-fragment institutionalMemoryFields on InstitutionalMemory {
- elements {
- url
- actor {
- ...resolvedActorFields
- __typename
- }
- description
- created {
- actor
- time
- __typename
- }
- associatedUrn
- __typename
- }
- __typename
-}
-
-fragment resolvedActorFields on ResolvedActor {
- ... on CorpUser {
- urn
- ...entityDisplayNameFields
- __typename
- }
- ... on CorpGroup {
- urn
- ...entityDisplayNameFields
- __typename
- }
- __typename
-}
-
-fragment nonRecursiveMLPrimaryKey on MLPrimaryKey {
- urn
- type
- exists
- lastIngested
- name
- featureNamespace
- description
- dataType
- properties {
- description
- dataType
- version {
- versionTag
- __typename
- }
- sources {
- urn
- name
- type
- origin
- description
- uri
- platform {
- ...platformFields
- __typename
- }
- platformNativeType
- __typename
- }
- __typename
- }
- ownership {
- ...ownershipFields
- __typename
- }
- institutionalMemory {
- ...institutionalMemoryFields
- __typename
- }
- status {
- removed
- __typename
- }
- glossaryTerms {
- ...glossaryTerms
- __typename
- }
- domain {
- ...entityDomain
- __typename
- }
- ...entityDataProduct
- tags {
- ...globalTagsFields
- __typename
- }
- editableProperties {
- description
- __typename
- }
- deprecation {
- ...deprecationFields
- __typename
- }
- dataPlatformInstance {
- ...dataPlatformInstanceFields
- __typename
- }
- featureTables: relationships(
- input: {types: ["KeyedBy"], direction: INCOMING, start: 0, count: 100}
- ) {
- relationships {
- type
- entity {
- ... on MLFeatureTable {
- platform {
- ...platformFields
- __typename
- }
- __typename
- }
- __typename
- }
- __typename
- }
- __typename
- }
- structuredProperties {
- properties {
- ...structuredPropertiesFields
- __typename
- }
- __typename
- }
- __typename
-}
-
-fragment dataProductSearchFields on DataProduct {
- urn
- type
- properties {
- name
- description
- externalUrl
- __typename
- }
- ownership {
- ...ownershipFields
- __typename
- }
- tags {
- ...globalTagsFields
- __typename
- }
- glossaryTerms {
- ...glossaryTerms
- __typename
- }
- domain {
- ...entityDomain
- __typename
- }
- entities(input: {start: 0, count: 0, query: "*"}) {
- total
- __typename
- }
- __typename
-}
-
-fragment ermodelrelationPropertiesFields on ERModelRelationshipProperties {
- name
- source {
- ...datasetERModelRelationshipFields
- __typename
- }
- destination {
- ...datasetERModelRelationshipFields
- __typename
- }
- relationshipFieldMappings {
- ...relationshipFieldMapping
- __typename
- }
- createdTime
- createdActor {
- urn
- __typename
- }
- __typename
-}
-
-fragment datasetERModelRelationshipFields on Dataset {
- urn
- name
- properties {
- name
- description
- __typename
- }
- editableProperties {
- description
- __typename
- }
- schemaMetadata {
- ...schemaMetadataFields
- __typename
- }
- __typename
-}
-
-fragment schemaMetadataFields on SchemaMetadata {
- aspectVersion
- createdAt
- datasetUrn
- name
- platformUrn
- version
- cluster
- hash
- platformSchema {
- ... on TableSchema {
- schema
- __typename
- }
- ... on KeyValueSchema {
- keySchema
- valueSchema
- __typename
- }
- __typename
- }
- fields {
- ...entitySchemaFieldFields
- __typename
- }
- primaryKeys
- foreignKeys {
- name
- sourceFields {
- fieldPath
- __typename
- }
- foreignFields {
- fieldPath
- __typename
- }
- foreignDataset {
- urn
- name
- type
- origin
- uri
- properties {
- description
- __typename
- }
- platform {
- ...platformFields
- __typename
- }
- platformNativeType
- ownership {
- ...ownershipFields
- __typename
- }
- globalTags {
- ...globalTagsFields
- __typename
- }
- glossaryTerms {
- ...glossaryTerms
- __typename
- }
- __typename
- }
- __typename
- }
- __typename
-}
-
-fragment entitySchemaFieldFields on SchemaField {
- fieldPath
- label
- jsonPath
- nullable
- description
- type
- nativeDataType
- recursive
- isPartOfKey
- isPartitioningKey
- globalTags {
- ...globalTagsFields
- __typename
- }
- glossaryTerms {
- ...glossaryTerms
- __typename
- }
- schemaFieldEntity {
- ...entitySchemaFieldEntityFields
- __typename
- }
- __typename
-}
-
-fragment entitySchemaFieldEntityFields on SchemaFieldEntity {
- deprecation {
- ...deprecationFields
- __typename
- }
- urn
- fieldPath
- type
- structuredProperties {
- properties {
- ...structuredPropertiesFields
- __typename
- }
- __typename
- }
- businessAttributes {
- businessAttribute {
- ...businessAttribute
- __typename
- }
- __typename
- }
- documentation {
- ...documentationFields
- __typename
- }
- __typename
-}
-
-fragment businessAttribute on BusinessAttributeAssociation {
- businessAttribute {
- urn
- type
- ownership {
- ...ownershipFields
- __typename
- }
- properties {
- name
- description
- businessAttributeDataType: type
- lastModified {
- time
- __typename
- }
- created {
- time
- __typename
- }
- tags {
- tags {
- tag {
- urn
- name
- properties {
- name
- __typename
- }
- __typename
- }
- associatedUrn
- __typename
- }
- __typename
- }
- glossaryTerms {
- terms {
- term {
- urn
- type
- properties {
- name
- __typename
- }
- __typename
- }
- associatedUrn
- __typename
- }
- __typename
- }
- __typename
- }
- __typename
- }
- associatedUrn
- __typename
-}
-
-fragment documentationFields on Documentation {
- documentations {
- documentation
- attribution {
- time
- actor {
- urn
- type
- ...entityDisplayNameFields
- __typename
- }
- source {
- urn
- type
- __typename
- }
- sourceDetail {
- key
- value
- __typename
- }
- __typename
- }
- __typename
- }
- __typename
-}
-
-fragment relationshipFieldMapping on RelationshipFieldMapping {
- sourceField
- destinationField
- __typename
-}
-
-fragment ermodelrelationEditablePropertiesFields on ERModelRelationshipEditableProperties {
- description
- name
- __typename
-}
-
-fragment businessAttributeFields on BusinessAttribute {
- urn
- type
- ownership {
- ...ownershipFields
- __typename
- }
- properties {
- name
- description
- businessAttributeDataType: type
- customProperties {
- key
- value
- associatedUrn
- __typename
- }
- lastModified {
- time
- __typename
- }
- created {
- time
- __typename
- }
- tags {
- tags {
- tag {
- urn
- name
- properties {
- name
- __typename
- }
- __typename
- }
- associatedUrn
- __typename
- }
- __typename
- }
- glossaryTerms {
- terms {
- term {
- urn
- type
- properties {
- name
- __typename
- }
- __typename
- }
- associatedUrn
- __typename
- }
- __typename
- }
- __typename
- }
- institutionalMemory {
- ...institutionalMemoryFields
- __typename
- }
- __typename
-}
-
-fragment versionProperties on VersionProperties {
- versionSet {
- urn
- type
- __typename
- }
- isLatest
- version {
- versionTag
- __typename
- }
- aliases {
- versionTag
- __typename
- }
- comment
- created {
- time
- actor {
- urn
- ...entityDisplayNameFields
- editableProperties {
- displayName
- pictureLink
- __typename
- }
- __typename
- }
- __typename
- }
- createdInSource {
- time
- actor {
- urn
- ...entityDisplayNameFields
- editableProperties {
- displayName
- pictureLink
- __typename
- }
- __typename
- }
- __typename
- }
- __typename
-}
-
-fragment dataProcessInstanceFields on DataProcessInstance {
- urn
- type
- exists
- status {
- removed
- __typename
- }
- parentContainers {
- ...parentContainersFields
- __typename
- }
- container {
- ...entityContainer
- __typename
- }
- subTypes {
- typeNames
- __typename
- }
- properties {
- name
- created {
- time
- actor
- __typename
- }
- customProperties {
- key
- value
- __typename
- }
- externalUrl
- __typename
- }
- mlTrainingRunProperties {
- id
- outputUrls
- trainingMetrics {
- name
- description
- value
- __typename
- }
- hyperParams {
- name
- description
- value
- __typename
- }
- __typename
- }
- optionalPlatform: platform {
- ...platformFields
- __typename
- }
- dataPlatformInstance {
- ...dataPlatformInstanceFields
- __typename
- }
- state(startTimeMillis: null, endTimeMillis: null, limit: 1) {
- status
- attempt
- result {
- resultType
- nativeResultType
- __typename
- }
- timestampMillis
- durationMillis
- __typename
- }
- parentTemplate {
- urn
- type
- ... on Dataset {
- name
- properties {
- name
- description
- qualifiedName
- __typename
- }
- editableProperties {
- description
- __typename
- }
- platform {
- ...platformFields
- __typename
- }
- subTypes {
- typeNames
- __typename
- }
- status {
- removed
- __typename
- }
- __typename
- }
- ... on DataJob {
- urn
- type
- dataFlow {
- ...nonRecursiveDataFlowFields
- __typename
- }
- jobId
- properties {
- name
- description
- externalUrl
- customProperties {
- key
- value
- __typename
- }
- __typename
- }
- deprecation {
- ...deprecationFields
- __typename
- }
- dataPlatformInstance {
- ...dataPlatformInstanceFields
- __typename
- }
- subTypes {
- typeNames
- __typename
- }
- editableProperties {
- description
- __typename
- }
- status {
- removed
- __typename
- }
- __typename
- }
- __typename
- }
- __typename
-}
-
-fragment entityContainer on Container {
- urn
- platform {
- ...platformFields
- __typename
- }
- properties {
- name
- __typename
- }
- subTypes {
- typeNames
- __typename
- }
- deprecation {
- ...deprecationFields
- __typename
- }
- __typename
-}
-
-fragment entityField on SchemaFieldEntity {
- urn
- type
- parent {
- urn
- type
- ...entityDisplayNameFields
- ... on Dataset {
- platform {
- ...platformFields
- __typename
- }
- dataPlatformInstance {
- ...dataPlatformInstanceFields
- __typename
- }
- parentContainers {
- ...parentContainersFields
- __typename
- }
- __typename
- }
- __typename
- }
- fieldPath
- structuredProperties {
- properties {
- ...structuredPropertiesFields
- __typename
- }
- __typename
- }
- businessAttributes {
- businessAttribute {
- ...businessAttribute
- __typename
- }
- __typename
- }
- __typename
-}
-"""
-
-# 특정 URN과 연관된 쿼리를 찾는 GraphQL 쿼리 (수정된 버전)
-QUERIES_BY_URN_QUERY = """
-query listQueries($input: ListQueriesInput!) {
- listQueries(input: $input) {
- start
- total
- count
- queries {
- urn
- properties {
- name
- description
- statement {
- value
- language
- __typename
- }
- __typename
- }
- subjects {
- dataset {
- urn
- name
- __typename
- }
- __typename
- }
- __typename
- }
- __typename
- }
-}
-"""
-
-# 특정 URN의 glossary terms를 조회하는 GraphQL 쿼리
-GLOSSARY_TERMS_BY_URN_QUERY = """
-query getDataset($urn: String!) {
- dataset(urn: $urn) {
- urn
- name
- glossaryTerms {
- terms {
- term {
- urn
- name
- type
- hierarchicalName
- properties {
- name
- description
- definition
- __typename
- }
- parentNodes {
- nodes {
- urn
- properties {
- name
- __typename
- }
- __typename
- }
- __typename
- }
- __typename
- }
- __typename
- }
- __typename
- }
- __typename
- }
-}
-"""
diff --git a/utils/databases/README.md b/utils/databases/README.md
deleted file mode 100644
index 03a161c..0000000
--- a/utils/databases/README.md
+++ /dev/null
@@ -1,251 +0,0 @@
-# Databases 모듈
-
-다양한 데이터베이스에 대한 연결 및 SQL 실행 기능을 제공하는 유틸리티 모듈입니다.
-
-## 디렉토리 구조
-
-```
-databases/
-├── __init__.py
-├── config.py
-├── factory.py
-├── logger.py
-└── connector/
- ├── base_connector.py
- ├── clickhouse_connector.py
- ├── databricks_connector.py
- ├── duckdb_connector.py
- ├── mariadb_connector.py
- ├── mysql_connector.py
- ├── oracle_connector.py
- ├── postgres_connector.py
- ├── snowflake_connector.py
- ├── sqlite_connector.py
- ├── trino_connector.py
- └── README.md
-```
-
-## 파일 설명
-
-### __init__.py
-
-데이터베이스 유틸리티 패키지 초기화 모듈입니다. 주요 구성 요소를 외부로 노출합니다.
-
-**Export:**
-- `DatabaseFactory`: 데이터베이스 커넥터 팩토리 클래스
-- `DBConfig`: 데이터베이스 설정 타입
-
-**사용처:**
-- `interface/app_pages/settings_sections/db_section.py`: DB 설정 인터페이스에서 import
-- 다른 모듈에서 `from utils.databases import DatabaseFactory` 형태로 import되어 사용됨
-
-### config.py
-
-데이터베이스 연결 설정 정보를 정의하는 모듈입니다.
-
-**클래스:**
-- `DBConfig(TypedDict)`: 데이터베이스 연결 설정 정보를 표현하는 타입 딕셔너리
- - `host` (str): 데이터베이스 호스트명 또는 IP 주소
- - `port` (Optional[int]): 데이터베이스 포트 번호
- - `user` (Optional[str]): 접속 사용자명
- - `password` (Optional[str]): 접속 비밀번호
- - `database` (Optional[str]): 대상 데이터베이스 이름
- - `extra` (Optional[Dict[str, str]]): 드라이버별 추가 설정값
-
-**사용처:**
-- `utils.databases.factory`: DBConfig 타입을 사용하여 설정 정보 전달
-- `utils.databases.connector.*`: 모든 커넥터가 이 타입을 사용
-- `interface/core/config/models.py`: DBConnectionProfile 모델에서 참조
-
-### factory.py
-
-데이터베이스 커넥터 팩토리 모듈입니다. DB 타입에 따라 알맞은 커넥터 클래스를 동적으로 로드하고 인스턴스를 생성합니다.
-
-**클래스:**
-- `DatabaseFactory`: 데이터베이스 커넥터 팩토리 클래스
- - `get_connector(db_type, config)`: 주어진 DB 타입에 해당하는 Connector 인스턴스 반환
- - DB 타입이 지정되지 않은 경우 환경 변수(`DB_TYPE`)에서 자동으로 가져옴
- - config가 지정되지 않은 경우 `load_config_from_env()`로 환경 변수에서 로드
- - 지원되지 않는 DB 타입이거나 모듈을 로드할 수 없는 경우 `ValueError` 발생
-
-**함수:**
-- `load_config_from_env(prefix) -> DBConfig`: 환경변수에서 데이터베이스 접속 설정을 로드
- - prefix: 환경변수 접두어 (예: 'POSTGRES', 'MYSQL')
- - `{PREFIX}_HOST`, `{PREFIX}_PORT`, `{PREFIX}_USER`, `{PREFIX}_PASSWORD`, `{PREFIX}_DATABASE` 로드
- - 추가 커스텀 환경변수는 `extra` 필드에 포함
-
-**사용처:**
-- `interface/app_pages/settings_sections/db_section.py`: DB 연결 테스트 및 설정 적용 (221번 라인)
-- `interface/app_pages/settings_sections/db_section.py`: 환경변수 설정 검증 (272번 라인)
-- `utils.databases.__init__.py`: DatabaseFactory를 외부로 노출
-
-**환경변수 예시:**
-```bash
-DB_TYPE=postgres
-POSTGRES_HOST=localhost
-POSTGRES_PORT=5432
-POSTGRES_USER=myuser
-POSTGRES_PASSWORD=mypassword
-POSTGRES_DATABASE=mydb
-```
-
-### logger.py
-
-로깅 설정 모듈입니다. 애플리케이션 전역에서 사용할 기본 로깅 설정을 정의합니다.
-
-**내보내기:**
-- `logger`: 표준 로거 인스턴스
-
-**설정:**
-- 레벨: `INFO`
-- 포맷: `%(asctime)s [%(levelname)s] %(message)s`
-- 날짜 포맷: `%Y-%m-%d %H:%M:%S`
-
-**사용처:**
-- `utils.databases.factory`: DB 타입 로딩 실패 시 로깅
-- `utils.databases.connector.*`: 모든 커넥터에서 연결 및 SQL 실행 로깅
-
-### connector/
-
-데이터베이스별 커넥터 구현이 위치한 디렉토리입니다.
-
-자세한 내용은 [connector/README.md](connector/README.md)를 참조하세요.
-
-**주요 특징:**
-- 모든 커넥터는 `BaseConnector` 추상 클래스를 상속
-- 공통 인터페이스: `connect()`, `run_sql(sql) -> pd.DataFrame`, `close()`
-- 지원 데이터베이스: PostgreSQL, MySQL, MariaDB, Oracle, SQLite, DuckDB, Snowflake, Databricks, Trino, ClickHouse
-
-## 사용 방법
-
-### Factory 패턴 사용 (권장)
-
-환경 변수를 설정하고 DatabaseFactory를 사용하여 커넥터를 생성합니다:
-
-```python
-from utils.databases import DatabaseFactory
-
-# 환경 변수 사용
-connector = DatabaseFactory.get_connector()
-result = connector.run_sql("SELECT * FROM users LIMIT 10")
-connector.close()
-```
-
-### 명시적 설정 사용
-
-DBConfig를 직접 생성하여 전달할 수 있습니다:
-
-```python
-from utils.databases import DatabaseFactory, DBConfig
-
-config = DBConfig(
- host="localhost",
- port=5432,
- user="myuser",
- password="mypassword",
- database="mydb",
- extra={}
-)
-connector = DatabaseFactory.get_connector(db_type="postgres", config=config)
-result = connector.run_sql("SELECT * FROM users LIMIT 10")
-connector.close()
-```
-
-### 직접 커넥터 사용
-
-특정 커넥터를 직접 import하여 사용할 수 있습니다:
-
-```python
-from utils.databases.connector.postgres_connector import PostgresConnector
-from utils.databases.config import DBConfig
-
-config = DBConfig(
- host="localhost",
- port=5432,
- user="myuser",
- password="mypassword",
- database="mydb",
- extra={}
-)
-
-connector = PostgresConnector(config)
-result = connector.run_sql("SELECT * FROM users LIMIT 10")
-print(result)
-connector.close()
-```
-
-## 지원되는 데이터베이스
-
-1. **PostgreSQL** (`postgres`)
-2. **MySQL** (`mysql`)
-3. **MariaDB** (`mariadb`)
-4. **Oracle** (`oracle`)
-5. **SQLite** (`sqlite`)
-6. **DuckDB** (`duckdb`)
-7. **Snowflake** (`snowflake`)
-8. **Databricks** (`databricks`)
-9. **Trino** (`trino`)
-10. **ClickHouse** (`clickhouse`)
-
-각 데이터베이스의 상세한 설정 방법은 [connector/README.md](connector/README.md)를 참조하세요.
-
-## 통합 사용 예시
-
-### Streamlit 인터페이스에서 사용
-
-`interface/app_pages/settings_sections/db_section.py`에서 DB 연결을 설정하고 테스트:
-
-```python
-from utils.databases import DatabaseFactory
-
-# 연결 테스트
-connector = DatabaseFactory.get_connector(db_type=db_type)
-test_sql = "SELECT 1"
-df = connector.run_sql(test_sql)
-connector.close()
-```
-
-### CLI에서 사용
-
-환경 변수를 통해 커넥터를 생성하고 SQL 쿼리를 실행할 수 있습니다.
-
-## 에러 처리
-
-- **연결 실패**: `ConnectionError` 발생
-- **SQL 실행 실패**: `RuntimeError` 발생
-- **지원되지 않는 DB 타입**: `ValueError` 발생
-- **DB_TYPE 미지정**: `ValueError("DB_TYPE이 환경변수 또는 인자로 제공되어야 합니다.")` 발생
-
-## 로깅
-
-모든 커넥터는 `utils.databases.logger`를 사용하여 다음 정보를 로깅합니다:
-- 연결 성공/실패
-- SQL 실행 결과
-- 에러 발생 상황
-
-로그 포맷: `YYYY-MM-DD HH:MM:SS [LEVEL] 메시지`
-
-## 의존성
-
-각 커넥터는 해당 데이터베이스의 Python 드라이버를 필요로 합니다:
-
-- PostgreSQL: `psycopg2`
-- MySQL/MariaDB: `mysql-connector-python`
-- Oracle: `oracledb`
-- SQLite: `sqlite3` (표준 라이브러리)
-- DuckDB: `duckdb`
-- Snowflake: `snowflake-connector-python`
-- Databricks: `databricks-sql-connector`
-- Trino: `trino`
-- ClickHouse: `clickhouse-driver`
-
-## 확장성
-
-새로운 데이터베이스를 추가하려면:
-
-1. `connector/` 디렉토리에 `{db_type}_connector.py` 파일 생성
-2. `BaseConnector`를 상속받는 `{DBType}Connector` 클래스 구현
-3. `connect()`, `run_sql(sql) -> pd.DataFrame`, `close()` 메서드 구현
-4. `DatabaseFactory.get_connector()`가 자동으로 새 커넥터를 로드
-
-자세한 내용은 `connector/base_connector.py`를 참조하세요.
diff --git a/utils/databases/__init__.py b/utils/databases/__init__.py
deleted file mode 100644
index 2a6cf19..0000000
--- a/utils/databases/__init__.py
+++ /dev/null
@@ -1,14 +0,0 @@
-"""
-데이터베이스 유틸리티 패키지 초기화 모듈.
-
-이 모듈은 주요 구성 요소인 DatabaseFactory와 DBConfig를 외부로 노출하여
-데이터베이스 관련 기능을 손쉽게 사용할 수 있도록 합니다.
-"""
-
-from utils.databases.config import DBConfig
-from utils.databases.factory import DatabaseFactory
-
-__all__ = [
- "DatabaseFactory",
- "DBConfig",
-]
diff --git a/utils/databases/config.py b/utils/databases/config.py
deleted file mode 100644
index 0b48328..0000000
--- a/utils/databases/config.py
+++ /dev/null
@@ -1,32 +0,0 @@
-"""
-데이터베이스 설정 정보를 정의하는 모듈.
-
-이 모듈은 데이터베이스 연결에 필요한 기본 설정값과
-추가 옵션(extra)을 포함한 타입 힌트를 제공합니다.
-"""
-
-from typing import Dict, Optional, TypedDict
-
-
-class DBConfig(TypedDict):
- """
- 데이터베이스 연결 설정 정보를 표현하는 타입 딕셔너리.
-
- 데이터베이스 커넥터가 공통적으로 사용하는 설정 필드를 정의합니다.
- 일부 필드는 선택적으로 제공될 수 있습니다.
-
- Attributes:
- host (str): 데이터베이스 호스트명 또는 IP 주소.
- port (Optional[int]): 데이터베이스 포트 번호.
- user (Optional[str]): 접속 사용자명.
- password (Optional[str]): 접속 비밀번호.
- database (Optional[str]): 대상 데이터베이스 이름.
- extra (Optional[Dict[str, str]]): 드라이버별 추가 설정값.
- """
-
- host: str
- port: Optional[int]
- user: Optional[str]
- password: Optional[str]
- database: Optional[str]
- extra: Optional[Dict[str, str]]
diff --git a/utils/databases/connector/README.md b/utils/databases/connector/README.md
deleted file mode 100644
index 8349e14..0000000
--- a/utils/databases/connector/README.md
+++ /dev/null
@@ -1,302 +0,0 @@
-# Connector 모듈
-
-데이터베이스 연결 및 SQL 실행을 위한 커넥터 클래스들을 제공하는 모듈입니다.
-
-## 디렉토리 구조
-
-```
-connector/
-├── __pycache__/
-├── base_connector.py
-├── clickhouse_connector.py
-├── databricks_connector.py
-├── duckdb_connector.py
-├── mariadb_connector.py
-├── mysql_connector.py
-├── oracle_connector.py
-├── postgres_connector.py
-├── snowflake_connector.py
-├── sqlite_connector.py
-└── trino_connector.py
-```
-
-## 파일 설명
-
-### base_connector.py
-
-데이터베이스 커넥터의 기본 인터페이스를 정의하는 모듈입니다.
-
-**클래스:**
-- `BaseConnector`: 모든 DB 커넥터가 상속받아야 하는 추상 클래스
- - `connection`: DB 연결 객체를 저장하는 클래스 변수
- - `connect()`: 데이터베이스 연결 수행 (abstractmethod)
- - `run_sql(sql: str) -> pd.DataFrame`: SQL 쿼리 실행 및 결과 반환 (abstractmethod)
- - `close() -> None`: 데이터베이스 연결 종료 (abstractmethod)
-
-**사용처:**
-- 모든 구체적인 DB 커넥터 클래스들이 이 클래스를 상속받음
-- `utils.databases.connector.*` 모듈들에서 import되어 사용됨
-
-### clickhouse_connector.py
-
-ClickHouse 데이터베이스 연결 및 SQL 실행을 담당하는 모듈입니다.
-
-**클래스:**
-- `ClickHouseConnector(BaseConnector)`: ClickHouse 서버 연결을 위한 커넥터
- - `client`: ClickHouse Client 객체
- - `__init__(config: DBConfig)`: 호스트, 포트, 사용자, 비밀번호, 데이터베이스 설정으로 초기화
- - `connect() -> None`: clickhouse_driver.Client로 ClickHouse 서버 연결
- - `run_sql(sql: str) -> pd.DataFrame`: 쿼리 실행 후 DataFrame 반환
- - `close() -> None`: 클라이언트 연결 해제
-
-**의존성:**
-- `clickhouse_driver`
-- `utils.databases.config.DBConfig`
-- `utils.databases.logger.logger`
-
-### databricks_connector.py
-
-Databricks SQL Warehouse 연결 및 SQL 실행을 담당하는 모듈입니다.
-
-**클래스:**
-- `DatabricksConnector(BaseConnector)`: Databricks SQL Warehouse 연결을 위한 커넥터
- - `connection`: Databricks 연결 객체
- - `__init__(config: DBConfig)`:
- - 필수 설정: host, extra.http_path, extra.access_token
- - 선택 설정: extra.catalog, extra.schema
- - `connect() -> None`: databricks.sql 모듈로 연결 설정
- - `run_sql(sql: str) -> pd.DataFrame`: 커서를 사용해 쿼리 실행 후 DataFrame 반환
- - `close() -> None`: 연결 종료
-
-**의존성:**
-- `databricks.sql`
-- `utils.databases.config.DBConfig`
-- `utils.databases.logger.logger`
-
-### duckdb_connector.py
-
-DuckDB 데이터베이스 연결 및 SQL 실행을 담당하는 모듈입니다.
-
-**클래스:**
-- `DuckDBConnector(BaseConnector)`: DuckDB 연결을 위한 커넥터
- - `connection`: DuckDB 연결 객체
- - `__init__(config: DBConfig)`: path 설정 (기본값: ":memory:")
- - `connect() -> None`: duckdb.connect로 데이터베이스 연결
- - `run_sql(sql: str) -> pd.DataFrame`: execute().fetchdf()로 DataFrame 반환
- - `close() -> None`: 연결 종료
-
-**의존성:**
-- `duckdb`
-- `utils.databases.config.DBConfig`
-- `utils.databases.logger.logger`
-
-### mariadb_connector.py
-
-MariaDB 데이터베이스 연결 및 SQL 실행을 담당하는 모듈입니다.
-
-**클래스:**
-- `MariaDBConnector(BaseConnector)`: MariaDB 서버 연결을 위한 커넥터
- - `connection`: MariaDB 연결 객체
- - `__init__(config: DBConfig)`: 호스트, 포트(기본: 3306), 사용자, 비밀번호, 데이터베이스 설정
- - `connect() -> None`: mysql.connector로 MariaDB 서버 연결
- - `run_sql(sql: str) -> pd.DataFrame`: 커서를 사용해 쿼리 실행 후 DataFrame 반환
- - `close() -> None`: 연결 종료
-
-**의존성:**
-- `mysql.connector`
-- `utils.databases.config.DBConfig`
-- `utils.databases.logger.logger`
-
-### mysql_connector.py
-
-MySQL 데이터베이스 연결 및 SQL 실행을 담당하는 모듈입니다.
-
-**클래스:**
-- `MySQLConnector(BaseConnector)`: MySQL 서버 연결을 위한 커넥터
- - `connection`: MySQL 연결 객체
- - `__init__(config: DBConfig)`: 호스트, 포트(기본: 3306), 사용자, 비밀번호, 데이터베이스 설정
- - `connect() -> None`: mysql.connector로 MySQL 서버 연결
- - `run_sql(sql: str) -> pd.DataFrame`: 커서를 사용해 쿼리 실행 후 DataFrame 반환
- - `close() -> None`: 연결 종료
-
-**의존성:**
-- `mysql.connector`
-- `utils.databases.config.DBConfig`
-- `utils.databases.logger.logger`
-
-### oracle_connector.py
-
-Oracle 데이터베이스 연결 및 SQL 실행을 담당하는 모듈입니다.
-
-**클래스:**
-- `OracleConnector(BaseConnector)`: Oracle 서버 연결을 위한 커넥터
- - `connection`: Oracle 연결 객체
- - `__init__(config: DBConfig)`:
- - 필수 설정: host, port, user, password
- - 선택 설정: extra.service_name (기본값: "orcl")
- - `connect() -> None`: oracledb.connect로 DSN 형식으로 연결
- - `run_sql(sql: str) -> pd.DataFrame`: 커서를 사용해 쿼리 실행 후 DataFrame 반환
- - `close() -> None`: 연결 종료
-
-**의존성:**
-- `oracledb`
-- `utils.databases.config.DBConfig`
-- `utils.databases.logger.logger`
-
-### postgres_connector.py
-
-PostgreSQL 데이터베이스 연결 및 SQL 실행을 담당하는 모듈입니다.
-
-**클래스:**
-- `PostgresConnector(BaseConnector)`: PostgreSQL 서버 연결을 위한 커넥터
- - `connection`: PostgreSQL 연결 객체
- - `__init__(config: DBConfig)`: 호스트, 포트, 사용자, 비밀번호, 데이터베이스 설정
- - `connect() -> None`: psycopg2.connect로 PostgreSQL 서버 연결
- - `run_sql(sql: str) -> pd.DataFrame`: 커서를 사용해 쿼리 실행 후 DataFrame 반환
- - `close() -> None`: 연결 종료
-
-**의존성:**
-- `psycopg2`
-- `utils.databases.config.DBConfig`
-- `utils.databases.logger.logger`
-
-### snowflake_connector.py
-
-Snowflake 데이터베이스 연결 및 SQL 실행을 담당하는 모듈입니다.
-
-**클래스:**
-- `SnowflakeConnector(BaseConnector)`: Snowflake 서버 연결을 위한 커넥터
- - `connection`: Snowflake 연결 객체
- - `cursor`: Snowflake 커서 객체
- - `__init__(config: DBConfig)`:
- - 필수 설정: user, password, extra.account
- - 선택 설정: extra.warehouse, database, extra.schema
- - `connect() -> None`: snowflake.connector로 연결 및 커서 생성
- - `run_sql(sql: str) -> pd.DataFrame`: 커서를 사용해 쿼리 실행 후 DataFrame 반환
- - `close() -> None`: 연결 종료
-
-**의존성:**
-- `snowflake.connector`
-- `utils.databases.config.DBConfig`
-- `utils.databases.logger.logger`
-
-### sqlite_connector.py
-
-SQLite 데이터베이스 연결 및 SQL 실행을 담당하는 모듈입니다.
-
-**클래스:**
-- `SQLiteConnector(BaseConnector)`: SQLite 파일 또는 인메모리 데이터베이스 연결을 위한 커넥터
- - `connection`: SQLite 연결 객체
- - `__init__(config: DBConfig)`: path 설정 (None 또는 ":memory:"인 경우 인메모리 DB)
- - `connect() -> None`: sqlite3.connect로 데이터베이스 연결
- - `run_sql(sql: str) -> pd.DataFrame`: 커서를 사용해 쿼리 실행 후 DataFrame 반환
- - `close() -> None`: 연결 종료
-
-**의존성:**
-- `sqlite3` (Python 표준 라이브러리)
-- `utils.databases.config.DBConfig`
-- `utils.databases.logger.logger`
-
-### trino_connector.py
-
-Trino 클러스터 연결 및 SQL 실행을 담당하는 모듈입니다.
-
-**클래스:**
-- `TrinoConnector(BaseConnector)`: Trino 클러스터 연결을 위한 커넥터
- - `connection`: Trino 연결 객체
- - `__init__(config: DBConfig)`:
- - 필수 설정: host, port
- - 선택 설정: user, password, database, extra.catalog, extra.schema, extra.http_scheme
- - database가 "catalog.schema" 형태일 경우 자동 분리
- - `connect() -> None`: trino.dbapi.connect로 연결 설정
- - `run_sql(sql: str) -> pd.DataFrame`: 커서를 사용해 쿼리 실행 후 DataFrame 반환
- - `close() -> None`: 연결 종료
-
-**의존성:**
-- `trino` (런타임에 동적으로 로드)
-- `utils.databases.config.DBConfig`
-- `utils.databases.logger.logger`
-
-## 사용 방법
-
-### 직접 사용
-
-각 커넥터 클래스를 직접 인스턴스화하여 사용할 수 있습니다:
-
-```python
-from utils.databases.connector.postgres_connector import PostgresConnector
-from utils.databases.config import DBConfig
-
-config = DBConfig(
- host="localhost",
- port=5432,
- user="myuser",
- password="mypassword",
- database="mydb",
- extra={}
-)
-
-connector = PostgresConnector(config)
-result = connector.run_sql("SELECT * FROM users")
-print(result)
-connector.close()
-```
-
-### Factory 패턴 사용 (권장)
-
-`DatabaseFactory`를 사용하여 DB 타입에 따라 자동으로 적절한 커넥터를 선택할 수 있습니다:
-
-```python
-from utils.databases import DatabaseFactory
-
-# 환경 변수 사용
-connector = DatabaseFactory.get_connector()
-result = connector.run_sql("SELECT * FROM users")
-connector.close()
-
-# 명시적 설정
-config = DBConfig(
- host="localhost",
- port=5432,
- user="myuser",
- password="mypassword",
- database="mydb",
- extra={}
-)
-connector = DatabaseFactory.get_connector(db_type="postgres", config=config)
-result = connector.run_sql("SELECT * FROM users")
-connector.close()
-```
-
-### 지원되는 데이터베이스 타입
-
-다음 데이터베이스들이 지원됩니다:
-
-1. **PostgreSQL** (`postgres`)
-2. **MySQL** (`mysql`)
-3. **MariaDB** (`mariadb`)
-4. **Oracle** (`oracle`)
-5. **SQLite** (`sqlite`)
-6. **DuckDB** (`duckdb`)
-7. **Snowflake** (`snowflake`)
-8. **Databricks** (`databricks`)
-9. **Trino** (`trino`)
-10. **ClickHouse** (`clickhouse`)
-
-## 공통 인터페이스
-
-모든 커넥터는 `BaseConnector` 추상 클래스를 상속받아 다음과 같은 공통 인터페이스를 구현합니다:
-
-- `connect() -> None`: 데이터베이스에 연결
-- `run_sql(sql: str) -> pd.DataFrame`: SQL 쿼리 실행 및 결과를 pandas DataFrame으로 반환
-- `close() -> None`: 데이터베이스 연결 종료
-
-## 로깅
-
-모든 커넥터는 `utils.databases.logger`를 사용하여 연결 성공/실패 및 SQL 실행 결과를 로깅합니다.
-
-## 에러 처리
-
-- 연결 실패 시: `ConnectionError` 발생
-- SQL 실행 실패 시: `RuntimeError` 발생
-- 지원되지 않는 DB 타입: `ValueError` 발생
diff --git a/utils/databases/connector/base_connector.py b/utils/databases/connector/base_connector.py
deleted file mode 100644
index c1fbe90..0000000
--- a/utils/databases/connector/base_connector.py
+++ /dev/null
@@ -1,58 +0,0 @@
-"""
-데이터베이스 커넥터의 기본 인터페이스 정의 모듈.
-
-이 모듈은 모든 DB 커넥터 클래스가 상속해야 하는
-공통 추상 클래스(BaseConnector)를 제공합니다.
-"""
-
-from abc import ABC, abstractmethod
-
-import pandas as pd
-
-
-class BaseConnector(ABC):
- """
- 데이터베이스 커넥터의 기본 추상 클래스.
-
- 모든 구체적인 DB 커넥터(Postgres, MySQL 등)는
- 이 클래스를 상속받아 공통 메서드(`connect`, `run_sql`, `close`)를 구현해야 합니다.
-
- Attributes:
- connection (Any): DB 연결 객체. 구체 클래스에서 초기화 및 관리됩니다.
- """
-
- connection = None
-
- @abstractmethod
- def connect(self):
- """
- 데이터베이스 연결을 수행합니다.
-
- 이 메서드는 각 DB별 커넥터에서 구체적으로 구현되어야 합니다.
- """
- pass
-
- @abstractmethod
- def run_sql(self, sql: str) -> pd.DataFrame:
- """
- SQL 쿼리를 실행하고 결과를 반환합니다.
-
- Args:
- sql (str): 실행할 SQL 쿼리 문자열.
-
- Returns:
- pd.DataFrame: 쿼리 결과를 포함하는 데이터프레임.
- """
- pass
-
- @abstractmethod
- def close(self) -> None:
- """
- 데이터베이스 연결을 종료합니다.
-
- 모든 리소스(커서, 연결 등)를 안전하게 해제해야 합니다.
-
- Raises:
- RuntimeError: 연결 종료 중 예외가 발생한 경우.
- """
- pass
diff --git a/utils/databases/connector/clickhouse_connector.py b/utils/databases/connector/clickhouse_connector.py
deleted file mode 100644
index 4e755b4..0000000
--- a/utils/databases/connector/clickhouse_connector.py
+++ /dev/null
@@ -1,91 +0,0 @@
-"""
-ClickHouse 데이터베이스 커넥터 모듈.
-
-이 모듈은 ClickHouse 서버에 연결하여 SQL 쿼리를 실행하고,
-그 결과를 pandas DataFrame 형태로 반환하는 기능을 제공합니다.
-"""
-
-import pandas as pd
-from clickhouse_driver import Client
-
-from utils.databases.config import DBConfig
-from utils.databases.connector.base_connector import BaseConnector
-from utils.databases.logger import logger
-
-
-class ClickHouseConnector(BaseConnector):
- """
- ClickHouse 데이터베이스 커넥터 클래스.
-
- ClickHouse 서버에 연결하고 SQL 쿼리를 실행하거나 연결을 종료하는 기능을 제공합니다.
- """
-
- client = None
-
- def __init__(self, config: DBConfig):
- """
- ClickHouseConnector 인스턴스를 초기화합니다.
-
- Args:
- config (DBConfig): ClickHouse 연결 설정 정보를 담은 객체.
- """
- self.host = config["host"]
- self.port = config["port"]
- self.user = config["user"]
- self.password = config["password"]
- self.database = config["database"]
- self.connect()
-
- def connect(self) -> None:
- """
- ClickHouse 서버에 연결을 설정합니다.
-
- Raises:
- ConnectionError: 서버 연결에 실패한 경우 발생합니다.
- """
- try:
- self.client = Client(
- host=self.host,
- port=self.port,
- user=self.user,
- password=self.password,
- database=self.database,
- )
- logger.info("Successfully connected to ClickHouse.")
- except Exception as e:
- logger.error("Failed to connect to ClickHouse: %s", e)
- raise
-
- def run_sql(self, sql: str) -> pd.DataFrame:
- """
- SQL 쿼리를 실행하고 결과를 DataFrame으로 반환합니다.
-
- Args:
- sql (str): 실행할 SQL 쿼리 문자열.
-
- Returns:
- pd.DataFrame: 쿼리 결과를 담은 DataFrame 객체.
-
- Raises:
- RuntimeError: SQL 실행 중 오류가 발생한 경우.
- """
- if self.client is None:
- self.connect()
-
- try:
- result = self.client.query_dataframe(sql)
- return result
- except Exception as e:
- logger.error("Failed to execute SQL query: %s", e)
- raise
-
- def close(self) -> None:
- """
- ClickHouse 서버와의 연결을 종료합니다.
-
- 연결이 존재할 경우 안전하게 닫고 리소스를 해제합니다.
- """
- if self.client:
- self.client.disconnect()
- logger.info("Connection to ClickHouse closed.")
- self.client = None
diff --git a/utils/databases/connector/databricks_connector.py b/utils/databases/connector/databricks_connector.py
deleted file mode 100644
index 34ddcfb..0000000
--- a/utils/databases/connector/databricks_connector.py
+++ /dev/null
@@ -1,99 +0,0 @@
-"""
-Databricks SQL Warehouse 커넥터 모듈.
-
-이 모듈은 Databricks SQL Warehouse에 연결하여 SQL 쿼리를 실행하고,
-결과를 pandas DataFrame 형태로 반환하는 기능을 제공합니다.
-"""
-
-import pandas as pd
-from databricks import sql
-
-from utils.databases.config import DBConfig
-from utils.databases.connector.base_connector import BaseConnector
-from utils.databases.logger import logger
-
-
-class DatabricksConnector(BaseConnector):
- """
- Databricks SQL Warehouse 커넥터 클래스.
-
- Databricks SQL 엔드포인트에 연결하여 쿼리를 실행하고,
- 결과를 DataFrame으로 반환하는 기능을 제공합니다.
- """
-
- connection = None
-
- def __init__(self, config: DBConfig):
- """
- DatabricksConnector 인스턴스를 초기화합니다.
-
- Args:
- config (DBConfig): Databricks 연결 정보를 담은 설정 객체.
- - 필수 키: host, extra.http_path, extra.access_token
- - 선택 키: extra.catalog, extra.schema
- """
- self.server_hostname = config["host"]
- self.http_path = config["extra"]["http_path"]
- self.access_token = config["extra"]["access_token"]
- self.catalog = config.get("extra", {}).get("catalog")
- self.schema = config.get("extra", {}).get("schema")
- self.connect()
-
- def connect(self) -> None:
- """
- Databricks SQL Warehouse에 연결을 설정합니다.
-
- Raises:
- ConnectionError: 연결 설정 중 오류가 발생한 경우.
- """
- try:
- self.connection = sql.connect(
- server_hostname=self.server_hostname,
- http_path=self.http_path,
- access_token=self.access_token,
- catalog=self.catalog,
- schema=self.schema,
- )
- logger.info("Successfully connected to Databricks.")
- except Exception as e:
- logger.error("Failed to connect to Databricks: %s", e)
- raise
-
- def run_sql(self, sql: str) -> pd.DataFrame:
- """
- SQL 쿼리를 실행하고 결과를 pandas DataFrame으로 반환합니다.
-
- Args:
- sql (str): 실행할 SQL 쿼리 문자열.
-
- Returns:
- pd.DataFrame: 쿼리 결과를 담은 DataFrame 객체.
-
- Raises:
- RuntimeError: SQL 실행 중 오류가 발생한 경우.
- """
- if self.connection is None:
- self.connect()
-
- try:
- cursor = self.connection.cursor()
- cursor.execute(sql)
- columns = [desc[0] for desc in cursor.description]
- rows = cursor.fetchall()
- return pd.DataFrame(rows, columns=columns)
- except Exception as e:
- logger.error("Failed to execute SQL query: %s", e)
- raise
- finally:
- cursor.close()
-
- def close(self) -> None:
- """
- Databricks SQL Warehouse와의 연결을 종료합니다.
-
- 연결이 존재할 경우 안전하게 닫고 리소스를 해제합니다.
- """
- if self.connection:
- self.connection.close()
- logger.info("Connection to Databricks closed.")
- self.connection = None
diff --git a/utils/databases/connector/duckdb_connector.py b/utils/databases/connector/duckdb_connector.py
deleted file mode 100644
index 7ad7f2a..0000000
--- a/utils/databases/connector/duckdb_connector.py
+++ /dev/null
@@ -1,79 +0,0 @@
-"""
-DuckDB 데이터베이스 커넥터 모듈.
-
-이 모듈은 DuckDB 데이터베이스에 연결하여 SQL 쿼리를 실행하고,
-결과를 pandas DataFrame 형태로 반환하는 기능을 제공합니다.
-"""
-
-import duckdb
-import pandas as pd
-
-from utils.databases.config import DBConfig
-from utils.databases.connector.base_connector import BaseConnector
-from utils.databases.logger import logger
-
-
-class DuckDBConnector(BaseConnector):
- """
- DuckDB 데이터베이스 커넥터 클래스.
-
- DuckDB 데이터베이스에 연결하여 쿼리를 실행하고,
- 결과를 DataFrame 형태로 반환하거나 연결을 종료하는 기능을 제공합니다.
- """
-
- connection = None
-
- def __init__(self, config: DBConfig):
- """
- DuckDBConnector 인스턴스를 초기화합니다.
-
- Args:
- config (DBConfig): DuckDB 연결 정보를 담은 설정 객체.
- `path` 키를 사용하여 파일 경로를 지정하거나, `:memory:`를 사용하여 인메모리 DB로 설정합니다.
- """
- self.database = config.get("path", ":memory:")
- self.connect()
-
- def connect(self) -> None:
- """
- DuckDB 데이터베이스에 연결을 설정합니다.
-
- Raises:
- ConnectionError: DuckDB 연결에 실패한 경우 발생합니다.
- """
- try:
- self.connection = duckdb.connect(database=self.database)
- logger.info("Successfully connected to DuckDB.")
- except Exception as e:
- logger.error("Failed to connect to DuckDB: %s", e)
- raise
-
- def run_sql(self, sql: str) -> pd.DataFrame:
- """
- SQL 쿼리를 실행하고 결과를 pandas DataFrame으로 반환합니다.
-
- Args:
- sql (str): 실행할 SQL 쿼리 문자열.
-
- Returns:
- pd.DataFrame: 쿼리 결과를 담은 DataFrame 객체.
-
- Raises:
- RuntimeError: SQL 실행 중 오류가 발생한 경우.
- """
- try:
- return self.connection.execute(sql).fetchdf()
- except Exception as e:
- logger.error("Failed to execute SQL query: %s", e)
- raise
-
- def close(self) -> None:
- """
- DuckDB 데이터베이스 연결을 종료합니다.
-
- 연결이 존재할 경우 안전하게 닫고 리소스를 해제합니다.
- """
- if self.connection:
- self.connection.close()
- logger.info("Connection to DuckDB closed.")
- self.connection = None
diff --git a/utils/databases/connector/mariadb_connector.py b/utils/databases/connector/mariadb_connector.py
deleted file mode 100644
index d6120ef..0000000
--- a/utils/databases/connector/mariadb_connector.py
+++ /dev/null
@@ -1,94 +0,0 @@
-"""
-MariaDB 데이터베이스 커넥터 모듈.
-
-이 모듈은 mysql-connector-python을 사용하여 MariaDB 서버에 연결하고,
-SQL 쿼리를 실행하여 pandas DataFrame 형태로 결과를 반환하는 기능을 제공합니다.
-"""
-
-import mysql.connector
-import pandas as pd
-
-from utils.databases.config import DBConfig
-from utils.databases.connector.base_connector import BaseConnector
-from utils.databases.logger import logger
-
-
-class MariaDBConnector(BaseConnector):
- """
- MariaDB 데이터베이스 커넥터 클래스.
-
- mysql-connector-python을 이용해 MariaDB 서버에 연결하고,
- SQL 쿼리를 실행하거나 연결을 종료하는 기능을 제공합니다.
- """
-
- connection = None
-
- def __init__(self, config: DBConfig):
- """
- MariaDBConnector 인스턴스를 초기화합니다.
-
- Args:
- config (DBConfig): MariaDB 연결 정보를 담은 설정 객체.
- """
- self.host = config["host"]
- self.port = config.get("port", 3306)
- self.user = config["user"]
- self.password = config["password"]
- self.database = config["database"]
- self.connect()
-
- def connect(self) -> None:
- """
- mysql-connector-python을 사용하여 MariaDB 서버에 연결을 설정합니다.
-
- Raises:
- ConnectionError: MariaDB 서버 연결에 실패한 경우 발생합니다.
- """
- try:
- self.connection = mysql.connector.connect(
- host=self.host,
- port=self.port,
- user=self.user,
- password=self.password,
- database=self.database,
- )
- logger.info("Successfully connected to MariaDB.")
- except Exception as e:
- logger.error("Failed to connect to MariaDB: %s", e)
- raise
-
- def run_sql(self, sql: str) -> pd.DataFrame:
- """
- SQL 쿼리를 실행하고 결과를 pandas DataFrame으로 반환합니다.
-
- Args:
- sql (str): 실행할 SQL 쿼리 문자열.
-
- Returns:
- pd.DataFrame: 쿼리 결과를 담은 DataFrame 객체.
-
- Raises:
- RuntimeError: SQL 실행 중 오류가 발생한 경우.
- """
- try:
- cursor = self.connection.cursor()
- cursor.execute(sql)
- columns = [column[0] for column in cursor.description]
- rows = cursor.fetchall()
- return pd.DataFrame(rows, columns=columns)
- except Exception as e:
- logger.error("Failed to execute SQL query: %s", e)
- raise
- finally:
- cursor.close()
-
- def close(self) -> None:
- """
- MariaDB 서버와의 연결을 종료합니다.
-
- 연결이 존재할 경우 안전하게 닫고 리소스를 해제합니다.
- """
- if self.connection:
- self.connection.close()
- logger.info("Connection to MariaDB closed.")
- self.connection = None
diff --git a/utils/databases/connector/mysql_connector.py b/utils/databases/connector/mysql_connector.py
deleted file mode 100644
index 1c02a11..0000000
--- a/utils/databases/connector/mysql_connector.py
+++ /dev/null
@@ -1,93 +0,0 @@
-"""
-MySQL 데이터베이스 커넥터 모듈.
-
-이 모듈은 MySQL 서버에 연결하여 SQL 쿼리를 실행하고,
-그 결과를 pandas DataFrame 형태로 반환하는 기능을 제공합니다.
-"""
-
-import mysql.connector
-import pandas as pd
-
-from utils.databases.config import DBConfig
-from utils.databases.connector.base_connector import BaseConnector
-from utils.databases.logger import logger
-
-
-class MySQLConnector(BaseConnector):
- """
- MySQL 데이터베이스 커넥터 클래스.
-
- MySQL 서버에 연결하여 SQL 쿼리를 실행하거나 연결을 종료하는 기능을 제공합니다.
- """
-
- connection = None
-
- def __init__(self, config: DBConfig):
- """
- MySQLConnector 인스턴스를 초기화합니다.
-
- Args:
- config (DBConfig): MySQL 연결 정보를 담은 설정 객체.
- """
- self.host = config["host"]
- self.port = config.get("port", 3306)
- self.user = config["user"]
- self.password = config["password"]
- self.database = config["database"]
- self.connect()
-
- def connect(self) -> None:
- """
- MySQL 서버에 연결을 설정합니다.
-
- Raises:
- ConnectionError: MySQL 서버 연결에 실패한 경우 발생합니다.
- """
- try:
- self.connection = mysql.connector.connect(
- host=self.host,
- port=self.port,
- user=self.user,
- password=self.password,
- database=self.database,
- )
- logger.info("Successfully connected to MySQL.")
- except Exception as e:
- logger.error("Failed to connect to MySQL: %s", e)
- raise
-
- def run_sql(self, sql: str) -> pd.DataFrame:
- """
- SQL 쿼리를 실행하고 결과를 pandas DataFrame으로 반환합니다.
-
- Args:
- sql (str): 실행할 SQL 쿼리 문자열.
-
- Returns:
- pd.DataFrame: 쿼리 결과를 담은 DataFrame 객체.
-
- Raises:
- RuntimeError: SQL 실행 중 오류가 발생한 경우.
- """
- try:
- cursor = self.connection.cursor()
- cursor.execute(sql)
- columns = [column[0] for column in cursor.description]
- rows = cursor.fetchall()
- return pd.DataFrame(rows, columns=columns)
- except Exception as e:
- logger.error("Failed to execute SQL query: %s", e)
- raise
- finally:
- cursor.close()
-
- def close(self) -> None:
- """
- MySQL 서버와의 연결을 종료합니다.
-
- 연결이 존재할 경우 안전하게 닫고 리소스를 해제합니다.
- """
- if self.connection:
- self.connection.close()
- logger.info("Connection to MySQL closed.")
- self.connection = None
diff --git a/utils/databases/connector/oracle_connector.py b/utils/databases/connector/oracle_connector.py
deleted file mode 100644
index 915614f..0000000
--- a/utils/databases/connector/oracle_connector.py
+++ /dev/null
@@ -1,93 +0,0 @@
-"""
-Oracle 데이터베이스 커넥터 모듈.
-
-이 모듈은 Oracle 데이터베이스에 연결하여 SQL 쿼리를 실행하고,
-결과를 pandas DataFrame 형태로 반환하는 기능을 제공합니다.
-"""
-
-import oracledb
-import pandas as pd
-
-from utils.databases.config import DBConfig
-from utils.databases.connector.base_connector import BaseConnector
-from utils.databases.logger import logger
-
-
-class OracleConnector(BaseConnector):
- """
- Oracle 데이터베이스 커넥터 클래스.
-
- Oracle 서버에 연결하여 SQL 쿼리를 실행하거나 연결을 종료하는 기능을 제공합니다.
- """
-
- connection = None
-
- def __init__(self, config: DBConfig):
- """
- OracleConnector 인스턴스를 초기화합니다.
-
- Args:
- config (DBConfig): Oracle 연결 정보를 담은 설정 객체.
- - 필수 키: host, port, user, password
- - 선택 키: extra.service_name (기본값: "orcl")
- """
- self.host = config["host"]
- self.port = config["port"]
- self.user = config["user"]
- self.password = config["password"]
- self.service_name = config.get("extra").get("service_name", "orcl")
- self.connect()
-
- def connect(self) -> None:
- """
- Oracle 데이터베이스에 연결을 설정합니다.
-
- Raises:
- ConnectionError: Oracle 서버 연결에 실패한 경우 발생합니다.
- """
- try:
- self.connection = oracledb.connect(
- user=self.user,
- password=self.password,
- dsn=f"{self.host}:{self.port}/{self.service_name}",
- )
- logger.info("Successfully connected to Oracle.")
- except Exception as e:
- logger.error("Failed to connect to Oracle: %s", e)
- raise
-
- def run_sql(self, sql: str) -> pd.DataFrame:
- """
- SQL 쿼리를 실행하고 결과를 pandas DataFrame으로 반환합니다.
-
- Args:
- sql (str): 실행할 SQL 쿼리 문자열.
-
- Returns:
- pd.DataFrame: 쿼리 결과를 담은 DataFrame 객체.
-
- Raises:
- RuntimeError: SQL 실행 중 오류가 발생한 경우.
- """
- try:
- cursor = self.connection.cursor()
- cursor.execute(sql)
- columns = [desc[0] for desc in cursor.description]
- rows = cursor.fetchall()
- return pd.DataFrame(rows, columns=columns)
- except Exception as e:
- logger.error("Failed to execute SQL query: %s", e)
- raise
- finally:
- cursor.close()
-
- def close(self) -> None:
- """
- Oracle 데이터베이스 연결을 종료합니다.
-
- 연결이 존재할 경우 안전하게 닫고 리소스를 해제합니다.
- """
- if self.connection:
- self.connection.close()
- logger.info("Connection to Oracle closed.")
- self.connection = None
diff --git a/utils/databases/connector/postgres_connector.py b/utils/databases/connector/postgres_connector.py
deleted file mode 100644
index a94ae4f..0000000
--- a/utils/databases/connector/postgres_connector.py
+++ /dev/null
@@ -1,93 +0,0 @@
-"""
-PostgreSQL 데이터베이스 커넥터 모듈.
-
-이 모듈은 PostgreSQL 서버에 연결하여 SQL 쿼리를 실행하고,
-결과를 pandas DataFrame 형태로 반환하는 기능을 제공합니다.
-"""
-
-import pandas as pd
-import psycopg2
-
-from utils.databases.config import DBConfig
-from utils.databases.connector.base_connector import BaseConnector
-from utils.databases.logger import logger
-
-
-class PostgresConnector(BaseConnector):
- """
- PostgreSQL 데이터베이스 커넥터 클래스.
-
- PostgreSQL 서버에 연결하고 SQL 쿼리를 실행하거나 연결을 종료하는 기능을 제공합니다.
- """
-
- connection = None
-
- def __init__(self, config: DBConfig):
- """
- PostgresConnector 인스턴스를 초기화합니다.
-
- Args:
- config (DBConfig): PostgreSQL 연결 정보를 담은 설정 객체.
- """
- self.host = config["host"]
- self.port = config["port"]
- self.user = config["user"]
- self.password = config["password"]
- self.database = config["database"]
- self.connect()
-
- def connect(self) -> None:
- """
- PostgreSQL 서버에 연결합니다.
-
- Raises:
- ConnectionError: 서버 연결에 실패한 경우 발생합니다.
- """
- try:
- self.connection = psycopg2.connect(
- host=self.host,
- port=self.port,
- user=self.user,
- password=self.password,
- dbname=self.database,
- )
- logger.info("Successfully connected to PostgreSQL.")
- except Exception as e:
- logger.error("Failed to connect to PostgreSQL: %s", e)
- raise
-
- def run_sql(self, sql: str) -> pd.DataFrame:
- """
- SQL 쿼리를 실행하고 결과를 DataFrame으로 반환합니다.
-
- Args:
- sql (str): 실행할 SQL 쿼리 문자열.
-
- Returns:
- pd.DataFrame: 쿼리 결과를 담은 DataFrame 객체.
-
- Raises:
- RuntimeError: SQL 실행 중 오류가 발생한 경우.
- """
- try:
- cursor = self.connection.cursor()
- cursor.execute(sql)
- columns = [desc[0] for desc in cursor.description]
- rows = cursor.fetchall()
- return pd.DataFrame(rows, columns=columns)
- except Exception as e:
- logger.error("Failed to execute SQL query: %s", e)
- raise
- finally:
- cursor.close()
-
- def close(self) -> None:
- """
- PostgreSQL 서버와의 연결을 종료합니다.
-
- 연결이 존재할 경우 안전하게 닫고 리소스를 해제합니다.
- """
- if self.connection:
- self.connection.close()
- logger.info("Connection to PostgreSQL closed.")
- self.connection = None
diff --git a/utils/databases/connector/snowflake_connector.py b/utils/databases/connector/snowflake_connector.py
deleted file mode 100644
index 69ef907..0000000
--- a/utils/databases/connector/snowflake_connector.py
+++ /dev/null
@@ -1,102 +0,0 @@
-"""
-Snowflake 데이터베이스 커넥터 모듈.
-
-이 모듈은 Snowflake 데이터베이스에 연결하여 SQL 쿼리를 실행하고,
-결과를 pandas DataFrame 형태로 반환하는 기능을 제공합니다.
-"""
-
-import pandas as pd
-from snowflake import connector
-
-from utils.databases.config import DBConfig
-from utils.databases.connector.base_connector import BaseConnector
-from utils.databases.logger import logger
-
-
-class SnowflakeConnector(BaseConnector):
- """
- Snowflake 데이터베이스 커넥터 클래스.
-
- Snowflake 서버에 연결하여 SQL 쿼리를 실행하거나 연결을 종료하는 기능을 제공합니다.
- """
-
- connection = None
-
- def __init__(self, config: DBConfig):
- """
- SnowflakeConnector 인스턴스를 초기화합니다.
-
- Args:
- config (DBConfig): Snowflake 연결 정보를 담은 설정 객체.
- - 필수 키: user, password, extra.account
- - 선택 키: extra.warehouse, database, extra.schema
- """
- self.user = config["user"]
- self.password = config["password"]
- self.account = config["extra"]["account"]
- self.warehouse = config.get("extra", {}).get("warehouse")
- self.database = config.get("database")
- self.schema = config.get("extra", {}).get("schema")
- self.connect()
-
- def connect(self) -> None:
- """
- Snowflake 데이터베이스에 연결을 설정합니다.
-
- Raises:
- ConnectionError: Snowflake 서버 연결에 실패한 경우 발생합니다.
- """
- try:
- self.connection = connector.connect(
- user=self.user,
- password=self.password,
- account=self.account,
- warehouse=self.warehouse,
- database=self.database,
- schema=self.schema,
- )
- logger.info("Successfully connected to Snowflake.")
- self.cursor = self.connection.cursor()
- except Exception as e:
- logger.error("Failed to connect to Snowflake: %s", e)
- raise
-
- def run_sql(self, sql: str) -> pd.DataFrame:
- """
- SQL 쿼리를 실행하고 결과를 pandas DataFrame으로 반환합니다.
-
- Args:
- sql (str): 실행할 SQL 쿼리 문자열.
-
- Returns:
- pd.DataFrame: 쿼리 결과를 담은 DataFrame 객체.
-
- Raises:
- RuntimeError: SQL 실행 중 오류가 발생한 경우.
- """
- if self.connection is None:
- self.connect()
-
- cursor = self.connection.cursor()
-
- try:
- self.cursor.execute(sql)
- columns = [col[0] for col in self.cursor.description]
- data = self.cursor.fetchall()
- return pd.DataFrame(data, columns=columns)
- except Exception as e:
- logger.error("Failed to execute SQL query: %s", e)
- raise
- finally:
- cursor.close()
-
- def close(self) -> None:
- """
- Snowflake 데이터베이스 연결을 종료합니다.
-
- 연결이 존재할 경우 안전하게 닫고 리소스를 해제합니다.
- """
- if self.connection:
- self.connection.close()
- logger.info("Connection to Snowflake closed.")
- self.connection = None
diff --git a/utils/databases/connector/sqlite_connector.py b/utils/databases/connector/sqlite_connector.py
deleted file mode 100644
index ca5b138..0000000
--- a/utils/databases/connector/sqlite_connector.py
+++ /dev/null
@@ -1,90 +0,0 @@
-"""
-SQLite 데이터베이스 커넥터 모듈.
-
-이 모듈은 SQLite 데이터베이스에 연결하여 SQL 쿼리를 실행하고,
-그 결과를 pandas DataFrame 형태로 반환하는 기능을 제공합니다.
-"""
-
-import sqlite3
-
-import pandas as pd
-
-from utils.databases.config import DBConfig
-from utils.databases.connector.base_connector import BaseConnector
-from utils.databases.logger import logger
-
-
-class SQLiteConnector(BaseConnector):
- """
- SQLite 데이터베이스 커넥터 클래스.
-
- SQLite 파일 또는 인메모리 데이터베이스에 연결하여
- SQL 쿼리를 실행하거나 연결을 종료하는 기능을 제공합니다.
- """
-
- connection = None
-
- def __init__(self, config: DBConfig):
- """
- SQLiteConnector 인스턴스를 초기화합니다.
-
- Args:
- config (DBConfig): SQLite 연결 정보를 담은 설정 객체.
- - `path` 키를 사용하여 SQLite 파일 경로를 지정합니다.
- - 값이 None 또는 ":memory:"인 경우 인메모리 데이터베이스를 생성합니다.
- """
- self.database = config.get("path", ":memory:")
- self.connect()
-
- def connect(self) -> None:
- """
- SQLite 데이터베이스에 연결을 설정합니다.
-
- Raises:
- ConnectionError: 데이터베이스 연결에 실패한 경우 발생합니다.
- """
- try:
- self.connection = sqlite3.connect(self.database)
- logger.info("Successfully connected to SQLite (%s).", self.database)
- except Exception as e:
- logger.error("Failed to connect to SQLite: %s", e)
- raise
-
- def run_sql(self, sql: str) -> pd.DataFrame:
- """
- SQL 쿼리를 실행하고 결과를 pandas DataFrame으로 반환합니다.
-
- Args:
- sql (str): 실행할 SQL 쿼리 문자열.
-
- Returns:
- pd.DataFrame: 쿼리 결과를 담은 DataFrame 객체.
-
- Raises:
- RuntimeError: SQL 실행 중 오류가 발생한 경우.
- """
- if self.connection is None:
- self.connect()
-
- try:
- cursor = self.connection.cursor()
- cursor.execute(sql)
- columns = [col[0] for col in cursor.description]
- rows = cursor.fetchall()
- return pd.DataFrame(rows, columns=columns)
- except Exception as e:
- logger.error("Failed to execute SQL query: %s", e)
- raise
- finally:
- cursor.close()
-
- def close(self) -> None:
- """
- SQLite 데이터베이스 연결을 종료합니다.
-
- 연결이 존재할 경우 안전하게 닫고 리소스를 해제합니다.
- """
- if self.connection:
- self.connection.close()
- logger.info("Connection to SQLite closed.")
- self.connection = None
diff --git a/utils/databases/connector/trino_connector.py b/utils/databases/connector/trino_connector.py
deleted file mode 100644
index 521c74e..0000000
--- a/utils/databases/connector/trino_connector.py
+++ /dev/null
@@ -1,132 +0,0 @@
-"""
-Trino 데이터베이스 커넥터 모듈.
-
-이 모듈은 Trino 클러스터에 연결하여 SQL 쿼리를 실행하고,
-그 결과를 pandas DataFrame 형태로 반환하는 기능을 제공합니다.
-"""
-
-import pandas as pd
-
-from utils.databases.config import DBConfig
-from utils.databases.connector.base_connector import BaseConnector
-from utils.databases.logger import logger
-
-
-class TrinoConnector(BaseConnector):
- """
- Trino 데이터베이스 커넥터 클래스.
-
- Trino 클러스터에 연결하여 SQL 쿼리를 실행하거나
- 연결을 종료하는 기능을 제공합니다.
- """
-
- connection = None
-
- def __init__(self, config: DBConfig):
- """
- TrinoConnector 인스턴스를 초기화합니다.
-
- Args:
- config (DBConfig): Trino 연결 정보를 담은 설정 객체.
- - 필수 키: host, port
- - 선택 키: user, password, database, extra.catalog, extra.schema, extra.http_scheme
- - database가 "catalog.schema" 형태일 경우 자동으로 분리되어 설정됩니다.
- """
- # pylint: disable=import-outside-toplevel
- try:
- import trino
-
- self.trino = trino
- except ImportError as e:
- logger.error(
- "Trino 드라이버가 설치되어 있지 않습니다. pip install trino 명령을 실행하세요."
- )
- raise ImportError("Trino 라이브러리가 설치되어 있지 않습니다.") from e
-
- self.host = config["host"]
- self.port = config["port"] or 8080
- self.user = config.get("user") or "anonymous"
- self.password = config.get("password")
- self.database = config.get("database") # e.g., catalog.schema
- self.extra = config.get("extra") or {}
- self.http_scheme = self.extra.get("http_scheme", "http")
- self.catalog = self.extra.get("catalog")
- self.schema = self.extra.get("schema")
-
- # If database given as "catalog.schema", split into fields
- if self.database and (not self.catalog or not self.schema):
- if "." in self.database:
- db_catalog, db_schema = self.database.split(".", 1)
- self.catalog = self.catalog or db_catalog
- self.schema = self.schema or db_schema
-
- self.connect()
-
- def connect(self) -> None:
- """
- Trino 클러스터에 연결을 설정합니다.
-
- Raises:
- ImportError: trino 드라이버를 불러오지 못한 경우 발생합니다.
- ConnectionError: Trino 서버 연결에 실패한 경우 발생합니다.
- """
- try:
- auth = None
- if self.password and self.http_scheme == "https":
- auth = self.trino.auth.BasicAuthentication(self.user, self.password)
-
- self.connection = self.trino.dbapi.connect(
- host=self.host,
- port=self.port,
- user=self.user,
- http_scheme=self.http_scheme,
- catalog=self.catalog,
- schema=self.schema,
- auth=auth,
- )
- logger.info("Successfully connected to Trino.")
- except Exception as e:
- logger.error("Failed to connect to Trino: %s", e)
- raise
-
- def run_sql(self, sql: str) -> pd.DataFrame:
- """
- SQL 쿼리를 실행하고 결과를 pandas DataFrame으로 반환합니다.
-
- Args:
- sql (str): 실행할 SQL 쿼리 문자열.
-
- Returns:
- pd.DataFrame: 쿼리 결과를 담은 DataFrame 객체.
-
- Raises:
- RuntimeError: SQL 실행 중 오류가 발생한 경우.
- """
- try:
- cursor = self.connection.cursor()
- cursor.execute(sql)
- columns = (
- [desc[0] for desc in cursor.description] if cursor.description else []
- )
- rows = cursor.fetchall() if cursor.description else []
- return pd.DataFrame(rows, columns=columns)
- except Exception as e:
- logger.error("Failed to execute SQL query on Trino: %s", e)
- raise
- finally:
- try:
- cursor.close()
- logger.info("Cursor closed successfully.")
- except Exception as e: # pylint: disable=broad-exception-caught
- logger.error("Failed to close cursor: %s", e)
-
- def close(self) -> None:
- """
- Trino 클러스터와의 연결을 종료합니다.
-
- 연결이 존재할 경우 안전하게 닫고 리소스를 해제합니다.
- """
- if self.connection:
- self.connection.close()
- logger.info("Connection to Trino closed.")
- self.connection = None
diff --git a/utils/databases/factory.py b/utils/databases/factory.py
deleted file mode 100644
index 4bfcb5b..0000000
--- a/utils/databases/factory.py
+++ /dev/null
@@ -1,115 +0,0 @@
-"""
-데이터베이스 커넥터 팩토리 모듈.
-
-이 모듈은 DB 타입에 따라 알맞은 커넥터 클래스를 동적으로 로드하여
-해당 DB에 연결할 수 있는 인스턴스를 생성하는 기능을 제공합니다.
-환경변수로부터 접속 설정을 자동으로 로드하는 유틸리티 함수도 포함합니다.
-"""
-
-import importlib
-import inspect
-import os
-from typing import Optional
-
-from utils.databases.config import DBConfig
-from utils.databases.logger import logger
-
-
-class DatabaseFactory:
- """
- 데이터베이스 커넥터 팩토리 클래스.
-
- DB 타입에 따라 알맞은 Connector 클래스를 동적으로 로드하고,
- 해당 인스턴스를 반환하는 기능을 제공합니다.
- """
-
- @staticmethod
- def get_connector(db_type: Optional[str] = None, config: Optional[DBConfig] = None):
- """
- 주어진 DB 타입에 해당하는 Connector 인스턴스를 반환합니다.
-
- 지정된 DB 타입에 맞는 커넥터 모듈을 동적으로 로드하고,
- 해당 모듈 내의 Connector 클래스를 탐색하여 인스턴스를 생성합니다.
- DB 타입이 지정되지 않은 경우 환경 변수(DB_TYPE)에서 자동으로 가져옵니다.
-
- Args:
- db_type (Optional[str]): 데이터베이스 타입 문자열 (예: 'postgres', 'mysql', 'trino').
- config (Optional[DBConfig]): 데이터베이스 연결 설정 객체.
- 지정되지 않은 경우 환경 변수에서 자동으로 로드됩니다.
-
- Returns:
- BaseConnector: 주어진 DB 타입에 해당하는 Connector 인스턴스.
-
- Raises:
- ValueError: DB_TYPE이 지정되지 않았거나,
- 지원되지 않는 DB 타입이거나,
- 모듈 또는 Connector 클래스를 찾을 수 없는 경우.
- """
- if not db_type:
- db_type = os.getenv("DB_TYPE")
- if not db_type:
- raise ValueError("DB_TYPE이 환경변수 또는 인자로 제공되어야 합니다.")
- db_type = db_type.lower()
-
- if not config:
- config = load_config_from_env(db_type.upper())
-
- try:
- module_name = f"utils.databases.connector.{db_type}_connector"
- module = importlib.import_module(module_name)
-
- connector_class = None
- for name, cls in inspect.getmembers(module, inspect.isclass):
- if name.lower() == f"{db_type}connector":
- connector_class = cls
- break
- if connector_class is None:
- raise ValueError(f"No matching Connector class found for {db_type}")
- except (ImportError, AttributeError) as e:
- logger.error(
- "지원되지 않는 DB 타입이거나 모듈을 로드할 수 없습니다: %s",
- db_type,
- )
- raise ValueError(f"Unsupported DB type: {db_type}") from e
-
- return connector_class(config)
-
-
-def load_config_from_env(prefix: str) -> DBConfig:
- """
- 환경변수에서 데이터베이스 접속 설정을 로드합니다.
-
- Args:
- prefix (str): 환경변수 접두어 (예: 'POSTGRES', 'MYSQL').
-
- Returns:
- DBConfig: 환경변수에서 로드된 설정 정보를 담은 DBConfig 객체.
- """
- base_keys = {
- "HOST",
- "PORT",
- "USER",
- "PASSWORD",
- "DATABASE",
- }
- config = {
- "host": os.getenv(f"{prefix}_HOST"),
- "port": (
- int(os.getenv(f"{prefix}_PORT")) if os.getenv(f"{prefix}_PORT") else None
- ),
- "user": os.getenv(f"{prefix}_USER"),
- "password": os.getenv(f"{prefix}_PASSWORD"),
- "database": os.getenv(f"{prefix}_DATABASE"),
- }
-
- extra = {}
- for key, value in os.environ.items():
- if (
- key.startswith(f"{prefix}_")
- and key.split("_", 1)[1].upper() not in base_keys
- ):
- extra[key[len(prefix) + 1 :].lower()] = value
- if extra:
- config["extra"] = extra
-
- return DBConfig(**config)
diff --git a/utils/databases/logger.py b/utils/databases/logger.py
deleted file mode 100644
index f9d0404..0000000
--- a/utils/databases/logger.py
+++ /dev/null
@@ -1,15 +0,0 @@
-"""
-로깅 설정 모듈.
-
-이 모듈은 애플리케이션 전역에서 사용할 기본 로깅 설정을 정의하고,
-표준 로거 인스턴스(logger)를 제공합니다.
-"""
-
-import logging
-
-logging.basicConfig(
- level=logging.INFO,
- format="%(asctime)s [%(levelname)s] %(message)s",
- datefmt="%Y-%m-%d %H:%M:%S",
-)
-logger = logging.getLogger(__name__)
diff --git a/utils/llm/README.md b/utils/llm/README.md
deleted file mode 100644
index 993ee98..0000000
--- a/utils/llm/README.md
+++ /dev/null
@@ -1,329 +0,0 @@
-## utils.llm 개요
-
-Lang2SQL 파이프라인에서 LLM, 검색(RAG), 그래프 워크플로우, DB 실행, 시각화 등 보조 유틸리티를 모아둔 패키지입니다. 이 문서는 depth(계층)별로 기능과 통합 흐름을 정리합니다.
-
-## 디렉토리 구조
-
-```
-utils/llm/
-├── README.md # 이 파일
-├── chains.py # LangChain 체인 생성 모듈
-├── retrieval.py # 테이블 메타 검색 및 재순위화
-├── llm_response_parser.py # LLM 응답에서 SQL 블록 추출
-├── chatbot.py # LangGraph ChatBot 구현
-├── core/ # LLM/Embedding 팩토리 모듈
-│ ├── __init__.py
-│ ├── factory.py # LLM 및 Embedding 모델 생성 팩토리
-│ └── README.md # [상세 문서](./core/README.md)
-├── graph_utils/ # LangGraph 워크플로우 모듈
-│ ├── __init__.py
-│ ├── base.py # 공통 상태 및 노드 함수
-│ ├── basic_graph.py # 기본 워크플로우 그래프
-│ ├── enriched_graph.py # 확장된 워크플로우 그래프
-│ ├── profile_utils.py # 프로파일 유틸리티 함수
-│ └── README.md # [상세 문서](./graph_utils/README.md)
-├── vectordb/ # 벡터 데이터베이스 모듈
-│ ├── __init__.py
-│ ├── factory.py # VectorDB 팩토리
-│ ├── faiss_db.py # FAISS 벡터DB 구현
-│ ├── pgvector_db.py # pgvector 벡터DB 구현
-│ └── README.md # [상세 문서](./vectordb/README.md)
-├── tools/ # DataHub 메타데이터 및 ChatBot 도구
-│ ├── __init__.py
-│ ├── datahub.py # DataHub 메타데이터 수집
-│ ├── chatbot_tool.py # LangGraph ChatBot용 Tool 함수들
-│ └── README.md # [상세 문서](./tools/README.md)
-└── output_schema/ # LLM 구조화 출력 Pydantic 모델
- ├── document_suitability.py # 문서 적합성 평가 모델
- ├── question_suitability.py # 질문 적합성 판단 모델
- └── README.md # [상세 문서](./output_schema/README.md)
-```
-
-## 모듈 상세 설명
-
-### 최상위 유틸리티 파일
-
-#### `chains.py`
-**목적**: LangChain 기반 체인 생성 모듈
-
-**주요 함수:**
-- `create_query_maker_chain(llm)`: SQL 쿼리 생성 체인
-- `create_profile_extraction_chain(llm)`: 질문 프로파일 추출 체인
-- `create_query_enrichment_chain(llm)`: 질문 컨텍스트 보강 체인
-- `create_question_gate_chain(llm)`: SQL 적합성 판별 체인
-- `create_document_suitability_chain(llm)`: 문서 적합성 평가 체인
-
-**내부 모델:**
-- `QuestionProfile`: 자연어 질문의 특징을 구조화하는 Pydantic 모델
-
-**의존성:**
-- `utils.llm.core.get_llm`: LLM 인스턴스 생성
-- `utils.llm.output_schema`: 구조화 출력 모델
-
-**사용처:**
-- `utils/llm/graph_utils/base.py`: 각 노드에서 체인 호출
-
-#### `retrieval.py`
-**목적**: 테이블 메타데이터 검색 및 재순위화
-
-**주요 함수:**
-- `search_tables(query, retriever_name, top_n, device)`: 테이블 메타데이터 검색
- - `retriever_name`: "기본" 또는 "Reranker"
- - `top_n`: 반환할 상위 결과 개수
-- `get_retriever(retriever_name, top_n, device)`: 검색기 생성
-- `load_reranker_model(device)`: 한국어 reranker 모델 로드
-
-**의존성:**
-- `utils.llm.vectordb.get_vector_db`: 벡터DB 인스턴스
-- `ko-reranker`: 한국어 재순위화 모델
-
-**사용처:**
-- `utils/llm/graph_utils/base.py`: `get_table_info_node`에서 호출
-- `utils/llm/tools/chatbot_tool.py`: `search_database_tables`에서 호출
-
-#### `llm_response_parser.py`
-**목적**: LLM 응답에서 ``, `<해석>` 블록 추출
-
-**사용처:**
-- `engine/query_executor.py`: SQL 추출 함수
-
-### 하위 디렉토리 모듈
-
-#### `core/` - LLM/Embedding 팩토리
-**목적**: 다양한 제공자(OpenAI, Azure, Bedrock, Gemini, Ollama, HuggingFace)의 LLM과 Embedding 모델을 통일된 인터페이스로 사용
-
-**주요 기능:**
-- `get_llm(**kwargs)`: 환경변수 기반 LLM 인스턴스 생성
-- `get_embeddings()`: 환경변수 기반 Embedding 인스턴스 생성
-- 제공자별 전용 함수들 제공
-
-**사용처:**
-- `utils/llm/chains.py`: `get_llm()` 호출
-- `utils/llm/vectordb/faiss_db.py`: `get_embeddings()` 호출
-- `utils/llm/vectordb/pgvector_db.py`: `get_embeddings()` 호출
-
-**상세 문서**: [core/README.md](./core/README.md)
-
-#### `graph_utils/` - LangGraph 워크플로우
-**목적**: LangGraph를 사용하여 자연어 질문을 SQL 쿼리로 변환하는 워크플로우 구현
-
-**주요 컴포넌트:**
-- `base.py`: 공통 상태(`QueryMakerState`) 및 노드 함수들
-- `basic_graph.py`: 기본 워크플로우 (QUESTION_GATE → GET_TABLE_INFO → EVALUATE_DOCUMENT_SUITABILITY → QUERY_MAKER)
-- `enriched_graph.py`: 확장 워크플로우 (PROFILE_EXTRACTION, CONTEXT_ENRICHMENT 추가)
-
-**워크플로우 노드:**
-- `question_gate_node`: SQL 적합성 판별
-- `get_table_info_node`: 벡터 검색으로 테이블 정보 수집
-- `document_suitability_node`: 문서 적합성 평가
-- `profile_extraction_node`: 질문 프로파일 추출
-- `context_enrichment_node`: 질문 컨텍스트 보강
-- `query_maker_node`: SQL 쿼리 생성
-
-**사용처:**
-- `engine/query_executor.py`: 그래프 선택 및 실행
-- `interface/core/session_utils.py`: Streamlit 세션 그래프 초기화
-
-**상세 문서**: [graph_utils/README.md](./graph_utils/README.md)
-
-#### `vectordb/` - 벡터 데이터베이스
-**목적**: 테이블 메타데이터를 벡터화하여 저장하고 검색
-
-**주요 기능:**
-- `get_vector_db()`: 환경변수 기반 벡터DB 인스턴스 반환
-- FAISS: 로컬 디스크 기반 벡터DB
-- pgvector: PostgreSQL 기반 벡터DB
-
-**사용처:**
-- `utils/llm/retrieval.py`: 테이블 검색 시 벡터DB 사용
-
-**상세 문서**: [vectordb/README.md](./vectordb/README.md)
-
-#### `tools/` - DataHub 메타데이터 및 ChatBot 도구
-**목적**: DataHub 메타데이터 수집 및 LangGraph ChatBot용 Tool 함수 제공
-
-**주요 기능:**
-- `get_info_from_db()`: DataHub에서 테이블 메타데이터를 LangChain Document로 수집
-- `get_metadata_from_db()`: 전체 메타데이터 딕셔너리 반환
-- `search_database_tables()`: 벡터 검색 기반 테이블 정보 검색 Tool
-- `get_glossary_terms()`: 용어집 정보 조회 Tool
-- `get_query_examples()`: 쿼리 예제 조회 Tool
-
-**사용처:**
-- `utils/llm/vectordb/faiss_db.py`: 벡터DB 초기화 시 메타데이터 수집
-- `utils/llm/vectordb/pgvector_db.py`: 벡터DB 초기화 시 메타데이터 수집
-- `utils/llm/chatbot.py`: ChatBot 도구로 사용
-
-**상세 문서**: [tools/README.md](./tools/README.md)
-
-#### `output_schema/` - 구조화 출력 모델
-**목적**: LLM 구조화 출력을 위한 Pydantic 모델 정의
-
-**주요 모델:**
-- `QuestionSuitability`: SQL 생성 적합성 판단 결과
-- `DocumentSuitability`: 단일 테이블 적합성 평가 결과
-- `DocumentSuitabilityList`: 문서 적합성 평가 결과 리스트
-
-**사용처:**
-- `utils/llm/chains.py`: 체인 생성 시 구조화 출력 모델로 사용
-
-**상세 문서**: [output_schema/README.md](./output_schema/README.md)
-
-### 통합 흐름(End-to-End)
-
-1. **사용자 질문 입력** → `engine/query_executor.execute_query()` 호출
-2. **그래프 선택 및 컴파일** → `graph_utils/basic_graph` 또는 `graph_utils/enriched_graph` 선택
-3. **QUESTION_GATE 노드** → `chains.create_question_gate_chain()`으로 SQL 적합성 판별
-4. **GET_TABLE_INFO 노드** → `retrieval.search_tables()` 호출
- - `vectordb.get_vector_db()`로 벡터DB 로드
- - 유사도 검색 또는 Reranker로 재순위화
- - 관련 테이블/컬럼 메타데이터 반환
-5. **EVALUATE_DOCUMENT_SUITABILITY 노드** → `chains.create_document_suitability_chain()`으로 문서 적합성 평가
-6. **PROFILE_EXTRACTION 노드** (확장 그래프만) → `chains.create_profile_extraction_chain()`으로 질문 특성 추출
- - `graph_utils/profile_utils.profile_to_text()`로 텍스트 변환
-7. **CONTEXT_ENRICHMENT 노드** (확장 그래프만) → `chains.create_query_enrichment_chain()`으로 질문 컨텍스트 보강
-8. **QUERY_MAKER 노드** → `chains.create_query_maker_chain()`으로 SQL 생성
- - DB 가이드/메타데이터 기반으로 SQL 생성 (`` 코드블록 포함)
-9. **SQL 추출** → `llm_response_parser.extract_sql()`로 최종 SQL 추출
-10. **실행 및 시각화** (선택적) → `infra/db/connect_db.run_sql()` 실행, `utils/visualization/display_chart`로 시각화
-
-### 환경 변수 요약
-
-#### LLM 관련
-- **`LLM_PROVIDER`**: LLM 제공자 선택 (`openai`, `azure`, `bedrock`, `gemini`, `ollama`, `huggingface`)
-- 제공자별 환경변수 (상세는 [core/README.md](./core/README.md) 참고)
- - OpenAI: `OPEN_AI_KEY`, `OPEN_AI_LLM_MODEL`
- - Azure: `AZURE_OPENAI_LLM_KEY`, `AZURE_OPENAI_LLM_ENDPOINT`, `AZURE_OPENAI_LLM_MODEL`, `AZURE_OPENAI_LLM_API_VERSION`
- - AWS Bedrock: `AWS_BEDROCK_LLM_MODEL`, `AWS_BEDROCK_LLM_ACCESS_KEY_ID`, `AWS_BEDROCK_LLM_SECRET_ACCESS_KEY`, `AWS_BEDROCK_LLM_REGION`
- - Gemini: `GEMINI_LLM_MODEL`
- - Ollama: `OLLAMA_LLM_MODEL`, `OLLAMA_LLM_BASE_URL`
- - HuggingFace: `HUGGING_FACE_LLM_MODEL`, `HUGGING_FACE_LLM_REPO_ID`, `HUGGING_FACE_LLM_ENDPOINT`, `HUGGING_FACE_LLM_API_TOKEN`
-
-#### Embedding 관련
-- **`EMBEDDING_PROVIDER`**: Embedding 제공자 선택 (`openai`, `azure`, `bedrock`, `gemini`, `ollama`, `huggingface`)
-- 제공자별 환경변수 (상세는 [core/README.md](./core/README.md) 참고)
-
-#### VectorDB 관련
-- **`VECTORDB_TYPE`**: 벡터DB 타입 선택 (`faiss` 또는 `pgvector`, 기본: `faiss`)
-- **FAISS**: `VECTORDB_LOCATION` (기본: `./dev/table_info_db`)
-- **pgvector**: `PGVECTOR_HOST`, `PGVECTOR_PORT`, `PGVECTOR_USER`, `PGVECTOR_PASSWORD`, `PGVECTOR_DATABASE`, `PGVECTOR_COLLECTION`
-- 상세는 [vectordb/README.md](./vectordb/README.md) 참고
-
-#### DataHub 관련
-- **`DATAHUB_SERVER`**: DataHub GMS 서버 URL (예: `http://localhost:8080`)
-- 상세는 [tools/README.md](./tools/README.md) 참고
-
-#### ClickHouse 관련 (SQL 실행 시)
-- `CLICKHOUSE_HOST`, `CLICKHOUSE_PORT`, `CLICKHOUSE_DATABASE`, `CLICKHOUSE_USER`, `CLICKHOUSE_PASSWORD`
-
-### 핵심 사용 예시
-
-#### 1. 기본 쿼리 실행
-
-```python
-from engine.query_executor import execute_query, extract_sql_from_result
-
-res = execute_query(
- query="지난달 매출 추이 보여줘",
- database_env="postgres",
- retriever_name="Reranker",
- top_n=5,
- device="cpu",
- use_enriched_graph=True,
-)
-
-sql = extract_sql_from_result(res)
-```
-
-#### 2. 체인 직접 사용
-
-```python
-from utils.llm.core import get_llm
-from utils.llm.chains import create_query_maker_chain
-
-llm = get_llm()
-chain = create_query_maker_chain(llm)
-
-result = chain.invoke({
- "user_input": "지난달 매출",
- "user_database_env": "postgres",
- "searched_tables": {...}
-})
-```
-
-#### 3. 벡터 검색 직접 사용
-
-```python
-from utils.llm.retrieval import search_tables
-
-tables = search_tables(
- query="고객 정보",
- retriever_name="Reranker",
- top_n=5,
- device="cpu"
-)
-```
-
-#### 4. 그래프 빌더 직접 사용
-
-```python
-from utils.llm.graph_utils import basic_builder, enriched_builder
-
-# 기본 그래프
-graph = basic_builder.compile()
-
-# 확장 그래프
-graph = enriched_builder.compile()
-
-# 그래프 실행
-result = graph.invoke({
- "messages": [HumanMessage(content="지난달 매출 보여줘")],
- "user_database_env": "postgres",
- # ... 기타 상태 정보
-})
-```
-
-### 파일간 의존 관계
-
-#### 상위 레벨 의존성
-```
-engine/query_executor.py
-├── utils/llm/graph_utils/
-│ ├── basic_graph.py
-│ ├── enriched_graph.py
-│ └── base.py
-│ ├── utils/llm/chains.py
-│ │ ├── utils/llm/core/get_llm()
-│ │ └── utils/llm/output_schema/
-│ └── utils/llm/retrieval.py
-│ └── utils/llm/vectordb/get_vector_db()
-│ ├── utils/llm/core/get_embeddings()
-│ └── utils/llm/tools/get_info_from_db()
-└── utils/llm/llm_response_parser.py
-```
-
-#### 주요 import 관계
-
-**graph_utils 모듈:**
-- `graph_utils/base.py` → `chains.py`, `retrieval.py` 사용
-- `graph_utils/basic_graph.py` → `graph_utils/base.py` 사용
-- `graph_utils/enriched_graph.py` → `graph_utils/base.py` 사용
-
-**chains 모듈:**
-- `chains.py` → `core/get_llm()`, `output_schema/` 사용
-
-**retrieval 모듈:**
-- `retrieval.py` → `vectordb/get_vector_db()` 사용
-
-**vectordb 모듈:**
-- `vectordb/faiss_db.py` → `core/get_embeddings()`, `tools/get_info_from_db()` 사용
-- `vectordb/pgvector_db.py` → `core/get_embeddings()`, `tools/get_info_from_db()` 사용
-
-**tools 모듈:**
-- `tools/datahub.py` → DataHub 메타데이터 수집
-- `tools/chatbot_tool.py` → `retrieval/search_tables()` 사용
-
-**외부 의존성:**
-- `engine/query_executor.py` → `graph_utils/` 사용
-- `interface/core/session_utils.py` → `graph_utils/` 사용
-- `interface/app_pages/graph_builder.py` → `graph_utils/base.py` 사용
diff --git a/utils/llm/chatbot.py b/utils/llm/chatbot.py
deleted file mode 100644
index d3b30b8..0000000
--- a/utils/llm/chatbot.py
+++ /dev/null
@@ -1,212 +0,0 @@
-"""
-LangGraph 기반 ChatBot 모델
-OpenAI의 ChatGPT 모델을 사용하여 대화 기록을 유지하는 챗봇 구현
-"""
-
-from typing import Annotated, Sequence, TypedDict
-
-from langchain_core.messages import BaseMessage, SystemMessage
-from langchain_openai import ChatOpenAI
-from langgraph.checkpoint.memory import MemorySaver
-from langgraph.graph import START, StateGraph
-from langgraph.graph.message import add_messages
-from langgraph.prebuilt import ToolNode
-
-from utils.llm.tools import (
- search_database_tables,
- get_glossary_terms,
- get_query_examples,
-)
-
-
-class ChatBotState(TypedDict):
- """
- 챗봇 상태 - 사용자 질문을 SQL로 변환 가능한 구체적인 질문으로 만들어가는 과정 추적
- """
-
- # 기본 메시지 (MessagesState와 동일)
- messages: Annotated[Sequence[BaseMessage], add_messages]
-
- # datahub 서버 정보
- gms_server: str
-
-
-class ChatBot:
- """
- LangGraph를 사용한 대화형 챗봇 클래스
- OpenAI API를 통해 다양한 GPT 모델을 사용할 수 있으며,
- MemorySaver를 통해 대화 기록을 관리합니다.
- """
-
- def __init__(
- self,
- openai_api_key: str,
- model_name: str = "gpt-4o-mini",
- gms_server: str = "http://localhost:8080",
- ):
- """
- ChatBot 인스턴스 초기화
-
- Args:
- openai_api_key: OpenAI API 키
- model_name: 사용할 모델명 (기본값: gpt-4o-mini)
- gms_server: DataHub GMS 서버 URL (기본값: http://localhost:8080)
- """
- self.openai_api_key = openai_api_key
- self.model_name = model_name
- self.gms_server = gms_server
- # SQL 생성을 위한 데이터베이스 메타데이터 조회 도구
- self.tools = [
- search_database_tables, # 데이터베이스 테이블 정보 검색
- get_glossary_terms, # 용어집 조회 도구
- get_query_examples, # 쿼리 예제 조회 도구
- ]
- self.llm = self._setup_llm() # LLM 인스턴스 설정
- self.app = self._setup_workflow() # LangGraph 워크플로우 설정
-
- def _setup_llm(self):
- """
- OpenAI ChatGPT LLM 인스턴스 생성
- Tool을 바인딩하여 LLM이 필요시 tool을 호출할 수 있도록 설정합니다.
-
- Returns:
- ChatOpenAI: Tool이 바인딩된 LLM 인스턴스
- """
- llm = ChatOpenAI(
- temperature=0.0, # SQL 생성은 정확성이 중요하므로 0으로 설정
- openai_api_key=self.openai_api_key,
- model_name=self.model_name,
- )
- # Tool을 LLM에 바인딩하여 함수 호출 기능 활성화
- return llm.bind_tools(self.tools)
-
- def _setup_workflow(self):
- """
- LangGraph 워크플로우 설정
- 대화 기록을 관리하고 LLM과 통신하는 그래프 구조를 생성합니다.
- Tool 호출 기능을 포함하여 LLM이 필요시 도구를 사용할 수 있도록 합니다.
-
- Returns:
- CompiledGraph: 컴파일된 LangGraph 워크플로우
- """
- # ChatBotState를 사용하는 StateGraph 생성
- workflow = StateGraph(state_schema=ChatBotState)
-
- def call_model(state: ChatBotState):
- """
- LLM 모델을 호출하는 노드 함수
- LLM이 응답을 생성하거나 tool 호출을 결정합니다.
-
- Args:
- state: 현재 메시지 상태
-
- Returns:
- dict: LLM 응답이 포함된 상태 업데이트
- """
- # 질문 구체화 전문 어시스턴트 시스템 메시지
- sys_msg = SystemMessage(content="""# 역할
-당신은 사용자의 모호한 질문을 명확하고 구체적인 질문으로 만드는 전문 AI 어시스턴트입니다.
-
-# 주요 임무
-- 사용자의 자연어 질문을 이해하고 의도를 정확히 파악합니다
-- 대화를 통해 날짜, 지표, 필터 조건 등 구체적인 정보를 수집합니다
-- 단계별로 사용자와 대화하며 명확하고 구체적인 질문으로 다듬어갑니다
-
-# 작업 프로세스
-1. 사용자의 최초 질문에서 의도 파악
-2. 질문을 명확히 하기 위해 필요한 정보 식별 (날짜, 지표, 대상, 조건 등)
-3. **도구를 적극 활용하여 데이터베이스 스키마, 테이블 정보, 용어집 등을 확인**
-4. 부족한 정보를 자연스럽게 질문하여 수집
-5. 수집된 정보를 바탕으로 질문을 점진적으로 구체화
-6. 충분히 구체화되면 최종 질문 확정
-
-# 도구 사용 가이드
-- **search_database_tables**: 사용자와의 대화를 데이터와 연관짓기 위해 관련 테이블을 적극적으로 확인할 수 있는 도구
-- **get_glossary_terms**: 사용자가 사용한 용어의 정확한 의미를 확인할 때 사용가능한 도구
-- **get_query_examples**: 조직내 저장된 쿼리 예제를 조회하여 참고할 수 있는 도구
-- 답변하기 전에 최대한 많은 도구를 적극 활용하여 정보를 수집하세요
-- 불확실한 정보가 있다면 추측하지 말고 도구를 사용하여 확인하세요
-
-# 예시
-- 모호한 질문: "KPI가 궁금해"
-- 대화 후 구체화: "2025-01-02 날짜의 신규 유저가 발생시킨 매출이 궁금해"
-
-# 주의사항
-- 항상 친절하고 명확하게 대화합니다
-- 이전 대화 맥락을 고려하여 일관성 있게 응답합니다
-- 한 번에 너무 많은 것을 물어보지 않고 단계적으로 진행합니다
-- **중요: 사용자가 말한 내용이 충분히 구체화되지 않거나 의도가 명확히 파악되지 않을 경우, 추측하지 말고 모든 도구(get_glossary_terms, get_query_examples, search_database_tables)를 적극적으로 사용하여 맥락을 파악하세요**
-- 도구를 통해 수집한 정보를 바탕으로 사용자에게 구체적인 방향성과 옵션을 제안하세요
-- 불확실한 정보가 있다면 추측하지 말고 도구를 사용하여 확인한 후 답변하세요
-
----
-다음은 사용자와의 대화입니다:""")
- # 시스템 메시지를 대화의 맨 앞에 추가
- messages = [sys_msg] + state["messages"]
- response = self.llm.invoke(messages)
- return {"messages": response}
-
- def route_model_output(state: ChatBotState):
- """
- LLM 출력에 따라 다음 노드를 결정하는 라우팅 함수
- Tool 호출이 필요한 경우 'tools' 노드로, 아니면 대화를 종료합니다.
-
- Args:
- state: 현재 메시지 상태
-
- Returns:
- str: 다음에 실행할 노드 이름 ('tools' 또는 '__end__')
- """
- messages = state["messages"]
- last_message = messages[-1]
- # LLM이 tool을 호출하려고 하는 경우 (tool_calls가 있는 경우)
- if hasattr(last_message, "tool_calls") and last_message.tool_calls:
- return "tools"
- # Tool 호출이 없으면 대화 종료
- return "__end__"
-
- # 워크플로우 구조 정의
- workflow.add_edge(START, "model") # 시작 -> model 노드
- workflow.add_node("model", call_model) # LLM 호출 노드
- workflow.add_node("tools", ToolNode(self.tools)) # Tool 실행 노드
-
- # model 노드 이후 조건부 라우팅
- workflow.add_conditional_edges("model", route_model_output)
- # Tool 실행 후 다시 model로 돌아가서 최종 응답 생성
- workflow.add_edge("tools", "model")
-
- # MemorySaver를 사용하여 대화 기록 저장 기능 추가
- return workflow.compile(checkpointer=MemorySaver())
-
- def chat(self, message: str, thread_id: str):
- """
- 사용자 메시지에 대한 응답 생성
-
- Args:
- message: 사용자 입력 메시지
- thread_id: 대화 세션을 구분하는 고유 ID
-
- Returns:
- dict: LLM 응답을 포함한 결과 딕셔너리
- """
- config = {"configurable": {"thread_id": thread_id}}
-
- # 상태 준비
- input_state = {
- "messages": [{"role": "user", "content": message}],
- "gms_server": self.gms_server, # DataHub 서버 URL을 상태에 포함
- }
-
- return self.app.invoke(input_state, config)
-
- def update_model(self, model_name: str):
- """
- 사용 중인 LLM 모델 변경
- 모델 변경 시 LLM 인스턴스와 워크플로우를 재설정합니다.
-
- Args:
- model_name: 변경할 모델명
- """
- self.model_name = model_name
- self.llm = self._setup_llm() # 새 모델로 LLM 재설정
- self.app = self._setup_workflow() # 워크플로우 재생성
diff --git a/utils/llm/core/README.md b/utils/llm/core/README.md
deleted file mode 100644
index c23e75c..0000000
--- a/utils/llm/core/README.md
+++ /dev/null
@@ -1,251 +0,0 @@
-# core
-
-LLM(Large Language Model)과 Embedding 모델을 생성하는 팩토리 패턴을 구현한 모듈입니다. 환경변수를 통해 다양한 제공자(OpenAI, Azure, Bedrock, Gemini, Ollama, HuggingFace)의 LLM과 Embedding 모델을 통일된 인터페이스로 사용할 수 있도록 합니다.
-
-## 디렉토리 구조
-
-```
-core/
-├── __init__.py
-├── factory.py
-└── README.md
-```
-
-## 파일 설명
-
-### `__init__.py`
-core 모듈의 공개 인터페이스를 정의합니다.
-
-**주요 내보내기:**
-- **LLM 팩토리 함수들:**
- - `get_llm`: 환경변수(`LLM_PROVIDER`)에 따라 적절한 LLM 제공자를 선택하여 반환
- - `get_llm_openai`: OpenAI LLM 인스턴스 생성
- - `get_llm_azure`: Azure OpenAI LLM 인스턴스 생성
- - `get_llm_bedrock`: AWS Bedrock LLM 인스턴스 생성
- - `get_llm_gemini`: Google Gemini LLM 인스턴스 생성
- - `get_llm_ollama`: Ollama LLM 인스턴스 생성
- - `get_llm_huggingface`: HuggingFace LLM 인스턴스 생성
-
-- **Embedding 팩토리 함수들:**
- - `get_embeddings`: 환경변수(`EMBEDDING_PROVIDER`)에 따라 적절한 Embedding 제공자를 선택하여 반환
- - `get_embeddings_openai`: OpenAI Embedding 인스턴스 생성
- - `get_embeddings_azure`: Azure OpenAI Embedding 인스턴스 생성
- - `get_embeddings_bedrock`: AWS Bedrock Embedding 인스턴스 생성
- - `get_embeddings_gemini`: Google Gemini Embedding 인스턴스 생성
- - `get_embeddings_ollama`: Ollama Embedding 인스턴스 생성
- - `get_embeddings_huggingface`: HuggingFace Embedding 인스턴스 생성
-
-### `factory.py`
-LLM과 Embedding 모델을 생성하는 팩토리 함수들의 구현을 포함합니다.
-
-**주요 내용:**
-
-#### LLM 팩토리 함수들
-
-- **`get_llm(**kwargs) -> BaseLanguageModel`**
- - 환경변수 `LLM_PROVIDER`를 확인하여 적절한 LLM 제공자를 선택합니다.
- - 지원하는 제공자: `openai`, `azure`, `bedrock`, `gemini`, `ollama`, `huggingface`
- - 각 제공자별 전용 함수를 호출하여 LLM 인스턴스를 반환합니다.
-
-- **제공자별 LLM 생성 함수들:**
- - `get_llm_openai(**kwargs)`: `ChatOpenAI` 인스턴스 생성
- - 환경변수: `OPEN_AI_LLM_MODEL`, `OPEN_AI_KEY`
- - `get_llm_azure(**kwargs)`: `AzureChatOpenAI` 인스턴스 생성
- - 환경변수: `AZURE_OPENAI_LLM_KEY`, `AZURE_OPENAI_LLM_ENDPOINT`, `AZURE_OPENAI_LLM_MODEL`, `AZURE_OPENAI_LLM_API_VERSION`
- - `get_llm_bedrock(**kwargs)`: `ChatBedrockConverse` 인스턴스 생성
- - 환경변수: `AWS_BEDROCK_LLM_MODEL`, `AWS_BEDROCK_LLM_ACCESS_KEY_ID`, `AWS_BEDROCK_LLM_SECRET_ACCESS_KEY`, `AWS_BEDROCK_LLM_REGION`
- - `get_llm_gemini(**kwargs)`: `ChatGoogleGenerativeAI` 인스턴스 생성
- - 환경변수: `GEMINI_LLM_MODEL`
- - `get_llm_ollama(**kwargs)`: `ChatOllama` 인스턴스 생성
- - 환경변수: `OLLAMA_LLM_MODEL`, `OLLAMA_LLM_BASE_URL` (선택적)
- - `get_llm_huggingface(**kwargs)`: `ChatHuggingFace` 인스턴스 생성
- - 환경변수: `HUGGING_FACE_LLM_MODEL`, `HUGGING_FACE_LLM_REPO_ID`, `HUGGING_FACE_LLM_ENDPOINT`, `HUGGING_FACE_LLM_API_TOKEN`
-
-#### Embedding 팩토리 함수들
-
-- **`get_embeddings() -> Optional[BaseLanguageModel]`**
- - 환경변수 `EMBEDDING_PROVIDER`를 확인하여 적절한 Embedding 제공자를 선택합니다.
- - 지원하는 제공자: `openai`, `azure`, `bedrock`, `gemini`, `ollama`, `huggingface`
- - 각 제공자별 전용 함수를 호출하여 Embedding 인스턴스를 반환합니다.
-
-- **제공자별 Embedding 생성 함수들:**
- - `get_embeddings_openai()`: `OpenAIEmbeddings` 인스턴스 생성
- - 환경변수: `OPEN_AI_EMBEDDING_MODEL`, `OPEN_AI_KEY`
- - `get_embeddings_azure()`: `AzureOpenAIEmbeddings` 인스턴스 생성
- - 환경변수: `AZURE_OPENAI_EMBEDDING_KEY`, `AZURE_OPENAI_EMBEDDING_ENDPOINT`, `AZURE_OPENAI_EMBEDDING_MODEL`, `AZURE_OPENAI_EMBEDDING_API_VERSION`
- - `get_embeddings_bedrock()`: `BedrockEmbeddings` 인스턴스 생성
- - 환경변수: `AWS_BEDROCK_EMBEDDING_MODEL`, `AWS_BEDROCK_EMBEDDING_ACCESS_KEY_ID`, `AWS_BEDROCK_EMBEDDING_SECRET_ACCESS_KEY`, `AWS_BEDROCK_EMBEDDING_REGION`
- - `get_embeddings_gemini()`: `GoogleGenerativeAIEmbeddings` 인스턴스 생성
- - 환경변수: `GEMINI_EMBEDDING_MODEL`, `GEMINI_EMBEDDING_KEY`
- - `get_embeddings_ollama()`: `OllamaEmbeddings` 인스턴스 생성
- - 환경변수: `OLLAMA_EMBEDDING_MODEL`, `OLLAMA_EMBEDDING_BASE_URL`
- - `get_embeddings_huggingface()`: `HuggingFaceEndpointEmbeddings` 인스턴스 생성
- - 환경변수: `HUGGING_FACE_EMBEDDING_MODEL`, `HUGGING_FACE_EMBEDDING_REPO_ID`, `HUGGING_FACE_EMBEDDING_API_TOKEN`
-
-## 사용 방법
-
-### 1. `utils/llm/chains.py`에서의 사용
-
-LangChain 체인을 생성할 때 LLM 인스턴스를 가져옵니다:
-
-```python
-from utils.llm.core import get_llm
-
-# 환경변수 LLM_PROVIDER에 따라 적절한 LLM 반환
-llm = get_llm()
-
-# 체인 생성 시 사용
-chain = prompt | llm | output_parser
-```
-
-**사용 위치:** `/home/dwlee/Lang2SQL/utils/llm/chains.py`의 모듈 레벨에서 `llm = get_llm()`로 전역 LLM 인스턴스 생성
-
-**사용되는 체인:**
-- Query Maker Chain
-- Query Enrichment Chain
-- Profile Extraction Chain
-- Question Gate Chain
-- Document Suitability Chain
-
-### 2. `utils/llm/vectordb/faiss_db.py`에서의 사용
-
-FAISS 벡터 데이터베이스를 생성하거나 로드할 때 Embedding 모델을 가져옵니다:
-
-```python
-from utils.llm.core import get_embeddings
-
-# 환경변수 EMBEDDING_PROVIDER에 따라 적절한 Embedding 반환
-embeddings = get_embeddings()
-
-# FAISS 벡터 DB 생성
-db = FAISS.from_documents(documents, embeddings)
-
-# 또는 로드
-db = FAISS.load_local(vectordb_path, embeddings, allow_dangerous_deserialization=True)
-```
-
-**사용 위치:** `/home/dwlee/Lang2SQL/utils/llm/vectordb/faiss_db.py`의 `get_faiss_vector_db()` 함수
-
-### 3. `utils/llm/vectordb/pgvector_db.py`에서의 사용
-
-PGVector 벡터 데이터베이스를 생성하거나 연결할 때 Embedding 모델을 가져옵니다:
-
-```python
-from utils.llm.core import get_embeddings
-
-# 환경변수 EMBEDDING_PROVIDER에 따라 적절한 Embedding 반환
-embeddings = get_embeddings()
-
-# PGVector 벡터 스토어 생성
-vector_store = PGVector.from_documents(
- documents=documents,
- embedding=embeddings,
- collection_name=collection_name,
- connection_string=connection_string,
-)
-```
-
-**사용 위치:** `/home/dwlee/Lang2SQL/utils/llm/vectordb/pgvector_db.py`의 `get_pgvector_db()` 함수
-
-## 환경변수 설정
-
-이 모듈을 사용하려면 다음 환경변수들을 설정해야 합니다:
-
-### LLM 설정
-
-```bash
-# LLM 제공자 선택 (필수)
-export LLM_PROVIDER="openai" # 또는 azure, bedrock, gemini, ollama, huggingface
-
-# OpenAI 설정
-export OPEN_AI_LLM_MODEL="gpt-4o"
-export OPEN_AI_KEY="your-api-key"
-
-# Azure OpenAI 설정
-export AZURE_OPENAI_LLM_KEY="your-key"
-export AZURE_OPENAI_LLM_ENDPOINT="your-endpoint"
-export AZURE_OPENAI_LLM_MODEL="your-deployment-name"
-export AZURE_OPENAI_LLM_API_VERSION="2023-07-01-preview"
-
-# AWS Bedrock 설정
-export AWS_BEDROCK_LLM_MODEL="anthropic.claude-3-sonnet-20240229-v1:0"
-export AWS_BEDROCK_LLM_ACCESS_KEY_ID="your-access-key"
-export AWS_BEDROCK_LLM_SECRET_ACCESS_KEY="your-secret-key"
-export AWS_BEDROCK_LLM_REGION="us-east-1"
-
-# Gemini 설정
-export GEMINI_LLM_MODEL="gemini-pro"
-
-# Ollama 설정
-export OLLAMA_LLM_MODEL="llama2"
-export OLLAMA_LLM_BASE_URL="http://localhost:11434" # 선택적
-
-# HuggingFace 설정
-export HUGGING_FACE_LLM_MODEL="meta-llama/Llama-2-7b-chat-hf"
-export HUGGING_FACE_LLM_REPO_ID="your-repo-id"
-export HUGGING_FACE_LLM_ENDPOINT="your-endpoint"
-export HUGGING_FACE_LLM_API_TOKEN="your-token"
-```
-
-### Embedding 설정
-
-```bash
-# Embedding 제공자 선택 (필수)
-export EMBEDDING_PROVIDER="openai" # 또는 azure, bedrock, gemini, ollama, huggingface
-
-# OpenAI Embedding 설정
-export OPEN_AI_EMBEDDING_MODEL="text-embedding-ada-002"
-export OPEN_AI_KEY="your-api-key"
-
-# Azure OpenAI Embedding 설정
-export AZURE_OPENAI_EMBEDDING_KEY="your-key"
-export AZURE_OPENAI_EMBEDDING_ENDPOINT="your-endpoint"
-export AZURE_OPENAI_EMBEDDING_MODEL="your-deployment-name"
-export AZURE_OPENAI_EMBEDDING_API_VERSION="2023-07-01-preview"
-
-# AWS Bedrock Embedding 설정
-export AWS_BEDROCK_EMBEDDING_MODEL="amazon.titan-embed-text-v1"
-export AWS_BEDROCK_EMBEDDING_ACCESS_KEY_ID="your-access-key"
-export AWS_BEDROCK_EMBEDDING_SECRET_ACCESS_KEY="your-secret-key"
-export AWS_BEDROCK_EMBEDDING_REGION="us-east-1"
-
-# Gemini Embedding 설정
-export GEMINI_EMBEDDING_MODEL="models/embedding-001"
-export GEMINI_EMBEDDING_KEY="your-api-key"
-
-# Ollama Embedding 설정
-export OLLAMA_EMBEDDING_MODEL="nomic-embed-text"
-export OLLAMA_EMBEDDING_BASE_URL="http://localhost:11434"
-
-# HuggingFace Embedding 설정
-export HUGGING_FACE_EMBEDDING_MODEL="sentence-transformers/all-MiniLM-L6-v2"
-export HUGGING_FACE_EMBEDDING_REPO_ID="your-repo-id"
-export HUGGING_FACE_EMBEDDING_API_TOKEN="your-token"
-```
-
-## 설계 패턴
-
-이 모듈은 **팩토리 패턴(Factory Pattern)**을 사용하여 구현되었습니다:
-
-1. **통일된 인터페이스**: `get_llm()`과 `get_embeddings()` 함수를 통해 다양한 제공자를 동일한 방식으로 사용 가능
-2. **환경변수 기반 설정**: 코드 변경 없이 환경변수만 수정하여 다른 제공자로 전환 가능
-3. **확장성**: 새로운 제공자를 추가하려면 `factory.py`에 해당 제공자의 생성 함수만 추가하면 됨
-
-## 지원하는 제공자
-
-### LLM 제공자
-- **OpenAI**: GPT 모델 시리즈
-- **Azure OpenAI**: Azure에서 호스팅되는 OpenAI 모델
-- **AWS Bedrock**: Claude, Llama 등 다양한 모델
-- **Google Gemini**: Gemini 프로/플래시 모델
-- **Ollama**: 로컬에서 실행되는 오픈소스 LLM
-- **HuggingFace**: HuggingFace Hub/Endpoint를 통한 모델 접근
-
-### Embedding 제공자
-- **OpenAI**: text-embedding-ada-002 등
-- **Azure OpenAI**: Azure에서 호스팅되는 OpenAI Embedding 모델
-- **AWS Bedrock**: Amazon Titan Embedding 모델
-- **Google Gemini**: Gemini Embedding 모델
-- **Ollama**: 로컬에서 실행되는 Embedding 모델
-- **HuggingFace**: HuggingFace Hub/Endpoint를 통한 Embedding 모델
-
diff --git a/utils/llm/core/__init__.py b/utils/llm/core/__init__.py
deleted file mode 100644
index 76e54fe..0000000
--- a/utils/llm/core/__init__.py
+++ /dev/null
@@ -1,3 +0,0 @@
-# Legacy LLM core module — factory removed (replaced by src/lang2sql/factory.py)
-
-__all__ = []
diff --git a/utils/llm/llm_response_parser.py b/utils/llm/llm_response_parser.py
deleted file mode 100644
index df94924..0000000
--- a/utils/llm/llm_response_parser.py
+++ /dev/null
@@ -1,57 +0,0 @@
-"""
-LLM 응답 텍스트에서 특정 마크업 태그(``, `<해석>`)에 포함된 콘텐츠 블록을 추출하는 유틸리티 모듈입니다.
-
-이 모듈은 OpenAI, LangChain 등에서 생성된 LLM 응답 문자열에서 Markdown 코드 블록을 파싱하여,
-SQL 쿼리 및 자연어 해석 설명을 분리하여 사용할 수 있도록 정적 메서드 형태의 API를 제공합니다.
-
-지원되는 태그:
- - : SQL 코드 블록 (```sql ... ```)
- - <해석>: 자연어 해석 블록 (```plaintext ... ```)
-"""
-
-import re
-
-
-class LLMResponseParser:
- """
- LLM 응답 문자열에서 특정 태그(, <해석>)에 포함된 블록을 추출하는 유틸리티 클래스입니다.
-
- 주요 기능:
- - 태그 내 ```sql ... ``` 블록에서 SQL 쿼리 추출
- - <해석> 태그 내 ```plaintext ... ``` 블록에서 자연어 해석 추출
- """
-
- @staticmethod
- def extract_sql(text: str) -> str:
- """
- 태그 내부의 SQL 코드 블록만 추출합니다.
-
- Args:
- text (str): 전체 LLM 응답 문자열.
-
- Returns:
- str: SQL 쿼리 문자열 (```sql ... ``` 내부 텍스트).
-
- Raises:
- ValueError: 태그 또는 SQL 코드 블록을 찾을 수 없는 경우.
- """
- match = re.search(r"\s*```sql\n(.*?)```", text, re.DOTALL)
- if match:
- return match.group(1).strip()
- raise ValueError("SQL 블록을 추출할 수 없습니다.")
-
- @staticmethod
- def extract_interpretation(text: str) -> str:
- """
- <해석> 태그 내부의 해석 설명 텍스트만 추출합니다.
-
- Args:
- text (str): 전체 LLM 응답 문자열.
-
- Returns:
- str: 해석 설명 텍스트. 블록이 존재하지 않으면 빈 문자열을 반환합니다.
- """
- match = re.search(r"<해석>\s*```plaintext\n(.*?)```", text, re.DOTALL)
- if match:
- return match.group(1).strip()
- return ""
diff --git a/utils/llm/tools/README.md b/utils/llm/tools/README.md
deleted file mode 100644
index fa7153d..0000000
--- a/utils/llm/tools/README.md
+++ /dev/null
@@ -1,284 +0,0 @@
-## utils.llm.tools 개요
-
-Lang2SQL 파이프라인에서 DataHub 메타데이터 수집 및 검색, 그리고 LangGraph ChatBot에서 사용하는 도구(Tool) 함수들을 제공하는 모듈입니다. 병렬 처리 기반의 효율적인 메타데이터 수집과 LangChain Tool 인터페이스를 통한 챗봇 기능을 지원합니다.
-
-### 파일 구조
-
-```
-utils/llm/tools/
-├── __init__.py # 모듈 진입점, 6개 함수 export
-├── datahub.py # DataHub 메타데이터 수집 및 처리
-└── chatbot_tool.py # LangGraph ChatBot용 Tool 함수들
-```
-
-### 각 파일 상세 설명
-
-#### __init__.py
-
-**목적**: tools 모듈의 공개 인터페이스 정의
-
-**Export 함수들**:
-
-**datahub 모듈에서**:
-- `set_gms_server`: GMS 서버 설정
-- `get_info_from_db`: LangChain Document 리스트로 테이블/컬럼 정보 반환
-- `get_metadata_from_db`: 전체 메타데이터 딕셔너리 리스트 반환
-
-**chatbot_tool 모듈에서**:
-- `search_database_tables`: 벡터 검색 기반 테이블 정보 검색
-- `get_glossary_terms`: 용어집 정보 조회
-- `get_query_examples`: 쿼리 예제 조회
-
-#### datahub.py
-
-**목적**: DataHub에서 테이블 및 컬럼 메타데이터를 수집하고 LangChain Document 형식으로 변환
-
-**주요 함수**:
-
-1. **`set_gms_server(gms_server: str)`**
- - 환경변수 `DATAHUB_SERVER`를 설정하고 DatahubMetadataFetcher 초기화
- - 유효하지 않은 서버 URL 시 ValueError 발생
-
-2. **`get_info_from_db(max_workers: int = 8) -> List[Document]`**
- - DataHub에서 모든 테이블 메타데이터를 수집하여 LangChain Document 리스트 반환
- - 각 Document에는 테이블명, 설명, 컬럼 정보가 포함
- - 형식: `"{테이블명}: {설명}\nColumns:\n {컬럼명}: {컬럼설명}"`
- - `parallel_process()`로 병렬 처리 (기본 8 워커)
- - 반환: LangChain Document 리스트
-
-3. **`get_metadata_from_db() -> List[Dict]`**
- - DataHub에서 전체 메타데이터를 딕셔너리 형태로 수집
- - 각 테이블의 상세 메타데이터 반환
- - 반환: 메타데이터 딕셔너리 리스트
-
-**내부 헬퍼 함수**:
-
-- **`parallel_process()`**: ThreadPoolExecutor 기반 병렬 처리 유틸리티
- - tqdm 진행률 표시 지원
- - `max_workers`: 동시 실행 워커 수 (기본: 8)
-
-- **`_get_fetcher()`**: 환경변수 기반 DatahubMetadataFetcher 인스턴스 생성
-
-- **`_process_urn()`**: URN에서 테이블명과 설명 추출
-
-- **`_get_table_info()`**: 병렬 처리로 모든 테이블 정보 수집
-
-- **`_get_column_info()`**: 특정 테이블의 컬럼 정보 수집
-
-- **`_extract_dataset_name_from_urn()`**: URN에서 데이터셋 이름 추출
-
-**의존성**:
-- `langchain.schema.Document`: LangChain Document 타입
-- `utils.data.datahub_source.DatahubMetadataFetcher`: DataHub 메타데이터 페처
-- `concurrent.futures.ThreadPoolExecutor`: 병렬 처리
-- `tqdm`: 진행률 표시
-
-**사용처**:
-- `utils/llm/vectordb/faiss_db.py`: FAISS 벡터DB 초기화 시 메타데이터 수집
-- `utils/llm/vectordb/pgvector_db.py`: pgvector 벡터DB 초기화 시 메타데이터 수집
-
-**특징**:
-- 병렬 처리로 성능 최적화
-- tqdm 진행률 표시
-- 자동 재시도/에러 핸들링
-- LangChain Document 형식 지원
-
-#### chatbot_tool.py
-
-**목적**: LangGraph ChatBot에서 사용하는 LangChain Tool 함수들
-
-**주요 함수 (모두 `@tool` 데코레이터 사용)**:
-
-1. **`search_database_tables(query, retriever_name, top_n, device)`**
- - **목적**: 자연어 쿼리를 기반으로 관련 테이블 정보 검색
- - **파라미터**:
- - `query`: 검색할 자연어 질문
- - `retriever_name`: "기본" 또는 "Reranker" (기본: "기본")
- - `top_n`: 반환할 테이블 개수 (기본: 5)
- - `device`: "cpu" 또는 "cuda" (기본: "cpu")
- - **반환**: `{테이블명: {table_description, 컬럼명: 컬럼설명, ...}}`
- - **내부**: `utils.llm.retrieval.search_tables()` 호출
- - **사용 시기**:
- - SQL 쿼리 생성 전 스키마 정보 필요
- - "어떤 테이블을 사용해야 해?"
- - "고객 관련 테이블 정보를 알려줘"
-
-2. **`get_glossary_terms(gms_server)`**
- - **목적**: DataHub 용어집(Glossary) 정보 조회
- - **파라미터**:
- - `gms_server`: DataHub GMS 서버 URL (기본: "http://35.222.65.99:8080")
- - **반환**: `[{name, description, children: [...]}, ...]`
- - **내부**: `GlossaryService.get_glossary_data()` 호출
- - **특징**:
- - 조직 특화 용어/비즈니스 정의 제공
- - `_simplify_glossary_data()`로 필수 필드만 추출
- - **사용 시기**:
- - 모호한 용어 이해 필요
- - "용어집을 보여줘"
- - 조직 내부 용어 확인
-
-3. **`get_query_examples(gms_server, start, count, query)`**
- - **목적**: DataHub에 저장된 SQL 쿼리 예제 조회
- - **파라미터**:
- - `gms_server`: DataHub GMS 서버 URL (기본: "http://35.222.65.99:8080")
- - `start`: 시작 위치 (기본: 0)
- - `count`: 반환 개수 (기본: 10)
- - `query`: 검색 쿼리 (기본: "*")
- - **반환**: `[{name, description, statement}, ...]`
- - **내부**: `QueryService.get_query_data()` 호출
- - **특징**: 조직 내 검증된 쿼리 패턴 참고
- - **사용 시기**:
- - 복잡한 쿼리 생성 시 참고
- - "쿼리 예제를 보여줘"
- - "비슷한 쿼리 있어?"
-
-**내부 헬퍼 함수**:
-
-- **`_simplify_glossary_data()`**: 용어집 데이터를 name, description, children만 포함하는 간단한 형태로 변환
-
-**의존성**:
-- `langchain_core.tools.tool`: LangChain Tool 데코레이터
-- `utils.data.datahub_services.base_client.DataHubBaseClient`: DataHub 기본 클라이언트
-- `utils.data.datahub_services.glossary_service.GlossaryService`: 용어집 서비스
-- `utils.data.datahub_services.query_service.QueryService`: 쿼리 서비스
-- `utils.llm.retrieval.search_tables`: 벡터 검색 함수
-
-**사용처**:
-- `utils/llm/chatbot.py`: ChatBot 클래스에서 LLM에 바인딩하여 사용
-
-**특징**:
-- LangChain Tool 인터페이스 준수
-- LLM이 자동으로 필요 시 호출
-- 상세한 docstring으로 LLM 이해도 향상
-- 에러 처리 및 fallback 제공
-
-### 사용 방법
-
-#### 1. DataHub 메타데이터 수집 (vectorDB 초기화)
-
-```python
-from utils.llm.tools import get_info_from_db
-
-# 모든 테이블 메타데이터를 LangChain Document로 수집
-documents = get_info_from_db(max_workers=8)
-
-# 각 document는 다음과 같은 형식:
-# "테이블명: 설명\nColumns:\n 컬럼1: 설명1\n 컬럼2: 설명2"
-```
-
-#### 2. GMS 서버 설정
-
-```python
-from utils.llm.tools import set_gms_server
-
-# DataHub 서버 설정
-set_gms_server("http://localhost:8080")
-```
-
-#### 3. ChatBot에서 Tool 사용
-
-```python
-from utils.llm.chatbot import ChatBot
-from utils.llm.tools import (
- search_database_tables,
- get_glossary_terms,
- get_query_examples
-)
-
-# ChatBot 초기화 (도구들이 자동으로 바인딩됨)
-chatbot = ChatBot(
- openai_api_key="your-key",
- model_name="gpt-4o-mini",
- gms_server="http://localhost:8080"
-)
-
-# LLM이 필요시 자동으로 tool 호출
-response = chatbot.invoke("고객 테이블 정보를 알려줘")
-# LLM이 search_database_tables를 자동으로 호출
-```
-
-#### 4. 직접 Tool 함수 호출
-
-```python
-from utils.llm.tools import search_database_tables, get_glossary_terms, get_query_examples
-
-# 테이블 검색
-tables = search_database_tables(
- query="고객 정보",
- retriever_name="기본",
- top_n=5
-)
-
-# 용어집 조회
-glossary = get_glossary_terms(gms_server="http://localhost:8080")
-
-# 쿼리 예제 조회
-queries = get_query_examples(
- gms_server="http://localhost:8080",
- start=0,
- count=10
-)
-```
-
-### import 관계
-
-**import하는 파일**:
-- `utils/llm/chatbot.py`: `from utils.llm.tools import search_database_tables, get_glossary_terms, get_query_examples`
-- `utils/llm/vectordb/faiss_db.py`: `from utils.llm.tools import get_info_from_db`
-- `utils/llm/vectordb/pgvector_db.py`: `from utils.llm.tools import get_info_from_db`
-- `interface/core/config/settings.py`: `from utils.llm.tools import set_gms_server`
-
-**내부 의존성**:
-- `utils.data.datahub_source.DatahubMetadataFetcher`: DataHub 메타데이터 페처
-- `utils.data.datahub_services.*`: DataHub 서비스 레이어
-- `utils.llm.retrieval.search_tables`: 벡터 검색 기능
-
-**외부 의존성**:
-- `langchain.schema.Document`: LangChain Document 타입
-- `langchain_core.tools.tool`: LangChain Tool 데코레이터
-- `concurrent.futures.ThreadPoolExecutor`: 병렬 처리
-- `tqdm`: 진행률 표시
-
-### 환경 변수 요약
-
-- **`DATAHUB_SERVER`**: DataHub GMS 서버 URL (예: "http://localhost:8080")
-- **`VECTORDB_TYPE`**: 벡터DB 타입 (datahub.py 사용 시 간접적으로 영향)
-- **`EMBEDDING_PROVIDER`**: 임베딩 공급자 (retrieval 기능 사용 시 필요)
-
-### 주요 특징
-
-1. **병렬 처리**: 8개 워커로 메타데이터 수집 속도 향상
-2. **LangChain 통합**: Document 및 Tool 인터페이스 지원
-3. **LangGraph ChatBot 연동**: LLM이 자동으로 도구 호출
-4. **진행률 표시**: tqdm을 통한 실시간 진행 상황 표시
-5. **에러 처리**: 유효한 GMS 서버 확인 및 예외 처리
-6. **데이터 정규화**: 조직 특화 데이터를 표준 형식으로 변환
-
-### 통합 흐름
-
-#### 메타데이터 수집 흐름 (벡터DB 초기화 시)
-
-1. `get_info_from_db()` 호출
-2. `_get_fetcher()`로 DatahubMetadataFetcher 인스턴스 생성
-3. `parallel_process()`로 병렬 테이블 정보 수집
-4. 각 테이블별로 컬럼 정보 추가 수집
-5. LangChain Document 리스트로 변환하여 반환
-6. vectordb가 이를 임베딩하여 벡터DB에 저장
-
-#### ChatBot 도구 사용 흐름
-
-1. ChatBot 초기화 시 `@tool` 데코레이터 함수들을 tools 리스트에 추가
-2. LLM에 `bind_tools(tools)`로 바인딩
-3. 사용자 질문 처리 중 LLM이 필요한 도구 판단
-4. ToolNode가 자동으로 해당 함수 호출
-5. 결과를 LLM에 전달하여 최종 응답 생성
-
-### 개선 가능 영역
-
-- 추가 메타데이터 타입 지원 (용어집, 관계 등)
-- 캐싱 메커니즘으로 성능 향상
-- 재시도 로직 강화
-- 더 많은 Tool 함수 추가
-- 도구 사용 통계 및 모니터링
-- 비동기 처리 지원
-
diff --git a/utils/llm/tools/__init__.py b/utils/llm/tools/__init__.py
deleted file mode 100644
index f0dcb9d..0000000
--- a/utils/llm/tools/__init__.py
+++ /dev/null
@@ -1,20 +0,0 @@
-from utils.llm.tools.datahub import (
- get_info_from_db,
- get_metadata_from_db,
- set_gms_server,
-)
-
-from utils.llm.tools.chatbot_tool import (
- search_database_tables,
- get_glossary_terms,
- get_query_examples,
-)
-
-__all__ = [
- "set_gms_server",
- "get_info_from_db",
- "get_metadata_from_db",
- "search_database_tables",
- "get_glossary_terms",
- "get_query_examples",
-]
diff --git a/utils/llm/tools/chatbot_tool.py b/utils/llm/tools/chatbot_tool.py
deleted file mode 100644
index 1c3aad8..0000000
--- a/utils/llm/tools/chatbot_tool.py
+++ /dev/null
@@ -1,329 +0,0 @@
-"""
-LangGraph ChatBot에서 사용하는 도구(Tool) 함수들
-"""
-
-from langchain_core.tools import tool
-from utils.data.datahub_services.base_client import DataHubBaseClient
-from utils.data.datahub_services.glossary_service import GlossaryService
-from utils.data.datahub_services.query_service import QueryService
-
-
-@tool
-def search_database_tables(
- query: str, retriever_name: str = "기본", top_n: int = 5, device: str = "cpu"
-) -> dict:
- """
- 사용자의 자연어 쿼리를 기반으로 관련된 데이터베이스 테이블 정보를 검색합니다.
-
- 이 함수는 SQL 쿼리 생성을 위해 필요한 테이블과 컬럼 정보를 찾아줍니다.
- 사용자가 어떤 테이블을 사용해야 할지, 어떤 컬럼이 있는지 물어보거나,
- SQL 쿼리를 만들기 위한 스키마 정보가 필요할 때 이 도구를 사용하세요.
-
- Args:
- query (str): 검색하려는 자연어 질문입니다.
- 예: "고객 정보를 조회하려면?", "주문 관련 테이블"
- retriever_name (str, optional): 검색기 유형입니다.
- "기본" 또는 "Reranker" 중 선택. 기본값은 "기본"
- top_n (int, optional): 검색할 테이블 개수입니다. 기본값은 5개
- device (str, optional): 모델 실행 장치입니다. "cpu" 또는 "cuda". 기본값은 "cpu"
-
- Returns:
- dict: 테이블 정보가 담긴 딕셔너리입니다.
- 각 테이블은 키로 저장되며, 값으로 테이블 설명과 컬럼 정보를 포함합니다.
-
- 예시 형태:
- {
- "customers": {
- "table_description": "고객 정보 테이블",
- "customer_id": "고객 고유 ID",
- "name": "고객 이름",
- "email": "고객 이메일"
- },
- "orders": {
- "table_description": "주문 정보 테이블",
- "order_id": "주문 ID",
- "customer_id": "고객 ID (외래키)"
- }
- }
-
- Examples:
- >>> search_database_tables("고객 정보가 필요해")
- {'customers': {'table_description': '고객 정보 테이블', ...}}
-
- Note:
- 이 도구는 다음과 같은 경우에 사용하세요:
- - "어떤 테이블을 사용해야 해?"
- - "고객 관련 테이블 정보를 알려줘"
- - "주문 데이터는 어디에 있어?"
- - "사용 가능한 컬럼을 보여줘"
- - SQL 쿼리를 생성하기 전에 스키마 정보가 필요할 때
- """
- try:
- import os
-
- from lang2sql.components.retrieval.keyword_retriever import KeywordRetriever
- from lang2sql.integrations.catalog.datahub_ import DataHubCatalogLoader
-
- gms_server = os.getenv("DATAHUB_SERVER", "http://localhost:8080")
- loader = DataHubCatalogLoader(gms_server=gms_server)
- catalog = loader.load()
- retriever = KeywordRetriever(catalog=catalog)
- results = retriever.run(query, top_k=top_n)
- return {
- entry["name"]: {
- "table_description": entry.get("description", ""),
- **entry.get("columns", {}),
- }
- for entry in results
- }
- except Exception as e:
- return {"error": True, "message": f"테이블 검색 중 오류 발생: {str(e)}"}
-
-
-def _simplify_glossary_data(glossary_data):
- """
- 용어집 데이터를 name, description, children만 포함하는 간단한 형태로 변환
-
- Args:
- glossary_data: 처리된 용어집 데이터
-
- Returns:
- list: 간소화된 용어집 데이터 (name, description, children만 포함)
- """
- if "error" in glossary_data:
- return glossary_data
-
- result = []
-
- for node in glossary_data.get("nodes", []):
- simplified_node = {
- "name": node.get("name"),
- "description": node.get("description"),
- }
-
- # children 정보가 있으면 추가
- if "details" in node and "children" in node["details"]:
- children = []
- for child in node["details"]["children"]:
- child_info = {
- "name": child.get("name"),
- "description": child.get("description"),
- }
- children.append(child_info)
-
- if children:
- simplified_node["children"] = children
-
- result.append(simplified_node)
-
- return result
-
-
-@tool
-def get_glossary_terms(gms_server: str = "http://35.222.65.99:8080") -> list:
- """
- DataHub에서 용어집(Glossary) 정보를 조회합니다.
-
- 이 함수는 DataHub 서버에 연결하여 전체 용어집 데이터를 가져옵니다.
- 용어집은 비즈니스 용어, 도메인 지식, 데이터 정의 등을 표준화하여 관리하는 곳입니다.
-
- **중요**: 사용자의 질문이나 대화에서 다음과 같은 상황이 발생하면 반드시 이 도구를 사용하세요:
- 1. 이해되지 않거나 모호한 단어가 나왔을 때
- 2. 특정 조직이나 도메인에서 고유하게 사용되는 전문 용어가 나왔을 때
- 3. 일반적이지 않은 약어나 줄임말이 나왔을 때
- 4. 조직 내부에서만 통용되는 용어가 나왔을 때
- 5. 표준 정의가 필요한 비즈니스 용어가 나왔을 때
-
- Args:
- gms_server (str, optional): DataHub GMS 서버 URL입니다.
- 기본값은 "http://35.222.65.99:8080"
-
- Returns:
- list: 간소화된 용어집 데이터 리스트입니다.
- 각 항목은 name, description, children(선택적) 필드를 포함합니다.
-
- 예시 형태:
- [
- {
- "name": "가짜연구소",
- "description": "스터디 단체 가짜연구소를 의미하며...",
- "children": [
- {
- "name": "빌더",
- "description": "가짜연구소 스터디 리더를 지칭..."
- }
- ]
- },
- {
- "name": "PII",
- "description": "개인 식별 정보...",
- "children": [
- {
- "name": "identifier",
- "description": "개인식별정보중 github 아이디..."
- }
- ]
- }
- ]
-
- Examples:
- >>> get_glossary_terms()
- [{'name': '가짜연구소', 'description': '...', 'children': [...]}]
-
- Note:
- 이 도구는 다음과 같은 경우에 **반드시** 사용하세요:
-
- [명시적 요청]
- - "용어집을 보여줘"
- - "비즈니스 용어가 뭐가 있어?"
- - "데이터 사전 정보를 알려줘"
- - "정의된 용어들을 보여줘"
-
- [이해되지 않는 단어 감지 - 매우 중요!]
- - 일반적이지 않은 약어나 전문 용어가 대화에 등장할 때
- - 표준 정의가 없는 도메인 특화 용어가 나올 때
- - 질문의 맥락에서 모호하거나 불명확한 용어가 있을 때
-
- [조직/도메인 특화 상황]
- - 특정 조직에서만 사용하는 내부 용어가 나올 때
- - 업계/도메인 전문 용어가 필요할 때
- - 데이터나 테이블 관련 비즈니스 컨텍스트를 이해하기 위해
-
- **핵심**: 응답하기 전에 사용자의 질문에 조직 특화 용어나 모호한 단어가
- 있는지 확인하고, 있다면 먼저 이 도구를 호출하여 정확한 정의를 파악하세요.
- """
- try:
- # DataHub 클라이언트 초기화
- client = DataHubBaseClient(gms_server=gms_server)
-
- # GlossaryService 초기화
- glossary_service = GlossaryService(client)
-
- # 전체 용어집 데이터 가져오기
- glossary_data = glossary_service.get_glossary_data()
-
- # 간소화된 데이터 반환
- simplified_data = _simplify_glossary_data(glossary_data)
-
- return simplified_data
-
- except ValueError as e:
- return {"error": True, "message": f"DataHub 서버 연결 실패: {str(e)}"}
- except Exception as e:
- return {"error": True, "message": f"용어집 조회 중 오류 발생: {str(e)}"}
-
-
-@tool
-def get_query_examples(
- gms_server: str = "http://35.222.65.99:8080",
- start: int = 0,
- count: int = 10,
- query: str = "*",
-) -> list:
- """
- DataHub에서 저장된 쿼리 예제들을 조회합니다.
-
- 이 함수는 DataHub 서버에 연결하여 저장된 SQL 쿼리 목록을 가져옵니다.
- 조직에서 실제로 사용되고 검증된 쿼리 패턴을 참고하여 더 정확한 SQL을 생성할 수 있습니다.
-
- **중요**: 사용자의 질문이나 대화에서 다음과 같은 상황이 발생하면 반드시 이 도구를 사용하세요:
- 1. 일반적인 SQL 패턴으로 해결하기 어려운 복잡한 쿼리 요청일 때
- 2. 조직 특화된 비즈니스 로직이나 데이터 처리 방식이 필요할 때
- 3. 특정 도메인의 표준 쿼리 패턴이나 관례를 따라야 할 때
- 4. 여러 테이블 간의 복잡한 JOIN이나 집계가 필요할 때
- 5. 사용자가 과거 실행했던 쿼리와 유사한 작업을 요청할 때
- 6. 조직 내에서 검증된 쿼리 작성 방식을 확인해야 할 때
-
- Args:
- gms_server (str, optional): DataHub GMS 서버 URL입니다.
- 기본값은 "http://35.222.65.99:8080"
- start (int, optional): 조회 시작 위치입니다. 기본값은 0
- count (int, optional): 조회할 쿼리 개수입니다. 기본값은 10
- query (str, optional): 검색 쿼리입니다. 기본값은 "*" (모든 쿼리)
-
- Returns:
- list: 쿼리 정보 리스트입니다.
- 각 항목은 name, description, statement 필드를 포함합니다.
-
- 예시 형태:
- [
- {
- "name": "고객별 주문 수 조회",
- "description": "각 고객별 주문 건수를 집계하는 쿼리",
- "statement": "SELECT customer_id, COUNT(*) as order_count FROM orders GROUP BY customer_id"
- },
- {
- "name": "월별 매출 현황",
- "description": "월별 총 매출을 계산하는 쿼리",
- "statement": "SELECT DATE_TRUNC('month', order_date) as month, SUM(amount) FROM orders GROUP BY month"
- }
- ]
-
- Examples:
- >>> get_query_examples()
- [{'name': '고객별 주문 수 조회', 'description': '...', 'statement': 'SELECT ...'}]
-
- >>> get_query_examples(count=5)
- # 5개의 쿼리 예제만 조회
-
- Note:
- 이 도구는 다음과 같은 경우에 **반드시** 사용하세요:
-
- [명시적 요청]
- - "쿼리 예제를 보여줘"
- - "저장된 쿼리들을 알려줘"
- - "과거 쿼리 내역을 보고 싶어"
- - "SQL 예제가 있어?"
-
- [도메인/조직 특화 패턴 감지 - 매우 중요!]
- - 조직 특화된 데이터 처리 방식이나 계산 로직이 필요할 때
- - 특정 도메인의 관례적인 쿼리 패턴을 따라야 할 때
- - 데이터 품질 규칙이나 비즈니스 룰이 반영된 쿼리가 필요할 때
- - 조직 내에서 표준화된 쿼리 작성 방식을 확인해야 할 때
-
- [쿼리 작성 참고]
- - "이런 유형의 쿼리는 어떻게 작성해?"
- - "비슷한 쿼리 있어?"
- - "다른 사람들은 어떻게 쿼리를 작성했어?"
- - "참고할만한 쿼리가 있을까?"
- - "이 테이블들을 어떻게 조인해야 해?"
-
- **핵심**: SQL 쿼리를 생성하기 전에 사용자의 요청이 복잡하거나,
- 조직 특화된 비즈니스 로직이 필요하거나, 일반적인 패턴으로 커버하기
- 어렵다고 판단되면, 먼저 이 도구를 호출하여 조직에서 검증된
- 쿼리 예제를 참고하세요. 이는 더 정확하고 조직의 표준을 따르는
- SQL을 생성하는 데 큰 도움이 됩니다.
- """
- try:
- # DataHub 클라이언트 초기화
- client = DataHubBaseClient(gms_server=gms_server)
-
- # QueryService 초기화
- query_service = QueryService(client)
-
- # 쿼리 데이터 가져오기
- result = query_service.get_query_data(start=start, count=count, query=query)
-
- # 오류 체크
- if "error" in result and result["error"]:
- return {"error": True, "message": result.get("message")}
-
- # name, description, statement만 추출하여 리스트 생성
- simplified_queries = []
- for query_item in result.get("queries", []):
- simplified_query = {
- "name": query_item.get("name"),
- "description": query_item.get("description", ""),
- "statement": query_item.get("statement", ""),
- }
- simplified_queries.append(simplified_query)
-
- return simplified_queries
-
- except ValueError as e:
- return {"error": True, "message": f"DataHub 서버 연결 실패: {str(e)}"}
- except Exception as e:
- return {
- "error": True,
- "message": f"쿼리 예제 조회 중 오류 발생: {str(e)}",
- }
diff --git a/utils/llm/tools/datahub.py b/utils/llm/tools/datahub.py
deleted file mode 100644
index 42e564d..0000000
--- a/utils/llm/tools/datahub.py
+++ /dev/null
@@ -1,167 +0,0 @@
-import os
-import re
-from concurrent.futures import ThreadPoolExecutor
-from typing import Callable, Dict, Iterable, List, Optional, TypeVar
-
-from langchain.schema import Document
-from tqdm import tqdm
-
-from utils.data.datahub_source import DatahubMetadataFetcher
-
-T = TypeVar("T")
-R = TypeVar("R")
-
-
-def parallel_process(
- items: Iterable[T],
- process_fn: Callable[[T], R],
- max_workers: int = 8,
- desc: Optional[str] = None,
- show_progress: bool = True,
-) -> List[R]:
- """병렬 처리를 위한 유틸리티 함수"""
- with ThreadPoolExecutor(max_workers=max_workers) as executor:
- futures = [executor.submit(process_fn, item) for item in items]
- if show_progress:
- futures = tqdm(futures, desc=desc)
- return [future.result() for future in futures]
-
-
-def set_gms_server(gms_server: str):
- try:
- os.environ["DATAHUB_SERVER"] = gms_server
- fetcher = DatahubMetadataFetcher(gms_server=gms_server)
- except ValueError as e:
- raise ValueError(f"GMS 서버 설정 실패: {str(e)}")
-
-
-def _get_fetcher():
- gms_server = os.getenv("DATAHUB_SERVER")
- if not gms_server:
- raise ValueError("GMS 서버가 설정되지 않았습니다.")
- return DatahubMetadataFetcher(gms_server=gms_server)
-
-
-def _process_urn(urn: str, fetcher: DatahubMetadataFetcher) -> tuple[str, str]:
- table_name = fetcher.get_table_name(urn)
- table_description = fetcher.get_table_description(urn)
- return (table_name, table_description)
-
-
-def _process_column_info(
- urn: str, table_name: str, fetcher: DatahubMetadataFetcher
-) -> Optional[List[Dict[str, str]]]:
- if fetcher.get_table_name(urn) == table_name:
- return fetcher.get_column_names_and_descriptions(urn)
- return None
-
-
-def _get_table_info(max_workers: int = 8) -> Dict[str, str]:
- fetcher = _get_fetcher()
- urns = fetcher.get_urns()
- table_info = {}
-
- results = parallel_process(
- urns,
- lambda urn: _process_urn(urn, fetcher),
- max_workers=max_workers,
- desc="테이블 정보 수집 중",
- )
-
- for table_name, table_description in results:
- if table_name and table_description:
- table_info[table_name] = table_description
-
- return table_info
-
-
-def _get_column_info(
- table_name: str, urn_table_mapping: Dict[str, str], max_workers: int = 8
-) -> List[Dict[str, str]]:
- target_urn = urn_table_mapping.get(table_name)
- if not target_urn:
- return []
-
- fetcher = _get_fetcher()
- column_info = fetcher.get_column_names_and_descriptions(target_urn)
-
- return column_info
-
-
-def _extract_dataset_name_from_urn(urn: str) -> Optional[str]:
- """URN 문자열에서 데이터셋 이름(예: delta.default.stg_gh_events)만 추출.
-
- 지원 패턴:
- - dataset URN: urn:li:dataset:(urn:li:dataPlatform:dbt,delta.default.stg_gh_events,PROD)
- - schemaField URN: urn:li:schemaField:(urn:li:dataset:(urn:li:dataPlatform:dbt,delta.default.stg_gh_events,PROD),event_id)
- """
- match = re.search(
- r"urn:li:dataset:\(urn:li:dataPlatform:[^,]+,([^,]+),[^)]+\)", urn
- )
- if match:
- return match.group(1)
- return None
-
-
-def get_info_from_db(max_workers: int = 8) -> List[Document]:
- table_info = _get_table_info(max_workers=max_workers)
-
- fetcher = _get_fetcher()
- urns = list(fetcher.get_urns())
- urn_table_mapping = {}
- display_name_by_table = {}
- for urn in urns:
- original_name = fetcher.get_table_name(urn)
- if original_name:
- urn_table_mapping[original_name] = urn
- parsed_name = _extract_dataset_name_from_urn(urn)
- if parsed_name:
- display_name_by_table[original_name] = parsed_name
-
- def process_table_info(item: tuple[str, str, str]) -> str:
- original_table_name, table_description, display_table_name = item
- # 컬럼 조회는 기존 테이블 이름으로 수행 (urn_table_mapping과 일치)
- column_info = _get_column_info(
- original_table_name, urn_table_mapping, max_workers=max_workers
- )
- column_info_str = "\n".join(
- [
- f"{col['column_name']}: {col['column_description']}"
- for col in column_info
- ]
- )
- used_name = display_table_name or original_table_name
- return f"{used_name}: {table_description}\nColumns:\n {column_info_str}"
-
- # 표시용 이름을 세 번째 파라미터로 함께 전달
- items_with_display = [
- (
- name,
- desc,
- display_name_by_table.get(name, name),
- )
- for name, desc in table_info.items()
- ]
-
- table_info_str_list = parallel_process(
- items_with_display,
- process_table_info,
- max_workers=max_workers,
- desc="컬럼 정보 수집 중",
- )
-
- return [Document(page_content=info) for info in table_info_str_list]
-
-
-def get_metadata_from_db() -> List[Dict]:
- fetcher = _get_fetcher()
- urns = list(fetcher.get_urns())
-
- metadata = []
- total = len(urns)
- for idx, urn in enumerate(urns, 1):
- print(f"[{idx}/{total}] Processing URN: {urn}")
- table_metadata = fetcher.build_table_metadata(urn)
- metadata.append(table_metadata)
-
- return metadata
diff --git a/utils/visualization/README.md b/utils/visualization/README.md
deleted file mode 100644
index 1ecd55e..0000000
--- a/utils/visualization/README.md
+++ /dev/null
@@ -1,125 +0,0 @@
-## utils.visualization 개요
-
-Lang2SQL 파이프라인에서 SQL 쿼리 결과를 시각화하기 위한 유틸리티 모듈입니다. LLM을 활용하여 적절한 차트를 자동 생성하고 Plotly를 통해 렌더링합니다.
-
-### 파일 구조
-
-```
-utils/visualization/
-└── display_chart.py # SQL 결과를 Plotly 차트로 변환하는 핵심 모듈
-```
-
-### 각 파일 상세 설명
-
-#### display_chart.py
-
-**목적**: SQL 쿼리 실행 결과를 다양한 형태의 Plotly 차트로 자동 변환하는 모듈
-
-**주요 클래스**:
-
-- **`DisplayChart`**: SQL 결과 시각화를 담당하는 메인 클래스
- - `question` (str): 사용자가 입력한 자연어 질문
- - `sql` (str): 실행된 SQL 쿼리
- - `df_metadata` (str): 데이터프레임의 메타데이터 정보
-
-**주요 메서드**:
-
-1. **`llm_model_for_chart(message_log)`**
- - 환경변수 `LLM_PROVIDER`가 "openai"일 경우 ChatOpenAI로 차트 코드 생성
- - 필요 환경변수: `OPEN_AI_KEY`, `OPEN_AI_LLM_MODEL` (기본: "gpt-4o")
- - 반환: 생성된 차트 코드 또는 None
-
-2. **`generate_plotly_code()`**
- - 사용자 질문, SQL 쿼리, 데이터프레임 메타데이터를 프롬프트로 구성
- - LLM이 데이터에 맞는 적절한 Plotly 코드 생성
- - 반환: Python 코드 문자열
-
-3. **`get_plotly_figure(plotly_code, df, dark_mode=True)`**
- - 생성된 Plotly 코드를 실행하여 Figure 객체 생성
- - 에러 발생 시 데이터 타입 기반 fallback 차트 생성:
- - 숫자 컬럼 2개 이상 → scatter plot
- - 숫자 1개 + 범주 1개 → bar plot
- - 범주 1개 (고유값 < 10) → pie chart
- - 기타 → line plot
- - dark_mode=True 시 "plotly_dark" 템플릿 적용
- - 반환: Plotly Figure 객체 또는 None
-
-4. **내부 헬퍼 메서드**:
- - `_extract_python_code(markdown_string)`: 마크다운에서 Python 코드 블록 추출
- - `_sanitize_plotly_code(raw_plotly_code)`: 불필요한 `fig.show()` 문 제거
-
-**의존성**:
-- `pandas`: 데이터프레임 처리
-- `plotly.express` (px): 간단한 차트 생성
-- `plotly.graph_objects` (go): 고급 차트 구성
-- `langchain_openai.ChatOpenAI`: LLM 차트 코드 생성
-- `langchain_core.messages`: SystemMessage, HumanMessage
-
-### 사용 방법
-
-#### 1. 기본 사용법 (interface/core/result_renderer.py에서 실제 사용)
-
-```python
-from utils.visualization.display_chart import DisplayChart
-import pandas as pd
-
-# DisplayChart 인스턴스 생성
-display_code = DisplayChart(
- question="지난달 매출 추이를 보여줘",
- sql="SELECT date, revenue FROM sales WHERE ...",
- df_metadata=f"Running df.dtypes gives:\n{df.dtypes}"
-)
-
-# Plotly 코드 생성
-plotly_code = display_code.generate_plotly_code()
-
-# Figure 객체 생성
-fig = display_code.get_plotly_figure(plotly_code=plotly_code, df=df)
-
-# Streamlit에서 차트 표시
-st.plotly_chart(fig)
-```
-
-#### 2. 통합 흐름 (Lang2SQL 파이프라인 내)
-
-`interface/core/result_renderer.py`의 `display_result()` 함수에서 사용:
-
-1. SQL 쿼리 실행 후 pandas DataFrame 반환
-2. `DisplayChart` 초기화 (질문, SQL, 메타데이터)
-3. `generate_plotly_code()`로 LLM 기반 차트 코드 생성
-4. `get_plotly_figure()`로 실행 및 Figure 객체 획득
-5. `st.plotly_chart()`로 Streamlit UI에 렌더링
-
-**경로**: `interface/core/result_renderer.py` (200-211번째 줄)
-
-### import 관계
-
-**import하는 파일**:
-- `interface/core/result_renderer.py`: `from utils.visualization.display_chart import DisplayChart`
-
-**외부 의존성**:
-- `langchain_openai.ChatOpenAI`: OpenAI LLM API 호출
-- `plotly`: 차트 렌더링 및 Figure 객체 관리
-- `pandas`: 데이터프레임 처리
-- 환경변수: `LLM_PROVIDER`, `OPEN_AI_KEY`, `OPEN_AI_LLM_MODEL`
-
-### 환경 변수 요약
-
-- **`LLM_PROVIDER`**: LLM 공급자 지정 (현재 "openai"만 지원)
-- **`OPEN_AI_KEY`**: OpenAI API 키
-- **`OPEN_AI_LLM_MODEL`**: 사용할 모델 (기본값: "gpt-4o")
-
-### 주요 특징
-
-1. **LLM 기반 지능형 차트 생성**: 데이터 구조와 질문 내용에 맞춰 적절한 차트 유형 자동 선택
-2. **Fallback 메커니즘**: LLM 코드 생성 실패 시 데이터 타입 기반 대체 차트 제공
-3. **다크 모드 지원**: 기본적으로 plotly_dark 템플릿 적용
-4. **에러 안전성**: 코드 실행 중 예외 발생 시에도 항상 유효한 Figure 객체 반환
-
-### 개선 가능 영역
-
-- 다른 LLM 공급자 지원 (현재 OpenAI만 지원)
-- 더 다양한 차트 유형 지원
-- 컬러 스킴 및 스타일 커스터마이징 옵션
-- 성능 최적화 (코드 생성 시간 단축)
-
diff --git a/utils/visualization/display_chart.py b/utils/visualization/display_chart.py
deleted file mode 100644
index 0a977d6..0000000
--- a/utils/visualization/display_chart.py
+++ /dev/null
@@ -1,188 +0,0 @@
-"""
-SQL 쿼리 결과를 Plotly로 시각화하는 모듈
-
-이 모듈은 Lang2SQL 실행 결과를 다양한 형태의 차트로 시각화하는 기능을 제공합니다.
-LLM을 활용하여 적절한 Plotly 코드를 생성하고 실행합니다.
-"""
-
-import os
-import re
-from typing import Optional
-
-import pandas as pd
-import plotly
-import plotly.express as px
-import plotly.graph_objects as go
-from langchain_core.messages import HumanMessage, SystemMessage
-from langchain_openai import ChatOpenAI
-
-
-class DisplayChart:
- """
- SQL쿼리가 실행된 결과를 그래프로 시각화하는 Class입니다.
-
- 쿼리 결과를 비롯한 유저 질문, sql를 prompt에 입력하여
- plotly코드를 출력하여 excute한 결과를 fig 객체로 반환합니다.
- """
-
- def __init__(self, question: str, sql: str, df_metadata: str):
- """
- DisplayChart 인스턴스를 초기화합니다.
-
- Args:
- question (str): 사용자 질문
- sql (str): 실행된 SQL 쿼리
- df_metadata (str): 데이터프레임 메타데이터
- """
- self.question = question
- self.sql = sql
- self.df_metadata = df_metadata
-
- def llm_model_for_chart(self, message_log) -> Optional[str]:
- """
- LLM 모델을 사용하여 차트 생성 코드를 생성합니다.
-
- Args:
- message_log: LLM에 전달할 메시지 목록
-
- Returns:
- Optional[str]: 생성된 차트 코드 또는 None
- """
- provider = os.getenv("LLM_PROVIDER")
- if provider == "openai":
- llm = ChatOpenAI(
- model=os.getenv("OPEN_AI_LLM_MODEL", "gpt-4o"),
- api_key=os.getenv("OPEN_AI_KEY"),
- )
- result = llm.invoke(message_log)
- return result
- return None
-
- def _extract_python_code(self, markdown_string: str) -> str:
- """
- 마크다운 문자열에서 Python 코드 블록을 추출합니다.
-
- Args:
- markdown_string: 마크다운 형식의 문자열
-
- Returns:
- str: 추출된 Python 코드
- """
- # Strip whitespace to avoid indentation errors in LLM-generated code
- if hasattr(markdown_string, "content"):
- markdown_string = markdown_string.content.split("```")[1][6:].strip()
- else:
- markdown_string = str(markdown_string)
-
- # Regex pattern to match Python code blocks
- pattern = r"```[\w\s]*python\n([\s\S]*?)```|```([\s\S]*?)```"
-
- # Find all matches in the markdown string
- matches = re.findall(pattern, markdown_string, re.IGNORECASE)
-
- # Extract the Python code from the matches
- python_code = []
- for match in matches:
- python = match[0] if match[0] else match[1]
- python_code.append(python.strip())
-
- if len(python_code) == 0:
- return markdown_string
-
- return python_code[0]
-
- def _sanitize_plotly_code(self, raw_plotly_code: str) -> str:
- """
- Plotly 코드에서 불필요한 부분을 제거합니다.
-
- Args:
- raw_plotly_code: 원본 Plotly 코드
-
- Returns:
- str: 정리된 Plotly 코드
- """
- # Remove the fig.show() statement from the plotly code
- plotly_code = raw_plotly_code.replace("fig.show()", "")
- return plotly_code
-
- def generate_plotly_code(self) -> str:
- """
- LLM을 사용하여 Plotly 코드를 생성합니다.
-
- Returns:
- str: 생성된 Plotly 코드
- """
- if self.question is not None:
- system_msg = f"The following is a pandas DataFrame that contains the results of the query that answers the question the user asked: '{self.question}'"
- else:
- system_msg = "The following is a pandas DataFrame "
-
- if self.sql is not None:
- system_msg += (
- f"\n\nThe DataFrame was produced using this query: {self.sql}\n\n"
- )
-
- system_msg += f"The following is information about the resulting pandas DataFrame 'df': \n{self.df_metadata}"
-
- message_log = [
- SystemMessage(content=system_msg),
- HumanMessage(
- content="Can you generate the Python plotly code to chart the results of the dataframe? Assume the data is in a pandas dataframe called 'df'. If there is only one value in the dataframe, use an Indicator. Respond with only Python code. Do not answer with any explanations -- just the code."
- ),
- ]
-
- plotly_code = self.llm_model_for_chart(message_log)
- if plotly_code is None:
- return ""
-
- return self._sanitize_plotly_code(self._extract_python_code(plotly_code))
-
- def get_plotly_figure(
- self, plotly_code: str, df: pd.DataFrame, dark_mode: bool = True
- ) -> Optional[plotly.graph_objs.Figure]:
- """
- Plotly 코드를 실행하여 Figure 객체를 생성합니다.
-
- Args:
- plotly_code: 실행할 Plotly 코드
- df: 데이터프레임
- dark_mode: 다크 모드 사용 여부
-
- Returns:
- Optional[plotly.graph_objs.Figure]: 생성된 Figure 객체 또는 None
- """
- ldict = {"df": df, "px": px, "go": go}
- fig = None
-
- try:
- exec(plotly_code, globals(), ldict) # noqa: S102
- fig = ldict.get("fig", None)
-
- except Exception:
- # Inspect data types
- numeric_cols = df.select_dtypes(include=["number"]).columns.tolist()
- categorical_cols = df.select_dtypes(
- include=["object", "category"]
- ).columns.tolist()
-
- # Decision-making for plot type
- if len(numeric_cols) >= 2:
- # Use the first two numeric columns for a scatter plot
- fig = px.scatter(df, x=numeric_cols[0], y=numeric_cols[1])
- elif len(numeric_cols) == 1 and len(categorical_cols) >= 1:
- # Use a bar plot if there's one numeric and one categorical column
- fig = px.bar(df, x=categorical_cols[0], y=numeric_cols[0])
- elif len(categorical_cols) >= 1 and df[categorical_cols[0]].nunique() < 10:
- # Use a pie chart for categorical data with fewer unique values
- fig = px.pie(df, names=categorical_cols[0])
- else:
- # Default to a simple line plot if above conditions are not met
- fig = px.line(df)
-
- if fig is None:
- return None
-
- if dark_mode:
- fig.update_layout(template="plotly_dark")
-
- return fig
diff --git a/uv.lock b/uv.lock
index f793c6a..77d1a8e 100644
--- a/uv.lock
+++ b/uv.lock
@@ -1,54 +1,9 @@
version = 1
revision = 3
-requires-python = ">=3.9"
+requires-python = ">=3.10"
resolution-markers = [
"python_full_version >= '3.13'",
- "python_full_version >= '3.12.4' and python_full_version < '3.13'",
- "python_full_version >= '3.12' and python_full_version < '3.12.4'",
- "python_full_version == '3.11.*'",
- "python_full_version == '3.10.*'",
- "python_full_version < '3.10'",
-]
-
-[[package]]
-name = "acryl-datahub"
-version = "1.2.0.6"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "aiohttp" },
- { name = "avro" },
- { name = "avro-gen3" },
- { name = "cached-property" },
- { name = "click", version = "8.1.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
- { name = "click", version = "8.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
- { name = "click-default-group" },
- { name = "click-spinner" },
- { name = "deprecated" },
- { name = "docker" },
- { name = "expandvars" },
- { name = "humanfriendly" },
- { name = "ijson" },
- { name = "importlib-metadata", marker = "python_full_version < '3.10'" },
- { name = "jsonref" },
- { name = "jsonschema" },
- { name = "mixpanel" },
- { name = "packaging" },
- { name = "progressbar2" },
- { name = "psutil" },
- { name = "pydantic" },
- { name = "python-dateutil" },
- { name = "pyyaml" },
- { name = "requests-file" },
- { name = "ruamel-yaml" },
- { name = "sentry-sdk" },
- { name = "tabulate" },
- { name = "toml" },
- { name = "typing-extensions" },
- { name = "typing-inspect" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/84/e0/9a2257831031331b8d59d9d1c8c875c0abe7e4d8e02c04790533b3d42618/acryl_datahub-1.2.0.6.tar.gz", hash = "sha256:3711434619530c206d5615ce3f50ba44c6d877a4d3dabb5cece4779a08e7e952", size = 1950150, upload-time = "2025-08-19T13:13:33.946Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/00/c9/60c1f32a7b000921f262e0fbbdf9894a9c91944e3bca2316ae2a9ac3aaf1/acryl_datahub-1.2.0.6-py3-none-any.whl", hash = "sha256:0aa1aee0d033c782580d54595fc5824de44a8aed9a45baa3d17755eed9389340", size = 2426885, upload-time = "2025-08-19T13:13:23.567Z" },
+ "python_full_version < '3.13'",
]
[[package]]
@@ -144,23 +99,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/05/6a/ea199e61b67f25ba688d3ce93f63b49b0a4e3b3d380f03971b4646412fc6/aiohttp-3.12.15-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ad702e57dc385cae679c39d318def49aef754455f237499d5b99bea4ef582e51", size = 1710050, upload-time = "2025-07-29T05:51:48.203Z" },
{ url = "https://files.pythonhosted.org/packages/b4/2e/ffeb7f6256b33635c29dbed29a22a723ff2dd7401fff42ea60cf2060abfb/aiohttp-3.12.15-cp313-cp313-win32.whl", hash = "sha256:f813c3e9032331024de2eb2e32a88d86afb69291fbc37a3a3ae81cc9917fb3d0", size = 422647, upload-time = "2025-07-29T05:51:50.718Z" },
{ url = "https://files.pythonhosted.org/packages/1b/8e/78ee35774201f38d5e1ba079c9958f7629b1fd079459aea9467441dbfbf5/aiohttp-3.12.15-cp313-cp313-win_amd64.whl", hash = "sha256:1a649001580bdb37c6fdb1bebbd7e3bc688e8ec2b5c6f52edbb664662b17dc84", size = 449067, upload-time = "2025-07-29T05:51:52.549Z" },
- { url = "https://files.pythonhosted.org/packages/18/8d/da08099af8db234d1cd43163e6ffc8e9313d0e988cee1901610f2fa5c764/aiohttp-3.12.15-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:691d203c2bdf4f4637792efbbcdcd157ae11e55eaeb5e9c360c1206fb03d4d98", size = 706829, upload-time = "2025-07-29T05:51:54.434Z" },
- { url = "https://files.pythonhosted.org/packages/4e/94/8eed385cfb60cf4fdb5b8a165f6148f3bebeb365f08663d83c35a5f273ef/aiohttp-3.12.15-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8e995e1abc4ed2a454c731385bf4082be06f875822adc4c6d9eaadf96e20d406", size = 481806, upload-time = "2025-07-29T05:51:56.355Z" },
- { url = "https://files.pythonhosted.org/packages/38/68/b13e1a34584fbf263151b3a72a084e89f2102afe38df1dce5a05a15b83e9/aiohttp-3.12.15-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:bd44d5936ab3193c617bfd6c9a7d8d1085a8dc8c3f44d5f1dcf554d17d04cf7d", size = 469205, upload-time = "2025-07-29T05:51:58.277Z" },
- { url = "https://files.pythonhosted.org/packages/38/14/3d7348bf53aa4af54416bc64cbef3a2ac5e8b9bfa97cc45f1cf9a94d9c8d/aiohttp-3.12.15-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:46749be6e89cd78d6068cdf7da51dbcfa4321147ab8e4116ee6678d9a056a0cf", size = 1644174, upload-time = "2025-07-29T05:52:00.23Z" },
- { url = "https://files.pythonhosted.org/packages/ba/ed/fd9b5b22b0f6ca1a85c33bb4868cbcc6ae5eae070a0f4c9c5cad003c89d7/aiohttp-3.12.15-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0c643f4d75adea39e92c0f01b3fb83d57abdec8c9279b3078b68a3a52b3933b6", size = 1618672, upload-time = "2025-07-29T05:52:02.272Z" },
- { url = "https://files.pythonhosted.org/packages/39/f7/f6530ab5f8c8c409e44a63fcad35e839c87aabecdfe5b8e96d671ed12f64/aiohttp-3.12.15-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0a23918fedc05806966a2438489dcffccbdf83e921a1170773b6178d04ade142", size = 1692295, upload-time = "2025-07-29T05:52:04.546Z" },
- { url = "https://files.pythonhosted.org/packages/cb/dc/3cf483bb0106566dc97ebaa2bb097f5e44d4bc4ab650a6f107151cd7b193/aiohttp-3.12.15-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:74bdd8c864b36c3673741023343565d95bfbd778ffe1eb4d412c135a28a8dc89", size = 1731609, upload-time = "2025-07-29T05:52:06.552Z" },
- { url = "https://files.pythonhosted.org/packages/de/a4/fd04bf807851197077d9cac9381d58f86d91c95c06cbaf9d3a776ac4467a/aiohttp-3.12.15-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0a146708808c9b7a988a4af3821379e379e0f0e5e466ca31a73dbdd0325b0263", size = 1637852, upload-time = "2025-07-29T05:52:08.975Z" },
- { url = "https://files.pythonhosted.org/packages/98/03/29d626ca3bcdcafbd74b45d77ca42645a5c94d396f2ee3446880ad2405fb/aiohttp-3.12.15-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7011a70b56facde58d6d26da4fec3280cc8e2a78c714c96b7a01a87930a9530", size = 1572852, upload-time = "2025-07-29T05:52:11.508Z" },
- { url = "https://files.pythonhosted.org/packages/5f/cd/b4777a9e204f4e01091091027e5d1e2fa86decd0fee5067bc168e4fa1e76/aiohttp-3.12.15-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:3bdd6e17e16e1dbd3db74d7f989e8af29c4d2e025f9828e6ef45fbdee158ec75", size = 1620813, upload-time = "2025-07-29T05:52:13.891Z" },
- { url = "https://files.pythonhosted.org/packages/ae/26/1a44a6e8417e84057beaf8c462529b9e05d4b53b8605784f1eb571f0ff68/aiohttp-3.12.15-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:57d16590a351dfc914670bd72530fd78344b885a00b250e992faea565b7fdc05", size = 1630951, upload-time = "2025-07-29T05:52:15.955Z" },
- { url = "https://files.pythonhosted.org/packages/dd/7f/10c605dbd01c40e2b27df7ef9004bec75d156f0705141e11047ecdfe264d/aiohttp-3.12.15-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:bc9a0f6569ff990e0bbd75506c8d8fe7214c8f6579cca32f0546e54372a3bb54", size = 1607595, upload-time = "2025-07-29T05:52:18.089Z" },
- { url = "https://files.pythonhosted.org/packages/66/f6/2560dcb01731c1d7df1d34b64de95bc4b3ed02bb78830fd82299c1eb314e/aiohttp-3.12.15-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:536ad7234747a37e50e7b6794ea868833d5220b49c92806ae2d7e8a9d6b5de02", size = 1695194, upload-time = "2025-07-29T05:52:20.255Z" },
- { url = "https://files.pythonhosted.org/packages/e7/02/ee105ae82dc2b981039fd25b0cf6eaa52b493731960f9bc861375a72b463/aiohttp-3.12.15-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:f0adb4177fa748072546fb650d9bd7398caaf0e15b370ed3317280b13f4083b0", size = 1710872, upload-time = "2025-07-29T05:52:22.769Z" },
- { url = "https://files.pythonhosted.org/packages/88/16/70c4e42ed6a04f78fb58d1a46500a6ce560741d13afde2a5f33840746a5f/aiohttp-3.12.15-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:14954a2988feae3987f1eb49c706bff39947605f4b6fa4027c1d75743723eb09", size = 1640539, upload-time = "2025-07-29T05:52:25.733Z" },
- { url = "https://files.pythonhosted.org/packages/fe/1d/a7eb5fa8a6967117c5c0ad5ab4b1dec0d21e178c89aa08bc442a0b836392/aiohttp-3.12.15-cp39-cp39-win32.whl", hash = "sha256:b784d6ed757f27574dca1c336f968f4e81130b27595e458e69457e6878251f5d", size = 430164, upload-time = "2025-07-29T05:52:27.905Z" },
- { url = "https://files.pythonhosted.org/packages/14/25/e0cf8793aedc41c6d7f2aad646a27e27bdacafe3b402bb373d7651c94d73/aiohttp-3.12.15-cp39-cp39-win_amd64.whl", hash = "sha256:86ceded4e78a992f835209e236617bffae649371c4a50d5e5a3987f237db84b8", size = 453370, upload-time = "2025-07-29T05:52:29.936Z" },
]
[[package]]
@@ -176,83 +114,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490, upload-time = "2025-07-03T22:54:42.156Z" },
]
-[[package]]
-name = "altair"
-version = "5.5.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "jinja2" },
- { name = "jsonschema" },
- { name = "narwhals" },
- { name = "packaging" },
- { name = "typing-extensions", marker = "python_full_version < '3.14'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/16/b1/f2969c7bdb8ad8bbdda031687defdce2c19afba2aa2c8e1d2a17f78376d8/altair-5.5.0.tar.gz", hash = "sha256:d960ebe6178c56de3855a68c47b516be38640b73fb3b5111c2a9ca90546dd73d", size = 705305, upload-time = "2024-11-23T23:39:58.542Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/aa/f3/0b6ced594e51cc95d8c1fc1640d3623770d01e4969d29c0bd09945fafefa/altair-5.5.0-py3-none-any.whl", hash = "sha256:91a310b926508d560fe0148d02a194f38b824122641ef528113d029fcd129f8c", size = 731200, upload-time = "2024-11-23T23:39:56.4Z" },
-]
-
-[[package]]
-name = "annotated-types"
-version = "0.7.0"
-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, upload-time = "2024-05-20T21:33:25.928Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" },
-]
-
-[[package]]
-name = "anthropic"
-version = "0.83.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "anyio" },
- { name = "distro" },
- { name = "docstring-parser" },
- { name = "httpx" },
- { name = "jiter" },
- { name = "pydantic" },
- { name = "sniffio" },
- { name = "typing-extensions" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/db/e5/02cd2919ec327b24234abb73082e6ab84c451182cc3cc60681af700f4c63/anthropic-0.83.0.tar.gz", hash = "sha256:a8732c68b41869266c3034541a31a29d8be0f8cd0a714f9edce3128b351eceb4", size = 534058, upload-time = "2026-02-19T19:26:38.904Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/5f/75/b9d58e4e2a4b1fc3e75ffbab978f999baf8b7c4ba9f96e60edb918ba386b/anthropic-0.83.0-py3-none-any.whl", hash = "sha256:f069ef508c73b8f9152e8850830d92bd5ef185645dbacf234bb213344a274810", size = 456991, upload-time = "2026-02-19T19:26:40.114Z" },
-]
-
-[[package]]
-name = "anyio"
-version = "4.10.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "exceptiongroup", marker = "python_full_version < '3.11'" },
- { name = "idna" },
- { name = "sniffio" },
- { name = "typing-extensions", marker = "python_full_version < '3.13'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/f1/b4/636b3b65173d3ce9a38ef5f0522789614e590dab6a8d505340a4efe4c567/anyio-4.10.0.tar.gz", hash = "sha256:3f3fae35c96039744587aa5b8371e7e8e603c0702999535961dd336026973ba6", size = 213252, upload-time = "2025-08-04T08:54:26.451Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/6f/12/e5e0282d673bb9746bacfb6e2dba8719989d3660cdb2ea79aee9a9651afb/anyio-4.10.0-py3-none-any.whl", hash = "sha256:60e474ac86736bbfd6f210f7a61218939c318f43f9972497381f1c5e930ed3d1", size = 107213, upload-time = "2025-08-04T08:54:24.882Z" },
-]
-
-[[package]]
-name = "asn1crypto"
-version = "1.5.1"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/de/cf/d547feed25b5244fcb9392e288ff9fdc3280b10260362fc45d37a798a6ee/asn1crypto-1.5.1.tar.gz", hash = "sha256:13ae38502be632115abf8a24cbe5f4da52e3b5231990aff31123c805306ccb9c", size = 121080, upload-time = "2022-03-15T14:46:52.889Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/c9/7f/09065fd9e27da0eda08b4d6897f1c13535066174cc023af248fc2a8d5e5a/asn1crypto-1.5.1-py2.py3-none-any.whl", hash = "sha256:db4e40728b728508912cbb3d44f19ce188f218e9eba635821bb4b68564f8fd67", size = 105045, upload-time = "2022-03-15T14:46:51.055Z" },
-]
-
-[[package]]
-name = "asttokens"
-version = "3.0.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/4a/e7/82da0a03e7ba5141f05cce0d302e6eed121ae055e0456ca228bf693984bc/asttokens-3.0.0.tar.gz", hash = "sha256:0dcd8baa8d62b0c1d118b399b2ddba3c4aff271d0d7a9e0d4c1681c79035bbc7", size = 61978, upload-time = "2024-11-30T04:30:14.439Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/25/8a/c46dcc25341b5bce5472c718902eb3d38600a903b14fa6aeecef3f21a46f/asttokens-3.0.0-py3-none-any.whl", hash = "sha256:e3078351a059199dd5138cb1c706e6430c05eff2ff136af5eb4790f9d28932e2", size = 26918, upload-time = "2024-11-30T04:30:10.946Z" },
-]
-
[[package]]
name = "async-timeout"
version = "4.0.3"
@@ -262,57 +123,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/a7/fa/e01228c2938de91d47b307831c62ab9e4001e747789d0b05baf779a6488c/async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028", size = 5721, upload-time = "2023-08-10T16:35:55.203Z" },
]
-[[package]]
-name = "asyncpg"
-version = "0.30.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "async-timeout", marker = "python_full_version < '3.11'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/2f/4c/7c991e080e106d854809030d8584e15b2e996e26f16aee6d757e387bc17d/asyncpg-0.30.0.tar.gz", hash = "sha256:c551e9928ab6707602f44811817f82ba3c446e018bfe1d3abecc8ba5f3eac851", size = 957746, upload-time = "2024-10-20T00:30:41.127Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/bb/07/1650a8c30e3a5c625478fa8aafd89a8dd7d85999bf7169b16f54973ebf2c/asyncpg-0.30.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bfb4dd5ae0699bad2b233672c8fc5ccbd9ad24b89afded02341786887e37927e", size = 673143, upload-time = "2024-10-20T00:29:08.846Z" },
- { url = "https://files.pythonhosted.org/packages/a0/9a/568ff9b590d0954553c56806766914c149609b828c426c5118d4869111d3/asyncpg-0.30.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dc1f62c792752a49f88b7e6f774c26077091b44caceb1983509edc18a2222ec0", size = 645035, upload-time = "2024-10-20T00:29:12.02Z" },
- { url = "https://files.pythonhosted.org/packages/de/11/6f2fa6c902f341ca10403743701ea952bca896fc5b07cc1f4705d2bb0593/asyncpg-0.30.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3152fef2e265c9c24eec4ee3d22b4f4d2703d30614b0b6753e9ed4115c8a146f", size = 2912384, upload-time = "2024-10-20T00:29:13.644Z" },
- { url = "https://files.pythonhosted.org/packages/83/83/44bd393919c504ffe4a82d0aed8ea0e55eb1571a1dea6a4922b723f0a03b/asyncpg-0.30.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c7255812ac85099a0e1ffb81b10dc477b9973345793776b128a23e60148dd1af", size = 2947526, upload-time = "2024-10-20T00:29:15.871Z" },
- { url = "https://files.pythonhosted.org/packages/08/85/e23dd3a2b55536eb0ded80c457b0693352262dc70426ef4d4a6fc994fa51/asyncpg-0.30.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:578445f09f45d1ad7abddbff2a3c7f7c291738fdae0abffbeb737d3fc3ab8b75", size = 2895390, upload-time = "2024-10-20T00:29:19.346Z" },
- { url = "https://files.pythonhosted.org/packages/9b/26/fa96c8f4877d47dc6c1864fef5500b446522365da3d3d0ee89a5cce71a3f/asyncpg-0.30.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c42f6bb65a277ce4d93f3fba46b91a265631c8df7250592dd4f11f8b0152150f", size = 3015630, upload-time = "2024-10-20T00:29:21.186Z" },
- { url = "https://files.pythonhosted.org/packages/34/00/814514eb9287614188a5179a8b6e588a3611ca47d41937af0f3a844b1b4b/asyncpg-0.30.0-cp310-cp310-win32.whl", hash = "sha256:aa403147d3e07a267ada2ae34dfc9324e67ccc4cdca35261c8c22792ba2b10cf", size = 568760, upload-time = "2024-10-20T00:29:22.769Z" },
- { url = "https://files.pythonhosted.org/packages/f0/28/869a7a279400f8b06dd237266fdd7220bc5f7c975348fea5d1e6909588e9/asyncpg-0.30.0-cp310-cp310-win_amd64.whl", hash = "sha256:fb622c94db4e13137c4c7f98834185049cc50ee01d8f657ef898b6407c7b9c50", size = 625764, upload-time = "2024-10-20T00:29:25.882Z" },
- { url = "https://files.pythonhosted.org/packages/4c/0e/f5d708add0d0b97446c402db7e8dd4c4183c13edaabe8a8500b411e7b495/asyncpg-0.30.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5e0511ad3dec5f6b4f7a9e063591d407eee66b88c14e2ea636f187da1dcfff6a", size = 674506, upload-time = "2024-10-20T00:29:27.988Z" },
- { url = "https://files.pythonhosted.org/packages/6a/a0/67ec9a75cb24a1d99f97b8437c8d56da40e6f6bd23b04e2f4ea5d5ad82ac/asyncpg-0.30.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:915aeb9f79316b43c3207363af12d0e6fd10776641a7de8a01212afd95bdf0ed", size = 645922, upload-time = "2024-10-20T00:29:29.391Z" },
- { url = "https://files.pythonhosted.org/packages/5c/d9/a7584f24174bd86ff1053b14bb841f9e714380c672f61c906eb01d8ec433/asyncpg-0.30.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c198a00cce9506fcd0bf219a799f38ac7a237745e1d27f0e1f66d3707c84a5a", size = 3079565, upload-time = "2024-10-20T00:29:30.832Z" },
- { url = "https://files.pythonhosted.org/packages/a0/d7/a4c0f9660e333114bdb04d1a9ac70db690dd4ae003f34f691139a5cbdae3/asyncpg-0.30.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3326e6d7381799e9735ca2ec9fd7be4d5fef5dcbc3cb555d8a463d8460607956", size = 3109962, upload-time = "2024-10-20T00:29:33.114Z" },
- { url = "https://files.pythonhosted.org/packages/3c/21/199fd16b5a981b1575923cbb5d9cf916fdc936b377e0423099f209e7e73d/asyncpg-0.30.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:51da377487e249e35bd0859661f6ee2b81db11ad1f4fc036194bc9cb2ead5056", size = 3064791, upload-time = "2024-10-20T00:29:34.677Z" },
- { url = "https://files.pythonhosted.org/packages/77/52/0004809b3427534a0c9139c08c87b515f1c77a8376a50ae29f001e53962f/asyncpg-0.30.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bc6d84136f9c4d24d358f3b02be4b6ba358abd09f80737d1ac7c444f36108454", size = 3188696, upload-time = "2024-10-20T00:29:36.389Z" },
- { url = "https://files.pythonhosted.org/packages/52/cb/fbad941cd466117be58b774a3f1cc9ecc659af625f028b163b1e646a55fe/asyncpg-0.30.0-cp311-cp311-win32.whl", hash = "sha256:574156480df14f64c2d76450a3f3aaaf26105869cad3865041156b38459e935d", size = 567358, upload-time = "2024-10-20T00:29:37.915Z" },
- { url = "https://files.pythonhosted.org/packages/3c/0a/0a32307cf166d50e1ad120d9b81a33a948a1a5463ebfa5a96cc5606c0863/asyncpg-0.30.0-cp311-cp311-win_amd64.whl", hash = "sha256:3356637f0bd830407b5597317b3cb3571387ae52ddc3bca6233682be88bbbc1f", size = 629375, upload-time = "2024-10-20T00:29:39.987Z" },
- { url = "https://files.pythonhosted.org/packages/4b/64/9d3e887bb7b01535fdbc45fbd5f0a8447539833b97ee69ecdbb7a79d0cb4/asyncpg-0.30.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c902a60b52e506d38d7e80e0dd5399f657220f24635fee368117b8b5fce1142e", size = 673162, upload-time = "2024-10-20T00:29:41.88Z" },
- { url = "https://files.pythonhosted.org/packages/6e/eb/8b236663f06984f212a087b3e849731f917ab80f84450e943900e8ca4052/asyncpg-0.30.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:aca1548e43bbb9f0f627a04666fedaca23db0a31a84136ad1f868cb15deb6e3a", size = 637025, upload-time = "2024-10-20T00:29:43.352Z" },
- { url = "https://files.pythonhosted.org/packages/cc/57/2dc240bb263d58786cfaa60920779af6e8d32da63ab9ffc09f8312bd7a14/asyncpg-0.30.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c2a2ef565400234a633da0eafdce27e843836256d40705d83ab7ec42074efb3", size = 3496243, upload-time = "2024-10-20T00:29:44.922Z" },
- { url = "https://files.pythonhosted.org/packages/f4/40/0ae9d061d278b10713ea9021ef6b703ec44698fe32178715a501ac696c6b/asyncpg-0.30.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1292b84ee06ac8a2ad8e51c7475aa309245874b61333d97411aab835c4a2f737", size = 3575059, upload-time = "2024-10-20T00:29:46.891Z" },
- { url = "https://files.pythonhosted.org/packages/c3/75/d6b895a35a2c6506952247640178e5f768eeb28b2e20299b6a6f1d743ba0/asyncpg-0.30.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0f5712350388d0cd0615caec629ad53c81e506b1abaaf8d14c93f54b35e3595a", size = 3473596, upload-time = "2024-10-20T00:29:49.201Z" },
- { url = "https://files.pythonhosted.org/packages/c8/e7/3693392d3e168ab0aebb2d361431375bd22ffc7b4a586a0fc060d519fae7/asyncpg-0.30.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:db9891e2d76e6f425746c5d2da01921e9a16b5a71a1c905b13f30e12a257c4af", size = 3641632, upload-time = "2024-10-20T00:29:50.768Z" },
- { url = "https://files.pythonhosted.org/packages/32/ea/15670cea95745bba3f0352341db55f506a820b21c619ee66b7d12ea7867d/asyncpg-0.30.0-cp312-cp312-win32.whl", hash = "sha256:68d71a1be3d83d0570049cd1654a9bdfe506e794ecc98ad0873304a9f35e411e", size = 560186, upload-time = "2024-10-20T00:29:52.394Z" },
- { url = "https://files.pythonhosted.org/packages/7e/6b/fe1fad5cee79ca5f5c27aed7bd95baee529c1bf8a387435c8ba4fe53d5c1/asyncpg-0.30.0-cp312-cp312-win_amd64.whl", hash = "sha256:9a0292c6af5c500523949155ec17b7fe01a00ace33b68a476d6b5059f9630305", size = 621064, upload-time = "2024-10-20T00:29:53.757Z" },
- { url = "https://files.pythonhosted.org/packages/3a/22/e20602e1218dc07692acf70d5b902be820168d6282e69ef0d3cb920dc36f/asyncpg-0.30.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:05b185ebb8083c8568ea8a40e896d5f7af4b8554b64d7719c0eaa1eb5a5c3a70", size = 670373, upload-time = "2024-10-20T00:29:55.165Z" },
- { url = "https://files.pythonhosted.org/packages/3d/b3/0cf269a9d647852a95c06eb00b815d0b95a4eb4b55aa2d6ba680971733b9/asyncpg-0.30.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c47806b1a8cbb0a0db896f4cd34d89942effe353a5035c62734ab13b9f938da3", size = 634745, upload-time = "2024-10-20T00:29:57.14Z" },
- { url = "https://files.pythonhosted.org/packages/8e/6d/a4f31bf358ce8491d2a31bfe0d7bcf25269e80481e49de4d8616c4295a34/asyncpg-0.30.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b6fde867a74e8c76c71e2f64f80c64c0f3163e687f1763cfaf21633ec24ec33", size = 3512103, upload-time = "2024-10-20T00:29:58.499Z" },
- { url = "https://files.pythonhosted.org/packages/96/19/139227a6e67f407b9c386cb594d9628c6c78c9024f26df87c912fabd4368/asyncpg-0.30.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46973045b567972128a27d40001124fbc821c87a6cade040cfcd4fa8a30bcdc4", size = 3592471, upload-time = "2024-10-20T00:30:00.354Z" },
- { url = "https://files.pythonhosted.org/packages/67/e4/ab3ca38f628f53f0fd28d3ff20edff1c975dd1cb22482e0061916b4b9a74/asyncpg-0.30.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9110df111cabc2ed81aad2f35394a00cadf4f2e0635603db6ebbd0fc896f46a4", size = 3496253, upload-time = "2024-10-20T00:30:02.794Z" },
- { url = "https://files.pythonhosted.org/packages/ef/5f/0bf65511d4eeac3a1f41c54034a492515a707c6edbc642174ae79034d3ba/asyncpg-0.30.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:04ff0785ae7eed6cc138e73fc67b8e51d54ee7a3ce9b63666ce55a0bf095f7ba", size = 3662720, upload-time = "2024-10-20T00:30:04.501Z" },
- { url = "https://files.pythonhosted.org/packages/e7/31/1513d5a6412b98052c3ed9158d783b1e09d0910f51fbe0e05f56cc370bc4/asyncpg-0.30.0-cp313-cp313-win32.whl", hash = "sha256:ae374585f51c2b444510cdf3595b97ece4f233fde739aa14b50e0d64e8a7a590", size = 560404, upload-time = "2024-10-20T00:30:06.537Z" },
- { url = "https://files.pythonhosted.org/packages/c8/a4/cec76b3389c4c5ff66301cd100fe88c318563ec8a520e0b2e792b5b84972/asyncpg-0.30.0-cp313-cp313-win_amd64.whl", hash = "sha256:f59b430b8e27557c3fb9869222559f7417ced18688375825f8f12302c34e915e", size = 621623, upload-time = "2024-10-20T00:30:09.024Z" },
- { url = "https://files.pythonhosted.org/packages/b4/82/d94f3ed6921136a0ef40a825740eda19437ccdad7d92d924302dca1d5c9e/asyncpg-0.30.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6f4e83f067b35ab5e6371f8a4c93296e0439857b4569850b178a01385e82e9ad", size = 673026, upload-time = "2024-10-20T00:30:26.928Z" },
- { url = "https://files.pythonhosted.org/packages/4e/db/7db8b73c5d86ec9a21807f405e0698f8f637a8a3ca14b7b6fd4259b66bcf/asyncpg-0.30.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5df69d55add4efcd25ea2a3b02025b669a285b767bfbf06e356d68dbce4234ff", size = 644732, upload-time = "2024-10-20T00:30:28.393Z" },
- { url = "https://files.pythonhosted.org/packages/eb/a0/1f1910659d08050cb3e8f7d82b32983974798d7fd4ddf7620b8e2023d4ac/asyncpg-0.30.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3479a0d9a852c7c84e822c073622baca862d1217b10a02dd57ee4a7a081f708", size = 2911761, upload-time = "2024-10-20T00:30:30.569Z" },
- { url = "https://files.pythonhosted.org/packages/4d/53/5aa0d92488ded50bab2b6626430ed9743b0b7e2d864a2b435af1ccbf219a/asyncpg-0.30.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26683d3b9a62836fad771a18ecf4659a30f348a561279d6227dab96182f46144", size = 2946595, upload-time = "2024-10-20T00:30:32.244Z" },
- { url = "https://files.pythonhosted.org/packages/c5/cd/d6d548d8ee721f4e0f7fbbe509bbac140d556c2e45814d945540c96cf7d4/asyncpg-0.30.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1b982daf2441a0ed314bd10817f1606f1c28b1136abd9e4f11335358c2c631cb", size = 2890135, upload-time = "2024-10-20T00:30:33.817Z" },
- { url = "https://files.pythonhosted.org/packages/46/f0/28df398b685dabee20235e24880e1f6486d84ae7e6b0d11bdebc17740e7a/asyncpg-0.30.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1c06a3a50d014b303e5f6fc1e5f95eb28d2cee89cf58384b700da621e5d5e547", size = 3011889, upload-time = "2024-10-20T00:30:35.378Z" },
- { url = "https://files.pythonhosted.org/packages/c8/07/8c7ffe6fe8bccff9b12fcb6410b1b2fa74b917fd8b837806a40217d5228b/asyncpg-0.30.0-cp39-cp39-win32.whl", hash = "sha256:1b11a555a198b08f5c4baa8f8231c74a366d190755aa4f99aacec5970afe929a", size = 569406, upload-time = "2024-10-20T00:30:37.644Z" },
- { url = "https://files.pythonhosted.org/packages/05/51/f59e4df6d9b8937530d4b9fdee1598b93db40c631fe94ff3ce64207b7a95/asyncpg-0.30.0-cp39-cp39-win_amd64.whl", hash = "sha256:8b684a3c858a83cd876f05958823b68e8d14ec01bb0c0d14a6704c5bf9711773", size = 626581, upload-time = "2024-10-20T00:30:39.69Z" },
-]
-
[[package]]
name = "attrs"
version = "25.3.0"
@@ -323,90 +133,68 @@ wheels = [
]
[[package]]
-name = "avro"
-version = "1.12.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/e6/73/48668732bbc8ae1e79b237f84e761204c8dd266c5e16e7601000aba471d3/avro-1.12.0.tar.gz", hash = "sha256:cad9c53b23ceed699c7af6bddced42e2c572fd6b408c257a7d4fc4e8cf2e2d6b", size = 91025, upload-time = "2024-08-05T12:12:57.926Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/a8/f9/6c55a90a834594b1c4c6184e8d1b97fa881af84be8e6f4b3ebb2e9d8da19/avro-1.12.0-py2.py3-none-any.whl", hash = "sha256:9a255c72e1837341dd4f6ff57b2b6f68c0f0cecdef62dd04962e10fd33bec05b", size = 124227, upload-time = "2024-08-05T12:12:56.329Z" },
-]
-
-[[package]]
-name = "avro-gen3"
-version = "0.7.16"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "avro" },
- { name = "six" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/ba/d2/58133bc977971a846ea2cb89fd4ae27fc71e05efd45c64a5c2996c597491/avro_gen3-0.7.16.tar.gz", hash = "sha256:1ef593e22d8876ec55b91aa75cb0581a4526bae4bb990fde7892208679dc44dc", size = 24057, upload-time = "2024-09-05T23:57:52.937Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/fd/11/966375cc964f8220f7bee5ac56a6ca81c636e5180d2454396ae5c452e0e2/avro_gen3-0.7.16-py3-none-any.whl", hash = "sha256:9f26de26214a8730d5e7d86b4a2c4afe8bedfaac5b770beb122cd0fa5fea60f8", size = 27604, upload-time = "2024-09-05T23:57:51.33Z" },
-]
-
-[[package]]
-name = "blinker"
-version = "1.9.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/21/28/9b3f50ce0e048515135495f198351908d99540d69bfdc8c1d15b73dc55ce/blinker-1.9.0.tar.gz", hash = "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf", size = 22460, upload-time = "2024-11-08T17:25:47.436Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/10/cb/f2ad4230dc2eb1a74edf38f1a38b9b52277f75bef262d8908e60d957e13c/blinker-1.9.0-py3-none-any.whl", hash = "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc", size = 8458, upload-time = "2024-11-08T17:25:46.184Z" },
-]
-
-[[package]]
-name = "boto3"
-version = "1.40.21"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "botocore" },
- { name = "jmespath" },
- { name = "s3transfer" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/d8/54/5ba3f69a892ff486f5925008da21618665cf321880f279e9605399d9cec3/boto3-1.40.21.tar.gz", hash = "sha256:876ccc0b25517b992bd27976282510773a11ebc771aa5b836a238ea426c82187", size = 111590, upload-time = "2025-08-29T19:20:57.901Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/86/76/48b982bb504ffbff8eb5522df8c144b98cdc38d574b3c55db1d82b5c0c7f/boto3-1.40.21-py3-none-any.whl", hash = "sha256:3772fb828864d3b7046c8bdf2f4860aaca4a79f25b7b060206c6a5f4944ea7f9", size = 139322, upload-time = "2025-08-29T19:20:55.888Z" },
-]
-
-[[package]]
-name = "botocore"
-version = "1.40.21"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "jmespath" },
- { name = "python-dateutil" },
- { name = "urllib3", version = "1.26.20", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
- { name = "urllib3", version = "2.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/50/11/d9a500a0e86b74017854e3ff12fd943f74f4358337799e0b272eaa6b4e27/botocore-1.40.21.tar.gz", hash = "sha256:f77e9c199df0252b14ea739a9ac99723940f6bde90f4c2e7802701553a62827b", size = 14321194, upload-time = "2025-08-29T19:20:46.892Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/df/6a/effb671afa31d35805d0760b45676136fd1209e263641861456b4566ae9b/botocore-1.40.21-py3-none-any.whl", hash = "sha256:574ecf9b68c1721650024a27e00e0080b6f141c281ebfce49e0d302969270ef4", size = 13993859, upload-time = "2025-08-29T19:20:41.404Z" },
-]
-
-[[package]]
-name = "cached-property"
-version = "2.0.1"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/76/4b/3d870836119dbe9a5e3c9a61af8cc1a8b69d75aea564572e385882d5aefb/cached_property-2.0.1.tar.gz", hash = "sha256:484d617105e3ee0e4f1f58725e72a8ef9e93deee462222dbd51cd91230897641", size = 10574, upload-time = "2024-10-25T15:43:55.667Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/11/0e/7d8225aab3bc1a0f5811f8e1b557aa034ac04bdf641925b30d3caf586b28/cached_property-2.0.1-py3-none-any.whl", hash = "sha256:f617d70ab1100b7bcf6e42228f9ddcb78c676ffa167278d9f730d1c2fba69ccb", size = 7428, upload-time = "2024-10-25T15:43:54.711Z" },
-]
-
-[[package]]
-name = "cachetools"
-version = "5.5.2"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/6c/81/3747dad6b14fa2cf53fcf10548cf5aea6913e96fab41a3c198676f8948a5/cachetools-5.5.2.tar.gz", hash = "sha256:1a661caa9175d26759571b2e19580f9d6393969e5dfca11fdb1f947a23e640d4", size = 28380, upload-time = "2025-02-20T21:01:19.524Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/72/76/20fa66124dbe6be5cafeb312ece67de6b61dd91a0247d1ea13db4ebb33c2/cachetools-5.5.2-py3-none-any.whl", hash = "sha256:d26a22bcc62eb95c3beabd9f1ee5e820d3d2704fe2967cbe350e20c8ffcd3f0a", size = 10080, upload-time = "2025-02-20T21:01:16.647Z" },
-]
-
-[[package]]
-name = "certifi"
-version = "2025.8.3"
+name = "audioop-lts"
+version = "0.2.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/38/53/946db57842a50b2da2e0c1e34bd37f36f5aadba1a929a3971c5d7841dbca/audioop_lts-0.2.2.tar.gz", hash = "sha256:64d0c62d88e67b98a1a5e71987b7aa7b5bcffc7dcee65b635823dbdd0a8dbbd0", size = 30686, upload-time = "2025-08-05T16:43:17.409Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/de/d4/94d277ca941de5a507b07f0b592f199c22454eeaec8f008a286b3fbbacd6/audioop_lts-0.2.2-cp313-abi3-macosx_10_13_universal2.whl", hash = "sha256:fd3d4602dc64914d462924a08c1a9816435a2155d74f325853c1f1ac3b2d9800", size = 46523, upload-time = "2025-08-05T16:42:20.836Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/5a/656d1c2da4b555920ce4177167bfeb8623d98765594af59702c8873f60ec/audioop_lts-0.2.2-cp313-abi3-macosx_10_13_x86_64.whl", hash = "sha256:550c114a8df0aafe9a05442a1162dfc8fec37e9af1d625ae6060fed6e756f303", size = 27455, upload-time = "2025-08-05T16:42:22.283Z" },
+ { url = "https://files.pythonhosted.org/packages/1b/83/ea581e364ce7b0d41456fb79d6ee0ad482beda61faf0cab20cbd4c63a541/audioop_lts-0.2.2-cp313-abi3-macosx_11_0_arm64.whl", hash = "sha256:9a13dc409f2564de15dd68be65b462ba0dde01b19663720c68c1140c782d1d75", size = 26997, upload-time = "2025-08-05T16:42:23.849Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/3b/e8964210b5e216e5041593b7d33e97ee65967f17c282e8510d19c666dab4/audioop_lts-0.2.2-cp313-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:51c916108c56aa6e426ce611946f901badac950ee2ddaf302b7ed35d9958970d", size = 85844, upload-time = "2025-08-05T16:42:25.208Z" },
+ { url = "https://files.pythonhosted.org/packages/c7/2e/0a1c52faf10d51def20531a59ce4c706cb7952323b11709e10de324d6493/audioop_lts-0.2.2-cp313-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:47eba38322370347b1c47024defbd36374a211e8dd5b0dcbce7b34fdb6f8847b", size = 85056, upload-time = "2025-08-05T16:42:26.559Z" },
+ { url = "https://files.pythonhosted.org/packages/75/e8/cd95eef479656cb75ab05dfece8c1f8c395d17a7c651d88f8e6e291a63ab/audioop_lts-0.2.2-cp313-abi3-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ba7c3a7e5f23e215cb271516197030c32aef2e754252c4c70a50aaff7031a2c8", size = 93892, upload-time = "2025-08-05T16:42:27.902Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/1e/a0c42570b74f83efa5cca34905b3eef03f7ab09fe5637015df538a7f3345/audioop_lts-0.2.2-cp313-abi3-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:def246fe9e180626731b26e89816e79aae2276f825420a07b4a647abaa84becc", size = 96660, upload-time = "2025-08-05T16:42:28.9Z" },
+ { url = "https://files.pythonhosted.org/packages/50/d5/8a0ae607ca07dbb34027bac8db805498ee7bfecc05fd2c148cc1ed7646e7/audioop_lts-0.2.2-cp313-abi3-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e160bf9df356d841bb6c180eeeea1834085464626dc1b68fa4e1d59070affdc3", size = 79143, upload-time = "2025-08-05T16:42:29.929Z" },
+ { url = "https://files.pythonhosted.org/packages/12/17/0d28c46179e7910bfb0bb62760ccb33edb5de973052cb2230b662c14ca2e/audioop_lts-0.2.2-cp313-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:4b4cd51a57b698b2d06cb9993b7ac8dfe89a3b2878e96bc7948e9f19ff51dba6", size = 84313, upload-time = "2025-08-05T16:42:30.949Z" },
+ { url = "https://files.pythonhosted.org/packages/84/ba/bd5d3806641564f2024e97ca98ea8f8811d4e01d9b9f9831474bc9e14f9e/audioop_lts-0.2.2-cp313-abi3-musllinux_1_2_ppc64le.whl", hash = "sha256:4a53aa7c16a60a6857e6b0b165261436396ef7293f8b5c9c828a3a203147ed4a", size = 93044, upload-time = "2025-08-05T16:42:31.959Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/5e/435ce8d5642f1f7679540d1e73c1c42d933331c0976eb397d1717d7f01a3/audioop_lts-0.2.2-cp313-abi3-musllinux_1_2_riscv64.whl", hash = "sha256:3fc38008969796f0f689f1453722a0f463da1b8a6fbee11987830bfbb664f623", size = 78766, upload-time = "2025-08-05T16:42:33.302Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/3b/b909e76b606cbfd53875693ec8c156e93e15a1366a012f0b7e4fb52d3c34/audioop_lts-0.2.2-cp313-abi3-musllinux_1_2_s390x.whl", hash = "sha256:15ab25dd3e620790f40e9ead897f91e79c0d3ce65fe193c8ed6c26cffdd24be7", size = 87640, upload-time = "2025-08-05T16:42:34.854Z" },
+ { url = "https://files.pythonhosted.org/packages/30/e7/8f1603b4572d79b775f2140d7952f200f5e6c62904585d08a01f0a70393a/audioop_lts-0.2.2-cp313-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:03f061a1915538fd96272bac9551841859dbb2e3bf73ebe4a23ef043766f5449", size = 86052, upload-time = "2025-08-05T16:42:35.839Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/96/c37846df657ccdda62ba1ae2b6534fa90e2e1b1742ca8dcf8ebd38c53801/audioop_lts-0.2.2-cp313-abi3-win32.whl", hash = "sha256:3bcddaaf6cc5935a300a8387c99f7a7fbbe212a11568ec6cf6e4bc458c048636", size = 26185, upload-time = "2025-08-05T16:42:37.04Z" },
+ { url = "https://files.pythonhosted.org/packages/34/a5/9d78fdb5b844a83da8a71226c7bdae7cc638861085fff7a1d707cb4823fa/audioop_lts-0.2.2-cp313-abi3-win_amd64.whl", hash = "sha256:a2c2a947fae7d1062ef08c4e369e0ba2086049a5e598fda41122535557012e9e", size = 30503, upload-time = "2025-08-05T16:42:38.427Z" },
+ { url = "https://files.pythonhosted.org/packages/34/25/20d8fde083123e90c61b51afb547bb0ea7e77bab50d98c0ab243d02a0e43/audioop_lts-0.2.2-cp313-abi3-win_arm64.whl", hash = "sha256:5f93a5db13927a37d2d09637ccca4b2b6b48c19cd9eda7b17a2e9f77edee6a6f", size = 24173, upload-time = "2025-08-05T16:42:39.704Z" },
+ { url = "https://files.pythonhosted.org/packages/58/a7/0a764f77b5c4ac58dc13c01a580f5d32ae8c74c92020b961556a43e26d02/audioop_lts-0.2.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:73f80bf4cd5d2ca7814da30a120de1f9408ee0619cc75da87d0641273d202a09", size = 47096, upload-time = "2025-08-05T16:42:40.684Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/ed/ebebedde1a18848b085ad0fa54b66ceb95f1f94a3fc04f1cd1b5ccb0ed42/audioop_lts-0.2.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:106753a83a25ee4d6f473f2be6b0966fc1c9af7e0017192f5531a3e7463dce58", size = 27748, upload-time = "2025-08-05T16:42:41.992Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/6e/11ca8c21af79f15dbb1c7f8017952ee8c810c438ce4e2b25638dfef2b02c/audioop_lts-0.2.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:fbdd522624141e40948ab3e8cdae6e04c748d78710e9f0f8d4dae2750831de19", size = 27329, upload-time = "2025-08-05T16:42:42.987Z" },
+ { url = "https://files.pythonhosted.org/packages/84/52/0022f93d56d85eec5da6b9da6a958a1ef09e80c39f2cc0a590c6af81dcbb/audioop_lts-0.2.2-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:143fad0311e8209ece30a8dbddab3b65ab419cbe8c0dde6e8828da25999be911", size = 92407, upload-time = "2025-08-05T16:42:44.336Z" },
+ { url = "https://files.pythonhosted.org/packages/87/1d/48a889855e67be8718adbc7a01f3c01d5743c325453a5e81cf3717664aad/audioop_lts-0.2.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dfbbc74ec68a0fd08cfec1f4b5e8cca3d3cd7de5501b01c4b5d209995033cde9", size = 91811, upload-time = "2025-08-05T16:42:45.325Z" },
+ { url = "https://files.pythonhosted.org/packages/98/a6/94b7213190e8077547ffae75e13ed05edc488653c85aa5c41472c297d295/audioop_lts-0.2.2-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cfcac6aa6f42397471e4943e0feb2244549db5c5d01efcd02725b96af417f3fe", size = 100470, upload-time = "2025-08-05T16:42:46.468Z" },
+ { url = "https://files.pythonhosted.org/packages/e9/e9/78450d7cb921ede0cfc33426d3a8023a3bda755883c95c868ee36db8d48d/audioop_lts-0.2.2-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:752d76472d9804ac60f0078c79cdae8b956f293177acd2316cd1e15149aee132", size = 103878, upload-time = "2025-08-05T16:42:47.576Z" },
+ { url = "https://files.pythonhosted.org/packages/4f/e2/cd5439aad4f3e34ae1ee852025dc6aa8f67a82b97641e390bf7bd9891d3e/audioop_lts-0.2.2-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:83c381767e2cc10e93e40281a04852facc4cd9334550e0f392f72d1c0a9c5753", size = 84867, upload-time = "2025-08-05T16:42:49.003Z" },
+ { url = "https://files.pythonhosted.org/packages/68/4b/9d853e9076c43ebba0d411e8d2aa19061083349ac695a7d082540bad64d0/audioop_lts-0.2.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c0022283e9556e0f3643b7c3c03f05063ca72b3063291834cca43234f20c60bb", size = 90001, upload-time = "2025-08-05T16:42:50.038Z" },
+ { url = "https://files.pythonhosted.org/packages/58/26/4bae7f9d2f116ed5593989d0e521d679b0d583973d203384679323d8fa85/audioop_lts-0.2.2-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:a2d4f1513d63c795e82948e1305f31a6d530626e5f9f2605408b300ae6095093", size = 99046, upload-time = "2025-08-05T16:42:51.111Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/67/a9f4fb3e250dda9e9046f8866e9fa7d52664f8985e445c6b4ad6dfb55641/audioop_lts-0.2.2-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:c9c8e68d8b4a56fda8c025e538e639f8c5953f5073886b596c93ec9b620055e7", size = 84788, upload-time = "2025-08-05T16:42:52.198Z" },
+ { url = "https://files.pythonhosted.org/packages/70/f7/3de86562db0121956148bcb0fe5b506615e3bcf6e63c4357a612b910765a/audioop_lts-0.2.2-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:96f19de485a2925314f5020e85911fb447ff5fbef56e8c7c6927851b95533a1c", size = 94472, upload-time = "2025-08-05T16:42:53.59Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/32/fd772bf9078ae1001207d2df1eef3da05bea611a87dd0e8217989b2848fa/audioop_lts-0.2.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:e541c3ef484852ef36545f66209444c48b28661e864ccadb29daddb6a4b8e5f5", size = 92279, upload-time = "2025-08-05T16:42:54.632Z" },
+ { url = "https://files.pythonhosted.org/packages/4f/41/affea7181592ab0ab560044632571a38edaf9130b84928177823fbf3176a/audioop_lts-0.2.2-cp313-cp313t-win32.whl", hash = "sha256:d5e73fa573e273e4f2e5ff96f9043858a5e9311e94ffefd88a3186a910c70917", size = 26568, upload-time = "2025-08-05T16:42:55.627Z" },
+ { url = "https://files.pythonhosted.org/packages/28/2b/0372842877016641db8fc54d5c88596b542eec2f8f6c20a36fb6612bf9ee/audioop_lts-0.2.2-cp313-cp313t-win_amd64.whl", hash = "sha256:9191d68659eda01e448188f60364c7763a7ca6653ed3f87ebb165822153a8547", size = 30942, upload-time = "2025-08-05T16:42:56.674Z" },
+ { url = "https://files.pythonhosted.org/packages/ee/ca/baf2b9cc7e96c179bb4a54f30fcd83e6ecb340031bde68f486403f943768/audioop_lts-0.2.2-cp313-cp313t-win_arm64.whl", hash = "sha256:c174e322bb5783c099aaf87faeb240c8d210686b04bd61dfd05a8e5a83d88969", size = 24603, upload-time = "2025-08-05T16:42:57.571Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/73/413b5a2804091e2c7d5def1d618e4837f1cb82464e230f827226278556b7/audioop_lts-0.2.2-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:f9ee9b52f5f857fbaf9d605a360884f034c92c1c23021fb90b2e39b8e64bede6", size = 47104, upload-time = "2025-08-05T16:42:58.518Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/8c/daa3308dc6593944410c2c68306a5e217f5c05b70a12e70228e7dd42dc5c/audioop_lts-0.2.2-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:49ee1a41738a23e98d98b937a0638357a2477bc99e61b0f768a8f654f45d9b7a", size = 27754, upload-time = "2025-08-05T16:43:00.132Z" },
+ { url = "https://files.pythonhosted.org/packages/4e/86/c2e0f627168fcf61781a8f72cab06b228fe1da4b9fa4ab39cfb791b5836b/audioop_lts-0.2.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5b00be98ccd0fc123dcfad31d50030d25fcf31488cde9e61692029cd7394733b", size = 27332, upload-time = "2025-08-05T16:43:01.666Z" },
+ { url = "https://files.pythonhosted.org/packages/c7/bd/35dce665255434f54e5307de39e31912a6f902d4572da7c37582809de14f/audioop_lts-0.2.2-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a6d2e0f9f7a69403e388894d4ca5ada5c47230716a03f2847cfc7bd1ecb589d6", size = 92396, upload-time = "2025-08-05T16:43:02.991Z" },
+ { url = "https://files.pythonhosted.org/packages/2d/d2/deeb9f51def1437b3afa35aeb729d577c04bcd89394cb56f9239a9f50b6f/audioop_lts-0.2.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f9b0b8a03ef474f56d1a842af1a2e01398b8f7654009823c6d9e0ecff4d5cfbf", size = 91811, upload-time = "2025-08-05T16:43:04.096Z" },
+ { url = "https://files.pythonhosted.org/packages/76/3b/09f8b35b227cee28cc8231e296a82759ed80c1a08e349811d69773c48426/audioop_lts-0.2.2-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2b267b70747d82125f1a021506565bdc5609a2b24bcb4773c16d79d2bb260bbd", size = 100483, upload-time = "2025-08-05T16:43:05.085Z" },
+ { url = "https://files.pythonhosted.org/packages/0b/15/05b48a935cf3b130c248bfdbdea71ce6437f5394ee8533e0edd7cfd93d5e/audioop_lts-0.2.2-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0337d658f9b81f4cd0fdb1f47635070cc084871a3d4646d9de74fdf4e7c3d24a", size = 103885, upload-time = "2025-08-05T16:43:06.197Z" },
+ { url = "https://files.pythonhosted.org/packages/83/80/186b7fce6d35b68d3d739f228dc31d60b3412105854edb975aa155a58339/audioop_lts-0.2.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:167d3b62586faef8b6b2275c3218796b12621a60e43f7e9d5845d627b9c9b80e", size = 84899, upload-time = "2025-08-05T16:43:07.291Z" },
+ { url = "https://files.pythonhosted.org/packages/49/89/c78cc5ac6cb5828f17514fb12966e299c850bc885e80f8ad94e38d450886/audioop_lts-0.2.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:0d9385e96f9f6da847f4d571ce3cb15b5091140edf3db97276872647ce37efd7", size = 89998, upload-time = "2025-08-05T16:43:08.335Z" },
+ { url = "https://files.pythonhosted.org/packages/4c/4b/6401888d0c010e586c2ca50fce4c903d70a6bb55928b16cfbdfd957a13da/audioop_lts-0.2.2-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:48159d96962674eccdca9a3df280e864e8ac75e40a577cc97c5c42667ffabfc5", size = 99046, upload-time = "2025-08-05T16:43:09.367Z" },
+ { url = "https://files.pythonhosted.org/packages/de/f8/c874ca9bb447dae0e2ef2e231f6c4c2b0c39e31ae684d2420b0f9e97ee68/audioop_lts-0.2.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:8fefe5868cd082db1186f2837d64cfbfa78b548ea0d0543e9b28935ccce81ce9", size = 84843, upload-time = "2025-08-05T16:43:10.749Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/c0/0323e66f3daebc13fd46b36b30c3be47e3fc4257eae44f1e77eb828c703f/audioop_lts-0.2.2-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:58cf54380c3884fb49fdd37dfb7a772632b6701d28edd3e2904743c5e1773602", size = 94490, upload-time = "2025-08-05T16:43:12.131Z" },
+ { url = "https://files.pythonhosted.org/packages/98/6b/acc7734ac02d95ab791c10c3f17ffa3584ccb9ac5c18fd771c638ed6d1f5/audioop_lts-0.2.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:088327f00488cdeed296edd9215ca159f3a5a5034741465789cad403fcf4bec0", size = 92297, upload-time = "2025-08-05T16:43:13.139Z" },
+ { url = "https://files.pythonhosted.org/packages/13/c3/c3dc3f564ce6877ecd2a05f8d751b9b27a8c320c2533a98b0c86349778d0/audioop_lts-0.2.2-cp314-cp314t-win32.whl", hash = "sha256:068aa17a38b4e0e7de771c62c60bbca2455924b67a8814f3b0dee92b5820c0b3", size = 27331, upload-time = "2025-08-05T16:43:14.19Z" },
+ { url = "https://files.pythonhosted.org/packages/72/bb/b4608537e9ffcb86449091939d52d24a055216a36a8bf66b936af8c3e7ac/audioop_lts-0.2.2-cp314-cp314t-win_amd64.whl", hash = "sha256:a5bf613e96f49712073de86f20dbdd4014ca18efd4d34ed18c75bd808337851b", size = 31697, upload-time = "2025-08-05T16:43:15.193Z" },
+ { url = "https://files.pythonhosted.org/packages/f6/22/91616fe707a5c5510de2cac9b046a30defe7007ba8a0c04f9c08f27df312/audioop_lts-0.2.2-cp314-cp314t-win_arm64.whl", hash = "sha256:b492c3b040153e68b9fdaff5913305aaaba5bb433d8a7f73d5cf6a64ed3cc1dd", size = 25206, upload-time = "2025-08-05T16:43:16.444Z" },
+]
+
+[[package]]
+name = "backports-asyncio-runner"
+version = "1.2.0"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/dc/67/960ebe6bf230a96cda2e0abcf73af550ec4f090005363542f0765df162e0/certifi-2025.8.3.tar.gz", hash = "sha256:e564105f78ded564e3ae7c923924435e1daa7463faeab5bb932bc53ffae63407", size = 162386, upload-time = "2025-08-03T03:07:47.08Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/8e/ff/70dca7d7cb1cbc0edb2c6cc0c38b65cba36cccc491eca64cabd5fe7f8670/backports_asyncio_runner-1.2.0.tar.gz", hash = "sha256:a5aa7b2b7d8f8bfcaa2b57313f70792df84e32a2a746f585213373f900b42162", size = 69893, upload-time = "2025-07-02T02:27:15.685Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/e5/48/1549795ba7742c948d2ad169c1c8cdbae65bc450d6cd753d124b17c8cd32/certifi-2025.8.3-py3-none-any.whl", hash = "sha256:f6c12493cfb1b06ba2ff328595af9350c65d6644968e5d3a2ffd78699af217a5", size = 161216, upload-time = "2025-08-03T03:07:45.777Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/59/76ab57e3fe74484f48a53f8e337171b4a2349e506eabe136d7e01d059086/backports_asyncio_runner-1.2.0-py3-none-any.whl", hash = "sha256:0da0a936a8aeb554eccb426dc55af3ba63bcdc69fa1a600b5bb305413a4477b5", size = 12313, upload-time = "2025-07-02T02:27:14.263Z" },
]
[[package]]
@@ -464,249 +252,6 @@ wheels = [
{ 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, upload-time = "2024-09-04T20:44:41.616Z" },
{ url = "https://files.pythonhosted.org/packages/bf/ee/f94057fa6426481d663b88637a9a10e859e492c73d0384514a17d78ee205/cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d", size = 172475, upload-time = "2024-09-04T20:44:43.733Z" },
{ url = "https://files.pythonhosted.org/packages/7c/fc/6a8cb64e5f0324877d503c854da15d76c1e50eb722e320b15345c4d0c6de/cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a", size = 182009, upload-time = "2024-09-04T20:44:45.309Z" },
- { url = "https://files.pythonhosted.org/packages/b9/ea/8bb50596b8ffbc49ddd7a1ad305035daa770202a6b782fc164647c2673ad/cffi-1.17.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b2ab587605f4ba0bf81dc0cb08a41bd1c0a5906bd59243d56bad7668a6fc6c16", size = 182220, upload-time = "2024-09-04T20:45:01.577Z" },
- { url = "https://files.pythonhosted.org/packages/ae/11/e77c8cd24f58285a82c23af484cf5b124a376b32644e445960d1a4654c3a/cffi-1.17.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:28b16024becceed8c6dfbc75629e27788d8a3f9030691a1dbf9821a128b22c36", size = 178605, upload-time = "2024-09-04T20:45:03.837Z" },
- { url = "https://files.pythonhosted.org/packages/ed/65/25a8dc32c53bf5b7b6c2686b42ae2ad58743f7ff644844af7cdb29b49361/cffi-1.17.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d599671f396c4723d016dbddb72fe8e0397082b0a77a4fab8028923bec050e8", size = 424910, upload-time = "2024-09-04T20:45:05.315Z" },
- { url = "https://files.pythonhosted.org/packages/42/7a/9d086fab7c66bd7c4d0f27c57a1b6b068ced810afc498cc8c49e0088661c/cffi-1.17.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca74b8dbe6e8e8263c0ffd60277de77dcee6c837a3d0881d8c1ead7268c9e576", size = 447200, upload-time = "2024-09-04T20:45:06.903Z" },
- { url = "https://files.pythonhosted.org/packages/da/63/1785ced118ce92a993b0ec9e0d0ac8dc3e5dbfbcaa81135be56c69cabbb6/cffi-1.17.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87", size = 454565, upload-time = "2024-09-04T20:45:08.975Z" },
- { url = "https://files.pythonhosted.org/packages/74/06/90b8a44abf3556599cdec107f7290277ae8901a58f75e6fe8f970cd72418/cffi-1.17.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98e3969bcff97cae1b2def8ba499ea3d6f31ddfdb7635374834cf89a1a08ecf0", size = 435635, upload-time = "2024-09-04T20:45:10.64Z" },
- { url = "https://files.pythonhosted.org/packages/bd/62/a1f468e5708a70b1d86ead5bab5520861d9c7eacce4a885ded9faa7729c3/cffi-1.17.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdf5ce3acdfd1661132f2a9c19cac174758dc2352bfe37d98aa7512c6b7178b3", size = 445218, upload-time = "2024-09-04T20:45:12.366Z" },
- { url = "https://files.pythonhosted.org/packages/5b/95/b34462f3ccb09c2594aa782d90a90b045de4ff1f70148ee79c69d37a0a5a/cffi-1.17.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9755e4345d1ec879e3849e62222a18c7174d65a6a92d5b346b1863912168b595", size = 460486, upload-time = "2024-09-04T20:45:13.935Z" },
- { url = "https://files.pythonhosted.org/packages/fc/fc/a1e4bebd8d680febd29cf6c8a40067182b64f00c7d105f8f26b5bc54317b/cffi-1.17.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f1e22e8c4419538cb197e4dd60acc919d7696e5ef98ee4da4e01d3f8cfa4cc5a", size = 437911, upload-time = "2024-09-04T20:45:15.696Z" },
- { url = "https://files.pythonhosted.org/packages/e6/c3/21cab7a6154b6a5ea330ae80de386e7665254835b9e98ecc1340b3a7de9a/cffi-1.17.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c03e868a0b3bc35839ba98e74211ed2b05d2119be4e8a0f224fba9384f1fe02e", size = 460632, upload-time = "2024-09-04T20:45:17.284Z" },
- { url = "https://files.pythonhosted.org/packages/cb/b5/fd9f8b5a84010ca169ee49f4e4ad6f8c05f4e3545b72ee041dbbcb159882/cffi-1.17.1-cp39-cp39-win32.whl", hash = "sha256:e31ae45bc2e29f6b2abd0de1cc3b9d5205aa847cafaecb8af1476a609a2f6eb7", size = 171820, upload-time = "2024-09-04T20:45:18.762Z" },
- { url = "https://files.pythonhosted.org/packages/8c/52/b08750ce0bce45c143e1b5d7357ee8c55341b52bdef4b0f081af1eb248c2/cffi-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662", size = 181290, upload-time = "2024-09-04T20:45:20.226Z" },
-]
-
-[[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, upload-time = "2023-08-12T20:38:17.776Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/c5/55/51844dd50c4fc7a33b653bfaba4c2456f06955289ca770a5dbd5fd267374/cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9", size = 7249, upload-time = "2023-08-12T20:38:16.269Z" },
-]
-
-[[package]]
-name = "charset-normalizer"
-version = "3.4.3"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/83/2d/5fd176ceb9b2fc619e63405525573493ca23441330fcdaee6bef9460e924/charset_normalizer-3.4.3.tar.gz", hash = "sha256:6fce4b8500244f6fcb71465d4a4930d132ba9ab8e71a7859e6a5d59851068d14", size = 122371, upload-time = "2025-08-09T07:57:28.46Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/d6/98/f3b8013223728a99b908c9344da3aa04ee6e3fa235f19409033eda92fb78/charset_normalizer-3.4.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fb7f67a1bfa6e40b438170ebdc8158b78dc465a5a67b6dde178a46987b244a72", size = 207695, upload-time = "2025-08-09T07:55:36.452Z" },
- { url = "https://files.pythonhosted.org/packages/21/40/5188be1e3118c82dcb7c2a5ba101b783822cfb413a0268ed3be0468532de/charset_normalizer-3.4.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cc9370a2da1ac13f0153780040f465839e6cccb4a1e44810124b4e22483c93fe", size = 147153, upload-time = "2025-08-09T07:55:38.467Z" },
- { url = "https://files.pythonhosted.org/packages/37/60/5d0d74bc1e1380f0b72c327948d9c2aca14b46a9efd87604e724260f384c/charset_normalizer-3.4.3-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:07a0eae9e2787b586e129fdcbe1af6997f8d0e5abaa0bc98c0e20e124d67e601", size = 160428, upload-time = "2025-08-09T07:55:40.072Z" },
- { url = "https://files.pythonhosted.org/packages/85/9a/d891f63722d9158688de58d050c59dc3da560ea7f04f4c53e769de5140f5/charset_normalizer-3.4.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:74d77e25adda8581ffc1c720f1c81ca082921329452eba58b16233ab1842141c", size = 157627, upload-time = "2025-08-09T07:55:41.706Z" },
- { url = "https://files.pythonhosted.org/packages/65/1a/7425c952944a6521a9cfa7e675343f83fd82085b8af2b1373a2409c683dc/charset_normalizer-3.4.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d0e909868420b7049dafd3a31d45125b31143eec59235311fc4c57ea26a4acd2", size = 152388, upload-time = "2025-08-09T07:55:43.262Z" },
- { url = "https://files.pythonhosted.org/packages/f0/c9/a2c9c2a355a8594ce2446085e2ec97fd44d323c684ff32042e2a6b718e1d/charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c6f162aabe9a91a309510d74eeb6507fab5fff92337a15acbe77753d88d9dcf0", size = 150077, upload-time = "2025-08-09T07:55:44.903Z" },
- { url = "https://files.pythonhosted.org/packages/3b/38/20a1f44e4851aa1c9105d6e7110c9d020e093dfa5836d712a5f074a12bf7/charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:4ca4c094de7771a98d7fbd67d9e5dbf1eb73efa4f744a730437d8a3a5cf994f0", size = 161631, upload-time = "2025-08-09T07:55:46.346Z" },
- { url = "https://files.pythonhosted.org/packages/a4/fa/384d2c0f57edad03d7bec3ebefb462090d8905b4ff5a2d2525f3bb711fac/charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:02425242e96bcf29a49711b0ca9f37e451da7c70562bc10e8ed992a5a7a25cc0", size = 159210, upload-time = "2025-08-09T07:55:47.539Z" },
- { url = "https://files.pythonhosted.org/packages/33/9e/eca49d35867ca2db336b6ca27617deed4653b97ebf45dfc21311ce473c37/charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:78deba4d8f9590fe4dae384aeff04082510a709957e968753ff3c48399f6f92a", size = 153739, upload-time = "2025-08-09T07:55:48.744Z" },
- { url = "https://files.pythonhosted.org/packages/2a/91/26c3036e62dfe8de8061182d33be5025e2424002125c9500faff74a6735e/charset_normalizer-3.4.3-cp310-cp310-win32.whl", hash = "sha256:d79c198e27580c8e958906f803e63cddb77653731be08851c7df0b1a14a8fc0f", size = 99825, upload-time = "2025-08-09T07:55:50.305Z" },
- { url = "https://files.pythonhosted.org/packages/e2/c6/f05db471f81af1fa01839d44ae2a8bfeec8d2a8b4590f16c4e7393afd323/charset_normalizer-3.4.3-cp310-cp310-win_amd64.whl", hash = "sha256:c6e490913a46fa054e03699c70019ab869e990270597018cef1d8562132c2669", size = 107452, upload-time = "2025-08-09T07:55:51.461Z" },
- { url = "https://files.pythonhosted.org/packages/7f/b5/991245018615474a60965a7c9cd2b4efbaabd16d582a5547c47ee1c7730b/charset_normalizer-3.4.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b256ee2e749283ef3ddcff51a675ff43798d92d746d1a6e4631bf8c707d22d0b", size = 204483, upload-time = "2025-08-09T07:55:53.12Z" },
- { url = "https://files.pythonhosted.org/packages/c7/2a/ae245c41c06299ec18262825c1569c5d3298fc920e4ddf56ab011b417efd/charset_normalizer-3.4.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:13faeacfe61784e2559e690fc53fa4c5ae97c6fcedb8eb6fb8d0a15b475d2c64", size = 145520, upload-time = "2025-08-09T07:55:54.712Z" },
- { url = "https://files.pythonhosted.org/packages/3a/a4/b3b6c76e7a635748c4421d2b92c7b8f90a432f98bda5082049af37ffc8e3/charset_normalizer-3.4.3-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:00237675befef519d9af72169d8604a067d92755e84fe76492fef5441db05b91", size = 158876, upload-time = "2025-08-09T07:55:56.024Z" },
- { url = "https://files.pythonhosted.org/packages/e2/e6/63bb0e10f90a8243c5def74b5b105b3bbbfb3e7bb753915fe333fb0c11ea/charset_normalizer-3.4.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:585f3b2a80fbd26b048a0be90c5aae8f06605d3c92615911c3a2b03a8a3b796f", size = 156083, upload-time = "2025-08-09T07:55:57.582Z" },
- { url = "https://files.pythonhosted.org/packages/87/df/b7737ff046c974b183ea9aa111b74185ac8c3a326c6262d413bd5a1b8c69/charset_normalizer-3.4.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e78314bdc32fa80696f72fa16dc61168fda4d6a0c014e0380f9d02f0e5d8a07", size = 150295, upload-time = "2025-08-09T07:55:59.147Z" },
- { url = "https://files.pythonhosted.org/packages/61/f1/190d9977e0084d3f1dc169acd060d479bbbc71b90bf3e7bf7b9927dec3eb/charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:96b2b3d1a83ad55310de8c7b4a2d04d9277d5591f40761274856635acc5fcb30", size = 148379, upload-time = "2025-08-09T07:56:00.364Z" },
- { url = "https://files.pythonhosted.org/packages/4c/92/27dbe365d34c68cfe0ca76f1edd70e8705d82b378cb54ebbaeabc2e3029d/charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:939578d9d8fd4299220161fdd76e86c6a251987476f5243e8864a7844476ba14", size = 160018, upload-time = "2025-08-09T07:56:01.678Z" },
- { url = "https://files.pythonhosted.org/packages/99/04/baae2a1ea1893a01635d475b9261c889a18fd48393634b6270827869fa34/charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:fd10de089bcdcd1be95a2f73dbe6254798ec1bda9f450d5828c96f93e2536b9c", size = 157430, upload-time = "2025-08-09T07:56:02.87Z" },
- { url = "https://files.pythonhosted.org/packages/2f/36/77da9c6a328c54d17b960c89eccacfab8271fdaaa228305330915b88afa9/charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1e8ac75d72fa3775e0b7cb7e4629cec13b7514d928d15ef8ea06bca03ef01cae", size = 151600, upload-time = "2025-08-09T07:56:04.089Z" },
- { url = "https://files.pythonhosted.org/packages/64/d4/9eb4ff2c167edbbf08cdd28e19078bf195762e9bd63371689cab5ecd3d0d/charset_normalizer-3.4.3-cp311-cp311-win32.whl", hash = "sha256:6cf8fd4c04756b6b60146d98cd8a77d0cdae0e1ca20329da2ac85eed779b6849", size = 99616, upload-time = "2025-08-09T07:56:05.658Z" },
- { url = "https://files.pythonhosted.org/packages/f4/9c/996a4a028222e7761a96634d1820de8a744ff4327a00ada9c8942033089b/charset_normalizer-3.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:31a9a6f775f9bcd865d88ee350f0ffb0e25936a7f930ca98995c05abf1faf21c", size = 107108, upload-time = "2025-08-09T07:56:07.176Z" },
- { url = "https://files.pythonhosted.org/packages/e9/5e/14c94999e418d9b87682734589404a25854d5f5d0408df68bc15b6ff54bb/charset_normalizer-3.4.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e28e334d3ff134e88989d90ba04b47d84382a828c061d0d1027b1b12a62b39b1", size = 205655, upload-time = "2025-08-09T07:56:08.475Z" },
- { url = "https://files.pythonhosted.org/packages/7d/a8/c6ec5d389672521f644505a257f50544c074cf5fc292d5390331cd6fc9c3/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0cacf8f7297b0c4fcb74227692ca46b4a5852f8f4f24b3c766dd94a1075c4884", size = 146223, upload-time = "2025-08-09T07:56:09.708Z" },
- { url = "https://files.pythonhosted.org/packages/fc/eb/a2ffb08547f4e1e5415fb69eb7db25932c52a52bed371429648db4d84fb1/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c6fd51128a41297f5409deab284fecbe5305ebd7e5a1f959bee1c054622b7018", size = 159366, upload-time = "2025-08-09T07:56:11.326Z" },
- { url = "https://files.pythonhosted.org/packages/82/10/0fd19f20c624b278dddaf83b8464dcddc2456cb4b02bb902a6da126b87a1/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3cfb2aad70f2c6debfbcb717f23b7eb55febc0bb23dcffc0f076009da10c6392", size = 157104, upload-time = "2025-08-09T07:56:13.014Z" },
- { url = "https://files.pythonhosted.org/packages/16/ab/0233c3231af734f5dfcf0844aa9582d5a1466c985bbed6cedab85af9bfe3/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1606f4a55c0fd363d754049cdf400175ee96c992b1f8018b993941f221221c5f", size = 151830, upload-time = "2025-08-09T07:56:14.428Z" },
- { url = "https://files.pythonhosted.org/packages/ae/02/e29e22b4e02839a0e4a06557b1999d0a47db3567e82989b5bb21f3fbbd9f/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:027b776c26d38b7f15b26a5da1044f376455fb3766df8fc38563b4efbc515154", size = 148854, upload-time = "2025-08-09T07:56:16.051Z" },
- { url = "https://files.pythonhosted.org/packages/05/6b/e2539a0a4be302b481e8cafb5af8792da8093b486885a1ae4d15d452bcec/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:42e5088973e56e31e4fa58eb6bd709e42fc03799c11c42929592889a2e54c491", size = 160670, upload-time = "2025-08-09T07:56:17.314Z" },
- { url = "https://files.pythonhosted.org/packages/31/e7/883ee5676a2ef217a40ce0bffcc3d0dfbf9e64cbcfbdf822c52981c3304b/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:cc34f233c9e71701040d772aa7490318673aa7164a0efe3172b2981218c26d93", size = 158501, upload-time = "2025-08-09T07:56:18.641Z" },
- { url = "https://files.pythonhosted.org/packages/c1/35/6525b21aa0db614cf8b5792d232021dca3df7f90a1944db934efa5d20bb1/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:320e8e66157cc4e247d9ddca8e21f427efc7a04bbd0ac8a9faf56583fa543f9f", size = 153173, upload-time = "2025-08-09T07:56:20.289Z" },
- { url = "https://files.pythonhosted.org/packages/50/ee/f4704bad8201de513fdc8aac1cabc87e38c5818c93857140e06e772b5892/charset_normalizer-3.4.3-cp312-cp312-win32.whl", hash = "sha256:fb6fecfd65564f208cbf0fba07f107fb661bcd1a7c389edbced3f7a493f70e37", size = 99822, upload-time = "2025-08-09T07:56:21.551Z" },
- { url = "https://files.pythonhosted.org/packages/39/f5/3b3836ca6064d0992c58c7561c6b6eee1b3892e9665d650c803bd5614522/charset_normalizer-3.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:86df271bf921c2ee3818f0522e9a5b8092ca2ad8b065ece5d7d9d0e9f4849bcc", size = 107543, upload-time = "2025-08-09T07:56:23.115Z" },
- { url = "https://files.pythonhosted.org/packages/65/ca/2135ac97709b400c7654b4b764daf5c5567c2da45a30cdd20f9eefe2d658/charset_normalizer-3.4.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:14c2a87c65b351109f6abfc424cab3927b3bdece6f706e4d12faaf3d52ee5efe", size = 205326, upload-time = "2025-08-09T07:56:24.721Z" },
- { url = "https://files.pythonhosted.org/packages/71/11/98a04c3c97dd34e49c7d247083af03645ca3730809a5509443f3c37f7c99/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:41d1fc408ff5fdfb910200ec0e74abc40387bccb3252f3f27c0676731df2b2c8", size = 146008, upload-time = "2025-08-09T07:56:26.004Z" },
- { url = "https://files.pythonhosted.org/packages/60/f5/4659a4cb3c4ec146bec80c32d8bb16033752574c20b1252ee842a95d1a1e/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1bb60174149316da1c35fa5233681f7c0f9f514509b8e399ab70fea5f17e45c9", size = 159196, upload-time = "2025-08-09T07:56:27.25Z" },
- { url = "https://files.pythonhosted.org/packages/86/9e/f552f7a00611f168b9a5865a1414179b2c6de8235a4fa40189f6f79a1753/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:30d006f98569de3459c2fc1f2acde170b7b2bd265dc1943e87e1a4efe1b67c31", size = 156819, upload-time = "2025-08-09T07:56:28.515Z" },
- { url = "https://files.pythonhosted.org/packages/7e/95/42aa2156235cbc8fa61208aded06ef46111c4d3f0de233107b3f38631803/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:416175faf02e4b0810f1f38bcb54682878a4af94059a1cd63b8747244420801f", size = 151350, upload-time = "2025-08-09T07:56:29.716Z" },
- { url = "https://files.pythonhosted.org/packages/c2/a9/3865b02c56f300a6f94fc631ef54f0a8a29da74fb45a773dfd3dcd380af7/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6aab0f181c486f973bc7262a97f5aca3ee7e1437011ef0c2ec04b5a11d16c927", size = 148644, upload-time = "2025-08-09T07:56:30.984Z" },
- { url = "https://files.pythonhosted.org/packages/77/d9/cbcf1a2a5c7d7856f11e7ac2d782aec12bdfea60d104e60e0aa1c97849dc/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:fdabf8315679312cfa71302f9bd509ded4f2f263fb5b765cf1433b39106c3cc9", size = 160468, upload-time = "2025-08-09T07:56:32.252Z" },
- { url = "https://files.pythonhosted.org/packages/f6/42/6f45efee8697b89fda4d50580f292b8f7f9306cb2971d4b53f8914e4d890/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:bd28b817ea8c70215401f657edef3a8aa83c29d447fb0b622c35403780ba11d5", size = 158187, upload-time = "2025-08-09T07:56:33.481Z" },
- { url = "https://files.pythonhosted.org/packages/70/99/f1c3bdcfaa9c45b3ce96f70b14f070411366fa19549c1d4832c935d8e2c3/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:18343b2d246dc6761a249ba1fb13f9ee9a2bcd95decc767319506056ea4ad4dc", size = 152699, upload-time = "2025-08-09T07:56:34.739Z" },
- { url = "https://files.pythonhosted.org/packages/a3/ad/b0081f2f99a4b194bcbb1934ef3b12aa4d9702ced80a37026b7607c72e58/charset_normalizer-3.4.3-cp313-cp313-win32.whl", hash = "sha256:6fb70de56f1859a3f71261cbe41005f56a7842cc348d3aeb26237560bfa5e0ce", size = 99580, upload-time = "2025-08-09T07:56:35.981Z" },
- { url = "https://files.pythonhosted.org/packages/9a/8f/ae790790c7b64f925e5c953b924aaa42a243fb778fed9e41f147b2a5715a/charset_normalizer-3.4.3-cp313-cp313-win_amd64.whl", hash = "sha256:cf1ebb7d78e1ad8ec2a8c4732c7be2e736f6e5123a4146c5b89c9d1f585f8cef", size = 107366, upload-time = "2025-08-09T07:56:37.339Z" },
- { url = "https://files.pythonhosted.org/packages/8e/91/b5a06ad970ddc7a0e513112d40113e834638f4ca1120eb727a249fb2715e/charset_normalizer-3.4.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3cd35b7e8aedeb9e34c41385fda4f73ba609e561faedfae0a9e75e44ac558a15", size = 204342, upload-time = "2025-08-09T07:56:38.687Z" },
- { url = "https://files.pythonhosted.org/packages/ce/ec/1edc30a377f0a02689342f214455c3f6c2fbedd896a1d2f856c002fc3062/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b89bc04de1d83006373429975f8ef9e7932534b8cc9ca582e4db7d20d91816db", size = 145995, upload-time = "2025-08-09T07:56:40.048Z" },
- { url = "https://files.pythonhosted.org/packages/17/e5/5e67ab85e6d22b04641acb5399c8684f4d37caf7558a53859f0283a650e9/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2001a39612b241dae17b4687898843f254f8748b796a2e16f1051a17078d991d", size = 158640, upload-time = "2025-08-09T07:56:41.311Z" },
- { url = "https://files.pythonhosted.org/packages/f1/e5/38421987f6c697ee3722981289d554957c4be652f963d71c5e46a262e135/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8dcfc373f888e4fb39a7bc57e93e3b845e7f462dacc008d9749568b1c4ece096", size = 156636, upload-time = "2025-08-09T07:56:43.195Z" },
- { url = "https://files.pythonhosted.org/packages/a0/e4/5a075de8daa3ec0745a9a3b54467e0c2967daaaf2cec04c845f73493e9a1/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:18b97b8404387b96cdbd30ad660f6407799126d26a39ca65729162fd810a99aa", size = 150939, upload-time = "2025-08-09T07:56:44.819Z" },
- { url = "https://files.pythonhosted.org/packages/02/f7/3611b32318b30974131db62b4043f335861d4d9b49adc6d57c1149cc49d4/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ccf600859c183d70eb47e05a44cd80a4ce77394d1ac0f79dbd2dd90a69a3a049", size = 148580, upload-time = "2025-08-09T07:56:46.684Z" },
- { url = "https://files.pythonhosted.org/packages/7e/61/19b36f4bd67f2793ab6a99b979b4e4f3d8fc754cbdffb805335df4337126/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:53cd68b185d98dde4ad8990e56a58dea83a4162161b1ea9272e5c9182ce415e0", size = 159870, upload-time = "2025-08-09T07:56:47.941Z" },
- { url = "https://files.pythonhosted.org/packages/06/57/84722eefdd338c04cf3030ada66889298eaedf3e7a30a624201e0cbe424a/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:30a96e1e1f865f78b030d65241c1ee850cdf422d869e9028e2fc1d5e4db73b92", size = 157797, upload-time = "2025-08-09T07:56:49.756Z" },
- { url = "https://files.pythonhosted.org/packages/72/2a/aff5dd112b2f14bcc3462c312dce5445806bfc8ab3a7328555da95330e4b/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d716a916938e03231e86e43782ca7878fb602a125a91e7acb8b5112e2e96ac16", size = 152224, upload-time = "2025-08-09T07:56:51.369Z" },
- { url = "https://files.pythonhosted.org/packages/b7/8c/9839225320046ed279c6e839d51f028342eb77c91c89b8ef2549f951f3ec/charset_normalizer-3.4.3-cp314-cp314-win32.whl", hash = "sha256:c6dbd0ccdda3a2ba7c2ecd9d77b37f3b5831687d8dc1b6ca5f56a4880cc7b7ce", size = 100086, upload-time = "2025-08-09T07:56:52.722Z" },
- { url = "https://files.pythonhosted.org/packages/ee/7a/36fbcf646e41f710ce0a563c1c9a343c6edf9be80786edeb15b6f62e17db/charset_normalizer-3.4.3-cp314-cp314-win_amd64.whl", hash = "sha256:73dc19b562516fc9bcf6e5d6e596df0b4eb98d87e4f79f3ae71840e6ed21361c", size = 107400, upload-time = "2025-08-09T07:56:55.172Z" },
- { url = "https://files.pythonhosted.org/packages/c2/ca/9a0983dd5c8e9733565cf3db4df2b0a2e9a82659fd8aa2a868ac6e4a991f/charset_normalizer-3.4.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:70bfc5f2c318afece2f5838ea5e4c3febada0be750fcf4775641052bbba14d05", size = 207520, upload-time = "2025-08-09T07:57:11.026Z" },
- { url = "https://files.pythonhosted.org/packages/39/c6/99271dc37243a4f925b09090493fb96c9333d7992c6187f5cfe5312008d2/charset_normalizer-3.4.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:23b6b24d74478dc833444cbd927c338349d6ae852ba53a0d02a2de1fce45b96e", size = 147307, upload-time = "2025-08-09T07:57:12.4Z" },
- { url = "https://files.pythonhosted.org/packages/e4/69/132eab043356bba06eb333cc2cc60c6340857d0a2e4ca6dc2b51312886b3/charset_normalizer-3.4.3-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:34a7f768e3f985abdb42841e20e17b330ad3aaf4bb7e7aeeb73db2e70f077b99", size = 160448, upload-time = "2025-08-09T07:57:13.712Z" },
- { url = "https://files.pythonhosted.org/packages/04/9a/914d294daa4809c57667b77470533e65def9c0be1ef8b4c1183a99170e9d/charset_normalizer-3.4.3-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fb731e5deb0c7ef82d698b0f4c5bb724633ee2a489401594c5c88b02e6cb15f7", size = 157758, upload-time = "2025-08-09T07:57:14.979Z" },
- { url = "https://files.pythonhosted.org/packages/b0/a8/6f5bcf1bcf63cb45625f7c5cadca026121ff8a6c8a3256d8d8cd59302663/charset_normalizer-3.4.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:257f26fed7d7ff59921b78244f3cd93ed2af1800ff048c33f624c87475819dd7", size = 152487, upload-time = "2025-08-09T07:57:16.332Z" },
- { url = "https://files.pythonhosted.org/packages/c4/72/d3d0e9592f4e504f9dea08b8db270821c909558c353dc3b457ed2509f2fb/charset_normalizer-3.4.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1ef99f0456d3d46a50945c98de1774da86f8e992ab5c77865ea8b8195341fc19", size = 150054, upload-time = "2025-08-09T07:57:17.576Z" },
- { url = "https://files.pythonhosted.org/packages/20/30/5f64fe3981677fe63fa987b80e6c01042eb5ff653ff7cec1b7bd9268e54e/charset_normalizer-3.4.3-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:2c322db9c8c89009a990ef07c3bcc9f011a3269bc06782f916cd3d9eed7c9312", size = 161703, upload-time = "2025-08-09T07:57:20.012Z" },
- { url = "https://files.pythonhosted.org/packages/e1/ef/dd08b2cac9284fd59e70f7d97382c33a3d0a926e45b15fc21b3308324ffd/charset_normalizer-3.4.3-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:511729f456829ef86ac41ca78c63a5cb55240ed23b4b737faca0eb1abb1c41bc", size = 159096, upload-time = "2025-08-09T07:57:21.329Z" },
- { url = "https://files.pythonhosted.org/packages/45/8c/dcef87cfc2b3f002a6478f38906f9040302c68aebe21468090e39cde1445/charset_normalizer-3.4.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:88ab34806dea0671532d3f82d82b85e8fc23d7b2dd12fa837978dad9bb392a34", size = 153852, upload-time = "2025-08-09T07:57:22.608Z" },
- { url = "https://files.pythonhosted.org/packages/63/86/9cbd533bd37883d467fcd1bd491b3547a3532d0fbb46de2b99feeebf185e/charset_normalizer-3.4.3-cp39-cp39-win32.whl", hash = "sha256:16a8770207946ac75703458e2c743631c79c59c5890c80011d536248f8eaa432", size = 99840, upload-time = "2025-08-09T07:57:23.883Z" },
- { url = "https://files.pythonhosted.org/packages/ce/d6/7e805c8e5c46ff9729c49950acc4ee0aeb55efb8b3a56687658ad10c3216/charset_normalizer-3.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:d22dbedd33326a4a5190dd4fe9e9e693ef12160c77382d9e87919bce54f3d4ca", size = 107438, upload-time = "2025-08-09T07:57:25.287Z" },
- { url = "https://files.pythonhosted.org/packages/8a/1f/f041989e93b001bc4e44bb1669ccdcf54d3f00e628229a85b08d330615c5/charset_normalizer-3.4.3-py3-none-any.whl", hash = "sha256:ce571ab16d890d23b5c278547ba694193a45011ff86a9162a71307ed9f86759a", size = 53175, upload-time = "2025-08-09T07:57:26.864Z" },
-]
-
-[[package]]
-name = "choreographer"
-version = "1.0.10"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "logistro" },
- { name = "simplejson" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/71/0e/88c2a0307e27f40bd0ce18e66ba2c8f7327b95b23adc51ea57a08cb96797/choreographer-1.0.10.tar.gz", hash = "sha256:7adf84a0d6a6054386d5cce013fdcadb2426479e49c9b0cb06af7d3712ed263c", size = 40455, upload-time = "2025-08-22T20:37:25.461Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/d3/c2/82389f6e20098a414ddcf88d3b8032809b7a66e70f9bc5264f81beb24b4a/choreographer-1.0.10-py3-none-any.whl", hash = "sha256:b23ec2409f38ec89a544558eadeb19688746bab9a0f47e7115477d6e80a14a41", size = 51300, upload-time = "2025-08-22T20:37:24.372Z" },
-]
-
-[[package]]
-name = "click"
-version = "8.1.8"
-source = { registry = "https://pypi.org/simple" }
-resolution-markers = [
- "python_full_version < '3.10'",
-]
-dependencies = [
- { name = "colorama", marker = "python_full_version < '3.10' and sys_platform == 'win32'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593, upload-time = "2024-12-21T18:38:44.339Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188, upload-time = "2024-12-21T18:38:41.666Z" },
-]
-
-[[package]]
-name = "click"
-version = "8.2.1"
-source = { registry = "https://pypi.org/simple" }
-resolution-markers = [
- "python_full_version >= '3.13'",
- "python_full_version >= '3.12.4' and python_full_version < '3.13'",
- "python_full_version >= '3.12' and python_full_version < '3.12.4'",
- "python_full_version == '3.11.*'",
- "python_full_version == '3.10.*'",
-]
-dependencies = [
- { name = "colorama", marker = "python_full_version >= '3.10' and sys_platform == 'win32'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/60/6c/8ca2efa64cf75a977a0d7fac081354553ebe483345c734fb6b6515d96bbc/click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202", size = 286342, upload-time = "2025-05-20T23:19:49.832Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/85/32/10bb5764d90a8eee674e9dc6f4db6a0ab47c8c4d0d83c27f7c39ac415a4d/click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b", size = 102215, upload-time = "2025-05-20T23:19:47.796Z" },
-]
-
-[[package]]
-name = "click-default-group"
-version = "1.2.4"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "click", version = "8.1.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
- { name = "click", version = "8.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/1d/ce/edb087fb53de63dad3b36408ca30368f438738098e668b78c87f93cd41df/click_default_group-1.2.4.tar.gz", hash = "sha256:eb3f3c99ec0d456ca6cd2a7f08f7d4e91771bef51b01bdd9580cc6450fe1251e", size = 3505, upload-time = "2023-08-04T07:54:58.425Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/2c/1a/aff8bb287a4b1400f69e09a53bd65de96aa5cee5691925b38731c67fc695/click_default_group-1.2.4-py2.py3-none-any.whl", hash = "sha256:9b60486923720e7fc61731bdb32b617039aba820e22e1c88766b1125592eaa5f", size = 4123, upload-time = "2023-08-04T07:54:56.875Z" },
-]
-
-[[package]]
-name = "click-spinner"
-version = "0.1.10"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/af/3a/7dbc558fcf0ae9e2e8b7ccc52daeb4eaf32b21f851497f5b409e1638dcee/click-spinner-0.1.10.tar.gz", hash = "sha256:87eacf9d7298973a25d7615ef57d4782aebf913a532bba4b28a37e366e975daf", size = 18720, upload-time = "2020-04-24T07:14:51.955Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/93/2a/04893832bfeddc2d40a7de2e8153b3085f12d63507d91a9cf0157dc3a1c2/click_spinner-0.1.10-py2.py3-none-any.whl", hash = "sha256:d1ffcff1fdad9882396367f15fb957bcf7f5c64ab91927dee2127e0d2991ee84", size = 3986, upload-time = "2020-04-24T07:14:50.575Z" },
-]
-
-[[package]]
-name = "clickhouse-driver"
-version = "0.2.9"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "pytz" },
- { name = "tzlocal" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/bf/0b/3790274f7591fc55b1f91bcc8e576338859cc632b1b17288b5bab79b769d/clickhouse-driver-0.2.9.tar.gz", hash = "sha256:050ea4870ead993910b39e7fae965dc1c347b2e8191dcd977cd4b385f9e19f87", size = 357752, upload-time = "2024-08-16T18:08:28.116Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/bd/0a/e5bb8a527f88acc1ee2efca28354cf5eab40e26e974df2221ab27abf9b9a/clickhouse_driver-0.2.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6ce04e9d0d0f39561f312d1ac1a8147bc9206e4267e1a23e20e0423ebac95534", size = 219246, upload-time = "2024-08-16T18:02:51.067Z" },
- { url = "https://files.pythonhosted.org/packages/e1/fd/8d057e77c8e7f1c739fcb76277edf83a863a259505031a2f8668d5bb2221/clickhouse_driver-0.2.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7ae5c8931bf290b9d85582e7955b9aad7f19ff9954e48caa4f9a180ea4d01078", size = 215395, upload-time = "2024-08-16T18:02:54.249Z" },
- { url = "https://files.pythonhosted.org/packages/8e/71/fa053b0327a8e7cdd3f69e3f01a5683f901e2b1388f057db9d39de86f016/clickhouse_driver-0.2.9-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e51792f3bd12c32cb15a907f12de3c9d264843f0bb33dce400e3966c9f09a3f", size = 923279, upload-time = "2024-08-16T18:02:59.01Z" },
- { url = "https://files.pythonhosted.org/packages/a2/a7/9be2e1a40543959c9c008bf01ae54aef793153eeef9885fca8a16ab677b8/clickhouse_driver-0.2.9-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:42fc546c31e4a04c97b749769335a679c9044dc693fa7a93e38c97fd6727173d", size = 967909, upload-time = "2024-08-16T18:03:03.453Z" },
- { url = "https://files.pythonhosted.org/packages/7f/ec/16b320623a30de3b62f37f7aec4d33fc10a09bd095160346abd288a71822/clickhouse_driver-0.2.9-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6a383a403d185185c64e49edd6a19b2ec973c5adcb8ebff7ed2fc539a2cc65a5", size = 965883, upload-time = "2024-08-16T18:03:09.093Z" },
- { url = "https://files.pythonhosted.org/packages/df/8b/1997d8c3f88127441229c8080b5701e3d8b6ffd591a0890d6c518f8b1355/clickhouse_driver-0.2.9-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f05321a97e816afc75b3e4f9eda989848fecf14ecf1a91d0f22c04258123d1f7", size = 935935, upload-time = "2024-08-16T18:03:11.956Z" },
- { url = "https://files.pythonhosted.org/packages/ca/60/1751a1ef2b70026c6c0a5d9b7e31a19bcd54dcc6b245de5d3db2034108cc/clickhouse_driver-0.2.9-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:be47e793846aac28442b6b1c6554e0731b848a5a7759a54aa2489997354efe4a", size = 911710, upload-time = "2024-08-16T18:03:14.958Z" },
- { url = "https://files.pythonhosted.org/packages/a8/e0/05697a02f3e388c64c57ae5bddc5deb45f5802d0d62a10c633965a3dad8b/clickhouse_driver-0.2.9-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:780e42a215d1ae2f6d695d74dd6f087781fb2fa51c508b58f79e68c24c5364e0", size = 933821, upload-time = "2024-08-16T18:03:18.801Z" },
- { url = "https://files.pythonhosted.org/packages/7e/44/650ea0e3bfaa587b062a3da60233d061e568b412c21e429a116ce7134023/clickhouse_driver-0.2.9-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:9e28f1fe850675e173db586e9f1ac790e8f7edd507a4227cd54cd7445f8e75b6", size = 920276, upload-time = "2024-08-16T18:03:23.252Z" },
- { url = "https://files.pythonhosted.org/packages/fb/66/0991e9f6a14838c900446b258ba55ef47a79bc224bf74c23c92dd157ad7d/clickhouse_driver-0.2.9-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:125aae7f1308d3083dadbb3c78f828ae492e060f13e4007a0cf53a8169ed7b39", size = 973511, upload-time = "2024-08-16T18:03:27.383Z" },
- { url = "https://files.pythonhosted.org/packages/8b/01/733d6f55344093a2b87d05db864df9044e756c13894c19000be3535b062b/clickhouse_driver-0.2.9-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:2f3c4fbb61e75c62a1ab93a1070d362de4cb5682f82833b2c12deccb3bae888d", size = 980838, upload-time = "2024-08-16T18:03:32.828Z" },
- { url = "https://files.pythonhosted.org/packages/63/0c/5dba2a82fe1701c7f0db88a1d7d08134a8c80192f2548871b87c54b066c3/clickhouse_driver-0.2.9-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0dc03196a84e32d23b88b665be69afae98f57426f5fdf203e16715b756757961", size = 950460, upload-time = "2024-08-16T18:03:37.034Z" },
- { url = "https://files.pythonhosted.org/packages/5b/24/08f1d0afceaf5e2f325d851add6b9f80bd71c6c540dc29a1197d8d37eadb/clickhouse_driver-0.2.9-cp310-cp310-win32.whl", hash = "sha256:25695d78a1d7ad6e221e800612eac08559f6182bf6dee0a220d08de7b612d993", size = 198432, upload-time = "2024-08-16T18:03:39.119Z" },
- { url = "https://files.pythonhosted.org/packages/73/9b/6c110f95530c26b9297bf204e487e645c90eb28dd3ca2cf40ac8ae61e4b4/clickhouse_driver-0.2.9-cp310-cp310-win_amd64.whl", hash = "sha256:367acac95398d721a0a2a6cf87e93638c5588b79498a9848676ce7f182540a6c", size = 213541, upload-time = "2024-08-16T18:03:41.141Z" },
- { url = "https://files.pythonhosted.org/packages/53/e9/8bf043fbac34e06eebb7d58647c1a28616bf065cdc3334c8d771902c8902/clickhouse_driver-0.2.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5a7353a7a08eee3aa0001d8a5d771cb1f37e2acae1b48178002431f23892121a", size = 220413, upload-time = "2024-08-16T18:03:43.928Z" },
- { url = "https://files.pythonhosted.org/packages/88/6d/e8b534bee11c809f52b1c9d77160da48a3d84c775d7e547a73f2d2ba1f9f/clickhouse_driver-0.2.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6af1c6cbc3481205503ab72a34aa76d6519249c904aa3f7a84b31e7b435555be", size = 215881, upload-time = "2024-08-16T18:03:46.153Z" },
- { url = "https://files.pythonhosted.org/packages/f0/5e/894f789b47a4deaff0a84b2660323b4b3e692c3ff5cc0783d59a7945b50d/clickhouse_driver-0.2.9-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48033803abd1100bfff6b9a1769d831b672cd3cda5147e0323b956fd1416d38d", size = 1015584, upload-time = "2024-08-16T18:03:49.407Z" },
- { url = "https://files.pythonhosted.org/packages/ab/91/951e0aa20d110f8d801023fec28c9cbbb0cfa9c596170e732e66bb30bf9d/clickhouse_driver-0.2.9-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1f202a58a540c85e47c31dabc8f84b6fe79dca5315c866450a538d58d6fa0571", size = 1058755, upload-time = "2024-08-16T18:03:53.236Z" },
- { url = "https://files.pythonhosted.org/packages/7a/25/2ff96df78d078150e5bc01307657a21b8bd29f040c11bffed7841228eabe/clickhouse_driver-0.2.9-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e4df50fd84bfa4aa1eb7b52d48136066bfb64fabb7ceb62d4c318b45a296200b", size = 1058250, upload-time = "2024-08-16T18:03:56.978Z" },
- { url = "https://files.pythonhosted.org/packages/f7/08/1b08d596dab964bd306a45d4d3fe84c1740888d5a392374621a5fac8f186/clickhouse_driver-0.2.9-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:433a650571a0d7766eb6f402e8f5930222997686c2ee01ded22f1d8fd46af9d4", size = 1026785, upload-time = "2024-08-16T18:03:59.999Z" },
- { url = "https://files.pythonhosted.org/packages/8e/34/8b34c729719617373f645ad4cdff38ff7f7903b417208ad97859407edb3f/clickhouse_driver-0.2.9-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:232ee260475611cbf7adb554b81db6b5790b36e634fe2164f4ffcd2ca3e63a71", size = 988253, upload-time = "2024-08-16T18:04:03.145Z" },
- { url = "https://files.pythonhosted.org/packages/f0/b0/d36ec521179b7d2966831ae875e2049800c653d6a6697e96c3143e09b66c/clickhouse_driver-0.2.9-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:09049f7e71f15c9c9a03f597f77fc1f7b61ababd155c06c0d9e64d1453d945d7", size = 1016946, upload-time = "2024-08-16T18:04:06.418Z" },
- { url = "https://files.pythonhosted.org/packages/0f/90/74e9cc608a9c0aca8fc58e3fa1dc5f8dda4e0c00bd3d4be8c9abd31a4e54/clickhouse_driver-0.2.9-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:424153d1d5f5a807f596a48cc88119f9fb3213ca7e38f57b8d15dcc964dd91f7", size = 993030, upload-time = "2024-08-16T18:04:10.521Z" },
- { url = "https://files.pythonhosted.org/packages/29/b5/7831b1eefb8450e12b263cc8d9e60615e361d698485ffc66fd8704145381/clickhouse_driver-0.2.9-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:4f078fd1cf19c4ca63b8d1e0803df665310c8d5b644c5b02bf2465e8d6ef8f55", size = 1053397, upload-time = "2024-08-16T18:04:13.313Z" },
- { url = "https://files.pythonhosted.org/packages/fc/d2/4c2206105f7e8d5a06b1aea6ea345fadf42d299f0b642b89b86c3be8b726/clickhouse_driver-0.2.9-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:f138d939e26e767537f891170b69a55a88038919f5c10d8865b67b8777fe4848", size = 1061742, upload-time = "2024-08-16T18:04:16.416Z" },
- { url = "https://files.pythonhosted.org/packages/10/1c/0951208141bb26681c06ea0c84a0fe5e4374b1560c38abe8b5f93da6bbb5/clickhouse_driver-0.2.9-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9aafabc7e32942f85dcb46f007f447ab69024831575df97cae28c6ed127654d1", size = 1031183, upload-time = "2024-08-16T18:04:19.368Z" },
- { url = "https://files.pythonhosted.org/packages/23/94/2e533b5638fddb6abfb3f3de41f9938445661aae781209c5d6634d53b40f/clickhouse_driver-0.2.9-cp311-cp311-win32.whl", hash = "sha256:935e16ebf1a1998d8493979d858821a755503c9b8af572d9c450173d4b88868c", size = 198031, upload-time = "2024-08-16T18:04:21.313Z" },
- { url = "https://files.pythonhosted.org/packages/45/87/163e4469d298d5f74a61328a129494c84404932ae25d7c585b172609def7/clickhouse_driver-0.2.9-cp311-cp311-win_amd64.whl", hash = "sha256:306b3102cba278b5dfec6f5f7dc8b78416c403901510475c74913345b56c9e42", size = 213510, upload-time = "2024-08-16T18:04:23.639Z" },
- { url = "https://files.pythonhosted.org/packages/60/58/acc74be412330aa4d681df2d13e013e84e27cc767dea73a507cb71c74cff/clickhouse_driver-0.2.9-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fcb2fd00e58650ae206a6d5dbc83117240e622471aa5124733fbf2805eb8bda0", size = 221487, upload-time = "2024-08-16T18:04:25.61Z" },
- { url = "https://files.pythonhosted.org/packages/f5/bc/09b69a1be0155e02a0df9ecafb63c9a2f7d9e412c865dd3c711e07967e85/clickhouse_driver-0.2.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b7a3e6b0a1eb218e3d870a94c76daaf65da46dca8f6888ea6542f94905c24d88", size = 217362, upload-time = "2024-08-16T18:04:27.565Z" },
- { url = "https://files.pythonhosted.org/packages/ed/58/79eadc238d6ee0d7920ae36f0ea3a6479a8310bffb6c97ea6aa060a98434/clickhouse_driver-0.2.9-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a8d8e2888a857d8db3d98765a5ad23ab561241feaef68bbffc5a0bd9c142342", size = 1018173, upload-time = "2024-08-16T18:04:31.052Z" },
- { url = "https://files.pythonhosted.org/packages/44/61/1647a0d8aae2c4a3d8c3093d1799f943ff38a0cb81d5e4050be18993f3fa/clickhouse_driver-0.2.9-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:85d50c011467f5ff6772c4059345968b854b72e07a0219030b7c3f68419eb7f7", size = 1046542, upload-time = "2024-08-16T18:04:34.75Z" },
- { url = "https://files.pythonhosted.org/packages/77/23/32bab0efeec64d56313b90c73d067440829630f9a5980de73cb52350a4c9/clickhouse_driver-0.2.9-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:93b395c1370629ccce8fb3e14cd5be2646d227bd32018c21f753c543e9a7e96b", size = 1057508, upload-time = "2024-08-16T18:04:38.81Z" },
- { url = "https://files.pythonhosted.org/packages/07/8e/9b79fd85d28a9e83b87a8722a8e736d69ef5edde8cee5d1dde6950aa512f/clickhouse_driver-0.2.9-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6dbcee870c60d9835e5dce1456ab6b9d807e6669246357f4b321ef747b90fa43", size = 1032860, upload-time = "2024-08-16T18:04:43.152Z" },
- { url = "https://files.pythonhosted.org/packages/bd/ca/208358dd8d80a25633b5f19a9acadb1fb23b55be7f2123e5e70d132de204/clickhouse_driver-0.2.9-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fffa5a5f317b1ec92e406a30a008929054cf3164d2324a3c465d0a0330273bf8", size = 984133, upload-time = "2024-08-16T18:04:47.357Z" },
- { url = "https://files.pythonhosted.org/packages/19/e1/9767cea5bfc9451b7a2680d5b0d4bd3261c56db92002f90ce716209f59c1/clickhouse_driver-0.2.9-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:476702740a279744badbd177ae1c4a2d089ec128bd676861219d1f92078e4530", size = 1020897, upload-time = "2024-08-16T18:04:50.27Z" },
- { url = "https://files.pythonhosted.org/packages/f5/bc/62511b61fbee97c8ab1c64ab4bf33045bcc132d236e61a65831c0de32b82/clickhouse_driver-0.2.9-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:5cd6d95fab5ff80e9dc9baedc9a926f62f74072d42d5804388d63b63bec0bb63", size = 989911, upload-time = "2024-08-16T18:04:53.191Z" },
- { url = "https://files.pythonhosted.org/packages/a0/a8/e3ff5cbc24dbc087acf0733c47fe7a6a6a2f3225e9c168af2414fb803f3c/clickhouse_driver-0.2.9-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:05027d32d7cf3e46cb8d04f8c984745ae01bd1bc7b3579f9dadf9b3cca735697", size = 1045389, upload-time = "2024-08-16T18:04:56.183Z" },
- { url = "https://files.pythonhosted.org/packages/d6/58/29f56e340094cfec72080773e3d94c7963c2e69f70edff83f2a139965d38/clickhouse_driver-0.2.9-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:3d11831842250b4c1b26503a6e9c511fc03db096608b7c6af743818c421a3032", size = 1063242, upload-time = "2024-08-16T18:04:59.452Z" },
- { url = "https://files.pythonhosted.org/packages/ff/0f/161626812ad2bd9480ff390a96489983709d94b33da68f028ace9d1367be/clickhouse_driver-0.2.9-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:81b4b671b785ebb0b8aeabf2432e47072413d81db959eb8cfd8b6ab58c5799c6", size = 1039703, upload-time = "2024-08-16T18:05:03.04Z" },
- { url = "https://files.pythonhosted.org/packages/4a/29/e353c4e835d722b4f6b259d668c2ac47f35bf6a0053414a80522df649ff5/clickhouse_driver-0.2.9-cp312-cp312-win32.whl", hash = "sha256:e893bd4e014877174a59e032b0e99809c95ec61328a0e6bd9352c74a2f6111a8", size = 198390, upload-time = "2024-08-16T18:05:05.175Z" },
- { url = "https://files.pythonhosted.org/packages/5b/09/ff81e99e9ecbb85f2ada57a690b1d0cfee6f2e1eff59ee08609a160d5644/clickhouse_driver-0.2.9-cp312-cp312-win_amd64.whl", hash = "sha256:de6624e28eeffd01668803d28ae89e3d4e359b1bff8b60e4933e1cb3c6f86f18", size = 213585, upload-time = "2024-08-16T18:05:06.981Z" },
- { url = "https://files.pythonhosted.org/packages/d9/bb/8500648908e72c53a691f853a8692274c07556b128dd27a355f31e9b5140/clickhouse_driver-0.2.9-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a46b227fab4420566ed24ee70d90076226d16fcf09c6ad4d428717efcf536446", size = 220741, upload-time = "2024-08-16T18:06:37.186Z" },
- { url = "https://files.pythonhosted.org/packages/3a/24/78a4e543c98d5317be28a933e1fffaa9211ede6424c606db406039731aa2/clickhouse_driver-0.2.9-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7eaa2ce5ea08cf5fddebb8c274c450e102f329f9e6966b6cd85aa671c48e5552", size = 216678, upload-time = "2024-08-16T18:06:40.057Z" },
- { url = "https://files.pythonhosted.org/packages/5f/f4/89a62565b138da166f4046b54e4b0e91b434ed88845095f62f5eed76ae1c/clickhouse_driver-0.2.9-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f97f0083194d6e23b5ef6156ed0d5388c37847b298118199d7937ba26412a9e2", size = 930441, upload-time = "2024-08-16T18:06:43.956Z" },
- { url = "https://files.pythonhosted.org/packages/c3/90/75557317e1d4594e4c309d286d461806d333d57106485619a9149836695b/clickhouse_driver-0.2.9-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a6cab5cdbb0f8ee51d879d977b78f07068b585225ac656f3c081896c362e8f83", size = 975763, upload-time = "2024-08-16T18:06:48.618Z" },
- { url = "https://files.pythonhosted.org/packages/19/54/763ae4db2f5317f779c989e45904a3de55adc0d6481beb49a83359974351/clickhouse_driver-0.2.9-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cdb1b011a53ee71539e9dc655f268b111bac484db300da92829ed59e910a8fd0", size = 973494, upload-time = "2024-08-16T18:06:53.842Z" },
- { url = "https://files.pythonhosted.org/packages/9d/3f/2aec5b1ad03b2cc175fa6e0e86a13369d52b1da62920b54a1b6671f142cd/clickhouse_driver-0.2.9-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7bf51bb761b281d20910b4b689c699ef98027845467daa5bb5dfdb53bd6ee404", size = 943742, upload-time = "2024-08-16T18:06:59.291Z" },
- { url = "https://files.pythonhosted.org/packages/0f/92/49dd26adc1f1bf81070ef88ce02d18874919255e4359209a332672c7ce23/clickhouse_driver-0.2.9-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b8ea462e3cebb121ff55002e9c8a9a0a3fd9b5bbbf688b4960f0a83c0172fb31", size = 916390, upload-time = "2024-08-16T18:07:03.876Z" },
- { url = "https://files.pythonhosted.org/packages/0d/44/b58c0b9e850ec5757fc77c83c29e811a6d98cc5409101c8848796b42fd4e/clickhouse_driver-0.2.9-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:70bee21c245226ad0d637bf470472e2d487b86911b6d673a862127b934336ff4", size = 936953, upload-time = "2024-08-16T18:07:09.232Z" },
- { url = "https://files.pythonhosted.org/packages/00/05/0f9a51bd3c649aa3cceec56429de408bb2b3eb12d3293410d0227e2a7c00/clickhouse_driver-0.2.9-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:253a3c223b944d691bf0abbd599f592ea3b36f0a71d2526833b1718f37eca5c2", size = 924109, upload-time = "2024-08-16T18:07:13.832Z" },
- { url = "https://files.pythonhosted.org/packages/c5/10/800c9002ab342edefbaa4d432404b96aaf61179e0b0f7117523424710b3c/clickhouse_driver-0.2.9-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:a6549b53fc5c403dc556cb39b2ae94d73f9b113daa00438a660bb1dd5380ae4d", size = 979587, upload-time = "2024-08-16T18:07:18.349Z" },
- { url = "https://files.pythonhosted.org/packages/70/85/f4d44509dce6dd20c78f425921f0624ae5e377f77c3b42f0d44d9cdb5b4a/clickhouse_driver-0.2.9-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:1c685cd4abe61af1c26279ff04b9f567eb4d6c1ec7fb265af7481b1f153043aa", size = 986099, upload-time = "2024-08-16T18:07:22.115Z" },
- { url = "https://files.pythonhosted.org/packages/6c/c4/1a8e9a1f4521f7fe46cf326b9b1f09a0b09ba4c15b9c01507d0cfefc02b7/clickhouse_driver-0.2.9-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7e25144219577491929d032a6c3ddd63c6cd7fa764af829a5637f798190d9b26", size = 953477, upload-time = "2024-08-16T18:07:25.35Z" },
- { url = "https://files.pythonhosted.org/packages/f4/17/182d40fd30da2f31f5758a3d3003383fd9520cc728dd1445057ed8304d91/clickhouse_driver-0.2.9-cp39-cp39-win32.whl", hash = "sha256:0b9925610d25405a8e6d83ff4f54fc2456a121adb0155999972f5edd6ba3efc8", size = 199857, upload-time = "2024-08-16T18:07:27.539Z" },
- { url = "https://files.pythonhosted.org/packages/05/96/3d7af33167edd2ec94d3bc89b2f7e093f64225ccc13bb97131e19f6c4b01/clickhouse_driver-0.2.9-cp39-cp39-win_amd64.whl", hash = "sha256:b243de483cfa02716053b0148d73558f4694f3c27b97fc1eaa97d7079563a14d", size = 214727, upload-time = "2024-08-16T18:07:29.695Z" },
- { url = "https://files.pythonhosted.org/packages/76/7a/961029af713ae42e43f2691abbcee248ed057229d24613d693260edbcb32/clickhouse_driver-0.2.9-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:45a3d5b1d06750fd6a18c29b871494a2635670099ec7693e756a5885a4a70dbf", size = 197672, upload-time = "2024-08-16T18:07:32.041Z" },
- { url = "https://files.pythonhosted.org/packages/5b/d4/1da419cb98e829bbacea4c07e184a747804f11543ff976c6539f4fbd62f2/clickhouse_driver-0.2.9-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8415ffebd6ca9eef3024763abc450f8659f1716d015bd563c537d01c7fbc3569", size = 220898, upload-time = "2024-08-16T18:07:34.029Z" },
- { url = "https://files.pythonhosted.org/packages/86/d6/f9042e885f4510ab3a09ef2f5d1acfd40f328794dff66ae12e52954cda04/clickhouse_driver-0.2.9-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ace48db993aa4bd31c42de0fa8d38c94ad47405916d6b61f7a7168a48fb52ac1", size = 228801, upload-time = "2024-08-16T18:07:35.959Z" },
- { url = "https://files.pythonhosted.org/packages/82/62/88ece5b7ca32d8a4bb4b1d0250cb54d1cfb1e7abfc759355d1fb51fda23a/clickhouse_driver-0.2.9-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b07123334fe143bfe6fa4e3d4b732d647d5fd2cfb9ec7f2f76104b46fe9d20c6", size = 234107, upload-time = "2024-08-16T18:07:38.296Z" },
- { url = "https://files.pythonhosted.org/packages/b2/ba/af473c973b853a9de1312d73f929f39e17424bd723966b398b898188a13a/clickhouse_driver-0.2.9-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:e2af3efa73d296420ce6362789f5b1febf75d4aa159a479393f01549115509d5", size = 199442, upload-time = "2024-08-16T18:07:41.879Z" },
- { url = "https://files.pythonhosted.org/packages/0b/e6/41fbf5c129af0c42260883d18563b6663e22578e79142ced138df339ad42/clickhouse_driver-0.2.9-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:7667ab423452754f36ba8fb41e006a46baace9c94e2aca2a745689b9f2753dfb", size = 197550, upload-time = "2024-08-16T18:08:13.673Z" },
- { url = "https://files.pythonhosted.org/packages/db/36/16409306bb69b2195cd20728c1a89c714c839e8df17a7e825de227fba58e/clickhouse_driver-0.2.9-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:653583b1f3b088d106f180d6f02c90917ecd669ec956b62903a05df4a7f44863", size = 220738, upload-time = "2024-08-16T18:08:15.968Z" },
- { url = "https://files.pythonhosted.org/packages/03/88/148b5a293bf3d716f0a58fc78000d29cca95950c4bbd9cd80b5d5c4bbb7f/clickhouse_driver-0.2.9-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ef3dd0cbdf2f0171caab90389af0ede068ec802bf46c6a77f14e6edc86671bc", size = 228544, upload-time = "2024-08-16T18:08:19.048Z" },
- { url = "https://files.pythonhosted.org/packages/44/2a/c5b29314d22b21eae39b4337207f1a65b519c2cf73df9fbb25fc60d77c99/clickhouse_driver-0.2.9-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:11b1833ee8ff8d5df39a34a895e060b57bd81e05ea68822bc60476daff4ce1c8", size = 233877, upload-time = "2024-08-16T18:08:21.9Z" },
- { url = "https://files.pythonhosted.org/packages/3a/d6/78f0e1ffeeb06f04ac0994362eca03cd0da42f52071241f00959699246ce/clickhouse_driver-0.2.9-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:8a3195639e6393b9d4aafe736036881ff86b6be5855d4bf7d9f5c31637181ec3", size = 199347, upload-time = "2024-08-16T18:08:25.106Z" },
]
[[package]]
@@ -718,256 +263,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
]
-[[package]]
-name = "contourpy"
-version = "1.3.0"
-source = { registry = "https://pypi.org/simple" }
-resolution-markers = [
- "python_full_version < '3.10'",
-]
-dependencies = [
- { name = "numpy", marker = "python_full_version < '3.10'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/f5/f6/31a8f28b4a2a4fa0e01085e542f3081ab0588eff8e589d39d775172c9792/contourpy-1.3.0.tar.gz", hash = "sha256:7ffa0db17717a8ffb127efd0c95a4362d996b892c2904db72428d5b52e1938a4", size = 13464370, upload-time = "2024-08-27T21:00:03.328Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/6c/e0/be8dcc796cfdd96708933e0e2da99ba4bb8f9b2caa9d560a50f3f09a65f3/contourpy-1.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:880ea32e5c774634f9fcd46504bf9f080a41ad855f4fef54f5380f5133d343c7", size = 265366, upload-time = "2024-08-27T20:50:09.947Z" },
- { url = "https://files.pythonhosted.org/packages/50/d6/c953b400219443535d412fcbbc42e7a5e823291236bc0bb88936e3cc9317/contourpy-1.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:76c905ef940a4474a6289c71d53122a4f77766eef23c03cd57016ce19d0f7b42", size = 249226, upload-time = "2024-08-27T20:50:16.1Z" },
- { url = "https://files.pythonhosted.org/packages/6f/b4/6fffdf213ffccc28483c524b9dad46bb78332851133b36ad354b856ddc7c/contourpy-1.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:92f8557cbb07415a4d6fa191f20fd9d2d9eb9c0b61d1b2f52a8926e43c6e9af7", size = 308460, upload-time = "2024-08-27T20:50:22.536Z" },
- { url = "https://files.pythonhosted.org/packages/cf/6c/118fc917b4050f0afe07179a6dcbe4f3f4ec69b94f36c9e128c4af480fb8/contourpy-1.3.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:36f965570cff02b874773c49bfe85562b47030805d7d8360748f3eca570f4cab", size = 347623, upload-time = "2024-08-27T20:50:28.806Z" },
- { url = "https://files.pythonhosted.org/packages/f9/a4/30ff110a81bfe3abf7b9673284d21ddce8cc1278f6f77393c91199da4c90/contourpy-1.3.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cacd81e2d4b6f89c9f8a5b69b86490152ff39afc58a95af002a398273e5ce589", size = 317761, upload-time = "2024-08-27T20:50:35.126Z" },
- { url = "https://files.pythonhosted.org/packages/99/e6/d11966962b1aa515f5586d3907ad019f4b812c04e4546cc19ebf62b5178e/contourpy-1.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:69375194457ad0fad3a839b9e29aa0b0ed53bb54db1bfb6c3ae43d111c31ce41", size = 322015, upload-time = "2024-08-27T20:50:40.318Z" },
- { url = "https://files.pythonhosted.org/packages/4d/e3/182383743751d22b7b59c3c753277b6aee3637049197624f333dac5b4c80/contourpy-1.3.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7a52040312b1a858b5e31ef28c2e865376a386c60c0e248370bbea2d3f3b760d", size = 1262672, upload-time = "2024-08-27T20:50:55.643Z" },
- { url = "https://files.pythonhosted.org/packages/78/53/974400c815b2e605f252c8fb9297e2204347d1755a5374354ee77b1ea259/contourpy-1.3.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3faeb2998e4fcb256542e8a926d08da08977f7f5e62cf733f3c211c2a5586223", size = 1321688, upload-time = "2024-08-27T20:51:11.293Z" },
- { url = "https://files.pythonhosted.org/packages/52/29/99f849faed5593b2926a68a31882af98afbeac39c7fdf7de491d9c85ec6a/contourpy-1.3.0-cp310-cp310-win32.whl", hash = "sha256:36e0cff201bcb17a0a8ecc7f454fe078437fa6bda730e695a92f2d9932bd507f", size = 171145, upload-time = "2024-08-27T20:51:15.2Z" },
- { url = "https://files.pythonhosted.org/packages/a9/97/3f89bba79ff6ff2b07a3cbc40aa693c360d5efa90d66e914f0ff03b95ec7/contourpy-1.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:87ddffef1dbe5e669b5c2440b643d3fdd8622a348fe1983fad7a0f0ccb1cd67b", size = 216019, upload-time = "2024-08-27T20:51:19.365Z" },
- { url = "https://files.pythonhosted.org/packages/b3/1f/9375917786cb39270b0ee6634536c0e22abf225825602688990d8f5c6c19/contourpy-1.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0fa4c02abe6c446ba70d96ece336e621efa4aecae43eaa9b030ae5fb92b309ad", size = 266356, upload-time = "2024-08-27T20:51:24.146Z" },
- { url = "https://files.pythonhosted.org/packages/05/46/9256dd162ea52790c127cb58cfc3b9e3413a6e3478917d1f811d420772ec/contourpy-1.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:834e0cfe17ba12f79963861e0f908556b2cedd52e1f75e6578801febcc6a9f49", size = 250915, upload-time = "2024-08-27T20:51:28.683Z" },
- { url = "https://files.pythonhosted.org/packages/e1/5d/3056c167fa4486900dfbd7e26a2fdc2338dc58eee36d490a0ed3ddda5ded/contourpy-1.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dbc4c3217eee163fa3984fd1567632b48d6dfd29216da3ded3d7b844a8014a66", size = 310443, upload-time = "2024-08-27T20:51:33.675Z" },
- { url = "https://files.pythonhosted.org/packages/ca/c2/1a612e475492e07f11c8e267ea5ec1ce0d89971be496c195e27afa97e14a/contourpy-1.3.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4865cd1d419e0c7a7bf6de1777b185eebdc51470800a9f42b9e9decf17762081", size = 348548, upload-time = "2024-08-27T20:51:39.322Z" },
- { url = "https://files.pythonhosted.org/packages/45/cf/2c2fc6bb5874158277b4faf136847f0689e1b1a1f640a36d76d52e78907c/contourpy-1.3.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:303c252947ab4b14c08afeb52375b26781ccd6a5ccd81abcdfc1fafd14cf93c1", size = 319118, upload-time = "2024-08-27T20:51:44.717Z" },
- { url = "https://files.pythonhosted.org/packages/03/33/003065374f38894cdf1040cef474ad0546368eea7e3a51d48b8a423961f8/contourpy-1.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:637f674226be46f6ba372fd29d9523dd977a291f66ab2a74fbeb5530bb3f445d", size = 323162, upload-time = "2024-08-27T20:51:49.683Z" },
- { url = "https://files.pythonhosted.org/packages/42/80/e637326e85e4105a802e42959f56cff2cd39a6b5ef68d5d9aee3ea5f0e4c/contourpy-1.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:76a896b2f195b57db25d6b44e7e03f221d32fe318d03ede41f8b4d9ba1bff53c", size = 1265396, upload-time = "2024-08-27T20:52:04.926Z" },
- { url = "https://files.pythonhosted.org/packages/7c/3b/8cbd6416ca1bbc0202b50f9c13b2e0b922b64be888f9d9ee88e6cfabfb51/contourpy-1.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e1fd23e9d01591bab45546c089ae89d926917a66dceb3abcf01f6105d927e2cb", size = 1324297, upload-time = "2024-08-27T20:52:21.843Z" },
- { url = "https://files.pythonhosted.org/packages/4d/2c/021a7afaa52fe891f25535506cc861c30c3c4e5a1c1ce94215e04b293e72/contourpy-1.3.0-cp311-cp311-win32.whl", hash = "sha256:d402880b84df3bec6eab53cd0cf802cae6a2ef9537e70cf75e91618a3801c20c", size = 171808, upload-time = "2024-08-27T20:52:25.163Z" },
- { url = "https://files.pythonhosted.org/packages/8d/2f/804f02ff30a7fae21f98198828d0857439ec4c91a96e20cf2d6c49372966/contourpy-1.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:6cb6cc968059db9c62cb35fbf70248f40994dfcd7aa10444bbf8b3faeb7c2d67", size = 217181, upload-time = "2024-08-27T20:52:29.13Z" },
- { url = "https://files.pythonhosted.org/packages/c9/92/8e0bbfe6b70c0e2d3d81272b58c98ac69ff1a4329f18c73bd64824d8b12e/contourpy-1.3.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:570ef7cf892f0afbe5b2ee410c507ce12e15a5fa91017a0009f79f7d93a1268f", size = 267838, upload-time = "2024-08-27T20:52:33.911Z" },
- { url = "https://files.pythonhosted.org/packages/e3/04/33351c5d5108460a8ce6d512307690b023f0cfcad5899499f5c83b9d63b1/contourpy-1.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:da84c537cb8b97d153e9fb208c221c45605f73147bd4cadd23bdae915042aad6", size = 251549, upload-time = "2024-08-27T20:52:39.179Z" },
- { url = "https://files.pythonhosted.org/packages/51/3d/aa0fe6ae67e3ef9f178389e4caaaa68daf2f9024092aa3c6032e3d174670/contourpy-1.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0be4d8425bfa755e0fd76ee1e019636ccc7c29f77a7c86b4328a9eb6a26d0639", size = 303177, upload-time = "2024-08-27T20:52:44.789Z" },
- { url = "https://files.pythonhosted.org/packages/56/c3/c85a7e3e0cab635575d3b657f9535443a6f5d20fac1a1911eaa4bbe1aceb/contourpy-1.3.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9c0da700bf58f6e0b65312d0a5e695179a71d0163957fa381bb3c1f72972537c", size = 341735, upload-time = "2024-08-27T20:52:51.05Z" },
- { url = "https://files.pythonhosted.org/packages/dd/8d/20f7a211a7be966a53f474bc90b1a8202e9844b3f1ef85f3ae45a77151ee/contourpy-1.3.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eb8b141bb00fa977d9122636b16aa67d37fd40a3d8b52dd837e536d64b9a4d06", size = 314679, upload-time = "2024-08-27T20:52:58.473Z" },
- { url = "https://files.pythonhosted.org/packages/6e/be/524e377567defac0e21a46e2a529652d165fed130a0d8a863219303cee18/contourpy-1.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3634b5385c6716c258d0419c46d05c8aa7dc8cb70326c9a4fb66b69ad2b52e09", size = 320549, upload-time = "2024-08-27T20:53:06.593Z" },
- { url = "https://files.pythonhosted.org/packages/0f/96/fdb2552a172942d888915f3a6663812e9bc3d359d53dafd4289a0fb462f0/contourpy-1.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0dce35502151b6bd35027ac39ba6e5a44be13a68f55735c3612c568cac3805fd", size = 1263068, upload-time = "2024-08-27T20:53:23.442Z" },
- { url = "https://files.pythonhosted.org/packages/2a/25/632eab595e3140adfa92f1322bf8915f68c932bac468e89eae9974cf1c00/contourpy-1.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:aea348f053c645100612b333adc5983d87be69acdc6d77d3169c090d3b01dc35", size = 1322833, upload-time = "2024-08-27T20:53:39.243Z" },
- { url = "https://files.pythonhosted.org/packages/73/e3/69738782e315a1d26d29d71a550dbbe3eb6c653b028b150f70c1a5f4f229/contourpy-1.3.0-cp312-cp312-win32.whl", hash = "sha256:90f73a5116ad1ba7174341ef3ea5c3150ddf20b024b98fb0c3b29034752c8aeb", size = 172681, upload-time = "2024-08-27T20:53:43.05Z" },
- { url = "https://files.pythonhosted.org/packages/0c/89/9830ba00d88e43d15e53d64931e66b8792b46eb25e2050a88fec4a0df3d5/contourpy-1.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:b11b39aea6be6764f84360fce6c82211a9db32a7c7de8fa6dd5397cf1d079c3b", size = 218283, upload-time = "2024-08-27T20:53:47.232Z" },
- { url = "https://files.pythonhosted.org/packages/53/a1/d20415febfb2267af2d7f06338e82171824d08614084714fb2c1dac9901f/contourpy-1.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3e1c7fa44aaae40a2247e2e8e0627f4bea3dd257014764aa644f319a5f8600e3", size = 267879, upload-time = "2024-08-27T20:53:51.597Z" },
- { url = "https://files.pythonhosted.org/packages/aa/45/5a28a3570ff6218d8bdfc291a272a20d2648104815f01f0177d103d985e1/contourpy-1.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:364174c2a76057feef647c802652f00953b575723062560498dc7930fc9b1cb7", size = 251573, upload-time = "2024-08-27T20:53:55.659Z" },
- { url = "https://files.pythonhosted.org/packages/39/1c/d3f51540108e3affa84f095c8b04f0aa833bb797bc8baa218a952a98117d/contourpy-1.3.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:32b238b3b3b649e09ce9aaf51f0c261d38644bdfa35cbaf7b263457850957a84", size = 303184, upload-time = "2024-08-27T20:54:00.225Z" },
- { url = "https://files.pythonhosted.org/packages/00/56/1348a44fb6c3a558c1a3a0cd23d329d604c99d81bf5a4b58c6b71aab328f/contourpy-1.3.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d51fca85f9f7ad0b65b4b9fe800406d0d77017d7270d31ec3fb1cc07358fdea0", size = 340262, upload-time = "2024-08-27T20:54:05.234Z" },
- { url = "https://files.pythonhosted.org/packages/2b/23/00d665ba67e1bb666152131da07e0f24c95c3632d7722caa97fb61470eca/contourpy-1.3.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:732896af21716b29ab3e988d4ce14bc5133733b85956316fb0c56355f398099b", size = 313806, upload-time = "2024-08-27T20:54:09.889Z" },
- { url = "https://files.pythonhosted.org/packages/5a/42/3cf40f7040bb8362aea19af9a5fb7b32ce420f645dd1590edcee2c657cd5/contourpy-1.3.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d73f659398a0904e125280836ae6f88ba9b178b2fed6884f3b1f95b989d2c8da", size = 319710, upload-time = "2024-08-27T20:54:14.536Z" },
- { url = "https://files.pythonhosted.org/packages/05/32/f3bfa3fc083b25e1a7ae09197f897476ee68e7386e10404bdf9aac7391f0/contourpy-1.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c6c7c2408b7048082932cf4e641fa3b8ca848259212f51c8c59c45aa7ac18f14", size = 1264107, upload-time = "2024-08-27T20:54:29.735Z" },
- { url = "https://files.pythonhosted.org/packages/1c/1e/1019d34473a736664f2439542b890b2dc4c6245f5c0d8cdfc0ccc2cab80c/contourpy-1.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f317576606de89da6b7e0861cf6061f6146ead3528acabff9236458a6ba467f8", size = 1322458, upload-time = "2024-08-27T20:54:45.507Z" },
- { url = "https://files.pythonhosted.org/packages/22/85/4f8bfd83972cf8909a4d36d16b177f7b8bdd942178ea4bf877d4a380a91c/contourpy-1.3.0-cp313-cp313-win32.whl", hash = "sha256:31cd3a85dbdf1fc002280c65caa7e2b5f65e4a973fcdf70dd2fdcb9868069294", size = 172643, upload-time = "2024-08-27T20:55:52.754Z" },
- { url = "https://files.pythonhosted.org/packages/cc/4a/fb3c83c1baba64ba90443626c228ca14f19a87c51975d3b1de308dd2cf08/contourpy-1.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:4553c421929ec95fb07b3aaca0fae668b2eb5a5203d1217ca7c34c063c53d087", size = 218301, upload-time = "2024-08-27T20:55:56.509Z" },
- { url = "https://files.pythonhosted.org/packages/76/65/702f4064f397821fea0cb493f7d3bc95a5d703e20954dce7d6d39bacf378/contourpy-1.3.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:345af746d7766821d05d72cb8f3845dfd08dd137101a2cb9b24de277d716def8", size = 278972, upload-time = "2024-08-27T20:54:50.347Z" },
- { url = "https://files.pythonhosted.org/packages/80/85/21f5bba56dba75c10a45ec00ad3b8190dbac7fd9a8a8c46c6116c933e9cf/contourpy-1.3.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3bb3808858a9dc68f6f03d319acd5f1b8a337e6cdda197f02f4b8ff67ad2057b", size = 263375, upload-time = "2024-08-27T20:54:54.909Z" },
- { url = "https://files.pythonhosted.org/packages/0a/64/084c86ab71d43149f91ab3a4054ccf18565f0a8af36abfa92b1467813ed6/contourpy-1.3.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:420d39daa61aab1221567b42eecb01112908b2cab7f1b4106a52caaec8d36973", size = 307188, upload-time = "2024-08-27T20:55:00.184Z" },
- { url = "https://files.pythonhosted.org/packages/3d/ff/d61a4c288dc42da0084b8d9dc2aa219a850767165d7d9a9c364ff530b509/contourpy-1.3.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4d63ee447261e963af02642ffcb864e5a2ee4cbfd78080657a9880b8b1868e18", size = 345644, upload-time = "2024-08-27T20:55:05.673Z" },
- { url = "https://files.pythonhosted.org/packages/ca/aa/00d2313d35ec03f188e8f0786c2fc61f589306e02fdc158233697546fd58/contourpy-1.3.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:167d6c890815e1dac9536dca00828b445d5d0df4d6a8c6adb4a7ec3166812fa8", size = 317141, upload-time = "2024-08-27T20:55:11.047Z" },
- { url = "https://files.pythonhosted.org/packages/8d/6a/b5242c8cb32d87f6abf4f5e3044ca397cb1a76712e3fa2424772e3ff495f/contourpy-1.3.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:710a26b3dc80c0e4febf04555de66f5fd17e9cf7170a7b08000601a10570bda6", size = 323469, upload-time = "2024-08-27T20:55:15.914Z" },
- { url = "https://files.pythonhosted.org/packages/6f/a6/73e929d43028a9079aca4bde107494864d54f0d72d9db508a51ff0878593/contourpy-1.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:75ee7cb1a14c617f34a51d11fa7524173e56551646828353c4af859c56b766e2", size = 1260894, upload-time = "2024-08-27T20:55:31.553Z" },
- { url = "https://files.pythonhosted.org/packages/2b/1e/1e726ba66eddf21c940821df8cf1a7d15cb165f0682d62161eaa5e93dae1/contourpy-1.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:33c92cdae89ec5135d036e7218e69b0bb2851206077251f04a6c4e0e21f03927", size = 1314829, upload-time = "2024-08-27T20:55:47.837Z" },
- { url = "https://files.pythonhosted.org/packages/b3/e3/b9f72758adb6ef7397327ceb8b9c39c75711affb220e4f53c745ea1d5a9a/contourpy-1.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a11077e395f67ffc2c44ec2418cfebed032cd6da3022a94fc227b6faf8e2acb8", size = 265518, upload-time = "2024-08-27T20:56:01.333Z" },
- { url = "https://files.pythonhosted.org/packages/ec/22/19f5b948367ab5260fb41d842c7a78dae645603881ea6bc39738bcfcabf6/contourpy-1.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e8134301d7e204c88ed7ab50028ba06c683000040ede1d617298611f9dc6240c", size = 249350, upload-time = "2024-08-27T20:56:05.432Z" },
- { url = "https://files.pythonhosted.org/packages/26/76/0c7d43263dd00ae21a91a24381b7e813d286a3294d95d179ef3a7b9fb1d7/contourpy-1.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e12968fdfd5bb45ffdf6192a590bd8ddd3ba9e58360b29683c6bb71a7b41edca", size = 309167, upload-time = "2024-08-27T20:56:10.034Z" },
- { url = "https://files.pythonhosted.org/packages/96/3b/cadff6773e89f2a5a492c1a8068e21d3fccaf1a1c1df7d65e7c8e3ef60ba/contourpy-1.3.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fd2a0fc506eccaaa7595b7e1418951f213cf8255be2600f1ea1b61e46a60c55f", size = 348279, upload-time = "2024-08-27T20:56:15.41Z" },
- { url = "https://files.pythonhosted.org/packages/e1/86/158cc43aa549d2081a955ab11c6bdccc7a22caacc2af93186d26f5f48746/contourpy-1.3.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4cfb5c62ce023dfc410d6059c936dcf96442ba40814aefbfa575425a3a7f19dc", size = 318519, upload-time = "2024-08-27T20:56:21.813Z" },
- { url = "https://files.pythonhosted.org/packages/05/11/57335544a3027e9b96a05948c32e566328e3a2f84b7b99a325b7a06d2b06/contourpy-1.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:68a32389b06b82c2fdd68276148d7b9275b5f5cf13e5417e4252f6d1a34f72a2", size = 321922, upload-time = "2024-08-27T20:56:26.983Z" },
- { url = "https://files.pythonhosted.org/packages/0b/e3/02114f96543f4a1b694333b92a6dcd4f8eebbefcc3a5f3bbb1316634178f/contourpy-1.3.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:94e848a6b83da10898cbf1311a815f770acc9b6a3f2d646f330d57eb4e87592e", size = 1258017, upload-time = "2024-08-27T20:56:42.246Z" },
- { url = "https://files.pythonhosted.org/packages/f3/3b/bfe4c81c6d5881c1c643dde6620be0b42bf8aab155976dd644595cfab95c/contourpy-1.3.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:d78ab28a03c854a873787a0a42254a0ccb3cb133c672f645c9f9c8f3ae9d0800", size = 1316773, upload-time = "2024-08-27T20:56:58.58Z" },
- { url = "https://files.pythonhosted.org/packages/f1/17/c52d2970784383cafb0bd918b6fb036d98d96bbf0bc1befb5d1e31a07a70/contourpy-1.3.0-cp39-cp39-win32.whl", hash = "sha256:81cb5ed4952aae6014bc9d0421dec7c5835c9c8c31cdf51910b708f548cf58e5", size = 171353, upload-time = "2024-08-27T20:57:02.718Z" },
- { url = "https://files.pythonhosted.org/packages/53/23/db9f69676308e094d3c45f20cc52e12d10d64f027541c995d89c11ad5c75/contourpy-1.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:14e262f67bd7e6eb6880bc564dcda30b15e351a594657e55b7eec94b6ef72843", size = 211817, upload-time = "2024-08-27T20:57:06.328Z" },
- { url = "https://files.pythonhosted.org/packages/d1/09/60e486dc2b64c94ed33e58dcfb6f808192c03dfc5574c016218b9b7680dc/contourpy-1.3.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:fe41b41505a5a33aeaed2a613dccaeaa74e0e3ead6dd6fd3a118fb471644fd6c", size = 261886, upload-time = "2024-08-27T20:57:10.863Z" },
- { url = "https://files.pythonhosted.org/packages/19/20/b57f9f7174fcd439a7789fb47d764974ab646fa34d1790551de386457a8e/contourpy-1.3.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eca7e17a65f72a5133bdbec9ecf22401c62bcf4821361ef7811faee695799779", size = 311008, upload-time = "2024-08-27T20:57:15.588Z" },
- { url = "https://files.pythonhosted.org/packages/74/fc/5040d42623a1845d4f17a418e590fd7a79ae8cb2bad2b2f83de63c3bdca4/contourpy-1.3.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:1ec4dc6bf570f5b22ed0d7efba0dfa9c5b9e0431aeea7581aa217542d9e809a4", size = 215690, upload-time = "2024-08-27T20:57:19.321Z" },
- { url = "https://files.pythonhosted.org/packages/2b/24/dc3dcd77ac7460ab7e9d2b01a618cb31406902e50e605a8d6091f0a8f7cc/contourpy-1.3.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:00ccd0dbaad6d804ab259820fa7cb0b8036bda0686ef844d24125d8287178ce0", size = 261894, upload-time = "2024-08-27T20:57:23.873Z" },
- { url = "https://files.pythonhosted.org/packages/b1/db/531642a01cfec39d1682e46b5457b07cf805e3c3c584ec27e2a6223f8f6c/contourpy-1.3.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ca947601224119117f7c19c9cdf6b3ab54c5726ef1d906aa4a69dfb6dd58102", size = 311099, upload-time = "2024-08-27T20:57:28.58Z" },
- { url = "https://files.pythonhosted.org/packages/38/1e/94bda024d629f254143a134eead69e21c836429a2a6ce82209a00ddcb79a/contourpy-1.3.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:c6ec93afeb848a0845a18989da3beca3eec2c0f852322efe21af1931147d12cb", size = 215838, upload-time = "2024-08-27T20:57:32.913Z" },
-]
-
-[[package]]
-name = "contourpy"
-version = "1.3.2"
-source = { registry = "https://pypi.org/simple" }
-resolution-markers = [
- "python_full_version == '3.10.*'",
-]
-dependencies = [
- { name = "numpy", marker = "python_full_version == '3.10.*'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/66/54/eb9bfc647b19f2009dd5c7f5ec51c4e6ca831725f1aea7a993034f483147/contourpy-1.3.2.tar.gz", hash = "sha256:b6945942715a034c671b7fc54f9588126b0b8bf23db2696e3ca8328f3ff0ab54", size = 13466130, upload-time = "2025-04-15T17:47:53.79Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/12/a3/da4153ec8fe25d263aa48c1a4cbde7f49b59af86f0b6f7862788c60da737/contourpy-1.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ba38e3f9f330af820c4b27ceb4b9c7feee5fe0493ea53a8720f4792667465934", size = 268551, upload-time = "2025-04-15T17:34:46.581Z" },
- { url = "https://files.pythonhosted.org/packages/2f/6c/330de89ae1087eb622bfca0177d32a7ece50c3ef07b28002de4757d9d875/contourpy-1.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dc41ba0714aa2968d1f8674ec97504a8f7e334f48eeacebcaa6256213acb0989", size = 253399, upload-time = "2025-04-15T17:34:51.427Z" },
- { url = "https://files.pythonhosted.org/packages/c1/bd/20c6726b1b7f81a8bee5271bed5c165f0a8e1f572578a9d27e2ccb763cb2/contourpy-1.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9be002b31c558d1ddf1b9b415b162c603405414bacd6932d031c5b5a8b757f0d", size = 312061, upload-time = "2025-04-15T17:34:55.961Z" },
- { url = "https://files.pythonhosted.org/packages/22/fc/a9665c88f8a2473f823cf1ec601de9e5375050f1958cbb356cdf06ef1ab6/contourpy-1.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8d2e74acbcba3bfdb6d9d8384cdc4f9260cae86ed9beee8bd5f54fee49a430b9", size = 351956, upload-time = "2025-04-15T17:35:00.992Z" },
- { url = "https://files.pythonhosted.org/packages/25/eb/9f0a0238f305ad8fb7ef42481020d6e20cf15e46be99a1fcf939546a177e/contourpy-1.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e259bced5549ac64410162adc973c5e2fb77f04df4a439d00b478e57a0e65512", size = 320872, upload-time = "2025-04-15T17:35:06.177Z" },
- { url = "https://files.pythonhosted.org/packages/32/5c/1ee32d1c7956923202f00cf8d2a14a62ed7517bdc0ee1e55301227fc273c/contourpy-1.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad687a04bc802cbe8b9c399c07162a3c35e227e2daccf1668eb1f278cb698631", size = 325027, upload-time = "2025-04-15T17:35:11.244Z" },
- { url = "https://files.pythonhosted.org/packages/83/bf/9baed89785ba743ef329c2b07fd0611d12bfecbedbdd3eeecf929d8d3b52/contourpy-1.3.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cdd22595308f53ef2f891040ab2b93d79192513ffccbd7fe19be7aa773a5e09f", size = 1306641, upload-time = "2025-04-15T17:35:26.701Z" },
- { url = "https://files.pythonhosted.org/packages/d4/cc/74e5e83d1e35de2d28bd97033426b450bc4fd96e092a1f7a63dc7369b55d/contourpy-1.3.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b4f54d6a2defe9f257327b0f243612dd051cc43825587520b1bf74a31e2f6ef2", size = 1374075, upload-time = "2025-04-15T17:35:43.204Z" },
- { url = "https://files.pythonhosted.org/packages/0c/42/17f3b798fd5e033b46a16f8d9fcb39f1aba051307f5ebf441bad1ecf78f8/contourpy-1.3.2-cp310-cp310-win32.whl", hash = "sha256:f939a054192ddc596e031e50bb13b657ce318cf13d264f095ce9db7dc6ae81c0", size = 177534, upload-time = "2025-04-15T17:35:46.554Z" },
- { url = "https://files.pythonhosted.org/packages/54/ec/5162b8582f2c994721018d0c9ece9dc6ff769d298a8ac6b6a652c307e7df/contourpy-1.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:c440093bbc8fc21c637c03bafcbef95ccd963bc6e0514ad887932c18ca2a759a", size = 221188, upload-time = "2025-04-15T17:35:50.064Z" },
- { url = "https://files.pythonhosted.org/packages/b3/b9/ede788a0b56fc5b071639d06c33cb893f68b1178938f3425debebe2dab78/contourpy-1.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6a37a2fb93d4df3fc4c0e363ea4d16f83195fc09c891bc8ce072b9d084853445", size = 269636, upload-time = "2025-04-15T17:35:54.473Z" },
- { url = "https://files.pythonhosted.org/packages/e6/75/3469f011d64b8bbfa04f709bfc23e1dd71be54d05b1b083be9f5b22750d1/contourpy-1.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b7cd50c38f500bbcc9b6a46643a40e0913673f869315d8e70de0438817cb7773", size = 254636, upload-time = "2025-04-15T17:35:58.283Z" },
- { url = "https://files.pythonhosted.org/packages/8d/2f/95adb8dae08ce0ebca4fd8e7ad653159565d9739128b2d5977806656fcd2/contourpy-1.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d6658ccc7251a4433eebd89ed2672c2ed96fba367fd25ca9512aa92a4b46c4f1", size = 313053, upload-time = "2025-04-15T17:36:03.235Z" },
- { url = "https://files.pythonhosted.org/packages/c3/a6/8ccf97a50f31adfa36917707fe39c9a0cbc24b3bbb58185577f119736cc9/contourpy-1.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:70771a461aaeb335df14deb6c97439973d253ae70660ca085eec25241137ef43", size = 352985, upload-time = "2025-04-15T17:36:08.275Z" },
- { url = "https://files.pythonhosted.org/packages/1d/b6/7925ab9b77386143f39d9c3243fdd101621b4532eb126743201160ffa7e6/contourpy-1.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65a887a6e8c4cd0897507d814b14c54a8c2e2aa4ac9f7686292f9769fcf9a6ab", size = 323750, upload-time = "2025-04-15T17:36:13.29Z" },
- { url = "https://files.pythonhosted.org/packages/c2/f3/20c5d1ef4f4748e52d60771b8560cf00b69d5c6368b5c2e9311bcfa2a08b/contourpy-1.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3859783aefa2b8355697f16642695a5b9792e7a46ab86da1118a4a23a51a33d7", size = 326246, upload-time = "2025-04-15T17:36:18.329Z" },
- { url = "https://files.pythonhosted.org/packages/8c/e5/9dae809e7e0b2d9d70c52b3d24cba134dd3dad979eb3e5e71f5df22ed1f5/contourpy-1.3.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:eab0f6db315fa4d70f1d8ab514e527f0366ec021ff853d7ed6a2d33605cf4b83", size = 1308728, upload-time = "2025-04-15T17:36:33.878Z" },
- { url = "https://files.pythonhosted.org/packages/e2/4a/0058ba34aeea35c0b442ae61a4f4d4ca84d6df8f91309bc2d43bb8dd248f/contourpy-1.3.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d91a3ccc7fea94ca0acab82ceb77f396d50a1f67412efe4c526f5d20264e6ecd", size = 1375762, upload-time = "2025-04-15T17:36:51.295Z" },
- { url = "https://files.pythonhosted.org/packages/09/33/7174bdfc8b7767ef2c08ed81244762d93d5c579336fc0b51ca57b33d1b80/contourpy-1.3.2-cp311-cp311-win32.whl", hash = "sha256:1c48188778d4d2f3d48e4643fb15d8608b1d01e4b4d6b0548d9b336c28fc9b6f", size = 178196, upload-time = "2025-04-15T17:36:55.002Z" },
- { url = "https://files.pythonhosted.org/packages/5e/fe/4029038b4e1c4485cef18e480b0e2cd2d755448bb071eb9977caac80b77b/contourpy-1.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:5ebac872ba09cb8f2131c46b8739a7ff71de28a24c869bcad554477eb089a878", size = 222017, upload-time = "2025-04-15T17:36:58.576Z" },
- { url = "https://files.pythonhosted.org/packages/34/f7/44785876384eff370c251d58fd65f6ad7f39adce4a093c934d4a67a7c6b6/contourpy-1.3.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4caf2bcd2969402bf77edc4cb6034c7dd7c0803213b3523f111eb7460a51b8d2", size = 271580, upload-time = "2025-04-15T17:37:03.105Z" },
- { url = "https://files.pythonhosted.org/packages/93/3b/0004767622a9826ea3d95f0e9d98cd8729015768075d61f9fea8eeca42a8/contourpy-1.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:82199cb78276249796419fe36b7386bd8d2cc3f28b3bc19fe2454fe2e26c4c15", size = 255530, upload-time = "2025-04-15T17:37:07.026Z" },
- { url = "https://files.pythonhosted.org/packages/e7/bb/7bd49e1f4fa805772d9fd130e0d375554ebc771ed7172f48dfcd4ca61549/contourpy-1.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:106fab697af11456fcba3e352ad50effe493a90f893fca6c2ca5c033820cea92", size = 307688, upload-time = "2025-04-15T17:37:11.481Z" },
- { url = "https://files.pythonhosted.org/packages/fc/97/e1d5dbbfa170725ef78357a9a0edc996b09ae4af170927ba8ce977e60a5f/contourpy-1.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d14f12932a8d620e307f715857107b1d1845cc44fdb5da2bc8e850f5ceba9f87", size = 347331, upload-time = "2025-04-15T17:37:18.212Z" },
- { url = "https://files.pythonhosted.org/packages/6f/66/e69e6e904f5ecf6901be3dd16e7e54d41b6ec6ae3405a535286d4418ffb4/contourpy-1.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:532fd26e715560721bb0d5fc7610fce279b3699b018600ab999d1be895b09415", size = 318963, upload-time = "2025-04-15T17:37:22.76Z" },
- { url = "https://files.pythonhosted.org/packages/a8/32/b8a1c8965e4f72482ff2d1ac2cd670ce0b542f203c8e1d34e7c3e6925da7/contourpy-1.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f26b383144cf2d2c29f01a1e8170f50dacf0eac02d64139dcd709a8ac4eb3cfe", size = 323681, upload-time = "2025-04-15T17:37:33.001Z" },
- { url = "https://files.pythonhosted.org/packages/30/c6/12a7e6811d08757c7162a541ca4c5c6a34c0f4e98ef2b338791093518e40/contourpy-1.3.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c49f73e61f1f774650a55d221803b101d966ca0c5a2d6d5e4320ec3997489441", size = 1308674, upload-time = "2025-04-15T17:37:48.64Z" },
- { url = "https://files.pythonhosted.org/packages/2a/8a/bebe5a3f68b484d3a2b8ffaf84704b3e343ef1addea528132ef148e22b3b/contourpy-1.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3d80b2c0300583228ac98d0a927a1ba6a2ba6b8a742463c564f1d419ee5b211e", size = 1380480, upload-time = "2025-04-15T17:38:06.7Z" },
- { url = "https://files.pythonhosted.org/packages/34/db/fcd325f19b5978fb509a7d55e06d99f5f856294c1991097534360b307cf1/contourpy-1.3.2-cp312-cp312-win32.whl", hash = "sha256:90df94c89a91b7362e1142cbee7568f86514412ab8a2c0d0fca72d7e91b62912", size = 178489, upload-time = "2025-04-15T17:38:10.338Z" },
- { url = "https://files.pythonhosted.org/packages/01/c8/fadd0b92ffa7b5eb5949bf340a63a4a496a6930a6c37a7ba0f12acb076d6/contourpy-1.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:8c942a01d9163e2e5cfb05cb66110121b8d07ad438a17f9e766317bcb62abf73", size = 223042, upload-time = "2025-04-15T17:38:14.239Z" },
- { url = "https://files.pythonhosted.org/packages/2e/61/5673f7e364b31e4e7ef6f61a4b5121c5f170f941895912f773d95270f3a2/contourpy-1.3.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:de39db2604ae755316cb5967728f4bea92685884b1e767b7c24e983ef5f771cb", size = 271630, upload-time = "2025-04-15T17:38:19.142Z" },
- { url = "https://files.pythonhosted.org/packages/ff/66/a40badddd1223822c95798c55292844b7e871e50f6bfd9f158cb25e0bd39/contourpy-1.3.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3f9e896f447c5c8618f1edb2bafa9a4030f22a575ec418ad70611450720b5b08", size = 255670, upload-time = "2025-04-15T17:38:23.688Z" },
- { url = "https://files.pythonhosted.org/packages/1e/c7/cf9fdee8200805c9bc3b148f49cb9482a4e3ea2719e772602a425c9b09f8/contourpy-1.3.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71e2bd4a1c4188f5c2b8d274da78faab884b59df20df63c34f74aa1813c4427c", size = 306694, upload-time = "2025-04-15T17:38:28.238Z" },
- { url = "https://files.pythonhosted.org/packages/dd/e7/ccb9bec80e1ba121efbffad7f38021021cda5be87532ec16fd96533bb2e0/contourpy-1.3.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de425af81b6cea33101ae95ece1f696af39446db9682a0b56daaa48cfc29f38f", size = 345986, upload-time = "2025-04-15T17:38:33.502Z" },
- { url = "https://files.pythonhosted.org/packages/dc/49/ca13bb2da90391fa4219fdb23b078d6065ada886658ac7818e5441448b78/contourpy-1.3.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:977e98a0e0480d3fe292246417239d2d45435904afd6d7332d8455981c408b85", size = 318060, upload-time = "2025-04-15T17:38:38.672Z" },
- { url = "https://files.pythonhosted.org/packages/c8/65/5245ce8c548a8422236c13ffcdcdada6a2a812c361e9e0c70548bb40b661/contourpy-1.3.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:434f0adf84911c924519d2b08fc10491dd282b20bdd3fa8f60fd816ea0b48841", size = 322747, upload-time = "2025-04-15T17:38:43.712Z" },
- { url = "https://files.pythonhosted.org/packages/72/30/669b8eb48e0a01c660ead3752a25b44fdb2e5ebc13a55782f639170772f9/contourpy-1.3.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c66c4906cdbc50e9cba65978823e6e00b45682eb09adbb78c9775b74eb222422", size = 1308895, upload-time = "2025-04-15T17:39:00.224Z" },
- { url = "https://files.pythonhosted.org/packages/05/5a/b569f4250decee6e8d54498be7bdf29021a4c256e77fe8138c8319ef8eb3/contourpy-1.3.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8b7fc0cd78ba2f4695fd0a6ad81a19e7e3ab825c31b577f384aa9d7817dc3bef", size = 1379098, upload-time = "2025-04-15T17:43:29.649Z" },
- { url = "https://files.pythonhosted.org/packages/19/ba/b227c3886d120e60e41b28740ac3617b2f2b971b9f601c835661194579f1/contourpy-1.3.2-cp313-cp313-win32.whl", hash = "sha256:15ce6ab60957ca74cff444fe66d9045c1fd3e92c8936894ebd1f3eef2fff075f", size = 178535, upload-time = "2025-04-15T17:44:44.532Z" },
- { url = "https://files.pythonhosted.org/packages/12/6e/2fed56cd47ca739b43e892707ae9a13790a486a3173be063681ca67d2262/contourpy-1.3.2-cp313-cp313-win_amd64.whl", hash = "sha256:e1578f7eafce927b168752ed7e22646dad6cd9bca673c60bff55889fa236ebf9", size = 223096, upload-time = "2025-04-15T17:44:48.194Z" },
- { url = "https://files.pythonhosted.org/packages/54/4c/e76fe2a03014a7c767d79ea35c86a747e9325537a8b7627e0e5b3ba266b4/contourpy-1.3.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0475b1f6604896bc7c53bb070e355e9321e1bc0d381735421a2d2068ec56531f", size = 285090, upload-time = "2025-04-15T17:43:34.084Z" },
- { url = "https://files.pythonhosted.org/packages/7b/e2/5aba47debd55d668e00baf9651b721e7733975dc9fc27264a62b0dd26eb8/contourpy-1.3.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:c85bb486e9be652314bb5b9e2e3b0d1b2e643d5eec4992c0fbe8ac71775da739", size = 268643, upload-time = "2025-04-15T17:43:38.626Z" },
- { url = "https://files.pythonhosted.org/packages/a1/37/cd45f1f051fe6230f751cc5cdd2728bb3a203f5619510ef11e732109593c/contourpy-1.3.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:745b57db7758f3ffc05a10254edd3182a2a83402a89c00957a8e8a22f5582823", size = 310443, upload-time = "2025-04-15T17:43:44.522Z" },
- { url = "https://files.pythonhosted.org/packages/8b/a2/36ea6140c306c9ff6dd38e3bcec80b3b018474ef4d17eb68ceecd26675f4/contourpy-1.3.2-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:970e9173dbd7eba9b4e01aab19215a48ee5dd3f43cef736eebde064a171f89a5", size = 349865, upload-time = "2025-04-15T17:43:49.545Z" },
- { url = "https://files.pythonhosted.org/packages/95/b7/2fc76bc539693180488f7b6cc518da7acbbb9e3b931fd9280504128bf956/contourpy-1.3.2-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c6c4639a9c22230276b7bffb6a850dfc8258a2521305e1faefe804d006b2e532", size = 321162, upload-time = "2025-04-15T17:43:54.203Z" },
- { url = "https://files.pythonhosted.org/packages/f4/10/76d4f778458b0aa83f96e59d65ece72a060bacb20cfbee46cf6cd5ceba41/contourpy-1.3.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc829960f34ba36aad4302e78eabf3ef16a3a100863f0d4eeddf30e8a485a03b", size = 327355, upload-time = "2025-04-15T17:44:01.025Z" },
- { url = "https://files.pythonhosted.org/packages/43/a3/10cf483ea683f9f8ab096c24bad3cce20e0d1dd9a4baa0e2093c1c962d9d/contourpy-1.3.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:d32530b534e986374fc19eaa77fcb87e8a99e5431499949b828312bdcd20ac52", size = 1307935, upload-time = "2025-04-15T17:44:17.322Z" },
- { url = "https://files.pythonhosted.org/packages/78/73/69dd9a024444489e22d86108e7b913f3528f56cfc312b5c5727a44188471/contourpy-1.3.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:e298e7e70cf4eb179cc1077be1c725b5fd131ebc81181bf0c03525c8abc297fd", size = 1372168, upload-time = "2025-04-15T17:44:33.43Z" },
- { url = "https://files.pythonhosted.org/packages/0f/1b/96d586ccf1b1a9d2004dd519b25fbf104a11589abfd05484ff12199cca21/contourpy-1.3.2-cp313-cp313t-win32.whl", hash = "sha256:d0e589ae0d55204991450bb5c23f571c64fe43adaa53f93fc902a84c96f52fe1", size = 189550, upload-time = "2025-04-15T17:44:37.092Z" },
- { url = "https://files.pythonhosted.org/packages/b0/e6/6000d0094e8a5e32ad62591c8609e269febb6e4db83a1c75ff8868b42731/contourpy-1.3.2-cp313-cp313t-win_amd64.whl", hash = "sha256:78e9253c3de756b3f6a5174d024c4835acd59eb3f8e2ca13e775dbffe1558f69", size = 238214, upload-time = "2025-04-15T17:44:40.827Z" },
- { url = "https://files.pythonhosted.org/packages/33/05/b26e3c6ecc05f349ee0013f0bb850a761016d89cec528a98193a48c34033/contourpy-1.3.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:fd93cc7f3139b6dd7aab2f26a90dde0aa9fc264dbf70f6740d498a70b860b82c", size = 265681, upload-time = "2025-04-15T17:44:59.314Z" },
- { url = "https://files.pythonhosted.org/packages/2b/25/ac07d6ad12affa7d1ffed11b77417d0a6308170f44ff20fa1d5aa6333f03/contourpy-1.3.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:107ba8a6a7eec58bb475329e6d3b95deba9440667c4d62b9b6063942b61d7f16", size = 315101, upload-time = "2025-04-15T17:45:04.165Z" },
- { url = "https://files.pythonhosted.org/packages/8f/4d/5bb3192bbe9d3f27e3061a6a8e7733c9120e203cb8515767d30973f71030/contourpy-1.3.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ded1706ed0c1049224531b81128efbd5084598f18d8a2d9efae833edbd2b40ad", size = 220599, upload-time = "2025-04-15T17:45:08.456Z" },
- { url = "https://files.pythonhosted.org/packages/ff/c0/91f1215d0d9f9f343e4773ba6c9b89e8c0cc7a64a6263f21139da639d848/contourpy-1.3.2-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5f5964cdad279256c084b69c3f412b7801e15356b16efa9d78aa974041903da0", size = 266807, upload-time = "2025-04-15T17:45:15.535Z" },
- { url = "https://files.pythonhosted.org/packages/d4/79/6be7e90c955c0487e7712660d6cead01fa17bff98e0ea275737cc2bc8e71/contourpy-1.3.2-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49b65a95d642d4efa8f64ba12558fcb83407e58a2dfba9d796d77b63ccfcaff5", size = 318729, upload-time = "2025-04-15T17:45:20.166Z" },
- { url = "https://files.pythonhosted.org/packages/87/68/7f46fb537958e87427d98a4074bcde4b67a70b04900cfc5ce29bc2f556c1/contourpy-1.3.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:8c5acb8dddb0752bf252e01a3035b21443158910ac16a3b0d20e7fed7d534ce5", size = 221791, upload-time = "2025-04-15T17:45:24.794Z" },
-]
-
-[[package]]
-name = "contourpy"
-version = "1.3.3"
-source = { registry = "https://pypi.org/simple" }
-resolution-markers = [
- "python_full_version >= '3.13'",
- "python_full_version >= '3.12.4' and python_full_version < '3.13'",
- "python_full_version >= '3.12' and python_full_version < '3.12.4'",
- "python_full_version == '3.11.*'",
-]
-dependencies = [
- { name = "numpy", marker = "python_full_version >= '3.11'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/58/01/1253e6698a07380cd31a736d248a3f2a50a7c88779a1813da27503cadc2a/contourpy-1.3.3.tar.gz", hash = "sha256:083e12155b210502d0bca491432bb04d56dc3432f95a979b429f2848c3dbe880", size = 13466174, upload-time = "2025-07-26T12:03:12.549Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/91/2e/c4390a31919d8a78b90e8ecf87cd4b4c4f05a5b48d05ec17db8e5404c6f4/contourpy-1.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:709a48ef9a690e1343202916450bc48b9e51c049b089c7f79a267b46cffcdaa1", size = 288773, upload-time = "2025-07-26T12:01:02.277Z" },
- { url = "https://files.pythonhosted.org/packages/0d/44/c4b0b6095fef4dc9c420e041799591e3b63e9619e3044f7f4f6c21c0ab24/contourpy-1.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:23416f38bfd74d5d28ab8429cc4d63fa67d5068bd711a85edb1c3fb0c3e2f381", size = 270149, upload-time = "2025-07-26T12:01:04.072Z" },
- { url = "https://files.pythonhosted.org/packages/30/2e/dd4ced42fefac8470661d7cb7e264808425e6c5d56d175291e93890cce09/contourpy-1.3.3-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:929ddf8c4c7f348e4c0a5a3a714b5c8542ffaa8c22954862a46ca1813b667ee7", size = 329222, upload-time = "2025-07-26T12:01:05.688Z" },
- { url = "https://files.pythonhosted.org/packages/f2/74/cc6ec2548e3d276c71389ea4802a774b7aa3558223b7bade3f25787fafc2/contourpy-1.3.3-cp311-cp311-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9e999574eddae35f1312c2b4b717b7885d4edd6cb46700e04f7f02db454e67c1", size = 377234, upload-time = "2025-07-26T12:01:07.054Z" },
- { url = "https://files.pythonhosted.org/packages/03/b3/64ef723029f917410f75c09da54254c5f9ea90ef89b143ccadb09df14c15/contourpy-1.3.3-cp311-cp311-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0bf67e0e3f482cb69779dd3061b534eb35ac9b17f163d851e2a547d56dba0a3a", size = 380555, upload-time = "2025-07-26T12:01:08.801Z" },
- { url = "https://files.pythonhosted.org/packages/5f/4b/6157f24ca425b89fe2eb7e7be642375711ab671135be21e6faa100f7448c/contourpy-1.3.3-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:51e79c1f7470158e838808d4a996fa9bac72c498e93d8ebe5119bc1e6becb0db", size = 355238, upload-time = "2025-07-26T12:01:10.319Z" },
- { url = "https://files.pythonhosted.org/packages/98/56/f914f0dd678480708a04cfd2206e7c382533249bc5001eb9f58aa693e200/contourpy-1.3.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:598c3aaece21c503615fd59c92a3598b428b2f01bfb4b8ca9c4edeecc2438620", size = 1326218, upload-time = "2025-07-26T12:01:12.659Z" },
- { url = "https://files.pythonhosted.org/packages/fb/d7/4a972334a0c971acd5172389671113ae82aa7527073980c38d5868ff1161/contourpy-1.3.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:322ab1c99b008dad206d406bb61d014cf0174df491ae9d9d0fac6a6fda4f977f", size = 1392867, upload-time = "2025-07-26T12:01:15.533Z" },
- { url = "https://files.pythonhosted.org/packages/75/3e/f2cc6cd56dc8cff46b1a56232eabc6feea52720083ea71ab15523daab796/contourpy-1.3.3-cp311-cp311-win32.whl", hash = "sha256:fd907ae12cd483cd83e414b12941c632a969171bf90fc937d0c9f268a31cafff", size = 183677, upload-time = "2025-07-26T12:01:17.088Z" },
- { url = "https://files.pythonhosted.org/packages/98/4b/9bd370b004b5c9d8045c6c33cf65bae018b27aca550a3f657cdc99acdbd8/contourpy-1.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:3519428f6be58431c56581f1694ba8e50626f2dd550af225f82fb5f5814d2a42", size = 225234, upload-time = "2025-07-26T12:01:18.256Z" },
- { url = "https://files.pythonhosted.org/packages/d9/b6/71771e02c2e004450c12b1120a5f488cad2e4d5b590b1af8bad060360fe4/contourpy-1.3.3-cp311-cp311-win_arm64.whl", hash = "sha256:15ff10bfada4bf92ec8b31c62bf7c1834c244019b4a33095a68000d7075df470", size = 193123, upload-time = "2025-07-26T12:01:19.848Z" },
- { url = "https://files.pythonhosted.org/packages/be/45/adfee365d9ea3d853550b2e735f9d66366701c65db7855cd07621732ccfc/contourpy-1.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b08a32ea2f8e42cf1d4be3169a98dd4be32bafe4f22b6c4cb4ba810fa9e5d2cb", size = 293419, upload-time = "2025-07-26T12:01:21.16Z" },
- { url = "https://files.pythonhosted.org/packages/53/3e/405b59cfa13021a56bba395a6b3aca8cec012b45bf177b0eaf7a202cde2c/contourpy-1.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:556dba8fb6f5d8742f2923fe9457dbdd51e1049c4a43fd3986a0b14a1d815fc6", size = 273979, upload-time = "2025-07-26T12:01:22.448Z" },
- { url = "https://files.pythonhosted.org/packages/d4/1c/a12359b9b2ca3a845e8f7f9ac08bdf776114eb931392fcad91743e2ea17b/contourpy-1.3.3-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92d9abc807cf7d0e047b95ca5d957cf4792fcd04e920ca70d48add15c1a90ea7", size = 332653, upload-time = "2025-07-26T12:01:24.155Z" },
- { url = "https://files.pythonhosted.org/packages/63/12/897aeebfb475b7748ea67b61e045accdfcf0d971f8a588b67108ed7f5512/contourpy-1.3.3-cp312-cp312-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b2e8faa0ed68cb29af51edd8e24798bb661eac3bd9f65420c1887b6ca89987c8", size = 379536, upload-time = "2025-07-26T12:01:25.91Z" },
- { url = "https://files.pythonhosted.org/packages/43/8a/a8c584b82deb248930ce069e71576fc09bd7174bbd35183b7943fb1064fd/contourpy-1.3.3-cp312-cp312-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:626d60935cf668e70a5ce6ff184fd713e9683fb458898e4249b63be9e28286ea", size = 384397, upload-time = "2025-07-26T12:01:27.152Z" },
- { url = "https://files.pythonhosted.org/packages/cc/8f/ec6289987824b29529d0dfda0d74a07cec60e54b9c92f3c9da4c0ac732de/contourpy-1.3.3-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4d00e655fcef08aba35ec9610536bfe90267d7ab5ba944f7032549c55a146da1", size = 362601, upload-time = "2025-07-26T12:01:28.808Z" },
- { url = "https://files.pythonhosted.org/packages/05/0a/a3fe3be3ee2dceb3e615ebb4df97ae6f3828aa915d3e10549ce016302bd1/contourpy-1.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:451e71b5a7d597379ef572de31eeb909a87246974d960049a9848c3bc6c41bf7", size = 1331288, upload-time = "2025-07-26T12:01:31.198Z" },
- { url = "https://files.pythonhosted.org/packages/33/1d/acad9bd4e97f13f3e2b18a3977fe1b4a37ecf3d38d815333980c6c72e963/contourpy-1.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:459c1f020cd59fcfe6650180678a9993932d80d44ccde1fa1868977438f0b411", size = 1403386, upload-time = "2025-07-26T12:01:33.947Z" },
- { url = "https://files.pythonhosted.org/packages/cf/8f/5847f44a7fddf859704217a99a23a4f6417b10e5ab1256a179264561540e/contourpy-1.3.3-cp312-cp312-win32.whl", hash = "sha256:023b44101dfe49d7d53932be418477dba359649246075c996866106da069af69", size = 185018, upload-time = "2025-07-26T12:01:35.64Z" },
- { url = "https://files.pythonhosted.org/packages/19/e8/6026ed58a64563186a9ee3f29f41261fd1828f527dd93d33b60feca63352/contourpy-1.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:8153b8bfc11e1e4d75bcb0bff1db232f9e10b274e0929de9d608027e0d34ff8b", size = 226567, upload-time = "2025-07-26T12:01:36.804Z" },
- { url = "https://files.pythonhosted.org/packages/d1/e2/f05240d2c39a1ed228d8328a78b6f44cd695f7ef47beb3e684cf93604f86/contourpy-1.3.3-cp312-cp312-win_arm64.whl", hash = "sha256:07ce5ed73ecdc4a03ffe3e1b3e3c1166db35ae7584be76f65dbbe28a7791b0cc", size = 193655, upload-time = "2025-07-26T12:01:37.999Z" },
- { url = "https://files.pythonhosted.org/packages/68/35/0167aad910bbdb9599272bd96d01a9ec6852f36b9455cf2ca67bd4cc2d23/contourpy-1.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:177fb367556747a686509d6fef71d221a4b198a3905fe824430e5ea0fda54eb5", size = 293257, upload-time = "2025-07-26T12:01:39.367Z" },
- { url = "https://files.pythonhosted.org/packages/96/e4/7adcd9c8362745b2210728f209bfbcf7d91ba868a2c5f40d8b58f54c509b/contourpy-1.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d002b6f00d73d69333dac9d0b8d5e84d9724ff9ef044fd63c5986e62b7c9e1b1", size = 274034, upload-time = "2025-07-26T12:01:40.645Z" },
- { url = "https://files.pythonhosted.org/packages/73/23/90e31ceeed1de63058a02cb04b12f2de4b40e3bef5e082a7c18d9c8ae281/contourpy-1.3.3-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:348ac1f5d4f1d66d3322420f01d42e43122f43616e0f194fc1c9f5d830c5b286", size = 334672, upload-time = "2025-07-26T12:01:41.942Z" },
- { url = "https://files.pythonhosted.org/packages/ed/93/b43d8acbe67392e659e1d984700e79eb67e2acb2bd7f62012b583a7f1b55/contourpy-1.3.3-cp313-cp313-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:655456777ff65c2c548b7c454af9c6f33f16c8884f11083244b5819cc214f1b5", size = 381234, upload-time = "2025-07-26T12:01:43.499Z" },
- { url = "https://files.pythonhosted.org/packages/46/3b/bec82a3ea06f66711520f75a40c8fc0b113b2a75edb36aa633eb11c4f50f/contourpy-1.3.3-cp313-cp313-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:644a6853d15b2512d67881586bd03f462c7ab755db95f16f14d7e238f2852c67", size = 385169, upload-time = "2025-07-26T12:01:45.219Z" },
- { url = "https://files.pythonhosted.org/packages/4b/32/e0f13a1c5b0f8572d0ec6ae2f6c677b7991fafd95da523159c19eff0696a/contourpy-1.3.3-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4debd64f124ca62069f313a9cb86656ff087786016d76927ae2cf37846b006c9", size = 362859, upload-time = "2025-07-26T12:01:46.519Z" },
- { url = "https://files.pythonhosted.org/packages/33/71/e2a7945b7de4e58af42d708a219f3b2f4cff7386e6b6ab0a0fa0033c49a9/contourpy-1.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a15459b0f4615b00bbd1e91f1b9e19b7e63aea7483d03d804186f278c0af2659", size = 1332062, upload-time = "2025-07-26T12:01:48.964Z" },
- { url = "https://files.pythonhosted.org/packages/12/fc/4e87ac754220ccc0e807284f88e943d6d43b43843614f0a8afa469801db0/contourpy-1.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ca0fdcd73925568ca027e0b17ab07aad764be4706d0a925b89227e447d9737b7", size = 1403932, upload-time = "2025-07-26T12:01:51.979Z" },
- { url = "https://files.pythonhosted.org/packages/a6/2e/adc197a37443f934594112222ac1aa7dc9a98faf9c3842884df9a9d8751d/contourpy-1.3.3-cp313-cp313-win32.whl", hash = "sha256:b20c7c9a3bf701366556e1b1984ed2d0cedf999903c51311417cf5f591d8c78d", size = 185024, upload-time = "2025-07-26T12:01:53.245Z" },
- { url = "https://files.pythonhosted.org/packages/18/0b/0098c214843213759692cc638fce7de5c289200a830e5035d1791d7a2338/contourpy-1.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:1cadd8b8969f060ba45ed7c1b714fe69185812ab43bd6b86a9123fe8f99c3263", size = 226578, upload-time = "2025-07-26T12:01:54.422Z" },
- { url = "https://files.pythonhosted.org/packages/8a/9a/2f6024a0c5995243cd63afdeb3651c984f0d2bc727fd98066d40e141ad73/contourpy-1.3.3-cp313-cp313-win_arm64.whl", hash = "sha256:fd914713266421b7536de2bfa8181aa8c699432b6763a0ea64195ebe28bff6a9", size = 193524, upload-time = "2025-07-26T12:01:55.73Z" },
- { url = "https://files.pythonhosted.org/packages/c0/b3/f8a1a86bd3298513f500e5b1f5fd92b69896449f6cab6a146a5d52715479/contourpy-1.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:88df9880d507169449d434c293467418b9f6cbe82edd19284aa0409e7fdb933d", size = 306730, upload-time = "2025-07-26T12:01:57.051Z" },
- { url = "https://files.pythonhosted.org/packages/3f/11/4780db94ae62fc0c2053909b65dc3246bd7cecfc4f8a20d957ad43aa4ad8/contourpy-1.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:d06bb1f751ba5d417047db62bca3c8fde202b8c11fb50742ab3ab962c81e8216", size = 287897, upload-time = "2025-07-26T12:01:58.663Z" },
- { url = "https://files.pythonhosted.org/packages/ae/15/e59f5f3ffdd6f3d4daa3e47114c53daabcb18574a26c21f03dc9e4e42ff0/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e4e6b05a45525357e382909a4c1600444e2a45b4795163d3b22669285591c1ae", size = 326751, upload-time = "2025-07-26T12:02:00.343Z" },
- { url = "https://files.pythonhosted.org/packages/0f/81/03b45cfad088e4770b1dcf72ea78d3802d04200009fb364d18a493857210/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ab3074b48c4e2cf1a960e6bbeb7f04566bf36b1861d5c9d4d8ac04b82e38ba20", size = 375486, upload-time = "2025-07-26T12:02:02.128Z" },
- { url = "https://files.pythonhosted.org/packages/0c/ba/49923366492ffbdd4486e970d421b289a670ae8cf539c1ea9a09822b371a/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6c3d53c796f8647d6deb1abe867daeb66dcc8a97e8455efa729516b997b8ed99", size = 388106, upload-time = "2025-07-26T12:02:03.615Z" },
- { url = "https://files.pythonhosted.org/packages/9f/52/5b00ea89525f8f143651f9f03a0df371d3cbd2fccd21ca9b768c7a6500c2/contourpy-1.3.3-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50ed930df7289ff2a8d7afeb9603f8289e5704755c7e5c3bbd929c90c817164b", size = 352548, upload-time = "2025-07-26T12:02:05.165Z" },
- { url = "https://files.pythonhosted.org/packages/32/1d/a209ec1a3a3452d490f6b14dd92e72280c99ae3d1e73da74f8277d4ee08f/contourpy-1.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4feffb6537d64b84877da813a5c30f1422ea5739566abf0bd18065ac040e120a", size = 1322297, upload-time = "2025-07-26T12:02:07.379Z" },
- { url = "https://files.pythonhosted.org/packages/bc/9e/46f0e8ebdd884ca0e8877e46a3f4e633f6c9c8c4f3f6e72be3fe075994aa/contourpy-1.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2b7e9480ffe2b0cd2e787e4df64270e3a0440d9db8dc823312e2c940c167df7e", size = 1391023, upload-time = "2025-07-26T12:02:10.171Z" },
- { url = "https://files.pythonhosted.org/packages/b9/70/f308384a3ae9cd2209e0849f33c913f658d3326900d0ff5d378d6a1422d2/contourpy-1.3.3-cp313-cp313t-win32.whl", hash = "sha256:283edd842a01e3dcd435b1c5116798d661378d83d36d337b8dde1d16a5fc9ba3", size = 196157, upload-time = "2025-07-26T12:02:11.488Z" },
- { url = "https://files.pythonhosted.org/packages/b2/dd/880f890a6663b84d9e34a6f88cded89d78f0091e0045a284427cb6b18521/contourpy-1.3.3-cp313-cp313t-win_amd64.whl", hash = "sha256:87acf5963fc2b34825e5b6b048f40e3635dd547f590b04d2ab317c2619ef7ae8", size = 240570, upload-time = "2025-07-26T12:02:12.754Z" },
- { url = "https://files.pythonhosted.org/packages/80/99/2adc7d8ffead633234817ef8e9a87115c8a11927a94478f6bb3d3f4d4f7d/contourpy-1.3.3-cp313-cp313t-win_arm64.whl", hash = "sha256:3c30273eb2a55024ff31ba7d052dde990d7d8e5450f4bbb6e913558b3d6c2301", size = 199713, upload-time = "2025-07-26T12:02:14.4Z" },
- { url = "https://files.pythonhosted.org/packages/72/8b/4546f3ab60f78c514ffb7d01a0bd743f90de36f0019d1be84d0a708a580a/contourpy-1.3.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fde6c716d51c04b1c25d0b90364d0be954624a0ee9d60e23e850e8d48353d07a", size = 292189, upload-time = "2025-07-26T12:02:16.095Z" },
- { url = "https://files.pythonhosted.org/packages/fd/e1/3542a9cb596cadd76fcef413f19c79216e002623158befe6daa03dbfa88c/contourpy-1.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:cbedb772ed74ff5be440fa8eee9bd49f64f6e3fc09436d9c7d8f1c287b121d77", size = 273251, upload-time = "2025-07-26T12:02:17.524Z" },
- { url = "https://files.pythonhosted.org/packages/b1/71/f93e1e9471d189f79d0ce2497007731c1e6bf9ef6d1d61b911430c3db4e5/contourpy-1.3.3-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:22e9b1bd7a9b1d652cd77388465dc358dafcd2e217d35552424aa4f996f524f5", size = 335810, upload-time = "2025-07-26T12:02:18.9Z" },
- { url = "https://files.pythonhosted.org/packages/91/f9/e35f4c1c93f9275d4e38681a80506b5510e9327350c51f8d4a5a724d178c/contourpy-1.3.3-cp314-cp314-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a22738912262aa3e254e4f3cb079a95a67132fc5a063890e224393596902f5a4", size = 382871, upload-time = "2025-07-26T12:02:20.418Z" },
- { url = "https://files.pythonhosted.org/packages/b5/71/47b512f936f66a0a900d81c396a7e60d73419868fba959c61efed7a8ab46/contourpy-1.3.3-cp314-cp314-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:afe5a512f31ee6bd7d0dda52ec9864c984ca3d66664444f2d72e0dc4eb832e36", size = 386264, upload-time = "2025-07-26T12:02:21.916Z" },
- { url = "https://files.pythonhosted.org/packages/04/5f/9ff93450ba96b09c7c2b3f81c94de31c89f92292f1380261bd7195bea4ea/contourpy-1.3.3-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f64836de09927cba6f79dcd00fdd7d5329f3fccc633468507079c829ca4db4e3", size = 363819, upload-time = "2025-07-26T12:02:23.759Z" },
- { url = "https://files.pythonhosted.org/packages/3e/a6/0b185d4cc480ee494945cde102cb0149ae830b5fa17bf855b95f2e70ad13/contourpy-1.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:1fd43c3be4c8e5fd6e4f2baeae35ae18176cf2e5cced681cca908addf1cdd53b", size = 1333650, upload-time = "2025-07-26T12:02:26.181Z" },
- { url = "https://files.pythonhosted.org/packages/43/d7/afdc95580ca56f30fbcd3060250f66cedbde69b4547028863abd8aa3b47e/contourpy-1.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6afc576f7b33cf00996e5c1102dc2a8f7cc89e39c0b55df93a0b78c1bd992b36", size = 1404833, upload-time = "2025-07-26T12:02:28.782Z" },
- { url = "https://files.pythonhosted.org/packages/e2/e2/366af18a6d386f41132a48f033cbd2102e9b0cf6345d35ff0826cd984566/contourpy-1.3.3-cp314-cp314-win32.whl", hash = "sha256:66c8a43a4f7b8df8b71ee1840e4211a3c8d93b214b213f590e18a1beca458f7d", size = 189692, upload-time = "2025-07-26T12:02:30.128Z" },
- { url = "https://files.pythonhosted.org/packages/7d/c2/57f54b03d0f22d4044b8afb9ca0e184f8b1afd57b4f735c2fa70883dc601/contourpy-1.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:cf9022ef053f2694e31d630feaacb21ea24224be1c3ad0520b13d844274614fd", size = 232424, upload-time = "2025-07-26T12:02:31.395Z" },
- { url = "https://files.pythonhosted.org/packages/18/79/a9416650df9b525737ab521aa181ccc42d56016d2123ddcb7b58e926a42c/contourpy-1.3.3-cp314-cp314-win_arm64.whl", hash = "sha256:95b181891b4c71de4bb404c6621e7e2390745f887f2a026b2d99e92c17892339", size = 198300, upload-time = "2025-07-26T12:02:32.956Z" },
- { url = "https://files.pythonhosted.org/packages/1f/42/38c159a7d0f2b7b9c04c64ab317042bb6952b713ba875c1681529a2932fe/contourpy-1.3.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:33c82d0138c0a062380332c861387650c82e4cf1747aaa6938b9b6516762e772", size = 306769, upload-time = "2025-07-26T12:02:34.2Z" },
- { url = "https://files.pythonhosted.org/packages/c3/6c/26a8205f24bca10974e77460de68d3d7c63e282e23782f1239f226fcae6f/contourpy-1.3.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ea37e7b45949df430fe649e5de8351c423430046a2af20b1c1961cae3afcda77", size = 287892, upload-time = "2025-07-26T12:02:35.807Z" },
- { url = "https://files.pythonhosted.org/packages/66/06/8a475c8ab718ebfd7925661747dbb3c3ee9c82ac834ccb3570be49d129f4/contourpy-1.3.3-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d304906ecc71672e9c89e87c4675dc5c2645e1f4269a5063b99b0bb29f232d13", size = 326748, upload-time = "2025-07-26T12:02:37.193Z" },
- { url = "https://files.pythonhosted.org/packages/b4/a3/c5ca9f010a44c223f098fccd8b158bb1cb287378a31ac141f04730dc49be/contourpy-1.3.3-cp314-cp314t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ca658cd1a680a5c9ea96dc61cdbae1e85c8f25849843aa799dfd3cb370ad4fbe", size = 375554, upload-time = "2025-07-26T12:02:38.894Z" },
- { url = "https://files.pythonhosted.org/packages/80/5b/68bd33ae63fac658a4145088c1e894405e07584a316738710b636c6d0333/contourpy-1.3.3-cp314-cp314t-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ab2fd90904c503739a75b7c8c5c01160130ba67944a7b77bbf36ef8054576e7f", size = 388118, upload-time = "2025-07-26T12:02:40.642Z" },
- { url = "https://files.pythonhosted.org/packages/40/52/4c285a6435940ae25d7410a6c36bda5145839bc3f0beb20c707cda18b9d2/contourpy-1.3.3-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7301b89040075c30e5768810bc96a8e8d78085b47d8be6e4c3f5a0b4ed478a0", size = 352555, upload-time = "2025-07-26T12:02:42.25Z" },
- { url = "https://files.pythonhosted.org/packages/24/ee/3e81e1dd174f5c7fefe50e85d0892de05ca4e26ef1c9a59c2a57e43b865a/contourpy-1.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:2a2a8b627d5cc6b7c41a4beff6c5ad5eb848c88255fda4a8745f7e901b32d8e4", size = 1322295, upload-time = "2025-07-26T12:02:44.668Z" },
- { url = "https://files.pythonhosted.org/packages/3c/b2/6d913d4d04e14379de429057cd169e5e00f6c2af3bb13e1710bcbdb5da12/contourpy-1.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:fd6ec6be509c787f1caf6b247f0b1ca598bef13f4ddeaa126b7658215529ba0f", size = 1391027, upload-time = "2025-07-26T12:02:47.09Z" },
- { url = "https://files.pythonhosted.org/packages/93/8a/68a4ec5c55a2971213d29a9374913f7e9f18581945a7a31d1a39b5d2dfe5/contourpy-1.3.3-cp314-cp314t-win32.whl", hash = "sha256:e74a9a0f5e3fff48fb5a7f2fd2b9b70a3fe014a67522f79b7cca4c0c7e43c9ae", size = 202428, upload-time = "2025-07-26T12:02:48.691Z" },
- { url = "https://files.pythonhosted.org/packages/fa/96/fd9f641ffedc4fa3ace923af73b9d07e869496c9cc7a459103e6e978992f/contourpy-1.3.3-cp314-cp314t-win_amd64.whl", hash = "sha256:13b68d6a62db8eafaebb8039218921399baf6e47bf85006fd8529f2a08ef33fc", size = 250331, upload-time = "2025-07-26T12:02:50.137Z" },
- { url = "https://files.pythonhosted.org/packages/ae/8c/469afb6465b853afff216f9528ffda78a915ff880ed58813ba4faf4ba0b6/contourpy-1.3.3-cp314-cp314t-win_arm64.whl", hash = "sha256:b7448cb5a725bb1e35ce88771b86fba35ef418952474492cf7c764059933ff8b", size = 203831, upload-time = "2025-07-26T12:02:51.449Z" },
- { url = "https://files.pythonhosted.org/packages/a5/29/8dcfe16f0107943fa92388c23f6e05cff0ba58058c4c95b00280d4c75a14/contourpy-1.3.3-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:cd5dfcaeb10f7b7f9dc8941717c6c2ade08f587be2226222c12b25f0483ed497", size = 278809, upload-time = "2025-07-26T12:02:52.74Z" },
- { url = "https://files.pythonhosted.org/packages/85/a9/8b37ef4f7dafeb335daee3c8254645ef5725be4d9c6aa70b50ec46ef2f7e/contourpy-1.3.3-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:0c1fc238306b35f246d61a1d416a627348b5cf0648648a031e14bb8705fcdfe8", size = 261593, upload-time = "2025-07-26T12:02:54.037Z" },
- { url = "https://files.pythonhosted.org/packages/0a/59/ebfb8c677c75605cc27f7122c90313fd2f375ff3c8d19a1694bda74aaa63/contourpy-1.3.3-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:70f9aad7de812d6541d29d2bbf8feb22ff7e1c299523db288004e3157ff4674e", size = 302202, upload-time = "2025-07-26T12:02:55.947Z" },
- { url = "https://files.pythonhosted.org/packages/3c/37/21972a15834d90bfbfb009b9d004779bd5a07a0ec0234e5ba8f64d5736f4/contourpy-1.3.3-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5ed3657edf08512fc3fe81b510e35c2012fbd3081d2e26160f27ca28affec989", size = 329207, upload-time = "2025-07-26T12:02:57.468Z" },
- { url = "https://files.pythonhosted.org/packages/0c/58/bd257695f39d05594ca4ad60df5bcb7e32247f9951fd09a9b8edb82d1daa/contourpy-1.3.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:3d1a3799d62d45c18bafd41c5fa05120b96a28079f2393af559b843d1a966a77", size = 225315, upload-time = "2025-07-26T12:02:58.801Z" },
-]
-
-[[package]]
-name = "crate"
-version = "0.35.2"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "urllib3", version = "1.26.20", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
- { name = "urllib3", version = "2.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
- { name = "verlib2" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/ef/2b/4d672c2285666054cf5d92cf63c397d633154f0059cf9c31ba66b72f725f/crate-0.35.2.tar.gz", hash = "sha256:e211800adb0aef586f727f0bf608080fbcd1fae9ad4f0bec901c521c1a4bc8c1", size = 71165, upload-time = "2024-01-18T18:22:16.294Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/de/59/47ac6669c67ca29ba9dc070003e56d54eda4482c98465d29743046f8ffe1/crate-0.35.2-py2.py3-none-any.whl", hash = "sha256:e37058cd6b1fd376be5333f9c26794785b5a74f970a1f23d7b2822e02edbf0e0", size = 113265, upload-time = "2024-01-18T18:22:14.284Z" },
-]
-
[[package]]
name = "cryptography"
version = "45.0.6"
@@ -1016,173 +311,16 @@ wheels = [
]
[[package]]
-name = "cycler"
-version = "0.12.1"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/a9/95/a3dbbb5028f35eafb79008e7522a75244477d2838f38cbb722248dabc2a8/cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c", size = 7615, upload-time = "2023-10-07T05:32:18.335Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30", size = 8321, upload-time = "2023-10-07T05:32:16.783Z" },
-]
-
-[[package]]
-name = "databricks-sql-connector"
-version = "4.1.2"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "lz4" },
- { name = "oauthlib" },
- { name = "openpyxl" },
- { name = "pandas" },
- { name = "pyjwt" },
- { name = "python-dateutil" },
- { name = "requests" },
- { name = "thrift" },
- { name = "urllib3", version = "1.26.20", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
- { name = "urllib3", version = "2.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/68/75/2587e876e545849cc89e64d1aee2d021126bd92ab46f7eec5194a6f6a395/databricks_sql_connector-4.1.2.tar.gz", hash = "sha256:7a4eee6c773e0415731530dd812cc4bae04067e73521e5f6161c05d25233e5e7", size = 175173, upload-time = "2025-08-22T12:10:45.058Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/25/ea/322be8c8b845f8fb3e7f879164c1c73bb269671460f1f031391ae800cea8/databricks_sql_connector-4.1.2-py3-none-any.whl", hash = "sha256:b2d05bde9eb48226a6ca56b2e862150445a323e93504379605568371c4c0af19", size = 198429, upload-time = "2025-08-22T12:10:43.232Z" },
-]
-
-[[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, upload-time = "2024-06-09T16:20:19.103Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/c3/be/d0d44e092656fe7a06b55e6103cbce807cdbdee17884a5367c68c9860853/dataclasses_json-0.6.7-py3-none-any.whl", hash = "sha256:0dbf33f26c8d5305befd61b39d2b3414e8a407bedc2834dea9b8d642666fb40a", size = 28686, upload-time = "2024-06-09T16:20:16.715Z" },
-]
-
-[[package]]
-name = "datahub"
-version = "0.999.1"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "acryl-datahub" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/67/2f/2925a8ea56220134d47e4836e7972503d6f186a6d54686329d6696f2ff36/datahub-0.999.1.tar.gz", hash = "sha256:76c94974d00897fb680ba1f3eceed029d13b8d048947862f3f1b9617efd81b01", size = 1239, upload-time = "2023-03-25T23:53:38.816Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/de/e4/3dcd3863303beef6911c4ddee5791be4c537f71324219338a91b78e81a8b/datahub-0.999.1-py3-none-any.whl", hash = "sha256:596785226854868e3c3827175fdac73df32831d18f758c520cda49098b73891e", size = 1305, upload-time = "2023-03-25T23:53:37.114Z" },
-]
-
-[[package]]
-name = "decorator"
-version = "5.2.1"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/43/fa/6d96a0978d19e17b68d634497769987b16c8f4cd0a7a05048bec693caa6b/decorator-5.2.1.tar.gz", hash = "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360", size = 56711, upload-time = "2025-02-24T04:41:34.073Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a", size = 9190, upload-time = "2025-02-24T04:41:32.565Z" },
-]
-
-[[package]]
-name = "deprecated"
-version = "1.2.18"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "wrapt" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/98/97/06afe62762c9a8a86af0cfb7bfdab22a43ad17138b07af5b1a58442690a2/deprecated-1.2.18.tar.gz", hash = "sha256:422b6f6d859da6f2ef57857761bfb392480502a64c3028ca9bbe86085d72115d", size = 2928744, upload-time = "2025-01-27T10:46:25.7Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/6e/c6/ac0b6c1e2d138f1002bcf799d330bd6d85084fece321e662a14223794041/Deprecated-1.2.18-py2.py3-none-any.whl", hash = "sha256:bd5011788200372a32418f888e326a09ff80d0214bd961147cfed01b5c018eec", size = 9998, upload-time = "2025-01-27T10:46:09.186Z" },
-]
-
-[[package]]
-name = "distlib"
-version = "0.4.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/96/8e/709914eb2b5749865801041647dc7f4e6d00b549cfe88b65ca192995f07c/distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d", size = 614605, upload-time = "2025-07-17T16:52:00.465Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047, upload-time = "2025-07-17T16:51:58.613Z" },
-]
-
-[[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, upload-time = "2023-12-24T09:54:32.31Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277, upload-time = "2023-12-24T09:54:30.421Z" },
-]
-
-[[package]]
-name = "docker"
-version = "7.1.0"
+name = "discord-py"
+version = "2.7.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pywin32", marker = "sys_platform == 'win32'" },
- { name = "requests" },
- { name = "urllib3", version = "1.26.20", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
- { name = "urllib3", version = "2.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/91/9b/4a2ea29aeba62471211598dac5d96825bb49348fa07e906ea930394a83ce/docker-7.1.0.tar.gz", hash = "sha256:ad8c70e6e3f8926cb8a92619b832b4ea5299e2831c14284663184e200546fa6c", size = 117834, upload-time = "2024-05-23T11:13:57.216Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/e3/26/57c6fb270950d476074c087527a558ccb6f4436657314bfb6cdf484114c4/docker-7.1.0-py3-none-any.whl", hash = "sha256:c96b93b7f0a746f9e77d325bcfb87422a3d8bd4f03136ae8a85b37f1898d5fc0", size = 147774, upload-time = "2024-05-23T11:13:55.01Z" },
-]
-
-[[package]]
-name = "docstring-parser"
-version = "0.17.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/b2/9d/c3b43da9515bd270df0f80548d9944e389870713cc1fe2b8fb35fe2bcefd/docstring_parser-0.17.0.tar.gz", hash = "sha256:583de4a309722b3315439bb31d64ba3eebada841f2e2cee23b99df001434c912", size = 27442, upload-time = "2025-07-21T07:35:01.868Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/55/e2/2537ebcff11c1ee1ff17d8d0b6f4db75873e3b0fb32c2d4a2ee31ecb310a/docstring_parser-0.17.0-py3-none-any.whl", hash = "sha256:cf2569abd23dce8099b300f9b4fa8191e9582dda731fd533daf54c4551658708", size = 36896, upload-time = "2025-07-21T07:35:00.684Z" },
-]
-
-[[package]]
-name = "duckdb"
-version = "1.3.2"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/47/24/a2e7fb78fba577641c286fe33185789ab1e1569ccdf4d142e005995991d2/duckdb-1.3.2.tar.gz", hash = "sha256:c658df8a1bc78704f702ad0d954d82a1edd4518d7a04f00027ec53e40f591ff5", size = 11627775, upload-time = "2025-07-08T10:41:14.444Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/6a/a0/13f45e67565800826ce0af12a0ab68fe9502dcac0e39bc03bf8a8cba61da/duckdb-1.3.2-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:14676651b86f827ea10bf965eec698b18e3519fdc6266d4ca849f5af7a8c315e", size = 15518888, upload-time = "2025-07-08T10:39:58.167Z" },
- { url = "https://files.pythonhosted.org/packages/ec/28/daf9c01b5cb4058fc80070c74284c52f11581c888db2b0e73ca48f9bae23/duckdb-1.3.2-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:e584f25892450757919639b148c2410402b17105bd404017a57fa9eec9c98919", size = 32495739, upload-time = "2025-07-08T10:40:01.027Z" },
- { url = "https://files.pythonhosted.org/packages/77/e0/5b50014d92eb6c879608183f6184186ab2cf324dd33e432174af93d19a44/duckdb-1.3.2-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:84a19f185ee0c5bc66d95908c6be19103e184b743e594e005dee6f84118dc22c", size = 17088139, upload-time = "2025-07-08T10:40:03.284Z" },
- { url = "https://files.pythonhosted.org/packages/a2/ff/291d74f8b4c988b2a7ee5f65d3073fe0cf4c6a4505aa1a6f28721bb2ebe2/duckdb-1.3.2-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:186fc3f98943e97f88a1e501d5720b11214695571f2c74745d6e300b18bef80e", size = 19157693, upload-time = "2025-07-08T10:40:05.17Z" },
- { url = "https://files.pythonhosted.org/packages/65/50/9a1289619447d93a8c63b08f6ab22e1e6ce73a681e0dceb0cd0ea7558613/duckdb-1.3.2-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6b7e6bb613b73745f03bff4bb412f362d4a1e158bdcb3946f61fd18e9e1a8ddf", size = 21090480, upload-time = "2025-07-08T10:40:07.571Z" },
- { url = "https://files.pythonhosted.org/packages/e0/d1/8dc959e3ca16c4c32ab34e28ceea189edc9bf32523aaa976080fd2101835/duckdb-1.3.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:1c90646b52a0eccda1f76b10ac98b502deb9017569e84073da00a2ab97763578", size = 22742078, upload-time = "2025-07-08T10:40:09.965Z" },
- { url = "https://files.pythonhosted.org/packages/7b/e8/126767fe5acbe01230f7431d999a2c2ef028ffdaebda8fe32ddb57628815/duckdb-1.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:4cdffb1e60defbfa75407b7f2ccc322f535fd462976940731dfd1644146f90c6", size = 11387096, upload-time = "2025-07-08T10:40:11.804Z" },
- { url = "https://files.pythonhosted.org/packages/38/16/4cde40c37dd1f48d2f9ffa63027e8b668391c5cc32cbb59f7ca8b1cec6e2/duckdb-1.3.2-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:e1872cf63aae28c3f1dc2e19b5e23940339fc39fb3425a06196c5d00a8d01040", size = 15520798, upload-time = "2025-07-08T10:40:13.867Z" },
- { url = "https://files.pythonhosted.org/packages/22/ca/9ca65db51868604007114a27cc7d44864d89328ad6a934668626618147ff/duckdb-1.3.2-cp311-cp311-macosx_12_0_universal2.whl", hash = "sha256:db256c206056468ae6a9e931776bdf7debaffc58e19a0ff4fa9e7e1e82d38b3b", size = 32502242, upload-time = "2025-07-08T10:40:15.949Z" },
- { url = "https://files.pythonhosted.org/packages/9e/ca/7f7cf01dd7731d358632fb516521f2962070a627558fb6fc3137e594bbaa/duckdb-1.3.2-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:1d57df2149d6e4e0bd5198689316c5e2ceec7f6ac0a9ec11bc2b216502a57b34", size = 17091841, upload-time = "2025-07-08T10:40:18.539Z" },
- { url = "https://files.pythonhosted.org/packages/4c/7f/38e518b8f51299410dcad9f1e99f1c99f3592516581467a2da344d3b5951/duckdb-1.3.2-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:54f76c8b1e2a19dfe194027894209ce9ddb073fd9db69af729a524d2860e4680", size = 19158775, upload-time = "2025-07-08T10:40:20.804Z" },
- { url = "https://files.pythonhosted.org/packages/90/a3/41f3d42fddd9629846aac328eb295170e76782d8dfc5e58b3584b96fa296/duckdb-1.3.2-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:45bea70b3e93c6bf766ce2f80fc3876efa94c4ee4de72036417a7bd1e32142fe", size = 21093951, upload-time = "2025-07-08T10:40:22.686Z" },
- { url = "https://files.pythonhosted.org/packages/11/8e/c5444b6890ae7f00836fd0cd17799abbcc3066bbab32e90b04aa8a8a5087/duckdb-1.3.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:003f7d36f0d8a430cb0e00521f18b7d5ee49ec98aaa541914c6d0e008c306f1a", size = 22743891, upload-time = "2025-07-08T10:40:24.987Z" },
- { url = "https://files.pythonhosted.org/packages/87/a1/e240bd07671542ddf2084962e68a7d5c9b068d8da3f938e935af69441355/duckdb-1.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:0eb210cedf08b067fa90c666339688f1c874844a54708562282bc54b0189aac6", size = 11387047, upload-time = "2025-07-08T10:40:27.443Z" },
- { url = "https://files.pythonhosted.org/packages/6c/5d/77f15528857c2b186ebec07778dc199ccc04aafb69fb7b15227af4f19ac9/duckdb-1.3.2-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:2455b1ffef4e3d3c7ef8b806977c0e3973c10ec85aa28f08c993ab7f2598e8dd", size = 15538413, upload-time = "2025-07-08T10:40:29.551Z" },
- { url = "https://files.pythonhosted.org/packages/78/67/7e4964f688b846676c813a4acc527cd3454be8a9cafa10f3a9aa78d0d165/duckdb-1.3.2-cp312-cp312-macosx_12_0_universal2.whl", hash = "sha256:9d0ae509713da3461c000af27496d5413f839d26111d2a609242d9d17b37d464", size = 32535307, upload-time = "2025-07-08T10:40:31.632Z" },
- { url = "https://files.pythonhosted.org/packages/95/3d/2d7f8078194130dbf30b5ae154ce454bfc208c91aa5f3e802531a3e09bca/duckdb-1.3.2-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:72ca6143d23c0bf6426396400f01fcbe4785ad9ceec771bd9a4acc5b5ef9a075", size = 17110219, upload-time = "2025-07-08T10:40:34.072Z" },
- { url = "https://files.pythonhosted.org/packages/cd/05/36ff9000b9c6d2a68c1b248f133ee316fcac10c0ff817112cbf5214dbe91/duckdb-1.3.2-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b49a11afba36b98436db83770df10faa03ebded06514cb9b180b513d8be7f392", size = 19178569, upload-time = "2025-07-08T10:40:35.995Z" },
- { url = "https://files.pythonhosted.org/packages/ac/73/f85acbb3ac319a86abbf6b46103d58594d73529123377219980f11b388e9/duckdb-1.3.2-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:36abdfe0d1704fe09b08d233165f312dad7d7d0ecaaca5fb3bb869f4838a2d0b", size = 21129975, upload-time = "2025-07-08T10:40:38.3Z" },
- { url = "https://files.pythonhosted.org/packages/32/40/9aa3267f3631ae06b30fb1045a48628f4dba7beb2efb485c0282b4a73367/duckdb-1.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3380aae1c4f2af3f37b0bf223fabd62077dd0493c84ef441e69b45167188e7b6", size = 22781859, upload-time = "2025-07-08T10:40:41.691Z" },
- { url = "https://files.pythonhosted.org/packages/8c/8d/47bf95f6999b327cf4da677e150cfce802abf9057b61a93a1f91e89d748c/duckdb-1.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:11af73963ae174aafd90ea45fb0317f1b2e28a7f1d9902819d47c67cc957d49c", size = 11395337, upload-time = "2025-07-08T10:40:43.651Z" },
- { url = "https://files.pythonhosted.org/packages/f5/f0/8cac9713735864899e8abc4065bbdb3d1617f2130006d508a80e1b1a6c53/duckdb-1.3.2-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:a3418c973b06ac4e97f178f803e032c30c9a9f56a3e3b43a866f33223dfbf60b", size = 15535350, upload-time = "2025-07-08T10:40:45.562Z" },
- { url = "https://files.pythonhosted.org/packages/c5/26/6698bbb30b7bce8b8b17697599f1517611c61e4bd68b37eaeaf4f5ddd915/duckdb-1.3.2-cp313-cp313-macosx_12_0_universal2.whl", hash = "sha256:2a741eae2cf110fd2223eeebe4151e22c0c02803e1cfac6880dbe8a39fecab6a", size = 32534715, upload-time = "2025-07-08T10:40:47.615Z" },
- { url = "https://files.pythonhosted.org/packages/10/75/8ab4da3099a2fac7335ecebce4246706d19bdd5dad167aa436b5b27c43c4/duckdb-1.3.2-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:51e62541341ea1a9e31f0f1ade2496a39b742caf513bebd52396f42ddd6525a0", size = 17110300, upload-time = "2025-07-08T10:40:49.674Z" },
- { url = "https://files.pythonhosted.org/packages/d1/46/af81b10d4a66a0f27c248df296d1b41ff2a305a235ed8488f93240f6f8b5/duckdb-1.3.2-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b3e519de5640e5671f1731b3ae6b496e0ed7e4de4a1c25c7a2f34c991ab64d71", size = 19180082, upload-time = "2025-07-08T10:40:51.679Z" },
- { url = "https://files.pythonhosted.org/packages/68/fc/259a54fc22111a847981927aa58528d766e8b228c6d41deb0ad8a1959f9f/duckdb-1.3.2-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4732fb8cc60566b60e7e53b8c19972cb5ed12d285147a3063b16cc64a79f6d9f", size = 21128404, upload-time = "2025-07-08T10:40:53.772Z" },
- { url = "https://files.pythonhosted.org/packages/ab/dc/5d5140383e40661173dacdceaddee2a97c3f6721a5e8d76e08258110595e/duckdb-1.3.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:97f7a22dcaa1cca889d12c3dc43a999468375cdb6f6fe56edf840e062d4a8293", size = 22779786, upload-time = "2025-07-08T10:40:55.826Z" },
- { url = "https://files.pythonhosted.org/packages/51/c9/2fcd86ab7530a5b6caff42dbe516ce7a86277e12c499d1c1f5acd266ffb2/duckdb-1.3.2-cp313-cp313-win_amd64.whl", hash = "sha256:cd3d717bf9c49ef4b1016c2216517572258fa645c2923e91c5234053defa3fb5", size = 11395370, upload-time = "2025-07-08T10:40:57.655Z" },
- { url = "https://files.pythonhosted.org/packages/e5/e1/2e98d78eebcf405f1900e22c4ec3f5f7e2d4ed889693f95103255f6a1452/duckdb-1.3.2-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:18862e3b8a805f2204543d42d5f103b629cb7f7f2e69f5188eceb0b8a023f0af", size = 15519301, upload-time = "2025-07-08T10:40:59.409Z" },
- { url = "https://files.pythonhosted.org/packages/f7/73/ee28ba97b5dd2da5d1bb4e592e79384d54288d82ec34e75c068012b36f53/duckdb-1.3.2-cp39-cp39-macosx_12_0_universal2.whl", hash = "sha256:75ed129761b6159f0b8eca4854e496a3c4c416e888537ec47ff8eb35fda2b667", size = 32494620, upload-time = "2025-07-08T10:41:01.546Z" },
- { url = "https://files.pythonhosted.org/packages/a6/0b/67f938499c6c52df90c821a8a3f25699274ce7fbf46fa9227bc4c0bd92fe/duckdb-1.3.2-cp39-cp39-macosx_12_0_x86_64.whl", hash = "sha256:875193ae9f718bc80ab5635435de5b313e3de3ec99420a9b25275ddc5c45ff58", size = 17086693, upload-time = "2025-07-08T10:41:04.51Z" },
- { url = "https://files.pythonhosted.org/packages/6c/2d/373665ef567ef0d6bcf9caf9803b697168f9e6904aff99d5782a1c5e91d1/duckdb-1.3.2-cp39-cp39-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:09b5fd8a112301096668903781ad5944c3aec2af27622bd80eae54149de42b42", size = 19144704, upload-time = "2025-07-08T10:41:06.458Z" },
- { url = "https://files.pythonhosted.org/packages/b1/18/9a89fa02689db8496d414f96d2e0ea56a24910c546c126c8a4626f3a51ee/duckdb-1.3.2-cp39-cp39-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:10cb87ad964b989175e7757d7ada0b1a7264b401a79be2f828cf8f7c366f7f95", size = 21073117, upload-time = "2025-07-08T10:41:08.396Z" },
- { url = "https://files.pythonhosted.org/packages/2e/97/2b09ad149081d75534fe063ff6a1b4b91fffe7e17816a7d9261aa7456788/duckdb-1.3.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:4389fc3812e26977034fe3ff08d1f7dbfe6d2d8337487b4686f2b50e254d7ee3", size = 22723577, upload-time = "2025-07-08T10:41:10.392Z" },
- { url = "https://files.pythonhosted.org/packages/6d/78/8c096f1ef46205f561e7e62d1aff749a079cf57f5c433485f55e15463041/duckdb-1.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:07952ec6f45dd3c7db0f825d231232dc889f1f2490b97a4e9b7abb6830145a19", size = 11387099, upload-time = "2025-07-08T10:41:12.691Z" },
+ { name = "aiohttp" },
+ { name = "audioop-lts", marker = "python_full_version >= '3.13'" },
]
-
-[[package]]
-name = "et-xmlfile"
-version = "2.0.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/d3/38/af70d7ab1ae9d4da450eeec1fa3918940a5fafb9055e934af8d6eb0c2313/et_xmlfile-2.0.0.tar.gz", hash = "sha256:dab3f4764309081ce75662649be815c4c9081e88f0837825f90fd28317d4da54", size = 17234, upload-time = "2024-10-25T17:25:40.039Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/ef/57/9a2d9abdabdc9db8ef28ce0cf4129669e1c8717ba28d607b5ba357c4de3b/discord_py-2.7.1.tar.gz", hash = "sha256:24d5e6a45535152e4b98148a9dd6b550d25dc2c9fb41b6d670319411641249da", size = 1106326, upload-time = "2026-03-03T18:40:46.24Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/c1/8b/5fe2cc11fee489817272089c4203e679c63b570a5aaeb18d852ae3cbba6a/et_xmlfile-2.0.0-py3-none-any.whl", hash = "sha256:7a91720bc756843502c3b7504c77b8fe44217c85c537d85037f0f536151b2caa", size = 18059, upload-time = "2024-10-25T17:25:39.051Z" },
+ { url = "https://files.pythonhosted.org/packages/f7/a7/17208c3b3f92319e7fad259f1c6d5a5baf8fd0654c54846ced329f83c3eb/discord_py-2.7.1-py3-none-any.whl", hash = "sha256:849dca2c63b171146f3a7f3f8acc04248098e9e6203412ce3cf2745f284f7439", size = 1227550, upload-time = "2026-03-03T18:40:44.492Z" },
]
[[package]]
@@ -1190,150 +328,13 @@ name = "exceptiongroup"
version = "1.3.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "typing-extensions", marker = "python_full_version < '3.11'" },
+ { name = "typing-extensions", marker = "python_full_version < '3.13'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/0b/9f/a65090624ecf468cdca03533906e7c69ed7588582240cfe7cc9e770b50eb/exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88", size = 29749, upload-time = "2025-05-10T17:42:51.123Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/36/f4/c6e662dade71f56cd2f3735141b265c3c79293c109549c1e6933b0651ffc/exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10", size = 16674, upload-time = "2025-05-10T17:42:49.33Z" },
]
-[[package]]
-name = "executing"
-version = "2.2.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/91/50/a9d80c47ff289c611ff12e63f7c5d13942c65d68125160cefd768c73e6e4/executing-2.2.0.tar.gz", hash = "sha256:5d108c028108fe2551d1a7b2e8b713341e2cb4fc0aa7dcf966fa4327a5226755", size = 978693, upload-time = "2025-01-22T15:41:29.403Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/7b/8f/c4d9bafc34ad7ad5d8dc16dd1347ee0e507a52c3adb6bfa8887e1c6a26ba/executing-2.2.0-py2.py3-none-any.whl", hash = "sha256:11387150cad388d62750327a53d3339fad4888b39a6fe233c3afbb54ecffd3aa", size = 26702, upload-time = "2025-01-22T15:41:25.929Z" },
-]
-
-[[package]]
-name = "expandvars"
-version = "1.1.1"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/dc/c9/c0a46f462058446aafe953bf76a957c17f78550216a95fbded2270f83117/expandvars-1.1.1.tar.gz", hash = "sha256:98add8268b760dfee457bde1c17bf745795fdebc22b7ddab75fd3278653f1e05", size = 70787, upload-time = "2025-07-12T07:46:22.308Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/2b/ca/0753ba3a81255ac49748ec8b665ab01f8efcf711f74bbccb5457a6193acc/expandvars-1.1.1-py3-none-any.whl", hash = "sha256:09ca39e6bfcb0d899db8778a00dd3d89cfeb0080795c54f16f6279afd0ef8c5b", size = 7522, upload-time = "2025-07-12T07:46:18.984Z" },
-]
-
-[[package]]
-name = "faiss-cpu"
-version = "1.10.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "numpy" },
- { name = "packaging" },
-]
-wheels = [
- { url = "https://files.pythonhosted.org/packages/8b/56/87eb506d8634f08fc7c63d1ca5631aeec7d6b9afbfabedf2cb7a2a804b13/faiss_cpu-1.10.0-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:6693474be296a7142ade1051ea18e7d85cedbfdee4b7eac9c52f83fed0467855", size = 7693034, upload-time = "2025-01-31T07:44:31.908Z" },
- { url = "https://files.pythonhosted.org/packages/51/46/f4d9de34ed1b06300b1a75b824d4857963216f5826de33f291af78088e39/faiss_cpu-1.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:70ebe60a560414dc8dd6cfe8fed105c8f002c0d11f765f5adfe8d63d42c0467f", size = 3234656, upload-time = "2025-01-31T07:44:34.418Z" },
- { url = "https://files.pythonhosted.org/packages/74/3a/e146861019d9290e0198b3470b8d13a658c3b5f228abefc3658ce0afd63d/faiss_cpu-1.10.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:74c5712d4890f15c661ab7b1b75867812e9596e1469759956fad900999bedbb5", size = 3663789, upload-time = "2025-01-31T07:44:36.698Z" },
- { url = "https://files.pythonhosted.org/packages/aa/40/624f0002bb777e37aac1aadfadec1eb4391be6ad05b7fcfbf66049b99a48/faiss_cpu-1.10.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:473d158fbd638d6ad5fb64469ba79a9f09d3494b5f4e8dfb4f40ce2fc335dca4", size = 30673545, upload-time = "2025-01-31T07:44:40.959Z" },
- { url = "https://files.pythonhosted.org/packages/d6/39/298ffcbefd899e84a43e63df217a6dc800d52bca37ebe0d1155ff367886a/faiss_cpu-1.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:dcd0cb2ec84698cbe3df9ed247d2392f09bda041ad34b92d38fa916cd019ad4b", size = 13684176, upload-time = "2025-01-31T07:44:45.399Z" },
- { url = "https://files.pythonhosted.org/packages/78/93/81800f41cb2c719c199d3eb534fcc154853123261d841e37482e8e468619/faiss_cpu-1.10.0-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:8ff6924b0f00df278afe70940ae86302066466580724c2f3238860039e9946f1", size = 7693037, upload-time = "2025-01-31T07:44:48.97Z" },
- { url = "https://files.pythonhosted.org/packages/8d/83/fc9028f6d6aec2c2f219f53a5d4a2b279434715643242e59a2e9755b1ce0/faiss_cpu-1.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cb80b530a9ded44a7d4031a7355a237aaa0ff1f150c1176df050e0254ea5f6f6", size = 3234657, upload-time = "2025-01-31T07:44:51.399Z" },
- { url = "https://files.pythonhosted.org/packages/af/45/588a02e60daa73f6052611334fbbdffcedf37122320f1c91cb90f3e69b96/faiss_cpu-1.10.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:7a9fef4039ed877d40e41d5563417b154c7f8cd57621487dad13c4eb4f32515f", size = 3663710, upload-time = "2025-01-31T07:44:53.198Z" },
- { url = "https://files.pythonhosted.org/packages/cb/cf/9caa08ca4e21ab935f82be0713e5d60566140414c3fff7932d9427c8fd72/faiss_cpu-1.10.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:49b6647aa9e159a2c4603cbff2e1b313becd98ad6e851737ab325c74fe8e0278", size = 30673629, upload-time = "2025-01-31T07:44:56.652Z" },
- { url = "https://files.pythonhosted.org/packages/2c/2d/d2a4171a9cca9a7c04cd9d6f9441a37f1e0558724b90bf7fc7db08553601/faiss_cpu-1.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:6f8c0ef8b615c12c7bf612bd1fc51cffa49c1ddaa6207c6981f01ab6782e6b3b", size = 13683966, upload-time = "2025-01-31T07:45:01.098Z" },
- { url = "https://files.pythonhosted.org/packages/bd/cc/f6aa1288dbb40b2a4f101d16900885e056541f37d8d08ec70462e92cf277/faiss_cpu-1.10.0-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:2aca486fe2d680ea64a18d356206c91ff85db99fd34c19a757298c67c23262b1", size = 7720242, upload-time = "2025-01-31T07:45:03.871Z" },
- { url = "https://files.pythonhosted.org/packages/be/56/40901306324a17fbc1eee8a6e86ba67bd99a67e768ce9908f271e648e9e0/faiss_cpu-1.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c1108a4059c66c37c403183e566ca1ed0974a6af7557c92d49207639aab661bc", size = 3239223, upload-time = "2025-01-31T07:45:06.585Z" },
- { url = "https://files.pythonhosted.org/packages/2e/34/5b1463c450c9a6de3109caf8f38fbf0c329ef940ed1973fcf8c8ec7fa27e/faiss_cpu-1.10.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:449f3eb778d6d937e01a16a3170de4bb8aabfe87c7cb479b458fb790276310c5", size = 3671461, upload-time = "2025-01-31T07:45:09.099Z" },
- { url = "https://files.pythonhosted.org/packages/78/d9/0b78c474289f23b31283d8fb64c8e6a522a7fa47b131a3c6c141c8e6639d/faiss_cpu-1.10.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:9899c340f92bd94071d6faf4bef0ccb5362843daea42144d4ba857a2a1f67511", size = 30663859, upload-time = "2025-01-31T07:45:13.027Z" },
- { url = "https://files.pythonhosted.org/packages/17/f0/194727b9e6e282e2877bc001ba886228f6af52e9a6730bbdb223e38591c3/faiss_cpu-1.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:345a52dbfa980d24b93c94410eadf82d1eef359c6a42e5e0768cca96539f1c3c", size = 13687087, upload-time = "2025-01-31T07:45:17.403Z" },
- { url = "https://files.pythonhosted.org/packages/93/25/23239a83142faa319c4f8c025e25fec6cccc7418995eba3515218a57a45b/faiss_cpu-1.10.0-cp313-cp313-macosx_10_14_x86_64.whl", hash = "sha256:cb8473d69c3964c1bf3f8eb3e04287bb3275f536e6d9635ef32242b5f506b45d", size = 7720240, upload-time = "2025-01-31T07:45:19.943Z" },
- { url = "https://files.pythonhosted.org/packages/18/f1/0e979277831af337739dbacf386d8a359a05eef9642df23d36e6c7d1b1a9/faiss_cpu-1.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:82ca5098de694e7b8495c1a8770e2c08df6e834922546dad0ae1284ff519ced6", size = 3239224, upload-time = "2025-01-31T07:45:21.744Z" },
- { url = "https://files.pythonhosted.org/packages/bd/fa/c2ad85b017a5754f6cdb09c179f8c4f4198d2a264046a8daa7a4d080521f/faiss_cpu-1.10.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:035e4d797e2db7fc0d0c90531d4a655d089ad5d1382b7a49358c1f2307b3a309", size = 3671236, upload-time = "2025-01-31T07:45:23.535Z" },
- { url = "https://files.pythonhosted.org/packages/4f/9b/759962f2c34800058f6a76457df3b0ab93b24f383650ea1ef0231acd322c/faiss_cpu-1.10.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:e02af3696a6b9e1f9072e502f48095a305de2163c42ceb1f6f6b1db9e7ffe574", size = 30663948, upload-time = "2025-01-31T07:45:27.271Z" },
- { url = "https://files.pythonhosted.org/packages/2c/9a/6c496e0189897761978653177386452d62f4060579413d109bff05f458f2/faiss_cpu-1.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:e71f7e24d5b02d3a51df47b77bd10f394a1b48a8331d5c817e71e9e27a8a75ac", size = 13687212, upload-time = "2025-01-31T07:45:32.198Z" },
- { url = "https://files.pythonhosted.org/packages/02/67/8876ed3d6b6a4b4ff7e5c0b12ee060d189c0067dbb6401531e83cf92b77e/faiss_cpu-1.10.0-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:3118b5d7680b0e0a3cd64b3d29389d8384de4298739504fc661b658109540b4b", size = 7693038, upload-time = "2025-01-31T07:45:35.539Z" },
- { url = "https://files.pythonhosted.org/packages/d6/87/6df14577034ab986bf98a11e94ee8912dff4363bc54b04f5a09b59d21816/faiss_cpu-1.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f71c5860c860df2320299f9e4f2ca1725beb559c04acb1cf961ed24e6218277a", size = 3235184, upload-time = "2025-01-31T07:45:37.946Z" },
- { url = "https://files.pythonhosted.org/packages/45/f2/e16ea4132a77bcf36d6616fc53e89b3a6be41220362bd2d43d8c8a72b502/faiss_cpu-1.10.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:2f15b7957d474391fc63f02bfb8011b95317a580e4d9bd70c276f4bc179a17b3", size = 3663597, upload-time = "2025-01-31T07:45:40.238Z" },
- { url = "https://files.pythonhosted.org/packages/8a/60/21447b2c68869c90c46e46d6391eb6df6ecf7294e9409848c0c0bf1ed9ee/faiss_cpu-1.10.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:dadbbb834ddc34ca7e21411811833cebaae4c5a86198dd7c2a349dbe4e7e0398", size = 30672600, upload-time = "2025-01-31T07:45:43.242Z" },
- { url = "https://files.pythonhosted.org/packages/82/81/5e3fee57a889ef5b87599b8d69cf8a897848f88bf9171394695e17a4a509/faiss_cpu-1.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:cb77a6a5f304890c23ffb4c566bc819c0e0cf34370b20ddff02477f2bbbaf7a3", size = 13684557, upload-time = "2025-01-31T07:45:47.195Z" },
-]
-
-[[package]]
-name = "filelock"
-version = "3.19.1"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/40/bb/0ab3e58d22305b6f5440629d20683af28959bf793d98d11950e305c1c326/filelock-3.19.1.tar.gz", hash = "sha256:66eda1888b0171c998b35be2bcc0f6d75c388a7ce20c3f3f37aa8e96c2dddf58", size = 17687, upload-time = "2025-08-14T16:56:03.016Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/42/14/42b2651a2f46b022ccd948bca9f2d5af0fd8929c4eec235b8d6d844fbe67/filelock-3.19.1-py3-none-any.whl", hash = "sha256:d38e30481def20772f5baf097c122c3babc4fcdb7e14e57049eb9d88c6dc017d", size = 15988, upload-time = "2025-08-14T16:56:01.633Z" },
-]
-
-[[package]]
-name = "filetype"
-version = "1.2.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/bb/29/745f7d30d47fe0f251d3ad3dc2978a23141917661998763bebb6da007eb1/filetype-1.2.0.tar.gz", hash = "sha256:66b56cd6474bf41d8c54660347d37afcc3f7d1970648de365c102ef77548aadb", size = 998020, upload-time = "2022-11-02T17:34:04.141Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/18/79/1b8fa1bb3568781e84c9200f951c735f3f157429f44be0495da55894d620/filetype-1.2.0-py2.py3-none-any.whl", hash = "sha256:7ce71b6880181241cf7ac8697a2f1eb6a8bd9b429f7ad6d27b8db9ba5f1c2d25", size = 19970, upload-time = "2022-11-02T17:34:01.425Z" },
-]
-
-[[package]]
-name = "fonttools"
-version = "4.59.2"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/0d/a5/fba25f9fbdab96e26dedcaeeba125e5f05a09043bf888e0305326e55685b/fonttools-4.59.2.tar.gz", hash = "sha256:e72c0749b06113f50bcb80332364c6be83a9582d6e3db3fe0b280f996dc2ef22", size = 3540889, upload-time = "2025-08-27T16:40:30.97Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/cc/a6/e72083ec030232f2aac372857d8f97240cf0c2886bac65fef5287b735633/fonttools-4.59.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:2a159e36ae530650acd13604f364b3a2477eff7408dcac6a640d74a3744d2514", size = 2753389, upload-time = "2025-08-27T16:38:30.021Z" },
- { url = "https://files.pythonhosted.org/packages/fe/96/6e511adbde7b44c0e57e27b767a46cde11d88de8ce76321d749ec7003fe2/fonttools-4.59.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8bd733e47bf4c6dee2b2d8af7a1f7b0c091909b22dbb969a29b2b991e61e5ba4", size = 2334628, upload-time = "2025-08-27T16:38:32.552Z" },
- { url = "https://files.pythonhosted.org/packages/cc/bb/acc8a09327e9bf3efd8db46f992e4d969575b8069a635716149749f78983/fonttools-4.59.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7bb32e0e33795e3b7795bb9b88cb6a9d980d3cbe26dd57642471be547708e17a", size = 4850251, upload-time = "2025-08-27T16:38:34.454Z" },
- { url = "https://files.pythonhosted.org/packages/31/ed/abed08178e06fab3513b845c045cb09145c877d50121668add2f308a6c19/fonttools-4.59.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cdcdf7aad4bab7fd0f2938624a5a84eb4893be269f43a6701b0720b726f24df0", size = 4779256, upload-time = "2025-08-27T16:38:36.527Z" },
- { url = "https://files.pythonhosted.org/packages/dc/1d/5ee99572c3e0e9004445dcfd694b5548ae9a218397fa6824e8cdaca4d253/fonttools-4.59.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4d974312a9f405628e64f475b1f5015a61fd338f0a1b61d15c4822f97d6b045b", size = 4829617, upload-time = "2025-08-27T16:38:39.37Z" },
- { url = "https://files.pythonhosted.org/packages/7d/29/0e20a6c18f550a64ed240b369296161a53bf9e4cf37733385afc62ede804/fonttools-4.59.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:12dc4670e6e6cc4553e8de190f86a549e08ca83a036363115d94a2d67488831e", size = 4939871, upload-time = "2025-08-27T16:38:41.558Z" },
- { url = "https://files.pythonhosted.org/packages/ad/19/969f586b401b0dce5d029491c9c2d6e80aafe2789ba055322e80b117ad67/fonttools-4.59.2-cp310-cp310-win32.whl", hash = "sha256:1603b85d5922042563eea518e272b037baf273b9a57d0f190852b0b075079000", size = 2219867, upload-time = "2025-08-27T16:38:43.642Z" },
- { url = "https://files.pythonhosted.org/packages/de/70/b439062e4b82082704f3f620077100361382a43539d4ff1d8f016b988fd5/fonttools-4.59.2-cp310-cp310-win_amd64.whl", hash = "sha256:2543b81641ea5b8ddfcae7926e62aafd5abc604320b1b119e5218c014a7a5d3c", size = 2264378, upload-time = "2025-08-27T16:38:45.497Z" },
- { url = "https://files.pythonhosted.org/packages/f8/53/742fcd750ae0bdc74de4c0ff923111199cc2f90a4ee87aaddad505b6f477/fonttools-4.59.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:511946e8d7ea5c0d6c7a53c4cb3ee48eda9ab9797cd9bf5d95829a398400354f", size = 2774961, upload-time = "2025-08-27T16:38:47.536Z" },
- { url = "https://files.pythonhosted.org/packages/57/2a/976f5f9fa3b4dd911dc58d07358467bec20e813d933bc5d3db1a955dd456/fonttools-4.59.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8e5e2682cf7be766d84f462ba8828d01e00c8751a8e8e7ce12d7784ccb69a30d", size = 2344690, upload-time = "2025-08-27T16:38:49.723Z" },
- { url = "https://files.pythonhosted.org/packages/c1/8f/b7eefc274fcf370911e292e95565c8253b0b87c82a53919ab3c795a4f50e/fonttools-4.59.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5729e12a982dba3eeae650de48b06f3b9ddb51e9aee2fcaf195b7d09a96250e2", size = 5026910, upload-time = "2025-08-27T16:38:51.904Z" },
- { url = "https://files.pythonhosted.org/packages/69/95/864726eaa8f9d4e053d0c462e64d5830ec7c599cbdf1db9e40f25ca3972e/fonttools-4.59.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c52694eae5d652361d59ecdb5a2246bff7cff13b6367a12da8499e9df56d148d", size = 4971031, upload-time = "2025-08-27T16:38:53.676Z" },
- { url = "https://files.pythonhosted.org/packages/24/4c/b8c4735ebdea20696277c70c79e0de615dbe477834e5a7c2569aa1db4033/fonttools-4.59.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:f1f1bbc23ba1312bd8959896f46f667753b90216852d2a8cfa2d07e0cb234144", size = 5006112, upload-time = "2025-08-27T16:38:55.69Z" },
- { url = "https://files.pythonhosted.org/packages/3b/23/f9ea29c292aa2fc1ea381b2e5621ac436d5e3e0a5dee24ffe5404e58eae8/fonttools-4.59.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1a1bfe5378962825dabe741720885e8b9ae9745ec7ecc4a5ec1f1ce59a6062bf", size = 5117671, upload-time = "2025-08-27T16:38:58.984Z" },
- { url = "https://files.pythonhosted.org/packages/ba/07/cfea304c555bf06e86071ff2a3916bc90f7c07ec85b23bab758d4908c33d/fonttools-4.59.2-cp311-cp311-win32.whl", hash = "sha256:e937790f3c2c18a1cbc7da101550a84319eb48023a715914477d2e7faeaba570", size = 2218157, upload-time = "2025-08-27T16:39:00.75Z" },
- { url = "https://files.pythonhosted.org/packages/d7/de/35d839aa69db737a3f9f3a45000ca24721834d40118652a5775d5eca8ebb/fonttools-4.59.2-cp311-cp311-win_amd64.whl", hash = "sha256:9836394e2f4ce5f9c0a7690ee93bd90aa1adc6b054f1a57b562c5d242c903104", size = 2265846, upload-time = "2025-08-27T16:39:02.453Z" },
- { url = "https://files.pythonhosted.org/packages/ba/3d/1f45db2df51e7bfa55492e8f23f383d372200be3a0ded4bf56a92753dd1f/fonttools-4.59.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:82906d002c349cad647a7634b004825a7335f8159d0d035ae89253b4abf6f3ea", size = 2769711, upload-time = "2025-08-27T16:39:04.423Z" },
- { url = "https://files.pythonhosted.org/packages/29/df/cd236ab32a8abfd11558f296e064424258db5edefd1279ffdbcfd4fd8b76/fonttools-4.59.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a10c1bd7644dc58f8862d8ba0cf9fb7fef0af01ea184ba6ce3f50ab7dfe74d5a", size = 2340225, upload-time = "2025-08-27T16:39:06.143Z" },
- { url = "https://files.pythonhosted.org/packages/98/12/b6f9f964fe6d4b4dd4406bcbd3328821c3de1f909ffc3ffa558fe72af48c/fonttools-4.59.2-cp312-cp312-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:738f31f23e0339785fd67652a94bc69ea49e413dfdb14dcb8c8ff383d249464e", size = 4912766, upload-time = "2025-08-27T16:39:08.138Z" },
- { url = "https://files.pythonhosted.org/packages/73/78/82bde2f2d2c306ef3909b927363170b83df96171f74e0ccb47ad344563cd/fonttools-4.59.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ec99f9bdfee9cdb4a9172f9e8fd578cce5feb231f598909e0aecf5418da4f25", size = 4955178, upload-time = "2025-08-27T16:39:10.094Z" },
- { url = "https://files.pythonhosted.org/packages/92/77/7de766afe2d31dda8ee46d7e479f35c7d48747e558961489a2d6e3a02bd4/fonttools-4.59.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0476ea74161322e08c7a982f83558a2b81b491509984523a1a540baf8611cc31", size = 4897898, upload-time = "2025-08-27T16:39:12.087Z" },
- { url = "https://files.pythonhosted.org/packages/c5/77/ce0e0b905d62a06415fda9f2b2e109a24a5db54a59502b769e9e297d2242/fonttools-4.59.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:95922a922daa1f77cc72611747c156cfb38030ead72436a2c551d30ecef519b9", size = 5049144, upload-time = "2025-08-27T16:39:13.84Z" },
- { url = "https://files.pythonhosted.org/packages/d9/ea/870d93aefd23fff2e07cbeebdc332527868422a433c64062c09d4d5e7fe6/fonttools-4.59.2-cp312-cp312-win32.whl", hash = "sha256:39ad9612c6a622726a6a130e8ab15794558591f999673f1ee7d2f3d30f6a3e1c", size = 2206473, upload-time = "2025-08-27T16:39:15.854Z" },
- { url = "https://files.pythonhosted.org/packages/61/c4/e44bad000c4a4bb2e9ca11491d266e857df98ab6d7428441b173f0fe2517/fonttools-4.59.2-cp312-cp312-win_amd64.whl", hash = "sha256:980fd7388e461b19a881d35013fec32c713ffea1fc37aef2f77d11f332dfd7da", size = 2254706, upload-time = "2025-08-27T16:39:17.893Z" },
- { url = "https://files.pythonhosted.org/packages/13/7b/d0d3b9431642947b5805201fbbbe938a47b70c76685ef1f0cb5f5d7140d6/fonttools-4.59.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:381bde13216ba09489864467f6bc0c57997bd729abfbb1ce6f807ba42c06cceb", size = 2761563, upload-time = "2025-08-27T16:39:20.286Z" },
- { url = "https://files.pythonhosted.org/packages/76/be/fc5fe58dd76af7127b769b68071dbc32d4b95adc8b58d1d28d42d93c90f2/fonttools-4.59.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f33839aa091f7eef4e9078f5b7ab1b8ea4b1d8a50aeaef9fdb3611bba80869ec", size = 2335671, upload-time = "2025-08-27T16:39:22.027Z" },
- { url = "https://files.pythonhosted.org/packages/f2/9f/bf231c2a3fac99d1d7f1d89c76594f158693f981a4aa02be406e9f036832/fonttools-4.59.2-cp313-cp313-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6235fc06bcbdb40186f483ba9d5d68f888ea68aa3c8dac347e05a7c54346fbc8", size = 4893967, upload-time = "2025-08-27T16:39:23.664Z" },
- { url = "https://files.pythonhosted.org/packages/26/a9/d46d2ad4fcb915198504d6727f83aa07f46764c64f425a861aa38756c9fd/fonttools-4.59.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:83ad6e5d06ef3a2884c4fa6384a20d6367b5cfe560e3b53b07c9dc65a7020e73", size = 4951986, upload-time = "2025-08-27T16:39:25.379Z" },
- { url = "https://files.pythonhosted.org/packages/07/90/1cc8d7dd8f707dfeeca472b82b898d3add0ebe85b1f645690dcd128ee63f/fonttools-4.59.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d029804c70fddf90be46ed5305c136cae15800a2300cb0f6bba96d48e770dde0", size = 4891630, upload-time = "2025-08-27T16:39:27.494Z" },
- { url = "https://files.pythonhosted.org/packages/d8/04/f0345b0d9fe67d65aa8d3f2d4cbf91d06f111bc7b8d802e65914eb06194d/fonttools-4.59.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:95807a3b5e78f2714acaa26a33bc2143005cc05c0217b322361a772e59f32b89", size = 5035116, upload-time = "2025-08-27T16:39:29.406Z" },
- { url = "https://files.pythonhosted.org/packages/d7/7d/5ba5eefffd243182fbd067cdbfeb12addd4e5aec45011b724c98a344ea33/fonttools-4.59.2-cp313-cp313-win32.whl", hash = "sha256:b3ebda00c3bb8f32a740b72ec38537d54c7c09f383a4cfefb0b315860f825b08", size = 2204907, upload-time = "2025-08-27T16:39:31.42Z" },
- { url = "https://files.pythonhosted.org/packages/ea/a9/be7219fc64a6026cc0aded17fa3720f9277001c185434230bd351bf678e6/fonttools-4.59.2-cp313-cp313-win_amd64.whl", hash = "sha256:a72155928d7053bbde499d32a9c77d3f0f3d29ae72b5a121752481bcbd71e50f", size = 2253742, upload-time = "2025-08-27T16:39:33.079Z" },
- { url = "https://files.pythonhosted.org/packages/fc/c7/486580d00be6fa5d45e41682e5ffa5c809f3d25773c6f39628d60f333521/fonttools-4.59.2-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:d09e487d6bfbe21195801323ba95c91cb3523f0fcc34016454d4d9ae9eaa57fe", size = 2762444, upload-time = "2025-08-27T16:39:34.759Z" },
- { url = "https://files.pythonhosted.org/packages/d3/9b/950ea9b7b764ceb8d18645c62191e14ce62124d8e05cb32a4dc5e65fde0b/fonttools-4.59.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:dec2f22486d7781087b173799567cffdcc75e9fb2f1c045f05f8317ccce76a3e", size = 2333256, upload-time = "2025-08-27T16:39:40.777Z" },
- { url = "https://files.pythonhosted.org/packages/9b/4d/8ee9d563126de9002eede950cde0051be86cc4e8c07c63eca0c9fc95734a/fonttools-4.59.2-cp314-cp314-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:1647201af10993090120da2e66e9526c4e20e88859f3e34aa05b8c24ded2a564", size = 4834846, upload-time = "2025-08-27T16:39:42.885Z" },
- { url = "https://files.pythonhosted.org/packages/03/26/f26d947b0712dce3d118e92ce30ca88f98938b066498f60d0ee000a892ae/fonttools-4.59.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:47742c33fe65f41eabed36eec2d7313a8082704b7b808752406452f766c573fc", size = 4930871, upload-time = "2025-08-27T16:39:44.818Z" },
- { url = "https://files.pythonhosted.org/packages/fc/7f/ebe878061a5a5e6b6502f0548489e01100f7e6c0049846e6546ba19a3ab4/fonttools-4.59.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:92ac2d45794f95d1ad4cb43fa07e7e3776d86c83dc4b9918cf82831518165b4b", size = 4876971, upload-time = "2025-08-27T16:39:47.027Z" },
- { url = "https://files.pythonhosted.org/packages/eb/0d/0d22e3a20ac566836098d30718092351935487e3271fd57385db1adb2fde/fonttools-4.59.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:fa9ecaf2dcef8941fb5719e16322345d730f4c40599bbf47c9753de40eb03882", size = 4987478, upload-time = "2025-08-27T16:39:48.774Z" },
- { url = "https://files.pythonhosted.org/packages/3b/a3/960cc83182a408ffacc795e61b5f698c6f7b0cfccf23da4451c39973f3c8/fonttools-4.59.2-cp314-cp314-win32.whl", hash = "sha256:a8d40594982ed858780e18a7e4c80415af65af0f22efa7de26bdd30bf24e1e14", size = 2208640, upload-time = "2025-08-27T16:39:50.592Z" },
- { url = "https://files.pythonhosted.org/packages/d8/74/55e5c57c414fa3965fee5fc036ed23f26a5c4e9e10f7f078a54ff9c7dfb7/fonttools-4.59.2-cp314-cp314-win_amd64.whl", hash = "sha256:9cde8b6a6b05f68516573523f2013a3574cb2c75299d7d500f44de82ba947b80", size = 2258457, upload-time = "2025-08-27T16:39:52.611Z" },
- { url = "https://files.pythonhosted.org/packages/e1/dc/8e4261dc591c5cfee68fecff3ffee2a9b29e1edc4c4d9cbafdc5aefe74ee/fonttools-4.59.2-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:036cd87a2dbd7ef72f7b68df8314ced00b8d9973aee296f2464d06a836aeb9a9", size = 2829901, upload-time = "2025-08-27T16:39:55.014Z" },
- { url = "https://files.pythonhosted.org/packages/fb/05/331538dcf21fd6331579cd628268150e85210d0d2bdae20f7598c2b36c05/fonttools-4.59.2-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:14870930181493b1d740b6f25483e20185e5aea58aec7d266d16da7be822b4bb", size = 2362717, upload-time = "2025-08-27T16:39:56.843Z" },
- { url = "https://files.pythonhosted.org/packages/60/ae/d26428ca9ede809c0a93f0af91f44c87433dc0251e2aec333da5ed00d38f/fonttools-4.59.2-cp314-cp314t-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7ff58ea1eb8fc7e05e9a949419f031890023f8785c925b44d6da17a6a7d6e85d", size = 4835120, upload-time = "2025-08-27T16:39:59.06Z" },
- { url = "https://files.pythonhosted.org/packages/07/c4/0f6ac15895de509e07688cb1d45f1ae583adbaa0fa5a5699d73f3bd58ca0/fonttools-4.59.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6dee142b8b3096514c96ad9e2106bf039e2fe34a704c587585b569a36df08c3c", size = 5071115, upload-time = "2025-08-27T16:40:01.009Z" },
- { url = "https://files.pythonhosted.org/packages/b2/b6/147a711b7ecf7ea39f9da9422a55866f6dd5747c2f36b3b0a7a7e0c6820b/fonttools-4.59.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8991bdbae39cf78bcc9cd3d81f6528df1f83f2e7c23ccf6f990fa1f0b6e19708", size = 4943905, upload-time = "2025-08-27T16:40:03.179Z" },
- { url = "https://files.pythonhosted.org/packages/5b/4e/2ab19006646b753855e2b02200fa1cabb75faa4eeca4ef289f269a936974/fonttools-4.59.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:53c1a411b7690042535a4f0edf2120096a39a506adeb6c51484a232e59f2aa0c", size = 4960313, upload-time = "2025-08-27T16:40:05.45Z" },
- { url = "https://files.pythonhosted.org/packages/98/3d/df77907e5be88adcca93cc2cee00646d039da220164be12bee028401e1cf/fonttools-4.59.2-cp314-cp314t-win32.whl", hash = "sha256:59d85088e29fa7a8f87d19e97a1beae2a35821ee48d8ef6d2c4f965f26cb9f8a", size = 2269719, upload-time = "2025-08-27T16:40:07.553Z" },
- { url = "https://files.pythonhosted.org/packages/2d/a0/d4c4bc5b50275449a9a908283b567caa032a94505fe1976e17f994faa6be/fonttools-4.59.2-cp314-cp314t-win_amd64.whl", hash = "sha256:7ad5d8d8cc9e43cb438b3eb4a0094dd6d4088daa767b0a24d52529361fd4c199", size = 2333169, upload-time = "2025-08-27T16:40:09.656Z" },
- { url = "https://files.pythonhosted.org/packages/cb/9b/76b6e83e226d089a1e922b1cee95dcb63f277e0514d6d36a19be7e3a683c/fonttools-4.59.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3cdf9d32690f0e235342055f0a6108eedfccf67b213b033bac747eb809809513", size = 2757672, upload-time = "2025-08-27T16:40:11.356Z" },
- { url = "https://files.pythonhosted.org/packages/4a/9a/bae50fec6ff9f211fa969dd560346343e768ee1d58198837d228267331c2/fonttools-4.59.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:67f9640d6b31d66c0bc54bdbe8ed50983c755521c101576a25e377a8711e8207", size = 2336849, upload-time = "2025-08-27T16:40:13.614Z" },
- { url = "https://files.pythonhosted.org/packages/62/f4/3558d9e90f7904f5d366e6e239e6eef1b65effa84ff0fbc66e397d77eb43/fonttools-4.59.2-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:464d15b58a9fd4304c728735fc1d42cd812fd9ebc27c45b18e78418efd337c28", size = 4825365, upload-time = "2025-08-27T16:40:15.657Z" },
- { url = "https://files.pythonhosted.org/packages/7c/ab/f6e879052e88a530bdecbb026391c1ed44786ef0460ee6b54743dd8d62bd/fonttools-4.59.2-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a039c38d5644c691eb53cd65360921338f54e44c90b4e764605711e046c926ee", size = 4758616, upload-time = "2025-08-27T16:40:17.853Z" },
- { url = "https://files.pythonhosted.org/packages/a2/06/8371749dfc5a40738fcd272dd6c85f1ddb8c08e8d4159ee407a86f96c24e/fonttools-4.59.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:e4f5100e66ec307cce8b52fc03e379b5d1596e9cb8d8b19dfeeccc1e68d86c96", size = 4808010, upload-time = "2025-08-27T16:40:20.541Z" },
- { url = "https://files.pythonhosted.org/packages/40/8b/7d24e759d9eb2463195b28ebe8626885d74ee1b01cfb0a7e0fc3c50ca21d/fonttools-4.59.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:af6dbd463a3530256abf21f675ddf87646272bc48901803a185c49d06287fbf1", size = 4917942, upload-time = "2025-08-27T16:40:22.718Z" },
- { url = "https://files.pythonhosted.org/packages/2a/36/cb17ba1ada96d9c4953711051a6d83473fb98520a1abc689964e97d77b44/fonttools-4.59.2-cp39-cp39-win32.whl", hash = "sha256:594a6fd2f8296583ac7babc4880c8deee7c4f05ab0141addc6bce8b8e367e996", size = 1487853, upload-time = "2025-08-27T16:40:25.034Z" },
- { url = "https://files.pythonhosted.org/packages/c7/62/c035c18887847ae949e059f07b292ed6ae87b3e3512b9a161c879ae1ed68/fonttools-4.59.2-cp39-cp39-win_amd64.whl", hash = "sha256:fc21c4a05226fd39715f66c1c28214862474db50df9f08fd1aa2f96698887bc3", size = 1532311, upload-time = "2025-08-27T16:40:27.19Z" },
- { url = "https://files.pythonhosted.org/packages/65/a4/d2f7be3c86708912c02571db0b550121caab8cd88a3c0aacb9cfa15ea66e/fonttools-4.59.2-py3-none-any.whl", hash = "sha256:8bd0f759020e87bb5d323e6283914d9bf4ae35a7307dafb2cbd1e379e720ad37", size = 1132315, upload-time = "2025-08-27T16:40:28.984Z" },
-]
-
[[package]]
name = "frozenlist"
version = "1.7.0"
@@ -1425,4596 +426,387 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/40/37/5f9f3c3fd7f7746082ec67bcdc204db72dad081f4f83a503d33220a92973/frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:1a85e345b4c43db8b842cab1feb41be5cc0b10a1830e6295b69d7310f99becaf", size = 282620, upload-time = "2025-06-09T23:02:00.493Z" },
{ url = "https://files.pythonhosted.org/packages/0b/31/8fbc5af2d183bff20f21aa743b4088eac4445d2bb1cdece449ae80e4e2d1/frozenlist-1.7.0-cp313-cp313t-win32.whl", hash = "sha256:3a14027124ddb70dfcee5148979998066897e79f89f64b13328595c4bdf77c81", size = 43059, upload-time = "2025-06-09T23:02:02.072Z" },
{ url = "https://files.pythonhosted.org/packages/bb/ed/41956f52105b8dbc26e457c5705340c67c8cc2b79f394b79bffc09d0e938/frozenlist-1.7.0-cp313-cp313t-win_amd64.whl", hash = "sha256:3bf8010d71d4507775f658e9823210b7427be36625b387221642725b515dcf3e", size = 47516, upload-time = "2025-06-09T23:02:03.779Z" },
- { url = "https://files.pythonhosted.org/packages/dd/b1/ee59496f51cd244039330015d60f13ce5a54a0f2bd8d79e4a4a375ab7469/frozenlist-1.7.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:cea3dbd15aea1341ea2de490574a4a37ca080b2ae24e4b4f4b51b9057b4c3630", size = 82434, upload-time = "2025-06-09T23:02:05.195Z" },
- { url = "https://files.pythonhosted.org/packages/75/e1/d518391ce36a6279b3fa5bc14327dde80bcb646bb50d059c6ca0756b8d05/frozenlist-1.7.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7d536ee086b23fecc36c2073c371572374ff50ef4db515e4e503925361c24f71", size = 48232, upload-time = "2025-06-09T23:02:07.728Z" },
- { url = "https://files.pythonhosted.org/packages/b7/8d/a0d04f28b6e821a9685c22e67b5fb798a5a7b68752f104bfbc2dccf080c4/frozenlist-1.7.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:dfcebf56f703cb2e346315431699f00db126d158455e513bd14089d992101e44", size = 47186, upload-time = "2025-06-09T23:02:09.243Z" },
- { url = "https://files.pythonhosted.org/packages/93/3a/a5334c0535c8b7c78eeabda1579179e44fe3d644e07118e59a2276dedaf1/frozenlist-1.7.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:974c5336e61d6e7eb1ea5b929cb645e882aadab0095c5a6974a111e6479f8878", size = 226617, upload-time = "2025-06-09T23:02:10.949Z" },
- { url = "https://files.pythonhosted.org/packages/0a/67/8258d971f519dc3f278c55069a775096cda6610a267b53f6248152b72b2f/frozenlist-1.7.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c70db4a0ab5ab20878432c40563573229a7ed9241506181bba12f6b7d0dc41cb", size = 224179, upload-time = "2025-06-09T23:02:12.603Z" },
- { url = "https://files.pythonhosted.org/packages/fc/89/8225905bf889b97c6d935dd3aeb45668461e59d415cb019619383a8a7c3b/frozenlist-1.7.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1137b78384eebaf70560a36b7b229f752fb64d463d38d1304939984d5cb887b6", size = 235783, upload-time = "2025-06-09T23:02:14.678Z" },
- { url = "https://files.pythonhosted.org/packages/54/6e/ef52375aa93d4bc510d061df06205fa6dcfd94cd631dd22956b09128f0d4/frozenlist-1.7.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e793a9f01b3e8b5c0bc646fb59140ce0efcc580d22a3468d70766091beb81b35", size = 229210, upload-time = "2025-06-09T23:02:16.313Z" },
- { url = "https://files.pythonhosted.org/packages/ee/55/62c87d1a6547bfbcd645df10432c129100c5bd0fd92a384de6e3378b07c1/frozenlist-1.7.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:74739ba8e4e38221d2c5c03d90a7e542cb8ad681915f4ca8f68d04f810ee0a87", size = 215994, upload-time = "2025-06-09T23:02:17.9Z" },
- { url = "https://files.pythonhosted.org/packages/45/d2/263fea1f658b8ad648c7d94d18a87bca7e8c67bd6a1bbf5445b1bd5b158c/frozenlist-1.7.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e63344c4e929b1a01e29bc184bbb5fd82954869033765bfe8d65d09e336a677", size = 225122, upload-time = "2025-06-09T23:02:19.479Z" },
- { url = "https://files.pythonhosted.org/packages/7b/22/7145e35d12fb368d92124f679bea87309495e2e9ddf14c6533990cb69218/frozenlist-1.7.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2ea2a7369eb76de2217a842f22087913cdf75f63cf1307b9024ab82dfb525938", size = 224019, upload-time = "2025-06-09T23:02:20.969Z" },
- { url = "https://files.pythonhosted.org/packages/44/1e/7dae8c54301beb87bcafc6144b9a103bfd2c8f38078c7902984c9a0c4e5b/frozenlist-1.7.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:836b42f472a0e006e02499cef9352ce8097f33df43baaba3e0a28a964c26c7d2", size = 239925, upload-time = "2025-06-09T23:02:22.466Z" },
- { url = "https://files.pythonhosted.org/packages/4b/1e/99c93e54aa382e949a98976a73b9b20c3aae6d9d893f31bbe4991f64e3a8/frozenlist-1.7.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e22b9a99741294b2571667c07d9f8cceec07cb92aae5ccda39ea1b6052ed4319", size = 220881, upload-time = "2025-06-09T23:02:24.521Z" },
- { url = "https://files.pythonhosted.org/packages/5e/9c/ca5105fa7fb5abdfa8837581be790447ae051da75d32f25c8f81082ffc45/frozenlist-1.7.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:9a19e85cc503d958abe5218953df722748d87172f71b73cf3c9257a91b999890", size = 234046, upload-time = "2025-06-09T23:02:26.206Z" },
- { url = "https://files.pythonhosted.org/packages/8d/4d/e99014756093b4ddbb67fb8f0df11fe7a415760d69ace98e2ac6d5d43402/frozenlist-1.7.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:f22dac33bb3ee8fe3e013aa7b91dc12f60d61d05b7fe32191ffa84c3aafe77bd", size = 235756, upload-time = "2025-06-09T23:02:27.79Z" },
- { url = "https://files.pythonhosted.org/packages/8b/72/a19a40bcdaa28a51add2aaa3a1a294ec357f36f27bd836a012e070c5e8a5/frozenlist-1.7.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:9ccec739a99e4ccf664ea0775149f2749b8a6418eb5b8384b4dc0a7d15d304cb", size = 222894, upload-time = "2025-06-09T23:02:29.848Z" },
- { url = "https://files.pythonhosted.org/packages/08/49/0042469993e023a758af81db68c76907cd29e847d772334d4d201cbe9a42/frozenlist-1.7.0-cp39-cp39-win32.whl", hash = "sha256:b3950f11058310008a87757f3eee16a8e1ca97979833239439586857bc25482e", size = 39848, upload-time = "2025-06-09T23:02:31.413Z" },
- { url = "https://files.pythonhosted.org/packages/5a/45/827d86ee475c877f5f766fbc23fb6acb6fada9e52f1c9720e2ba3eae32da/frozenlist-1.7.0-cp39-cp39-win_amd64.whl", hash = "sha256:43a82fce6769c70f2f5a06248b614a7d268080a9d20f7457ef10ecee5af82b63", size = 44102, upload-time = "2025-06-09T23:02:32.808Z" },
{ url = "https://files.pythonhosted.org/packages/ee/45/b82e3c16be2182bff01179db177fe144d58b5dc787a7d4492c6ed8b9317f/frozenlist-1.7.0-py3-none-any.whl", hash = "sha256:9a5af342e34f7e97caf8c995864c7a396418ae2859cc6fdf1b1073020d516a7e", size = 13106, upload-time = "2025-06-09T23:02:34.204Z" },
]
[[package]]
-name = "fsspec"
-version = "2025.7.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/8b/02/0835e6ab9cfc03916fe3f78c0956cfcdb6ff2669ffa6651065d5ebf7fc98/fsspec-2025.7.0.tar.gz", hash = "sha256:786120687ffa54b8283d942929540d8bc5ccfa820deb555a2b5d0ed2b737bf58", size = 304432, upload-time = "2025-07-15T16:05:21.19Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/2f/e0/014d5d9d7a4564cf1c40b5039bc882db69fd881111e03ab3657ac0b218e2/fsspec-2025.7.0-py3-none-any.whl", hash = "sha256:8b012e39f63c7d5f10474de957f3ab793b47b45ae7d39f2fb735f8bbe25c0e21", size = 199597, upload-time = "2025-07-15T16:05:19.529Z" },
-]
-
-[[package]]
-name = "future"
-version = "1.0.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/a7/b2/4140c69c6a66432916b26158687e821ba631a4c9273c474343badf84d3ba/future-1.0.0.tar.gz", hash = "sha256:bd2968309307861edae1458a4f8a4f3598c03be43b97521076aebf5d94c07b05", size = 1228490, upload-time = "2024-02-21T11:52:38.461Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/da/71/ae30dadffc90b9006d77af76b393cb9dfbfc9629f339fc1574a1c52e6806/future-1.0.0-py3-none-any.whl", hash = "sha256:929292d34f5872e70396626ef385ec22355a1fae8ad29e1a734c3e43f9fbc216", size = 491326, upload-time = "2024-02-21T11:52:35.956Z" },
-]
-
-[[package]]
-name = "gitdb"
-version = "4.0.12"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "smmap" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/72/94/63b0fc47eb32792c7ba1fe1b694daec9a63620db1e313033d18140c2320a/gitdb-4.0.12.tar.gz", hash = "sha256:5ef71f855d191a3326fcfbc0d5da835f26b13fbcba60c32c21091c349ffdb571", size = 394684, upload-time = "2025-01-02T07:20:46.413Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl", hash = "sha256:67073e15955400952c6565cc3e707c554a4eea2e428946f7a4c162fab9bd9bcf", size = 62794, upload-time = "2025-01-02T07:20:43.624Z" },
-]
-
-[[package]]
-name = "gitpython"
-version = "3.1.45"
+name = "idna"
+version = "3.10"
source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "gitdb" },
- { name = "typing-extensions", marker = "python_full_version < '3.10'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/9a/c8/dd58967d119baab745caec2f9d853297cec1989ec1d63f677d3880632b88/gitpython-3.1.45.tar.gz", hash = "sha256:85b0ee964ceddf211c41b9f27a49086010a190fd8132a24e21f362a4b36a791c", size = 215076, upload-time = "2025-07-24T03:45:54.871Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490, upload-time = "2024-09-15T18:07:39.745Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/01/61/d4b89fec821f72385526e1b9d9a3a0385dda4a72b206d28049e2c7cd39b8/gitpython-3.1.45-py3-none-any.whl", hash = "sha256:8908cb2e02fb3b93b7eb0f2827125cb699869470432cc885f019b8fd0fccff77", size = 208168, upload-time = "2025-07-24T03:45:52.517Z" },
+ { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" },
]
[[package]]
-name = "google-ai-generativelanguage"
-version = "0.6.18"
+name = "iniconfig"
+version = "2.1.0"
source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "google-api-core", extra = ["grpc"] },
- { name = "google-auth" },
- { name = "proto-plus" },
- { name = "protobuf" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/eb/77/3e89a4c4200135eac74eca2f6c9153127e3719a825681ad55f5a4a58b422/google_ai_generativelanguage-0.6.18.tar.gz", hash = "sha256:274ba9fcf69466ff64e971d565884434388e523300afd468fc8e3033cd8e606e", size = 1444757, upload-time = "2025-04-29T15:45:45.527Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793, upload-time = "2025-03-19T20:09:59.721Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/e5/77/ca2889903a2d93b3072a49056d48b3f55410219743e338a1d7f94dc6455e/google_ai_generativelanguage-0.6.18-py3-none-any.whl", hash = "sha256:13d8174fea90b633f520789d32df7b422058fd5883b022989c349f1017db7fcf", size = 1372256, upload-time = "2025-04-29T15:45:43.601Z" },
+ { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050, upload-time = "2025-03-19T20:10:01.071Z" },
]
[[package]]
-name = "google-api-core"
-version = "2.25.1"
-source = { registry = "https://pypi.org/simple" }
+name = "lang2sql"
+version = "0.4.0.dev0"
+source = { editable = "." }
dependencies = [
- { name = "google-auth" },
- { name = "googleapis-common-protos" },
- { name = "proto-plus" },
- { name = "protobuf" },
- { name = "requests" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/dc/21/e9d043e88222317afdbdb567165fdbc3b0aad90064c7e0c9eb0ad9955ad8/google_api_core-2.25.1.tar.gz", hash = "sha256:d2aaa0b13c78c61cb3f4282c464c046e45fbd75755683c9c525e6e8f7ed0a5e8", size = 165443, upload-time = "2025-06-12T20:52:20.439Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/14/4b/ead00905132820b623732b175d66354e9d3e69fcf2a5dcdab780664e7896/google_api_core-2.25.1-py3-none-any.whl", hash = "sha256:8a2a56c1fef82987a524371f99f3bd0143702fecc670c72e600c1cda6bf8dbb7", size = 160807, upload-time = "2025-06-12T20:52:19.334Z" },
+ { name = "cryptography" },
+ { name = "discord-py" },
]
[package.optional-dependencies]
-grpc = [
- { name = "grpcio" },
- { name = "grpcio-status" },
-]
-
-[[package]]
-name = "google-auth"
-version = "2.40.3"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "cachetools" },
- { name = "pyasn1-modules" },
- { name = "rsa" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/9e/9b/e92ef23b84fa10a64ce4831390b7a4c2e53c0132568d99d4ae61d04c8855/google_auth-2.40.3.tar.gz", hash = "sha256:500c3a29adedeb36ea9cf24b8d10858e152f2412e3ca37829b3fa18e33d63b77", size = 281029, upload-time = "2025-06-04T18:04:57.577Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/17/63/b19553b658a1692443c62bd07e5868adaa0ad746a0751ba62c59568cd45b/google_auth-2.40.3-py2.py3-none-any.whl", hash = "sha256:1370d4593e86213563547f97a92752fc658456fe4514c809544f330fed45a7ca", size = 216137, upload-time = "2025-06-04T18:04:55.573Z" },
-]
-
-[[package]]
-name = "google-cloud-bigquery"
-version = "3.36.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "google-api-core", extra = ["grpc"] },
- { name = "google-auth" },
- { name = "google-cloud-core" },
- { name = "google-resumable-media" },
- { name = "packaging" },
- { name = "python-dateutil" },
- { name = "requests" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/ac/76/a9bc50b0b14732f81f18b523f273f89c637a5f62187413d7296a91915e57/google_cloud_bigquery-3.36.0.tar.gz", hash = "sha256:519d7a16be2119dca1ea8871e6dd45f971a8382c337cbe045319543b9e743bdd", size = 502014, upload-time = "2025-08-20T20:12:28.941Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/f3/41/47fbf5881f35b5a3adeeb3e39bdfa54e5512c22fb5c6a48c3b8d4be13ba9/google_cloud_bigquery-3.36.0-py3-none-any.whl", hash = "sha256:0cfbad09999907600fd0618794491db10000d98911ec7768ac6041cb9a0257dd", size = 258479, upload-time = "2025-08-20T20:12:27.472Z" },
-]
-
-[[package]]
-name = "google-cloud-core"
-version = "2.4.3"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "google-api-core" },
- { name = "google-auth" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/d6/b8/2b53838d2acd6ec6168fd284a990c76695e84c65deee79c9f3a4276f6b4f/google_cloud_core-2.4.3.tar.gz", hash = "sha256:1fab62d7102844b278fe6dead3af32408b1df3eb06f5c7e8634cbd40edc4da53", size = 35861, upload-time = "2025-03-10T21:05:38.948Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/40/86/bda7241a8da2d28a754aad2ba0f6776e35b67e37c36ae0c45d49370f1014/google_cloud_core-2.4.3-py2.py3-none-any.whl", hash = "sha256:5130f9f4c14b4fafdff75c79448f9495cfade0d8775facf1b09c3bf67e027f6e", size = 29348, upload-time = "2025-03-10T21:05:37.785Z" },
-]
-
-[[package]]
-name = "google-crc32c"
-version = "1.7.1"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/19/ae/87802e6d9f9d69adfaedfcfd599266bf386a54d0be058b532d04c794f76d/google_crc32c-1.7.1.tar.gz", hash = "sha256:2bff2305f98846f3e825dbeec9ee406f89da7962accdb29356e4eadc251bd472", size = 14495, upload-time = "2025-03-26T14:29:13.32Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/eb/69/b1b05cf415df0d86691d6a8b4b7e60ab3a6fb6efb783ee5cd3ed1382bfd3/google_crc32c-1.7.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:b07d48faf8292b4db7c3d64ab86f950c2e94e93a11fd47271c28ba458e4a0d76", size = 30467, upload-time = "2025-03-26T14:31:11.92Z" },
- { url = "https://files.pythonhosted.org/packages/44/3d/92f8928ecd671bd5b071756596971c79d252d09b835cdca5a44177fa87aa/google_crc32c-1.7.1-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:7cc81b3a2fbd932a4313eb53cc7d9dde424088ca3a0337160f35d91826880c1d", size = 30311, upload-time = "2025-03-26T14:53:14.161Z" },
- { url = "https://files.pythonhosted.org/packages/33/42/c2d15a73df79d45ed6b430b9e801d0bd8e28ac139a9012d7d58af50a385d/google_crc32c-1.7.1-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:1c67ca0a1f5b56162951a9dae987988679a7db682d6f97ce0f6381ebf0fbea4c", size = 37889, upload-time = "2025-03-26T14:41:27.83Z" },
- { url = "https://files.pythonhosted.org/packages/57/ea/ac59c86a3c694afd117bb669bde32aaf17d0de4305d01d706495f09cbf19/google_crc32c-1.7.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc5319db92daa516b653600794d5b9f9439a9a121f3e162f94b0e1891c7933cb", size = 33028, upload-time = "2025-03-26T14:41:29.141Z" },
- { url = "https://files.pythonhosted.org/packages/60/44/87e77e8476767a4a93f6cf271157c6d948eacec63688c093580af13b04be/google_crc32c-1.7.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dcdf5a64adb747610140572ed18d011896e3b9ae5195f2514b7ff678c80f1603", size = 38026, upload-time = "2025-03-26T14:41:29.921Z" },
- { url = "https://files.pythonhosted.org/packages/c8/bf/21ac7bb305cd7c1a6de9c52f71db0868e104a5b573a4977cd9d0ff830f82/google_crc32c-1.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:754561c6c66e89d55754106739e22fdaa93fafa8da7221b29c8b8e8270c6ec8a", size = 33476, upload-time = "2025-03-26T14:29:09.086Z" },
- { url = "https://files.pythonhosted.org/packages/f7/94/220139ea87822b6fdfdab4fb9ba81b3fff7ea2c82e2af34adc726085bffc/google_crc32c-1.7.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:6fbab4b935989e2c3610371963ba1b86afb09537fd0c633049be82afe153ac06", size = 30468, upload-time = "2025-03-26T14:32:52.215Z" },
- { url = "https://files.pythonhosted.org/packages/94/97/789b23bdeeb9d15dc2904660463ad539d0318286d7633fe2760c10ed0c1c/google_crc32c-1.7.1-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:ed66cbe1ed9cbaaad9392b5259b3eba4a9e565420d734e6238813c428c3336c9", size = 30313, upload-time = "2025-03-26T14:57:38.758Z" },
- { url = "https://files.pythonhosted.org/packages/81/b8/976a2b843610c211e7ccb3e248996a61e87dbb2c09b1499847e295080aec/google_crc32c-1.7.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee6547b657621b6cbed3562ea7826c3e11cab01cd33b74e1f677690652883e77", size = 33048, upload-time = "2025-03-26T14:41:30.679Z" },
- { url = "https://files.pythonhosted.org/packages/c9/16/a3842c2cf591093b111d4a5e2bfb478ac6692d02f1b386d2a33283a19dc9/google_crc32c-1.7.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d68e17bad8f7dd9a49181a1f5a8f4b251c6dbc8cc96fb79f1d321dfd57d66f53", size = 32669, upload-time = "2025-03-26T14:41:31.432Z" },
- { url = "https://files.pythonhosted.org/packages/04/17/ed9aba495916fcf5fe4ecb2267ceb851fc5f273c4e4625ae453350cfd564/google_crc32c-1.7.1-cp311-cp311-win_amd64.whl", hash = "sha256:6335de12921f06e1f774d0dd1fbea6bf610abe0887a1638f64d694013138be5d", size = 33476, upload-time = "2025-03-26T14:29:10.211Z" },
- { url = "https://files.pythonhosted.org/packages/dd/b7/787e2453cf8639c94b3d06c9d61f512234a82e1d12d13d18584bd3049904/google_crc32c-1.7.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:2d73a68a653c57281401871dd4aeebbb6af3191dcac751a76ce430df4d403194", size = 30470, upload-time = "2025-03-26T14:34:31.655Z" },
- { url = "https://files.pythonhosted.org/packages/ed/b4/6042c2b0cbac3ec3a69bb4c49b28d2f517b7a0f4a0232603c42c58e22b44/google_crc32c-1.7.1-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:22beacf83baaf59f9d3ab2bbb4db0fb018da8e5aebdce07ef9f09fce8220285e", size = 30315, upload-time = "2025-03-26T15:01:54.634Z" },
- { url = "https://files.pythonhosted.org/packages/29/ad/01e7a61a5d059bc57b702d9ff6a18b2585ad97f720bd0a0dbe215df1ab0e/google_crc32c-1.7.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:19eafa0e4af11b0a4eb3974483d55d2d77ad1911e6cf6f832e1574f6781fd337", size = 33180, upload-time = "2025-03-26T14:41:32.168Z" },
- { url = "https://files.pythonhosted.org/packages/3b/a5/7279055cf004561894ed3a7bfdf5bf90a53f28fadd01af7cd166e88ddf16/google_crc32c-1.7.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6d86616faaea68101195c6bdc40c494e4d76f41e07a37ffdef270879c15fb65", size = 32794, upload-time = "2025-03-26T14:41:33.264Z" },
- { url = "https://files.pythonhosted.org/packages/0f/d6/77060dbd140c624e42ae3ece3df53b9d811000729a5c821b9fd671ceaac6/google_crc32c-1.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:b7491bdc0c7564fcf48c0179d2048ab2f7c7ba36b84ccd3a3e1c3f7a72d3bba6", size = 33477, upload-time = "2025-03-26T14:29:10.94Z" },
- { url = "https://files.pythonhosted.org/packages/8b/72/b8d785e9184ba6297a8620c8a37cf6e39b81a8ca01bb0796d7cbb28b3386/google_crc32c-1.7.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:df8b38bdaf1629d62d51be8bdd04888f37c451564c2042d36e5812da9eff3c35", size = 30467, upload-time = "2025-03-26T14:36:06.909Z" },
- { url = "https://files.pythonhosted.org/packages/34/25/5f18076968212067c4e8ea95bf3b69669f9fc698476e5f5eb97d5b37999f/google_crc32c-1.7.1-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:e42e20a83a29aa2709a0cf271c7f8aefaa23b7ab52e53b322585297bb94d4638", size = 30309, upload-time = "2025-03-26T15:06:15.318Z" },
- { url = "https://files.pythonhosted.org/packages/92/83/9228fe65bf70e93e419f38bdf6c5ca5083fc6d32886ee79b450ceefd1dbd/google_crc32c-1.7.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:905a385140bf492ac300026717af339790921f411c0dfd9aa5a9e69a08ed32eb", size = 33133, upload-time = "2025-03-26T14:41:34.388Z" },
- { url = "https://files.pythonhosted.org/packages/c3/ca/1ea2fd13ff9f8955b85e7956872fdb7050c4ace8a2306a6d177edb9cf7fe/google_crc32c-1.7.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b211ddaf20f7ebeec5c333448582c224a7c90a9d98826fbab82c0ddc11348e6", size = 32773, upload-time = "2025-03-26T14:41:35.19Z" },
- { url = "https://files.pythonhosted.org/packages/89/32/a22a281806e3ef21b72db16f948cad22ec68e4bdd384139291e00ff82fe2/google_crc32c-1.7.1-cp313-cp313-win_amd64.whl", hash = "sha256:0f99eaa09a9a7e642a61e06742856eec8b19fc0037832e03f941fe7cf0c8e4db", size = 33475, upload-time = "2025-03-26T14:29:11.771Z" },
- { url = "https://files.pythonhosted.org/packages/b8/c5/002975aff514e57fc084ba155697a049b3f9b52225ec3bc0f542871dd524/google_crc32c-1.7.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:32d1da0d74ec5634a05f53ef7df18fc646666a25efaaca9fc7dcfd4caf1d98c3", size = 33243, upload-time = "2025-03-26T14:41:35.975Z" },
- { url = "https://files.pythonhosted.org/packages/61/cb/c585282a03a0cea70fcaa1bf55d5d702d0f2351094d663ec3be1c6c67c52/google_crc32c-1.7.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e10554d4abc5238823112c2ad7e4560f96c7bf3820b202660373d769d9e6e4c9", size = 32870, upload-time = "2025-03-26T14:41:37.08Z" },
- { url = "https://files.pythonhosted.org/packages/e3/89/940d170a9f24e6e711666a7c5596561358243023b4060869d9adae97a762/google_crc32c-1.7.1-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:9fc196f0b8d8bd2789352c6a522db03f89e83a0ed6b64315923c396d7a932315", size = 30462, upload-time = "2025-03-26T14:29:25.969Z" },
- { url = "https://files.pythonhosted.org/packages/42/0c/22bebe2517368e914a63e5378aab74e2b6357eb739d94b6bc0e830979a37/google_crc32c-1.7.1-cp39-cp39-macosx_12_0_x86_64.whl", hash = "sha256:bb5e35dcd8552f76eed9461a23de1030920a3c953c1982f324be8f97946e7127", size = 30304, upload-time = "2025-03-26T14:49:16.642Z" },
- { url = "https://files.pythonhosted.org/packages/36/32/2daf4c46f875aaa3a057ecc8569406979cb29fb1e2389e4f2570d8ed6a5c/google_crc32c-1.7.1-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f2226b6a8da04f1d9e61d3e357f2460b9551c5e6950071437e122c958a18ae14", size = 37734, upload-time = "2025-03-26T14:41:37.88Z" },
- { url = "https://files.pythonhosted.org/packages/76/b5/b3e220b68d5d265c4aacd2878301fdb2df72715c45ba49acc19f310d4555/google_crc32c-1.7.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f2b3522222746fff0e04a9bd0a23ea003ba3cccc8cf21385c564deb1f223242", size = 32869, upload-time = "2025-03-26T14:41:38.965Z" },
- { url = "https://files.pythonhosted.org/packages/0a/90/2931c3c8d2de1e7cde89945d3ceb2c4258a1f23f0c22c3c1c921c3c026a6/google_crc32c-1.7.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3bda0fcb632d390e3ea8b6b07bf6b4f4a66c9d02dcd6fbf7ba00a197c143f582", size = 37875, upload-time = "2025-03-26T14:41:41.732Z" },
- { url = "https://files.pythonhosted.org/packages/30/9e/0aaed8a209ea6fa4b50f66fed2d977f05c6c799e10bb509f5523a5a5c90c/google_crc32c-1.7.1-cp39-cp39-win_amd64.whl", hash = "sha256:713121af19f1a617054c41f952294764e0c5443d5a5d9034b2cd60f5dd7e0349", size = 33471, upload-time = "2025-03-26T14:29:12.578Z" },
- { url = "https://files.pythonhosted.org/packages/0b/43/31e57ce04530794917dfe25243860ec141de9fadf4aa9783dffe7dac7c39/google_crc32c-1.7.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a8e9afc74168b0b2232fb32dd202c93e46b7d5e4bf03e66ba5dc273bb3559589", size = 28242, upload-time = "2025-03-26T14:41:42.858Z" },
- { url = "https://files.pythonhosted.org/packages/eb/f3/8b84cd4e0ad111e63e30eb89453f8dd308e3ad36f42305cf8c202461cdf0/google_crc32c-1.7.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa8136cc14dd27f34a3221c0f16fd42d8a40e4778273e61a3c19aedaa44daf6b", size = 28049, upload-time = "2025-03-26T14:41:44.651Z" },
- { url = "https://files.pythonhosted.org/packages/16/1b/1693372bf423ada422f80fd88260dbfd140754adb15cbc4d7e9a68b1cb8e/google_crc32c-1.7.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85fef7fae11494e747c9fd1359a527e5970fc9603c90764843caabd3a16a0a48", size = 28241, upload-time = "2025-03-26T14:41:45.898Z" },
- { url = "https://files.pythonhosted.org/packages/fd/3c/2a19a60a473de48717b4efb19398c3f914795b64a96cf3fbe82588044f78/google_crc32c-1.7.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6efb97eb4369d52593ad6f75e7e10d053cf00c48983f7a973105bc70b0ac4d82", size = 28048, upload-time = "2025-03-26T14:41:46.696Z" },
-]
-
-[[package]]
-name = "google-resumable-media"
-version = "2.7.2"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "google-crc32c" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/58/5a/0efdc02665dca14e0837b62c8a1a93132c264bd02054a15abb2218afe0ae/google_resumable_media-2.7.2.tar.gz", hash = "sha256:5280aed4629f2b60b847b0d42f9857fd4935c11af266744df33d8074cae92fe0", size = 2163099, upload-time = "2024-08-07T22:20:38.555Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/82/35/b8d3baf8c46695858cb9d8835a53baa1eeb9906ddaf2f728a5f5b640fd1e/google_resumable_media-2.7.2-py2.py3-none-any.whl", hash = "sha256:3ce7551e9fe6d99e9a126101d2536612bb73486721951e9562fee0f90c6ababa", size = 81251, upload-time = "2024-08-07T22:20:36.409Z" },
-]
-
-[[package]]
-name = "googleapis-common-protos"
-version = "1.70.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "protobuf" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/39/24/33db22342cf4a2ea27c9955e6713140fedd51e8b141b5ce5260897020f1a/googleapis_common_protos-1.70.0.tar.gz", hash = "sha256:0e1b44e0ea153e6594f9f394fef15193a68aaaea2d843f83e2742717ca753257", size = 145903, upload-time = "2025-04-14T10:17:02.924Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/86/f1/62a193f0227cf15a920390abe675f386dec35f7ae3ffe6da582d3ade42c7/googleapis_common_protos-1.70.0-py3-none-any.whl", hash = "sha256:b8bfcca8c25a2bb253e0e0b0adaf8c00773e5e6af6fd92397576680b807e0fd8", size = 294530, upload-time = "2025-04-14T10:17:01.271Z" },
-]
-
-[[package]]
-name = "greenlet"
-version = "3.2.4"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/03/b8/704d753a5a45507a7aab61f18db9509302ed3d0a27ac7e0359ec2905b1a6/greenlet-3.2.4.tar.gz", hash = "sha256:0dca0d95ff849f9a364385f36ab49f50065d76964944638be9691e1832e9f86d", size = 188260, upload-time = "2025-08-07T13:24:33.51Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/7d/ed/6bfa4109fcb23a58819600392564fea69cdc6551ffd5e69ccf1d52a40cbc/greenlet-3.2.4-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:8c68325b0d0acf8d91dde4e6f930967dd52a5302cd4062932a6b2e7c2969f47c", size = 271061, upload-time = "2025-08-07T13:17:15.373Z" },
- { url = "https://files.pythonhosted.org/packages/2a/fc/102ec1a2fc015b3a7652abab7acf3541d58c04d3d17a8d3d6a44adae1eb1/greenlet-3.2.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:94385f101946790ae13da500603491f04a76b6e4c059dab271b3ce2e283b2590", size = 629475, upload-time = "2025-08-07T13:42:54.009Z" },
- { url = "https://files.pythonhosted.org/packages/c5/26/80383131d55a4ac0fb08d71660fd77e7660b9db6bdb4e8884f46d9f2cc04/greenlet-3.2.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f10fd42b5ee276335863712fa3da6608e93f70629c631bf77145021600abc23c", size = 640802, upload-time = "2025-08-07T13:45:25.52Z" },
- { url = "https://files.pythonhosted.org/packages/9f/7c/e7833dbcd8f376f3326bd728c845d31dcde4c84268d3921afcae77d90d08/greenlet-3.2.4-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:c8c9e331e58180d0d83c5b7999255721b725913ff6bc6cf39fa2a45841a4fd4b", size = 636703, upload-time = "2025-08-07T13:53:12.622Z" },
- { url = "https://files.pythonhosted.org/packages/e9/49/547b93b7c0428ede7b3f309bc965986874759f7d89e4e04aeddbc9699acb/greenlet-3.2.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:58b97143c9cc7b86fc458f215bd0932f1757ce649e05b640fea2e79b54cedb31", size = 635417, upload-time = "2025-08-07T13:18:25.189Z" },
- { url = "https://files.pythonhosted.org/packages/7f/91/ae2eb6b7979e2f9b035a9f612cf70f1bf54aad4e1d125129bef1eae96f19/greenlet-3.2.4-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c2ca18a03a8cfb5b25bc1cbe20f3d9a4c80d8c3b13ba3df49ac3961af0b1018d", size = 584358, upload-time = "2025-08-07T13:18:23.708Z" },
- { url = "https://files.pythonhosted.org/packages/f7/85/433de0c9c0252b22b16d413c9407e6cb3b41df7389afc366ca204dbc1393/greenlet-3.2.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9fe0a28a7b952a21e2c062cd5756d34354117796c6d9215a87f55e38d15402c5", size = 1113550, upload-time = "2025-08-07T13:42:37.467Z" },
- { url = "https://files.pythonhosted.org/packages/a1/8d/88f3ebd2bc96bf7747093696f4335a0a8a4c5acfcf1b757717c0d2474ba3/greenlet-3.2.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8854167e06950ca75b898b104b63cc646573aa5fef1353d4508ecdd1ee76254f", size = 1137126, upload-time = "2025-08-07T13:18:20.239Z" },
- { url = "https://files.pythonhosted.org/packages/f1/29/74242b7d72385e29bcc5563fba67dad94943d7cd03552bac320d597f29b2/greenlet-3.2.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f47617f698838ba98f4ff4189aef02e7343952df3a615f847bb575c3feb177a7", size = 1544904, upload-time = "2025-11-04T12:42:04.763Z" },
- { url = "https://files.pythonhosted.org/packages/c8/e2/1572b8eeab0f77df5f6729d6ab6b141e4a84ee8eb9bc8c1e7918f94eda6d/greenlet-3.2.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:af41be48a4f60429d5cad9d22175217805098a9ef7c40bfef44f7669fb9d74d8", size = 1611228, upload-time = "2025-11-04T12:42:08.423Z" },
- { url = "https://files.pythonhosted.org/packages/d6/6f/b60b0291d9623c496638c582297ead61f43c4b72eef5e9c926ef4565ec13/greenlet-3.2.4-cp310-cp310-win_amd64.whl", hash = "sha256:73f49b5368b5359d04e18d15828eecc1806033db5233397748f4ca813ff1056c", size = 298654, upload-time = "2025-08-07T13:50:00.469Z" },
- { url = "https://files.pythonhosted.org/packages/a4/de/f28ced0a67749cac23fecb02b694f6473f47686dff6afaa211d186e2ef9c/greenlet-3.2.4-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:96378df1de302bc38e99c3a9aa311967b7dc80ced1dcc6f171e99842987882a2", size = 272305, upload-time = "2025-08-07T13:15:41.288Z" },
- { url = "https://files.pythonhosted.org/packages/09/16/2c3792cba130000bf2a31c5272999113f4764fd9d874fb257ff588ac779a/greenlet-3.2.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1ee8fae0519a337f2329cb78bd7a8e128ec0f881073d43f023c7b8d4831d5246", size = 632472, upload-time = "2025-08-07T13:42:55.044Z" },
- { url = "https://files.pythonhosted.org/packages/ae/8f/95d48d7e3d433e6dae5b1682e4292242a53f22df82e6d3dda81b1701a960/greenlet-3.2.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:94abf90142c2a18151632371140b3dba4dee031633fe614cb592dbb6c9e17bc3", size = 644646, upload-time = "2025-08-07T13:45:26.523Z" },
- { url = "https://files.pythonhosted.org/packages/d5/5e/405965351aef8c76b8ef7ad370e5da58d57ef6068df197548b015464001a/greenlet-3.2.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:4d1378601b85e2e5171b99be8d2dc85f594c79967599328f95c1dc1a40f1c633", size = 640519, upload-time = "2025-08-07T13:53:13.928Z" },
- { url = "https://files.pythonhosted.org/packages/25/5d/382753b52006ce0218297ec1b628e048c4e64b155379331f25a7316eb749/greenlet-3.2.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0db5594dce18db94f7d1650d7489909b57afde4c580806b8d9203b6e79cdc079", size = 639707, upload-time = "2025-08-07T13:18:27.146Z" },
- { url = "https://files.pythonhosted.org/packages/1f/8e/abdd3f14d735b2929290a018ecf133c901be4874b858dd1c604b9319f064/greenlet-3.2.4-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2523e5246274f54fdadbce8494458a2ebdcdbc7b802318466ac5606d3cded1f8", size = 587684, upload-time = "2025-08-07T13:18:25.164Z" },
- { url = "https://files.pythonhosted.org/packages/5d/65/deb2a69c3e5996439b0176f6651e0052542bb6c8f8ec2e3fba97c9768805/greenlet-3.2.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1987de92fec508535687fb807a5cea1560f6196285a4cde35c100b8cd632cc52", size = 1116647, upload-time = "2025-08-07T13:42:38.655Z" },
- { url = "https://files.pythonhosted.org/packages/3f/cc/b07000438a29ac5cfb2194bfc128151d52f333cee74dd7dfe3fb733fc16c/greenlet-3.2.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:55e9c5affaa6775e2c6b67659f3a71684de4c549b3dd9afca3bc773533d284fa", size = 1142073, upload-time = "2025-08-07T13:18:21.737Z" },
- { url = "https://files.pythonhosted.org/packages/67/24/28a5b2fa42d12b3d7e5614145f0bd89714c34c08be6aabe39c14dd52db34/greenlet-3.2.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c9c6de1940a7d828635fbd254d69db79e54619f165ee7ce32fda763a9cb6a58c", size = 1548385, upload-time = "2025-11-04T12:42:11.067Z" },
- { url = "https://files.pythonhosted.org/packages/6a/05/03f2f0bdd0b0ff9a4f7b99333d57b53a7709c27723ec8123056b084e69cd/greenlet-3.2.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:03c5136e7be905045160b1b9fdca93dd6727b180feeafda6818e6496434ed8c5", size = 1613329, upload-time = "2025-11-04T12:42:12.928Z" },
- { url = "https://files.pythonhosted.org/packages/d8/0f/30aef242fcab550b0b3520b8e3561156857c94288f0332a79928c31a52cf/greenlet-3.2.4-cp311-cp311-win_amd64.whl", hash = "sha256:9c40adce87eaa9ddb593ccb0fa6a07caf34015a29bf8d344811665b573138db9", size = 299100, upload-time = "2025-08-07T13:44:12.287Z" },
- { url = "https://files.pythonhosted.org/packages/44/69/9b804adb5fd0671f367781560eb5eb586c4d495277c93bde4307b9e28068/greenlet-3.2.4-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:3b67ca49f54cede0186854a008109d6ee71f66bd57bb36abd6d0a0267b540cdd", size = 274079, upload-time = "2025-08-07T13:15:45.033Z" },
- { url = "https://files.pythonhosted.org/packages/46/e9/d2a80c99f19a153eff70bc451ab78615583b8dac0754cfb942223d2c1a0d/greenlet-3.2.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ddf9164e7a5b08e9d22511526865780a576f19ddd00d62f8a665949327fde8bb", size = 640997, upload-time = "2025-08-07T13:42:56.234Z" },
- { url = "https://files.pythonhosted.org/packages/3b/16/035dcfcc48715ccd345f3a93183267167cdd162ad123cd93067d86f27ce4/greenlet-3.2.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f28588772bb5fb869a8eb331374ec06f24a83a9c25bfa1f38b6993afe9c1e968", size = 655185, upload-time = "2025-08-07T13:45:27.624Z" },
- { url = "https://files.pythonhosted.org/packages/31/da/0386695eef69ffae1ad726881571dfe28b41970173947e7c558d9998de0f/greenlet-3.2.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:5c9320971821a7cb77cfab8d956fa8e39cd07ca44b6070db358ceb7f8797c8c9", size = 649926, upload-time = "2025-08-07T13:53:15.251Z" },
- { url = "https://files.pythonhosted.org/packages/68/88/69bf19fd4dc19981928ceacbc5fd4bb6bc2215d53199e367832e98d1d8fe/greenlet-3.2.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c60a6d84229b271d44b70fb6e5fa23781abb5d742af7b808ae3f6efd7c9c60f6", size = 651839, upload-time = "2025-08-07T13:18:30.281Z" },
- { url = "https://files.pythonhosted.org/packages/19/0d/6660d55f7373b2ff8152401a83e02084956da23ae58cddbfb0b330978fe9/greenlet-3.2.4-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3b3812d8d0c9579967815af437d96623f45c0f2ae5f04e366de62a12d83a8fb0", size = 607586, upload-time = "2025-08-07T13:18:28.544Z" },
- { url = "https://files.pythonhosted.org/packages/8e/1a/c953fdedd22d81ee4629afbb38d2f9d71e37d23caace44775a3a969147d4/greenlet-3.2.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:abbf57b5a870d30c4675928c37278493044d7c14378350b3aa5d484fa65575f0", size = 1123281, upload-time = "2025-08-07T13:42:39.858Z" },
- { url = "https://files.pythonhosted.org/packages/3f/c7/12381b18e21aef2c6bd3a636da1088b888b97b7a0362fac2e4de92405f97/greenlet-3.2.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:20fb936b4652b6e307b8f347665e2c615540d4b42b3b4c8a321d8286da7e520f", size = 1151142, upload-time = "2025-08-07T13:18:22.981Z" },
- { url = "https://files.pythonhosted.org/packages/27/45/80935968b53cfd3f33cf99ea5f08227f2646e044568c9b1555b58ffd61c2/greenlet-3.2.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ee7a6ec486883397d70eec05059353b8e83eca9168b9f3f9a361971e77e0bcd0", size = 1564846, upload-time = "2025-11-04T12:42:15.191Z" },
- { url = "https://files.pythonhosted.org/packages/69/02/b7c30e5e04752cb4db6202a3858b149c0710e5453b71a3b2aec5d78a1aab/greenlet-3.2.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:326d234cbf337c9c3def0676412eb7040a35a768efc92504b947b3e9cfc7543d", size = 1633814, upload-time = "2025-11-04T12:42:17.175Z" },
- { url = "https://files.pythonhosted.org/packages/e9/08/b0814846b79399e585f974bbeebf5580fbe59e258ea7be64d9dfb253c84f/greenlet-3.2.4-cp312-cp312-win_amd64.whl", hash = "sha256:a7d4e128405eea3814a12cc2605e0e6aedb4035bf32697f72deca74de4105e02", size = 299899, upload-time = "2025-08-07T13:38:53.448Z" },
- { url = "https://files.pythonhosted.org/packages/49/e8/58c7f85958bda41dafea50497cbd59738c5c43dbbea5ee83d651234398f4/greenlet-3.2.4-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:1a921e542453fe531144e91e1feedf12e07351b1cf6c9e8a3325ea600a715a31", size = 272814, upload-time = "2025-08-07T13:15:50.011Z" },
- { url = "https://files.pythonhosted.org/packages/62/dd/b9f59862e9e257a16e4e610480cfffd29e3fae018a68c2332090b53aac3d/greenlet-3.2.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cd3c8e693bff0fff6ba55f140bf390fa92c994083f838fece0f63be121334945", size = 641073, upload-time = "2025-08-07T13:42:57.23Z" },
- { url = "https://files.pythonhosted.org/packages/f7/0b/bc13f787394920b23073ca3b6c4a7a21396301ed75a655bcb47196b50e6e/greenlet-3.2.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:710638eb93b1fa52823aa91bf75326f9ecdfd5e0466f00789246a5280f4ba0fc", size = 655191, upload-time = "2025-08-07T13:45:29.752Z" },
- { url = "https://files.pythonhosted.org/packages/f2/d6/6adde57d1345a8d0f14d31e4ab9c23cfe8e2cd39c3baf7674b4b0338d266/greenlet-3.2.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:c5111ccdc9c88f423426df3fd1811bfc40ed66264d35aa373420a34377efc98a", size = 649516, upload-time = "2025-08-07T13:53:16.314Z" },
- { url = "https://files.pythonhosted.org/packages/7f/3b/3a3328a788d4a473889a2d403199932be55b1b0060f4ddd96ee7cdfcad10/greenlet-3.2.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d76383238584e9711e20ebe14db6c88ddcedc1829a9ad31a584389463b5aa504", size = 652169, upload-time = "2025-08-07T13:18:32.861Z" },
- { url = "https://files.pythonhosted.org/packages/ee/43/3cecdc0349359e1a527cbf2e3e28e5f8f06d3343aaf82ca13437a9aa290f/greenlet-3.2.4-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:23768528f2911bcd7e475210822ffb5254ed10d71f4028387e5a99b4c6699671", size = 610497, upload-time = "2025-08-07T13:18:31.636Z" },
- { url = "https://files.pythonhosted.org/packages/b8/19/06b6cf5d604e2c382a6f31cafafd6f33d5dea706f4db7bdab184bad2b21d/greenlet-3.2.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:00fadb3fedccc447f517ee0d3fd8fe49eae949e1cd0f6a611818f4f6fb7dc83b", size = 1121662, upload-time = "2025-08-07T13:42:41.117Z" },
- { url = "https://files.pythonhosted.org/packages/a2/15/0d5e4e1a66fab130d98168fe984c509249c833c1a3c16806b90f253ce7b9/greenlet-3.2.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:d25c5091190f2dc0eaa3f950252122edbbadbb682aa7b1ef2f8af0f8c0afefae", size = 1149210, upload-time = "2025-08-07T13:18:24.072Z" },
- { url = "https://files.pythonhosted.org/packages/1c/53/f9c440463b3057485b8594d7a638bed53ba531165ef0ca0e6c364b5cc807/greenlet-3.2.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6e343822feb58ac4d0a1211bd9399de2b3a04963ddeec21530fc426cc121f19b", size = 1564759, upload-time = "2025-11-04T12:42:19.395Z" },
- { url = "https://files.pythonhosted.org/packages/47/e4/3bb4240abdd0a8d23f4f88adec746a3099f0d86bfedb623f063b2e3b4df0/greenlet-3.2.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ca7f6f1f2649b89ce02f6f229d7c19f680a6238af656f61e0115b24857917929", size = 1634288, upload-time = "2025-11-04T12:42:21.174Z" },
- { url = "https://files.pythonhosted.org/packages/0b/55/2321e43595e6801e105fcfdee02b34c0f996eb71e6ddffca6b10b7e1d771/greenlet-3.2.4-cp313-cp313-win_amd64.whl", hash = "sha256:554b03b6e73aaabec3745364d6239e9e012d64c68ccd0b8430c64ccc14939a8b", size = 299685, upload-time = "2025-08-07T13:24:38.824Z" },
- { url = "https://files.pythonhosted.org/packages/22/5c/85273fd7cc388285632b0498dbbab97596e04b154933dfe0f3e68156c68c/greenlet-3.2.4-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:49a30d5fda2507ae77be16479bdb62a660fa51b1eb4928b524975b3bde77b3c0", size = 273586, upload-time = "2025-08-07T13:16:08.004Z" },
- { url = "https://files.pythonhosted.org/packages/d1/75/10aeeaa3da9332c2e761e4c50d4c3556c21113ee3f0afa2cf5769946f7a3/greenlet-3.2.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:299fd615cd8fc86267b47597123e3f43ad79c9d8a22bebdce535e53550763e2f", size = 686346, upload-time = "2025-08-07T13:42:59.944Z" },
- { url = "https://files.pythonhosted.org/packages/c0/aa/687d6b12ffb505a4447567d1f3abea23bd20e73a5bed63871178e0831b7a/greenlet-3.2.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:c17b6b34111ea72fc5a4e4beec9711d2226285f0386ea83477cbb97c30a3f3a5", size = 699218, upload-time = "2025-08-07T13:45:30.969Z" },
- { url = "https://files.pythonhosted.org/packages/dc/8b/29aae55436521f1d6f8ff4e12fb676f3400de7fcf27fccd1d4d17fd8fecd/greenlet-3.2.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b4a1870c51720687af7fa3e7cda6d08d801dae660f75a76f3845b642b4da6ee1", size = 694659, upload-time = "2025-08-07T13:53:17.759Z" },
- { url = "https://files.pythonhosted.org/packages/92/2e/ea25914b1ebfde93b6fc4ff46d6864564fba59024e928bdc7de475affc25/greenlet-3.2.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:061dc4cf2c34852b052a8620d40f36324554bc192be474b9e9770e8c042fd735", size = 695355, upload-time = "2025-08-07T13:18:34.517Z" },
- { url = "https://files.pythonhosted.org/packages/72/60/fc56c62046ec17f6b0d3060564562c64c862948c9d4bc8aa807cf5bd74f4/greenlet-3.2.4-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:44358b9bf66c8576a9f57a590d5f5d6e72fa4228b763d0e43fee6d3b06d3a337", size = 657512, upload-time = "2025-08-07T13:18:33.969Z" },
- { url = "https://files.pythonhosted.org/packages/23/6e/74407aed965a4ab6ddd93a7ded3180b730d281c77b765788419484cdfeef/greenlet-3.2.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2917bdf657f5859fbf3386b12d68ede4cf1f04c90c3a6bc1f013dd68a22e2269", size = 1612508, upload-time = "2025-11-04T12:42:23.427Z" },
- { url = "https://files.pythonhosted.org/packages/0d/da/343cd760ab2f92bac1845ca07ee3faea9fe52bee65f7bcb19f16ad7de08b/greenlet-3.2.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:015d48959d4add5d6c9f6c5210ee3803a830dce46356e3bc326d6776bde54681", size = 1680760, upload-time = "2025-11-04T12:42:25.341Z" },
- { url = "https://files.pythonhosted.org/packages/e3/a5/6ddab2b4c112be95601c13428db1d8b6608a8b6039816f2ba09c346c08fc/greenlet-3.2.4-cp314-cp314-win_amd64.whl", hash = "sha256:e37ab26028f12dbb0ff65f29a8d3d44a765c61e729647bf2ddfbbed621726f01", size = 303425, upload-time = "2025-08-07T13:32:27.59Z" },
- { url = "https://files.pythonhosted.org/packages/f7/c0/93885c4106d2626bf51fdec377d6aef740dfa5c4877461889a7cf8e565cc/greenlet-3.2.4-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:b6a7c19cf0d2742d0809a4c05975db036fdff50cd294a93632d6a310bf9ac02c", size = 269859, upload-time = "2025-08-07T13:16:16.003Z" },
- { url = "https://files.pythonhosted.org/packages/4d/f5/33f05dc3ba10a02dedb1485870cf81c109227d3d3aa280f0e48486cac248/greenlet-3.2.4-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:27890167f55d2387576d1f41d9487ef171849ea0359ce1510ca6e06c8bece11d", size = 627610, upload-time = "2025-08-07T13:43:01.345Z" },
- { url = "https://files.pythonhosted.org/packages/b2/a7/9476decef51a0844195f99ed5dc611d212e9b3515512ecdf7321543a7225/greenlet-3.2.4-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:18d9260df2b5fbf41ae5139e1be4e796d99655f023a636cd0e11e6406cca7d58", size = 639417, upload-time = "2025-08-07T13:45:32.094Z" },
- { url = "https://files.pythonhosted.org/packages/bd/e0/849b9159cbb176f8c0af5caaff1faffdece7a8417fcc6fe1869770e33e21/greenlet-3.2.4-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:671df96c1f23c4a0d4077a325483c1503c96a1b7d9db26592ae770daa41233d4", size = 634751, upload-time = "2025-08-07T13:53:18.848Z" },
- { url = "https://files.pythonhosted.org/packages/5f/d3/844e714a9bbd39034144dca8b658dcd01839b72bb0ec7d8014e33e3705f0/greenlet-3.2.4-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:16458c245a38991aa19676900d48bd1a6f2ce3e16595051a4db9d012154e8433", size = 634020, upload-time = "2025-08-07T13:18:36.841Z" },
- { url = "https://files.pythonhosted.org/packages/6b/4c/f3de2a8de0e840ecb0253ad0dc7e2bb3747348e798ec7e397d783a3cb380/greenlet-3.2.4-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c9913f1a30e4526f432991f89ae263459b1c64d1608c0d22a5c79c287b3c70df", size = 582817, upload-time = "2025-08-07T13:18:35.48Z" },
- { url = "https://files.pythonhosted.org/packages/89/80/7332915adc766035c8980b161c2e5d50b2f941f453af232c164cff5e0aeb/greenlet-3.2.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b90654e092f928f110e0007f572007c9727b5265f7632c2fa7415b4689351594", size = 1111985, upload-time = "2025-08-07T13:42:42.425Z" },
- { url = "https://files.pythonhosted.org/packages/66/71/1928e2c80197353bcb9b50aa19c4d8e26ee6d7a900c564907665cf4b9a41/greenlet-3.2.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:81701fd84f26330f0d5f4944d4e92e61afe6319dcd9775e39396e39d7c3e5f98", size = 1136137, upload-time = "2025-08-07T13:18:26.168Z" },
- { url = "https://files.pythonhosted.org/packages/4b/bf/7bd33643e48ed45dcc0e22572f650767832bd4e1287f97434943cc402148/greenlet-3.2.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:28a3c6b7cd72a96f61b0e4b2a36f681025b60ae4779cc73c1535eb5f29560b10", size = 1542941, upload-time = "2025-11-04T12:42:27.427Z" },
- { url = "https://files.pythonhosted.org/packages/9b/74/4bc433f91d0d09a1c22954a371f9df928cb85e72640870158853a83415e5/greenlet-3.2.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:52206cd642670b0b320a1fd1cbfd95bca0e043179c1d8a045f2c6109dfe973be", size = 1609685, upload-time = "2025-11-04T12:42:29.242Z" },
- { url = "https://files.pythonhosted.org/packages/89/48/a5dc74dde38aeb2b15d418cec76ed50e1dd3d620ccda84d8199703248968/greenlet-3.2.4-cp39-cp39-win32.whl", hash = "sha256:65458b409c1ed459ea899e939f0e1cdb14f58dbc803f2f93c5eab5694d32671b", size = 281400, upload-time = "2025-08-07T14:02:20.263Z" },
- { url = "https://files.pythonhosted.org/packages/e5/44/342c4591db50db1076b8bda86ed0ad59240e3e1da17806a4cf10a6d0e447/greenlet-3.2.4-cp39-cp39-win_amd64.whl", hash = "sha256:d2e685ade4dafd447ede19c31277a224a239a0a1a4eca4e6390efedf20260cfb", size = 298533, upload-time = "2025-08-07T13:56:34.168Z" },
-]
-
-[[package]]
-name = "grpcio"
-version = "1.74.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/38/b4/35feb8f7cab7239c5b94bd2db71abb3d6adb5f335ad8f131abb6060840b6/grpcio-1.74.0.tar.gz", hash = "sha256:80d1f4fbb35b0742d3e3d3bb654b7381cd5f015f8497279a1e9c21ba623e01b1", size = 12756048, upload-time = "2025-07-24T18:54:23.039Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/66/54/68e51a90797ad7afc5b0a7881426c337f6a9168ebab73c3210b76aa7c90d/grpcio-1.74.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:85bd5cdf4ed7b2d6438871adf6afff9af7096486fcf51818a81b77ef4dd30907", size = 5481935, upload-time = "2025-07-24T18:52:43.756Z" },
- { url = "https://files.pythonhosted.org/packages/32/2a/af817c7e9843929e93e54d09c9aee2555c2e8d81b93102a9426b36e91833/grpcio-1.74.0-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:68c8ebcca945efff9d86d8d6d7bfb0841cf0071024417e2d7f45c5e46b5b08eb", size = 10986796, upload-time = "2025-07-24T18:52:47.219Z" },
- { url = "https://files.pythonhosted.org/packages/d5/94/d67756638d7bb07750b07d0826c68e414124574b53840ba1ff777abcd388/grpcio-1.74.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:e154d230dc1bbbd78ad2fdc3039fa50ad7ffcf438e4eb2fa30bce223a70c7486", size = 5983663, upload-time = "2025-07-24T18:52:49.463Z" },
- { url = "https://files.pythonhosted.org/packages/35/f5/c5e4853bf42148fea8532d49e919426585b73eafcf379a712934652a8de9/grpcio-1.74.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e8978003816c7b9eabe217f88c78bc26adc8f9304bf6a594b02e5a49b2ef9c11", size = 6653765, upload-time = "2025-07-24T18:52:51.094Z" },
- { url = "https://files.pythonhosted.org/packages/fd/75/a1991dd64b331d199935e096cc9daa3415ee5ccbe9f909aa48eded7bba34/grpcio-1.74.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3d7bd6e3929fd2ea7fbc3f562e4987229ead70c9ae5f01501a46701e08f1ad9", size = 6215172, upload-time = "2025-07-24T18:52:53.282Z" },
- { url = "https://files.pythonhosted.org/packages/01/a4/7cef3dbb3b073d0ce34fd507efc44ac4c9442a0ef9fba4fb3f5c551efef5/grpcio-1.74.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:136b53c91ac1d02c8c24201bfdeb56f8b3ac3278668cbb8e0ba49c88069e1bdc", size = 6329142, upload-time = "2025-07-24T18:52:54.927Z" },
- { url = "https://files.pythonhosted.org/packages/bf/d3/587920f882b46e835ad96014087054655312400e2f1f1446419e5179a383/grpcio-1.74.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fe0f540750a13fd8e5da4b3eaba91a785eea8dca5ccd2bc2ffe978caa403090e", size = 7018632, upload-time = "2025-07-24T18:52:56.523Z" },
- { url = "https://files.pythonhosted.org/packages/1f/95/c70a3b15a0bc83334b507e3d2ae20ee8fa38d419b8758a4d838f5c2a7d32/grpcio-1.74.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4e4181bfc24413d1e3a37a0b7889bea68d973d4b45dd2bc68bb766c140718f82", size = 6509641, upload-time = "2025-07-24T18:52:58.495Z" },
- { url = "https://files.pythonhosted.org/packages/4b/06/2e7042d06247d668ae69ea6998eca33f475fd4e2855f94dcb2aa5daef334/grpcio-1.74.0-cp310-cp310-win32.whl", hash = "sha256:1733969040989f7acc3d94c22f55b4a9501a30f6aaacdbccfaba0a3ffb255ab7", size = 3817478, upload-time = "2025-07-24T18:53:00.128Z" },
- { url = "https://files.pythonhosted.org/packages/93/20/e02b9dcca3ee91124060b65bbf5b8e1af80b3b76a30f694b44b964ab4d71/grpcio-1.74.0-cp310-cp310-win_amd64.whl", hash = "sha256:9e912d3c993a29df6c627459af58975b2e5c897d93287939b9d5065f000249b5", size = 4493971, upload-time = "2025-07-24T18:53:02.068Z" },
- { url = "https://files.pythonhosted.org/packages/e7/77/b2f06db9f240a5abeddd23a0e49eae2b6ac54d85f0e5267784ce02269c3b/grpcio-1.74.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:69e1a8180868a2576f02356565f16635b99088da7df3d45aaa7e24e73a054e31", size = 5487368, upload-time = "2025-07-24T18:53:03.548Z" },
- { url = "https://files.pythonhosted.org/packages/48/99/0ac8678a819c28d9a370a663007581744a9f2a844e32f0fa95e1ddda5b9e/grpcio-1.74.0-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:8efe72fde5500f47aca1ef59495cb59c885afe04ac89dd11d810f2de87d935d4", size = 10999804, upload-time = "2025-07-24T18:53:05.095Z" },
- { url = "https://files.pythonhosted.org/packages/45/c6/a2d586300d9e14ad72e8dc211c7aecb45fe9846a51e558c5bca0c9102c7f/grpcio-1.74.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:a8f0302f9ac4e9923f98d8e243939a6fb627cd048f5cd38595c97e38020dffce", size = 5987667, upload-time = "2025-07-24T18:53:07.157Z" },
- { url = "https://files.pythonhosted.org/packages/c9/57/5f338bf56a7f22584e68d669632e521f0de460bb3749d54533fc3d0fca4f/grpcio-1.74.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2f609a39f62a6f6f05c7512746798282546358a37ea93c1fcbadf8b2fed162e3", size = 6655612, upload-time = "2025-07-24T18:53:09.244Z" },
- { url = "https://files.pythonhosted.org/packages/82/ea/a4820c4c44c8b35b1903a6c72a5bdccec92d0840cf5c858c498c66786ba5/grpcio-1.74.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c98e0b7434a7fa4e3e63f250456eaef52499fba5ae661c58cc5b5477d11e7182", size = 6219544, upload-time = "2025-07-24T18:53:11.221Z" },
- { url = "https://files.pythonhosted.org/packages/a4/17/0537630a921365928f5abb6d14c79ba4dcb3e662e0dbeede8af4138d9dcf/grpcio-1.74.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:662456c4513e298db6d7bd9c3b8df6f75f8752f0ba01fb653e252ed4a59b5a5d", size = 6334863, upload-time = "2025-07-24T18:53:12.925Z" },
- { url = "https://files.pythonhosted.org/packages/e2/a6/85ca6cb9af3f13e1320d0a806658dca432ff88149d5972df1f7b51e87127/grpcio-1.74.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:3d14e3c4d65e19d8430a4e28ceb71ace4728776fd6c3ce34016947474479683f", size = 7019320, upload-time = "2025-07-24T18:53:15.002Z" },
- { url = "https://files.pythonhosted.org/packages/4f/a7/fe2beab970a1e25d2eff108b3cf4f7d9a53c185106377a3d1989216eba45/grpcio-1.74.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1bf949792cee20d2078323a9b02bacbbae002b9e3b9e2433f2741c15bdeba1c4", size = 6514228, upload-time = "2025-07-24T18:53:16.999Z" },
- { url = "https://files.pythonhosted.org/packages/6a/c2/2f9c945c8a248cebc3ccda1b7a1bf1775b9d7d59e444dbb18c0014e23da6/grpcio-1.74.0-cp311-cp311-win32.whl", hash = "sha256:55b453812fa7c7ce2f5c88be3018fb4a490519b6ce80788d5913f3f9d7da8c7b", size = 3817216, upload-time = "2025-07-24T18:53:20.564Z" },
- { url = "https://files.pythonhosted.org/packages/ff/d1/a9cf9c94b55becda2199299a12b9feef0c79946b0d9d34c989de6d12d05d/grpcio-1.74.0-cp311-cp311-win_amd64.whl", hash = "sha256:86ad489db097141a907c559988c29718719aa3e13370d40e20506f11b4de0d11", size = 4495380, upload-time = "2025-07-24T18:53:22.058Z" },
- { url = "https://files.pythonhosted.org/packages/4c/5d/e504d5d5c4469823504f65687d6c8fb97b7f7bf0b34873b7598f1df24630/grpcio-1.74.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:8533e6e9c5bd630ca98062e3a1326249e6ada07d05acf191a77bc33f8948f3d8", size = 5445551, upload-time = "2025-07-24T18:53:23.641Z" },
- { url = "https://files.pythonhosted.org/packages/43/01/730e37056f96f2f6ce9f17999af1556df62ee8dab7fa48bceeaab5fd3008/grpcio-1.74.0-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:2918948864fec2a11721d91568effffbe0a02b23ecd57f281391d986847982f6", size = 10979810, upload-time = "2025-07-24T18:53:25.349Z" },
- { url = "https://files.pythonhosted.org/packages/79/3d/09fd100473ea5c47083889ca47ffd356576173ec134312f6aa0e13111dee/grpcio-1.74.0-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:60d2d48b0580e70d2e1954d0d19fa3c2e60dd7cbed826aca104fff518310d1c5", size = 5941946, upload-time = "2025-07-24T18:53:27.387Z" },
- { url = "https://files.pythonhosted.org/packages/8a/99/12d2cca0a63c874c6d3d195629dcd85cdf5d6f98a30d8db44271f8a97b93/grpcio-1.74.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3601274bc0523f6dc07666c0e01682c94472402ac2fd1226fd96e079863bfa49", size = 6621763, upload-time = "2025-07-24T18:53:29.193Z" },
- { url = "https://files.pythonhosted.org/packages/9d/2c/930b0e7a2f1029bbc193443c7bc4dc2a46fedb0203c8793dcd97081f1520/grpcio-1.74.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:176d60a5168d7948539def20b2a3adcce67d72454d9ae05969a2e73f3a0feee7", size = 6180664, upload-time = "2025-07-24T18:53:30.823Z" },
- { url = "https://files.pythonhosted.org/packages/db/d5/ff8a2442180ad0867717e670f5ec42bfd8d38b92158ad6bcd864e6d4b1ed/grpcio-1.74.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e759f9e8bc908aaae0412642afe5416c9f983a80499448fcc7fab8692ae044c3", size = 6301083, upload-time = "2025-07-24T18:53:32.454Z" },
- { url = "https://files.pythonhosted.org/packages/b0/ba/b361d390451a37ca118e4ec7dccec690422e05bc85fba2ec72b06cefec9f/grpcio-1.74.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:9e7c4389771855a92934b2846bd807fc25a3dfa820fd912fe6bd8136026b2707", size = 6994132, upload-time = "2025-07-24T18:53:34.506Z" },
- { url = "https://files.pythonhosted.org/packages/3b/0c/3a5fa47d2437a44ced74141795ac0251bbddeae74bf81df3447edd767d27/grpcio-1.74.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:cce634b10aeab37010449124814b05a62fb5f18928ca878f1bf4750d1f0c815b", size = 6489616, upload-time = "2025-07-24T18:53:36.217Z" },
- { url = "https://files.pythonhosted.org/packages/ae/95/ab64703b436d99dc5217228babc76047d60e9ad14df129e307b5fec81fd0/grpcio-1.74.0-cp312-cp312-win32.whl", hash = "sha256:885912559974df35d92219e2dc98f51a16a48395f37b92865ad45186f294096c", size = 3807083, upload-time = "2025-07-24T18:53:37.911Z" },
- { url = "https://files.pythonhosted.org/packages/84/59/900aa2445891fc47a33f7d2f76e00ca5d6ae6584b20d19af9c06fa09bf9a/grpcio-1.74.0-cp312-cp312-win_amd64.whl", hash = "sha256:42f8fee287427b94be63d916c90399ed310ed10aadbf9e2e5538b3e497d269bc", size = 4490123, upload-time = "2025-07-24T18:53:39.528Z" },
- { url = "https://files.pythonhosted.org/packages/d4/d8/1004a5f468715221450e66b051c839c2ce9a985aa3ee427422061fcbb6aa/grpcio-1.74.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:2bc2d7d8d184e2362b53905cb1708c84cb16354771c04b490485fa07ce3a1d89", size = 5449488, upload-time = "2025-07-24T18:53:41.174Z" },
- { url = "https://files.pythonhosted.org/packages/94/0e/33731a03f63740d7743dced423846c831d8e6da808fcd02821a4416df7fa/grpcio-1.74.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:c14e803037e572c177ba54a3e090d6eb12efd795d49327c5ee2b3bddb836bf01", size = 10974059, upload-time = "2025-07-24T18:53:43.066Z" },
- { url = "https://files.pythonhosted.org/packages/0d/c6/3d2c14d87771a421205bdca991467cfe473ee4c6a1231c1ede5248c62ab8/grpcio-1.74.0-cp313-cp313-manylinux_2_17_aarch64.whl", hash = "sha256:f6ec94f0e50eb8fa1744a731088b966427575e40c2944a980049798b127a687e", size = 5945647, upload-time = "2025-07-24T18:53:45.269Z" },
- { url = "https://files.pythonhosted.org/packages/c5/83/5a354c8aaff58594eef7fffebae41a0f8995a6258bbc6809b800c33d4c13/grpcio-1.74.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:566b9395b90cc3d0d0c6404bc8572c7c18786ede549cdb540ae27b58afe0fb91", size = 6626101, upload-time = "2025-07-24T18:53:47.015Z" },
- { url = "https://files.pythonhosted.org/packages/3f/ca/4fdc7bf59bf6994aa45cbd4ef1055cd65e2884de6113dbd49f75498ddb08/grpcio-1.74.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1ea6176d7dfd5b941ea01c2ec34de9531ba494d541fe2057c904e601879f249", size = 6182562, upload-time = "2025-07-24T18:53:48.967Z" },
- { url = "https://files.pythonhosted.org/packages/fd/48/2869e5b2c1922583686f7ae674937986807c2f676d08be70d0a541316270/grpcio-1.74.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:64229c1e9cea079420527fa8ac45d80fc1e8d3f94deaa35643c381fa8d98f362", size = 6303425, upload-time = "2025-07-24T18:53:50.847Z" },
- { url = "https://files.pythonhosted.org/packages/a6/0e/bac93147b9a164f759497bc6913e74af1cb632c733c7af62c0336782bd38/grpcio-1.74.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:0f87bddd6e27fc776aacf7ebfec367b6d49cad0455123951e4488ea99d9b9b8f", size = 6996533, upload-time = "2025-07-24T18:53:52.747Z" },
- { url = "https://files.pythonhosted.org/packages/84/35/9f6b2503c1fd86d068b46818bbd7329db26a87cdd8c01e0d1a9abea1104c/grpcio-1.74.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:3b03d8f2a07f0fea8c8f74deb59f8352b770e3900d143b3d1475effcb08eec20", size = 6491489, upload-time = "2025-07-24T18:53:55.06Z" },
- { url = "https://files.pythonhosted.org/packages/75/33/a04e99be2a82c4cbc4039eb3a76f6c3632932b9d5d295221389d10ac9ca7/grpcio-1.74.0-cp313-cp313-win32.whl", hash = "sha256:b6a73b2ba83e663b2480a90b82fdae6a7aa6427f62bf43b29912c0cfd1aa2bfa", size = 3805811, upload-time = "2025-07-24T18:53:56.798Z" },
- { url = "https://files.pythonhosted.org/packages/34/80/de3eb55eb581815342d097214bed4c59e806b05f1b3110df03b2280d6dfd/grpcio-1.74.0-cp313-cp313-win_amd64.whl", hash = "sha256:fd3c71aeee838299c5887230b8a1822795325ddfea635edd82954c1eaa831e24", size = 4489214, upload-time = "2025-07-24T18:53:59.771Z" },
- { url = "https://files.pythonhosted.org/packages/0d/de/dd7db504703c3b669f1b83265e2fbb5d79c8d3da86ea52cbd9202b9a8b05/grpcio-1.74.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:4bc5fca10aaf74779081e16c2bcc3d5ec643ffd528d9e7b1c9039000ead73bae", size = 5480998, upload-time = "2025-07-24T18:54:01.868Z" },
- { url = "https://files.pythonhosted.org/packages/1c/57/6537ace3af4c97f2b013ceff1f2e789c52b8448334ca3a0c36e7421cf6ed/grpcio-1.74.0-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:6bab67d15ad617aff094c382c882e0177637da73cbc5532d52c07b4ee887a87b", size = 10990945, upload-time = "2025-07-24T18:54:03.854Z" },
- { url = "https://files.pythonhosted.org/packages/d8/f2/579410017de16cd27f35326df6fecde81bff6e9b43c871d28263fa8a77a4/grpcio-1.74.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:655726919b75ab3c34cdad39da5c530ac6fa32696fb23119e36b64adcfca174a", size = 5983968, upload-time = "2025-07-24T18:54:06.434Z" },
- { url = "https://files.pythonhosted.org/packages/a0/6a/0f3571003663d991f4ea953b82dc518fed094c182decc48c9b0242bec7e3/grpcio-1.74.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1a2b06afe2e50ebfd46247ac3ba60cac523f54ec7792ae9ba6073c12daf26f0a", size = 6654768, upload-time = "2025-07-24T18:54:08.632Z" },
- { url = "https://files.pythonhosted.org/packages/24/e3/1d42cb00e0390bacab3c9ee79e37416140d907c8c7c7a92654c535805963/grpcio-1.74.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f251c355167b2360537cf17bea2cf0197995e551ab9da6a0a59b3da5e8704f9", size = 6215627, upload-time = "2025-07-24T18:54:10.584Z" },
- { url = "https://files.pythonhosted.org/packages/77/84/4f8312bc4430eda1cdbc4e8689f54daa807b5d304d4ea53e9d27c448889b/grpcio-1.74.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:8f7b5882fb50632ab1e48cb3122d6df55b9afabc265582808036b6e51b9fd6b7", size = 6330938, upload-time = "2025-07-24T18:54:12.557Z" },
- { url = "https://files.pythonhosted.org/packages/2f/c0/422d2b40110716a4775212256a56ac71586be2403a7b7055818bfd0fc203/grpcio-1.74.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:834988b6c34515545b3edd13e902c1acdd9f2465d386ea5143fb558f153a7176", size = 7019216, upload-time = "2025-07-24T18:54:14.475Z" },
- { url = "https://files.pythonhosted.org/packages/6f/84/668ab6df27fb35886dfa1242f2d302d0cd319c72e3dd3845a322ecabf61b/grpcio-1.74.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:22b834cef33429ca6cc28303c9c327ba9a3fafecbf62fae17e9a7b7163cc43ac", size = 6510719, upload-time = "2025-07-24T18:54:16.775Z" },
- { url = "https://files.pythonhosted.org/packages/ed/6a/981150f20dd435b9c46cd504038b4fbae2171b43fe70019914d80159e156/grpcio-1.74.0-cp39-cp39-win32.whl", hash = "sha256:7d95d71ff35291bab3f1c52f52f474c632db26ea12700c2ff0ea0532cb0b5854", size = 3819185, upload-time = "2025-07-24T18:54:18.673Z" },
- { url = "https://files.pythonhosted.org/packages/75/5f/d64b9745bb9def186e1be11b42d4d310570799d6170ac75829ef1c67c176/grpcio-1.74.0-cp39-cp39-win_amd64.whl", hash = "sha256:ecde9ab49f58433abe02f9ed076c7b5be839cf0153883a6d23995937a82392fa", size = 4495789, upload-time = "2025-07-24T18:54:20.582Z" },
+postgres = [
+ { name = "psycopg", extra = ["binary"] },
]
-[[package]]
-name = "grpcio-status"
-version = "1.71.2"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "googleapis-common-protos" },
- { name = "grpcio" },
- { name = "protobuf" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/fd/d1/b6e9877fedae3add1afdeae1f89d1927d296da9cf977eca0eb08fb8a460e/grpcio_status-1.71.2.tar.gz", hash = "sha256:c7a97e176df71cdc2c179cd1847d7fc86cca5832ad12e9798d7fed6b7a1aab50", size = 13677, upload-time = "2025-06-28T04:24:05.426Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/67/58/317b0134129b556a93a3b0afe00ee675b5657f0155509e22fcb853bafe2d/grpcio_status-1.71.2-py3-none-any.whl", hash = "sha256:803c98cb6a8b7dc6dbb785b1111aed739f241ab5e9da0bba96888aa74704cfd3", size = 14424, upload-time = "2025-06-28T04:23:42.136Z" },
+[package.dev-dependencies]
+dev = [
+ { name = "pytest" },
+ { name = "pytest-asyncio" },
]
-[[package]]
-name = "h11"
-version = "0.16.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" },
+[package.metadata]
+requires-dist = [
+ { name = "cryptography", specifier = ">=42.0" },
+ { name = "discord-py", specifier = ">=2.3,<3.0" },
+ { name = "psycopg", extras = ["binary"], marker = "extra == 'postgres'", specifier = ">=3.2,<4.0" },
]
+provides-extras = ["postgres"]
-[[package]]
-name = "hf-xet"
-version = "1.1.9"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/23/0f/5b60fc28ee7f8cc17a5114a584fd6b86e11c3e0a6e142a7f97a161e9640a/hf_xet-1.1.9.tar.gz", hash = "sha256:c99073ce404462e909f1d5839b2d14a3827b8fe75ed8aed551ba6609c026c803", size = 484242, upload-time = "2025-08-27T23:05:19.441Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/de/12/56e1abb9a44cdef59a411fe8a8673313195711b5ecce27880eb9c8fa90bd/hf_xet-1.1.9-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:a3b6215f88638dd7a6ff82cb4e738dcbf3d863bf667997c093a3c990337d1160", size = 2762553, upload-time = "2025-08-27T23:05:15.153Z" },
- { url = "https://files.pythonhosted.org/packages/3a/e6/2d0d16890c5f21b862f5df3146519c182e7f0ae49b4b4bf2bd8a40d0b05e/hf_xet-1.1.9-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:9b486de7a64a66f9a172f4b3e0dfe79c9f0a93257c501296a2521a13495a698a", size = 2623216, upload-time = "2025-08-27T23:05:13.778Z" },
- { url = "https://files.pythonhosted.org/packages/81/42/7e6955cf0621e87491a1fb8cad755d5c2517803cea174229b0ec00ff0166/hf_xet-1.1.9-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a4c5a840c2c4e6ec875ed13703a60e3523bc7f48031dfd750923b2a4d1a5fc3c", size = 3186789, upload-time = "2025-08-27T23:05:12.368Z" },
- { url = "https://files.pythonhosted.org/packages/df/8b/759233bce05457f5f7ec062d63bbfd2d0c740b816279eaaa54be92aa452a/hf_xet-1.1.9-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:96a6139c9e44dad1c52c52520db0fffe948f6bce487cfb9d69c125f254bb3790", size = 3088747, upload-time = "2025-08-27T23:05:10.439Z" },
- { url = "https://files.pythonhosted.org/packages/6c/3c/28cc4db153a7601a996985bcb564f7b8f5b9e1a706c7537aad4b4809f358/hf_xet-1.1.9-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ad1022e9a998e784c97b2173965d07fe33ee26e4594770b7785a8cc8f922cd95", size = 3251429, upload-time = "2025-08-27T23:05:16.471Z" },
- { url = "https://files.pythonhosted.org/packages/84/17/7caf27a1d101bfcb05be85850d4aa0a265b2e1acc2d4d52a48026ef1d299/hf_xet-1.1.9-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:86754c2d6d5afb11b0a435e6e18911a4199262fe77553f8c50d75e21242193ea", size = 3354643, upload-time = "2025-08-27T23:05:17.828Z" },
- { url = "https://files.pythonhosted.org/packages/cd/50/0c39c9eed3411deadcc98749a6699d871b822473f55fe472fad7c01ec588/hf_xet-1.1.9-cp37-abi3-win_amd64.whl", hash = "sha256:5aad3933de6b725d61d51034e04174ed1dce7a57c63d530df0014dea15a40127", size = 2804797, upload-time = "2025-08-27T23:05:20.77Z" },
+[package.metadata.requires-dev]
+dev = [
+ { name = "pytest", specifier = ">=8.3.5" },
+ { name = "pytest-asyncio", specifier = ">=0.24" },
]
[[package]]
-name = "httpcore"
-version = "1.0.9"
+name = "multidict"
+version = "6.6.4"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "certifi" },
- { name = "h11" },
+ { name = "typing-extensions", marker = "python_full_version < '3.11'" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/69/7f/0652e6ed47ab288e3756ea9c0df8b14950781184d4bd7883f4d87dd41245/multidict-6.6.4.tar.gz", hash = "sha256:d2d4e4787672911b48350df02ed3fa3fffdc2f2e8ca06dd6afdf34189b76a9dd", size = 101843, upload-time = "2025-08-11T12:08:48.217Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" },
+ { url = "https://files.pythonhosted.org/packages/eb/6b/86f353088c1358e76fd30b0146947fddecee812703b604ee901e85cd2a80/multidict-6.6.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b8aa6f0bd8125ddd04a6593437bad6a7e70f300ff4180a531654aa2ab3f6d58f", size = 77054, upload-time = "2025-08-11T12:06:02.99Z" },
+ { url = "https://files.pythonhosted.org/packages/19/5d/c01dc3d3788bb877bd7f5753ea6eb23c1beeca8044902a8f5bfb54430f63/multidict-6.6.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b9e5853bbd7264baca42ffc53391b490d65fe62849bf2c690fa3f6273dbcd0cb", size = 44914, upload-time = "2025-08-11T12:06:05.264Z" },
+ { url = "https://files.pythonhosted.org/packages/46/44/964dae19ea42f7d3e166474d8205f14bb811020e28bc423d46123ddda763/multidict-6.6.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0af5f9dee472371e36d6ae38bde009bd8ce65ac7335f55dcc240379d7bed1495", size = 44601, upload-time = "2025-08-11T12:06:06.627Z" },
+ { url = "https://files.pythonhosted.org/packages/31/20/0616348a1dfb36cb2ab33fc9521de1f27235a397bf3f59338e583afadd17/multidict-6.6.4-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:d24f351e4d759f5054b641c81e8291e5d122af0fca5c72454ff77f7cbe492de8", size = 224821, upload-time = "2025-08-11T12:06:08.06Z" },
+ { url = "https://files.pythonhosted.org/packages/14/26/5d8923c69c110ff51861af05bd27ca6783011b96725d59ccae6d9daeb627/multidict-6.6.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:db6a3810eec08280a172a6cd541ff4a5f6a97b161d93ec94e6c4018917deb6b7", size = 242608, upload-time = "2025-08-11T12:06:09.697Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/cc/e2ad3ba9459aa34fa65cf1f82a5c4a820a2ce615aacfb5143b8817f76504/multidict-6.6.4-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a1b20a9d56b2d81e2ff52ecc0670d583eaabaa55f402e8d16dd062373dbbe796", size = 222324, upload-time = "2025-08-11T12:06:10.905Z" },
+ { url = "https://files.pythonhosted.org/packages/19/db/4ed0f65701afbc2cb0c140d2d02928bb0fe38dd044af76e58ad7c54fd21f/multidict-6.6.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8c9854df0eaa610a23494c32a6f44a3a550fb398b6b51a56e8c6b9b3689578db", size = 253234, upload-time = "2025-08-11T12:06:12.658Z" },
+ { url = "https://files.pythonhosted.org/packages/94/c1/5160c9813269e39ae14b73debb907bfaaa1beee1762da8c4fb95df4764ed/multidict-6.6.4-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4bb7627fd7a968f41905a4d6343b0d63244a0623f006e9ed989fa2b78f4438a0", size = 251613, upload-time = "2025-08-11T12:06:13.97Z" },
+ { url = "https://files.pythonhosted.org/packages/05/a9/48d1bd111fc2f8fb98b2ed7f9a115c55a9355358432a19f53c0b74d8425d/multidict-6.6.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:caebafea30ed049c57c673d0b36238b1748683be2593965614d7b0e99125c877", size = 241649, upload-time = "2025-08-11T12:06:15.204Z" },
+ { url = "https://files.pythonhosted.org/packages/85/2a/f7d743df0019408768af8a70d2037546a2be7b81fbb65f040d76caafd4c5/multidict-6.6.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ad887a8250eb47d3ab083d2f98db7f48098d13d42eb7a3b67d8a5c795f224ace", size = 239238, upload-time = "2025-08-11T12:06:16.467Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/b8/4f4bb13323c2d647323f7919201493cf48ebe7ded971717bfb0f1a79b6bf/multidict-6.6.4-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:ed8358ae7d94ffb7c397cecb62cbac9578a83ecefc1eba27b9090ee910e2efb6", size = 233517, upload-time = "2025-08-11T12:06:18.107Z" },
+ { url = "https://files.pythonhosted.org/packages/33/29/4293c26029ebfbba4f574febd2ed01b6f619cfa0d2e344217d53eef34192/multidict-6.6.4-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:ecab51ad2462197a4c000b6d5701fc8585b80eecb90583635d7e327b7b6923eb", size = 243122, upload-time = "2025-08-11T12:06:19.361Z" },
+ { url = "https://files.pythonhosted.org/packages/20/60/a1c53628168aa22447bfde3a8730096ac28086704a0d8c590f3b63388d0c/multidict-6.6.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:c5c97aa666cf70e667dfa5af945424ba1329af5dd988a437efeb3a09430389fb", size = 248992, upload-time = "2025-08-11T12:06:20.661Z" },
+ { url = "https://files.pythonhosted.org/packages/a3/3b/55443a0c372f33cae5d9ec37a6a973802884fa0ab3586659b197cf8cc5e9/multidict-6.6.4-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:9a950b7cf54099c1209f455ac5970b1ea81410f2af60ed9eb3c3f14f0bfcf987", size = 243708, upload-time = "2025-08-11T12:06:21.891Z" },
+ { url = "https://files.pythonhosted.org/packages/7c/60/a18c6900086769312560b2626b18e8cca22d9e85b1186ba77f4755b11266/multidict-6.6.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:163c7ea522ea9365a8a57832dea7618e6cbdc3cd75f8c627663587459a4e328f", size = 237498, upload-time = "2025-08-11T12:06:23.206Z" },
+ { url = "https://files.pythonhosted.org/packages/11/3d/8bdd8bcaff2951ce2affccca107a404925a2beafedd5aef0b5e4a71120a6/multidict-6.6.4-cp310-cp310-win32.whl", hash = "sha256:17d2cbbfa6ff20821396b25890f155f40c986f9cfbce5667759696d83504954f", size = 41415, upload-time = "2025-08-11T12:06:24.77Z" },
+ { url = "https://files.pythonhosted.org/packages/c0/53/cab1ad80356a4cd1b685a254b680167059b433b573e53872fab245e9fc95/multidict-6.6.4-cp310-cp310-win_amd64.whl", hash = "sha256:ce9a40fbe52e57e7edf20113a4eaddfacac0561a0879734e636aa6d4bb5e3fb0", size = 46046, upload-time = "2025-08-11T12:06:25.893Z" },
+ { url = "https://files.pythonhosted.org/packages/cf/9a/874212b6f5c1c2d870d0a7adc5bb4cfe9b0624fa15cdf5cf757c0f5087ae/multidict-6.6.4-cp310-cp310-win_arm64.whl", hash = "sha256:01d0959807a451fe9fdd4da3e139cb5b77f7328baf2140feeaf233e1d777b729", size = 43147, upload-time = "2025-08-11T12:06:27.534Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/7f/90a7f01e2d005d6653c689039977f6856718c75c5579445effb7e60923d1/multidict-6.6.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c7a0e9b561e6460484318a7612e725df1145d46b0ef57c6b9866441bf6e27e0c", size = 76472, upload-time = "2025-08-11T12:06:29.006Z" },
+ { url = "https://files.pythonhosted.org/packages/54/a3/bed07bc9e2bb302ce752f1dabc69e884cd6a676da44fb0e501b246031fdd/multidict-6.6.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6bf2f10f70acc7a2446965ffbc726e5fc0b272c97a90b485857e5c70022213eb", size = 44634, upload-time = "2025-08-11T12:06:30.374Z" },
+ { url = "https://files.pythonhosted.org/packages/a7/4b/ceeb4f8f33cf81277da464307afeaf164fb0297947642585884f5cad4f28/multidict-6.6.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:66247d72ed62d5dd29752ffc1d3b88f135c6a8de8b5f63b7c14e973ef5bda19e", size = 44282, upload-time = "2025-08-11T12:06:31.958Z" },
+ { url = "https://files.pythonhosted.org/packages/03/35/436a5da8702b06866189b69f655ffdb8f70796252a8772a77815f1812679/multidict-6.6.4-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:105245cc6b76f51e408451a844a54e6823bbd5a490ebfe5bdfc79798511ceded", size = 229696, upload-time = "2025-08-11T12:06:33.087Z" },
+ { url = "https://files.pythonhosted.org/packages/b6/0e/915160be8fecf1fca35f790c08fb74ca684d752fcba62c11daaf3d92c216/multidict-6.6.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cbbc54e58b34c3bae389ef00046be0961f30fef7cb0dd9c7756aee376a4f7683", size = 246665, upload-time = "2025-08-11T12:06:34.448Z" },
+ { url = "https://files.pythonhosted.org/packages/08/ee/2f464330acd83f77dcc346f0b1a0eaae10230291450887f96b204b8ac4d3/multidict-6.6.4-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:56c6b3652f945c9bc3ac6c8178cd93132b8d82dd581fcbc3a00676c51302bc1a", size = 225485, upload-time = "2025-08-11T12:06:35.672Z" },
+ { url = "https://files.pythonhosted.org/packages/71/cc/9a117f828b4d7fbaec6adeed2204f211e9caf0a012692a1ee32169f846ae/multidict-6.6.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b95494daf857602eccf4c18ca33337dd2be705bccdb6dddbfc9d513e6addb9d9", size = 257318, upload-time = "2025-08-11T12:06:36.98Z" },
+ { url = "https://files.pythonhosted.org/packages/25/77/62752d3dbd70e27fdd68e86626c1ae6bccfebe2bb1f84ae226363e112f5a/multidict-6.6.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e5b1413361cef15340ab9dc61523e653d25723e82d488ef7d60a12878227ed50", size = 254689, upload-time = "2025-08-11T12:06:38.233Z" },
+ { url = "https://files.pythonhosted.org/packages/00/6e/fac58b1072a6fc59af5e7acb245e8754d3e1f97f4f808a6559951f72a0d4/multidict-6.6.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e167bf899c3d724f9662ef00b4f7fef87a19c22b2fead198a6f68b263618df52", size = 246709, upload-time = "2025-08-11T12:06:39.517Z" },
+ { url = "https://files.pythonhosted.org/packages/01/ef/4698d6842ef5e797c6db7744b0081e36fb5de3d00002cc4c58071097fac3/multidict-6.6.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:aaea28ba20a9026dfa77f4b80369e51cb767c61e33a2d4043399c67bd95fb7c6", size = 243185, upload-time = "2025-08-11T12:06:40.796Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/c9/d82e95ae1d6e4ef396934e9b0e942dfc428775f9554acf04393cce66b157/multidict-6.6.4-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:8c91cdb30809a96d9ecf442ec9bc45e8cfaa0f7f8bdf534e082c2443a196727e", size = 237838, upload-time = "2025-08-11T12:06:42.595Z" },
+ { url = "https://files.pythonhosted.org/packages/57/cf/f94af5c36baaa75d44fab9f02e2a6bcfa0cd90acb44d4976a80960759dbc/multidict-6.6.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1a0ccbfe93ca114c5d65a2471d52d8829e56d467c97b0e341cf5ee45410033b3", size = 246368, upload-time = "2025-08-11T12:06:44.304Z" },
+ { url = "https://files.pythonhosted.org/packages/4a/fe/29f23460c3d995f6a4b678cb2e9730e7277231b981f0b234702f0177818a/multidict-6.6.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:55624b3f321d84c403cb7d8e6e982f41ae233d85f85db54ba6286f7295dc8a9c", size = 253339, upload-time = "2025-08-11T12:06:45.597Z" },
+ { url = "https://files.pythonhosted.org/packages/29/b6/fd59449204426187b82bf8a75f629310f68c6adc9559dc922d5abe34797b/multidict-6.6.4-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:4a1fb393a2c9d202cb766c76208bd7945bc194eba8ac920ce98c6e458f0b524b", size = 246933, upload-time = "2025-08-11T12:06:46.841Z" },
+ { url = "https://files.pythonhosted.org/packages/19/52/d5d6b344f176a5ac3606f7a61fb44dc746e04550e1a13834dff722b8d7d6/multidict-6.6.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:43868297a5759a845fa3a483fb4392973a95fb1de891605a3728130c52b8f40f", size = 242225, upload-time = "2025-08-11T12:06:48.588Z" },
+ { url = "https://files.pythonhosted.org/packages/ec/d3/5b2281ed89ff4d5318d82478a2a2450fcdfc3300da48ff15c1778280ad26/multidict-6.6.4-cp311-cp311-win32.whl", hash = "sha256:ed3b94c5e362a8a84d69642dbeac615452e8af9b8eb825b7bc9f31a53a1051e2", size = 41306, upload-time = "2025-08-11T12:06:49.95Z" },
+ { url = "https://files.pythonhosted.org/packages/74/7d/36b045c23a1ab98507aefd44fd8b264ee1dd5e5010543c6fccf82141ccef/multidict-6.6.4-cp311-cp311-win_amd64.whl", hash = "sha256:d8c112f7a90d8ca5d20213aa41eac690bb50a76da153e3afb3886418e61cb22e", size = 46029, upload-time = "2025-08-11T12:06:51.082Z" },
+ { url = "https://files.pythonhosted.org/packages/0f/5e/553d67d24432c5cd52b49047f2d248821843743ee6d29a704594f656d182/multidict-6.6.4-cp311-cp311-win_arm64.whl", hash = "sha256:3bb0eae408fa1996d87247ca0d6a57b7fc1dcf83e8a5c47ab82c558c250d4adf", size = 43017, upload-time = "2025-08-11T12:06:52.243Z" },
+ { url = "https://files.pythonhosted.org/packages/05/f6/512ffd8fd8b37fb2680e5ac35d788f1d71bbaf37789d21a820bdc441e565/multidict-6.6.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0ffb87be160942d56d7b87b0fdf098e81ed565add09eaa1294268c7f3caac4c8", size = 76516, upload-time = "2025-08-11T12:06:53.393Z" },
+ { url = "https://files.pythonhosted.org/packages/99/58/45c3e75deb8855c36bd66cc1658007589662ba584dbf423d01df478dd1c5/multidict-6.6.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d191de6cbab2aff5de6c5723101705fd044b3e4c7cfd587a1929b5028b9714b3", size = 45394, upload-time = "2025-08-11T12:06:54.555Z" },
+ { url = "https://files.pythonhosted.org/packages/fd/ca/e8c4472a93a26e4507c0b8e1f0762c0d8a32de1328ef72fd704ef9cc5447/multidict-6.6.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:38a0956dd92d918ad5feff3db8fcb4a5eb7dba114da917e1a88475619781b57b", size = 43591, upload-time = "2025-08-11T12:06:55.672Z" },
+ { url = "https://files.pythonhosted.org/packages/05/51/edf414f4df058574a7265034d04c935aa84a89e79ce90fcf4df211f47b16/multidict-6.6.4-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:6865f6d3b7900ae020b495d599fcf3765653bc927951c1abb959017f81ae8287", size = 237215, upload-time = "2025-08-11T12:06:57.213Z" },
+ { url = "https://files.pythonhosted.org/packages/c8/45/8b3d6dbad8cf3252553cc41abea09ad527b33ce47a5e199072620b296902/multidict-6.6.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a2088c126b6f72db6c9212ad827d0ba088c01d951cee25e758c450da732c138", size = 258299, upload-time = "2025-08-11T12:06:58.946Z" },
+ { url = "https://files.pythonhosted.org/packages/3c/e8/8ca2e9a9f5a435fc6db40438a55730a4bf4956b554e487fa1b9ae920f825/multidict-6.6.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0f37bed7319b848097085d7d48116f545985db988e2256b2e6f00563a3416ee6", size = 242357, upload-time = "2025-08-11T12:07:00.301Z" },
+ { url = "https://files.pythonhosted.org/packages/0f/84/80c77c99df05a75c28490b2af8f7cba2a12621186e0a8b0865d8e745c104/multidict-6.6.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:01368e3c94032ba6ca0b78e7ccb099643466cf24f8dc8eefcfdc0571d56e58f9", size = 268369, upload-time = "2025-08-11T12:07:01.638Z" },
+ { url = "https://files.pythonhosted.org/packages/0d/e9/920bfa46c27b05fb3e1ad85121fd49f441492dca2449c5bcfe42e4565d8a/multidict-6.6.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8fe323540c255db0bffee79ad7f048c909f2ab0edb87a597e1c17da6a54e493c", size = 269341, upload-time = "2025-08-11T12:07:02.943Z" },
+ { url = "https://files.pythonhosted.org/packages/af/65/753a2d8b05daf496f4a9c367fe844e90a1b2cac78e2be2c844200d10cc4c/multidict-6.6.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8eb3025f17b0a4c3cd08cda49acf312a19ad6e8a4edd9dbd591e6506d999402", size = 256100, upload-time = "2025-08-11T12:07:04.564Z" },
+ { url = "https://files.pythonhosted.org/packages/09/54/655be13ae324212bf0bc15d665a4e34844f34c206f78801be42f7a0a8aaa/multidict-6.6.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bbc14f0365534d35a06970d6a83478b249752e922d662dc24d489af1aa0d1be7", size = 253584, upload-time = "2025-08-11T12:07:05.914Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/74/ab2039ecc05264b5cec73eb018ce417af3ebb384ae9c0e9ed42cb33f8151/multidict-6.6.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:75aa52fba2d96bf972e85451b99d8e19cc37ce26fd016f6d4aa60da9ab2b005f", size = 251018, upload-time = "2025-08-11T12:07:08.301Z" },
+ { url = "https://files.pythonhosted.org/packages/af/0a/ccbb244ac848e56c6427f2392741c06302bbfba49c0042f1eb3c5b606497/multidict-6.6.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4fefd4a815e362d4f011919d97d7b4a1e566f1dde83dc4ad8cfb5b41de1df68d", size = 251477, upload-time = "2025-08-11T12:07:10.248Z" },
+ { url = "https://files.pythonhosted.org/packages/0e/b0/0ed49bba775b135937f52fe13922bc64a7eaf0a3ead84a36e8e4e446e096/multidict-6.6.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:db9801fe021f59a5b375ab778973127ca0ac52429a26e2fd86aa9508f4d26eb7", size = 263575, upload-time = "2025-08-11T12:07:11.928Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/d9/7fb85a85e14de2e44dfb6a24f03c41e2af8697a6df83daddb0e9b7569f73/multidict-6.6.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:a650629970fa21ac1fb06ba25dabfc5b8a2054fcbf6ae97c758aa956b8dba802", size = 259649, upload-time = "2025-08-11T12:07:13.244Z" },
+ { url = "https://files.pythonhosted.org/packages/03/9e/b3a459bcf9b6e74fa461a5222a10ff9b544cb1cd52fd482fb1b75ecda2a2/multidict-6.6.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:452ff5da78d4720d7516a3a2abd804957532dd69296cb77319c193e3ffb87e24", size = 251505, upload-time = "2025-08-11T12:07:14.57Z" },
+ { url = "https://files.pythonhosted.org/packages/86/a2/8022f78f041dfe6d71e364001a5cf987c30edfc83c8a5fb7a3f0974cff39/multidict-6.6.4-cp312-cp312-win32.whl", hash = "sha256:8c2fcb12136530ed19572bbba61b407f655e3953ba669b96a35036a11a485793", size = 41888, upload-time = "2025-08-11T12:07:15.904Z" },
+ { url = "https://files.pythonhosted.org/packages/c7/eb/d88b1780d43a56db2cba24289fa744a9d216c1a8546a0dc3956563fd53ea/multidict-6.6.4-cp312-cp312-win_amd64.whl", hash = "sha256:047d9425860a8c9544fed1b9584f0c8bcd31bcde9568b047c5e567a1025ecd6e", size = 46072, upload-time = "2025-08-11T12:07:17.045Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/16/b929320bf5750e2d9d4931835a4c638a19d2494a5b519caaaa7492ebe105/multidict-6.6.4-cp312-cp312-win_arm64.whl", hash = "sha256:14754eb72feaa1e8ae528468f24250dd997b8e2188c3d2f593f9eba259e4b364", size = 43222, upload-time = "2025-08-11T12:07:18.328Z" },
+ { url = "https://files.pythonhosted.org/packages/3a/5d/e1db626f64f60008320aab00fbe4f23fc3300d75892a3381275b3d284580/multidict-6.6.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f46a6e8597f9bd71b31cc708195d42b634c8527fecbcf93febf1052cacc1f16e", size = 75848, upload-time = "2025-08-11T12:07:19.912Z" },
+ { url = "https://files.pythonhosted.org/packages/4c/aa/8b6f548d839b6c13887253af4e29c939af22a18591bfb5d0ee6f1931dae8/multidict-6.6.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:22e38b2bc176c5eb9c0a0e379f9d188ae4cd8b28c0f53b52bce7ab0a9e534657", size = 45060, upload-time = "2025-08-11T12:07:21.163Z" },
+ { url = "https://files.pythonhosted.org/packages/eb/c6/f5e97e5d99a729bc2aa58eb3ebfa9f1e56a9b517cc38c60537c81834a73f/multidict-6.6.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5df8afd26f162da59e218ac0eefaa01b01b2e6cd606cffa46608f699539246da", size = 43269, upload-time = "2025-08-11T12:07:22.392Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/31/d54eb0c62516776f36fe67f84a732f97e0b0e12f98d5685bebcc6d396910/multidict-6.6.4-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:49517449b58d043023720aa58e62b2f74ce9b28f740a0b5d33971149553d72aa", size = 237158, upload-time = "2025-08-11T12:07:23.636Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/1c/8a10c1c25b23156e63b12165a929d8eb49a6ed769fdbefb06e6f07c1e50d/multidict-6.6.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ae9408439537c5afdca05edd128a63f56a62680f4b3c234301055d7a2000220f", size = 257076, upload-time = "2025-08-11T12:07:25.049Z" },
+ { url = "https://files.pythonhosted.org/packages/ad/86/90e20b5771d6805a119e483fd3d1e8393e745a11511aebca41f0da38c3e2/multidict-6.6.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:87a32d20759dc52a9e850fe1061b6e41ab28e2998d44168a8a341b99ded1dba0", size = 240694, upload-time = "2025-08-11T12:07:26.458Z" },
+ { url = "https://files.pythonhosted.org/packages/e7/49/484d3e6b535bc0555b52a0a26ba86e4d8d03fd5587d4936dc59ba7583221/multidict-6.6.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:52e3c8d43cdfff587ceedce9deb25e6ae77daba560b626e97a56ddcad3756879", size = 266350, upload-time = "2025-08-11T12:07:27.94Z" },
+ { url = "https://files.pythonhosted.org/packages/bf/b4/aa4c5c379b11895083d50021e229e90c408d7d875471cb3abf721e4670d6/multidict-6.6.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ad8850921d3a8d8ff6fbef790e773cecfc260bbfa0566998980d3fa8f520bc4a", size = 267250, upload-time = "2025-08-11T12:07:29.303Z" },
+ { url = "https://files.pythonhosted.org/packages/80/e5/5e22c5bf96a64bdd43518b1834c6d95a4922cc2066b7d8e467dae9b6cee6/multidict-6.6.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:497a2954adc25c08daff36f795077f63ad33e13f19bfff7736e72c785391534f", size = 254900, upload-time = "2025-08-11T12:07:30.764Z" },
+ { url = "https://files.pythonhosted.org/packages/17/38/58b27fed927c07035abc02befacab42491e7388ca105e087e6e0215ead64/multidict-6.6.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:024ce601f92d780ca1617ad4be5ac15b501cc2414970ffa2bb2bbc2bd5a68fa5", size = 252355, upload-time = "2025-08-11T12:07:32.205Z" },
+ { url = "https://files.pythonhosted.org/packages/d0/a1/dad75d23a90c29c02b5d6f3d7c10ab36c3197613be5d07ec49c7791e186c/multidict-6.6.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:a693fc5ed9bdd1c9e898013e0da4dcc640de7963a371c0bd458e50e046bf6438", size = 250061, upload-time = "2025-08-11T12:07:33.623Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/1a/ac2216b61c7f116edab6dc3378cca6c70dc019c9a457ff0d754067c58b20/multidict-6.6.4-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:190766dac95aab54cae5b152a56520fd99298f32a1266d66d27fdd1b5ac00f4e", size = 249675, upload-time = "2025-08-11T12:07:34.958Z" },
+ { url = "https://files.pythonhosted.org/packages/d4/79/1916af833b800d13883e452e8e0977c065c4ee3ab7a26941fbfdebc11895/multidict-6.6.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:34d8f2a5ffdceab9dcd97c7a016deb2308531d5f0fced2bb0c9e1df45b3363d7", size = 261247, upload-time = "2025-08-11T12:07:36.588Z" },
+ { url = "https://files.pythonhosted.org/packages/c5/65/d1f84fe08ac44a5fc7391cbc20a7cedc433ea616b266284413fd86062f8c/multidict-6.6.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:59e8d40ab1f5a8597abcef00d04845155a5693b5da00d2c93dbe88f2050f2812", size = 257960, upload-time = "2025-08-11T12:07:39.735Z" },
+ { url = "https://files.pythonhosted.org/packages/13/b5/29ec78057d377b195ac2c5248c773703a6b602e132a763e20ec0457e7440/multidict-6.6.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:467fe64138cfac771f0e949b938c2e1ada2b5af22f39692aa9258715e9ea613a", size = 250078, upload-time = "2025-08-11T12:07:41.525Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/0e/7e79d38f70a872cae32e29b0d77024bef7834b0afb406ddae6558d9e2414/multidict-6.6.4-cp313-cp313-win32.whl", hash = "sha256:14616a30fe6d0a48d0a48d1a633ab3b8bec4cf293aac65f32ed116f620adfd69", size = 41708, upload-time = "2025-08-11T12:07:43.405Z" },
+ { url = "https://files.pythonhosted.org/packages/9d/34/746696dffff742e97cd6a23da953e55d0ea51fa601fa2ff387b3edcfaa2c/multidict-6.6.4-cp313-cp313-win_amd64.whl", hash = "sha256:40cd05eaeb39e2bc8939451f033e57feaa2ac99e07dbca8afe2be450a4a3b6cf", size = 45912, upload-time = "2025-08-11T12:07:45.082Z" },
+ { url = "https://files.pythonhosted.org/packages/c7/87/3bac136181e271e29170d8d71929cdeddeb77f3e8b6a0c08da3a8e9da114/multidict-6.6.4-cp313-cp313-win_arm64.whl", hash = "sha256:f6eb37d511bfae9e13e82cb4d1af36b91150466f24d9b2b8a9785816deb16605", size = 43076, upload-time = "2025-08-11T12:07:46.746Z" },
+ { url = "https://files.pythonhosted.org/packages/64/94/0a8e63e36c049b571c9ae41ee301ada29c3fee9643d9c2548d7d558a1d99/multidict-6.6.4-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:6c84378acd4f37d1b507dfa0d459b449e2321b3ba5f2338f9b085cf7a7ba95eb", size = 82812, upload-time = "2025-08-11T12:07:48.402Z" },
+ { url = "https://files.pythonhosted.org/packages/25/1a/be8e369dfcd260d2070a67e65dd3990dd635cbd735b98da31e00ea84cd4e/multidict-6.6.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0e0558693063c75f3d952abf645c78f3c5dfdd825a41d8c4d8156fc0b0da6e7e", size = 48313, upload-time = "2025-08-11T12:07:49.679Z" },
+ { url = "https://files.pythonhosted.org/packages/26/5a/dd4ade298674b2f9a7b06a32c94ffbc0497354df8285f27317c66433ce3b/multidict-6.6.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3f8e2384cb83ebd23fd07e9eada8ba64afc4c759cd94817433ab8c81ee4b403f", size = 46777, upload-time = "2025-08-11T12:07:51.318Z" },
+ { url = "https://files.pythonhosted.org/packages/89/db/98aa28bc7e071bfba611ac2ae803c24e96dd3a452b4118c587d3d872c64c/multidict-6.6.4-cp313-cp313t-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:f996b87b420995a9174b2a7c1a8daf7db4750be6848b03eb5e639674f7963773", size = 229321, upload-time = "2025-08-11T12:07:52.965Z" },
+ { url = "https://files.pythonhosted.org/packages/c7/bc/01ddda2a73dd9d167bd85d0e8ef4293836a8f82b786c63fb1a429bc3e678/multidict-6.6.4-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cc356250cffd6e78416cf5b40dc6a74f1edf3be8e834cf8862d9ed5265cf9b0e", size = 249954, upload-time = "2025-08-11T12:07:54.423Z" },
+ { url = "https://files.pythonhosted.org/packages/06/78/6b7c0f020f9aa0acf66d0ab4eb9f08375bac9a50ff5e3edb1c4ccd59eafc/multidict-6.6.4-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:dadf95aa862714ea468a49ad1e09fe00fcc9ec67d122f6596a8d40caf6cec7d0", size = 228612, upload-time = "2025-08-11T12:07:55.914Z" },
+ { url = "https://files.pythonhosted.org/packages/00/44/3faa416f89b2d5d76e9d447296a81521e1c832ad6e40b92f990697b43192/multidict-6.6.4-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7dd57515bebffd8ebd714d101d4c434063322e4fe24042e90ced41f18b6d3395", size = 257528, upload-time = "2025-08-11T12:07:57.371Z" },
+ { url = "https://files.pythonhosted.org/packages/05/5f/77c03b89af0fcb16f018f668207768191fb9dcfb5e3361a5e706a11db2c9/multidict-6.6.4-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:967af5f238ebc2eb1da4e77af5492219fbd9b4b812347da39a7b5f5c72c0fa45", size = 256329, upload-time = "2025-08-11T12:07:58.844Z" },
+ { url = "https://files.pythonhosted.org/packages/cf/e9/ed750a2a9afb4f8dc6f13dc5b67b514832101b95714f1211cd42e0aafc26/multidict-6.6.4-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2a4c6875c37aae9794308ec43e3530e4aa0d36579ce38d89979bbf89582002bb", size = 247928, upload-time = "2025-08-11T12:08:01.037Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/b5/e0571bc13cda277db7e6e8a532791d4403dacc9850006cb66d2556e649c0/multidict-6.6.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:7f683a551e92bdb7fac545b9c6f9fa2aebdeefa61d607510b3533286fcab67f5", size = 245228, upload-time = "2025-08-11T12:08:02.96Z" },
+ { url = "https://files.pythonhosted.org/packages/f3/a3/69a84b0eccb9824491f06368f5b86e72e4af54c3067c37c39099b6687109/multidict-6.6.4-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:3ba5aaf600edaf2a868a391779f7a85d93bed147854925f34edd24cc70a3e141", size = 235869, upload-time = "2025-08-11T12:08:04.746Z" },
+ { url = "https://files.pythonhosted.org/packages/a9/9d/28802e8f9121a6a0804fa009debf4e753d0a59969ea9f70be5f5fdfcb18f/multidict-6.6.4-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:580b643b7fd2c295d83cad90d78419081f53fd532d1f1eb67ceb7060f61cff0d", size = 243446, upload-time = "2025-08-11T12:08:06.332Z" },
+ { url = "https://files.pythonhosted.org/packages/38/ea/6c98add069b4878c1d66428a5f5149ddb6d32b1f9836a826ac764b9940be/multidict-6.6.4-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:37b7187197da6af3ee0b044dbc9625afd0c885f2800815b228a0e70f9a7f473d", size = 252299, upload-time = "2025-08-11T12:08:07.931Z" },
+ { url = "https://files.pythonhosted.org/packages/3a/09/8fe02d204473e14c0af3affd50af9078839dfca1742f025cca765435d6b4/multidict-6.6.4-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e1b93790ed0bc26feb72e2f08299691ceb6da5e9e14a0d13cc74f1869af327a0", size = 246926, upload-time = "2025-08-11T12:08:09.467Z" },
+ { url = "https://files.pythonhosted.org/packages/37/3d/7b1e10d774a6df5175ecd3c92bff069e77bed9ec2a927fdd4ff5fe182f67/multidict-6.6.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a506a77ddee1efcca81ecbeae27ade3e09cdf21a8ae854d766c2bb4f14053f92", size = 243383, upload-time = "2025-08-11T12:08:10.981Z" },
+ { url = "https://files.pythonhosted.org/packages/50/b0/a6fae46071b645ae98786ab738447de1ef53742eaad949f27e960864bb49/multidict-6.6.4-cp313-cp313t-win32.whl", hash = "sha256:f93b2b2279883d1d0a9e1bd01f312d6fc315c5e4c1f09e112e4736e2f650bc4e", size = 47775, upload-time = "2025-08-11T12:08:12.439Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/0a/2436550b1520091af0600dff547913cb2d66fbac27a8c33bc1b1bccd8d98/multidict-6.6.4-cp313-cp313t-win_amd64.whl", hash = "sha256:6d46a180acdf6e87cc41dc15d8f5c2986e1e8739dc25dbb7dac826731ef381a4", size = 53100, upload-time = "2025-08-11T12:08:13.823Z" },
+ { url = "https://files.pythonhosted.org/packages/97/ea/43ac51faff934086db9c072a94d327d71b7d8b40cd5dcb47311330929ef0/multidict-6.6.4-cp313-cp313t-win_arm64.whl", hash = "sha256:756989334015e3335d087a27331659820d53ba432befdef6a718398b0a8493ad", size = 45501, upload-time = "2025-08-11T12:08:15.173Z" },
+ { url = "https://files.pythonhosted.org/packages/fd/69/b547032297c7e63ba2af494edba695d781af8a0c6e89e4d06cf848b21d80/multidict-6.6.4-py3-none-any.whl", hash = "sha256:27d8f8e125c07cb954e54d75d04905a9bba8a439c1d84aca94949d4d03d8601c", size = 12313, upload-time = "2025-08-11T12:08:46.891Z" },
]
[[package]]
-name = "httpx"
-version = "0.28.1"
+name = "packaging"
+version = "24.2"
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, upload-time = "2024-12-06T15:37:23.222Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950, upload-time = "2024-11-08T09:47:47.202Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" },
+ { url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451, upload-time = "2024-11-08T09:47:44.722Z" },
]
[[package]]
-name = "httpx-sse"
-version = "0.4.1"
+name = "pluggy"
+version = "1.6.0"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/6e/fa/66bd985dd0b7c109a3bcb89272ee0bfb7e2b4d06309ad7b38ff866734b2a/httpx_sse-0.4.1.tar.gz", hash = "sha256:8f44d34414bc7b21bf3602713005c5df4917884f76072479b21f68befa4ea26e", size = 12998, upload-time = "2025-06-24T13:21:05.71Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/25/0a/6269e3473b09aed2dab8aa1a600c70f31f00ae1349bee30658f7e358a159/httpx_sse-0.4.1-py3-none-any.whl", hash = "sha256:cba42174344c3a5b06f255ce65b350880f962d99ead85e776f23c6618a377a37", size = 8054, upload-time = "2025-06-24T13:21:04.772Z" },
+ { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" },
]
[[package]]
-name = "huggingface-hub"
-version = "0.34.4"
+name = "propcache"
+version = "0.3.2"
source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "filelock" },
- { name = "fsspec" },
- { name = "hf-xet", marker = "platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'arm64' or platform_machine == 'x86_64'" },
- { name = "packaging" },
- { name = "pyyaml" },
- { name = "requests" },
- { name = "tqdm" },
- { name = "typing-extensions" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/45/c9/bdbe19339f76d12985bc03572f330a01a93c04dffecaaea3061bdd7fb892/huggingface_hub-0.34.4.tar.gz", hash = "sha256:a4228daa6fb001be3f4f4bdaf9a0db00e1739235702848df00885c9b5742c85c", size = 459768, upload-time = "2025-08-08T09:14:52.365Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/a6/16/43264e4a779dd8588c21a70f0709665ee8f611211bdd2c87d952cfa7c776/propcache-0.3.2.tar.gz", hash = "sha256:20d7d62e4e7ef05f221e0db2856b979540686342e7dd9973b815599c7057e168", size = 44139, upload-time = "2025-06-09T22:56:06.081Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/39/7b/bb06b061991107cd8783f300adff3e7b7f284e330fd82f507f2a1417b11d/huggingface_hub-0.34.4-py3-none-any.whl", hash = "sha256:9b365d781739c93ff90c359844221beef048403f1bc1f1c123c191257c3c890a", size = 561452, upload-time = "2025-08-08T09:14:50.159Z" },
-]
-
-[[package]]
-name = "humanfriendly"
-version = "10.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "pyreadline3", marker = "sys_platform == 'win32'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/cc/3f/2c29224acb2e2df4d2046e4c73ee2662023c58ff5b113c4c1adac0886c43/humanfriendly-10.0.tar.gz", hash = "sha256:6b0b831ce8f15f7300721aa49829fc4e83921a9a301cc7f606be6686a2288ddc", size = 360702, upload-time = "2021-09-17T21:40:43.31Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/f0/0f/310fb31e39e2d734ccaa2c0fb981ee41f7bd5056ce9bc29b2248bd569169/humanfriendly-10.0-py2.py3-none-any.whl", hash = "sha256:1697e1a8a8f550fd43c2865cd84542fc175a61dcb779b6fee18cf6b6ccba1477", size = 86794, upload-time = "2021-09-17T21:40:39.897Z" },
-]
-
-[[package]]
-name = "identify"
-version = "2.6.13"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/82/ca/ffbabe3635bb839aa36b3a893c91a9b0d368cb4d8073e03a12896970af82/identify-2.6.13.tar.gz", hash = "sha256:da8d6c828e773620e13bfa86ea601c5a5310ba4bcd65edf378198b56a1f9fb32", size = 99243, upload-time = "2025-08-09T19:35:00.6Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/e7/ce/461b60a3ee109518c055953729bf9ed089a04db895d47e95444071dcdef2/identify-2.6.13-py2.py3-none-any.whl", hash = "sha256:60381139b3ae39447482ecc406944190f690d4a2997f2584062089848361b33b", size = 99153, upload-time = "2025-08-09T19:34:59.1Z" },
-]
-
-[[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, upload-time = "2024-09-15T18:07:39.745Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" },
-]
-
-[[package]]
-name = "ijson"
-version = "3.4.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/a3/4f/1cfeada63f5fce87536651268ddf5cca79b8b4bbb457aee4e45777964a0a/ijson-3.4.0.tar.gz", hash = "sha256:5f74dcbad9d592c428d3ca3957f7115a42689ee7ee941458860900236ae9bb13", size = 65782, upload-time = "2025-05-08T02:37:20.135Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/eb/6b/a247ba44004154aaa71f9e6bd9f05ba412f490cc4043618efb29314f035e/ijson-3.4.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e27e50f6dcdee648f704abc5d31b976cd2f90b4642ed447cf03296d138433d09", size = 87609, upload-time = "2025-05-08T02:35:20.535Z" },
- { url = "https://files.pythonhosted.org/packages/3c/1d/8d2009d74373b7dec2a49b1167e396debb896501396c70a674bb9ccc41ff/ijson-3.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2a753be681ac930740a4af9c93cfb4edc49a167faed48061ea650dc5b0f406f1", size = 59243, upload-time = "2025-05-08T02:35:21.958Z" },
- { url = "https://files.pythonhosted.org/packages/a7/b2/a85a21ebaba81f64a326c303a94625fb94b84890c52d9efdd8acb38b6312/ijson-3.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a07c47aed534e0ec198e6a2d4360b259d32ac654af59c015afc517ad7973b7fb", size = 59309, upload-time = "2025-05-08T02:35:23.317Z" },
- { url = "https://files.pythonhosted.org/packages/b1/35/273dfa1f27c38eeaba105496ecb54532199f76c0120177b28315daf5aec3/ijson-3.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c55f48181e11c597cd7146fb31edc8058391201ead69f8f40d2ecbb0b3e4fc6", size = 131213, upload-time = "2025-05-08T02:35:24.735Z" },
- { url = "https://files.pythonhosted.org/packages/4d/37/9d3bb0e200a103ca9f8e9315c4d96ecaca43a3c1957c1ac069ea9dc9c6ba/ijson-3.4.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd5669f96f79d8a2dd5ae81cbd06770a4d42c435fd4a75c74ef28d9913b697d", size = 125456, upload-time = "2025-05-08T02:35:25.896Z" },
- { url = "https://files.pythonhosted.org/packages/00/54/8f015c4df30200fd14435dec9c67bf675dff0fee44a16c084a8ec0f82922/ijson-3.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3e3ddd46d16b8542c63b1b8af7006c758d4e21cc1b86122c15f8530fae773461", size = 130192, upload-time = "2025-05-08T02:35:27.367Z" },
- { url = "https://files.pythonhosted.org/packages/88/01/46a0540ad3461332edcc689a8874fa13f0a4c00f60f02d155b70e36f5e0b/ijson-3.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:1504cec7fe04be2bb0cc33b50c9dd3f83f98c0540ad4991d4017373b7853cfe6", size = 132217, upload-time = "2025-05-08T02:35:28.545Z" },
- { url = "https://files.pythonhosted.org/packages/d7/da/8f8df42f3fd7ef279e20eae294738eed62d41ed5b6a4baca5121abc7cf0f/ijson-3.4.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:2f2ff456adeb216603e25d7915f10584c1b958b6eafa60038d76d08fc8a5fb06", size = 127118, upload-time = "2025-05-08T02:35:29.726Z" },
- { url = "https://files.pythonhosted.org/packages/82/0a/a410d9d3b082cc2ec9738d54935a589974cbe54c0f358e4d17465594d660/ijson-3.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0ab00d75d61613a125fbbb524551658b1ad6919a52271ca16563ca5bc2737bb1", size = 129808, upload-time = "2025-05-08T02:35:31.247Z" },
- { url = "https://files.pythonhosted.org/packages/2e/c6/a3e2a446b8bd2cf91cb4ca7439f128d2b379b5a79794d0ea25e379b0f4f3/ijson-3.4.0-cp310-cp310-win32.whl", hash = "sha256:ada421fd59fe2bfa4cfa64ba39aeba3f0753696cdcd4d50396a85f38b1d12b01", size = 51160, upload-time = "2025-05-08T02:35:32.964Z" },
- { url = "https://files.pythonhosted.org/packages/18/7c/e6620603df42d2ef8a92076eaa5cd2b905366e86e113adf49e7b79970bd3/ijson-3.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:8c75e82cec05d00ed3a4af5f4edf08f59d536ed1a86ac7e84044870872d82a33", size = 53710, upload-time = "2025-05-08T02:35:34.033Z" },
- { url = "https://files.pythonhosted.org/packages/1a/0d/3e2998f4d7b7d2db2d511e4f0cf9127b6e2140c325c3cb77be46ae46ff1d/ijson-3.4.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9e369bf5a173ca51846c243002ad8025d32032532523b06510881ecc8723ee54", size = 87643, upload-time = "2025-05-08T02:35:35.693Z" },
- { url = "https://files.pythonhosted.org/packages/e9/7b/afef2b08af2fee5ead65fcd972fadc3e31f9ae2b517fe2c378d50a9bf79b/ijson-3.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:26e7da0a3cd2a56a1fde1b34231867693f21c528b683856f6691e95f9f39caec", size = 59260, upload-time = "2025-05-08T02:35:37.166Z" },
- { url = "https://files.pythonhosted.org/packages/da/4a/39f583a2a13096f5063028bb767622f09cafc9ec254c193deee6c80af59f/ijson-3.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1c28c7f604729be22aa453e604e9617b665fa0c24cd25f9f47a970e8130c571a", size = 59311, upload-time = "2025-05-08T02:35:38.538Z" },
- { url = "https://files.pythonhosted.org/packages/3c/58/5b80efd54b093e479c98d14b31d7794267281f6a8729f2c94fbfab661029/ijson-3.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bed8bcb84d3468940f97869da323ba09ae3e6b950df11dea9b62e2b231ca1e3", size = 136125, upload-time = "2025-05-08T02:35:39.976Z" },
- { url = "https://files.pythonhosted.org/packages/e5/f5/f37659b1647ecc3992216277cd8a45e2194e84e8818178f77c99e1d18463/ijson-3.4.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:296bc824f4088f2af814aaf973b0435bc887ce3d9f517b1577cc4e7d1afb1cb7", size = 130699, upload-time = "2025-05-08T02:35:41.483Z" },
- { url = "https://files.pythonhosted.org/packages/ee/2f/4c580ac4bb5eda059b672ad0a05e4bafdae5182a6ec6ab43546763dafa91/ijson-3.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8145f8f40617b6a8aa24e28559d0adc8b889e56a203725226a8a60fa3501073f", size = 134963, upload-time = "2025-05-08T02:35:43.017Z" },
- { url = "https://files.pythonhosted.org/packages/6d/9e/64ec39718609faab6ed6e1ceb44f9c35d71210ad9c87fff477c03503e8f8/ijson-3.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b674a97bd503ea21bc85103e06b6493b1b2a12da3372950f53e1c664566a33a4", size = 137405, upload-time = "2025-05-08T02:35:44.618Z" },
- { url = "https://files.pythonhosted.org/packages/71/b2/f0bf0e4a0962845597996de6de59c0078bc03a1f899e03908220039f4cf6/ijson-3.4.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8bc731cf1c3282b021d3407a601a5a327613da9ad3c4cecb1123232623ae1826", size = 131861, upload-time = "2025-05-08T02:35:46.22Z" },
- { url = "https://files.pythonhosted.org/packages/17/83/4a2e3611e2b4842b413ec84d2e54adea55ab52e4408ea0f1b1b927e19536/ijson-3.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:42ace5e940e0cf58c9de72f688d6829ddd815096d07927ee7e77df2648006365", size = 134297, upload-time = "2025-05-08T02:35:47.401Z" },
- { url = "https://files.pythonhosted.org/packages/38/75/2d332911ac765b44cd7da0cb2b06143521ad5e31dfcc8d8587e6e6168bc8/ijson-3.4.0-cp311-cp311-win32.whl", hash = "sha256:5be39a0df4cd3f02b304382ea8885391900ac62e95888af47525a287c50005e9", size = 51161, upload-time = "2025-05-08T02:35:49.164Z" },
- { url = "https://files.pythonhosted.org/packages/7d/ba/4ad571f9f7fcf5906b26e757b130c1713c5f0198a1e59568f05d53a0816c/ijson-3.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:0b1be1781792291e70d2e177acf564ec672a7907ba74f313583bdf39fe81f9b7", size = 53710, upload-time = "2025-05-08T02:35:50.323Z" },
- { url = "https://files.pythonhosted.org/packages/f8/ec/317ee5b2d13e50448833ead3aa906659a32b376191f6abc2a7c6112d2b27/ijson-3.4.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:956b148f88259a80a9027ffbe2d91705fae0c004fbfba3e5a24028fbe72311a9", size = 87212, upload-time = "2025-05-08T02:35:51.835Z" },
- { url = "https://files.pythonhosted.org/packages/f8/43/b06c96ced30cacecc5d518f89b0fd1c98c294a30ff88848b70ed7b7f72a1/ijson-3.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:06b89960f5c721106394c7fba5760b3f67c515b8eb7d80f612388f5eca2f4621", size = 59175, upload-time = "2025-05-08T02:35:52.988Z" },
- { url = "https://files.pythonhosted.org/packages/e9/df/b4aeafb7ecde463130840ee9be36130823ec94a00525049bf700883378b8/ijson-3.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9a0bb591cf250dd7e9dfab69d634745a7f3272d31cfe879f9156e0a081fd97ee", size = 59011, upload-time = "2025-05-08T02:35:54.394Z" },
- { url = "https://files.pythonhosted.org/packages/e3/7c/a80b8e361641609507f62022089626d4b8067f0826f51e1c09e4ba86eba8/ijson-3.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:72e92de999977f4c6b660ffcf2b8d59604ccd531edcbfde05b642baf283e0de8", size = 146094, upload-time = "2025-05-08T02:35:55.601Z" },
- { url = "https://files.pythonhosted.org/packages/01/44/fa416347b9a802e3646c6ff377fc3278bd7d6106e17beb339514b6a3184e/ijson-3.4.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9e9602157a5b869d44b6896e64f502c712a312fcde044c2e586fccb85d3e316e", size = 137903, upload-time = "2025-05-08T02:35:56.814Z" },
- { url = "https://files.pythonhosted.org/packages/24/c6/41a9ad4d42df50ff6e70fdce79b034f09b914802737ebbdc141153d8d791/ijson-3.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1e83660edb931a425b7ff662eb49db1f10d30ca6d4d350e5630edbed098bc01", size = 148339, upload-time = "2025-05-08T02:35:58.595Z" },
- { url = "https://files.pythonhosted.org/packages/5f/6f/7d01efda415b8502dce67e067ed9e8a124f53e763002c02207e542e1a2f1/ijson-3.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:49bf8eac1c7b7913073865a859c215488461f7591b4fa6a33c14b51cb73659d0", size = 149383, upload-time = "2025-05-08T02:36:00.197Z" },
- { url = "https://files.pythonhosted.org/packages/95/6c/0d67024b9ecb57916c5e5ab0350251c9fe2f86dc9c8ca2b605c194bdad6a/ijson-3.4.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:160b09273cb42019f1811469508b0a057d19f26434d44752bde6f281da6d3f32", size = 141580, upload-time = "2025-05-08T02:36:01.998Z" },
- { url = "https://files.pythonhosted.org/packages/06/43/e10edcc1c6a3b619294de835e7678bfb3a1b8a75955f3689fd66a1e9e7b4/ijson-3.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2019ff4e6f354aa00c76c8591bd450899111c61f2354ad55cc127e2ce2492c44", size = 150280, upload-time = "2025-05-08T02:36:03.926Z" },
- { url = "https://files.pythonhosted.org/packages/07/84/1cbeee8e8190a1ebe6926569a92cf1fa80ddb380c129beb6f86559e1bb24/ijson-3.4.0-cp312-cp312-win32.whl", hash = "sha256:931c007bf6bb8330705429989b2deed6838c22b63358a330bf362b6e458ba0bf", size = 51512, upload-time = "2025-05-08T02:36:05.595Z" },
- { url = "https://files.pythonhosted.org/packages/66/13/530802bc391c95be6fe9f96e9aa427d94067e7c0b7da7a9092344dc44c4b/ijson-3.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:71523f2b64cb856a820223e94d23e88369f193017ecc789bb4de198cc9d349eb", size = 54081, upload-time = "2025-05-08T02:36:07.099Z" },
- { url = "https://files.pythonhosted.org/packages/77/b3/b1d2eb2745e5204ec7a25365a6deb7868576214feb5e109bce368fb692c9/ijson-3.4.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e8d96f88d75196a61c9d9443de2b72c2d4a7ba9456ff117b57ae3bba23a54256", size = 87216, upload-time = "2025-05-08T02:36:08.414Z" },
- { url = "https://files.pythonhosted.org/packages/b1/cd/cd6d340087617f8cc9bedbb21d974542fe2f160ed0126b8288d3499a469b/ijson-3.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c45906ce2c1d3b62f15645476fc3a6ca279549127f01662a39ca5ed334a00cf9", size = 59170, upload-time = "2025-05-08T02:36:09.604Z" },
- { url = "https://files.pythonhosted.org/packages/3e/4d/32d3a9903b488d3306e3c8288f6ee4217d2eea82728261db03a1045eb5d1/ijson-3.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4ab4bc2119b35c4363ea49f29563612237cae9413d2fbe54b223be098b97bc9e", size = 59013, upload-time = "2025-05-08T02:36:10.696Z" },
- { url = "https://files.pythonhosted.org/packages/d5/c8/db15465ab4b0b477cee5964c8bfc94bf8c45af8e27a23e1ad78d1926e587/ijson-3.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:97b0a9b5a15e61dfb1f14921ea4e0dba39f3a650df6d8f444ddbc2b19b479ff1", size = 146564, upload-time = "2025-05-08T02:36:11.916Z" },
- { url = "https://files.pythonhosted.org/packages/c4/d8/0755545bc122473a9a434ab90e0f378780e603d75495b1ca3872de757873/ijson-3.4.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e3047bb994dabedf11de11076ed1147a307924b6e5e2df6784fb2599c4ad8c60", size = 137917, upload-time = "2025-05-08T02:36:13.532Z" },
- { url = "https://files.pythonhosted.org/packages/d0/c6/aeb89c8939ebe3f534af26c8c88000c5e870dbb6ae33644c21a4531f87d2/ijson-3.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:68c83161b052e9f5dc8191acbc862bb1e63f8a35344cb5cd0db1afd3afd487a6", size = 148897, upload-time = "2025-05-08T02:36:14.813Z" },
- { url = "https://files.pythonhosted.org/packages/be/0e/7ef6e9b372106f2682a4a32b3c65bf86bb471a1670e4dac242faee4a7d3f/ijson-3.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1eebd9b6c20eb1dffde0ae1f0fbb4aeacec2eb7b89adb5c7c0449fc9fd742760", size = 149711, upload-time = "2025-05-08T02:36:16.476Z" },
- { url = "https://files.pythonhosted.org/packages/d1/5d/9841c3ed75bcdabf19b3202de5f862a9c9c86ce5c7c9d95fa32347fdbf5f/ijson-3.4.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:13fb6d5c35192c541421f3ee81239d91fc15a8d8f26c869250f941f4b346a86c", size = 141691, upload-time = "2025-05-08T02:36:18.044Z" },
- { url = "https://files.pythonhosted.org/packages/d5/d2/ce74e17218dba292e9be10a44ed0c75439f7958cdd263adb0b5b92d012d5/ijson-3.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:28b7196ff7b37c4897c547a28fa4876919696739fc91c1f347651c9736877c69", size = 150738, upload-time = "2025-05-08T02:36:19.483Z" },
- { url = "https://files.pythonhosted.org/packages/4e/43/dcc480f94453b1075c9911d4755b823f3ace275761bb37b40139f22109ca/ijson-3.4.0-cp313-cp313-win32.whl", hash = "sha256:3c2691d2da42629522140f77b99587d6f5010440d58d36616f33bc7bdc830cc3", size = 51512, upload-time = "2025-05-08T02:36:20.99Z" },
- { url = "https://files.pythonhosted.org/packages/35/dd/d8c5f15efd85ba51e6e11451ebe23d779361a9ec0d192064c2a8c3cdfcb8/ijson-3.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:c4554718c275a044c47eb3874f78f2c939f300215d9031e785a6711cc51b83fc", size = 54074, upload-time = "2025-05-08T02:36:22.075Z" },
- { url = "https://files.pythonhosted.org/packages/79/73/24ad8cd106203419c4d22bed627e02e281d66b83e91bc206a371893d0486/ijson-3.4.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:915a65e3f3c0eee2ea937bc62aaedb6c14cc1e8f0bb9f3f4fb5a9e2bbfa4b480", size = 91694, upload-time = "2025-05-08T02:36:23.289Z" },
- { url = "https://files.pythonhosted.org/packages/17/2d/f7f680984bcb7324a46a4c2df3bd73cf70faef0acfeb85a3f811abdfd590/ijson-3.4.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:afbe9748707684b6c5adc295c4fdcf27765b300aec4d484e14a13dca4e5c0afa", size = 61390, upload-time = "2025-05-08T02:36:24.42Z" },
- { url = "https://files.pythonhosted.org/packages/09/a1/f3ca7bab86f95bdb82494739e71d271410dfefce4590785d511669127145/ijson-3.4.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:d823f8f321b4d8d5fa020d0a84f089fec5d52b7c0762430476d9f8bf95bbc1a9", size = 61140, upload-time = "2025-05-08T02:36:26.708Z" },
- { url = "https://files.pythonhosted.org/packages/51/79/dd340df3d4fc7771c95df29997956b92ed0570fe7b616d1792fea9ad93f2/ijson-3.4.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b8a0a2c54f3becf76881188beefd98b484b1d3bd005769a740d5b433b089fa23", size = 214739, upload-time = "2025-05-08T02:36:27.973Z" },
- { url = "https://files.pythonhosted.org/packages/59/f0/85380b7f51d1f5fb7065d76a7b623e02feca920cc678d329b2eccc0011e0/ijson-3.4.0-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ced19a83ab09afa16257a0b15bc1aa888dbc555cb754be09d375c7f8d41051f2", size = 198338, upload-time = "2025-05-08T02:36:29.496Z" },
- { url = "https://files.pythonhosted.org/packages/a5/cd/313264cf2ec42e0f01d198c49deb7b6fadeb793b3685e20e738eb6b3fa13/ijson-3.4.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8100f9885eff1f38d35cef80ef759a1bbf5fc946349afa681bd7d0e681b7f1a0", size = 207515, upload-time = "2025-05-08T02:36:30.981Z" },
- { url = "https://files.pythonhosted.org/packages/12/94/bf14457aa87ea32641f2db577c9188ef4e4ae373478afef422b31fc7f309/ijson-3.4.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:d7bcc3f7f21b0f703031ecd15209b1284ea51b2a329d66074b5261de3916c1eb", size = 210081, upload-time = "2025-05-08T02:36:32.403Z" },
- { url = "https://files.pythonhosted.org/packages/7d/b4/eaee39e290e40e52d665db9bd1492cfdce86bd1e47948e0440db209c6023/ijson-3.4.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:2dcb190227b09dd171bdcbfe4720fddd574933c66314818dfb3960c8a6246a77", size = 199253, upload-time = "2025-05-08T02:36:33.861Z" },
- { url = "https://files.pythonhosted.org/packages/c5/9c/e09c7b9ac720a703ab115b221b819f149ed54c974edfff623c1e925e57da/ijson-3.4.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:eda4cfb1d49c6073a901735aaa62e39cb7ab47f3ad7bb184862562f776f1fa8a", size = 203816, upload-time = "2025-05-08T02:36:35.348Z" },
- { url = "https://files.pythonhosted.org/packages/7c/14/acd304f412e32d16a2c12182b9d78206bb0ae35354d35664f45db05c1b3b/ijson-3.4.0-cp313-cp313t-win32.whl", hash = "sha256:0772638efa1f3b72b51736833404f1cbd2f5beeb9c1a3d392e7d385b9160cba7", size = 53760, upload-time = "2025-05-08T02:36:36.608Z" },
- { url = "https://files.pythonhosted.org/packages/2f/24/93dd0a467191590a5ed1fc2b35842bca9d09900d001e00b0b497c0208ef6/ijson-3.4.0-cp313-cp313t-win_amd64.whl", hash = "sha256:3d8a0d67f36e4fb97c61a724456ef0791504b16ce6f74917a31c2e92309bbeb9", size = 56948, upload-time = "2025-05-08T02:36:37.849Z" },
- { url = "https://files.pythonhosted.org/packages/77/bc/a6777b5c3505b12fa9c5c0b9b3601418ae664653b032697ff465a4ecf508/ijson-3.4.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8a990401dc7350c1739f42187823e68d2ef6964b55040c6e9f3a29461f9929e2", size = 87662, upload-time = "2025-05-08T02:36:39.378Z" },
- { url = "https://files.pythonhosted.org/packages/eb/89/adc0ac5c24fc6524d52893d951a66120416ced4ceee9fa53de649624fa5d/ijson-3.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:80f50e0f5da4cd6b65e2d8ff38cb61b26559608a05dd3a3f9cfa6f19848e6f22", size = 59262, upload-time = "2025-05-08T02:36:40.8Z" },
- { url = "https://files.pythonhosted.org/packages/ea/c4/22e4eb1c12dde0a1c59ff321793ca8b796d85fa2ff638ec06a8e66f98b02/ijson-3.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2d9ca52f5650d820a2e7aa672dea1c560f609e165337e5b3ed7cf56d696bf309", size = 59323, upload-time = "2025-05-08T02:36:41.95Z" },
- { url = "https://files.pythonhosted.org/packages/c5/64/83457822e41fb9ecaf36e50d149978c4bf693cc9e14a72a34afe6ca5d133/ijson-3.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:940c8c5fd20fb89b56dde9194a4f1c7b779149f1ab26af6d8dc1da51a95d26dd", size = 130202, upload-time = "2025-05-08T02:36:43.202Z" },
- { url = "https://files.pythonhosted.org/packages/9e/a0/ce14ccfcddb039c115fc879380695bad5e8d8f3ba092454df5cb6ed4771c/ijson-3.4.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:41dbb525666017ad856ac9b4f0f4b87d3e56b7dfde680d5f6d123556b22e2172", size = 124547, upload-time = "2025-05-08T02:36:44.761Z" },
- { url = "https://files.pythonhosted.org/packages/59/7c/f78870bf57daa578542b2ea46da336d03de7c2971d2b2fcfed3773757a17/ijson-3.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9f84f5e2eea5c2d271c97221c382db005534294d1175ddd046a12369617c41c", size = 129407, upload-time = "2025-05-08T02:36:46.319Z" },
- { url = "https://files.pythonhosted.org/packages/02/08/693a327b50f9036026e062016d6417cd2ce31699cc56c27fe82fb9185140/ijson-3.4.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:c0cd126c11835839bba8ac0baaba568f67d701fc4f717791cf37b10b74a2ebd7", size = 130991, upload-time = "2025-05-08T02:36:47.595Z" },
- { url = "https://files.pythonhosted.org/packages/83/22/96ff12c3ca91613bb020bcf9b3aaee510324af999b08b7e7d2e7acb14123/ijson-3.4.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:f9a9d3bbc6d91c24a2524a189d2aca703cb5f7e8eb34ad0aff3c91702404a983", size = 126175, upload-time = "2025-05-08T02:36:48.992Z" },
- { url = "https://files.pythonhosted.org/packages/e9/59/3b37550686448fc053c456b9af47aa407e6ac4183015f435c0ea11db5849/ijson-3.4.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:56679ee133470d0f1f598a8ad109d760fcfebeef4819531e29335aefb7e4cb1a", size = 128775, upload-time = "2025-05-08T02:36:50.54Z" },
- { url = "https://files.pythonhosted.org/packages/5b/27/6922201d19427c1c6d1f970de3ede105d52ab87654c4d2c76920815bc57a/ijson-3.4.0-cp39-cp39-win32.whl", hash = "sha256:583c15ded42ba80104fa1d0fa0dfdd89bb47922f3bb893a931bb843aeb55a3f3", size = 51250, upload-time = "2025-05-08T02:36:51.811Z" },
- { url = "https://files.pythonhosted.org/packages/c3/70/9939dbbe3541d7cca69c95f64201cd2fd6dba7a6488e3b55e6227d6f6e42/ijson-3.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:4563e603e56f4451572d96b47311dffef5b933d825f3417881d4d3630c6edac2", size = 53737, upload-time = "2025-05-08T02:36:53.369Z" },
- { url = "https://files.pythonhosted.org/packages/a7/22/da919f16ca9254f8a9ea0ba482d2c1d012ce6e4c712dcafd8adb16b16c63/ijson-3.4.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:54e989c35dba9cf163d532c14bcf0c260897d5f465643f0cd1fba9c908bed7ef", size = 56480, upload-time = "2025-05-08T02:36:54.942Z" },
- { url = "https://files.pythonhosted.org/packages/6d/54/c2afd289e034d11c4909f4ea90c9dae55053bed358064f310c3dd5033657/ijson-3.4.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:494eeb8e87afef22fbb969a4cb81ac2c535f30406f334fb6136e9117b0bb5380", size = 55956, upload-time = "2025-05-08T02:36:56.178Z" },
- { url = "https://files.pythonhosted.org/packages/43/d6/18799b0fca9ecb8a47e22527eedcea3267e95d4567b564ef21d0299e2d12/ijson-3.4.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:81603de95de1688958af65cd2294881a4790edae7de540b70c65c8253c5dc44a", size = 69394, upload-time = "2025-05-08T02:36:57.699Z" },
- { url = "https://files.pythonhosted.org/packages/c2/d6/c58032c69e9e977bf6d954f22cad0cd52092db89c454ea98926744523665/ijson-3.4.0-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8524be12c1773e1be466034cc49c1ecbe3d5b47bb86217bd2a57f73f970a6c19", size = 70378, upload-time = "2025-05-08T02:36:58.98Z" },
- { url = "https://files.pythonhosted.org/packages/da/03/07c6840454d5d228bb5b4509c9a7ac5b9c0b8258e2b317a53f97372be1eb/ijson-3.4.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17994696ec895d05e0cfa21b11c68c920c82634b4a3d8b8a1455d6fe9fdee8f7", size = 67770, upload-time = "2025-05-08T02:37:00.162Z" },
- { url = "https://files.pythonhosted.org/packages/32/c7/da58a9840380308df574dfdb0276c9d802b12f6125f999e92bcef36db552/ijson-3.4.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:0b67727aaee55d43b2e82b6a866c3cbcb2b66a5e9894212190cbd8773d0d9857", size = 53858, upload-time = "2025-05-08T02:37:01.691Z" },
- { url = "https://files.pythonhosted.org/packages/a3/9b/0bc0594d357600c03c3b5a3a34043d764fc3ad3f0757d2f3aae5b28f6c1c/ijson-3.4.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:cdc8c5ca0eec789ed99db29c68012dda05027af0860bb360afd28d825238d69d", size = 56483, upload-time = "2025-05-08T02:37:03.274Z" },
- { url = "https://files.pythonhosted.org/packages/00/1f/506cf2574673da1adcc8a794ebb85bf857cabe6294523978637e646814de/ijson-3.4.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:8e6b44b6ec45d5b1a0ee9d97e0e65ab7f62258727004cbbe202bf5f198bc21f7", size = 55957, upload-time = "2025-05-08T02:37:04.865Z" },
- { url = "https://files.pythonhosted.org/packages/dc/3d/a7cd8d8a6de0f3084fe4d457a8f76176e11b013867d1cad16c67d25e8bec/ijson-3.4.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b51e239e4cb537929796e840d349fc731fdc0d58b1a0683ce5465ad725321e0f", size = 69394, upload-time = "2025-05-08T02:37:06.142Z" },
- { url = "https://files.pythonhosted.org/packages/32/51/aa30abc02aabfc41c95887acf5f1f88da569642d7197fbe5aa105545226d/ijson-3.4.0-pp311-pypy311_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed05d43ec02be8ddb1ab59579761f6656b25d241a77fd74f4f0f7ec09074318a", size = 70377, upload-time = "2025-05-08T02:37:07.353Z" },
- { url = "https://files.pythonhosted.org/packages/c7/37/7773659b8d8d98b34234e1237352f6b446a3c12941619686c7d4a8a5c69c/ijson-3.4.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cfeca1aaa59d93fd0a3718cbe5f7ef0effff85cf837e0bceb71831a47f39cc14", size = 67767, upload-time = "2025-05-08T02:37:08.587Z" },
- { url = "https://files.pythonhosted.org/packages/cd/1f/dd52a84ed140e31a5d226cd47d98d21aa559aead35ef7bae479eab4c494c/ijson-3.4.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:7ca72ca12e9a1dd4252c97d952be34282907f263f7e28fcdff3a01b83981e837", size = 53864, upload-time = "2025-05-08T02:37:10.044Z" },
- { url = "https://files.pythonhosted.org/packages/9f/08/0bbdce5e765fee9b5a29f8a9670c00adb54809122cdadd06cd2d33244d68/ijson-3.4.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0f79b2cd52bd220fff83b3ee4ef89b54fd897f57cc8564a6d8ab7ac669de3930", size = 56416, upload-time = "2025-05-08T02:37:11.23Z" },
- { url = "https://files.pythonhosted.org/packages/fd/33/3f62475b40ddb2bf9de1fb9e5f47d89748b4b91fe3c2cd645111d62438fb/ijson-3.4.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:d16eed737610ad5ad8989b5864fbe09c64133129734e840c29085bb0d497fb03", size = 55903, upload-time = "2025-05-08T02:37:12.476Z" },
- { url = "https://files.pythonhosted.org/packages/9f/25/c8955e4fef31f7d16635361ec9a2195845c45a2db1483d7790a57a640cc2/ijson-3.4.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b3aac1d7a27e1e3bdec5bd0689afe55c34aa499baa06a80852eda31f1ffa6dc", size = 69358, upload-time = "2025-05-08T02:37:14.854Z" },
- { url = "https://files.pythonhosted.org/packages/45/b1/900f5d9a868304ff571bab7d10491df17e92105a9846a619d6e4d806e60e/ijson-3.4.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:784ae654aa9851851e87f323e9429b20b58a5399f83e6a7e348e080f2892081f", size = 70343, upload-time = "2025-05-08T02:37:16.115Z" },
- { url = "https://files.pythonhosted.org/packages/b8/ed/2a6e467b4c403b0f182724929dd0c85da98e1d1b84e4766028d2c3220eea/ijson-3.4.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d05bd8fa6a8adefb32bbf7b993d2a2f4507db08453dd1a444c281413a6d9685", size = 67710, upload-time = "2025-05-08T02:37:17.675Z" },
- { url = "https://files.pythonhosted.org/packages/11/c8/de4e995b17effb92f610efc3193393d05f8f233062a716d254d7b4e736c1/ijson-3.4.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:b5a05fd935cc28786b88c16976313086cd96414c6a3eb0a3822c47ab48b1793e", size = 53782, upload-time = "2025-05-08T02:37:18.894Z" },
-]
-
-[[package]]
-name = "importlib-metadata"
-version = "8.7.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "zipp", marker = "python_full_version < '3.10'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/76/66/650a33bd90f786193e4de4b3ad86ea60b53c89b669a5c7be931fac31cdb0/importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000", size = 56641, upload-time = "2025-04-27T15:29:01.736Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/20/b0/36bd937216ec521246249be3bf9855081de4c5e06a0c9b4219dbeda50373/importlib_metadata-8.7.0-py3-none-any.whl", hash = "sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd", size = 27656, upload-time = "2025-04-27T15:29:00.214Z" },
-]
-
-[[package]]
-name = "importlib-resources"
-version = "6.5.2"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "zipp", marker = "python_full_version < '3.10'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/cf/8c/f834fbf984f691b4f7ff60f50b514cc3de5cc08abfc3295564dd89c5e2e7/importlib_resources-6.5.2.tar.gz", hash = "sha256:185f87adef5bcc288449d98fb4fba07cea78bc036455dd44c5fc4a2fe78fed2c", size = 44693, upload-time = "2025-01-03T18:51:56.698Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/a4/ed/1f1afb2e9e7f38a545d628f864d562a5ae64fe6f7a10e28ffb9b185b4e89/importlib_resources-6.5.2-py3-none-any.whl", hash = "sha256:789cfdc3ed28c78b67a06acb8126751ced69a3d5f79c095a98298cd8a760ccec", size = 37461, upload-time = "2025-01-03T18:51:54.306Z" },
-]
-
-[[package]]
-name = "iniconfig"
-version = "2.1.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793, upload-time = "2025-03-19T20:09:59.721Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050, upload-time = "2025-03-19T20:10:01.071Z" },
-]
-
-[[package]]
-name = "ipython"
-version = "8.18.1"
-source = { registry = "https://pypi.org/simple" }
-resolution-markers = [
- "python_full_version < '3.10'",
-]
-dependencies = [
- { name = "colorama", marker = "python_full_version < '3.10' and sys_platform == 'win32'" },
- { name = "decorator", marker = "python_full_version < '3.10'" },
- { name = "exceptiongroup", marker = "python_full_version < '3.10'" },
- { name = "jedi", marker = "python_full_version < '3.10'" },
- { name = "matplotlib-inline", marker = "python_full_version < '3.10'" },
- { name = "pexpect", marker = "python_full_version < '3.10' and sys_platform != 'win32'" },
- { name = "prompt-toolkit", marker = "python_full_version < '3.10'" },
- { name = "pygments", marker = "python_full_version < '3.10'" },
- { name = "stack-data", marker = "python_full_version < '3.10'" },
- { name = "traitlets", marker = "python_full_version < '3.10'" },
- { name = "typing-extensions", marker = "python_full_version < '3.10'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/b1/b9/3ba6c45a6df813c09a48bac313c22ff83efa26cbb55011218d925a46e2ad/ipython-8.18.1.tar.gz", hash = "sha256:ca6f079bb33457c66e233e4580ebfc4128855b4cf6370dddd73842a9563e8a27", size = 5486330, upload-time = "2023-11-27T09:58:34.596Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/47/6b/d9fdcdef2eb6a23f391251fde8781c38d42acd82abe84d054cb74f7863b0/ipython-8.18.1-py3-none-any.whl", hash = "sha256:e8267419d72d81955ec1177f8a29aaa90ac80ad647499201119e2f05e99aa397", size = 808161, upload-time = "2023-11-27T09:58:30.538Z" },
-]
-
-[[package]]
-name = "ipython"
-version = "8.37.0"
-source = { registry = "https://pypi.org/simple" }
-resolution-markers = [
- "python_full_version == '3.10.*'",
-]
-dependencies = [
- { name = "colorama", marker = "python_full_version == '3.10.*' and sys_platform == 'win32'" },
- { name = "decorator", marker = "python_full_version == '3.10.*'" },
- { name = "exceptiongroup", marker = "python_full_version == '3.10.*'" },
- { name = "jedi", marker = "python_full_version == '3.10.*'" },
- { name = "matplotlib-inline", marker = "python_full_version == '3.10.*'" },
- { name = "pexpect", marker = "python_full_version == '3.10.*' and sys_platform != 'emscripten' and sys_platform != 'win32'" },
- { name = "prompt-toolkit", marker = "python_full_version == '3.10.*'" },
- { name = "pygments", marker = "python_full_version == '3.10.*'" },
- { name = "stack-data", marker = "python_full_version == '3.10.*'" },
- { name = "traitlets", marker = "python_full_version == '3.10.*'" },
- { name = "typing-extensions", marker = "python_full_version == '3.10.*'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/85/31/10ac88f3357fc276dc8a64e8880c82e80e7459326ae1d0a211b40abf6665/ipython-8.37.0.tar.gz", hash = "sha256:ca815841e1a41a1e6b73a0b08f3038af9b2252564d01fc405356d34033012216", size = 5606088, upload-time = "2025-05-31T16:39:09.613Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/91/d0/274fbf7b0b12643cbbc001ce13e6a5b1607ac4929d1b11c72460152c9fc3/ipython-8.37.0-py3-none-any.whl", hash = "sha256:ed87326596b878932dbcb171e3e698845434d8c61b8d8cd474bf663041a9dcf2", size = 831864, upload-time = "2025-05-31T16:39:06.38Z" },
-]
-
-[[package]]
-name = "ipython"
-version = "9.5.0"
-source = { registry = "https://pypi.org/simple" }
-resolution-markers = [
- "python_full_version >= '3.13'",
- "python_full_version >= '3.12.4' and python_full_version < '3.13'",
- "python_full_version >= '3.12' and python_full_version < '3.12.4'",
- "python_full_version == '3.11.*'",
-]
-dependencies = [
- { name = "colorama", marker = "python_full_version >= '3.11' and sys_platform == 'win32'" },
- { name = "decorator", marker = "python_full_version >= '3.11'" },
- { name = "ipython-pygments-lexers", marker = "python_full_version >= '3.11'" },
- { name = "jedi", marker = "python_full_version >= '3.11'" },
- { name = "matplotlib-inline", marker = "python_full_version >= '3.11'" },
- { name = "pexpect", marker = "python_full_version >= '3.11' and sys_platform != 'emscripten' and sys_platform != 'win32'" },
- { name = "prompt-toolkit", marker = "python_full_version >= '3.11'" },
- { name = "pygments", marker = "python_full_version >= '3.11'" },
- { name = "stack-data", marker = "python_full_version >= '3.11'" },
- { name = "traitlets", marker = "python_full_version >= '3.11'" },
- { name = "typing-extensions", marker = "python_full_version == '3.11.*'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/6e/71/a86262bf5a68bf211bcc71fe302af7e05f18a2852fdc610a854d20d085e6/ipython-9.5.0.tar.gz", hash = "sha256:129c44b941fe6d9b82d36fc7a7c18127ddb1d6f02f78f867f402e2e3adde3113", size = 4389137, upload-time = "2025-08-29T12:15:21.519Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/08/2a/5628a99d04acb2d2f2e749cdf4ea571d2575e898df0528a090948018b726/ipython-9.5.0-py3-none-any.whl", hash = "sha256:88369ffa1d5817d609120daa523a6da06d02518e582347c29f8451732a9c5e72", size = 612426, upload-time = "2025-08-29T12:15:18.866Z" },
-]
-
-[[package]]
-name = "ipython-pygments-lexers"
-version = "1.1.1"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "pygments", marker = "python_full_version >= '3.11'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/ef/4c/5dd1d8af08107f88c7f741ead7a40854b8ac24ddf9ae850afbcf698aa552/ipython_pygments_lexers-1.1.1.tar.gz", hash = "sha256:09c0138009e56b6854f9535736f4171d855c8c08a563a0dcd8022f78355c7e81", size = 8393, upload-time = "2025-01-17T11:24:34.505Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/d9/33/1f075bf72b0b747cb3288d011319aaf64083cf2efef8354174e3ed4540e2/ipython_pygments_lexers-1.1.1-py3-none-any.whl", hash = "sha256:a9462224a505ade19a605f71f8fa63c2048833ce50abc86768a0d81d876dc81c", size = 8074, upload-time = "2025-01-17T11:24:33.271Z" },
-]
-
-[[package]]
-name = "jedi"
-version = "0.19.2"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "parso" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/72/3a/79a912fbd4d8dd6fbb02bf69afd3bb72cf0c729bb3063c6f4498603db17a/jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0", size = 1231287, upload-time = "2024-11-11T01:41:42.873Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/c0/5a/9cac0c82afec3d09ccd97c8b6502d48f165f9124db81b4bcb90b4af974ee/jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9", size = 1572278, upload-time = "2024-11-11T01:41:40.175Z" },
-]
-
-[[package]]
-name = "jinja2"
-version = "3.1.6"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "markupsafe" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" },
-]
-
-[[package]]
-name = "jiter"
-version = "0.10.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/ee/9d/ae7ddb4b8ab3fb1b51faf4deb36cb48a4fbbd7cb36bad6a5fca4741306f7/jiter-0.10.0.tar.gz", hash = "sha256:07a7142c38aacc85194391108dc91b5b57093c978a9932bd86a36862759d9500", size = 162759, upload-time = "2025-05-18T19:04:59.73Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/be/7e/4011b5c77bec97cb2b572f566220364e3e21b51c48c5bd9c4a9c26b41b67/jiter-0.10.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:cd2fb72b02478f06a900a5782de2ef47e0396b3e1f7d5aba30daeb1fce66f303", size = 317215, upload-time = "2025-05-18T19:03:04.303Z" },
- { url = "https://files.pythonhosted.org/packages/8a/4f/144c1b57c39692efc7ea7d8e247acf28e47d0912800b34d0ad815f6b2824/jiter-0.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:32bb468e3af278f095d3fa5b90314728a6916d89ba3d0ffb726dd9bf7367285e", size = 322814, upload-time = "2025-05-18T19:03:06.433Z" },
- { url = "https://files.pythonhosted.org/packages/63/1f/db977336d332a9406c0b1f0b82be6f71f72526a806cbb2281baf201d38e3/jiter-0.10.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa8b3e0068c26ddedc7abc6fac37da2d0af16b921e288a5a613f4b86f050354f", size = 345237, upload-time = "2025-05-18T19:03:07.833Z" },
- { url = "https://files.pythonhosted.org/packages/d7/1c/aa30a4a775e8a672ad7f21532bdbfb269f0706b39c6ff14e1f86bdd9e5ff/jiter-0.10.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:286299b74cc49e25cd42eea19b72aa82c515d2f2ee12d11392c56d8701f52224", size = 370999, upload-time = "2025-05-18T19:03:09.338Z" },
- { url = "https://files.pythonhosted.org/packages/35/df/f8257abc4207830cb18880781b5f5b716bad5b2a22fb4330cfd357407c5b/jiter-0.10.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6ed5649ceeaeffc28d87fb012d25a4cd356dcd53eff5acff1f0466b831dda2a7", size = 491109, upload-time = "2025-05-18T19:03:11.13Z" },
- { url = "https://files.pythonhosted.org/packages/06/76/9e1516fd7b4278aa13a2cc7f159e56befbea9aa65c71586305e7afa8b0b3/jiter-0.10.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2ab0051160cb758a70716448908ef14ad476c3774bd03ddce075f3c1f90a3d6", size = 388608, upload-time = "2025-05-18T19:03:12.911Z" },
- { url = "https://files.pythonhosted.org/packages/6d/64/67750672b4354ca20ca18d3d1ccf2c62a072e8a2d452ac3cf8ced73571ef/jiter-0.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:03997d2f37f6b67d2f5c475da4412be584e1cec273c1cfc03d642c46db43f8cf", size = 352454, upload-time = "2025-05-18T19:03:14.741Z" },
- { url = "https://files.pythonhosted.org/packages/96/4d/5c4e36d48f169a54b53a305114be3efa2bbffd33b648cd1478a688f639c1/jiter-0.10.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c404a99352d839fed80d6afd6c1d66071f3bacaaa5c4268983fc10f769112e90", size = 391833, upload-time = "2025-05-18T19:03:16.426Z" },
- { url = "https://files.pythonhosted.org/packages/0b/de/ce4a6166a78810bd83763d2fa13f85f73cbd3743a325469a4a9289af6dae/jiter-0.10.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:66e989410b6666d3ddb27a74c7e50d0829704ede652fd4c858e91f8d64b403d0", size = 523646, upload-time = "2025-05-18T19:03:17.704Z" },
- { url = "https://files.pythonhosted.org/packages/a2/a6/3bc9acce53466972964cf4ad85efecb94f9244539ab6da1107f7aed82934/jiter-0.10.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b532d3af9ef4f6374609a3bcb5e05a1951d3bf6190dc6b176fdb277c9bbf15ee", size = 514735, upload-time = "2025-05-18T19:03:19.44Z" },
- { url = "https://files.pythonhosted.org/packages/b4/d8/243c2ab8426a2a4dea85ba2a2ba43df379ccece2145320dfd4799b9633c5/jiter-0.10.0-cp310-cp310-win32.whl", hash = "sha256:da9be20b333970e28b72edc4dff63d4fec3398e05770fb3205f7fb460eb48dd4", size = 210747, upload-time = "2025-05-18T19:03:21.184Z" },
- { url = "https://files.pythonhosted.org/packages/37/7a/8021bd615ef7788b98fc76ff533eaac846322c170e93cbffa01979197a45/jiter-0.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:f59e533afed0c5b0ac3eba20d2548c4a550336d8282ee69eb07b37ea526ee4e5", size = 207484, upload-time = "2025-05-18T19:03:23.046Z" },
- { url = "https://files.pythonhosted.org/packages/1b/dd/6cefc6bd68b1c3c979cecfa7029ab582b57690a31cd2f346c4d0ce7951b6/jiter-0.10.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:3bebe0c558e19902c96e99217e0b8e8b17d570906e72ed8a87170bc290b1e978", size = 317473, upload-time = "2025-05-18T19:03:25.942Z" },
- { url = "https://files.pythonhosted.org/packages/be/cf/fc33f5159ce132be1d8dd57251a1ec7a631c7df4bd11e1cd198308c6ae32/jiter-0.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:558cc7e44fd8e507a236bee6a02fa17199ba752874400a0ca6cd6e2196cdb7dc", size = 321971, upload-time = "2025-05-18T19:03:27.255Z" },
- { url = "https://files.pythonhosted.org/packages/68/a4/da3f150cf1d51f6c472616fb7650429c7ce053e0c962b41b68557fdf6379/jiter-0.10.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d613e4b379a07d7c8453c5712ce7014e86c6ac93d990a0b8e7377e18505e98d", size = 345574, upload-time = "2025-05-18T19:03:28.63Z" },
- { url = "https://files.pythonhosted.org/packages/84/34/6e8d412e60ff06b186040e77da5f83bc158e9735759fcae65b37d681f28b/jiter-0.10.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f62cf8ba0618eda841b9bf61797f21c5ebd15a7a1e19daab76e4e4b498d515b2", size = 371028, upload-time = "2025-05-18T19:03:30.292Z" },
- { url = "https://files.pythonhosted.org/packages/fb/d9/9ee86173aae4576c35a2f50ae930d2ccb4c4c236f6cb9353267aa1d626b7/jiter-0.10.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:919d139cdfa8ae8945112398511cb7fca58a77382617d279556b344867a37e61", size = 491083, upload-time = "2025-05-18T19:03:31.654Z" },
- { url = "https://files.pythonhosted.org/packages/d9/2c/f955de55e74771493ac9e188b0f731524c6a995dffdcb8c255b89c6fb74b/jiter-0.10.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:13ddbc6ae311175a3b03bd8994881bc4635c923754932918e18da841632349db", size = 388821, upload-time = "2025-05-18T19:03:33.184Z" },
- { url = "https://files.pythonhosted.org/packages/81/5a/0e73541b6edd3f4aada586c24e50626c7815c561a7ba337d6a7eb0a915b4/jiter-0.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c440ea003ad10927a30521a9062ce10b5479592e8a70da27f21eeb457b4a9c5", size = 352174, upload-time = "2025-05-18T19:03:34.965Z" },
- { url = "https://files.pythonhosted.org/packages/1c/c0/61eeec33b8c75b31cae42be14d44f9e6fe3ac15a4e58010256ac3abf3638/jiter-0.10.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:dc347c87944983481e138dea467c0551080c86b9d21de6ea9306efb12ca8f606", size = 391869, upload-time = "2025-05-18T19:03:36.436Z" },
- { url = "https://files.pythonhosted.org/packages/41/22/5beb5ee4ad4ef7d86f5ea5b4509f680a20706c4a7659e74344777efb7739/jiter-0.10.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:13252b58c1f4d8c5b63ab103c03d909e8e1e7842d302473f482915d95fefd605", size = 523741, upload-time = "2025-05-18T19:03:38.168Z" },
- { url = "https://files.pythonhosted.org/packages/ea/10/768e8818538e5817c637b0df52e54366ec4cebc3346108a4457ea7a98f32/jiter-0.10.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7d1bbf3c465de4a24ab12fb7766a0003f6f9bce48b8b6a886158c4d569452dc5", size = 514527, upload-time = "2025-05-18T19:03:39.577Z" },
- { url = "https://files.pythonhosted.org/packages/73/6d/29b7c2dc76ce93cbedabfd842fc9096d01a0550c52692dfc33d3cc889815/jiter-0.10.0-cp311-cp311-win32.whl", hash = "sha256:db16e4848b7e826edca4ccdd5b145939758dadf0dc06e7007ad0e9cfb5928ae7", size = 210765, upload-time = "2025-05-18T19:03:41.271Z" },
- { url = "https://files.pythonhosted.org/packages/c2/c9/d394706deb4c660137caf13e33d05a031d734eb99c051142e039d8ceb794/jiter-0.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:9c9c1d5f10e18909e993f9641f12fe1c77b3e9b533ee94ffa970acc14ded3812", size = 209234, upload-time = "2025-05-18T19:03:42.918Z" },
- { url = "https://files.pythonhosted.org/packages/6d/b5/348b3313c58f5fbfb2194eb4d07e46a35748ba6e5b3b3046143f3040bafa/jiter-0.10.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:1e274728e4a5345a6dde2d343c8da018b9d4bd4350f5a472fa91f66fda44911b", size = 312262, upload-time = "2025-05-18T19:03:44.637Z" },
- { url = "https://files.pythonhosted.org/packages/9c/4a/6a2397096162b21645162825f058d1709a02965606e537e3304b02742e9b/jiter-0.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7202ae396446c988cb2a5feb33a543ab2165b786ac97f53b59aafb803fef0744", size = 320124, upload-time = "2025-05-18T19:03:46.341Z" },
- { url = "https://files.pythonhosted.org/packages/2a/85/1ce02cade7516b726dd88f59a4ee46914bf79d1676d1228ef2002ed2f1c9/jiter-0.10.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23ba7722d6748b6920ed02a8f1726fb4b33e0fd2f3f621816a8b486c66410ab2", size = 345330, upload-time = "2025-05-18T19:03:47.596Z" },
- { url = "https://files.pythonhosted.org/packages/75/d0/bb6b4f209a77190ce10ea8d7e50bf3725fc16d3372d0a9f11985a2b23eff/jiter-0.10.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:371eab43c0a288537d30e1f0b193bc4eca90439fc08a022dd83e5e07500ed026", size = 369670, upload-time = "2025-05-18T19:03:49.334Z" },
- { url = "https://files.pythonhosted.org/packages/a0/f5/a61787da9b8847a601e6827fbc42ecb12be2c925ced3252c8ffcb56afcaf/jiter-0.10.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6c675736059020365cebc845a820214765162728b51ab1e03a1b7b3abb70f74c", size = 489057, upload-time = "2025-05-18T19:03:50.66Z" },
- { url = "https://files.pythonhosted.org/packages/12/e4/6f906272810a7b21406c760a53aadbe52e99ee070fc5c0cb191e316de30b/jiter-0.10.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0c5867d40ab716e4684858e4887489685968a47e3ba222e44cde6e4a2154f959", size = 389372, upload-time = "2025-05-18T19:03:51.98Z" },
- { url = "https://files.pythonhosted.org/packages/e2/ba/77013b0b8ba904bf3762f11e0129b8928bff7f978a81838dfcc958ad5728/jiter-0.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:395bb9a26111b60141757d874d27fdea01b17e8fac958b91c20128ba8f4acc8a", size = 352038, upload-time = "2025-05-18T19:03:53.703Z" },
- { url = "https://files.pythonhosted.org/packages/67/27/c62568e3ccb03368dbcc44a1ef3a423cb86778a4389e995125d3d1aaa0a4/jiter-0.10.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6842184aed5cdb07e0c7e20e5bdcfafe33515ee1741a6835353bb45fe5d1bd95", size = 391538, upload-time = "2025-05-18T19:03:55.046Z" },
- { url = "https://files.pythonhosted.org/packages/c0/72/0d6b7e31fc17a8fdce76164884edef0698ba556b8eb0af9546ae1a06b91d/jiter-0.10.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:62755d1bcea9876770d4df713d82606c8c1a3dca88ff39046b85a048566d56ea", size = 523557, upload-time = "2025-05-18T19:03:56.386Z" },
- { url = "https://files.pythonhosted.org/packages/2f/09/bc1661fbbcbeb6244bd2904ff3a06f340aa77a2b94e5a7373fd165960ea3/jiter-0.10.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:533efbce2cacec78d5ba73a41756beff8431dfa1694b6346ce7af3a12c42202b", size = 514202, upload-time = "2025-05-18T19:03:57.675Z" },
- { url = "https://files.pythonhosted.org/packages/1b/84/5a5d5400e9d4d54b8004c9673bbe4403928a00d28529ff35b19e9d176b19/jiter-0.10.0-cp312-cp312-win32.whl", hash = "sha256:8be921f0cadd245e981b964dfbcd6fd4bc4e254cdc069490416dd7a2632ecc01", size = 211781, upload-time = "2025-05-18T19:03:59.025Z" },
- { url = "https://files.pythonhosted.org/packages/9b/52/7ec47455e26f2d6e5f2ea4951a0652c06e5b995c291f723973ae9e724a65/jiter-0.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:a7c7d785ae9dda68c2678532a5a1581347e9c15362ae9f6e68f3fdbfb64f2e49", size = 206176, upload-time = "2025-05-18T19:04:00.305Z" },
- { url = "https://files.pythonhosted.org/packages/2e/b0/279597e7a270e8d22623fea6c5d4eeac328e7d95c236ed51a2b884c54f70/jiter-0.10.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:e0588107ec8e11b6f5ef0e0d656fb2803ac6cf94a96b2b9fc675c0e3ab5e8644", size = 311617, upload-time = "2025-05-18T19:04:02.078Z" },
- { url = "https://files.pythonhosted.org/packages/91/e3/0916334936f356d605f54cc164af4060e3e7094364add445a3bc79335d46/jiter-0.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cafc4628b616dc32530c20ee53d71589816cf385dd9449633e910d596b1f5c8a", size = 318947, upload-time = "2025-05-18T19:04:03.347Z" },
- { url = "https://files.pythonhosted.org/packages/6a/8e/fd94e8c02d0e94539b7d669a7ebbd2776e51f329bb2c84d4385e8063a2ad/jiter-0.10.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:520ef6d981172693786a49ff5b09eda72a42e539f14788124a07530f785c3ad6", size = 344618, upload-time = "2025-05-18T19:04:04.709Z" },
- { url = "https://files.pythonhosted.org/packages/6f/b0/f9f0a2ec42c6e9c2e61c327824687f1e2415b767e1089c1d9135f43816bd/jiter-0.10.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:554dedfd05937f8fc45d17ebdf298fe7e0c77458232bcb73d9fbbf4c6455f5b3", size = 368829, upload-time = "2025-05-18T19:04:06.912Z" },
- { url = "https://files.pythonhosted.org/packages/e8/57/5bbcd5331910595ad53b9fd0c610392ac68692176f05ae48d6ce5c852967/jiter-0.10.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5bc299da7789deacf95f64052d97f75c16d4fc8c4c214a22bf8d859a4288a1c2", size = 491034, upload-time = "2025-05-18T19:04:08.222Z" },
- { url = "https://files.pythonhosted.org/packages/9b/be/c393df00e6e6e9e623a73551774449f2f23b6ec6a502a3297aeeece2c65a/jiter-0.10.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5161e201172de298a8a1baad95eb85db4fb90e902353b1f6a41d64ea64644e25", size = 388529, upload-time = "2025-05-18T19:04:09.566Z" },
- { url = "https://files.pythonhosted.org/packages/42/3e/df2235c54d365434c7f150b986a6e35f41ebdc2f95acea3036d99613025d/jiter-0.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e2227db6ba93cb3e2bf67c87e594adde0609f146344e8207e8730364db27041", size = 350671, upload-time = "2025-05-18T19:04:10.98Z" },
- { url = "https://files.pythonhosted.org/packages/c6/77/71b0b24cbcc28f55ab4dbfe029f9a5b73aeadaba677843fc6dc9ed2b1d0a/jiter-0.10.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:15acb267ea5e2c64515574b06a8bf393fbfee6a50eb1673614aa45f4613c0cca", size = 390864, upload-time = "2025-05-18T19:04:12.722Z" },
- { url = "https://files.pythonhosted.org/packages/6a/d3/ef774b6969b9b6178e1d1e7a89a3bd37d241f3d3ec5f8deb37bbd203714a/jiter-0.10.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:901b92f2e2947dc6dfcb52fd624453862e16665ea909a08398dde19c0731b7f4", size = 522989, upload-time = "2025-05-18T19:04:14.261Z" },
- { url = "https://files.pythonhosted.org/packages/0c/41/9becdb1d8dd5d854142f45a9d71949ed7e87a8e312b0bede2de849388cb9/jiter-0.10.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:d0cb9a125d5a3ec971a094a845eadde2db0de85b33c9f13eb94a0c63d463879e", size = 513495, upload-time = "2025-05-18T19:04:15.603Z" },
- { url = "https://files.pythonhosted.org/packages/9c/36/3468e5a18238bdedae7c4d19461265b5e9b8e288d3f86cd89d00cbb48686/jiter-0.10.0-cp313-cp313-win32.whl", hash = "sha256:48a403277ad1ee208fb930bdf91745e4d2d6e47253eedc96e2559d1e6527006d", size = 211289, upload-time = "2025-05-18T19:04:17.541Z" },
- { url = "https://files.pythonhosted.org/packages/7e/07/1c96b623128bcb913706e294adb5f768fb7baf8db5e1338ce7b4ee8c78ef/jiter-0.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:75f9eb72ecb640619c29bf714e78c9c46c9c4eaafd644bf78577ede459f330d4", size = 205074, upload-time = "2025-05-18T19:04:19.21Z" },
- { url = "https://files.pythonhosted.org/packages/54/46/caa2c1342655f57d8f0f2519774c6d67132205909c65e9aa8255e1d7b4f4/jiter-0.10.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:28ed2a4c05a1f32ef0e1d24c2611330219fed727dae01789f4a335617634b1ca", size = 318225, upload-time = "2025-05-18T19:04:20.583Z" },
- { url = "https://files.pythonhosted.org/packages/43/84/c7d44c75767e18946219ba2d703a5a32ab37b0bc21886a97bc6062e4da42/jiter-0.10.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14a4c418b1ec86a195f1ca69da8b23e8926c752b685af665ce30777233dfe070", size = 350235, upload-time = "2025-05-18T19:04:22.363Z" },
- { url = "https://files.pythonhosted.org/packages/01/16/f5a0135ccd968b480daad0e6ab34b0c7c5ba3bc447e5088152696140dcb3/jiter-0.10.0-cp313-cp313t-win_amd64.whl", hash = "sha256:d7bfed2fe1fe0e4dda6ef682cee888ba444b21e7a6553e03252e4feb6cf0adca", size = 207278, upload-time = "2025-05-18T19:04:23.627Z" },
- { url = "https://files.pythonhosted.org/packages/1c/9b/1d646da42c3de6c2188fdaa15bce8ecb22b635904fc68be025e21249ba44/jiter-0.10.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:5e9251a5e83fab8d87799d3e1a46cb4b7f2919b895c6f4483629ed2446f66522", size = 310866, upload-time = "2025-05-18T19:04:24.891Z" },
- { url = "https://files.pythonhosted.org/packages/ad/0e/26538b158e8a7c7987e94e7aeb2999e2e82b1f9d2e1f6e9874ddf71ebda0/jiter-0.10.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:023aa0204126fe5b87ccbcd75c8a0d0261b9abdbbf46d55e7ae9f8e22424eeb8", size = 318772, upload-time = "2025-05-18T19:04:26.161Z" },
- { url = "https://files.pythonhosted.org/packages/7b/fb/d302893151caa1c2636d6574d213e4b34e31fd077af6050a9c5cbb42f6fb/jiter-0.10.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c189c4f1779c05f75fc17c0c1267594ed918996a231593a21a5ca5438445216", size = 344534, upload-time = "2025-05-18T19:04:27.495Z" },
- { url = "https://files.pythonhosted.org/packages/01/d8/5780b64a149d74e347c5128d82176eb1e3241b1391ac07935693466d6219/jiter-0.10.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:15720084d90d1098ca0229352607cd68256c76991f6b374af96f36920eae13c4", size = 369087, upload-time = "2025-05-18T19:04:28.896Z" },
- { url = "https://files.pythonhosted.org/packages/e8/5b/f235a1437445160e777544f3ade57544daf96ba7e96c1a5b24a6f7ac7004/jiter-0.10.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e4f2fb68e5f1cfee30e2b2a09549a00683e0fde4c6a2ab88c94072fc33cb7426", size = 490694, upload-time = "2025-05-18T19:04:30.183Z" },
- { url = "https://files.pythonhosted.org/packages/85/a9/9c3d4617caa2ff89cf61b41e83820c27ebb3f7b5fae8a72901e8cd6ff9be/jiter-0.10.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ce541693355fc6da424c08b7edf39a2895f58d6ea17d92cc2b168d20907dee12", size = 388992, upload-time = "2025-05-18T19:04:32.028Z" },
- { url = "https://files.pythonhosted.org/packages/68/b1/344fd14049ba5c94526540af7eb661871f9c54d5f5601ff41a959b9a0bbd/jiter-0.10.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31c50c40272e189d50006ad5c73883caabb73d4e9748a688b216e85a9a9ca3b9", size = 351723, upload-time = "2025-05-18T19:04:33.467Z" },
- { url = "https://files.pythonhosted.org/packages/41/89/4c0e345041186f82a31aee7b9d4219a910df672b9fef26f129f0cda07a29/jiter-0.10.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fa3402a2ff9815960e0372a47b75c76979d74402448509ccd49a275fa983ef8a", size = 392215, upload-time = "2025-05-18T19:04:34.827Z" },
- { url = "https://files.pythonhosted.org/packages/55/58/ee607863e18d3f895feb802154a2177d7e823a7103f000df182e0f718b38/jiter-0.10.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:1956f934dca32d7bb647ea21d06d93ca40868b505c228556d3373cbd255ce853", size = 522762, upload-time = "2025-05-18T19:04:36.19Z" },
- { url = "https://files.pythonhosted.org/packages/15/d0/9123fb41825490d16929e73c212de9a42913d68324a8ce3c8476cae7ac9d/jiter-0.10.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:fcedb049bdfc555e261d6f65a6abe1d5ad68825b7202ccb9692636c70fcced86", size = 513427, upload-time = "2025-05-18T19:04:37.544Z" },
- { url = "https://files.pythonhosted.org/packages/d8/b3/2bd02071c5a2430d0b70403a34411fc519c2f227da7b03da9ba6a956f931/jiter-0.10.0-cp314-cp314-win32.whl", hash = "sha256:ac509f7eccca54b2a29daeb516fb95b6f0bd0d0d8084efaf8ed5dfc7b9f0b357", size = 210127, upload-time = "2025-05-18T19:04:38.837Z" },
- { url = "https://files.pythonhosted.org/packages/03/0c/5fe86614ea050c3ecd728ab4035534387cd41e7c1855ef6c031f1ca93e3f/jiter-0.10.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5ed975b83a2b8639356151cef5c0d597c68376fc4922b45d0eb384ac058cfa00", size = 318527, upload-time = "2025-05-18T19:04:40.612Z" },
- { url = "https://files.pythonhosted.org/packages/b3/4a/4175a563579e884192ba6e81725fc0448b042024419be8d83aa8a80a3f44/jiter-0.10.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3aa96f2abba33dc77f79b4cf791840230375f9534e5fac927ccceb58c5e604a5", size = 354213, upload-time = "2025-05-18T19:04:41.894Z" },
- { url = "https://files.pythonhosted.org/packages/98/fd/aced428e2bd3c6c1132f67c5a708f9e7fd161d0ca8f8c5862b17b93cdf0a/jiter-0.10.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:bd6292a43c0fc09ce7c154ec0fa646a536b877d1e8f2f96c19707f65355b5a4d", size = 317665, upload-time = "2025-05-18T19:04:43.417Z" },
- { url = "https://files.pythonhosted.org/packages/b6/2e/47d42f15d53ed382aef8212a737101ae2720e3697a954f9b95af06d34e89/jiter-0.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:39de429dcaeb6808d75ffe9effefe96a4903c6a4b376b2f6d08d77c1aaee2f18", size = 312152, upload-time = "2025-05-18T19:04:44.797Z" },
- { url = "https://files.pythonhosted.org/packages/7b/02/aae834228ef4834fc18718724017995ace8da5f70aa1ec225b9bc2b2d7aa/jiter-0.10.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52ce124f13a7a616fad3bb723f2bfb537d78239d1f7f219566dc52b6f2a9e48d", size = 346708, upload-time = "2025-05-18T19:04:46.127Z" },
- { url = "https://files.pythonhosted.org/packages/35/d4/6ff39dee2d0a9abd69d8a3832ce48a3aa644eed75e8515b5ff86c526ca9a/jiter-0.10.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:166f3606f11920f9a1746b2eea84fa2c0a5d50fd313c38bdea4edc072000b0af", size = 371360, upload-time = "2025-05-18T19:04:47.448Z" },
- { url = "https://files.pythonhosted.org/packages/a9/67/c749d962b4eb62445867ae4e64a543cbb5d63cc7d78ada274ac515500a7f/jiter-0.10.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:28dcecbb4ba402916034fc14eba7709f250c4d24b0c43fc94d187ee0580af181", size = 492105, upload-time = "2025-05-18T19:04:48.792Z" },
- { url = "https://files.pythonhosted.org/packages/f6/d3/8fe1b1bae5161f27b1891c256668f598fa4c30c0a7dacd668046a6215fca/jiter-0.10.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:86c5aa6910f9bebcc7bc4f8bc461aff68504388b43bfe5e5c0bd21efa33b52f4", size = 389577, upload-time = "2025-05-18T19:04:50.13Z" },
- { url = "https://files.pythonhosted.org/packages/ef/28/ecb19d789b4777898a4252bfaac35e3f8caf16c93becd58dcbaac0dc24ad/jiter-0.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ceeb52d242b315d7f1f74b441b6a167f78cea801ad7c11c36da77ff2d42e8a28", size = 353849, upload-time = "2025-05-18T19:04:51.443Z" },
- { url = "https://files.pythonhosted.org/packages/77/69/261f798f84790da6482ebd8c87ec976192b8c846e79444d0a2e0d33ebed8/jiter-0.10.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ff76d8887c8c8ee1e772274fcf8cc1071c2c58590d13e33bd12d02dc9a560397", size = 392029, upload-time = "2025-05-18T19:04:52.792Z" },
- { url = "https://files.pythonhosted.org/packages/cb/08/b8d15140d4d91f16faa2f5d416c1a71ab1bbe2b66c57197b692d04c0335f/jiter-0.10.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a9be4d0fa2b79f7222a88aa488bd89e2ae0a0a5b189462a12def6ece2faa45f1", size = 524386, upload-time = "2025-05-18T19:04:54.203Z" },
- { url = "https://files.pythonhosted.org/packages/9b/1d/23c41765cc95c0e23ac492a88450d34bf0fd87a37218d1b97000bffe0f53/jiter-0.10.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9ab7fd8738094139b6c1ab1822d6f2000ebe41515c537235fd45dabe13ec9324", size = 515234, upload-time = "2025-05-18T19:04:55.838Z" },
- { url = "https://files.pythonhosted.org/packages/9f/14/381d8b151132e79790579819c3775be32820569f23806769658535fe467f/jiter-0.10.0-cp39-cp39-win32.whl", hash = "sha256:5f51e048540dd27f204ff4a87f5d79294ea0aa3aa552aca34934588cf27023cf", size = 211436, upload-time = "2025-05-18T19:04:57.183Z" },
- { url = "https://files.pythonhosted.org/packages/59/66/f23ae51dea8ee8ce429027b60008ca895d0fa0704f0c7fe5f09014a6cffb/jiter-0.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:1b28302349dc65703a9e4ead16f163b1c339efffbe1049c30a44b001a2a4fff9", size = 208777, upload-time = "2025-05-18T19:04:58.454Z" },
-]
-
-[[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, upload-time = "2022-06-17T18:00:12.224Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/31/b4/b9b800c45527aadd64d5b442f9b932b00648617eb5d63d2c7a6587b7cafc/jmespath-1.0.1-py3-none-any.whl", hash = "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980", size = 20256, upload-time = "2022-06-17T18:00:10.251Z" },
-]
-
-[[package]]
-name = "joblib"
-version = "1.5.2"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/e8/5d/447af5ea094b9e4c4054f82e223ada074c552335b9b4b2d14bd9b35a67c4/joblib-1.5.2.tar.gz", hash = "sha256:3faa5c39054b2f03ca547da9b2f52fde67c06240c31853f306aea97f13647b55", size = 331077, upload-time = "2025-08-27T12:15:46.575Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/1e/e8/685f47e0d754320684db4425a0967f7d3fa70126bffd76110b7009a0090f/joblib-1.5.2-py3-none-any.whl", hash = "sha256:4e1f0bdbb987e6d843c70cf43714cb276623def372df3c22fe5266b2670bc241", size = 308396, upload-time = "2025-08-27T12:15:45.188Z" },
-]
-
-[[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, upload-time = "2023-06-26T12:07:29.144Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/73/07/02e16ed01e04a374e644b575638ec7987ae846d25ad97bcc9945a3ee4b0e/jsonpatch-1.33-py2.py3-none-any.whl", hash = "sha256:0ae28c0cd062bbd8b8ecc26d7d164fbbea9652a1a3693f3b956c1eae5145dade", size = 12898, upload-time = "2023-06-16T21:01:28.466Z" },
-]
-
-[[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, upload-time = "2024-06-10T19:24:42.462Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/71/92/5e77f98553e9e75130c78900d000368476aed74276eb8ae8796f65f00918/jsonpointer-3.0.0-py2.py3-none-any.whl", hash = "sha256:13e088adc14fca8b6aa8177c044e12701e6ad4b28ff10e65f2267a90109c9942", size = 7595, upload-time = "2024-06-10T19:24:40.698Z" },
-]
-
-[[package]]
-name = "jsonref"
-version = "1.1.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/aa/0d/c1f3277e90ccdb50d33ed5ba1ec5b3f0a242ed8c1b1a85d3afeb68464dca/jsonref-1.1.0.tar.gz", hash = "sha256:32fe8e1d85af0fdefbebce950af85590b22b60f9e95443176adbde4e1ecea552", size = 8814, upload-time = "2023-01-16T16:10:04.455Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/0c/ec/e1db9922bceb168197a558a2b8c03a7963f1afe93517ddd3cf99f202f996/jsonref-1.1.0-py3-none-any.whl", hash = "sha256:590dc7773df6c21cbf948b5dac07a72a251db28b0238ceecce0a2abfa8ec30a9", size = 9425, upload-time = "2023-01-16T16:10:02.255Z" },
-]
-
-[[package]]
-name = "jsonschema"
-version = "4.25.1"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "attrs" },
- { name = "jsonschema-specifications" },
- { name = "referencing" },
- { name = "rpds-py" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/74/69/f7185de793a29082a9f3c7728268ffb31cb5095131a9c139a74078e27336/jsonschema-4.25.1.tar.gz", hash = "sha256:e4a9655ce0da0c0b67a085847e00a3a51449e1157f4f75e9fb5aa545e122eb85", size = 357342, upload-time = "2025-08-18T17:03:50.038Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/bf/9c/8c95d856233c1f82500c2450b8c68576b4cf1c871db3afac5c34ff84e6fd/jsonschema-4.25.1-py3-none-any.whl", hash = "sha256:3fba0169e345c7175110351d456342c364814cfcf3b964ba4587f22915230a63", size = 90040, upload-time = "2025-08-18T17:03:48.373Z" },
-]
-
-[[package]]
-name = "jsonschema-specifications"
-version = "2025.4.1"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "referencing" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/bf/ce/46fbd9c8119cfc3581ee5643ea49464d168028cfb5caff5fc0596d0cf914/jsonschema_specifications-2025.4.1.tar.gz", hash = "sha256:630159c9f4dbea161a6a2205c3011cc4f18ff381b189fff48bb39b9bf26ae608", size = 15513, upload-time = "2025-04-23T12:34:07.418Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/01/0e/b27cdbaccf30b890c40ed1da9fd4a3593a5cf94dae54fb34f8a4b74fcd3f/jsonschema_specifications-2025.4.1-py3-none-any.whl", hash = "sha256:4653bffbd6584f7de83a67e0d620ef16900b390ddc7939d56684d6c81e33f1af", size = 18437, upload-time = "2025-04-23T12:34:05.422Z" },
-]
-
-[[package]]
-name = "kaleido"
-version = "1.0.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "choreographer" },
- { name = "logistro" },
- { name = "orjson" },
- { name = "packaging" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/15/dc/fd2d955884f45f852152d44f5ecf79de3cb58da0f995b6f6f9acfc80dd94/kaleido-1.0.0.tar.gz", hash = "sha256:502d8ba64737924efaf5e94c2736745bcc7c926e6cc535838be36c0fc06330bd", size = 49400, upload-time = "2025-06-19T15:50:39.357Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/28/0e/853df00cac4823eaa8f35114d0d0ad1e2ec6b8243ce4acf5f8ac5235c517/kaleido-1.0.0-py3-none-any.whl", hash = "sha256:a7e8bd95648378d2746f6c86084d419d15f95b1ec7bb0ec810289b7feb25b18d", size = 51479, upload-time = "2025-06-19T15:50:37.884Z" },
-]
-
-[[package]]
-name = "kiwisolver"
-version = "1.4.7"
-source = { registry = "https://pypi.org/simple" }
-resolution-markers = [
- "python_full_version < '3.10'",
-]
-sdist = { url = "https://files.pythonhosted.org/packages/85/4d/2255e1c76304cbd60b48cee302b66d1dde4468dc5b1160e4b7cb43778f2a/kiwisolver-1.4.7.tar.gz", hash = "sha256:9893ff81bd7107f7b685d3017cc6583daadb4fc26e4a888350df530e41980a60", size = 97286, upload-time = "2024-09-04T09:39:44.302Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/97/14/fc943dd65268a96347472b4fbe5dcc2f6f55034516f80576cd0dd3a8930f/kiwisolver-1.4.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8a9c83f75223d5e48b0bc9cb1bf2776cf01563e00ade8775ffe13b0b6e1af3a6", size = 122440, upload-time = "2024-09-04T09:03:44.9Z" },
- { url = "https://files.pythonhosted.org/packages/1e/46/e68fed66236b69dd02fcdb506218c05ac0e39745d696d22709498896875d/kiwisolver-1.4.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:58370b1ffbd35407444d57057b57da5d6549d2d854fa30249771775c63b5fe17", size = 65758, upload-time = "2024-09-04T09:03:46.582Z" },
- { url = "https://files.pythonhosted.org/packages/ef/fa/65de49c85838681fc9cb05de2a68067a683717321e01ddafb5b8024286f0/kiwisolver-1.4.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:aa0abdf853e09aff551db11fce173e2177d00786c688203f52c87ad7fcd91ef9", size = 64311, upload-time = "2024-09-04T09:03:47.973Z" },
- { url = "https://files.pythonhosted.org/packages/42/9c/cc8d90f6ef550f65443bad5872ffa68f3dee36de4974768628bea7c14979/kiwisolver-1.4.7-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:8d53103597a252fb3ab8b5845af04c7a26d5e7ea8122303dd7a021176a87e8b9", size = 1637109, upload-time = "2024-09-04T09:03:49.281Z" },
- { url = "https://files.pythonhosted.org/packages/55/91/0a57ce324caf2ff5403edab71c508dd8f648094b18cfbb4c8cc0fde4a6ac/kiwisolver-1.4.7-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:88f17c5ffa8e9462fb79f62746428dd57b46eb931698e42e990ad63103f35e6c", size = 1617814, upload-time = "2024-09-04T09:03:51.444Z" },
- { url = "https://files.pythonhosted.org/packages/12/5d/c36140313f2510e20207708adf36ae4919416d697ee0236b0ddfb6fd1050/kiwisolver-1.4.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88a9ca9c710d598fd75ee5de59d5bda2684d9db36a9f50b6125eaea3969c2599", size = 1400881, upload-time = "2024-09-04T09:03:53.357Z" },
- { url = "https://files.pythonhosted.org/packages/56/d0/786e524f9ed648324a466ca8df86298780ef2b29c25313d9a4f16992d3cf/kiwisolver-1.4.7-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f4d742cb7af1c28303a51b7a27aaee540e71bb8e24f68c736f6f2ffc82f2bf05", size = 1512972, upload-time = "2024-09-04T09:03:55.082Z" },
- { url = "https://files.pythonhosted.org/packages/67/5a/77851f2f201e6141d63c10a0708e996a1363efaf9e1609ad0441b343763b/kiwisolver-1.4.7-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e28c7fea2196bf4c2f8d46a0415c77a1c480cc0724722f23d7410ffe9842c407", size = 1444787, upload-time = "2024-09-04T09:03:56.588Z" },
- { url = "https://files.pythonhosted.org/packages/06/5f/1f5eaab84355885e224a6fc8d73089e8713dc7e91c121f00b9a1c58a2195/kiwisolver-1.4.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e968b84db54f9d42046cf154e02911e39c0435c9801681e3fc9ce8a3c4130278", size = 2199212, upload-time = "2024-09-04T09:03:58.557Z" },
- { url = "https://files.pythonhosted.org/packages/b5/28/9152a3bfe976a0ae21d445415defc9d1cd8614b2910b7614b30b27a47270/kiwisolver-1.4.7-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0c18ec74c0472de033e1bebb2911c3c310eef5649133dd0bedf2a169a1b269e5", size = 2346399, upload-time = "2024-09-04T09:04:00.178Z" },
- { url = "https://files.pythonhosted.org/packages/26/f6/453d1904c52ac3b400f4d5e240ac5fec25263716723e44be65f4d7149d13/kiwisolver-1.4.7-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8f0ea6da6d393d8b2e187e6a5e3fb81f5862010a40c3945e2c6d12ae45cfb2ad", size = 2308688, upload-time = "2024-09-04T09:04:02.216Z" },
- { url = "https://files.pythonhosted.org/packages/5a/9a/d4968499441b9ae187e81745e3277a8b4d7c60840a52dc9d535a7909fac3/kiwisolver-1.4.7-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:f106407dda69ae456dd1227966bf445b157ccc80ba0dff3802bb63f30b74e895", size = 2445493, upload-time = "2024-09-04T09:04:04.571Z" },
- { url = "https://files.pythonhosted.org/packages/07/c9/032267192e7828520dacb64dfdb1d74f292765f179e467c1cba97687f17d/kiwisolver-1.4.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:84ec80df401cfee1457063732d90022f93951944b5b58975d34ab56bb150dfb3", size = 2262191, upload-time = "2024-09-04T09:04:05.969Z" },
- { url = "https://files.pythonhosted.org/packages/6c/ad/db0aedb638a58b2951da46ddaeecf204be8b4f5454df020d850c7fa8dca8/kiwisolver-1.4.7-cp310-cp310-win32.whl", hash = "sha256:71bb308552200fb2c195e35ef05de12f0c878c07fc91c270eb3d6e41698c3bcc", size = 46644, upload-time = "2024-09-04T09:04:07.408Z" },
- { url = "https://files.pythonhosted.org/packages/12/ca/d0f7b7ffbb0be1e7c2258b53554efec1fd652921f10d7d85045aff93ab61/kiwisolver-1.4.7-cp310-cp310-win_amd64.whl", hash = "sha256:44756f9fd339de0fb6ee4f8c1696cfd19b2422e0d70b4cefc1cc7f1f64045a8c", size = 55877, upload-time = "2024-09-04T09:04:08.869Z" },
- { url = "https://files.pythonhosted.org/packages/97/6c/cfcc128672f47a3e3c0d918ecb67830600078b025bfc32d858f2e2d5c6a4/kiwisolver-1.4.7-cp310-cp310-win_arm64.whl", hash = "sha256:78a42513018c41c2ffd262eb676442315cbfe3c44eed82385c2ed043bc63210a", size = 48347, upload-time = "2024-09-04T09:04:10.106Z" },
- { url = "https://files.pythonhosted.org/packages/e9/44/77429fa0a58f941d6e1c58da9efe08597d2e86bf2b2cce6626834f49d07b/kiwisolver-1.4.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d2b0e12a42fb4e72d509fc994713d099cbb15ebf1103545e8a45f14da2dfca54", size = 122442, upload-time = "2024-09-04T09:04:11.432Z" },
- { url = "https://files.pythonhosted.org/packages/e5/20/8c75caed8f2462d63c7fd65e16c832b8f76cda331ac9e615e914ee80bac9/kiwisolver-1.4.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2a8781ac3edc42ea4b90bc23e7d37b665d89423818e26eb6df90698aa2287c95", size = 65762, upload-time = "2024-09-04T09:04:12.468Z" },
- { url = "https://files.pythonhosted.org/packages/f4/98/fe010f15dc7230f45bc4cf367b012d651367fd203caaa992fd1f5963560e/kiwisolver-1.4.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:46707a10836894b559e04b0fd143e343945c97fd170d69a2d26d640b4e297935", size = 64319, upload-time = "2024-09-04T09:04:13.635Z" },
- { url = "https://files.pythonhosted.org/packages/8b/1b/b5d618f4e58c0675654c1e5051bcf42c776703edb21c02b8c74135541f60/kiwisolver-1.4.7-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ef97b8df011141c9b0f6caf23b29379f87dd13183c978a30a3c546d2c47314cb", size = 1334260, upload-time = "2024-09-04T09:04:14.878Z" },
- { url = "https://files.pythonhosted.org/packages/b8/01/946852b13057a162a8c32c4c8d2e9ed79f0bb5d86569a40c0b5fb103e373/kiwisolver-1.4.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ab58c12a2cd0fc769089e6d38466c46d7f76aced0a1f54c77652446733d2d02", size = 1426589, upload-time = "2024-09-04T09:04:16.514Z" },
- { url = "https://files.pythonhosted.org/packages/70/d1/c9f96df26b459e15cf8a965304e6e6f4eb291e0f7a9460b4ad97b047561e/kiwisolver-1.4.7-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:803b8e1459341c1bb56d1c5c010406d5edec8a0713a0945851290a7930679b51", size = 1541080, upload-time = "2024-09-04T09:04:18.322Z" },
- { url = "https://files.pythonhosted.org/packages/d3/73/2686990eb8b02d05f3de759d6a23a4ee7d491e659007dd4c075fede4b5d0/kiwisolver-1.4.7-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f9a9e8a507420fe35992ee9ecb302dab68550dedc0da9e2880dd88071c5fb052", size = 1470049, upload-time = "2024-09-04T09:04:20.266Z" },
- { url = "https://files.pythonhosted.org/packages/a7/4b/2db7af3ed3af7c35f388d5f53c28e155cd402a55432d800c543dc6deb731/kiwisolver-1.4.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18077b53dc3bb490e330669a99920c5e6a496889ae8c63b58fbc57c3d7f33a18", size = 1426376, upload-time = "2024-09-04T09:04:22.419Z" },
- { url = "https://files.pythonhosted.org/packages/05/83/2857317d04ea46dc5d115f0df7e676997bbd968ced8e2bd6f7f19cfc8d7f/kiwisolver-1.4.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6af936f79086a89b3680a280c47ea90b4df7047b5bdf3aa5c524bbedddb9e545", size = 2222231, upload-time = "2024-09-04T09:04:24.526Z" },
- { url = "https://files.pythonhosted.org/packages/0d/b5/866f86f5897cd4ab6d25d22e403404766a123f138bd6a02ecb2cdde52c18/kiwisolver-1.4.7-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:3abc5b19d24af4b77d1598a585b8a719beb8569a71568b66f4ebe1fb0449460b", size = 2368634, upload-time = "2024-09-04T09:04:25.899Z" },
- { url = "https://files.pythonhosted.org/packages/c1/ee/73de8385403faba55f782a41260210528fe3273d0cddcf6d51648202d6d0/kiwisolver-1.4.7-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:933d4de052939d90afbe6e9d5273ae05fb836cc86c15b686edd4b3560cc0ee36", size = 2329024, upload-time = "2024-09-04T09:04:28.523Z" },
- { url = "https://files.pythonhosted.org/packages/a1/e7/cd101d8cd2cdfaa42dc06c433df17c8303d31129c9fdd16c0ea37672af91/kiwisolver-1.4.7-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:65e720d2ab2b53f1f72fb5da5fb477455905ce2c88aaa671ff0a447c2c80e8e3", size = 2468484, upload-time = "2024-09-04T09:04:30.547Z" },
- { url = "https://files.pythonhosted.org/packages/e1/72/84f09d45a10bc57a40bb58b81b99d8f22b58b2040c912b7eb97ebf625bf2/kiwisolver-1.4.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3bf1ed55088f214ba6427484c59553123fdd9b218a42bbc8c6496d6754b1e523", size = 2284078, upload-time = "2024-09-04T09:04:33.218Z" },
- { url = "https://files.pythonhosted.org/packages/d2/d4/71828f32b956612dc36efd7be1788980cb1e66bfb3706e6dec9acad9b4f9/kiwisolver-1.4.7-cp311-cp311-win32.whl", hash = "sha256:4c00336b9dd5ad96d0a558fd18a8b6f711b7449acce4c157e7343ba92dd0cf3d", size = 46645, upload-time = "2024-09-04T09:04:34.371Z" },
- { url = "https://files.pythonhosted.org/packages/a1/65/d43e9a20aabcf2e798ad1aff6c143ae3a42cf506754bcb6a7ed8259c8425/kiwisolver-1.4.7-cp311-cp311-win_amd64.whl", hash = "sha256:929e294c1ac1e9f615c62a4e4313ca1823ba37326c164ec720a803287c4c499b", size = 56022, upload-time = "2024-09-04T09:04:35.786Z" },
- { url = "https://files.pythonhosted.org/packages/35/b3/9f75a2e06f1b4ca00b2b192bc2b739334127d27f1d0625627ff8479302ba/kiwisolver-1.4.7-cp311-cp311-win_arm64.whl", hash = "sha256:e33e8fbd440c917106b237ef1a2f1449dfbb9b6f6e1ce17c94cd6a1e0d438376", size = 48536, upload-time = "2024-09-04T09:04:37.525Z" },
- { url = "https://files.pythonhosted.org/packages/97/9c/0a11c714cf8b6ef91001c8212c4ef207f772dd84540104952c45c1f0a249/kiwisolver-1.4.7-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:5360cc32706dab3931f738d3079652d20982511f7c0ac5711483e6eab08efff2", size = 121808, upload-time = "2024-09-04T09:04:38.637Z" },
- { url = "https://files.pythonhosted.org/packages/f2/d8/0fe8c5f5d35878ddd135f44f2af0e4e1d379e1c7b0716f97cdcb88d4fd27/kiwisolver-1.4.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:942216596dc64ddb25adb215c3c783215b23626f8d84e8eff8d6d45c3f29f75a", size = 65531, upload-time = "2024-09-04T09:04:39.694Z" },
- { url = "https://files.pythonhosted.org/packages/80/c5/57fa58276dfdfa612241d640a64ca2f76adc6ffcebdbd135b4ef60095098/kiwisolver-1.4.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:48b571ecd8bae15702e4f22d3ff6a0f13e54d3d00cd25216d5e7f658242065ee", size = 63894, upload-time = "2024-09-04T09:04:41.6Z" },
- { url = "https://files.pythonhosted.org/packages/8b/e9/26d3edd4c4ad1c5b891d8747a4f81b1b0aba9fb9721de6600a4adc09773b/kiwisolver-1.4.7-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ad42ba922c67c5f219097b28fae965e10045ddf145d2928bfac2eb2e17673640", size = 1369296, upload-time = "2024-09-04T09:04:42.886Z" },
- { url = "https://files.pythonhosted.org/packages/b6/67/3f4850b5e6cffb75ec40577ddf54f7b82b15269cc5097ff2e968ee32ea7d/kiwisolver-1.4.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:612a10bdae23404a72941a0fc8fa2660c6ea1217c4ce0dbcab8a8f6543ea9e7f", size = 1461450, upload-time = "2024-09-04T09:04:46.284Z" },
- { url = "https://files.pythonhosted.org/packages/52/be/86cbb9c9a315e98a8dc6b1d23c43cffd91d97d49318854f9c37b0e41cd68/kiwisolver-1.4.7-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9e838bba3a3bac0fe06d849d29772eb1afb9745a59710762e4ba3f4cb8424483", size = 1579168, upload-time = "2024-09-04T09:04:47.91Z" },
- { url = "https://files.pythonhosted.org/packages/0f/00/65061acf64bd5fd34c1f4ae53f20b43b0a017a541f242a60b135b9d1e301/kiwisolver-1.4.7-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:22f499f6157236c19f4bbbd472fa55b063db77a16cd74d49afe28992dff8c258", size = 1507308, upload-time = "2024-09-04T09:04:49.465Z" },
- { url = "https://files.pythonhosted.org/packages/21/e4/c0b6746fd2eb62fe702118b3ca0cb384ce95e1261cfada58ff693aeec08a/kiwisolver-1.4.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:693902d433cf585133699972b6d7c42a8b9f8f826ebcaf0132ff55200afc599e", size = 1464186, upload-time = "2024-09-04T09:04:50.949Z" },
- { url = "https://files.pythonhosted.org/packages/0a/0f/529d0a9fffb4d514f2782c829b0b4b371f7f441d61aa55f1de1c614c4ef3/kiwisolver-1.4.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4e77f2126c3e0b0d055f44513ed349038ac180371ed9b52fe96a32aa071a5107", size = 2247877, upload-time = "2024-09-04T09:04:52.388Z" },
- { url = "https://files.pythonhosted.org/packages/d1/e1/66603ad779258843036d45adcbe1af0d1a889a07af4635f8b4ec7dccda35/kiwisolver-1.4.7-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:657a05857bda581c3656bfc3b20e353c232e9193eb167766ad2dc58b56504948", size = 2404204, upload-time = "2024-09-04T09:04:54.385Z" },
- { url = "https://files.pythonhosted.org/packages/8d/61/de5fb1ca7ad1f9ab7970e340a5b833d735df24689047de6ae71ab9d8d0e7/kiwisolver-1.4.7-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4bfa75a048c056a411f9705856abfc872558e33c055d80af6a380e3658766038", size = 2352461, upload-time = "2024-09-04T09:04:56.307Z" },
- { url = "https://files.pythonhosted.org/packages/ba/d2/0edc00a852e369827f7e05fd008275f550353f1f9bcd55db9363d779fc63/kiwisolver-1.4.7-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:34ea1de54beef1c104422d210c47c7d2a4999bdecf42c7b5718fbe59a4cac383", size = 2501358, upload-time = "2024-09-04T09:04:57.922Z" },
- { url = "https://files.pythonhosted.org/packages/84/15/adc15a483506aec6986c01fb7f237c3aec4d9ed4ac10b756e98a76835933/kiwisolver-1.4.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:90da3b5f694b85231cf93586dad5e90e2d71b9428f9aad96952c99055582f520", size = 2314119, upload-time = "2024-09-04T09:04:59.332Z" },
- { url = "https://files.pythonhosted.org/packages/36/08/3a5bb2c53c89660863a5aa1ee236912269f2af8762af04a2e11df851d7b2/kiwisolver-1.4.7-cp312-cp312-win32.whl", hash = "sha256:18e0cca3e008e17fe9b164b55735a325140a5a35faad8de92dd80265cd5eb80b", size = 46367, upload-time = "2024-09-04T09:05:00.804Z" },
- { url = "https://files.pythonhosted.org/packages/19/93/c05f0a6d825c643779fc3c70876bff1ac221f0e31e6f701f0e9578690d70/kiwisolver-1.4.7-cp312-cp312-win_amd64.whl", hash = "sha256:58cb20602b18f86f83a5c87d3ee1c766a79c0d452f8def86d925e6c60fbf7bfb", size = 55884, upload-time = "2024-09-04T09:05:01.924Z" },
- { url = "https://files.pythonhosted.org/packages/d2/f9/3828d8f21b6de4279f0667fb50a9f5215e6fe57d5ec0d61905914f5b6099/kiwisolver-1.4.7-cp312-cp312-win_arm64.whl", hash = "sha256:f5a8b53bdc0b3961f8b6125e198617c40aeed638b387913bf1ce78afb1b0be2a", size = 48528, upload-time = "2024-09-04T09:05:02.983Z" },
- { url = "https://files.pythonhosted.org/packages/c4/06/7da99b04259b0f18b557a4effd1b9c901a747f7fdd84cf834ccf520cb0b2/kiwisolver-1.4.7-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:2e6039dcbe79a8e0f044f1c39db1986a1b8071051efba3ee4d74f5b365f5226e", size = 121913, upload-time = "2024-09-04T09:05:04.072Z" },
- { url = "https://files.pythonhosted.org/packages/97/f5/b8a370d1aa593c17882af0a6f6755aaecd643640c0ed72dcfd2eafc388b9/kiwisolver-1.4.7-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a1ecf0ac1c518487d9d23b1cd7139a6a65bc460cd101ab01f1be82ecf09794b6", size = 65627, upload-time = "2024-09-04T09:05:05.119Z" },
- { url = "https://files.pythonhosted.org/packages/2a/fc/6c0374f7503522539e2d4d1b497f5ebad3f8ed07ab51aed2af988dd0fb65/kiwisolver-1.4.7-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7ab9ccab2b5bd5702ab0803676a580fffa2aa178c2badc5557a84cc943fcf750", size = 63888, upload-time = "2024-09-04T09:05:06.191Z" },
- { url = "https://files.pythonhosted.org/packages/bf/3e/0b7172793d0f41cae5c923492da89a2ffcd1adf764c16159ca047463ebd3/kiwisolver-1.4.7-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f816dd2277f8d63d79f9c8473a79fe54047bc0467754962840782c575522224d", size = 1369145, upload-time = "2024-09-04T09:05:07.919Z" },
- { url = "https://files.pythonhosted.org/packages/77/92/47d050d6f6aced2d634258123f2688fbfef8ded3c5baf2c79d94d91f1f58/kiwisolver-1.4.7-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf8bcc23ceb5a1b624572a1623b9f79d2c3b337c8c455405ef231933a10da379", size = 1461448, upload-time = "2024-09-04T09:05:10.01Z" },
- { url = "https://files.pythonhosted.org/packages/9c/1b/8f80b18e20b3b294546a1adb41701e79ae21915f4175f311a90d042301cf/kiwisolver-1.4.7-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dea0bf229319828467d7fca8c7c189780aa9ff679c94539eed7532ebe33ed37c", size = 1578750, upload-time = "2024-09-04T09:05:11.598Z" },
- { url = "https://files.pythonhosted.org/packages/a4/fe/fe8e72f3be0a844f257cadd72689c0848c6d5c51bc1d60429e2d14ad776e/kiwisolver-1.4.7-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c06a4c7cf15ec739ce0e5971b26c93638730090add60e183530d70848ebdd34", size = 1507175, upload-time = "2024-09-04T09:05:13.22Z" },
- { url = "https://files.pythonhosted.org/packages/39/fa/cdc0b6105d90eadc3bee525fecc9179e2b41e1ce0293caaf49cb631a6aaf/kiwisolver-1.4.7-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:913983ad2deb14e66d83c28b632fd35ba2b825031f2fa4ca29675e665dfecbe1", size = 1463963, upload-time = "2024-09-04T09:05:15.925Z" },
- { url = "https://files.pythonhosted.org/packages/6e/5c/0c03c4e542720c6177d4f408e56d1c8315899db72d46261a4e15b8b33a41/kiwisolver-1.4.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5337ec7809bcd0f424c6b705ecf97941c46279cf5ed92311782c7c9c2026f07f", size = 2248220, upload-time = "2024-09-04T09:05:17.434Z" },
- { url = "https://files.pythonhosted.org/packages/3d/ee/55ef86d5a574f4e767df7da3a3a7ff4954c996e12d4fbe9c408170cd7dcc/kiwisolver-1.4.7-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4c26ed10c4f6fa6ddb329a5120ba3b6db349ca192ae211e882970bfc9d91420b", size = 2404463, upload-time = "2024-09-04T09:05:18.997Z" },
- { url = "https://files.pythonhosted.org/packages/0f/6d/73ad36170b4bff4825dc588acf4f3e6319cb97cd1fb3eb04d9faa6b6f212/kiwisolver-1.4.7-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c619b101e6de2222c1fcb0531e1b17bbffbe54294bfba43ea0d411d428618c27", size = 2352842, upload-time = "2024-09-04T09:05:21.299Z" },
- { url = "https://files.pythonhosted.org/packages/0b/16/fa531ff9199d3b6473bb4d0f47416cdb08d556c03b8bc1cccf04e756b56d/kiwisolver-1.4.7-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:073a36c8273647592ea332e816e75ef8da5c303236ec0167196793eb1e34657a", size = 2501635, upload-time = "2024-09-04T09:05:23.588Z" },
- { url = "https://files.pythonhosted.org/packages/78/7e/aa9422e78419db0cbe75fb86d8e72b433818f2e62e2e394992d23d23a583/kiwisolver-1.4.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:3ce6b2b0231bda412463e152fc18335ba32faf4e8c23a754ad50ffa70e4091ee", size = 2314556, upload-time = "2024-09-04T09:05:25.907Z" },
- { url = "https://files.pythonhosted.org/packages/a8/b2/15f7f556df0a6e5b3772a1e076a9d9f6c538ce5f05bd590eca8106508e06/kiwisolver-1.4.7-cp313-cp313-win32.whl", hash = "sha256:f4c9aee212bc89d4e13f58be11a56cc8036cabad119259d12ace14b34476fd07", size = 46364, upload-time = "2024-09-04T09:05:27.184Z" },
- { url = "https://files.pythonhosted.org/packages/0b/db/32e897e43a330eee8e4770bfd2737a9584b23e33587a0812b8e20aac38f7/kiwisolver-1.4.7-cp313-cp313-win_amd64.whl", hash = "sha256:8a3ec5aa8e38fc4c8af308917ce12c536f1c88452ce554027e55b22cbbfbff76", size = 55887, upload-time = "2024-09-04T09:05:28.372Z" },
- { url = "https://files.pythonhosted.org/packages/c8/a4/df2bdca5270ca85fd25253049eb6708d4127be2ed0e5c2650217450b59e9/kiwisolver-1.4.7-cp313-cp313-win_arm64.whl", hash = "sha256:76c8094ac20ec259471ac53e774623eb62e6e1f56cd8690c67ce6ce4fcb05650", size = 48530, upload-time = "2024-09-04T09:05:30.225Z" },
- { url = "https://files.pythonhosted.org/packages/11/88/37ea0ea64512997b13d69772db8dcdc3bfca5442cda3a5e4bb943652ee3e/kiwisolver-1.4.7-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3f9362ecfca44c863569d3d3c033dbe8ba452ff8eed6f6b5806382741a1334bd", size = 122449, upload-time = "2024-09-04T09:05:55.311Z" },
- { url = "https://files.pythonhosted.org/packages/4e/45/5a5c46078362cb3882dcacad687c503089263c017ca1241e0483857791eb/kiwisolver-1.4.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e8df2eb9b2bac43ef8b082e06f750350fbbaf2887534a5be97f6cf07b19d9583", size = 65757, upload-time = "2024-09-04T09:05:56.906Z" },
- { url = "https://files.pythonhosted.org/packages/8a/be/a6ae58978772f685d48dd2e84460937761c53c4bbd84e42b0336473d9775/kiwisolver-1.4.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f32d6edbc638cde7652bd690c3e728b25332acbadd7cad670cc4a02558d9c417", size = 64312, upload-time = "2024-09-04T09:05:58.384Z" },
- { url = "https://files.pythonhosted.org/packages/f4/04/18ef6f452d311e1e1eb180c9bf5589187fa1f042db877e6fe443ef10099c/kiwisolver-1.4.7-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:e2e6c39bd7b9372b0be21456caab138e8e69cc0fc1190a9dfa92bd45a1e6e904", size = 1626966, upload-time = "2024-09-04T09:05:59.855Z" },
- { url = "https://files.pythonhosted.org/packages/21/b1/40655f6c3fa11ce740e8a964fa8e4c0479c87d6a7944b95af799c7a55dfe/kiwisolver-1.4.7-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:dda56c24d869b1193fcc763f1284b9126550eaf84b88bbc7256e15028f19188a", size = 1607044, upload-time = "2024-09-04T09:06:02.16Z" },
- { url = "https://files.pythonhosted.org/packages/fd/93/af67dbcfb9b3323bbd2c2db1385a7139d8f77630e4a37bb945b57188eb2d/kiwisolver-1.4.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79849239c39b5e1fd906556c474d9b0439ea6792b637511f3fe3a41158d89ca8", size = 1391879, upload-time = "2024-09-04T09:06:03.908Z" },
- { url = "https://files.pythonhosted.org/packages/40/6f/d60770ef98e77b365d96061d090c0cd9e23418121c55fff188fa4bdf0b54/kiwisolver-1.4.7-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5e3bc157fed2a4c02ec468de4ecd12a6e22818d4f09cde2c31ee3226ffbefab2", size = 1504751, upload-time = "2024-09-04T09:06:05.58Z" },
- { url = "https://files.pythonhosted.org/packages/fa/3a/5f38667d313e983c432f3fcd86932177519ed8790c724e07d77d1de0188a/kiwisolver-1.4.7-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3da53da805b71e41053dc670f9a820d1157aae77b6b944e08024d17bcd51ef88", size = 1436990, upload-time = "2024-09-04T09:06:08.126Z" },
- { url = "https://files.pythonhosted.org/packages/cb/3b/1520301a47326e6a6043b502647e42892be33b3f051e9791cc8bb43f1a32/kiwisolver-1.4.7-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:8705f17dfeb43139a692298cb6637ee2e59c0194538153e83e9ee0c75c2eddde", size = 2191122, upload-time = "2024-09-04T09:06:10.345Z" },
- { url = "https://files.pythonhosted.org/packages/cf/c4/eb52da300c166239a2233f1f9c4a1b767dfab98fae27681bfb7ea4873cb6/kiwisolver-1.4.7-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:82a5c2f4b87c26bb1a0ef3d16b5c4753434633b83d365cc0ddf2770c93829e3c", size = 2338126, upload-time = "2024-09-04T09:06:12.321Z" },
- { url = "https://files.pythonhosted.org/packages/1a/cb/42b92fd5eadd708dd9107c089e817945500685f3437ce1fd387efebc6d6e/kiwisolver-1.4.7-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ce8be0466f4c0d585cdb6c1e2ed07232221df101a4c6f28821d2aa754ca2d9e2", size = 2298313, upload-time = "2024-09-04T09:06:14.562Z" },
- { url = "https://files.pythonhosted.org/packages/4f/eb/be25aa791fe5fc75a8b1e0c965e00f942496bc04635c9aae8035f6b76dcd/kiwisolver-1.4.7-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:409afdfe1e2e90e6ee7fc896f3df9a7fec8e793e58bfa0d052c8a82f99c37abb", size = 2437784, upload-time = "2024-09-04T09:06:16.767Z" },
- { url = "https://files.pythonhosted.org/packages/c5/22/30a66be7f3368d76ff95689e1c2e28d382383952964ab15330a15d8bfd03/kiwisolver-1.4.7-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:5b9c3f4ee0b9a439d2415012bd1b1cc2df59e4d6a9939f4d669241d30b414327", size = 2253988, upload-time = "2024-09-04T09:06:18.705Z" },
- { url = "https://files.pythonhosted.org/packages/35/d3/5f2ecb94b5211c8a04f218a76133cc8d6d153b0f9cd0b45fad79907f0689/kiwisolver-1.4.7-cp39-cp39-win32.whl", hash = "sha256:a79ae34384df2b615eefca647a2873842ac3b596418032bef9a7283675962644", size = 46980, upload-time = "2024-09-04T09:06:20.106Z" },
- { url = "https://files.pythonhosted.org/packages/ef/17/cd10d020578764ea91740204edc6b3236ed8106228a46f568d716b11feb2/kiwisolver-1.4.7-cp39-cp39-win_amd64.whl", hash = "sha256:cf0438b42121a66a3a667de17e779330fc0f20b0d97d59d2f2121e182b0505e4", size = 55847, upload-time = "2024-09-04T09:06:21.407Z" },
- { url = "https://files.pythonhosted.org/packages/91/84/32232502020bd78d1d12be7afde15811c64a95ed1f606c10456db4e4c3ac/kiwisolver-1.4.7-cp39-cp39-win_arm64.whl", hash = "sha256:764202cc7e70f767dab49e8df52c7455e8de0df5d858fa801a11aa0d882ccf3f", size = 48494, upload-time = "2024-09-04T09:06:22.648Z" },
- { url = "https://files.pythonhosted.org/packages/ac/59/741b79775d67ab67ced9bb38552da688c0305c16e7ee24bba7a2be253fb7/kiwisolver-1.4.7-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:94252291e3fe68001b1dd747b4c0b3be12582839b95ad4d1b641924d68fd4643", size = 59491, upload-time = "2024-09-04T09:06:24.188Z" },
- { url = "https://files.pythonhosted.org/packages/58/cc/fb239294c29a5656e99e3527f7369b174dd9cc7c3ef2dea7cb3c54a8737b/kiwisolver-1.4.7-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:5b7dfa3b546da08a9f622bb6becdb14b3e24aaa30adba66749d38f3cc7ea9706", size = 57648, upload-time = "2024-09-04T09:06:25.559Z" },
- { url = "https://files.pythonhosted.org/packages/3b/ef/2f009ac1f7aab9f81efb2d837301d255279d618d27b6015780115ac64bdd/kiwisolver-1.4.7-pp310-pypy310_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd3de6481f4ed8b734da5df134cd5a6a64fe32124fe83dde1e5b5f29fe30b1e6", size = 84257, upload-time = "2024-09-04T09:06:27.038Z" },
- { url = "https://files.pythonhosted.org/packages/81/e1/c64f50987f85b68b1c52b464bb5bf73e71570c0f7782d626d1eb283ad620/kiwisolver-1.4.7-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a91b5f9f1205845d488c928e8570dcb62b893372f63b8b6e98b863ebd2368ff2", size = 80906, upload-time = "2024-09-04T09:06:28.48Z" },
- { url = "https://files.pythonhosted.org/packages/fd/71/1687c5c0a0be2cee39a5c9c389e546f9c6e215e46b691d00d9f646892083/kiwisolver-1.4.7-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40fa14dbd66b8b8f470d5fc79c089a66185619d31645f9b0773b88b19f7223c4", size = 79951, upload-time = "2024-09-04T09:06:29.966Z" },
- { url = "https://files.pythonhosted.org/packages/ea/8b/d7497df4a1cae9367adf21665dd1f896c2a7aeb8769ad77b662c5e2bcce7/kiwisolver-1.4.7-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:eb542fe7933aa09d8d8f9d9097ef37532a7df6497819d16efe4359890a2f417a", size = 55715, upload-time = "2024-09-04T09:06:31.489Z" },
- { url = "https://files.pythonhosted.org/packages/d5/df/ce37d9b26f07ab90880923c94d12a6ff4d27447096b4c849bfc4339ccfdf/kiwisolver-1.4.7-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:8b01aac285f91ca889c800042c35ad3b239e704b150cfd3382adfc9dcc780e39", size = 58666, upload-time = "2024-09-04T09:06:43.756Z" },
- { url = "https://files.pythonhosted.org/packages/b0/d3/e4b04f43bc629ac8e186b77b2b1a251cdfa5b7610fa189dc0db622672ce6/kiwisolver-1.4.7-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:48be928f59a1f5c8207154f935334d374e79f2b5d212826307d072595ad76a2e", size = 57088, upload-time = "2024-09-04T09:06:45.406Z" },
- { url = "https://files.pythonhosted.org/packages/30/1c/752df58e2d339e670a535514d2db4fe8c842ce459776b8080fbe08ebb98e/kiwisolver-1.4.7-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f37cfe618a117e50d8c240555331160d73d0411422b59b5ee217843d7b693608", size = 84321, upload-time = "2024-09-04T09:06:47.557Z" },
- { url = "https://files.pythonhosted.org/packages/f0/f8/fe6484e847bc6e238ec9f9828089fb2c0bb53f2f5f3a79351fde5b565e4f/kiwisolver-1.4.7-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:599b5c873c63a1f6ed7eead644a8a380cfbdf5db91dcb6f85707aaab213b1674", size = 80776, upload-time = "2024-09-04T09:06:49.235Z" },
- { url = "https://files.pythonhosted.org/packages/9b/57/d7163c0379f250ef763aba85330a19feefb5ce6cb541ade853aaba881524/kiwisolver-1.4.7-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:801fa7802e5cfabe3ab0c81a34c323a319b097dfb5004be950482d882f3d7225", size = 79984, upload-time = "2024-09-04T09:06:51.336Z" },
- { url = "https://files.pythonhosted.org/packages/8c/95/4a103776c265d13b3d2cd24fb0494d4e04ea435a8ef97e1b2c026d43250b/kiwisolver-1.4.7-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:0c6c43471bc764fad4bc99c5c2d6d16a676b1abf844ca7c8702bdae92df01ee0", size = 55811, upload-time = "2024-09-04T09:06:53.078Z" },
-]
-
-[[package]]
-name = "kiwisolver"
-version = "1.4.9"
-source = { registry = "https://pypi.org/simple" }
-resolution-markers = [
- "python_full_version >= '3.13'",
- "python_full_version >= '3.12.4' and python_full_version < '3.13'",
- "python_full_version >= '3.12' and python_full_version < '3.12.4'",
- "python_full_version == '3.11.*'",
- "python_full_version == '3.10.*'",
-]
-sdist = { url = "https://files.pythonhosted.org/packages/5c/3c/85844f1b0feb11ee581ac23fe5fce65cd049a200c1446708cc1b7f922875/kiwisolver-1.4.9.tar.gz", hash = "sha256:c3b22c26c6fd6811b0ae8363b95ca8ce4ea3c202d3d0975b2914310ceb1bcc4d", size = 97564, upload-time = "2025-08-10T21:27:49.279Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/c6/5d/8ce64e36d4e3aac5ca96996457dcf33e34e6051492399a3f1fec5657f30b/kiwisolver-1.4.9-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b4b4d74bda2b8ebf4da5bd42af11d02d04428b2c32846e4c2c93219df8a7987b", size = 124159, upload-time = "2025-08-10T21:25:35.472Z" },
- { url = "https://files.pythonhosted.org/packages/96/1e/22f63ec454874378175a5f435d6ea1363dd33fb2af832c6643e4ccea0dc8/kiwisolver-1.4.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:fb3b8132019ea572f4611d770991000d7f58127560c4889729248eb5852a102f", size = 66578, upload-time = "2025-08-10T21:25:36.73Z" },
- { url = "https://files.pythonhosted.org/packages/41/4c/1925dcfff47a02d465121967b95151c82d11027d5ec5242771e580e731bd/kiwisolver-1.4.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:84fd60810829c27ae375114cd379da1fa65e6918e1da405f356a775d49a62bcf", size = 65312, upload-time = "2025-08-10T21:25:37.658Z" },
- { url = "https://files.pythonhosted.org/packages/d4/42/0f333164e6307a0687d1eb9ad256215aae2f4bd5d28f4653d6cd319a3ba3/kiwisolver-1.4.9-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b78efa4c6e804ecdf727e580dbb9cba85624d2e1c6b5cb059c66290063bd99a9", size = 1628458, upload-time = "2025-08-10T21:25:39.067Z" },
- { url = "https://files.pythonhosted.org/packages/86/b6/2dccb977d651943995a90bfe3495c2ab2ba5cd77093d9f2318a20c9a6f59/kiwisolver-1.4.9-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d4efec7bcf21671db6a3294ff301d2fc861c31faa3c8740d1a94689234d1b415", size = 1225640, upload-time = "2025-08-10T21:25:40.489Z" },
- { url = "https://files.pythonhosted.org/packages/50/2b/362ebd3eec46c850ccf2bfe3e30f2fc4c008750011f38a850f088c56a1c6/kiwisolver-1.4.9-cp310-cp310-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:90f47e70293fc3688b71271100a1a5453aa9944a81d27ff779c108372cf5567b", size = 1244074, upload-time = "2025-08-10T21:25:42.221Z" },
- { url = "https://files.pythonhosted.org/packages/6f/bb/f09a1e66dab8984773d13184a10a29fe67125337649d26bdef547024ed6b/kiwisolver-1.4.9-cp310-cp310-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8fdca1def57a2e88ef339de1737a1449d6dbf5fab184c54a1fca01d541317154", size = 1293036, upload-time = "2025-08-10T21:25:43.801Z" },
- { url = "https://files.pythonhosted.org/packages/ea/01/11ecf892f201cafda0f68fa59212edaea93e96c37884b747c181303fccd1/kiwisolver-1.4.9-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:9cf554f21be770f5111a1690d42313e140355e687e05cf82cb23d0a721a64a48", size = 2175310, upload-time = "2025-08-10T21:25:45.045Z" },
- { url = "https://files.pythonhosted.org/packages/7f/5f/bfe11d5b934f500cc004314819ea92427e6e5462706a498c1d4fc052e08f/kiwisolver-1.4.9-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fc1795ac5cd0510207482c3d1d3ed781143383b8cfd36f5c645f3897ce066220", size = 2270943, upload-time = "2025-08-10T21:25:46.393Z" },
- { url = "https://files.pythonhosted.org/packages/3d/de/259f786bf71f1e03e73d87e2db1a9a3bcab64d7b4fd780167123161630ad/kiwisolver-1.4.9-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:ccd09f20ccdbbd341b21a67ab50a119b64a403b09288c27481575105283c1586", size = 2440488, upload-time = "2025-08-10T21:25:48.074Z" },
- { url = "https://files.pythonhosted.org/packages/1b/76/c989c278faf037c4d3421ec07a5c452cd3e09545d6dae7f87c15f54e4edf/kiwisolver-1.4.9-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:540c7c72324d864406a009d72f5d6856f49693db95d1fbb46cf86febef873634", size = 2246787, upload-time = "2025-08-10T21:25:49.442Z" },
- { url = "https://files.pythonhosted.org/packages/a2/55/c2898d84ca440852e560ca9f2a0d28e6e931ac0849b896d77231929900e7/kiwisolver-1.4.9-cp310-cp310-win_amd64.whl", hash = "sha256:ede8c6d533bc6601a47ad4046080d36b8fc99f81e6f1c17b0ac3c2dc91ac7611", size = 73730, upload-time = "2025-08-10T21:25:51.102Z" },
- { url = "https://files.pythonhosted.org/packages/e8/09/486d6ac523dd33b80b368247f238125d027964cfacb45c654841e88fb2ae/kiwisolver-1.4.9-cp310-cp310-win_arm64.whl", hash = "sha256:7b4da0d01ac866a57dd61ac258c5607b4cd677f63abaec7b148354d2b2cdd536", size = 65036, upload-time = "2025-08-10T21:25:52.063Z" },
- { url = "https://files.pythonhosted.org/packages/6f/ab/c80b0d5a9d8a1a65f4f815f2afff9798b12c3b9f31f1d304dd233dd920e2/kiwisolver-1.4.9-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:eb14a5da6dc7642b0f3a18f13654847cd8b7a2550e2645a5bda677862b03ba16", size = 124167, upload-time = "2025-08-10T21:25:53.403Z" },
- { url = "https://files.pythonhosted.org/packages/a0/c0/27fe1a68a39cf62472a300e2879ffc13c0538546c359b86f149cc19f6ac3/kiwisolver-1.4.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:39a219e1c81ae3b103643d2aedb90f1ef22650deb266ff12a19e7773f3e5f089", size = 66579, upload-time = "2025-08-10T21:25:54.79Z" },
- { url = "https://files.pythonhosted.org/packages/31/a2/a12a503ac1fd4943c50f9822678e8015a790a13b5490354c68afb8489814/kiwisolver-1.4.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2405a7d98604b87f3fc28b1716783534b1b4b8510d8142adca34ee0bc3c87543", size = 65309, upload-time = "2025-08-10T21:25:55.76Z" },
- { url = "https://files.pythonhosted.org/packages/66/e1/e533435c0be77c3f64040d68d7a657771194a63c279f55573188161e81ca/kiwisolver-1.4.9-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:dc1ae486f9abcef254b5618dfb4113dd49f94c68e3e027d03cf0143f3f772b61", size = 1435596, upload-time = "2025-08-10T21:25:56.861Z" },
- { url = "https://files.pythonhosted.org/packages/67/1e/51b73c7347f9aabdc7215aa79e8b15299097dc2f8e67dee2b095faca9cb0/kiwisolver-1.4.9-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8a1f570ce4d62d718dce3f179ee78dac3b545ac16c0c04bb363b7607a949c0d1", size = 1246548, upload-time = "2025-08-10T21:25:58.246Z" },
- { url = "https://files.pythonhosted.org/packages/21/aa/72a1c5d1e430294f2d32adb9542719cfb441b5da368d09d268c7757af46c/kiwisolver-1.4.9-cp311-cp311-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cb27e7b78d716c591e88e0a09a2139c6577865d7f2e152488c2cc6257f460872", size = 1263618, upload-time = "2025-08-10T21:25:59.857Z" },
- { url = "https://files.pythonhosted.org/packages/a3/af/db1509a9e79dbf4c260ce0cfa3903ea8945f6240e9e59d1e4deb731b1a40/kiwisolver-1.4.9-cp311-cp311-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:15163165efc2f627eb9687ea5f3a28137217d217ac4024893d753f46bce9de26", size = 1317437, upload-time = "2025-08-10T21:26:01.105Z" },
- { url = "https://files.pythonhosted.org/packages/e0/f2/3ea5ee5d52abacdd12013a94130436e19969fa183faa1e7c7fbc89e9a42f/kiwisolver-1.4.9-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bdee92c56a71d2b24c33a7d4c2856bd6419d017e08caa7802d2963870e315028", size = 2195742, upload-time = "2025-08-10T21:26:02.675Z" },
- { url = "https://files.pythonhosted.org/packages/6f/9b/1efdd3013c2d9a2566aa6a337e9923a00590c516add9a1e89a768a3eb2fc/kiwisolver-1.4.9-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:412f287c55a6f54b0650bd9b6dce5aceddb95864a1a90c87af16979d37c89771", size = 2290810, upload-time = "2025-08-10T21:26:04.009Z" },
- { url = "https://files.pythonhosted.org/packages/fb/e5/cfdc36109ae4e67361f9bc5b41323648cb24a01b9ade18784657e022e65f/kiwisolver-1.4.9-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:2c93f00dcba2eea70af2be5f11a830a742fe6b579a1d4e00f47760ef13be247a", size = 2461579, upload-time = "2025-08-10T21:26:05.317Z" },
- { url = "https://files.pythonhosted.org/packages/62/86/b589e5e86c7610842213994cdea5add00960076bef4ae290c5fa68589cac/kiwisolver-1.4.9-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f117e1a089d9411663a3207ba874f31be9ac8eaa5b533787024dc07aeb74f464", size = 2268071, upload-time = "2025-08-10T21:26:06.686Z" },
- { url = "https://files.pythonhosted.org/packages/3b/c6/f8df8509fd1eee6c622febe54384a96cfaf4d43bf2ccec7a0cc17e4715c9/kiwisolver-1.4.9-cp311-cp311-win_amd64.whl", hash = "sha256:be6a04e6c79819c9a8c2373317d19a96048e5a3f90bec587787e86a1153883c2", size = 73840, upload-time = "2025-08-10T21:26:07.94Z" },
- { url = "https://files.pythonhosted.org/packages/e2/2d/16e0581daafd147bc11ac53f032a2b45eabac897f42a338d0a13c1e5c436/kiwisolver-1.4.9-cp311-cp311-win_arm64.whl", hash = "sha256:0ae37737256ba2de764ddc12aed4956460277f00c4996d51a197e72f62f5eec7", size = 65159, upload-time = "2025-08-10T21:26:09.048Z" },
- { url = "https://files.pythonhosted.org/packages/86/c9/13573a747838aeb1c76e3267620daa054f4152444d1f3d1a2324b78255b5/kiwisolver-1.4.9-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ac5a486ac389dddcc5bef4f365b6ae3ffff2c433324fb38dd35e3fab7c957999", size = 123686, upload-time = "2025-08-10T21:26:10.034Z" },
- { url = "https://files.pythonhosted.org/packages/51/ea/2ecf727927f103ffd1739271ca19c424d0e65ea473fbaeea1c014aea93f6/kiwisolver-1.4.9-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f2ba92255faa7309d06fe44c3a4a97efe1c8d640c2a79a5ef728b685762a6fd2", size = 66460, upload-time = "2025-08-10T21:26:11.083Z" },
- { url = "https://files.pythonhosted.org/packages/5b/5a/51f5464373ce2aeb5194508298a508b6f21d3867f499556263c64c621914/kiwisolver-1.4.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4a2899935e724dd1074cb568ce7ac0dce28b2cd6ab539c8e001a8578eb106d14", size = 64952, upload-time = "2025-08-10T21:26:12.058Z" },
- { url = "https://files.pythonhosted.org/packages/70/90/6d240beb0f24b74371762873e9b7f499f1e02166a2d9c5801f4dbf8fa12e/kiwisolver-1.4.9-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f6008a4919fdbc0b0097089f67a1eb55d950ed7e90ce2cc3e640abadd2757a04", size = 1474756, upload-time = "2025-08-10T21:26:13.096Z" },
- { url = "https://files.pythonhosted.org/packages/12/42/f36816eaf465220f683fb711efdd1bbf7a7005a2473d0e4ed421389bd26c/kiwisolver-1.4.9-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:67bb8b474b4181770f926f7b7d2f8c0248cbcb78b660fdd41a47054b28d2a752", size = 1276404, upload-time = "2025-08-10T21:26:14.457Z" },
- { url = "https://files.pythonhosted.org/packages/2e/64/bc2de94800adc830c476dce44e9b40fd0809cddeef1fde9fcf0f73da301f/kiwisolver-1.4.9-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2327a4a30d3ee07d2fbe2e7933e8a37c591663b96ce42a00bc67461a87d7df77", size = 1294410, upload-time = "2025-08-10T21:26:15.73Z" },
- { url = "https://files.pythonhosted.org/packages/5f/42/2dc82330a70aa8e55b6d395b11018045e58d0bb00834502bf11509f79091/kiwisolver-1.4.9-cp312-cp312-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7a08b491ec91b1d5053ac177afe5290adacf1f0f6307d771ccac5de30592d198", size = 1343631, upload-time = "2025-08-10T21:26:17.045Z" },
- { url = "https://files.pythonhosted.org/packages/22/fd/f4c67a6ed1aab149ec5a8a401c323cee7a1cbe364381bb6c9c0d564e0e20/kiwisolver-1.4.9-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d8fc5c867c22b828001b6a38d2eaeb88160bf5783c6cb4a5e440efc981ce286d", size = 2224963, upload-time = "2025-08-10T21:26:18.737Z" },
- { url = "https://files.pythonhosted.org/packages/45/aa/76720bd4cb3713314677d9ec94dcc21ced3f1baf4830adde5bb9b2430a5f/kiwisolver-1.4.9-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:3b3115b2581ea35bb6d1f24a4c90af37e5d9b49dcff267eeed14c3893c5b86ab", size = 2321295, upload-time = "2025-08-10T21:26:20.11Z" },
- { url = "https://files.pythonhosted.org/packages/80/19/d3ec0d9ab711242f56ae0dc2fc5d70e298bb4a1f9dfab44c027668c673a1/kiwisolver-1.4.9-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:858e4c22fb075920b96a291928cb7dea5644e94c0ee4fcd5af7e865655e4ccf2", size = 2487987, upload-time = "2025-08-10T21:26:21.49Z" },
- { url = "https://files.pythonhosted.org/packages/39/e9/61e4813b2c97e86b6fdbd4dd824bf72d28bcd8d4849b8084a357bc0dd64d/kiwisolver-1.4.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ed0fecd28cc62c54b262e3736f8bb2512d8dcfdc2bcf08be5f47f96bf405b145", size = 2291817, upload-time = "2025-08-10T21:26:22.812Z" },
- { url = "https://files.pythonhosted.org/packages/a0/41/85d82b0291db7504da3c2defe35c9a8a5c9803a730f297bd823d11d5fb77/kiwisolver-1.4.9-cp312-cp312-win_amd64.whl", hash = "sha256:f68208a520c3d86ea51acf688a3e3002615a7f0238002cccc17affecc86a8a54", size = 73895, upload-time = "2025-08-10T21:26:24.37Z" },
- { url = "https://files.pythonhosted.org/packages/e2/92/5f3068cf15ee5cb624a0c7596e67e2a0bb2adee33f71c379054a491d07da/kiwisolver-1.4.9-cp312-cp312-win_arm64.whl", hash = "sha256:2c1a4f57df73965f3f14df20b80ee29e6a7930a57d2d9e8491a25f676e197c60", size = 64992, upload-time = "2025-08-10T21:26:25.732Z" },
- { url = "https://files.pythonhosted.org/packages/31/c1/c2686cda909742ab66c7388e9a1a8521a59eb89f8bcfbee28fc980d07e24/kiwisolver-1.4.9-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a5d0432ccf1c7ab14f9949eec60c5d1f924f17c037e9f8b33352fa05799359b8", size = 123681, upload-time = "2025-08-10T21:26:26.725Z" },
- { url = "https://files.pythonhosted.org/packages/ca/f0/f44f50c9f5b1a1860261092e3bc91ecdc9acda848a8b8c6abfda4a24dd5c/kiwisolver-1.4.9-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efb3a45b35622bb6c16dbfab491a8f5a391fe0e9d45ef32f4df85658232ca0e2", size = 66464, upload-time = "2025-08-10T21:26:27.733Z" },
- { url = "https://files.pythonhosted.org/packages/2d/7a/9d90a151f558e29c3936b8a47ac770235f436f2120aca41a6d5f3d62ae8d/kiwisolver-1.4.9-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1a12cf6398e8a0a001a059747a1cbf24705e18fe413bc22de7b3d15c67cffe3f", size = 64961, upload-time = "2025-08-10T21:26:28.729Z" },
- { url = "https://files.pythonhosted.org/packages/e9/e9/f218a2cb3a9ffbe324ca29a9e399fa2d2866d7f348ec3a88df87fc248fc5/kiwisolver-1.4.9-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b67e6efbf68e077dd71d1a6b37e43e1a99d0bff1a3d51867d45ee8908b931098", size = 1474607, upload-time = "2025-08-10T21:26:29.798Z" },
- { url = "https://files.pythonhosted.org/packages/d9/28/aac26d4c882f14de59041636292bc838db8961373825df23b8eeb807e198/kiwisolver-1.4.9-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5656aa670507437af0207645273ccdfee4f14bacd7f7c67a4306d0dcaeaf6eed", size = 1276546, upload-time = "2025-08-10T21:26:31.401Z" },
- { url = "https://files.pythonhosted.org/packages/8b/ad/8bfc1c93d4cc565e5069162f610ba2f48ff39b7de4b5b8d93f69f30c4bed/kiwisolver-1.4.9-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:bfc08add558155345129c7803b3671cf195e6a56e7a12f3dde7c57d9b417f525", size = 1294482, upload-time = "2025-08-10T21:26:32.721Z" },
- { url = "https://files.pythonhosted.org/packages/da/f1/6aca55ff798901d8ce403206d00e033191f63d82dd708a186e0ed2067e9c/kiwisolver-1.4.9-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:40092754720b174e6ccf9e845d0d8c7d8e12c3d71e7fc35f55f3813e96376f78", size = 1343720, upload-time = "2025-08-10T21:26:34.032Z" },
- { url = "https://files.pythonhosted.org/packages/d1/91/eed031876c595c81d90d0f6fc681ece250e14bf6998c3d7c419466b523b7/kiwisolver-1.4.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:497d05f29a1300d14e02e6441cf0f5ee81c1ff5a304b0d9fb77423974684e08b", size = 2224907, upload-time = "2025-08-10T21:26:35.824Z" },
- { url = "https://files.pythonhosted.org/packages/e9/ec/4d1925f2e49617b9cca9c34bfa11adefad49d00db038e692a559454dfb2e/kiwisolver-1.4.9-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:bdd1a81a1860476eb41ac4bc1e07b3f07259e6d55bbf739b79c8aaedcf512799", size = 2321334, upload-time = "2025-08-10T21:26:37.534Z" },
- { url = "https://files.pythonhosted.org/packages/43/cb/450cd4499356f68802750c6ddc18647b8ea01ffa28f50d20598e0befe6e9/kiwisolver-1.4.9-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:e6b93f13371d341afee3be9f7c5964e3fe61d5fa30f6a30eb49856935dfe4fc3", size = 2488313, upload-time = "2025-08-10T21:26:39.191Z" },
- { url = "https://files.pythonhosted.org/packages/71/67/fc76242bd99f885651128a5d4fa6083e5524694b7c88b489b1b55fdc491d/kiwisolver-1.4.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d75aa530ccfaa593da12834b86a0724f58bff12706659baa9227c2ccaa06264c", size = 2291970, upload-time = "2025-08-10T21:26:40.828Z" },
- { url = "https://files.pythonhosted.org/packages/75/bd/f1a5d894000941739f2ae1b65a32892349423ad49c2e6d0771d0bad3fae4/kiwisolver-1.4.9-cp313-cp313-win_amd64.whl", hash = "sha256:dd0a578400839256df88c16abddf9ba14813ec5f21362e1fe65022e00c883d4d", size = 73894, upload-time = "2025-08-10T21:26:42.33Z" },
- { url = "https://files.pythonhosted.org/packages/95/38/dce480814d25b99a391abbddadc78f7c117c6da34be68ca8b02d5848b424/kiwisolver-1.4.9-cp313-cp313-win_arm64.whl", hash = "sha256:d4188e73af84ca82468f09cadc5ac4db578109e52acb4518d8154698d3a87ca2", size = 64995, upload-time = "2025-08-10T21:26:43.889Z" },
- { url = "https://files.pythonhosted.org/packages/e2/37/7d218ce5d92dadc5ebdd9070d903e0c7cf7edfe03f179433ac4d13ce659c/kiwisolver-1.4.9-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:5a0f2724dfd4e3b3ac5a82436a8e6fd16baa7d507117e4279b660fe8ca38a3a1", size = 126510, upload-time = "2025-08-10T21:26:44.915Z" },
- { url = "https://files.pythonhosted.org/packages/23/b0/e85a2b48233daef4b648fb657ebbb6f8367696a2d9548a00b4ee0eb67803/kiwisolver-1.4.9-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:1b11d6a633e4ed84fc0ddafd4ebfd8ea49b3f25082c04ad12b8315c11d504dc1", size = 67903, upload-time = "2025-08-10T21:26:45.934Z" },
- { url = "https://files.pythonhosted.org/packages/44/98/f2425bc0113ad7de24da6bb4dae1343476e95e1d738be7c04d31a5d037fd/kiwisolver-1.4.9-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61874cdb0a36016354853593cffc38e56fc9ca5aa97d2c05d3dcf6922cd55a11", size = 66402, upload-time = "2025-08-10T21:26:47.101Z" },
- { url = "https://files.pythonhosted.org/packages/98/d8/594657886df9f34c4177cc353cc28ca7e6e5eb562d37ccc233bff43bbe2a/kiwisolver-1.4.9-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:60c439763a969a6af93b4881db0eed8fadf93ee98e18cbc35bc8da868d0c4f0c", size = 1582135, upload-time = "2025-08-10T21:26:48.665Z" },
- { url = "https://files.pythonhosted.org/packages/5c/c6/38a115b7170f8b306fc929e166340c24958347308ea3012c2b44e7e295db/kiwisolver-1.4.9-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92a2f997387a1b79a75e7803aa7ded2cfbe2823852ccf1ba3bcf613b62ae3197", size = 1389409, upload-time = "2025-08-10T21:26:50.335Z" },
- { url = "https://files.pythonhosted.org/packages/bf/3b/e04883dace81f24a568bcee6eb3001da4ba05114afa622ec9b6fafdc1f5e/kiwisolver-1.4.9-cp313-cp313t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a31d512c812daea6d8b3be3b2bfcbeb091dbb09177706569bcfc6240dcf8b41c", size = 1401763, upload-time = "2025-08-10T21:26:51.867Z" },
- { url = "https://files.pythonhosted.org/packages/9f/80/20ace48e33408947af49d7d15c341eaee69e4e0304aab4b7660e234d6288/kiwisolver-1.4.9-cp313-cp313t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:52a15b0f35dad39862d376df10c5230155243a2c1a436e39eb55623ccbd68185", size = 1453643, upload-time = "2025-08-10T21:26:53.592Z" },
- { url = "https://files.pythonhosted.org/packages/64/31/6ce4380a4cd1f515bdda976a1e90e547ccd47b67a1546d63884463c92ca9/kiwisolver-1.4.9-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a30fd6fdef1430fd9e1ba7b3398b5ee4e2887783917a687d86ba69985fb08748", size = 2330818, upload-time = "2025-08-10T21:26:55.051Z" },
- { url = "https://files.pythonhosted.org/packages/fa/e9/3f3fcba3bcc7432c795b82646306e822f3fd74df0ee81f0fa067a1f95668/kiwisolver-1.4.9-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:cc9617b46837c6468197b5945e196ee9ca43057bb7d9d1ae688101e4e1dddf64", size = 2419963, upload-time = "2025-08-10T21:26:56.421Z" },
- { url = "https://files.pythonhosted.org/packages/99/43/7320c50e4133575c66e9f7dadead35ab22d7c012a3b09bb35647792b2a6d/kiwisolver-1.4.9-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:0ab74e19f6a2b027ea4f845a78827969af45ce790e6cb3e1ebab71bdf9f215ff", size = 2594639, upload-time = "2025-08-10T21:26:57.882Z" },
- { url = "https://files.pythonhosted.org/packages/65/d6/17ae4a270d4a987ef8a385b906d2bdfc9fce502d6dc0d3aea865b47f548c/kiwisolver-1.4.9-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dba5ee5d3981160c28d5490f0d1b7ed730c22470ff7f6cc26cfcfaacb9896a07", size = 2391741, upload-time = "2025-08-10T21:26:59.237Z" },
- { url = "https://files.pythonhosted.org/packages/2a/8f/8f6f491d595a9e5912971f3f863d81baddccc8a4d0c3749d6a0dd9ffc9df/kiwisolver-1.4.9-cp313-cp313t-win_arm64.whl", hash = "sha256:0749fd8f4218ad2e851e11cc4dc05c7cbc0cbc4267bdfdb31782e65aace4ee9c", size = 68646, upload-time = "2025-08-10T21:27:00.52Z" },
- { url = "https://files.pythonhosted.org/packages/6b/32/6cc0fbc9c54d06c2969faa9c1d29f5751a2e51809dd55c69055e62d9b426/kiwisolver-1.4.9-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:9928fe1eb816d11ae170885a74d074f57af3a0d65777ca47e9aeb854a1fba386", size = 123806, upload-time = "2025-08-10T21:27:01.537Z" },
- { url = "https://files.pythonhosted.org/packages/b2/dd/2bfb1d4a4823d92e8cbb420fe024b8d2167f72079b3bb941207c42570bdf/kiwisolver-1.4.9-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:d0005b053977e7b43388ddec89fa567f43d4f6d5c2c0affe57de5ebf290dc552", size = 66605, upload-time = "2025-08-10T21:27:03.335Z" },
- { url = "https://files.pythonhosted.org/packages/f7/69/00aafdb4e4509c2ca6064646cba9cd4b37933898f426756adb2cb92ebbed/kiwisolver-1.4.9-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2635d352d67458b66fd0667c14cb1d4145e9560d503219034a18a87e971ce4f3", size = 64925, upload-time = "2025-08-10T21:27:04.339Z" },
- { url = "https://files.pythonhosted.org/packages/43/dc/51acc6791aa14e5cb6d8a2e28cefb0dc2886d8862795449d021334c0df20/kiwisolver-1.4.9-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:767c23ad1c58c9e827b649a9ab7809fd5fd9db266a9cf02b0e926ddc2c680d58", size = 1472414, upload-time = "2025-08-10T21:27:05.437Z" },
- { url = "https://files.pythonhosted.org/packages/3d/bb/93fa64a81db304ac8a246f834d5094fae4b13baf53c839d6bb6e81177129/kiwisolver-1.4.9-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:72d0eb9fba308b8311685c2268cf7d0a0639a6cd027d8128659f72bdd8a024b4", size = 1281272, upload-time = "2025-08-10T21:27:07.063Z" },
- { url = "https://files.pythonhosted.org/packages/70/e6/6df102916960fb8d05069d4bd92d6d9a8202d5a3e2444494e7cd50f65b7a/kiwisolver-1.4.9-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f68e4f3eeca8fb22cc3d731f9715a13b652795ef657a13df1ad0c7dc0e9731df", size = 1298578, upload-time = "2025-08-10T21:27:08.452Z" },
- { url = "https://files.pythonhosted.org/packages/7c/47/e142aaa612f5343736b087864dbaebc53ea8831453fb47e7521fa8658f30/kiwisolver-1.4.9-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d84cd4061ae292d8ac367b2c3fa3aad11cb8625a95d135fe93f286f914f3f5a6", size = 1345607, upload-time = "2025-08-10T21:27:10.125Z" },
- { url = "https://files.pythonhosted.org/packages/54/89/d641a746194a0f4d1a3670fb900d0dbaa786fb98341056814bc3f058fa52/kiwisolver-1.4.9-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a60ea74330b91bd22a29638940d115df9dc00af5035a9a2a6ad9399ffb4ceca5", size = 2230150, upload-time = "2025-08-10T21:27:11.484Z" },
- { url = "https://files.pythonhosted.org/packages/aa/6b/5ee1207198febdf16ac11f78c5ae40861b809cbe0e6d2a8d5b0b3044b199/kiwisolver-1.4.9-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:ce6a3a4e106cf35c2d9c4fa17c05ce0b180db622736845d4315519397a77beaf", size = 2325979, upload-time = "2025-08-10T21:27:12.917Z" },
- { url = "https://files.pythonhosted.org/packages/fc/ff/b269eefd90f4ae14dcc74973d5a0f6d28d3b9bb1afd8c0340513afe6b39a/kiwisolver-1.4.9-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:77937e5e2a38a7b48eef0585114fe7930346993a88060d0bf886086d2aa49ef5", size = 2491456, upload-time = "2025-08-10T21:27:14.353Z" },
- { url = "https://files.pythonhosted.org/packages/fc/d4/10303190bd4d30de547534601e259a4fbf014eed94aae3e5521129215086/kiwisolver-1.4.9-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:24c175051354f4a28c5d6a31c93906dc653e2bf234e8a4bbfb964892078898ce", size = 2294621, upload-time = "2025-08-10T21:27:15.808Z" },
- { url = "https://files.pythonhosted.org/packages/28/e0/a9a90416fce5c0be25742729c2ea52105d62eda6c4be4d803c2a7be1fa50/kiwisolver-1.4.9-cp314-cp314-win_amd64.whl", hash = "sha256:0763515d4df10edf6d06a3c19734e2566368980d21ebec439f33f9eb936c07b7", size = 75417, upload-time = "2025-08-10T21:27:17.436Z" },
- { url = "https://files.pythonhosted.org/packages/1f/10/6949958215b7a9a264299a7db195564e87900f709db9245e4ebdd3c70779/kiwisolver-1.4.9-cp314-cp314-win_arm64.whl", hash = "sha256:0e4e2bf29574a6a7b7f6cb5fa69293b9f96c928949ac4a53ba3f525dffb87f9c", size = 66582, upload-time = "2025-08-10T21:27:18.436Z" },
- { url = "https://files.pythonhosted.org/packages/ec/79/60e53067903d3bc5469b369fe0dfc6b3482e2133e85dae9daa9527535991/kiwisolver-1.4.9-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:d976bbb382b202f71c67f77b0ac11244021cfa3f7dfd9e562eefcea2df711548", size = 126514, upload-time = "2025-08-10T21:27:19.465Z" },
- { url = "https://files.pythonhosted.org/packages/25/d1/4843d3e8d46b072c12a38c97c57fab4608d36e13fe47d47ee96b4d61ba6f/kiwisolver-1.4.9-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2489e4e5d7ef9a1c300a5e0196e43d9c739f066ef23270607d45aba368b91f2d", size = 67905, upload-time = "2025-08-10T21:27:20.51Z" },
- { url = "https://files.pythonhosted.org/packages/8c/ae/29ffcbd239aea8b93108de1278271ae764dfc0d803a5693914975f200596/kiwisolver-1.4.9-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:e2ea9f7ab7fbf18fffb1b5434ce7c69a07582f7acc7717720f1d69f3e806f90c", size = 66399, upload-time = "2025-08-10T21:27:21.496Z" },
- { url = "https://files.pythonhosted.org/packages/a1/ae/d7ba902aa604152c2ceba5d352d7b62106bedbccc8e95c3934d94472bfa3/kiwisolver-1.4.9-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b34e51affded8faee0dfdb705416153819d8ea9250bbbf7ea1b249bdeb5f1122", size = 1582197, upload-time = "2025-08-10T21:27:22.604Z" },
- { url = "https://files.pythonhosted.org/packages/f2/41/27c70d427eddb8bc7e4f16420a20fefc6f480312122a59a959fdfe0445ad/kiwisolver-1.4.9-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d8aacd3d4b33b772542b2e01beb50187536967b514b00003bdda7589722d2a64", size = 1390125, upload-time = "2025-08-10T21:27:24.036Z" },
- { url = "https://files.pythonhosted.org/packages/41/42/b3799a12bafc76d962ad69083f8b43b12bf4fe78b097b12e105d75c9b8f1/kiwisolver-1.4.9-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7cf974dd4e35fa315563ac99d6287a1024e4dc2077b8a7d7cd3d2fb65d283134", size = 1402612, upload-time = "2025-08-10T21:27:25.773Z" },
- { url = "https://files.pythonhosted.org/packages/d2/b5/a210ea073ea1cfaca1bb5c55a62307d8252f531beb364e18aa1e0888b5a0/kiwisolver-1.4.9-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:85bd218b5ecfbee8c8a82e121802dcb519a86044c9c3b2e4aef02fa05c6da370", size = 1453990, upload-time = "2025-08-10T21:27:27.089Z" },
- { url = "https://files.pythonhosted.org/packages/5f/ce/a829eb8c033e977d7ea03ed32fb3c1781b4fa0433fbadfff29e39c676f32/kiwisolver-1.4.9-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:0856e241c2d3df4efef7c04a1e46b1936b6120c9bcf36dd216e3acd84bc4fb21", size = 2331601, upload-time = "2025-08-10T21:27:29.343Z" },
- { url = "https://files.pythonhosted.org/packages/e0/4b/b5e97eb142eb9cd0072dacfcdcd31b1c66dc7352b0f7c7255d339c0edf00/kiwisolver-1.4.9-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:9af39d6551f97d31a4deebeac6f45b156f9755ddc59c07b402c148f5dbb6482a", size = 2422041, upload-time = "2025-08-10T21:27:30.754Z" },
- { url = "https://files.pythonhosted.org/packages/40/be/8eb4cd53e1b85ba4edc3a9321666f12b83113a178845593307a3e7891f44/kiwisolver-1.4.9-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:bb4ae2b57fc1d8cbd1cf7b1d9913803681ffa903e7488012be5b76dedf49297f", size = 2594897, upload-time = "2025-08-10T21:27:32.803Z" },
- { url = "https://files.pythonhosted.org/packages/99/dd/841e9a66c4715477ea0abc78da039832fbb09dac5c35c58dc4c41a407b8a/kiwisolver-1.4.9-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:aedff62918805fb62d43a4aa2ecd4482c380dc76cd31bd7c8878588a61bd0369", size = 2391835, upload-time = "2025-08-10T21:27:34.23Z" },
- { url = "https://files.pythonhosted.org/packages/0c/28/4b2e5c47a0da96896fdfdb006340ade064afa1e63675d01ea5ac222b6d52/kiwisolver-1.4.9-cp314-cp314t-win_amd64.whl", hash = "sha256:1fa333e8b2ce4d9660f2cda9c0e1b6bafcfb2457a9d259faa82289e73ec24891", size = 79988, upload-time = "2025-08-10T21:27:35.587Z" },
- { url = "https://files.pythonhosted.org/packages/80/be/3578e8afd18c88cdf9cb4cffde75a96d2be38c5a903f1ed0ceec061bd09e/kiwisolver-1.4.9-cp314-cp314t-win_arm64.whl", hash = "sha256:4a48a2ce79d65d363597ef7b567ce3d14d68783d2b2263d98db3d9477805ba32", size = 70260, upload-time = "2025-08-10T21:27:36.606Z" },
- { url = "https://files.pythonhosted.org/packages/a2/63/fde392691690f55b38d5dd7b3710f5353bf7a8e52de93a22968801ab8978/kiwisolver-1.4.9-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:4d1d9e582ad4d63062d34077a9a1e9f3c34088a2ec5135b1f7190c07cf366527", size = 60183, upload-time = "2025-08-10T21:27:37.669Z" },
- { url = "https://files.pythonhosted.org/packages/27/b1/6aad34edfdb7cced27f371866f211332bba215bfd918ad3322a58f480d8b/kiwisolver-1.4.9-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:deed0c7258ceb4c44ad5ec7d9918f9f14fd05b2be86378d86cf50e63d1e7b771", size = 58675, upload-time = "2025-08-10T21:27:39.031Z" },
- { url = "https://files.pythonhosted.org/packages/9d/1a/23d855a702bb35a76faed5ae2ba3de57d323f48b1f6b17ee2176c4849463/kiwisolver-1.4.9-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0a590506f303f512dff6b7f75fd2fd18e16943efee932008fe7140e5fa91d80e", size = 80277, upload-time = "2025-08-10T21:27:40.129Z" },
- { url = "https://files.pythonhosted.org/packages/5a/5b/5239e3c2b8fb5afa1e8508f721bb77325f740ab6994d963e61b2b7abcc1e/kiwisolver-1.4.9-pp310-pypy310_pp73-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e09c2279a4d01f099f52d5c4b3d9e208e91edcbd1a175c9662a8b16e000fece9", size = 77994, upload-time = "2025-08-10T21:27:41.181Z" },
- { url = "https://files.pythonhosted.org/packages/f9/1c/5d4d468fb16f8410e596ed0eac02d2c68752aa7dc92997fe9d60a7147665/kiwisolver-1.4.9-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c9e7cdf45d594ee04d5be1b24dd9d49f3d1590959b2271fb30b5ca2b262c00fb", size = 73744, upload-time = "2025-08-10T21:27:42.254Z" },
- { url = "https://files.pythonhosted.org/packages/a3/0f/36d89194b5a32c054ce93e586d4049b6c2c22887b0eb229c61c68afd3078/kiwisolver-1.4.9-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:720e05574713db64c356e86732c0f3c5252818d05f9df320f0ad8380641acea5", size = 60104, upload-time = "2025-08-10T21:27:43.287Z" },
- { url = "https://files.pythonhosted.org/packages/52/ba/4ed75f59e4658fd21fe7dde1fee0ac397c678ec3befba3fe6482d987af87/kiwisolver-1.4.9-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:17680d737d5335b552994a2008fab4c851bcd7de33094a82067ef3a576ff02fa", size = 58592, upload-time = "2025-08-10T21:27:44.314Z" },
- { url = "https://files.pythonhosted.org/packages/33/01/a8ea7c5ea32a9b45ceeaee051a04c8ed4320f5add3c51bfa20879b765b70/kiwisolver-1.4.9-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:85b5352f94e490c028926ea567fc569c52ec79ce131dadb968d3853e809518c2", size = 80281, upload-time = "2025-08-10T21:27:45.369Z" },
- { url = "https://files.pythonhosted.org/packages/da/e3/dbd2ecdce306f1d07a1aaf324817ee993aab7aee9db47ceac757deabafbe/kiwisolver-1.4.9-pp311-pypy311_pp73-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:464415881e4801295659462c49461a24fb107c140de781d55518c4b80cb6790f", size = 78009, upload-time = "2025-08-10T21:27:46.376Z" },
- { url = "https://files.pythonhosted.org/packages/da/e9/0d4add7873a73e462aeb45c036a2dead2562b825aa46ba326727b3f31016/kiwisolver-1.4.9-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:fb940820c63a9590d31d88b815e7a3aa5915cad3ce735ab45f0c730b39547de1", size = 73929, upload-time = "2025-08-10T21:27:48.236Z" },
-]
-
-[[package]]
-name = "lang2sql"
-source = { editable = "." }
-dependencies = [
- { name = "anthropic" },
- { name = "clickhouse-driver" },
- { name = "crate" },
- { name = "databricks-sql-connector" },
- { name = "datahub" },
- { name = "duckdb" },
- { name = "faiss-cpu" },
- { name = "google-cloud-bigquery" },
- { name = "ipython", version = "8.18.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
- { name = "ipython", version = "8.37.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" },
- { name = "ipython", version = "9.5.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
- { name = "kaleido" },
- { name = "langchain" },
- { name = "langchain-aws" },
- { name = "langchain-community" },
- { name = "langchain-google-genai" },
- { name = "langchain-huggingface" },
- { name = "langchain-ollama" },
- { name = "langchain-openai" },
- { name = "langchain-postgres" },
- { name = "langgraph" },
- { name = "matplotlib", version = "3.9.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
- { name = "matplotlib", version = "3.10.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
- { name = "mysql-connector-python" },
- { name = "numpy" },
- { name = "openai" },
- { name = "oracledb" },
- { name = "pgvector" },
- { name = "plotly" },
- { name = "psycopg", extra = ["binary"] },
- { name = "psycopg2-binary" },
- { name = "pyhive" },
- { name = "pyodbc" },
- { name = "python-dotenv" },
- { name = "snowflake-connector-python" },
- { name = "sqlalchemy" },
- { name = "streamlit" },
- { name = "transformers" },
- { name = "trino" },
-]
-
-[package.dev-dependencies]
-dev = [
- { name = "pre-commit" },
- { name = "pytest" },
-]
-
-[package.metadata]
-requires-dist = [
- { name = "anthropic", specifier = ">=0.20.0" },
- { name = "clickhouse-driver", specifier = ">=0.2.9,<0.3.0" },
- { name = "crate", specifier = ">=0.29.0,<1.0.0" },
- { name = "databricks-sql-connector", specifier = ">=4.0.3,<5.0.0" },
- { name = "datahub", specifier = "==0.999.1" },
- { name = "duckdb", specifier = ">=1.2.2,<2.0.0" },
- { name = "faiss-cpu", specifier = "==1.10.0" },
- { name = "google-cloud-bigquery", specifier = ">=3.20.1,<4.0.0" },
- { name = "ipython" },
- { name = "kaleido" },
- { name = "langchain", specifier = "==0.3.14" },
- { name = "langchain-aws", specifier = ">=0.2.21,<0.3.0" },
- { name = "langchain-community", specifier = "==0.3.14" },
- { name = "langchain-google-genai", specifier = ">=2.1.3,<3.0.0" },
- { name = "langchain-huggingface", specifier = ">=0.1.2,<0.2.0" },
- { name = "langchain-ollama", specifier = ">=0.3.2,<0.4.0" },
- { name = "langchain-openai", specifier = "==0.3.0" },
- { name = "langchain-postgres", specifier = "==0.0.15" },
- { name = "langgraph", specifier = "==0.2.62" },
- { name = "matplotlib" },
- { name = "mysql-connector-python", specifier = ">=9.3.0,<10.0.0" },
- { name = "numpy", specifier = "<2.0" },
- { name = "openai", specifier = "==1.59.8" },
- { name = "oracledb", specifier = ">=3.1.0,<4.0.0" },
- { name = "pgvector", specifier = "==0.3.6" },
- { name = "plotly" },
- { name = "psycopg", extras = ["binary"], specifier = ">=3.2,<4.0" },
- { name = "psycopg2-binary", specifier = ">=2.9.10,<3.0.0" },
- { name = "pyhive", specifier = ">=0.6.6,<1.0.0" },
- { name = "pyodbc", specifier = ">=5.1.0,<6.0.0" },
- { name = "python-dotenv", specifier = "==1.0.1" },
- { name = "snowflake-connector-python", specifier = ">=3.15.0,<4.0.0" },
- { name = "sqlalchemy", specifier = ">=2.0" },
- { name = "streamlit", specifier = "==1.41.1" },
- { name = "transformers", specifier = "==4.51.2" },
- { name = "trino", specifier = ">=0.329.0,<1.0.0" },
-]
-
-[package.metadata.requires-dev]
-dev = [
- { name = "pre-commit", specifier = "==4.1.0" },
- { name = "pytest", specifier = ">=8.3.5" },
-]
-
-[[package]]
-name = "langchain"
-version = "0.3.14"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "aiohttp" },
- { name = "async-timeout", marker = "python_full_version < '3.11'" },
- { 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/18/35/2adb0693acc149e462bc0e7856ecd58096c285f66a78bc44fc2b8ae91ce0/langchain-0.3.14.tar.gz", hash = "sha256:4a5ae817b5832fa0e1fcadc5353fbf74bebd2f8e550294d4dc039f651ddcd3d1", size = 420409, upload-time = "2025-01-03T20:22:40.463Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/d0/a8/0a8f868615b7a30636b1d15b718e3ea9875bf0dccced03583477c2372495/langchain-0.3.14-py3-none-any.whl", hash = "sha256:5df9031702f7fe6c956e84256b4639a46d5d03a75be1ca4c1bc9479b358061a2", size = 1009213, upload-time = "2025-01-03T20:22:36.997Z" },
-]
-
-[[package]]
-name = "langchain-aws"
-version = "0.2.24"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "boto3" },
- { name = "langchain-core" },
- { name = "numpy" },
- { name = "pydantic" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/a4/65/51b38bdef5488c2f37d27ffef57d838b99de86a7b2b91fa1db4f78b27865/langchain_aws-0.2.24.tar.gz", hash = "sha256:1ef09f3b84b94e5cc816b21ade391b4c3b14aba3ad8dc75968fac50ffe6bbd1a", size = 98341, upload-time = "2025-05-27T20:57:03.065Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/ee/81/315e265a8ce740959f1d358d4d93f2f13315dcd6fc70ce82b04f0548fef0/langchain_aws-0.2.24-py3-none-any.whl", hash = "sha256:464df2e6535001e5424786c493906d2e5d4a01bd1906e9e8d4da1404e13bd25c", size = 120268, upload-time = "2025-05-27T20:57:02.024Z" },
-]
-
-[[package]]
-name = "langchain-community"
-version = "0.3.14"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "aiohttp" },
- { name = "dataclasses-json" },
- { name = "httpx-sse" },
- { name = "langchain" },
- { name = "langchain-core" },
- { name = "langsmith" },
- { name = "numpy" },
- { name = "pydantic-settings" },
- { name = "pyyaml" },
- { name = "requests" },
- { name = "sqlalchemy" },
- { name = "tenacity" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/2a/9a/a32cddaa9e8c618e69cfbfdb11cb8718bd9a531ae8426f6a2125a7a5d31f/langchain_community-0.3.14.tar.gz", hash = "sha256:d8ba0fe2dbb5795bff707684b712baa5ee379227194610af415ccdfdefda0479", size = 1720031, upload-time = "2025-01-03T20:41:27.506Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/7b/df/3a226f47aad50605a4ff77a30e876d7520f2060aa624532872e44ea048d8/langchain_community-0.3.14-py3-none-any.whl", hash = "sha256:cc02a0abad0551edef3e565dff643386a5b2ee45b933b6d883d4a935b9649f3c", size = 2502417, upload-time = "2025-01-03T20:41:24.802Z" },
-]
-
-[[package]]
-name = "langchain-core"
-version = "0.3.63"
-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/0a/b71a9a5d42e743d6876cce23d803e284b191ed4d6544e2f7fe1b37f7854c/langchain_core-0.3.63.tar.gz", hash = "sha256:e2e30cfbb7684a5a0319f6cbf065fc3c438bfd1060302f085a122527890fb01e", size = 558302, upload-time = "2025-05-29T18:57:19.933Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/5c/71/a748861e6a69ab6ef50ab8e65120422a1f36245c71a0dd0f02de49c208e1/langchain_core-0.3.63-py3-none-any.whl", hash = "sha256:f91db8221b1bc6808f70b2e72fded1a94d50ee3f1dff1636fb5a5a514c64b7f5", size = 438468, upload-time = "2025-05-29T18:57:17.424Z" },
-]
-
-[[package]]
-name = "langchain-google-genai"
-version = "2.1.5"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "filetype" },
- { name = "google-ai-generativelanguage" },
- { name = "langchain-core" },
- { name = "pydantic" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/2a/a5/d9b8d5afdf4a33f13e7d973f2705891cd13cc1dfb578719c9861a8d8385b/langchain_google_genai-2.1.5.tar.gz", hash = "sha256:6e71375a7707667bdecc5a7d1c86438ec10f2c7bb6dc6e3f095f5b22523c4fc9", size = 40813, upload-time = "2025-05-28T13:49:09.574Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/5e/70/0747358eca996f713f715e2bfc2d0805804f8f705af57381fbee91bb475a/langchain_google_genai-2.1.5-py3-none-any.whl", hash = "sha256:6c8ccaf33a41f83b1d08a2398edbf47a1eebea27a7ec6930f34a0c019f309253", size = 44788, upload-time = "2025-05-28T13:49:08.22Z" },
-]
-
-[[package]]
-name = "langchain-huggingface"
-version = "0.1.2"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "huggingface-hub" },
- { name = "langchain-core" },
- { name = "sentence-transformers" },
- { name = "tokenizers" },
- { name = "transformers" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/a1/0f/8277d993d5307f06523e72c9bc8a505ed028f7b1c1e5276d8e89044b6036/langchain_huggingface-0.1.2.tar.gz", hash = "sha256:4a66d5c449298fd353bd84c9ed01f9bf4303bf2e4ffce14aab8c55c584eee57c", size = 16129, upload-time = "2024-10-31T18:56:53.894Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/9d/f8/77a303ddc492f6eed8bf0979f2bc6db4fa6eb1089c5e9f0f977dd87bc9c2/langchain_huggingface-0.1.2-py3-none-any.whl", hash = "sha256:7de5cfcae32bfb6a99c084fc16176f02583a4f8d94febb6bb45bed5b34699174", size = 21251, upload-time = "2024-10-31T18:56:52.21Z" },
-]
-
-[[package]]
-name = "langchain-ollama"
-version = "0.3.3"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "langchain-core" },
- { name = "ollama" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/59/9f/6683f69f14b0cde3556c6b7752fb290bfce743981dc1312efa924619365f/langchain_ollama-0.3.3.tar.gz", hash = "sha256:7d6ed75bfb706751b83173fe886b72ae25bb0b1bd7f3eb2622821c4149f7807b", size = 21913, upload-time = "2025-05-15T20:27:06.027Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/84/6f/ab7a470522e27b95ed008eb9ef81b1ab55321f3f3aff21ca0109aae53cdf/langchain_ollama-0.3.3-py3-none-any.whl", hash = "sha256:f1c745a4b59d36bb51995c23c6b0fbc20f71956715659425ab88639a14b213cd", size = 21156, upload-time = "2025-05-15T20:27:05.159Z" },
-]
-
-[[package]]
-name = "langchain-openai"
-version = "0.3.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "langchain-core" },
- { name = "openai" },
- { name = "tiktoken" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/16/0a/0711117a4e8273d5edd4899399a8597d848b2d7b3c9ba3be97038b4fbc1a/langchain_openai-0.3.0.tar.gz", hash = "sha256:88d623eeb2aaa1fff65c2b419a4a1cfd37d3a1d504e598b87cf0bc822a3b70d0", size = 48067, upload-time = "2025-01-10T16:04:19.607Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/4a/9c/b38e308ac668f6db067b424a2a78e5b865753c144a119456f008a09230db/langchain_openai-0.3.0-py3-none-any.whl", hash = "sha256:49c921a22d272b04749a61e78bffa83aecdb8840b24b69f2909e115a357a9a5b", size = 54218, upload-time = "2025-01-10T16:04:17.377Z" },
-]
-
-[[package]]
-name = "langchain-postgres"
-version = "0.0.15"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "asyncpg" },
- { name = "langchain-core" },
- { name = "numpy" },
- { name = "pgvector" },
- { name = "psycopg" },
- { name = "psycopg-pool" },
- { name = "sqlalchemy" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/1f/b9/b9eb61d2f2679bacea0bea02e71e715c18f02c9f72f6c3d523fe7f7f65be/langchain_postgres-0.0.15.tar.gz", hash = "sha256:d6be01ab3a802881e7dcd16439a4efda5a8eba15c368e04fe9a96134ad90854e", size = 198495, upload-time = "2025-06-24T14:19:21.324Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/15/5e/572c90fce17462bfcb7e7b7ac2e24bbdbaced338fb271172c7b96a24ccee/langchain_postgres-0.0.15-py3-none-any.whl", hash = "sha256:dc3d083f6e2ac08fe918f658b63886586be62c057cab0ad30c1c6b38023d99b7", size = 45059, upload-time = "2025-06-24T14:19:19.781Z" },
-]
-
-[[package]]
-name = "langchain-text-splitters"
-version = "0.3.8"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "langchain-core" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/e7/ac/b4a25c5716bb0103b1515f1f52cc69ffb1035a5a225ee5afe3aed28bf57b/langchain_text_splitters-0.3.8.tar.gz", hash = "sha256:116d4b9f2a22dda357d0b79e30acf005c5518177971c66a9f1ab0edfdb0f912e", size = 42128, upload-time = "2025-04-04T14:03:51.521Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/8b/a3/3696ff2444658053c01b6b7443e761f28bb71217d82bb89137a978c5f66f/langchain_text_splitters-0.3.8-py3-none-any.whl", hash = "sha256:e75cc0f4ae58dcf07d9f18776400cf8ade27fadd4ff6d264df6278bb302f6f02", size = 32440, upload-time = "2025-04-04T14:03:50.6Z" },
-]
-
-[[package]]
-name = "langgraph"
-version = "0.2.62"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "langchain-core" },
- { name = "langgraph-checkpoint" },
- { name = "langgraph-sdk" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/54/bb/23859e3c219944bc9f1f3093629970631b3a6dc0aeaf607d7d205b2b551e/langgraph-0.2.62.tar.gz", hash = "sha256:0aac9fd55ffe669bc1312203e0f9ea2733c65cc276f196e7ff0d443cf4efbb89", size = 119343, upload-time = "2025-01-10T19:53:09.375Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/30/38/571f8bc14d4ced8e9d86aca657ffd9a6d076e32e2a62d487eab15f2ceca7/langgraph-0.2.62-py3-none-any.whl", hash = "sha256:51ae9e02a52485a837642eebe7ae43269af7d7305d62f8f69ac11589b2fbba26", size = 138170, upload-time = "2025-01-10T19:53:06.811Z" },
-]
-
-[[package]]
-name = "langgraph-checkpoint"
-version = "2.1.1"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "langchain-core" },
- { name = "ormsgpack" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/73/3e/d00eb2b56c3846a0cabd2e5aa71c17a95f882d4f799a6ffe96a19b55eba9/langgraph_checkpoint-2.1.1.tar.gz", hash = "sha256:72038c0f9e22260cb9bff1f3ebe5eb06d940b7ee5c1e4765019269d4f21cf92d", size = 136256, upload-time = "2025-07-17T13:07:52.411Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/4c/dd/64686797b0927fb18b290044be12ae9d4df01670dce6bb2498d5ab65cb24/langgraph_checkpoint-2.1.1-py3-none-any.whl", hash = "sha256:5a779134fd28134a9a83d078be4450bbf0e0c79fdf5e992549658899e6fc5ea7", size = 43925, upload-time = "2025-07-17T13:07:51.023Z" },
-]
-
-[[package]]
-name = "langgraph-sdk"
-version = "0.1.74"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "httpx" },
- { name = "orjson" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/6d/f7/3807b72988f7eef5e0eb41e7e695eca50f3ed31f7cab5602db3b651c85ff/langgraph_sdk-0.1.74.tar.gz", hash = "sha256:7450e0db5b226cc2e5328ca22c5968725873630ef47c4206a30707cb25dc3ad6", size = 72190, upload-time = "2025-07-21T16:36:50.032Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/1f/1a/3eacc4df8127781ee4b0b1e5cad7dbaf12510f58c42cbcb9d1e2dba2a164/langgraph_sdk-0.1.74-py3-none-any.whl", hash = "sha256:3a265c3757fe0048adad4391d10486db63ef7aa5a2cbd22da22d4503554cb890", size = 50254, upload-time = "2025-07-21T16:36:49.134Z" },
-]
-
-[[package]]
-name = "langsmith"
-version = "0.2.11"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "httpx" },
- { name = "orjson", marker = "platform_python_implementation != 'PyPy'" },
- { name = "pydantic" },
- { name = "requests" },
- { name = "requests-toolbelt" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/ae/03/dd26e203a6cc4df053b3f2a3d40bd17cce7b495f5fab2c05ff8005303b68/langsmith-0.2.11.tar.gz", hash = "sha256:edf070349dbfc63dc4fc30e22533a11d77768e99ef269399b221c48fee25c737", size = 314724, upload-time = "2025-01-17T00:25:37.495Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/f1/5b/b17c0c3dea1a76d75a386fde192221de8fccf3110f200a08aefd6666cfd7/langsmith-0.2.11-py3-none-any.whl", hash = "sha256:084cf66a7f093c25e6b30fb4005008ec5fa9843110e2f0b265ce133c6a0225e6", size = 326892, upload-time = "2025-01-17T00:25:33.456Z" },
-]
-
-[[package]]
-name = "logistro"
-version = "1.1.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/27/c1/aa8bc9e07e4b4bd9a3bc05804c483ba3f334c94dcd54995da856103a204d/logistro-1.1.0.tar.gz", hash = "sha256:ad51f0efa2bc705bea7c266e8a759cf539457cf7108202a5eec77bdf6300d774", size = 8269, upload-time = "2025-04-26T20:14:11.012Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/bc/df/e51691ab004d74fa25b751527d041ad1b4d84ee86cbcb8630ab0d7d5188e/logistro-1.1.0-py3-none-any.whl", hash = "sha256:4f88541fe7f3c545561b754d86121abd9c6d4d8b312381046a78dcd794fddc7c", size = 7894, upload-time = "2025-04-26T20:14:09.363Z" },
-]
-
-[[package]]
-name = "lz4"
-version = "4.4.4"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/c6/5a/945f5086326d569f14c84ac6f7fcc3229f0b9b1e8cc536b951fd53dfb9e1/lz4-4.4.4.tar.gz", hash = "sha256:070fd0627ec4393011251a094e08ed9fdcc78cb4e7ab28f507638eee4e39abda", size = 171884, upload-time = "2025-04-01T22:55:58.62Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/b0/80/4054e99cda2e003097f59aeb3ad470128f3298db5065174a84564d2d6983/lz4-4.4.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f170abb8416c4efca48e76cac2c86c3185efdf841aecbe5c190121c42828ced0", size = 220896, upload-time = "2025-04-01T22:55:13.577Z" },
- { url = "https://files.pythonhosted.org/packages/dd/4e/f92424d5734e772b05ddbeec739e2566e2a2336995b36a180e1dd9411e9a/lz4-4.4.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d33a5105cd96ebd32c3e78d7ece6123a9d2fb7c18b84dec61f27837d9e0c496c", size = 189679, upload-time = "2025-04-01T22:55:15.471Z" },
- { url = "https://files.pythonhosted.org/packages/a2/70/71ffd496067cba6ba352e10b89c0e9cee3e4bc4717ba866b6aa350f4c7ac/lz4-4.4.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:30ebbc5b76b4f0018988825a7e9ce153be4f0d4eba34e6c1f2fcded120573e88", size = 1237940, upload-time = "2025-04-01T22:55:16.498Z" },
- { url = "https://files.pythonhosted.org/packages/6e/59/cf34d1e232b11e1ae7122300be00529f369a7cd80f74ac351d58c4c4eedf/lz4-4.4.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc64d6dfa7a89397529b22638939e70d85eaedc1bd68e30a29c78bfb65d4f715", size = 1264105, upload-time = "2025-04-01T22:55:17.606Z" },
- { url = "https://files.pythonhosted.org/packages/f9/f6/3a00a98ff5b872d572cc6e9c88e0f6275bea0f3ed1dc1b8f8b736c85784c/lz4-4.4.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a355223a284f42a723c120ce68827de66d5cb872a38732b3d5abbf544fa2fe26", size = 1184179, upload-time = "2025-04-01T22:55:19.206Z" },
- { url = "https://files.pythonhosted.org/packages/bc/de/6aeb602786174bad290609c0c988afb1077b74a80eaea23ebc3b5de6e2fa/lz4-4.4.4-cp310-cp310-win32.whl", hash = "sha256:b28228197775b7b5096898851d59ef43ccaf151136f81d9c436bc9ba560bc2ba", size = 88265, upload-time = "2025-04-01T22:55:20.215Z" },
- { url = "https://files.pythonhosted.org/packages/e4/b5/1f52c8b17d02ae637f85911c0135ca08be1c9bbdfb3e7de1c4ae7af0bac6/lz4-4.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:45e7c954546de4f85d895aa735989d77f87dd649f503ce1c8a71a151b092ed36", size = 99916, upload-time = "2025-04-01T22:55:21.332Z" },
- { url = "https://files.pythonhosted.org/packages/01/e7/123587e7dae6cdba48393e4fdad2b9412f43f51346afe9ca6f697029de11/lz4-4.4.4-cp310-cp310-win_arm64.whl", hash = "sha256:e3fc90f766401684740978cd781d73b9685bd81b5dbf7257542ef9de4612e4d2", size = 89746, upload-time = "2025-04-01T22:55:22.205Z" },
- { url = "https://files.pythonhosted.org/packages/28/e8/63843dc5ecb1529eb38e1761ceed04a0ad52a9ad8929ab8b7930ea2e4976/lz4-4.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ddfc7194cd206496c445e9e5b0c47f970ce982c725c87bd22de028884125b68f", size = 220898, upload-time = "2025-04-01T22:55:23.085Z" },
- { url = "https://files.pythonhosted.org/packages/e4/94/c53de5f07c7dc11cf459aab2a1d754f5df5f693bfacbbe1e4914bfd02f1e/lz4-4.4.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:714f9298c86f8e7278f1c6af23e509044782fa8220eb0260f8f8f1632f820550", size = 189685, upload-time = "2025-04-01T22:55:24.413Z" },
- { url = "https://files.pythonhosted.org/packages/fe/59/c22d516dd0352f2a3415d1f665ccef2f3e74ecec3ca6a8f061a38f97d50d/lz4-4.4.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a8474c91de47733856c6686df3c4aca33753741da7e757979369c2c0d32918ba", size = 1239225, upload-time = "2025-04-01T22:55:25.737Z" },
- { url = "https://files.pythonhosted.org/packages/81/af/665685072e71f3f0e626221b7922867ec249cd8376aca761078c8f11f5da/lz4-4.4.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80dd27d7d680ea02c261c226acf1d41de2fd77af4fb2da62b278a9376e380de0", size = 1265881, upload-time = "2025-04-01T22:55:26.817Z" },
- { url = "https://files.pythonhosted.org/packages/90/04/b4557ae381d3aa451388a29755cc410066f5e2f78c847f66f154f4520a68/lz4-4.4.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9b7d6dddfd01b49aedb940fdcaf32f41dc58c926ba35f4e31866aeec2f32f4f4", size = 1185593, upload-time = "2025-04-01T22:55:27.896Z" },
- { url = "https://files.pythonhosted.org/packages/7b/e4/03636979f4e8bf92c557f998ca98ee4e6ef92e92eaf0ed6d3c7f2524e790/lz4-4.4.4-cp311-cp311-win32.whl", hash = "sha256:4134b9fd70ac41954c080b772816bb1afe0c8354ee993015a83430031d686a4c", size = 88259, upload-time = "2025-04-01T22:55:29.03Z" },
- { url = "https://files.pythonhosted.org/packages/07/f0/9efe53b4945441a5d2790d455134843ad86739855b7e6199977bf6dc8898/lz4-4.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:f5024d3ca2383470f7c4ef4d0ed8eabad0b22b23eeefde1c192cf1a38d5e9f78", size = 99916, upload-time = "2025-04-01T22:55:29.933Z" },
- { url = "https://files.pythonhosted.org/packages/87/c8/1675527549ee174b9e1db089f7ddfbb962a97314657269b1e0344a5eaf56/lz4-4.4.4-cp311-cp311-win_arm64.whl", hash = "sha256:6ea715bb3357ea1665f77874cf8f55385ff112553db06f3742d3cdcec08633f7", size = 89741, upload-time = "2025-04-01T22:55:31.184Z" },
- { url = "https://files.pythonhosted.org/packages/f7/2d/5523b4fabe11cd98f040f715728d1932eb7e696bfe94391872a823332b94/lz4-4.4.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:23ae267494fdd80f0d2a131beff890cf857f1b812ee72dbb96c3204aab725553", size = 220669, upload-time = "2025-04-01T22:55:32.032Z" },
- { url = "https://files.pythonhosted.org/packages/91/06/1a5bbcacbfb48d8ee5b6eb3fca6aa84143a81d92946bdb5cd6b005f1863e/lz4-4.4.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fff9f3a1ed63d45cb6514bfb8293005dc4141341ce3500abdfeb76124c0b9b2e", size = 189661, upload-time = "2025-04-01T22:55:33.413Z" },
- { url = "https://files.pythonhosted.org/packages/fa/08/39eb7ac907f73e11a69a11576a75a9e36406b3241c0ba41453a7eb842abb/lz4-4.4.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ea7f07329f85a8eda4d8cf937b87f27f0ac392c6400f18bea2c667c8b7f8ecc", size = 1238775, upload-time = "2025-04-01T22:55:34.835Z" },
- { url = "https://files.pythonhosted.org/packages/e9/26/05840fbd4233e8d23e88411a066ab19f1e9de332edddb8df2b6a95c7fddc/lz4-4.4.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ccab8f7f7b82f9fa9fc3b0ba584d353bd5aa818d5821d77d5b9447faad2aaad", size = 1265143, upload-time = "2025-04-01T22:55:35.933Z" },
- { url = "https://files.pythonhosted.org/packages/b7/5d/5f2db18c298a419932f3ab2023deb689863cf8fd7ed875b1c43492479af2/lz4-4.4.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e43e9d48b2daf80e486213128b0763deed35bbb7a59b66d1681e205e1702d735", size = 1185032, upload-time = "2025-04-01T22:55:37.454Z" },
- { url = "https://files.pythonhosted.org/packages/c4/e6/736ab5f128694b0f6aac58343bcf37163437ac95997276cd0be3ea4c3342/lz4-4.4.4-cp312-cp312-win32.whl", hash = "sha256:33e01e18e4561b0381b2c33d58e77ceee850a5067f0ece945064cbaac2176962", size = 88284, upload-time = "2025-04-01T22:55:38.536Z" },
- { url = "https://files.pythonhosted.org/packages/40/b8/243430cb62319175070e06e3a94c4c7bd186a812e474e22148ae1290d47d/lz4-4.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:d21d1a2892a2dcc193163dd13eaadabb2c1b803807a5117d8f8588b22eaf9f12", size = 99918, upload-time = "2025-04-01T22:55:39.628Z" },
- { url = "https://files.pythonhosted.org/packages/6c/e1/0686c91738f3e6c2e1a243e0fdd4371667c4d2e5009b0a3605806c2aa020/lz4-4.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:2f4f2965c98ab254feddf6b5072854a6935adab7bc81412ec4fe238f07b85f62", size = 89736, upload-time = "2025-04-01T22:55:40.5Z" },
- { url = "https://files.pythonhosted.org/packages/3b/3c/d1d1b926d3688263893461e7c47ed7382a969a0976fc121fc678ec325fc6/lz4-4.4.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ed6eb9f8deaf25ee4f6fad9625d0955183fdc90c52b6f79a76b7f209af1b6e54", size = 220678, upload-time = "2025-04-01T22:55:41.78Z" },
- { url = "https://files.pythonhosted.org/packages/26/89/8783d98deb058800dabe07e6cdc90f5a2a8502a9bad8c5343c641120ace2/lz4-4.4.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:18ae4fe3bafb344dbd09f976d45cbf49c05c34416f2462828f9572c1fa6d5af7", size = 189670, upload-time = "2025-04-01T22:55:42.775Z" },
- { url = "https://files.pythonhosted.org/packages/22/ab/a491ace69a83a8914a49f7391e92ca0698f11b28d5ce7b2ececa2be28e9a/lz4-4.4.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:57fd20c5fc1a49d1bbd170836fccf9a338847e73664f8e313dce6ac91b8c1e02", size = 1238746, upload-time = "2025-04-01T22:55:43.797Z" },
- { url = "https://files.pythonhosted.org/packages/97/12/a1f2f4fdc6b7159c0d12249456f9fe454665b6126e98dbee9f2bd3cf735c/lz4-4.4.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e9cb387c33f014dae4db8cb4ba789c8d2a0a6d045ddff6be13f6c8d9def1d2a6", size = 1265119, upload-time = "2025-04-01T22:55:44.943Z" },
- { url = "https://files.pythonhosted.org/packages/50/6e/e22e50f5207649db6ea83cd31b79049118305be67e96bec60becf317afc6/lz4-4.4.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d0be9f68240231e1e44118a4ebfecd8a5d4184f0bdf5c591c98dd6ade9720afd", size = 1184954, upload-time = "2025-04-01T22:55:46.161Z" },
- { url = "https://files.pythonhosted.org/packages/4c/c4/2a458039645fcc6324ece731d4d1361c5daf960b553d1fcb4261ba07d51c/lz4-4.4.4-cp313-cp313-win32.whl", hash = "sha256:e9ec5d45ea43684f87c316542af061ef5febc6a6b322928f059ce1fb289c298a", size = 88289, upload-time = "2025-04-01T22:55:47.601Z" },
- { url = "https://files.pythonhosted.org/packages/00/96/b8e24ea7537ab418074c226279acfcaa470e1ea8271003e24909b6db942b/lz4-4.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:a760a175b46325b2bb33b1f2bbfb8aa21b48e1b9653e29c10b6834f9bb44ead4", size = 99925, upload-time = "2025-04-01T22:55:48.463Z" },
- { url = "https://files.pythonhosted.org/packages/a5/a5/f9838fe6aa132cfd22733ed2729d0592259fff074cefb80f19aa0607367b/lz4-4.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:f4c21648d81e0dda38b4720dccc9006ae33b0e9e7ffe88af6bf7d4ec124e2fba", size = 89743, upload-time = "2025-04-01T22:55:49.716Z" },
- { url = "https://files.pythonhosted.org/packages/60/92/84d57db743cef59b2277cf40577de12ab48cbcd327772273a81a39d70580/lz4-4.4.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bd1add57b6fe1f96bed2d529de085e9378a3ac04b86f116d10506f85b68e97fc", size = 220887, upload-time = "2025-04-01T22:55:50.607Z" },
- { url = "https://files.pythonhosted.org/packages/87/b7/afa1ba2f827c1ec9d0b571e4fc71a2357a7fc735430cfb9b4c03f94f5d8a/lz4-4.4.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:900912e8a7cf74b4a2bea18a3594ae0bf1138f99919c20017167b6e05f760aa4", size = 189664, upload-time = "2025-04-01T22:55:51.579Z" },
- { url = "https://files.pythonhosted.org/packages/6f/6b/bfa74d3412cc9a5c787b44e1941b3186a813f8354ea633aed785ad8af106/lz4-4.4.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:017f8d269a739405a59d68a4d63d23a8df23e3bb2c70aa069b7563af08dfdffb", size = 1237318, upload-time = "2025-04-01T22:55:52.55Z" },
- { url = "https://files.pythonhosted.org/packages/b8/c7/3e826333be0034ac702a6fa25ed289002c83a154c69d8b9da7f3583157c5/lz4-4.4.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dac522788296a9a02a39f620970dea86c38e141e21e51238f1b5e9fa629f8e69", size = 1263452, upload-time = "2025-04-01T22:55:53.757Z" },
- { url = "https://files.pythonhosted.org/packages/45/e5/06b90dbe76f21475aab0052e0f1a8598d651a5a269f2e9a86f05241142ed/lz4-4.4.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6b56aa9eef830bf6443acd8c4e18b208a8993dc32e0d6ef4263ecfa6afb3f599", size = 1183617, upload-time = "2025-04-01T22:55:54.858Z" },
- { url = "https://files.pythonhosted.org/packages/0b/9e/1781ecb72300aed4d74484dff9a16a585e82c5de47c52930bb6e5a415c40/lz4-4.4.4-cp39-cp39-win32.whl", hash = "sha256:585b42eb37ab16a278c3a917ec23b2beef175aa669f4120142b97aebf90ef775", size = 88266, upload-time = "2025-04-01T22:55:55.988Z" },
- { url = "https://files.pythonhosted.org/packages/f8/2d/2426270bc39cd39a49773559137415b5a1bb43d3be75a44807806f5cb503/lz4-4.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:4ab1537bd3b3bfbafd3c8847e06827129794488304f21945fc2f5b669649d94f", size = 99915, upload-time = "2025-04-01T22:55:56.849Z" },
- { url = "https://files.pythonhosted.org/packages/66/ea/cadcd430073925e1acd7c509333101cc1b922593af6779355bf6cc064c73/lz4-4.4.4-cp39-cp39-win_arm64.whl", hash = "sha256:38730927ad51beb42ab8dbc5555270bfbe86167ba734265f88bbd799fced1004", size = 89747, upload-time = "2025-04-01T22:55:57.749Z" },
-]
-
-[[package]]
-name = "markdown-it-py"
-version = "3.0.0"
-source = { registry = "https://pypi.org/simple" }
-resolution-markers = [
- "python_full_version < '3.10'",
-]
-dependencies = [
- { name = "mdurl", marker = "python_full_version < '3.10'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596, upload-time = "2023-06-03T06:41:14.443Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528, upload-time = "2023-06-03T06:41:11.019Z" },
-]
-
-[[package]]
-name = "markdown-it-py"
-version = "4.0.0"
-source = { registry = "https://pypi.org/simple" }
-resolution-markers = [
- "python_full_version >= '3.13'",
- "python_full_version >= '3.12.4' and python_full_version < '3.13'",
- "python_full_version >= '3.12' and python_full_version < '3.12.4'",
- "python_full_version == '3.11.*'",
- "python_full_version == '3.10.*'",
-]
-dependencies = [
- { name = "mdurl", marker = "python_full_version >= '3.10'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3", size = 73070, upload-time = "2025-08-11T12:57:52.854Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147", size = 87321, upload-time = "2025-08-11T12:57:51.923Z" },
-]
-
-[[package]]
-name = "markupsafe"
-version = "3.0.2"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537, upload-time = "2024-10-18T15:21:54.129Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/04/90/d08277ce111dd22f77149fd1a5d4653eeb3b3eaacbdfcbae5afb2600eebd/MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8", size = 14357, upload-time = "2024-10-18T15:20:51.44Z" },
- { url = "https://files.pythonhosted.org/packages/04/e1/6e2194baeae0bca1fae6629dc0cbbb968d4d941469cbab11a3872edff374/MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158", size = 12393, upload-time = "2024-10-18T15:20:52.426Z" },
- { url = "https://files.pythonhosted.org/packages/1d/69/35fa85a8ece0a437493dc61ce0bb6d459dcba482c34197e3efc829aa357f/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579", size = 21732, upload-time = "2024-10-18T15:20:53.578Z" },
- { url = "https://files.pythonhosted.org/packages/22/35/137da042dfb4720b638d2937c38a9c2df83fe32d20e8c8f3185dbfef05f7/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d", size = 20866, upload-time = "2024-10-18T15:20:55.06Z" },
- { url = "https://files.pythonhosted.org/packages/29/28/6d029a903727a1b62edb51863232152fd335d602def598dade38996887f0/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb", size = 20964, upload-time = "2024-10-18T15:20:55.906Z" },
- { url = "https://files.pythonhosted.org/packages/cc/cd/07438f95f83e8bc028279909d9c9bd39e24149b0d60053a97b2bc4f8aa51/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b", size = 21977, upload-time = "2024-10-18T15:20:57.189Z" },
- { url = "https://files.pythonhosted.org/packages/29/01/84b57395b4cc062f9c4c55ce0df7d3108ca32397299d9df00fedd9117d3d/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c", size = 21366, upload-time = "2024-10-18T15:20:58.235Z" },
- { url = "https://files.pythonhosted.org/packages/bd/6e/61ebf08d8940553afff20d1fb1ba7294b6f8d279df9fd0c0db911b4bbcfd/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171", size = 21091, upload-time = "2024-10-18T15:20:59.235Z" },
- { url = "https://files.pythonhosted.org/packages/11/23/ffbf53694e8c94ebd1e7e491de185124277964344733c45481f32ede2499/MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50", size = 15065, upload-time = "2024-10-18T15:21:00.307Z" },
- { url = "https://files.pythonhosted.org/packages/44/06/e7175d06dd6e9172d4a69a72592cb3f7a996a9c396eee29082826449bbc3/MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a", size = 15514, upload-time = "2024-10-18T15:21:01.122Z" },
- { url = "https://files.pythonhosted.org/packages/6b/28/bbf83e3f76936960b850435576dd5e67034e200469571be53f69174a2dfd/MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d", size = 14353, upload-time = "2024-10-18T15:21:02.187Z" },
- { url = "https://files.pythonhosted.org/packages/6c/30/316d194b093cde57d448a4c3209f22e3046c5bb2fb0820b118292b334be7/MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93", size = 12392, upload-time = "2024-10-18T15:21:02.941Z" },
- { url = "https://files.pythonhosted.org/packages/f2/96/9cdafba8445d3a53cae530aaf83c38ec64c4d5427d975c974084af5bc5d2/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832", size = 23984, upload-time = "2024-10-18T15:21:03.953Z" },
- { url = "https://files.pythonhosted.org/packages/f1/a4/aefb044a2cd8d7334c8a47d3fb2c9f328ac48cb349468cc31c20b539305f/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84", size = 23120, upload-time = "2024-10-18T15:21:06.495Z" },
- { url = "https://files.pythonhosted.org/packages/8d/21/5e4851379f88f3fad1de30361db501300d4f07bcad047d3cb0449fc51f8c/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca", size = 23032, upload-time = "2024-10-18T15:21:07.295Z" },
- { url = "https://files.pythonhosted.org/packages/00/7b/e92c64e079b2d0d7ddf69899c98842f3f9a60a1ae72657c89ce2655c999d/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798", size = 24057, upload-time = "2024-10-18T15:21:08.073Z" },
- { url = "https://files.pythonhosted.org/packages/f9/ac/46f960ca323037caa0a10662ef97d0a4728e890334fc156b9f9e52bcc4ca/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e", size = 23359, upload-time = "2024-10-18T15:21:09.318Z" },
- { url = "https://files.pythonhosted.org/packages/69/84/83439e16197337b8b14b6a5b9c2105fff81d42c2a7c5b58ac7b62ee2c3b1/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4", size = 23306, upload-time = "2024-10-18T15:21:10.185Z" },
- { url = "https://files.pythonhosted.org/packages/9a/34/a15aa69f01e2181ed8d2b685c0d2f6655d5cca2c4db0ddea775e631918cd/MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d", size = 15094, upload-time = "2024-10-18T15:21:11.005Z" },
- { url = "https://files.pythonhosted.org/packages/da/b8/3a3bd761922d416f3dc5d00bfbed11f66b1ab89a0c2b6e887240a30b0f6b/MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b", size = 15521, upload-time = "2024-10-18T15:21:12.911Z" },
- { 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, upload-time = "2024-10-18T15:21:13.777Z" },
- { 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, upload-time = "2024-10-18T15:21:14.822Z" },
- { 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, upload-time = "2024-10-18T15:21:15.642Z" },
- { 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, upload-time = "2024-10-18T15:21:17.133Z" },
- { 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, upload-time = "2024-10-18T15:21:18.064Z" },
- { 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, upload-time = "2024-10-18T15:21:18.859Z" },
- { 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, upload-time = "2024-10-18T15:21:19.671Z" },
- { 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, upload-time = "2024-10-18T15:21:20.971Z" },
- { url = "https://files.pythonhosted.org/packages/51/ae/97827349d3fcffee7e184bdf7f41cd6b88d9919c80f0263ba7acd1bbcb18/MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30", size = 15097, upload-time = "2024-10-18T15:21:22.646Z" },
- { url = "https://files.pythonhosted.org/packages/c1/80/a61f99dc3a936413c3ee4e1eecac96c0da5ed07ad56fd975f1a9da5bc630/MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87", size = 15601, upload-time = "2024-10-18T15:21:23.499Z" },
- { url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274, upload-time = "2024-10-18T15:21:24.577Z" },
- { url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352, upload-time = "2024-10-18T15:21:25.382Z" },
- { url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122, upload-time = "2024-10-18T15:21:26.199Z" },
- { url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085, upload-time = "2024-10-18T15:21:27.029Z" },
- { url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978, upload-time = "2024-10-18T15:21:27.846Z" },
- { url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208, upload-time = "2024-10-18T15:21:28.744Z" },
- { url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357, upload-time = "2024-10-18T15:21:29.545Z" },
- { url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344, upload-time = "2024-10-18T15:21:30.366Z" },
- { url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101, upload-time = "2024-10-18T15:21:31.207Z" },
- { url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603, upload-time = "2024-10-18T15:21:32.032Z" },
- { url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510, upload-time = "2024-10-18T15:21:33.625Z" },
- { url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486, upload-time = "2024-10-18T15:21:34.611Z" },
- { url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480, upload-time = "2024-10-18T15:21:35.398Z" },
- { url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914, upload-time = "2024-10-18T15:21:36.231Z" },
- { url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796, upload-time = "2024-10-18T15:21:37.073Z" },
- { url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473, upload-time = "2024-10-18T15:21:37.932Z" },
- { url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114, upload-time = "2024-10-18T15:21:39.799Z" },
- { url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098, upload-time = "2024-10-18T15:21:40.813Z" },
- { url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208, upload-time = "2024-10-18T15:21:41.814Z" },
- { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739, upload-time = "2024-10-18T15:21:42.784Z" },
- { url = "https://files.pythonhosted.org/packages/a7/ea/9b1530c3fdeeca613faeb0fb5cbcf2389d816072fab72a71b45749ef6062/MarkupSafe-3.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a", size = 14344, upload-time = "2024-10-18T15:21:43.721Z" },
- { url = "https://files.pythonhosted.org/packages/4b/c2/fbdbfe48848e7112ab05e627e718e854d20192b674952d9042ebd8c9e5de/MarkupSafe-3.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff", size = 12389, upload-time = "2024-10-18T15:21:44.666Z" },
- { url = "https://files.pythonhosted.org/packages/f0/25/7a7c6e4dbd4f867d95d94ca15449e91e52856f6ed1905d58ef1de5e211d0/MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13", size = 21607, upload-time = "2024-10-18T15:21:45.452Z" },
- { url = "https://files.pythonhosted.org/packages/53/8f/f339c98a178f3c1e545622206b40986a4c3307fe39f70ccd3d9df9a9e425/MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144", size = 20728, upload-time = "2024-10-18T15:21:46.295Z" },
- { url = "https://files.pythonhosted.org/packages/1a/03/8496a1a78308456dbd50b23a385c69b41f2e9661c67ea1329849a598a8f9/MarkupSafe-3.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29", size = 20826, upload-time = "2024-10-18T15:21:47.134Z" },
- { url = "https://files.pythonhosted.org/packages/e6/cf/0a490a4bd363048c3022f2f475c8c05582179bb179defcee4766fb3dcc18/MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0", size = 21843, upload-time = "2024-10-18T15:21:48.334Z" },
- { url = "https://files.pythonhosted.org/packages/19/a3/34187a78613920dfd3cdf68ef6ce5e99c4f3417f035694074beb8848cd77/MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0", size = 21219, upload-time = "2024-10-18T15:21:49.587Z" },
- { url = "https://files.pythonhosted.org/packages/17/d8/5811082f85bb88410ad7e452263af048d685669bbbfb7b595e8689152498/MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178", size = 20946, upload-time = "2024-10-18T15:21:50.441Z" },
- { url = "https://files.pythonhosted.org/packages/7c/31/bd635fb5989440d9365c5e3c47556cfea121c7803f5034ac843e8f37c2f2/MarkupSafe-3.0.2-cp39-cp39-win32.whl", hash = "sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f", size = 15063, upload-time = "2024-10-18T15:21:51.385Z" },
- { url = "https://files.pythonhosted.org/packages/b3/73/085399401383ce949f727afec55ec3abd76648d04b9f22e1c0e99cb4bec3/MarkupSafe-3.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a", size = 15506, upload-time = "2024-10-18T15:21:52.974Z" },
-]
-
-[[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, upload-time = "2025-02-03T15:32:25.093Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/34/75/51952c7b2d3873b44a0028b1bd26a25078c18f92f256608e8d1dc61b39fd/marshmallow-3.26.1-py3-none-any.whl", hash = "sha256:3350409f20a70a7e4e11a27661187b77cdcaeb20abca41c1454fe33636bea09c", size = 50878, upload-time = "2025-02-03T15:32:22.295Z" },
-]
-
-[[package]]
-name = "matplotlib"
-version = "3.9.4"
-source = { registry = "https://pypi.org/simple" }
-resolution-markers = [
- "python_full_version < '3.10'",
-]
-dependencies = [
- { name = "contourpy", version = "1.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
- { name = "cycler", marker = "python_full_version < '3.10'" },
- { name = "fonttools", marker = "python_full_version < '3.10'" },
- { name = "importlib-resources", marker = "python_full_version < '3.10'" },
- { name = "kiwisolver", version = "1.4.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
- { name = "numpy", marker = "python_full_version < '3.10'" },
- { name = "packaging", marker = "python_full_version < '3.10'" },
- { name = "pillow", marker = "python_full_version < '3.10'" },
- { name = "pyparsing", marker = "python_full_version < '3.10'" },
- { name = "python-dateutil", marker = "python_full_version < '3.10'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/df/17/1747b4154034befd0ed33b52538f5eb7752d05bb51c5e2a31470c3bc7d52/matplotlib-3.9.4.tar.gz", hash = "sha256:1e00e8be7393cbdc6fedfa8a6fba02cf3e83814b285db1c60b906a023ba41bc3", size = 36106529, upload-time = "2024-12-13T05:56:34.184Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/7e/94/27d2e2c30d54b56c7b764acc1874a909e34d1965a427fc7092bb6a588b63/matplotlib-3.9.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:c5fdd7abfb706dfa8d307af64a87f1a862879ec3cd8d0ec8637458f0885b9c50", size = 7885089, upload-time = "2024-12-13T05:54:24.224Z" },
- { url = "https://files.pythonhosted.org/packages/c6/25/828273307e40a68eb8e9df832b6b2aaad075864fdc1de4b1b81e40b09e48/matplotlib-3.9.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d89bc4e85e40a71d1477780366c27fb7c6494d293e1617788986f74e2a03d7ff", size = 7770600, upload-time = "2024-12-13T05:54:27.214Z" },
- { url = "https://files.pythonhosted.org/packages/f2/65/f841a422ec994da5123368d76b126acf4fc02ea7459b6e37c4891b555b83/matplotlib-3.9.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ddf9f3c26aae695c5daafbf6b94e4c1a30d6cd617ba594bbbded3b33a1fcfa26", size = 8200138, upload-time = "2024-12-13T05:54:29.497Z" },
- { url = "https://files.pythonhosted.org/packages/07/06/272aca07a38804d93b6050813de41ca7ab0e29ba7a9dd098e12037c919a9/matplotlib-3.9.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18ebcf248030173b59a868fda1fe42397253f6698995b55e81e1f57431d85e50", size = 8312711, upload-time = "2024-12-13T05:54:34.396Z" },
- { url = "https://files.pythonhosted.org/packages/98/37/f13e23b233c526b7e27ad61be0a771894a079e0f7494a10d8d81557e0e9a/matplotlib-3.9.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:974896ec43c672ec23f3f8c648981e8bc880ee163146e0312a9b8def2fac66f5", size = 9090622, upload-time = "2024-12-13T05:54:36.808Z" },
- { url = "https://files.pythonhosted.org/packages/4f/8c/b1f5bd2bd70e60f93b1b54c4d5ba7a992312021d0ddddf572f9a1a6d9348/matplotlib-3.9.4-cp310-cp310-win_amd64.whl", hash = "sha256:4598c394ae9711cec135639374e70871fa36b56afae17bdf032a345be552a88d", size = 7828211, upload-time = "2024-12-13T05:54:40.596Z" },
- { url = "https://files.pythonhosted.org/packages/74/4b/65be7959a8fa118a3929b49a842de5b78bb55475236fcf64f3e308ff74a0/matplotlib-3.9.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d4dd29641d9fb8bc4492420c5480398dd40a09afd73aebe4eb9d0071a05fbe0c", size = 7894430, upload-time = "2024-12-13T05:54:44.049Z" },
- { url = "https://files.pythonhosted.org/packages/e9/18/80f70d91896e0a517b4a051c3fd540daa131630fd75e02e250365353b253/matplotlib-3.9.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30e5b22e8bcfb95442bf7d48b0d7f3bdf4a450cbf68986ea45fca3d11ae9d099", size = 7780045, upload-time = "2024-12-13T05:54:46.414Z" },
- { url = "https://files.pythonhosted.org/packages/a2/73/ccb381026e3238c5c25c3609ba4157b2d1a617ec98d65a8b4ee4e1e74d02/matplotlib-3.9.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2bb0030d1d447fd56dcc23b4c64a26e44e898f0416276cac1ebc25522e0ac249", size = 8209906, upload-time = "2024-12-13T05:54:49.459Z" },
- { url = "https://files.pythonhosted.org/packages/ab/33/1648da77b74741c89f5ea95cbf42a291b4b364f2660b316318811404ed97/matplotlib-3.9.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aca90ed222ac3565d2752b83dbb27627480d27662671e4d39da72e97f657a423", size = 8322873, upload-time = "2024-12-13T05:54:53.066Z" },
- { url = "https://files.pythonhosted.org/packages/57/d3/8447ba78bc6593c9044c372d1609f8ea10fb1e071e7a9e0747bea74fc16c/matplotlib-3.9.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a181b2aa2906c608fcae72f977a4a2d76e385578939891b91c2550c39ecf361e", size = 9099566, upload-time = "2024-12-13T05:54:55.522Z" },
- { url = "https://files.pythonhosted.org/packages/23/e1/4f0e237bf349c02ff9d1b6e7109f1a17f745263809b9714a8576dc17752b/matplotlib-3.9.4-cp311-cp311-win_amd64.whl", hash = "sha256:1f6882828231eca17f501c4dcd98a05abb3f03d157fbc0769c6911fe08b6cfd3", size = 7838065, upload-time = "2024-12-13T05:54:58.337Z" },
- { url = "https://files.pythonhosted.org/packages/1a/2b/c918bf6c19d6445d1cefe3d2e42cb740fb997e14ab19d4daeb6a7ab8a157/matplotlib-3.9.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:dfc48d67e6661378a21c2983200a654b72b5c5cdbd5d2cf6e5e1ece860f0cc70", size = 7891131, upload-time = "2024-12-13T05:55:02.837Z" },
- { url = "https://files.pythonhosted.org/packages/c1/e5/b4e8fc601ca302afeeabf45f30e706a445c7979a180e3a978b78b2b681a4/matplotlib-3.9.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:47aef0fab8332d02d68e786eba8113ffd6f862182ea2999379dec9e237b7e483", size = 7776365, upload-time = "2024-12-13T05:55:05.158Z" },
- { url = "https://files.pythonhosted.org/packages/99/06/b991886c506506476e5d83625c5970c656a491b9f80161458fed94597808/matplotlib-3.9.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fba1f52c6b7dc764097f52fd9ab627b90db452c9feb653a59945de16752e965f", size = 8200707, upload-time = "2024-12-13T05:55:09.48Z" },
- { url = "https://files.pythonhosted.org/packages/c3/e2/556b627498cb27e61026f2d1ba86a78ad1b836fef0996bef5440e8bc9559/matplotlib-3.9.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:173ac3748acaac21afcc3fa1633924609ba1b87749006bc25051c52c422a5d00", size = 8313761, upload-time = "2024-12-13T05:55:12.95Z" },
- { url = "https://files.pythonhosted.org/packages/58/ff/165af33ec766ff818306ea88e91f9f60d2a6ed543be1eb122a98acbf3b0d/matplotlib-3.9.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:320edea0cadc07007765e33f878b13b3738ffa9745c5f707705692df70ffe0e0", size = 9095284, upload-time = "2024-12-13T05:55:16.199Z" },
- { url = "https://files.pythonhosted.org/packages/9f/8b/3d0c7a002db3b1ed702731c2a9a06d78d035f1f2fb0fb936a8e43cc1e9f4/matplotlib-3.9.4-cp312-cp312-win_amd64.whl", hash = "sha256:a4a4cfc82330b27042a7169533da7991e8789d180dd5b3daeaee57d75cd5a03b", size = 7841160, upload-time = "2024-12-13T05:55:19.991Z" },
- { url = "https://files.pythonhosted.org/packages/49/b1/999f89a7556d101b23a2f0b54f1b6e140d73f56804da1398f2f0bc0924bc/matplotlib-3.9.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:37eeffeeca3c940985b80f5b9a7b95ea35671e0e7405001f249848d2b62351b6", size = 7891499, upload-time = "2024-12-13T05:55:22.142Z" },
- { url = "https://files.pythonhosted.org/packages/87/7b/06a32b13a684977653396a1bfcd34d4e7539c5d55c8cbfaa8ae04d47e4a9/matplotlib-3.9.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3e7465ac859ee4abcb0d836137cd8414e7bb7ad330d905abced457217d4f0f45", size = 7776802, upload-time = "2024-12-13T05:55:25.947Z" },
- { url = "https://files.pythonhosted.org/packages/65/87/ac498451aff739e515891bbb92e566f3c7ef31891aaa878402a71f9b0910/matplotlib-3.9.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4c12302c34afa0cf061bea23b331e747e5e554b0fa595c96e01c7b75bc3b858", size = 8200802, upload-time = "2024-12-13T05:55:28.461Z" },
- { url = "https://files.pythonhosted.org/packages/f8/6b/9eb761c00e1cb838f6c92e5f25dcda3f56a87a52f6cb8fdfa561e6cf6a13/matplotlib-3.9.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2b8c97917f21b75e72108b97707ba3d48f171541a74aa2a56df7a40626bafc64", size = 8313880, upload-time = "2024-12-13T05:55:30.965Z" },
- { url = "https://files.pythonhosted.org/packages/d7/a2/c8eaa600e2085eec7e38cbbcc58a30fc78f8224939d31d3152bdafc01fd1/matplotlib-3.9.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0229803bd7e19271b03cb09f27db76c918c467aa4ce2ae168171bc67c3f508df", size = 9094637, upload-time = "2024-12-13T05:55:33.701Z" },
- { url = "https://files.pythonhosted.org/packages/71/1f/c6e1daea55b7bfeb3d84c6cb1abc449f6a02b181e7e2a5e4db34c3afb793/matplotlib-3.9.4-cp313-cp313-win_amd64.whl", hash = "sha256:7c0d8ef442ebf56ff5e206f8083d08252ee738e04f3dc88ea882853a05488799", size = 7841311, upload-time = "2024-12-13T05:55:36.737Z" },
- { url = "https://files.pythonhosted.org/packages/c0/3a/2757d3f7d388b14dd48f5a83bea65b6d69f000e86b8f28f74d86e0d375bd/matplotlib-3.9.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a04c3b00066a688834356d196136349cb32f5e1003c55ac419e91585168b88fb", size = 7919989, upload-time = "2024-12-13T05:55:39.024Z" },
- { url = "https://files.pythonhosted.org/packages/24/28/f5077c79a4f521589a37fe1062d6a6ea3534e068213f7357e7cfffc2e17a/matplotlib-3.9.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:04c519587f6c210626741a1e9a68eefc05966ede24205db8982841826af5871a", size = 7809417, upload-time = "2024-12-13T05:55:42.412Z" },
- { url = "https://files.pythonhosted.org/packages/36/c8/c523fd2963156692916a8eb7d4069084cf729359f7955cf09075deddfeaf/matplotlib-3.9.4-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:308afbf1a228b8b525fcd5cec17f246bbbb63b175a3ef6eb7b4d33287ca0cf0c", size = 8226258, upload-time = "2024-12-13T05:55:47.259Z" },
- { url = "https://files.pythonhosted.org/packages/f6/88/499bf4b8fa9349b6f5c0cf4cead0ebe5da9d67769129f1b5651e5ac51fbc/matplotlib-3.9.4-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ddb3b02246ddcffd3ce98e88fed5b238bc5faff10dbbaa42090ea13241d15764", size = 8335849, upload-time = "2024-12-13T05:55:49.763Z" },
- { url = "https://files.pythonhosted.org/packages/b8/9f/20a4156b9726188646a030774ee337d5ff695a965be45ce4dbcb9312c170/matplotlib-3.9.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8a75287e9cb9eee48cb79ec1d806f75b29c0fde978cb7223a1f4c5848d696041", size = 9102152, upload-time = "2024-12-13T05:55:51.997Z" },
- { url = "https://files.pythonhosted.org/packages/10/11/237f9c3a4e8d810b1759b67ff2da7c32c04f9c80aa475e7beb36ed43a8fb/matplotlib-3.9.4-cp313-cp313t-win_amd64.whl", hash = "sha256:488deb7af140f0ba86da003e66e10d55ff915e152c78b4b66d231638400b1965", size = 7896987, upload-time = "2024-12-13T05:55:55.941Z" },
- { url = "https://files.pythonhosted.org/packages/56/eb/501b465c9fef28f158e414ea3a417913dc2ac748564c7ed41535f23445b4/matplotlib-3.9.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:3c3724d89a387ddf78ff88d2a30ca78ac2b4c89cf37f2db4bd453c34799e933c", size = 7885919, upload-time = "2024-12-13T05:55:59.66Z" },
- { url = "https://files.pythonhosted.org/packages/da/36/236fbd868b6c91309a5206bd90c3f881f4f44b2d997cd1d6239ef652f878/matplotlib-3.9.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d5f0a8430ffe23d7e32cfd86445864ccad141797f7d25b7c41759a5b5d17cfd7", size = 7771486, upload-time = "2024-12-13T05:56:04.264Z" },
- { url = "https://files.pythonhosted.org/packages/e0/4b/105caf2d54d5ed11d9f4335398f5103001a03515f2126c936a752ccf1461/matplotlib-3.9.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6bb0141a21aef3b64b633dc4d16cbd5fc538b727e4958be82a0e1c92a234160e", size = 8201838, upload-time = "2024-12-13T05:56:06.792Z" },
- { url = "https://files.pythonhosted.org/packages/5d/a7/bb01188fb4013d34d274caf44a2f8091255b0497438e8b6c0a7c1710c692/matplotlib-3.9.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:57aa235109e9eed52e2c2949db17da185383fa71083c00c6c143a60e07e0888c", size = 8314492, upload-time = "2024-12-13T05:56:09.964Z" },
- { url = "https://files.pythonhosted.org/packages/33/19/02e1a37f7141fc605b193e927d0a9cdf9dc124a20b9e68793f4ffea19695/matplotlib-3.9.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:b18c600061477ccfdd1e6fd050c33d8be82431700f3452b297a56d9ed7037abb", size = 9092500, upload-time = "2024-12-13T05:56:13.55Z" },
- { url = "https://files.pythonhosted.org/packages/57/68/c2feb4667adbf882ffa4b3e0ac9967f848980d9f8b5bebd86644aa67ce6a/matplotlib-3.9.4-cp39-cp39-win_amd64.whl", hash = "sha256:ef5f2d1b67d2d2145ff75e10f8c008bfbf71d45137c4b648c87193e7dd053eac", size = 7822962, upload-time = "2024-12-13T05:56:16.358Z" },
- { url = "https://files.pythonhosted.org/packages/0c/22/2ef6a364cd3f565442b0b055e0599744f1e4314ec7326cdaaa48a4d864d7/matplotlib-3.9.4-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:44e0ed786d769d85bc787b0606a53f2d8d2d1d3c8a2608237365e9121c1a338c", size = 7877995, upload-time = "2024-12-13T05:56:18.805Z" },
- { url = "https://files.pythonhosted.org/packages/87/b8/2737456e566e9f4d94ae76b8aa0d953d9acb847714f9a7ad80184474f5be/matplotlib-3.9.4-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:09debb9ce941eb23ecdbe7eab972b1c3e0276dcf01688073faff7b0f61d6c6ca", size = 7769300, upload-time = "2024-12-13T05:56:21.315Z" },
- { url = "https://files.pythonhosted.org/packages/b2/1f/e709c6ec7b5321e6568769baa288c7178e60a93a9da9e682b39450da0e29/matplotlib-3.9.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bcc53cf157a657bfd03afab14774d54ba73aa84d42cfe2480c91bd94873952db", size = 8313423, upload-time = "2024-12-13T05:56:26.719Z" },
- { url = "https://files.pythonhosted.org/packages/5e/b6/5a1f868782cd13f053a679984e222007ecff654a9bfbac6b27a65f4eeb05/matplotlib-3.9.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:ad45da51be7ad02387801fd154ef74d942f49fe3fcd26a64c94842ba7ec0d865", size = 7854624, upload-time = "2024-12-13T05:56:29.359Z" },
-]
-
-[[package]]
-name = "matplotlib"
-version = "3.10.6"
-source = { registry = "https://pypi.org/simple" }
-resolution-markers = [
- "python_full_version >= '3.13'",
- "python_full_version >= '3.12.4' and python_full_version < '3.13'",
- "python_full_version >= '3.12' and python_full_version < '3.12.4'",
- "python_full_version == '3.11.*'",
- "python_full_version == '3.10.*'",
-]
-dependencies = [
- { name = "contourpy", version = "1.3.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" },
- { name = "contourpy", version = "1.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
- { name = "cycler", marker = "python_full_version >= '3.10'" },
- { name = "fonttools", marker = "python_full_version >= '3.10'" },
- { name = "kiwisolver", version = "1.4.9", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
- { name = "numpy", marker = "python_full_version >= '3.10'" },
- { name = "packaging", marker = "python_full_version >= '3.10'" },
- { name = "pillow", marker = "python_full_version >= '3.10'" },
- { name = "pyparsing", marker = "python_full_version >= '3.10'" },
- { name = "python-dateutil", marker = "python_full_version >= '3.10'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/a0/59/c3e6453a9676ffba145309a73c462bb407f4400de7de3f2b41af70720a3c/matplotlib-3.10.6.tar.gz", hash = "sha256:ec01b645840dd1996df21ee37f208cd8ba57644779fa20464010638013d3203c", size = 34804264, upload-time = "2025-08-30T00:14:25.137Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/da/dc/ab89f7a5efd0cbaaebf2c3cf1881f4cba20c8925bb43f64511059df76895/matplotlib-3.10.6-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:bc7316c306d97463a9866b89d5cc217824e799fa0de346c8f68f4f3d27c8693d", size = 8247159, upload-time = "2025-08-30T00:12:30.507Z" },
- { url = "https://files.pythonhosted.org/packages/30/a5/ddaee1a383ab28174093644fff7438eddb87bf8dbd58f7b85f5cdd6b2485/matplotlib-3.10.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d00932b0d160ef03f59f9c0e16d1e3ac89646f7785165ce6ad40c842db16cc2e", size = 8108011, upload-time = "2025-08-30T00:12:32.771Z" },
- { url = "https://files.pythonhosted.org/packages/75/5b/a53f69bb0522db352b1135bb57cd9fe00fd7252072409392d991d3a755d0/matplotlib-3.10.6-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8fa4c43d6bfdbfec09c733bca8667de11bfa4970e8324c471f3a3632a0301c15", size = 8680518, upload-time = "2025-08-30T00:12:34.387Z" },
- { url = "https://files.pythonhosted.org/packages/5f/31/e059ddce95f68819b005a2d6820b2d6ed0307827a04598891f00649bed2d/matplotlib-3.10.6-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ea117a9c1627acaa04dbf36265691921b999cbf515a015298e54e1a12c3af837", size = 9514997, upload-time = "2025-08-30T00:12:36.272Z" },
- { url = "https://files.pythonhosted.org/packages/66/d5/28b408a7c0f07b41577ee27e4454fe329e78ca21fe46ae7a27d279165fb5/matplotlib-3.10.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:08fc803293b4e1694ee325896030de97f74c141ccff0be886bb5915269247676", size = 9566440, upload-time = "2025-08-30T00:12:41.675Z" },
- { url = "https://files.pythonhosted.org/packages/2d/99/8325b3386b479b1d182ab1a7fd588fd393ff00a99dc04b7cf7d06668cf0f/matplotlib-3.10.6-cp310-cp310-win_amd64.whl", hash = "sha256:2adf92d9b7527fbfb8818e050260f0ebaa460f79d61546374ce73506c9421d09", size = 8108186, upload-time = "2025-08-30T00:12:43.621Z" },
- { url = "https://files.pythonhosted.org/packages/80/d6/5d3665aa44c49005aaacaa68ddea6fcb27345961cd538a98bb0177934ede/matplotlib-3.10.6-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:905b60d1cb0ee604ce65b297b61cf8be9f4e6cfecf95a3fe1c388b5266bc8f4f", size = 8257527, upload-time = "2025-08-30T00:12:45.31Z" },
- { url = "https://files.pythonhosted.org/packages/8c/af/30ddefe19ca67eebd70047dabf50f899eaff6f3c5e6a1a7edaecaf63f794/matplotlib-3.10.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7bac38d816637343e53d7185d0c66677ff30ffb131044a81898b5792c956ba76", size = 8119583, upload-time = "2025-08-30T00:12:47.236Z" },
- { url = "https://files.pythonhosted.org/packages/d3/29/4a8650a3dcae97fa4f375d46efcb25920d67b512186f8a6788b896062a81/matplotlib-3.10.6-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:942a8de2b5bfff1de31d95722f702e2966b8a7e31f4e68f7cd963c7cd8861cf6", size = 8692682, upload-time = "2025-08-30T00:12:48.781Z" },
- { url = "https://files.pythonhosted.org/packages/aa/d3/b793b9cb061cfd5d42ff0f69d1822f8d5dbc94e004618e48a97a8373179a/matplotlib-3.10.6-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a3276c85370bc0dfca051ec65c5817d1e0f8f5ce1b7787528ec8ed2d524bbc2f", size = 9521065, upload-time = "2025-08-30T00:12:50.602Z" },
- { url = "https://files.pythonhosted.org/packages/f7/c5/53de5629f223c1c66668d46ac2621961970d21916a4bc3862b174eb2a88f/matplotlib-3.10.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9df5851b219225731f564e4b9e7f2ac1e13c9e6481f941b5631a0f8e2d9387ce", size = 9576888, upload-time = "2025-08-30T00:12:52.92Z" },
- { url = "https://files.pythonhosted.org/packages/fc/8e/0a18d6d7d2d0a2e66585032a760d13662e5250c784d53ad50434e9560991/matplotlib-3.10.6-cp311-cp311-win_amd64.whl", hash = "sha256:abb5d9478625dd9c9eb51a06d39aae71eda749ae9b3138afb23eb38824026c7e", size = 8115158, upload-time = "2025-08-30T00:12:54.863Z" },
- { url = "https://files.pythonhosted.org/packages/07/b3/1a5107bb66c261e23b9338070702597a2d374e5aa7004b7adfc754fbed02/matplotlib-3.10.6-cp311-cp311-win_arm64.whl", hash = "sha256:886f989ccfae63659183173bb3fced7fd65e9eb793c3cc21c273add368536951", size = 7992444, upload-time = "2025-08-30T00:12:57.067Z" },
- { url = "https://files.pythonhosted.org/packages/ea/1a/7042f7430055d567cc3257ac409fcf608599ab27459457f13772c2d9778b/matplotlib-3.10.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:31ca662df6a80bd426f871105fdd69db7543e28e73a9f2afe80de7e531eb2347", size = 8272404, upload-time = "2025-08-30T00:12:59.112Z" },
- { url = "https://files.pythonhosted.org/packages/a9/5d/1d5f33f5b43f4f9e69e6a5fe1fb9090936ae7bc8e2ff6158e7a76542633b/matplotlib-3.10.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1678bb61d897bb4ac4757b5ecfb02bfb3fddf7f808000fb81e09c510712fda75", size = 8128262, upload-time = "2025-08-30T00:13:01.141Z" },
- { url = "https://files.pythonhosted.org/packages/67/c3/135fdbbbf84e0979712df58e5e22b4f257b3f5e52a3c4aacf1b8abec0d09/matplotlib-3.10.6-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:56cd2d20842f58c03d2d6e6c1f1cf5548ad6f66b91e1e48f814e4fb5abd1cb95", size = 8697008, upload-time = "2025-08-30T00:13:03.24Z" },
- { url = "https://files.pythonhosted.org/packages/9c/be/c443ea428fb2488a3ea7608714b1bd85a82738c45da21b447dc49e2f8e5d/matplotlib-3.10.6-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:662df55604a2f9a45435566d6e2660e41efe83cd94f4288dfbf1e6d1eae4b0bb", size = 9530166, upload-time = "2025-08-30T00:13:05.951Z" },
- { url = "https://files.pythonhosted.org/packages/a9/35/48441422b044d74034aea2a3e0d1a49023f12150ebc58f16600132b9bbaf/matplotlib-3.10.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:08f141d55148cd1fc870c3387d70ca4df16dee10e909b3b038782bd4bda6ea07", size = 9593105, upload-time = "2025-08-30T00:13:08.356Z" },
- { url = "https://files.pythonhosted.org/packages/45/c3/994ef20eb4154ab84cc08d033834555319e4af970165e6c8894050af0b3c/matplotlib-3.10.6-cp312-cp312-win_amd64.whl", hash = "sha256:590f5925c2d650b5c9d813c5b3b5fc53f2929c3f8ef463e4ecfa7e052044fb2b", size = 8122784, upload-time = "2025-08-30T00:13:10.367Z" },
- { url = "https://files.pythonhosted.org/packages/57/b8/5c85d9ae0e40f04e71bedb053aada5d6bab1f9b5399a0937afb5d6b02d98/matplotlib-3.10.6-cp312-cp312-win_arm64.whl", hash = "sha256:f44c8d264a71609c79a78d50349e724f5d5fc3684ead7c2a473665ee63d868aa", size = 7992823, upload-time = "2025-08-30T00:13:12.24Z" },
- { url = "https://files.pythonhosted.org/packages/a0/db/18380e788bb837e724358287b08e223b32bc8dccb3b0c12fa8ca20bc7f3b/matplotlib-3.10.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:819e409653c1106c8deaf62e6de6b8611449c2cd9939acb0d7d4e57a3d95cc7a", size = 8273231, upload-time = "2025-08-30T00:13:13.881Z" },
- { url = "https://files.pythonhosted.org/packages/d3/0f/38dd49445b297e0d4f12a322c30779df0d43cb5873c7847df8a82e82ec67/matplotlib-3.10.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:59c8ac8382fefb9cb71308dde16a7c487432f5255d8f1fd32473523abecfecdf", size = 8128730, upload-time = "2025-08-30T00:13:15.556Z" },
- { url = "https://files.pythonhosted.org/packages/e5/b8/9eea6630198cb303d131d95d285a024b3b8645b1763a2916fddb44ca8760/matplotlib-3.10.6-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:84e82d9e0fd70c70bc55739defbd8055c54300750cbacf4740c9673a24d6933a", size = 8698539, upload-time = "2025-08-30T00:13:17.297Z" },
- { url = "https://files.pythonhosted.org/packages/71/34/44c7b1f075e1ea398f88aeabcc2907c01b9cc99e2afd560c1d49845a1227/matplotlib-3.10.6-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:25f7a3eb42d6c1c56e89eacd495661fc815ffc08d9da750bca766771c0fd9110", size = 9529702, upload-time = "2025-08-30T00:13:19.248Z" },
- { url = "https://files.pythonhosted.org/packages/b5/7f/e5c2dc9950c7facaf8b461858d1b92c09dd0cf174fe14e21953b3dda06f7/matplotlib-3.10.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f9c862d91ec0b7842920a4cfdaaec29662195301914ea54c33e01f1a28d014b2", size = 9593742, upload-time = "2025-08-30T00:13:21.181Z" },
- { url = "https://files.pythonhosted.org/packages/ff/1d/70c28528794f6410ee2856cd729fa1f1756498b8d3126443b0a94e1a8695/matplotlib-3.10.6-cp313-cp313-win_amd64.whl", hash = "sha256:1b53bd6337eba483e2e7d29c5ab10eee644bc3a2491ec67cc55f7b44583ffb18", size = 8122753, upload-time = "2025-08-30T00:13:23.44Z" },
- { url = "https://files.pythonhosted.org/packages/e8/74/0e1670501fc7d02d981564caf7c4df42974464625935424ca9654040077c/matplotlib-3.10.6-cp313-cp313-win_arm64.whl", hash = "sha256:cbd5eb50b7058b2892ce45c2f4e92557f395c9991f5c886d1bb74a1582e70fd6", size = 7992973, upload-time = "2025-08-30T00:13:26.632Z" },
- { url = "https://files.pythonhosted.org/packages/b1/4e/60780e631d73b6b02bd7239f89c451a72970e5e7ec34f621eda55cd9a445/matplotlib-3.10.6-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:acc86dd6e0e695c095001a7fccff158c49e45e0758fdf5dcdbb0103318b59c9f", size = 8316869, upload-time = "2025-08-30T00:13:28.262Z" },
- { url = "https://files.pythonhosted.org/packages/f8/15/baa662374a579413210fc2115d40c503b7360a08e9cc254aa0d97d34b0c1/matplotlib-3.10.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e228cd2ffb8f88b7d0b29e37f68ca9aaf83e33821f24a5ccc4f082dd8396bc27", size = 8178240, upload-time = "2025-08-30T00:13:30.007Z" },
- { url = "https://files.pythonhosted.org/packages/c6/3f/3c38e78d2aafdb8829fcd0857d25aaf9e7dd2dfcf7ec742765b585774931/matplotlib-3.10.6-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:658bc91894adeab669cf4bb4a186d049948262987e80f0857216387d7435d833", size = 8711719, upload-time = "2025-08-30T00:13:31.72Z" },
- { url = "https://files.pythonhosted.org/packages/96/4b/2ec2bbf8cefaa53207cc56118d1fa8a0f9b80642713ea9390235d331ede4/matplotlib-3.10.6-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8913b7474f6dd83ac444c9459c91f7f0f2859e839f41d642691b104e0af056aa", size = 9541422, upload-time = "2025-08-30T00:13:33.611Z" },
- { url = "https://files.pythonhosted.org/packages/83/7d/40255e89b3ef11c7871020563b2dd85f6cb1b4eff17c0f62b6eb14c8fa80/matplotlib-3.10.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:091cea22e059b89f6d7d1a18e2c33a7376c26eee60e401d92a4d6726c4e12706", size = 9594068, upload-time = "2025-08-30T00:13:35.833Z" },
- { url = "https://files.pythonhosted.org/packages/f0/a9/0213748d69dc842537a113493e1c27daf9f96bd7cc316f933dc8ec4de985/matplotlib-3.10.6-cp313-cp313t-win_amd64.whl", hash = "sha256:491e25e02a23d7207629d942c666924a6b61e007a48177fdd231a0097b7f507e", size = 8200100, upload-time = "2025-08-30T00:13:37.668Z" },
- { url = "https://files.pythonhosted.org/packages/be/15/79f9988066ce40b8a6f1759a934ea0cde8dc4adc2262255ee1bc98de6ad0/matplotlib-3.10.6-cp313-cp313t-win_arm64.whl", hash = "sha256:3d80d60d4e54cda462e2cd9a086d85cd9f20943ead92f575ce86885a43a565d5", size = 8042142, upload-time = "2025-08-30T00:13:39.426Z" },
- { url = "https://files.pythonhosted.org/packages/7c/58/e7b6d292beae6fb4283ca6fb7fa47d7c944a68062d6238c07b497dd35493/matplotlib-3.10.6-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:70aaf890ce1d0efd482df969b28a5b30ea0b891224bb315810a3940f67182899", size = 8273802, upload-time = "2025-08-30T00:13:41.006Z" },
- { url = "https://files.pythonhosted.org/packages/9f/f6/7882d05aba16a8cdd594fb9a03a9d3cca751dbb6816adf7b102945522ee9/matplotlib-3.10.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1565aae810ab79cb72e402b22facfa6501365e73ebab70a0fdfb98488d2c3c0c", size = 8131365, upload-time = "2025-08-30T00:13:42.664Z" },
- { url = "https://files.pythonhosted.org/packages/94/bf/ff32f6ed76e78514e98775a53715eca4804b12bdcf35902cdd1cf759d324/matplotlib-3.10.6-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f3b23315a01981689aa4e1a179dbf6ef9fbd17143c3eea77548c2ecfb0499438", size = 9533961, upload-time = "2025-08-30T00:13:44.372Z" },
- { url = "https://files.pythonhosted.org/packages/fe/c3/6bf88c2fc2da7708a2ff8d2eeb5d68943130f50e636d5d3dcf9d4252e971/matplotlib-3.10.6-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:30fdd37edf41a4e6785f9b37969de57aea770696cb637d9946eb37470c94a453", size = 9804262, upload-time = "2025-08-30T00:13:46.614Z" },
- { url = "https://files.pythonhosted.org/packages/0f/7a/e05e6d9446d2d577b459427ad060cd2de5742d0e435db3191fea4fcc7e8b/matplotlib-3.10.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:bc31e693da1c08012c764b053e702c1855378e04102238e6a5ee6a7117c53a47", size = 9595508, upload-time = "2025-08-30T00:13:48.731Z" },
- { url = "https://files.pythonhosted.org/packages/39/fb/af09c463ced80b801629fd73b96f726c9f6124c3603aa2e480a061d6705b/matplotlib-3.10.6-cp314-cp314-win_amd64.whl", hash = "sha256:05be9bdaa8b242bc6ff96330d18c52f1fc59c6fb3a4dd411d953d67e7e1baf98", size = 8252742, upload-time = "2025-08-30T00:13:50.539Z" },
- { url = "https://files.pythonhosted.org/packages/b1/f9/b682f6db9396d9ab8f050c0a3bfbb5f14fb0f6518f08507c04cc02f8f229/matplotlib-3.10.6-cp314-cp314-win_arm64.whl", hash = "sha256:f56a0d1ab05d34c628592435781d185cd99630bdfd76822cd686fb5a0aecd43a", size = 8124237, upload-time = "2025-08-30T00:13:54.3Z" },
- { url = "https://files.pythonhosted.org/packages/b5/d2/b69b4a0923a3c05ab90527c60fdec899ee21ca23ede7f0fb818e6620d6f2/matplotlib-3.10.6-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:94f0b4cacb23763b64b5dace50d5b7bfe98710fed5f0cef5c08135a03399d98b", size = 8316956, upload-time = "2025-08-30T00:13:55.932Z" },
- { url = "https://files.pythonhosted.org/packages/28/e9/dc427b6f16457ffaeecb2fc4abf91e5adb8827861b869c7a7a6d1836fa73/matplotlib-3.10.6-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:cc332891306b9fb39462673d8225d1b824c89783fee82840a709f96714f17a5c", size = 8178260, upload-time = "2025-08-30T00:14:00.942Z" },
- { url = "https://files.pythonhosted.org/packages/c4/89/1fbd5ad611802c34d1c7ad04607e64a1350b7fb9c567c4ec2c19e066ed35/matplotlib-3.10.6-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee1d607b3fb1590deb04b69f02ea1d53ed0b0bf75b2b1a5745f269afcbd3cdd3", size = 9541422, upload-time = "2025-08-30T00:14:02.664Z" },
- { url = "https://files.pythonhosted.org/packages/b0/3b/65fec8716025b22c1d72d5a82ea079934c76a547696eaa55be6866bc89b1/matplotlib-3.10.6-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:376a624a218116461696b27b2bbf7a8945053e6d799f6502fc03226d077807bf", size = 9803678, upload-time = "2025-08-30T00:14:04.741Z" },
- { url = "https://files.pythonhosted.org/packages/c7/b0/40fb2b3a1ab9381bb39a952e8390357c8be3bdadcf6d5055d9c31e1b35ae/matplotlib-3.10.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:83847b47f6524c34b4f2d3ce726bb0541c48c8e7692729865c3df75bfa0f495a", size = 9594077, upload-time = "2025-08-30T00:14:07.012Z" },
- { url = "https://files.pythonhosted.org/packages/76/34/c4b71b69edf5b06e635eee1ed10bfc73cf8df058b66e63e30e6a55e231d5/matplotlib-3.10.6-cp314-cp314t-win_amd64.whl", hash = "sha256:c7e0518e0d223683532a07f4b512e2e0729b62674f1b3a1a69869f98e6b1c7e3", size = 8342822, upload-time = "2025-08-30T00:14:09.041Z" },
- { url = "https://files.pythonhosted.org/packages/e8/62/aeabeef1a842b6226a30d49dd13e8a7a1e81e9ec98212c0b5169f0a12d83/matplotlib-3.10.6-cp314-cp314t-win_arm64.whl", hash = "sha256:4dd83e029f5b4801eeb87c64efd80e732452781c16a9cf7415b7b63ec8f374d7", size = 8172588, upload-time = "2025-08-30T00:14:11.166Z" },
- { url = "https://files.pythonhosted.org/packages/17/6f/2551e45bea2938e0363ccdd54fa08dae7605ce782d4332497d31a7b97672/matplotlib-3.10.6-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:13fcd07ccf17e354398358e0307a1f53f5325dca22982556ddb9c52837b5af41", size = 8241220, upload-time = "2025-08-30T00:14:12.888Z" },
- { url = "https://files.pythonhosted.org/packages/54/7e/0f4c6e8b98105fdb162a4efde011af204ca47d7c05d735aff480ebfead1b/matplotlib-3.10.6-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:470fc846d59d1406e34fa4c32ba371039cd12c2fe86801159a965956f2575bd1", size = 8104624, upload-time = "2025-08-30T00:14:14.511Z" },
- { url = "https://files.pythonhosted.org/packages/27/27/c29696702b9317a6ade1ba6f8861e02d7423f18501729203d7a80b686f23/matplotlib-3.10.6-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f7173f8551b88f4ef810a94adae3128c2530e0d07529f7141be7f8d8c365f051", size = 8682271, upload-time = "2025-08-30T00:14:17.273Z" },
- { url = "https://files.pythonhosted.org/packages/12/bb/02c35a51484aae5f49bd29f091286e7af5f3f677a9736c58a92b3c78baeb/matplotlib-3.10.6-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:f2d684c3204fa62421bbf770ddfebc6b50130f9cad65531eeba19236d73bb488", size = 8252296, upload-time = "2025-08-30T00:14:19.49Z" },
- { url = "https://files.pythonhosted.org/packages/7d/85/41701e3092005aee9a2445f5ee3904d9dbd4a7df7a45905ffef29b7ef098/matplotlib-3.10.6-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:6f4a69196e663a41d12a728fab8751177215357906436804217d6d9cf0d4d6cf", size = 8116749, upload-time = "2025-08-30T00:14:21.344Z" },
- { url = "https://files.pythonhosted.org/packages/16/53/8d8fa0ea32a8c8239e04d022f6c059ee5e1b77517769feccd50f1df43d6d/matplotlib-3.10.6-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d6ca6ef03dfd269f4ead566ec6f3fb9becf8dab146fb999022ed85ee9f6b3eb", size = 8693933, upload-time = "2025-08-30T00:14:22.942Z" },
-]
-
-[[package]]
-name = "matplotlib-inline"
-version = "0.1.7"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "traitlets" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/99/5b/a36a337438a14116b16480db471ad061c36c3694df7c2084a0da7ba538b7/matplotlib_inline-0.1.7.tar.gz", hash = "sha256:8423b23ec666be3d16e16b60bdd8ac4e86e840ebd1dd11a30b9f117f2fa0ab90", size = 8159, upload-time = "2024-04-15T13:44:44.803Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/8f/8e/9ad090d3553c280a8060fbf6e24dc1c0c29704ee7d1c372f0c174aa59285/matplotlib_inline-0.1.7-py3-none-any.whl", hash = "sha256:df192d39a4ff8f21b1895d72e6a13f5fcc5099f00fa84384e0ea28c2cc0653ca", size = 9899, upload-time = "2024-04-15T13:44:43.265Z" },
-]
-
-[[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, upload-time = "2022-08-14T12:40:10.846Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" },
-]
-
-[[package]]
-name = "mixpanel"
-version = "4.11.1"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "requests" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/41/d9/9d02602a65d6d6874ee938e66beeefa52689f52a9ffd19ceb3fed501a123/mixpanel-4.11.1.tar.gz", hash = "sha256:96440d36c30ce9a845141302b6249ac1c6dccc097ce20d480100c6a1041ce78e", size = 11976, upload-time = "2025-08-26T01:17:09.053Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/37/de/1de316b634b0ac5eafab086d6a6bd60b69e3ff7df1634bd3996a772db4f8/mixpanel-4.11.1-py3-none-any.whl", hash = "sha256:130633fb7d65f1fea10a9b6a6d3b8239121f2039ff2280ceb97cb9a73a711f29", size = 10026, upload-time = "2025-08-26T01:17:07.125Z" },
-]
-
-[[package]]
-name = "mpmath"
-version = "1.3.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106, upload-time = "2023-03-07T16:47:11.061Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198, upload-time = "2023-03-07T16:47:09.197Z" },
-]
-
-[[package]]
-name = "multidict"
-version = "6.6.4"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "typing-extensions", marker = "python_full_version < '3.11'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/69/7f/0652e6ed47ab288e3756ea9c0df8b14950781184d4bd7883f4d87dd41245/multidict-6.6.4.tar.gz", hash = "sha256:d2d4e4787672911b48350df02ed3fa3fffdc2f2e8ca06dd6afdf34189b76a9dd", size = 101843, upload-time = "2025-08-11T12:08:48.217Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/eb/6b/86f353088c1358e76fd30b0146947fddecee812703b604ee901e85cd2a80/multidict-6.6.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b8aa6f0bd8125ddd04a6593437bad6a7e70f300ff4180a531654aa2ab3f6d58f", size = 77054, upload-time = "2025-08-11T12:06:02.99Z" },
- { url = "https://files.pythonhosted.org/packages/19/5d/c01dc3d3788bb877bd7f5753ea6eb23c1beeca8044902a8f5bfb54430f63/multidict-6.6.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b9e5853bbd7264baca42ffc53391b490d65fe62849bf2c690fa3f6273dbcd0cb", size = 44914, upload-time = "2025-08-11T12:06:05.264Z" },
- { url = "https://files.pythonhosted.org/packages/46/44/964dae19ea42f7d3e166474d8205f14bb811020e28bc423d46123ddda763/multidict-6.6.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0af5f9dee472371e36d6ae38bde009bd8ce65ac7335f55dcc240379d7bed1495", size = 44601, upload-time = "2025-08-11T12:06:06.627Z" },
- { url = "https://files.pythonhosted.org/packages/31/20/0616348a1dfb36cb2ab33fc9521de1f27235a397bf3f59338e583afadd17/multidict-6.6.4-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:d24f351e4d759f5054b641c81e8291e5d122af0fca5c72454ff77f7cbe492de8", size = 224821, upload-time = "2025-08-11T12:06:08.06Z" },
- { url = "https://files.pythonhosted.org/packages/14/26/5d8923c69c110ff51861af05bd27ca6783011b96725d59ccae6d9daeb627/multidict-6.6.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:db6a3810eec08280a172a6cd541ff4a5f6a97b161d93ec94e6c4018917deb6b7", size = 242608, upload-time = "2025-08-11T12:06:09.697Z" },
- { url = "https://files.pythonhosted.org/packages/5c/cc/e2ad3ba9459aa34fa65cf1f82a5c4a820a2ce615aacfb5143b8817f76504/multidict-6.6.4-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a1b20a9d56b2d81e2ff52ecc0670d583eaabaa55f402e8d16dd062373dbbe796", size = 222324, upload-time = "2025-08-11T12:06:10.905Z" },
- { url = "https://files.pythonhosted.org/packages/19/db/4ed0f65701afbc2cb0c140d2d02928bb0fe38dd044af76e58ad7c54fd21f/multidict-6.6.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8c9854df0eaa610a23494c32a6f44a3a550fb398b6b51a56e8c6b9b3689578db", size = 253234, upload-time = "2025-08-11T12:06:12.658Z" },
- { url = "https://files.pythonhosted.org/packages/94/c1/5160c9813269e39ae14b73debb907bfaaa1beee1762da8c4fb95df4764ed/multidict-6.6.4-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4bb7627fd7a968f41905a4d6343b0d63244a0623f006e9ed989fa2b78f4438a0", size = 251613, upload-time = "2025-08-11T12:06:13.97Z" },
- { url = "https://files.pythonhosted.org/packages/05/a9/48d1bd111fc2f8fb98b2ed7f9a115c55a9355358432a19f53c0b74d8425d/multidict-6.6.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:caebafea30ed049c57c673d0b36238b1748683be2593965614d7b0e99125c877", size = 241649, upload-time = "2025-08-11T12:06:15.204Z" },
- { url = "https://files.pythonhosted.org/packages/85/2a/f7d743df0019408768af8a70d2037546a2be7b81fbb65f040d76caafd4c5/multidict-6.6.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ad887a8250eb47d3ab083d2f98db7f48098d13d42eb7a3b67d8a5c795f224ace", size = 239238, upload-time = "2025-08-11T12:06:16.467Z" },
- { url = "https://files.pythonhosted.org/packages/cb/b8/4f4bb13323c2d647323f7919201493cf48ebe7ded971717bfb0f1a79b6bf/multidict-6.6.4-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:ed8358ae7d94ffb7c397cecb62cbac9578a83ecefc1eba27b9090ee910e2efb6", size = 233517, upload-time = "2025-08-11T12:06:18.107Z" },
- { url = "https://files.pythonhosted.org/packages/33/29/4293c26029ebfbba4f574febd2ed01b6f619cfa0d2e344217d53eef34192/multidict-6.6.4-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:ecab51ad2462197a4c000b6d5701fc8585b80eecb90583635d7e327b7b6923eb", size = 243122, upload-time = "2025-08-11T12:06:19.361Z" },
- { url = "https://files.pythonhosted.org/packages/20/60/a1c53628168aa22447bfde3a8730096ac28086704a0d8c590f3b63388d0c/multidict-6.6.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:c5c97aa666cf70e667dfa5af945424ba1329af5dd988a437efeb3a09430389fb", size = 248992, upload-time = "2025-08-11T12:06:20.661Z" },
- { url = "https://files.pythonhosted.org/packages/a3/3b/55443a0c372f33cae5d9ec37a6a973802884fa0ab3586659b197cf8cc5e9/multidict-6.6.4-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:9a950b7cf54099c1209f455ac5970b1ea81410f2af60ed9eb3c3f14f0bfcf987", size = 243708, upload-time = "2025-08-11T12:06:21.891Z" },
- { url = "https://files.pythonhosted.org/packages/7c/60/a18c6900086769312560b2626b18e8cca22d9e85b1186ba77f4755b11266/multidict-6.6.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:163c7ea522ea9365a8a57832dea7618e6cbdc3cd75f8c627663587459a4e328f", size = 237498, upload-time = "2025-08-11T12:06:23.206Z" },
- { url = "https://files.pythonhosted.org/packages/11/3d/8bdd8bcaff2951ce2affccca107a404925a2beafedd5aef0b5e4a71120a6/multidict-6.6.4-cp310-cp310-win32.whl", hash = "sha256:17d2cbbfa6ff20821396b25890f155f40c986f9cfbce5667759696d83504954f", size = 41415, upload-time = "2025-08-11T12:06:24.77Z" },
- { url = "https://files.pythonhosted.org/packages/c0/53/cab1ad80356a4cd1b685a254b680167059b433b573e53872fab245e9fc95/multidict-6.6.4-cp310-cp310-win_amd64.whl", hash = "sha256:ce9a40fbe52e57e7edf20113a4eaddfacac0561a0879734e636aa6d4bb5e3fb0", size = 46046, upload-time = "2025-08-11T12:06:25.893Z" },
- { url = "https://files.pythonhosted.org/packages/cf/9a/874212b6f5c1c2d870d0a7adc5bb4cfe9b0624fa15cdf5cf757c0f5087ae/multidict-6.6.4-cp310-cp310-win_arm64.whl", hash = "sha256:01d0959807a451fe9fdd4da3e139cb5b77f7328baf2140feeaf233e1d777b729", size = 43147, upload-time = "2025-08-11T12:06:27.534Z" },
- { url = "https://files.pythonhosted.org/packages/6b/7f/90a7f01e2d005d6653c689039977f6856718c75c5579445effb7e60923d1/multidict-6.6.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c7a0e9b561e6460484318a7612e725df1145d46b0ef57c6b9866441bf6e27e0c", size = 76472, upload-time = "2025-08-11T12:06:29.006Z" },
- { url = "https://files.pythonhosted.org/packages/54/a3/bed07bc9e2bb302ce752f1dabc69e884cd6a676da44fb0e501b246031fdd/multidict-6.6.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6bf2f10f70acc7a2446965ffbc726e5fc0b272c97a90b485857e5c70022213eb", size = 44634, upload-time = "2025-08-11T12:06:30.374Z" },
- { url = "https://files.pythonhosted.org/packages/a7/4b/ceeb4f8f33cf81277da464307afeaf164fb0297947642585884f5cad4f28/multidict-6.6.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:66247d72ed62d5dd29752ffc1d3b88f135c6a8de8b5f63b7c14e973ef5bda19e", size = 44282, upload-time = "2025-08-11T12:06:31.958Z" },
- { url = "https://files.pythonhosted.org/packages/03/35/436a5da8702b06866189b69f655ffdb8f70796252a8772a77815f1812679/multidict-6.6.4-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:105245cc6b76f51e408451a844a54e6823bbd5a490ebfe5bdfc79798511ceded", size = 229696, upload-time = "2025-08-11T12:06:33.087Z" },
- { url = "https://files.pythonhosted.org/packages/b6/0e/915160be8fecf1fca35f790c08fb74ca684d752fcba62c11daaf3d92c216/multidict-6.6.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cbbc54e58b34c3bae389ef00046be0961f30fef7cb0dd9c7756aee376a4f7683", size = 246665, upload-time = "2025-08-11T12:06:34.448Z" },
- { url = "https://files.pythonhosted.org/packages/08/ee/2f464330acd83f77dcc346f0b1a0eaae10230291450887f96b204b8ac4d3/multidict-6.6.4-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:56c6b3652f945c9bc3ac6c8178cd93132b8d82dd581fcbc3a00676c51302bc1a", size = 225485, upload-time = "2025-08-11T12:06:35.672Z" },
- { url = "https://files.pythonhosted.org/packages/71/cc/9a117f828b4d7fbaec6adeed2204f211e9caf0a012692a1ee32169f846ae/multidict-6.6.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b95494daf857602eccf4c18ca33337dd2be705bccdb6dddbfc9d513e6addb9d9", size = 257318, upload-time = "2025-08-11T12:06:36.98Z" },
- { url = "https://files.pythonhosted.org/packages/25/77/62752d3dbd70e27fdd68e86626c1ae6bccfebe2bb1f84ae226363e112f5a/multidict-6.6.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e5b1413361cef15340ab9dc61523e653d25723e82d488ef7d60a12878227ed50", size = 254689, upload-time = "2025-08-11T12:06:38.233Z" },
- { url = "https://files.pythonhosted.org/packages/00/6e/fac58b1072a6fc59af5e7acb245e8754d3e1f97f4f808a6559951f72a0d4/multidict-6.6.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e167bf899c3d724f9662ef00b4f7fef87a19c22b2fead198a6f68b263618df52", size = 246709, upload-time = "2025-08-11T12:06:39.517Z" },
- { url = "https://files.pythonhosted.org/packages/01/ef/4698d6842ef5e797c6db7744b0081e36fb5de3d00002cc4c58071097fac3/multidict-6.6.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:aaea28ba20a9026dfa77f4b80369e51cb767c61e33a2d4043399c67bd95fb7c6", size = 243185, upload-time = "2025-08-11T12:06:40.796Z" },
- { url = "https://files.pythonhosted.org/packages/aa/c9/d82e95ae1d6e4ef396934e9b0e942dfc428775f9554acf04393cce66b157/multidict-6.6.4-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:8c91cdb30809a96d9ecf442ec9bc45e8cfaa0f7f8bdf534e082c2443a196727e", size = 237838, upload-time = "2025-08-11T12:06:42.595Z" },
- { url = "https://files.pythonhosted.org/packages/57/cf/f94af5c36baaa75d44fab9f02e2a6bcfa0cd90acb44d4976a80960759dbc/multidict-6.6.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1a0ccbfe93ca114c5d65a2471d52d8829e56d467c97b0e341cf5ee45410033b3", size = 246368, upload-time = "2025-08-11T12:06:44.304Z" },
- { url = "https://files.pythonhosted.org/packages/4a/fe/29f23460c3d995f6a4b678cb2e9730e7277231b981f0b234702f0177818a/multidict-6.6.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:55624b3f321d84c403cb7d8e6e982f41ae233d85f85db54ba6286f7295dc8a9c", size = 253339, upload-time = "2025-08-11T12:06:45.597Z" },
- { url = "https://files.pythonhosted.org/packages/29/b6/fd59449204426187b82bf8a75f629310f68c6adc9559dc922d5abe34797b/multidict-6.6.4-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:4a1fb393a2c9d202cb766c76208bd7945bc194eba8ac920ce98c6e458f0b524b", size = 246933, upload-time = "2025-08-11T12:06:46.841Z" },
- { url = "https://files.pythonhosted.org/packages/19/52/d5d6b344f176a5ac3606f7a61fb44dc746e04550e1a13834dff722b8d7d6/multidict-6.6.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:43868297a5759a845fa3a483fb4392973a95fb1de891605a3728130c52b8f40f", size = 242225, upload-time = "2025-08-11T12:06:48.588Z" },
- { url = "https://files.pythonhosted.org/packages/ec/d3/5b2281ed89ff4d5318d82478a2a2450fcdfc3300da48ff15c1778280ad26/multidict-6.6.4-cp311-cp311-win32.whl", hash = "sha256:ed3b94c5e362a8a84d69642dbeac615452e8af9b8eb825b7bc9f31a53a1051e2", size = 41306, upload-time = "2025-08-11T12:06:49.95Z" },
- { url = "https://files.pythonhosted.org/packages/74/7d/36b045c23a1ab98507aefd44fd8b264ee1dd5e5010543c6fccf82141ccef/multidict-6.6.4-cp311-cp311-win_amd64.whl", hash = "sha256:d8c112f7a90d8ca5d20213aa41eac690bb50a76da153e3afb3886418e61cb22e", size = 46029, upload-time = "2025-08-11T12:06:51.082Z" },
- { url = "https://files.pythonhosted.org/packages/0f/5e/553d67d24432c5cd52b49047f2d248821843743ee6d29a704594f656d182/multidict-6.6.4-cp311-cp311-win_arm64.whl", hash = "sha256:3bb0eae408fa1996d87247ca0d6a57b7fc1dcf83e8a5c47ab82c558c250d4adf", size = 43017, upload-time = "2025-08-11T12:06:52.243Z" },
- { url = "https://files.pythonhosted.org/packages/05/f6/512ffd8fd8b37fb2680e5ac35d788f1d71bbaf37789d21a820bdc441e565/multidict-6.6.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0ffb87be160942d56d7b87b0fdf098e81ed565add09eaa1294268c7f3caac4c8", size = 76516, upload-time = "2025-08-11T12:06:53.393Z" },
- { url = "https://files.pythonhosted.org/packages/99/58/45c3e75deb8855c36bd66cc1658007589662ba584dbf423d01df478dd1c5/multidict-6.6.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d191de6cbab2aff5de6c5723101705fd044b3e4c7cfd587a1929b5028b9714b3", size = 45394, upload-time = "2025-08-11T12:06:54.555Z" },
- { url = "https://files.pythonhosted.org/packages/fd/ca/e8c4472a93a26e4507c0b8e1f0762c0d8a32de1328ef72fd704ef9cc5447/multidict-6.6.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:38a0956dd92d918ad5feff3db8fcb4a5eb7dba114da917e1a88475619781b57b", size = 43591, upload-time = "2025-08-11T12:06:55.672Z" },
- { url = "https://files.pythonhosted.org/packages/05/51/edf414f4df058574a7265034d04c935aa84a89e79ce90fcf4df211f47b16/multidict-6.6.4-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:6865f6d3b7900ae020b495d599fcf3765653bc927951c1abb959017f81ae8287", size = 237215, upload-time = "2025-08-11T12:06:57.213Z" },
- { url = "https://files.pythonhosted.org/packages/c8/45/8b3d6dbad8cf3252553cc41abea09ad527b33ce47a5e199072620b296902/multidict-6.6.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a2088c126b6f72db6c9212ad827d0ba088c01d951cee25e758c450da732c138", size = 258299, upload-time = "2025-08-11T12:06:58.946Z" },
- { url = "https://files.pythonhosted.org/packages/3c/e8/8ca2e9a9f5a435fc6db40438a55730a4bf4956b554e487fa1b9ae920f825/multidict-6.6.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0f37bed7319b848097085d7d48116f545985db988e2256b2e6f00563a3416ee6", size = 242357, upload-time = "2025-08-11T12:07:00.301Z" },
- { url = "https://files.pythonhosted.org/packages/0f/84/80c77c99df05a75c28490b2af8f7cba2a12621186e0a8b0865d8e745c104/multidict-6.6.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:01368e3c94032ba6ca0b78e7ccb099643466cf24f8dc8eefcfdc0571d56e58f9", size = 268369, upload-time = "2025-08-11T12:07:01.638Z" },
- { url = "https://files.pythonhosted.org/packages/0d/e9/920bfa46c27b05fb3e1ad85121fd49f441492dca2449c5bcfe42e4565d8a/multidict-6.6.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8fe323540c255db0bffee79ad7f048c909f2ab0edb87a597e1c17da6a54e493c", size = 269341, upload-time = "2025-08-11T12:07:02.943Z" },
- { url = "https://files.pythonhosted.org/packages/af/65/753a2d8b05daf496f4a9c367fe844e90a1b2cac78e2be2c844200d10cc4c/multidict-6.6.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8eb3025f17b0a4c3cd08cda49acf312a19ad6e8a4edd9dbd591e6506d999402", size = 256100, upload-time = "2025-08-11T12:07:04.564Z" },
- { url = "https://files.pythonhosted.org/packages/09/54/655be13ae324212bf0bc15d665a4e34844f34c206f78801be42f7a0a8aaa/multidict-6.6.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bbc14f0365534d35a06970d6a83478b249752e922d662dc24d489af1aa0d1be7", size = 253584, upload-time = "2025-08-11T12:07:05.914Z" },
- { url = "https://files.pythonhosted.org/packages/5c/74/ab2039ecc05264b5cec73eb018ce417af3ebb384ae9c0e9ed42cb33f8151/multidict-6.6.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:75aa52fba2d96bf972e85451b99d8e19cc37ce26fd016f6d4aa60da9ab2b005f", size = 251018, upload-time = "2025-08-11T12:07:08.301Z" },
- { url = "https://files.pythonhosted.org/packages/af/0a/ccbb244ac848e56c6427f2392741c06302bbfba49c0042f1eb3c5b606497/multidict-6.6.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4fefd4a815e362d4f011919d97d7b4a1e566f1dde83dc4ad8cfb5b41de1df68d", size = 251477, upload-time = "2025-08-11T12:07:10.248Z" },
- { url = "https://files.pythonhosted.org/packages/0e/b0/0ed49bba775b135937f52fe13922bc64a7eaf0a3ead84a36e8e4e446e096/multidict-6.6.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:db9801fe021f59a5b375ab778973127ca0ac52429a26e2fd86aa9508f4d26eb7", size = 263575, upload-time = "2025-08-11T12:07:11.928Z" },
- { url = "https://files.pythonhosted.org/packages/3e/d9/7fb85a85e14de2e44dfb6a24f03c41e2af8697a6df83daddb0e9b7569f73/multidict-6.6.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:a650629970fa21ac1fb06ba25dabfc5b8a2054fcbf6ae97c758aa956b8dba802", size = 259649, upload-time = "2025-08-11T12:07:13.244Z" },
- { url = "https://files.pythonhosted.org/packages/03/9e/b3a459bcf9b6e74fa461a5222a10ff9b544cb1cd52fd482fb1b75ecda2a2/multidict-6.6.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:452ff5da78d4720d7516a3a2abd804957532dd69296cb77319c193e3ffb87e24", size = 251505, upload-time = "2025-08-11T12:07:14.57Z" },
- { url = "https://files.pythonhosted.org/packages/86/a2/8022f78f041dfe6d71e364001a5cf987c30edfc83c8a5fb7a3f0974cff39/multidict-6.6.4-cp312-cp312-win32.whl", hash = "sha256:8c2fcb12136530ed19572bbba61b407f655e3953ba669b96a35036a11a485793", size = 41888, upload-time = "2025-08-11T12:07:15.904Z" },
- { url = "https://files.pythonhosted.org/packages/c7/eb/d88b1780d43a56db2cba24289fa744a9d216c1a8546a0dc3956563fd53ea/multidict-6.6.4-cp312-cp312-win_amd64.whl", hash = "sha256:047d9425860a8c9544fed1b9584f0c8bcd31bcde9568b047c5e567a1025ecd6e", size = 46072, upload-time = "2025-08-11T12:07:17.045Z" },
- { url = "https://files.pythonhosted.org/packages/9f/16/b929320bf5750e2d9d4931835a4c638a19d2494a5b519caaaa7492ebe105/multidict-6.6.4-cp312-cp312-win_arm64.whl", hash = "sha256:14754eb72feaa1e8ae528468f24250dd997b8e2188c3d2f593f9eba259e4b364", size = 43222, upload-time = "2025-08-11T12:07:18.328Z" },
- { url = "https://files.pythonhosted.org/packages/3a/5d/e1db626f64f60008320aab00fbe4f23fc3300d75892a3381275b3d284580/multidict-6.6.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f46a6e8597f9bd71b31cc708195d42b634c8527fecbcf93febf1052cacc1f16e", size = 75848, upload-time = "2025-08-11T12:07:19.912Z" },
- { url = "https://files.pythonhosted.org/packages/4c/aa/8b6f548d839b6c13887253af4e29c939af22a18591bfb5d0ee6f1931dae8/multidict-6.6.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:22e38b2bc176c5eb9c0a0e379f9d188ae4cd8b28c0f53b52bce7ab0a9e534657", size = 45060, upload-time = "2025-08-11T12:07:21.163Z" },
- { url = "https://files.pythonhosted.org/packages/eb/c6/f5e97e5d99a729bc2aa58eb3ebfa9f1e56a9b517cc38c60537c81834a73f/multidict-6.6.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5df8afd26f162da59e218ac0eefaa01b01b2e6cd606cffa46608f699539246da", size = 43269, upload-time = "2025-08-11T12:07:22.392Z" },
- { url = "https://files.pythonhosted.org/packages/dc/31/d54eb0c62516776f36fe67f84a732f97e0b0e12f98d5685bebcc6d396910/multidict-6.6.4-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:49517449b58d043023720aa58e62b2f74ce9b28f740a0b5d33971149553d72aa", size = 237158, upload-time = "2025-08-11T12:07:23.636Z" },
- { url = "https://files.pythonhosted.org/packages/c4/1c/8a10c1c25b23156e63b12165a929d8eb49a6ed769fdbefb06e6f07c1e50d/multidict-6.6.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ae9408439537c5afdca05edd128a63f56a62680f4b3c234301055d7a2000220f", size = 257076, upload-time = "2025-08-11T12:07:25.049Z" },
- { url = "https://files.pythonhosted.org/packages/ad/86/90e20b5771d6805a119e483fd3d1e8393e745a11511aebca41f0da38c3e2/multidict-6.6.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:87a32d20759dc52a9e850fe1061b6e41ab28e2998d44168a8a341b99ded1dba0", size = 240694, upload-time = "2025-08-11T12:07:26.458Z" },
- { url = "https://files.pythonhosted.org/packages/e7/49/484d3e6b535bc0555b52a0a26ba86e4d8d03fd5587d4936dc59ba7583221/multidict-6.6.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:52e3c8d43cdfff587ceedce9deb25e6ae77daba560b626e97a56ddcad3756879", size = 266350, upload-time = "2025-08-11T12:07:27.94Z" },
- { url = "https://files.pythonhosted.org/packages/bf/b4/aa4c5c379b11895083d50021e229e90c408d7d875471cb3abf721e4670d6/multidict-6.6.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ad8850921d3a8d8ff6fbef790e773cecfc260bbfa0566998980d3fa8f520bc4a", size = 267250, upload-time = "2025-08-11T12:07:29.303Z" },
- { url = "https://files.pythonhosted.org/packages/80/e5/5e22c5bf96a64bdd43518b1834c6d95a4922cc2066b7d8e467dae9b6cee6/multidict-6.6.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:497a2954adc25c08daff36f795077f63ad33e13f19bfff7736e72c785391534f", size = 254900, upload-time = "2025-08-11T12:07:30.764Z" },
- { url = "https://files.pythonhosted.org/packages/17/38/58b27fed927c07035abc02befacab42491e7388ca105e087e6e0215ead64/multidict-6.6.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:024ce601f92d780ca1617ad4be5ac15b501cc2414970ffa2bb2bbc2bd5a68fa5", size = 252355, upload-time = "2025-08-11T12:07:32.205Z" },
- { url = "https://files.pythonhosted.org/packages/d0/a1/dad75d23a90c29c02b5d6f3d7c10ab36c3197613be5d07ec49c7791e186c/multidict-6.6.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:a693fc5ed9bdd1c9e898013e0da4dcc640de7963a371c0bd458e50e046bf6438", size = 250061, upload-time = "2025-08-11T12:07:33.623Z" },
- { url = "https://files.pythonhosted.org/packages/b8/1a/ac2216b61c7f116edab6dc3378cca6c70dc019c9a457ff0d754067c58b20/multidict-6.6.4-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:190766dac95aab54cae5b152a56520fd99298f32a1266d66d27fdd1b5ac00f4e", size = 249675, upload-time = "2025-08-11T12:07:34.958Z" },
- { url = "https://files.pythonhosted.org/packages/d4/79/1916af833b800d13883e452e8e0977c065c4ee3ab7a26941fbfdebc11895/multidict-6.6.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:34d8f2a5ffdceab9dcd97c7a016deb2308531d5f0fced2bb0c9e1df45b3363d7", size = 261247, upload-time = "2025-08-11T12:07:36.588Z" },
- { url = "https://files.pythonhosted.org/packages/c5/65/d1f84fe08ac44a5fc7391cbc20a7cedc433ea616b266284413fd86062f8c/multidict-6.6.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:59e8d40ab1f5a8597abcef00d04845155a5693b5da00d2c93dbe88f2050f2812", size = 257960, upload-time = "2025-08-11T12:07:39.735Z" },
- { url = "https://files.pythonhosted.org/packages/13/b5/29ec78057d377b195ac2c5248c773703a6b602e132a763e20ec0457e7440/multidict-6.6.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:467fe64138cfac771f0e949b938c2e1ada2b5af22f39692aa9258715e9ea613a", size = 250078, upload-time = "2025-08-11T12:07:41.525Z" },
- { url = "https://files.pythonhosted.org/packages/c4/0e/7e79d38f70a872cae32e29b0d77024bef7834b0afb406ddae6558d9e2414/multidict-6.6.4-cp313-cp313-win32.whl", hash = "sha256:14616a30fe6d0a48d0a48d1a633ab3b8bec4cf293aac65f32ed116f620adfd69", size = 41708, upload-time = "2025-08-11T12:07:43.405Z" },
- { url = "https://files.pythonhosted.org/packages/9d/34/746696dffff742e97cd6a23da953e55d0ea51fa601fa2ff387b3edcfaa2c/multidict-6.6.4-cp313-cp313-win_amd64.whl", hash = "sha256:40cd05eaeb39e2bc8939451f033e57feaa2ac99e07dbca8afe2be450a4a3b6cf", size = 45912, upload-time = "2025-08-11T12:07:45.082Z" },
- { url = "https://files.pythonhosted.org/packages/c7/87/3bac136181e271e29170d8d71929cdeddeb77f3e8b6a0c08da3a8e9da114/multidict-6.6.4-cp313-cp313-win_arm64.whl", hash = "sha256:f6eb37d511bfae9e13e82cb4d1af36b91150466f24d9b2b8a9785816deb16605", size = 43076, upload-time = "2025-08-11T12:07:46.746Z" },
- { url = "https://files.pythonhosted.org/packages/64/94/0a8e63e36c049b571c9ae41ee301ada29c3fee9643d9c2548d7d558a1d99/multidict-6.6.4-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:6c84378acd4f37d1b507dfa0d459b449e2321b3ba5f2338f9b085cf7a7ba95eb", size = 82812, upload-time = "2025-08-11T12:07:48.402Z" },
- { url = "https://files.pythonhosted.org/packages/25/1a/be8e369dfcd260d2070a67e65dd3990dd635cbd735b98da31e00ea84cd4e/multidict-6.6.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0e0558693063c75f3d952abf645c78f3c5dfdd825a41d8c4d8156fc0b0da6e7e", size = 48313, upload-time = "2025-08-11T12:07:49.679Z" },
- { url = "https://files.pythonhosted.org/packages/26/5a/dd4ade298674b2f9a7b06a32c94ffbc0497354df8285f27317c66433ce3b/multidict-6.6.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3f8e2384cb83ebd23fd07e9eada8ba64afc4c759cd94817433ab8c81ee4b403f", size = 46777, upload-time = "2025-08-11T12:07:51.318Z" },
- { url = "https://files.pythonhosted.org/packages/89/db/98aa28bc7e071bfba611ac2ae803c24e96dd3a452b4118c587d3d872c64c/multidict-6.6.4-cp313-cp313t-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:f996b87b420995a9174b2a7c1a8daf7db4750be6848b03eb5e639674f7963773", size = 229321, upload-time = "2025-08-11T12:07:52.965Z" },
- { url = "https://files.pythonhosted.org/packages/c7/bc/01ddda2a73dd9d167bd85d0e8ef4293836a8f82b786c63fb1a429bc3e678/multidict-6.6.4-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cc356250cffd6e78416cf5b40dc6a74f1edf3be8e834cf8862d9ed5265cf9b0e", size = 249954, upload-time = "2025-08-11T12:07:54.423Z" },
- { url = "https://files.pythonhosted.org/packages/06/78/6b7c0f020f9aa0acf66d0ab4eb9f08375bac9a50ff5e3edb1c4ccd59eafc/multidict-6.6.4-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:dadf95aa862714ea468a49ad1e09fe00fcc9ec67d122f6596a8d40caf6cec7d0", size = 228612, upload-time = "2025-08-11T12:07:55.914Z" },
- { url = "https://files.pythonhosted.org/packages/00/44/3faa416f89b2d5d76e9d447296a81521e1c832ad6e40b92f990697b43192/multidict-6.6.4-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7dd57515bebffd8ebd714d101d4c434063322e4fe24042e90ced41f18b6d3395", size = 257528, upload-time = "2025-08-11T12:07:57.371Z" },
- { url = "https://files.pythonhosted.org/packages/05/5f/77c03b89af0fcb16f018f668207768191fb9dcfb5e3361a5e706a11db2c9/multidict-6.6.4-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:967af5f238ebc2eb1da4e77af5492219fbd9b4b812347da39a7b5f5c72c0fa45", size = 256329, upload-time = "2025-08-11T12:07:58.844Z" },
- { url = "https://files.pythonhosted.org/packages/cf/e9/ed750a2a9afb4f8dc6f13dc5b67b514832101b95714f1211cd42e0aafc26/multidict-6.6.4-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2a4c6875c37aae9794308ec43e3530e4aa0d36579ce38d89979bbf89582002bb", size = 247928, upload-time = "2025-08-11T12:08:01.037Z" },
- { url = "https://files.pythonhosted.org/packages/1f/b5/e0571bc13cda277db7e6e8a532791d4403dacc9850006cb66d2556e649c0/multidict-6.6.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:7f683a551e92bdb7fac545b9c6f9fa2aebdeefa61d607510b3533286fcab67f5", size = 245228, upload-time = "2025-08-11T12:08:02.96Z" },
- { url = "https://files.pythonhosted.org/packages/f3/a3/69a84b0eccb9824491f06368f5b86e72e4af54c3067c37c39099b6687109/multidict-6.6.4-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:3ba5aaf600edaf2a868a391779f7a85d93bed147854925f34edd24cc70a3e141", size = 235869, upload-time = "2025-08-11T12:08:04.746Z" },
- { url = "https://files.pythonhosted.org/packages/a9/9d/28802e8f9121a6a0804fa009debf4e753d0a59969ea9f70be5f5fdfcb18f/multidict-6.6.4-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:580b643b7fd2c295d83cad90d78419081f53fd532d1f1eb67ceb7060f61cff0d", size = 243446, upload-time = "2025-08-11T12:08:06.332Z" },
- { url = "https://files.pythonhosted.org/packages/38/ea/6c98add069b4878c1d66428a5f5149ddb6d32b1f9836a826ac764b9940be/multidict-6.6.4-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:37b7187197da6af3ee0b044dbc9625afd0c885f2800815b228a0e70f9a7f473d", size = 252299, upload-time = "2025-08-11T12:08:07.931Z" },
- { url = "https://files.pythonhosted.org/packages/3a/09/8fe02d204473e14c0af3affd50af9078839dfca1742f025cca765435d6b4/multidict-6.6.4-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e1b93790ed0bc26feb72e2f08299691ceb6da5e9e14a0d13cc74f1869af327a0", size = 246926, upload-time = "2025-08-11T12:08:09.467Z" },
- { url = "https://files.pythonhosted.org/packages/37/3d/7b1e10d774a6df5175ecd3c92bff069e77bed9ec2a927fdd4ff5fe182f67/multidict-6.6.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a506a77ddee1efcca81ecbeae27ade3e09cdf21a8ae854d766c2bb4f14053f92", size = 243383, upload-time = "2025-08-11T12:08:10.981Z" },
- { url = "https://files.pythonhosted.org/packages/50/b0/a6fae46071b645ae98786ab738447de1ef53742eaad949f27e960864bb49/multidict-6.6.4-cp313-cp313t-win32.whl", hash = "sha256:f93b2b2279883d1d0a9e1bd01f312d6fc315c5e4c1f09e112e4736e2f650bc4e", size = 47775, upload-time = "2025-08-11T12:08:12.439Z" },
- { url = "https://files.pythonhosted.org/packages/b2/0a/2436550b1520091af0600dff547913cb2d66fbac27a8c33bc1b1bccd8d98/multidict-6.6.4-cp313-cp313t-win_amd64.whl", hash = "sha256:6d46a180acdf6e87cc41dc15d8f5c2986e1e8739dc25dbb7dac826731ef381a4", size = 53100, upload-time = "2025-08-11T12:08:13.823Z" },
- { url = "https://files.pythonhosted.org/packages/97/ea/43ac51faff934086db9c072a94d327d71b7d8b40cd5dcb47311330929ef0/multidict-6.6.4-cp313-cp313t-win_arm64.whl", hash = "sha256:756989334015e3335d087a27331659820d53ba432befdef6a718398b0a8493ad", size = 45501, upload-time = "2025-08-11T12:08:15.173Z" },
- { url = "https://files.pythonhosted.org/packages/d4/d3/f04c5db316caee9b5b2cbba66270b358c922a959855995bedde87134287c/multidict-6.6.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:af7618b591bae552b40dbb6f93f5518328a949dac626ee75927bba1ecdeea9f4", size = 76977, upload-time = "2025-08-11T12:08:16.667Z" },
- { url = "https://files.pythonhosted.org/packages/70/39/a6200417d883e510728ab3caec02d3b66ff09e1c85e0aab2ba311abfdf06/multidict-6.6.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b6819f83aef06f560cb15482d619d0e623ce9bf155115150a85ab11b8342a665", size = 44878, upload-time = "2025-08-11T12:08:18.157Z" },
- { url = "https://files.pythonhosted.org/packages/6f/7e/815be31ed35571b137d65232816f61513fcd97b2717d6a9d7800b5a0c6e0/multidict-6.6.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4d09384e75788861e046330308e7af54dd306aaf20eb760eb1d0de26b2bea2cb", size = 44546, upload-time = "2025-08-11T12:08:19.694Z" },
- { url = "https://files.pythonhosted.org/packages/e2/f1/21b5bff6a8c3e2aff56956c241941ace6b8820e1abe6b12d3c52868a773d/multidict-6.6.4-cp39-cp39-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:a59c63061f1a07b861c004e53869eb1211ffd1a4acbca330e3322efa6dd02978", size = 223020, upload-time = "2025-08-11T12:08:21.554Z" },
- { url = "https://files.pythonhosted.org/packages/15/59/37083f1dd3439979a0ffeb1906818d978d88b4cc7f4600a9f89b1cb6713c/multidict-6.6.4-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:350f6b0fe1ced61e778037fdc7613f4051c8baf64b1ee19371b42a3acdb016a0", size = 240528, upload-time = "2025-08-11T12:08:23.45Z" },
- { url = "https://files.pythonhosted.org/packages/d1/f0/f054d123c87784307a27324c829eb55bcfd2e261eb785fcabbd832c8dc4a/multidict-6.6.4-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0c5cbac6b55ad69cb6aa17ee9343dfbba903118fd530348c330211dc7aa756d1", size = 219540, upload-time = "2025-08-11T12:08:24.965Z" },
- { url = "https://files.pythonhosted.org/packages/e8/26/8f78ce17b7118149c17f238f28fba2a850b660b860f9b024a34d0191030f/multidict-6.6.4-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:630f70c32b8066ddfd920350bc236225814ad94dfa493fe1910ee17fe4365cbb", size = 251182, upload-time = "2025-08-11T12:08:26.511Z" },
- { url = "https://files.pythonhosted.org/packages/00/c3/a21466322d69f6594fe22d9379200f99194d21c12a5bbf8c2a39a46b83b6/multidict-6.6.4-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f8d4916a81697faec6cb724a273bd5457e4c6c43d82b29f9dc02c5542fd21fc9", size = 249371, upload-time = "2025-08-11T12:08:28.075Z" },
- { url = "https://files.pythonhosted.org/packages/c2/8e/2e673124eb05cf8dc82e9265eccde01a36bcbd3193e27799b8377123c976/multidict-6.6.4-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8e42332cf8276bb7645d310cdecca93a16920256a5b01bebf747365f86a1675b", size = 239235, upload-time = "2025-08-11T12:08:29.937Z" },
- { url = "https://files.pythonhosted.org/packages/2b/2d/bdd9f05e7c89e30a4b0e4faf0681a30748f8d1310f68cfdc0e3571e75bd5/multidict-6.6.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:f3be27440f7644ab9a13a6fc86f09cdd90b347c3c5e30c6d6d860de822d7cb53", size = 237410, upload-time = "2025-08-11T12:08:31.872Z" },
- { url = "https://files.pythonhosted.org/packages/46/4c/3237b83f8ca9a2673bb08fc340c15da005a80f5cc49748b587c8ae83823b/multidict-6.6.4-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:21f216669109e02ef3e2415ede07f4f8987f00de8cdfa0cc0b3440d42534f9f0", size = 232979, upload-time = "2025-08-11T12:08:33.399Z" },
- { url = "https://files.pythonhosted.org/packages/55/a6/a765decff625ae9bc581aed303cd1837955177dafc558859a69f56f56ba8/multidict-6.6.4-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:d9890d68c45d1aeac5178ded1d1cccf3bc8d7accf1f976f79bf63099fb16e4bd", size = 240979, upload-time = "2025-08-11T12:08:35.02Z" },
- { url = "https://files.pythonhosted.org/packages/6b/2d/9c75975cb0c66ea33cae1443bb265b2b3cd689bffcbc68872565f401da23/multidict-6.6.4-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:edfdcae97cdc5d1a89477c436b61f472c4d40971774ac4729c613b4b133163cb", size = 246849, upload-time = "2025-08-11T12:08:37.038Z" },
- { url = "https://files.pythonhosted.org/packages/3e/71/d21ac0843c1d8751fb5dcf8a1f436625d39d4577bc27829799d09b419af7/multidict-6.6.4-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:0b2e886624be5773e69cf32bcb8534aecdeb38943520b240fed3d5596a430f2f", size = 241798, upload-time = "2025-08-11T12:08:38.669Z" },
- { url = "https://files.pythonhosted.org/packages/94/3d/1d8911e53092837bd11b1c99d71de3e2a9a26f8911f864554677663242aa/multidict-6.6.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:be5bf4b3224948032a845d12ab0f69f208293742df96dc14c4ff9b09e508fc17", size = 235315, upload-time = "2025-08-11T12:08:40.266Z" },
- { url = "https://files.pythonhosted.org/packages/86/c5/4b758df96376f73e936b1942c6c2dfc17e37ed9d5ff3b01a811496966ca0/multidict-6.6.4-cp39-cp39-win32.whl", hash = "sha256:10a68a9191f284fe9d501fef4efe93226e74df92ce7a24e301371293bd4918ae", size = 41434, upload-time = "2025-08-11T12:08:41.965Z" },
- { url = "https://files.pythonhosted.org/packages/58/16/f1dfa2a0f25f2717a5e9e5fe8fd30613f7fe95e3530cec8d11f5de0b709c/multidict-6.6.4-cp39-cp39-win_amd64.whl", hash = "sha256:ee25f82f53262f9ac93bd7e58e47ea1bdcc3393cef815847e397cba17e284210", size = 46186, upload-time = "2025-08-11T12:08:43.367Z" },
- { url = "https://files.pythonhosted.org/packages/88/7d/a0568bac65438c494cb6950b29f394d875a796a237536ac724879cf710c9/multidict-6.6.4-cp39-cp39-win_arm64.whl", hash = "sha256:f9867e55590e0855bcec60d4f9a092b69476db64573c9fe17e92b0c50614c16a", size = 43115, upload-time = "2025-08-11T12:08:45.126Z" },
- { url = "https://files.pythonhosted.org/packages/fd/69/b547032297c7e63ba2af494edba695d781af8a0c6e89e4d06cf848b21d80/multidict-6.6.4-py3-none-any.whl", hash = "sha256:27d8f8e125c07cb954e54d75d04905a9bba8a439c1d84aca94949d4d03d8601c", size = 12313, upload-time = "2025-08-11T12:08:46.891Z" },
-]
-
-[[package]]
-name = "mypy-extensions"
-version = "1.1.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343, upload-time = "2025-04-22T14:54:24.164Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" },
-]
-
-[[package]]
-name = "mysql-connector-python"
-version = "9.4.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/02/77/2b45e6460d05b1f1b7a4c8eb79a50440b4417971973bb78c9ef6cad630a6/mysql_connector_python-9.4.0.tar.gz", hash = "sha256:d111360332ae78933daf3d48ff497b70739aa292ab0017791a33e826234e743b", size = 12185532, upload-time = "2025-07-22T08:02:05.788Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/a2/ef/1a35d9ebfaf80cf5aa238be471480e16a69a494d276fb07b889dc9a5cfc3/mysql_connector_python-9.4.0-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:3c2603e00516cf4208c6266e85c5c87d5f4d0ac79768106d50de42ccc8414c05", size = 17501678, upload-time = "2025-07-22T07:57:23.237Z" },
- { url = "https://files.pythonhosted.org/packages/3c/39/09ae7082c77a978f2d72d94856e2e57906165c645693bc3a940bcad3a32d/mysql_connector_python-9.4.0-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:47884fcb050112b8bef3458e17eac47cc81a6cbbf3524e3456146c949772d9b4", size = 18369526, upload-time = "2025-07-22T07:57:27.569Z" },
- { url = "https://files.pythonhosted.org/packages/40/56/1bea00f5129550bcd0175781b9cd467e865d4aea4a6f38f700f34d95dcb8/mysql_connector_python-9.4.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:f14b6936cd326e212fc9ab5f666dea3efea654f0cb644460334e60e22986e735", size = 33508525, upload-time = "2025-07-22T07:57:32.935Z" },
- { url = "https://files.pythonhosted.org/packages/0f/ec/86dfefd3e6c0fca13085bc28b7f9baae3fce9f6af243d8693729f6b5063c/mysql_connector_python-9.4.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:0f5ad70355720e64b72d7c068e858c9fd1f69b671d9575f857f235a10f878939", size = 33911834, upload-time = "2025-07-22T07:57:38.203Z" },
- { url = "https://files.pythonhosted.org/packages/2c/11/6907d53349b11478f72c8f22e38368d18262fbffc27e0f30e365d76dad93/mysql_connector_python-9.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:7106670abce510e440d393e27fc3602b8cf21e7a8a80216cc9ad9a68cd2e4595", size = 16393044, upload-time = "2025-07-22T07:57:42.053Z" },
- { url = "https://files.pythonhosted.org/packages/fe/0c/4365a802129be9fa63885533c38be019f1c6b6f5bcf8844ac53902314028/mysql_connector_python-9.4.0-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:7df1a8ddd182dd8adc914f6dc902a986787bf9599705c29aca7b2ce84e79d361", size = 17501627, upload-time = "2025-07-22T07:57:45.416Z" },
- { url = "https://files.pythonhosted.org/packages/c0/bf/ca596c00d7a6eaaf8ef2f66c9b23cd312527f483073c43ffac7843049cb4/mysql_connector_python-9.4.0-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:3892f20472e13e63b1fb4983f454771dd29f211b09724e69a9750e299542f2f8", size = 18369494, upload-time = "2025-07-22T07:57:49.714Z" },
- { url = "https://files.pythonhosted.org/packages/25/14/6510a11ed9f80d77f743dc207773092c4ab78d5efa454b39b48480315d85/mysql_connector_python-9.4.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:d3e87142103d71c4df647ece30f98e85e826652272ed1c74822b56f6acdc38e7", size = 33516187, upload-time = "2025-07-22T07:57:55.294Z" },
- { url = "https://files.pythonhosted.org/packages/16/a8/4f99d80f1cf77733ce9a44b6adb7f0dd7079e7afa51ca4826515ef0c3e16/mysql_connector_python-9.4.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:b27fcd403436fe83bafb2fe7fcb785891e821e639275c4ad3b3bd1e25f533206", size = 33917818, upload-time = "2025-07-22T07:58:00.523Z" },
- { url = "https://files.pythonhosted.org/packages/15/9c/127f974ca9d5ee25373cb5433da06bb1f36e05f2a6b7436da1fe9c6346b0/mysql_connector_python-9.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:fd6ff5afb9c324b0bbeae958c93156cce4168c743bf130faf224d52818d1f0ee", size = 16392378, upload-time = "2025-07-22T07:58:04.669Z" },
- { url = "https://files.pythonhosted.org/packages/03/7c/a543fb17c2dfa6be8548dfdc5879a0c7924cd5d1c79056c48472bb8fe858/mysql_connector_python-9.4.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:4efa3898a24aba6a4bfdbf7c1f5023c78acca3150d72cc91199cca2ccd22f76f", size = 17503693, upload-time = "2025-07-22T07:58:08.96Z" },
- { url = "https://files.pythonhosted.org/packages/cb/6e/c22fbee05f5cfd6ba76155b6d45f6261d8d4c1e36e23de04e7f25fbd01a4/mysql_connector_python-9.4.0-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:665c13e7402235162e5b7a2bfdee5895192121b64ea455c90a81edac6a48ede5", size = 18371987, upload-time = "2025-07-22T07:58:13.273Z" },
- { url = "https://files.pythonhosted.org/packages/b4/fd/f426f5f35a3d3180c7f84d1f96b4631be2574df94ca1156adab8618b236c/mysql_connector_python-9.4.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:815aa6cad0f351c1223ef345781a538f2e5e44ef405fdb3851eb322bd9c4ca2b", size = 33516214, upload-time = "2025-07-22T07:58:18.967Z" },
- { url = "https://files.pythonhosted.org/packages/45/5a/1b053ae80b43cd3ccebc4bb99a98826969b3b0f8adebdcc2530750ad76ed/mysql_connector_python-9.4.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:b3436a2c8c0ec7052932213e8d01882e6eb069dbab33402e685409084b133a1c", size = 33918565, upload-time = "2025-07-22T07:58:25.28Z" },
- { url = "https://files.pythonhosted.org/packages/cb/69/36b989de675d98ba8ff7d45c96c30c699865c657046f2e32db14e78f13d9/mysql_connector_python-9.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:57b0c224676946b70548c56798d5023f65afa1ba5b8ac9f04a143d27976c7029", size = 16392563, upload-time = "2025-07-22T07:58:29.623Z" },
- { url = "https://files.pythonhosted.org/packages/79/e2/13036479cd1070d1080cee747de6c96bd6fbb021b736dd3ccef2b19016c8/mysql_connector_python-9.4.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:fde3bbffb5270a4b02077029914e6a9d2ec08f67d8375b4111432a2778e7540b", size = 17503749, upload-time = "2025-07-22T07:58:33.649Z" },
- { url = "https://files.pythonhosted.org/packages/31/df/b89e6551b91332716d384dcc3223e1f8065902209dcd9e477a3df80154f7/mysql_connector_python-9.4.0-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:25f77ad7d845df3b5a5a3a6a8d1fed68248dc418a6938a371d1ddaaab6b9a8e3", size = 18372145, upload-time = "2025-07-22T07:58:37.384Z" },
- { url = "https://files.pythonhosted.org/packages/07/bd/af0de40a01d5cb4df19318cc018e64666f2b7fa89bffa1ab5b35337aae2c/mysql_connector_python-9.4.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:227dd420c71e6d4788d52d98f298e563f16b6853577e5ade4bd82d644257c812", size = 33516503, upload-time = "2025-07-22T07:58:41.987Z" },
- { url = "https://files.pythonhosted.org/packages/d1/9b/712053216fcbe695e519ecb1035ffd767c2de9f51ccba15078537c99d6fa/mysql_connector_python-9.4.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:5163381a312d38122eded2197eb5cd7ccf1a5c5881d4e7a6de10d6ea314d088e", size = 33918904, upload-time = "2025-07-22T07:58:46.796Z" },
- { url = "https://files.pythonhosted.org/packages/64/15/cbd996d425c59811849f3c1d1b1dae089a1ae18c4acd4d8de2b847b772df/mysql_connector_python-9.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:c727cb1f82b40c9aaa7a15ab5cf0a7f87c5d8dce32eab5ff2530a4aa6054e7df", size = 16392566, upload-time = "2025-07-22T07:58:50.223Z" },
- { url = "https://files.pythonhosted.org/packages/6d/36/b32635b69729f144d45c0cbcd135cfd6c480a62160ac015ca71ebf68fca7/mysql_connector_python-9.4.0-cp39-cp39-macosx_14_0_arm64.whl", hash = "sha256:20f8154ab5c0ed444f8ef8e5fa91e65215037db102c137b5f995ebfffd309b78", size = 17501675, upload-time = "2025-07-22T07:58:53.049Z" },
- { url = "https://files.pythonhosted.org/packages/a0/23/65e801f74b3fcc2a6944242d64f0d623af48497e4d9cf55419c2c6d6439b/mysql_connector_python-9.4.0-cp39-cp39-macosx_14_0_x86_64.whl", hash = "sha256:7b8976d89d67c8b0dc452471cb557d9998ed30601fb69a876bf1f0ecaa7954a4", size = 18369579, upload-time = "2025-07-22T07:58:55.995Z" },
- { url = "https://files.pythonhosted.org/packages/86/e9/dc31eeffe33786016e1370be72f339544ee00034cb702c0b4a3c6f5c1585/mysql_connector_python-9.4.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:4ee4fe1b067e243aae21981e4b9f9d300a3104814b8274033ca8fc7a89b1729e", size = 33506513, upload-time = "2025-07-22T07:58:59.341Z" },
- { url = "https://files.pythonhosted.org/packages/dd/c7/aa6f4cc2e5e3fb68b5a6bba680429b761e387b8a040cf16a5f17e0b09df6/mysql_connector_python-9.4.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:1c6b95404e80d003cd452e38674e91528e2b3a089fe505c882f813b564e64f9d", size = 33909982, upload-time = "2025-07-22T07:59:02.832Z" },
- { url = "https://files.pythonhosted.org/packages/0c/a4/b1e2adc65121e7eabed06d09bed87638e7f9a51e9b5dbb1cfb17b58b1181/mysql_connector_python-9.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:a8f820c111335f225d63367307456eb7e10494f87e7a94acded3bb762e55a6d4", size = 16393051, upload-time = "2025-07-22T07:59:05.983Z" },
- { url = "https://files.pythonhosted.org/packages/36/34/b6165e15fd45a8deb00932d8e7d823de7650270873b4044c4db6688e1d8f/mysql_connector_python-9.4.0-py2.py3-none-any.whl", hash = "sha256:56e679169c704dab279b176fab2a9ee32d2c632a866c0f7cd48a8a1e2cf802c4", size = 406574, upload-time = "2025-07-22T07:59:08.394Z" },
-]
-
-[[package]]
-name = "narwhals"
-version = "2.2.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/01/8f/6b3d8c19540eaaa50778a8bbbe54e025d3f93aca6cdd5a4de3044c36f83c/narwhals-2.2.0.tar.gz", hash = "sha256:f6a34f2699acabe2c17339c104f0bec28b9f7a55fbc7f8d485d49bea72d12b8a", size = 547070, upload-time = "2025-08-25T07:51:58.904Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/dd/54/1ecca75e51d7da8ca53d1ffa8636ef9077a6eaa31f43ade71360b3e6449a/narwhals-2.2.0-py3-none-any.whl", hash = "sha256:2b5e3d61a486fa4328c286b0c8018b3e781a964947ff725d66ba12f6d5ca3d2a", size = 401021, upload-time = "2025-08-25T07:51:56.97Z" },
-]
-
-[[package]]
-name = "networkx"
-version = "3.2.1"
-source = { registry = "https://pypi.org/simple" }
-resolution-markers = [
- "python_full_version < '3.10'",
-]
-sdist = { url = "https://files.pythonhosted.org/packages/c4/80/a84676339aaae2f1cfdf9f418701dd634aef9cc76f708ef55c36ff39c3ca/networkx-3.2.1.tar.gz", hash = "sha256:9f1bb5cf3409bf324e0a722c20bdb4c20ee39bf1c30ce8ae499c8502b0b5e0c6", size = 2073928, upload-time = "2023-10-28T08:41:39.364Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/d5/f0/8fbc882ca80cf077f1b246c0e3c3465f7f415439bdea6b899f6b19f61f70/networkx-3.2.1-py3-none-any.whl", hash = "sha256:f18c69adc97877c42332c170849c96cefa91881c99a7cb3e95b7c659ebdc1ec2", size = 1647772, upload-time = "2023-10-28T08:41:36.945Z" },
-]
-
-[[package]]
-name = "networkx"
-version = "3.4.2"
-source = { registry = "https://pypi.org/simple" }
-resolution-markers = [
- "python_full_version == '3.10.*'",
-]
-sdist = { url = "https://files.pythonhosted.org/packages/fd/1d/06475e1cd5264c0b870ea2cc6fdb3e37177c1e565c43f56ff17a10e3937f/networkx-3.4.2.tar.gz", hash = "sha256:307c3669428c5362aab27c8a1260aa8f47c4e91d3891f48be0141738d8d053e1", size = 2151368, upload-time = "2024-10-21T12:39:38.695Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/b9/54/dd730b32ea14ea797530a4479b2ed46a6fb250f682a9cfb997e968bf0261/networkx-3.4.2-py3-none-any.whl", hash = "sha256:df5d4365b724cf81b8c6a7312509d0c22386097011ad1abe274afd5e9d3bbc5f", size = 1723263, upload-time = "2024-10-21T12:39:36.247Z" },
-]
-
-[[package]]
-name = "networkx"
-version = "3.5"
-source = { registry = "https://pypi.org/simple" }
-resolution-markers = [
- "python_full_version >= '3.13'",
- "python_full_version >= '3.12.4' and python_full_version < '3.13'",
- "python_full_version >= '3.12' and python_full_version < '3.12.4'",
- "python_full_version == '3.11.*'",
-]
-sdist = { url = "https://files.pythonhosted.org/packages/6c/4f/ccdb8ad3a38e583f214547fd2f7ff1fc160c43a75af88e6aec213404b96a/networkx-3.5.tar.gz", hash = "sha256:d4c6f9cf81f52d69230866796b82afbccdec3db7ae4fbd1b65ea750feed50037", size = 2471065, upload-time = "2025-05-29T11:35:07.804Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/eb/8d/776adee7bbf76365fdd7f2552710282c79a4ead5d2a46408c9043a2b70ba/networkx-3.5-py3-none-any.whl", hash = "sha256:0030d386a9a06dee3565298b4a734b68589749a544acbb6c412dc9e2489ec6ec", size = 2034406, upload-time = "2025-05-29T11:35:04.961Z" },
-]
-
-[[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, upload-time = "2024-06-04T18:44:11.171Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314, upload-time = "2024-06-04T18:44:08.352Z" },
-]
-
-[[package]]
-name = "numpy"
-version = "1.26.4"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/65/6e/09db70a523a96d25e115e71cc56a6f9031e7b8cd166c1ac8438307c14058/numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010", size = 15786129, upload-time = "2024-02-06T00:26:44.495Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/a7/94/ace0fdea5241a27d13543ee117cbc65868e82213fb31a8eb7fe9ff23f313/numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0", size = 20631468, upload-time = "2024-02-05T23:48:01.194Z" },
- { url = "https://files.pythonhosted.org/packages/20/f7/b24208eba89f9d1b58c1668bc6c8c4fd472b20c45573cb767f59d49fb0f6/numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a", size = 13966411, upload-time = "2024-02-05T23:48:29.038Z" },
- { url = "https://files.pythonhosted.org/packages/fc/a5/4beee6488160798683eed5bdb7eead455892c3b4e1f78d79d8d3f3b084ac/numpy-1.26.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d209d8969599b27ad20994c8e41936ee0964e6da07478d6c35016bc386b66ad4", size = 14219016, upload-time = "2024-02-05T23:48:54.098Z" },
- { url = "https://files.pythonhosted.org/packages/4b/d7/ecf66c1cd12dc28b4040b15ab4d17b773b87fa9d29ca16125de01adb36cd/numpy-1.26.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f", size = 18240889, upload-time = "2024-02-05T23:49:25.361Z" },
- { url = "https://files.pythonhosted.org/packages/24/03/6f229fe3187546435c4f6f89f6d26c129d4f5bed40552899fcf1f0bf9e50/numpy-1.26.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:62b8e4b1e28009ef2846b4c7852046736bab361f7aeadeb6a5b89ebec3c7055a", size = 13876746, upload-time = "2024-02-05T23:49:51.983Z" },
- { url = "https://files.pythonhosted.org/packages/39/fe/39ada9b094f01f5a35486577c848fe274e374bbf8d8f472e1423a0bbd26d/numpy-1.26.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a4abb4f9001ad2858e7ac189089c42178fcce737e4169dc61321660f1a96c7d2", size = 18078620, upload-time = "2024-02-05T23:50:22.515Z" },
- { url = "https://files.pythonhosted.org/packages/d5/ef/6ad11d51197aad206a9ad2286dc1aac6a378059e06e8cf22cd08ed4f20dc/numpy-1.26.4-cp310-cp310-win32.whl", hash = "sha256:bfe25acf8b437eb2a8b2d49d443800a5f18508cd811fea3181723922a8a82b07", size = 5972659, upload-time = "2024-02-05T23:50:35.834Z" },
- { url = "https://files.pythonhosted.org/packages/19/77/538f202862b9183f54108557bfda67e17603fc560c384559e769321c9d92/numpy-1.26.4-cp310-cp310-win_amd64.whl", hash = "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5", size = 15808905, upload-time = "2024-02-05T23:51:03.701Z" },
- { url = "https://files.pythonhosted.org/packages/11/57/baae43d14fe163fa0e4c47f307b6b2511ab8d7d30177c491960504252053/numpy-1.26.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c66707fabe114439db9068ee468c26bbdf909cac0fb58686a42a24de1760c71", size = 20630554, upload-time = "2024-02-05T23:51:50.149Z" },
- { url = "https://files.pythonhosted.org/packages/1a/2e/151484f49fd03944c4a3ad9c418ed193cfd02724e138ac8a9505d056c582/numpy-1.26.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:edd8b5fe47dab091176d21bb6de568acdd906d1887a4584a15a9a96a1dca06ef", size = 13997127, upload-time = "2024-02-05T23:52:15.314Z" },
- { url = "https://files.pythonhosted.org/packages/79/ae/7e5b85136806f9dadf4878bf73cf223fe5c2636818ba3ab1c585d0403164/numpy-1.26.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab55401287bfec946ced39700c053796e7cc0e3acbef09993a9ad2adba6ca6e", size = 14222994, upload-time = "2024-02-05T23:52:47.569Z" },
- { url = "https://files.pythonhosted.org/packages/3a/d0/edc009c27b406c4f9cbc79274d6e46d634d139075492ad055e3d68445925/numpy-1.26.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:666dbfb6ec68962c033a450943ded891bed2d54e6755e35e5835d63f4f6931d5", size = 18252005, upload-time = "2024-02-05T23:53:15.637Z" },
- { url = "https://files.pythonhosted.org/packages/09/bf/2b1aaf8f525f2923ff6cfcf134ae5e750e279ac65ebf386c75a0cf6da06a/numpy-1.26.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:96ff0b2ad353d8f990b63294c8986f1ec3cb19d749234014f4e7eb0112ceba5a", size = 13885297, upload-time = "2024-02-05T23:53:42.16Z" },
- { url = "https://files.pythonhosted.org/packages/df/a0/4e0f14d847cfc2a633a1c8621d00724f3206cfeddeb66d35698c4e2cf3d2/numpy-1.26.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:60dedbb91afcbfdc9bc0b1f3f402804070deed7392c23eb7a7f07fa857868e8a", size = 18093567, upload-time = "2024-02-05T23:54:11.696Z" },
- { url = "https://files.pythonhosted.org/packages/d2/b7/a734c733286e10a7f1a8ad1ae8c90f2d33bf604a96548e0a4a3a6739b468/numpy-1.26.4-cp311-cp311-win32.whl", hash = "sha256:1af303d6b2210eb850fcf03064d364652b7120803a0b872f5211f5234b399f20", size = 5968812, upload-time = "2024-02-05T23:54:26.453Z" },
- { url = "https://files.pythonhosted.org/packages/3f/6b/5610004206cf7f8e7ad91c5a85a8c71b2f2f8051a0c0c4d5916b76d6cbb2/numpy-1.26.4-cp311-cp311-win_amd64.whl", hash = "sha256:cd25bcecc4974d09257ffcd1f098ee778f7834c3ad767fe5db785be9a4aa9cb2", size = 15811913, upload-time = "2024-02-05T23:54:53.933Z" },
- { url = "https://files.pythonhosted.org/packages/95/12/8f2020a8e8b8383ac0177dc9570aad031a3beb12e38847f7129bacd96228/numpy-1.26.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b3ce300f3644fb06443ee2222c2201dd3a89ea6040541412b8fa189341847218", size = 20335901, upload-time = "2024-02-05T23:55:32.801Z" },
- { url = "https://files.pythonhosted.org/packages/75/5b/ca6c8bd14007e5ca171c7c03102d17b4f4e0ceb53957e8c44343a9546dcc/numpy-1.26.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:03a8c78d01d9781b28a6989f6fa1bb2c4f2d51201cf99d3dd875df6fbd96b23b", size = 13685868, upload-time = "2024-02-05T23:55:56.28Z" },
- { url = "https://files.pythonhosted.org/packages/79/f8/97f10e6755e2a7d027ca783f63044d5b1bc1ae7acb12afe6a9b4286eac17/numpy-1.26.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9fad7dcb1aac3c7f0584a5a8133e3a43eeb2fe127f47e3632d43d677c66c102b", size = 13925109, upload-time = "2024-02-05T23:56:20.368Z" },
- { url = "https://files.pythonhosted.org/packages/0f/50/de23fde84e45f5c4fda2488c759b69990fd4512387a8632860f3ac9cd225/numpy-1.26.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675d61ffbfa78604709862923189bad94014bef562cc35cf61d3a07bba02a7ed", size = 17950613, upload-time = "2024-02-05T23:56:56.054Z" },
- { url = "https://files.pythonhosted.org/packages/4c/0c/9c603826b6465e82591e05ca230dfc13376da512b25ccd0894709b054ed0/numpy-1.26.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ab47dbe5cc8210f55aa58e4805fe224dac469cde56b9f731a4c098b91917159a", size = 13572172, upload-time = "2024-02-05T23:57:21.56Z" },
- { url = "https://files.pythonhosted.org/packages/76/8c/2ba3902e1a0fc1c74962ea9bb33a534bb05984ad7ff9515bf8d07527cadd/numpy-1.26.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1dda2e7b4ec9dd512f84935c5f126c8bd8b9f2fc001e9f54af255e8c5f16b0e0", size = 17786643, upload-time = "2024-02-05T23:57:56.585Z" },
- { url = "https://files.pythonhosted.org/packages/28/4a/46d9e65106879492374999e76eb85f87b15328e06bd1550668f79f7b18c6/numpy-1.26.4-cp312-cp312-win32.whl", hash = "sha256:50193e430acfc1346175fcbdaa28ffec49947a06918b7b92130744e81e640110", size = 5677803, upload-time = "2024-02-05T23:58:08.963Z" },
- { url = "https://files.pythonhosted.org/packages/16/2e/86f24451c2d530c88daf997cb8d6ac622c1d40d19f5a031ed68a4b73a374/numpy-1.26.4-cp312-cp312-win_amd64.whl", hash = "sha256:08beddf13648eb95f8d867350f6a018a4be2e5ad54c8d8caed89ebca558b2818", size = 15517754, upload-time = "2024-02-05T23:58:36.364Z" },
- { url = "https://files.pythonhosted.org/packages/7d/24/ce71dc08f06534269f66e73c04f5709ee024a1afe92a7b6e1d73f158e1f8/numpy-1.26.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7349ab0fa0c429c82442a27a9673fc802ffdb7c7775fad780226cb234965e53c", size = 20636301, upload-time = "2024-02-05T23:59:10.976Z" },
- { url = "https://files.pythonhosted.org/packages/ae/8c/ab03a7c25741f9ebc92684a20125fbc9fc1b8e1e700beb9197d750fdff88/numpy-1.26.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:52b8b60467cd7dd1e9ed082188b4e6bb35aa5cdd01777621a1658910745b90be", size = 13971216, upload-time = "2024-02-05T23:59:35.472Z" },
- { url = "https://files.pythonhosted.org/packages/6d/64/c3bcdf822269421d85fe0d64ba972003f9bb4aa9a419da64b86856c9961f/numpy-1.26.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5241e0a80d808d70546c697135da2c613f30e28251ff8307eb72ba696945764", size = 14226281, upload-time = "2024-02-05T23:59:59.372Z" },
- { url = "https://files.pythonhosted.org/packages/54/30/c2a907b9443cf42b90c17ad10c1e8fa801975f01cb9764f3f8eb8aea638b/numpy-1.26.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f870204a840a60da0b12273ef34f7051e98c3b5961b61b0c2c1be6dfd64fbcd3", size = 18249516, upload-time = "2024-02-06T00:00:32.79Z" },
- { url = "https://files.pythonhosted.org/packages/43/12/01a563fc44c07095996d0129b8899daf89e4742146f7044cdbdb3101c57f/numpy-1.26.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:679b0076f67ecc0138fd2ede3a8fd196dddc2ad3254069bcb9faf9a79b1cebcd", size = 13882132, upload-time = "2024-02-06T00:00:58.197Z" },
- { url = "https://files.pythonhosted.org/packages/16/ee/9df80b06680aaa23fc6c31211387e0db349e0e36d6a63ba3bd78c5acdf11/numpy-1.26.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:47711010ad8555514b434df65f7d7b076bb8261df1ca9bb78f53d3b2db02e95c", size = 18084181, upload-time = "2024-02-06T00:01:31.21Z" },
- { url = "https://files.pythonhosted.org/packages/28/7d/4b92e2fe20b214ffca36107f1a3e75ef4c488430e64de2d9af5db3a4637d/numpy-1.26.4-cp39-cp39-win32.whl", hash = "sha256:a354325ee03388678242a4d7ebcd08b5c727033fcff3b2f536aea978e15ee9e6", size = 5976360, upload-time = "2024-02-06T00:01:43.013Z" },
- { url = "https://files.pythonhosted.org/packages/b5/42/054082bd8220bbf6f297f982f0a8f5479fcbc55c8b511d928df07b965869/numpy-1.26.4-cp39-cp39-win_amd64.whl", hash = "sha256:3373d5d70a5fe74a2c1bb6d2cfd9609ecf686d47a2d7b1d37a8f3b6bf6003aea", size = 15814633, upload-time = "2024-02-06T00:02:16.694Z" },
- { url = "https://files.pythonhosted.org/packages/3f/72/3df6c1c06fc83d9cfe381cccb4be2532bbd38bf93fbc9fad087b6687f1c0/numpy-1.26.4-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:afedb719a9dcfc7eaf2287b839d8198e06dcd4cb5d276a3df279231138e83d30", size = 20455961, upload-time = "2024-02-06T00:03:05.993Z" },
- { url = "https://files.pythonhosted.org/packages/8e/02/570545bac308b58ffb21adda0f4e220ba716fb658a63c151daecc3293350/numpy-1.26.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95a7476c59002f2f6c590b9b7b998306fba6a5aa646b1e22ddfeaf8f78c3a29c", size = 18061071, upload-time = "2024-02-06T00:03:41.5Z" },
- { url = "https://files.pythonhosted.org/packages/f4/5f/fafd8c51235f60d49f7a88e2275e13971e90555b67da52dd6416caec32fe/numpy-1.26.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7e50d0a0cc3189f9cb0aeb3a6a6af18c16f59f004b866cd2be1c14b36134a4a0", size = 15709730, upload-time = "2024-02-06T00:04:11.719Z" },
-]
-
-[[package]]
-name = "nvidia-cublas-cu12"
-version = "12.8.4.1"
-source = { registry = "https://pypi.org/simple" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/dc/61/e24b560ab2e2eaeb3c839129175fb330dfcfc29e5203196e5541a4c44682/nvidia_cublas_cu12-12.8.4.1-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:8ac4e771d5a348c551b2a426eda6193c19aa630236b418086020df5ba9667142", size = 594346921, upload-time = "2025-03-07T01:44:31.254Z" },
-]
-
-[[package]]
-name = "nvidia-cuda-cupti-cu12"
-version = "12.8.90"
-source = { registry = "https://pypi.org/simple" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/f8/02/2adcaa145158bf1a8295d83591d22e4103dbfd821bcaf6f3f53151ca4ffa/nvidia_cuda_cupti_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ea0cb07ebda26bb9b29ba82cda34849e73c166c18162d3913575b0c9db9a6182", size = 10248621, upload-time = "2025-03-07T01:40:21.213Z" },
-]
-
-[[package]]
-name = "nvidia-cuda-nvrtc-cu12"
-version = "12.8.93"
-source = { registry = "https://pypi.org/simple" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/05/6b/32f747947df2da6994e999492ab306a903659555dddc0fbdeb9d71f75e52/nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:a7756528852ef889772a84c6cd89d41dfa74667e24cca16bb31f8f061e3e9994", size = 88040029, upload-time = "2025-03-07T01:42:13.562Z" },
-]
-
-[[package]]
-name = "nvidia-cuda-runtime-cu12"
-version = "12.8.90"
-source = { registry = "https://pypi.org/simple" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/0d/9b/a997b638fcd068ad6e4d53b8551a7d30fe8b404d6f1804abf1df69838932/nvidia_cuda_runtime_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:adade8dcbd0edf427b7204d480d6066d33902cab2a4707dcfc48a2d0fd44ab90", size = 954765, upload-time = "2025-03-07T01:40:01.615Z" },
-]
-
-[[package]]
-name = "nvidia-cudnn-cu12"
-version = "9.10.2.21"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "nvidia-cublas-cu12" },
-]
-wheels = [
- { url = "https://files.pythonhosted.org/packages/ba/51/e123d997aa098c61d029f76663dedbfb9bc8dcf8c60cbd6adbe42f76d049/nvidia_cudnn_cu12-9.10.2.21-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:949452be657fa16687d0930933f032835951ef0892b37d2d53824d1a84dc97a8", size = 706758467, upload-time = "2025-06-06T21:54:08.597Z" },
-]
-
-[[package]]
-name = "nvidia-cufft-cu12"
-version = "11.3.3.83"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "nvidia-nvjitlink-cu12" },
-]
-wheels = [
- { url = "https://files.pythonhosted.org/packages/1f/13/ee4e00f30e676b66ae65b4f08cb5bcbb8392c03f54f2d5413ea99a5d1c80/nvidia_cufft_cu12-11.3.3.83-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d2dd21ec0b88cf61b62e6b43564355e5222e4a3fb394cac0db101f2dd0d4f74", size = 193118695, upload-time = "2025-03-07T01:45:27.821Z" },
-]
-
-[[package]]
-name = "nvidia-cufile-cu12"
-version = "1.13.1.3"
-source = { registry = "https://pypi.org/simple" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/bb/fe/1bcba1dfbfb8d01be8d93f07bfc502c93fa23afa6fd5ab3fc7c1df71038a/nvidia_cufile_cu12-1.13.1.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1d069003be650e131b21c932ec3d8969c1715379251f8d23a1860554b1cb24fc", size = 1197834, upload-time = "2025-03-07T01:45:50.723Z" },
-]
-
-[[package]]
-name = "nvidia-curand-cu12"
-version = "10.3.9.90"
-source = { registry = "https://pypi.org/simple" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/fb/aa/6584b56dc84ebe9cf93226a5cde4d99080c8e90ab40f0c27bda7a0f29aa1/nvidia_curand_cu12-10.3.9.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:b32331d4f4df5d6eefa0554c565b626c7216f87a06a4f56fab27c3b68a830ec9", size = 63619976, upload-time = "2025-03-07T01:46:23.323Z" },
-]
-
-[[package]]
-name = "nvidia-cusolver-cu12"
-version = "11.7.3.90"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "nvidia-cublas-cu12" },
- { name = "nvidia-cusparse-cu12" },
- { name = "nvidia-nvjitlink-cu12" },
-]
-wheels = [
- { url = "https://files.pythonhosted.org/packages/85/48/9a13d2975803e8cf2777d5ed57b87a0b6ca2cc795f9a4f59796a910bfb80/nvidia_cusolver_cu12-11.7.3.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:4376c11ad263152bd50ea295c05370360776f8c3427b30991df774f9fb26c450", size = 267506905, upload-time = "2025-03-07T01:47:16.273Z" },
-]
-
-[[package]]
-name = "nvidia-cusparse-cu12"
-version = "12.5.8.93"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "nvidia-nvjitlink-cu12" },
-]
-wheels = [
- { url = "https://files.pythonhosted.org/packages/c2/f5/e1854cb2f2bcd4280c44736c93550cc300ff4b8c95ebe370d0aa7d2b473d/nvidia_cusparse_cu12-12.5.8.93-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1ec05d76bbbd8b61b06a80e1eaf8cf4959c3d4ce8e711b65ebd0443bb0ebb13b", size = 288216466, upload-time = "2025-03-07T01:48:13.779Z" },
-]
-
-[[package]]
-name = "nvidia-cusparselt-cu12"
-version = "0.7.1"
-source = { registry = "https://pypi.org/simple" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/56/79/12978b96bd44274fe38b5dde5cfb660b1d114f70a65ef962bcbbed99b549/nvidia_cusparselt_cu12-0.7.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:f1bb701d6b930d5a7cea44c19ceb973311500847f81b634d802b7b539dc55623", size = 287193691, upload-time = "2025-02-26T00:15:44.104Z" },
-]
-
-[[package]]
-name = "nvidia-nccl-cu12"
-version = "2.27.3"
-source = { registry = "https://pypi.org/simple" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/5c/5b/4e4fff7bad39adf89f735f2bc87248c81db71205b62bcc0d5ca5b606b3c3/nvidia_nccl_cu12-2.27.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:adf27ccf4238253e0b826bce3ff5fa532d65fc42322c8bfdfaf28024c0fbe039", size = 322364134, upload-time = "2025-06-03T21:58:04.013Z" },
-]
-
-[[package]]
-name = "nvidia-nvjitlink-cu12"
-version = "12.8.93"
-source = { registry = "https://pypi.org/simple" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/f6/74/86a07f1d0f42998ca31312f998bd3b9a7eff7f52378f4f270c8679c77fb9/nvidia_nvjitlink_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:81ff63371a7ebd6e6451970684f916be2eab07321b73c9d244dc2b4da7f73b88", size = 39254836, upload-time = "2025-03-07T01:49:55.661Z" },
-]
-
-[[package]]
-name = "nvidia-nvtx-cu12"
-version = "12.8.90"
-source = { registry = "https://pypi.org/simple" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/a2/eb/86626c1bbc2edb86323022371c39aa48df6fd8b0a1647bc274577f72e90b/nvidia_nvtx_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5b17e2001cc0d751a5bc2c6ec6d26ad95913324a4adb86788c944f8ce9ba441f", size = 89954, upload-time = "2025-03-07T01:42:44.131Z" },
-]
-
-[[package]]
-name = "oauthlib"
-version = "3.3.1"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/0b/5f/19930f824ffeb0ad4372da4812c50edbd1434f678c90c2733e1188edfc63/oauthlib-3.3.1.tar.gz", hash = "sha256:0f0f8aa759826a193cf66c12ea1af1637f87b9b4622d46e866952bb022e538c9", size = 185918, upload-time = "2025-06-19T22:48:08.269Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/be/9c/92789c596b8df838baa98fa71844d84283302f7604ed565dafe5a6b5041a/oauthlib-3.3.1-py3-none-any.whl", hash = "sha256:88119c938d2b8fb88561af5f6ee0eec8cc8d552b7bb1f712743136eb7523b7a1", size = 160065, upload-time = "2025-06-19T22:48:06.508Z" },
-]
-
-[[package]]
-name = "ollama"
-version = "0.5.3"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "httpx" },
- { name = "pydantic" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/91/6d/ae96027416dcc2e98c944c050c492789502d7d7c0b95a740f0bb39268632/ollama-0.5.3.tar.gz", hash = "sha256:40b6dff729df3b24e56d4042fd9d37e231cee8e528677e0d085413a1d6692394", size = 43331, upload-time = "2025-08-07T21:44:10.422Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/be/f6/2091e50b8b6c3e6901f6eab283d5efd66fb71c86ddb1b4d68766c3eeba0f/ollama-0.5.3-py3-none-any.whl", hash = "sha256:a8303b413d99a9043dbf77ebf11ced672396b59bec27e6d5db67c88f01b279d2", size = 13490, upload-time = "2025-08-07T21:44:09.353Z" },
-]
-
-[[package]]
-name = "openai"
-version = "1.59.8"
-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/e0/c4/b4482784de63c7158f6c0afcb07fd66450ea6c912d6bddf9d7599f2eda25/openai-1.59.8.tar.gz", hash = "sha256:ac4bda5fa9819fdc6127e8ea8a63501f425c587244bc653c7c11a8ad84f953e1", size = 346775, upload-time = "2025-01-17T17:37:53.697Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/8c/cf/5b235e12ead3cd2098f9792776c966994c1bc558cba5799e12f3045227df/openai-1.59.8-py3-none-any.whl", hash = "sha256:a8b8ee35c4083b88e6da45406d883cf6bd91a98ab7dd79178b8bc24c8bfb09d9", size = 455567, upload-time = "2025-01-17T17:37:50.005Z" },
-]
-
-[[package]]
-name = "openpyxl"
-version = "3.1.5"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "et-xmlfile" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/3d/f9/88d94a75de065ea32619465d2f77b29a0469500e99012523b91cc4141cd1/openpyxl-3.1.5.tar.gz", hash = "sha256:cf0e3cf56142039133628b5acffe8ef0c12bc902d2aadd3e0fe5878dc08d1050", size = 186464, upload-time = "2024-06-28T14:03:44.161Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/c0/da/977ded879c29cbd04de313843e76868e6e13408a94ed6b987245dc7c8506/openpyxl-3.1.5-py2.py3-none-any.whl", hash = "sha256:5282c12b107bffeef825f4617dc029afaf41d0ea60823bbb665ef3079dc79de2", size = 250910, upload-time = "2024-06-28T14:03:41.161Z" },
-]
-
-[[package]]
-name = "oracledb"
-version = "3.3.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "cryptography" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/51/c9/fae18fa5d803712d188486f8e86ad4f4e00316793ca19745d7c11092c360/oracledb-3.3.0.tar.gz", hash = "sha256:e830d3544a1578296bcaa54c6e8c8ae10a58c7db467c528c4b27adbf9c8b4cb0", size = 811776, upload-time = "2025-07-29T22:34:10.489Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/3e/4b/83157e8cf02049aae2529736c5080fce8322251cd590c911c11321190391/oracledb-3.3.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e9b52231f34349165dd9a70fe7ce20bc4d6b4ee1233462937fad79396bb1af6", size = 3909356, upload-time = "2025-07-29T22:34:18.02Z" },
- { url = "https://files.pythonhosted.org/packages/af/bf/fb5fb7f53a2c5894b85a82fde274decf3482eb0a67b4e9d6975091c6e32b/oracledb-3.3.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3e9e3da89174461ceebd3401817b4020b3812bfa221fcd6419bfec877972a890", size = 2406423, upload-time = "2025-07-29T22:34:20.185Z" },
- { url = "https://files.pythonhosted.org/packages/c4/87/0a482f98efa91f5c46b17d63a8c078d6110a97e97efbb66196b89b82edfa/oracledb-3.3.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:605a58ade4e967bdf61284cc16417a36f42e5778191c702234adf558b799b822", size = 2597340, upload-time = "2025-07-29T22:34:22.265Z" },
- { url = "https://files.pythonhosted.org/packages/85/3c/7fb18f461035e2b480265af16a6989878f4eb7781d3c02f2966547aaf4e6/oracledb-3.3.0-cp310-cp310-win32.whl", hash = "sha256:f449925215cac7e41ce24107db614f49817d0a3032a595f47212bac418b14345", size = 1486535, upload-time = "2025-07-29T22:34:24.122Z" },
- { url = "https://files.pythonhosted.org/packages/1b/77/c65ad5b27608b44ee24f6e1cd54a0dd87b645907c018910b41c57ae65155/oracledb-3.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:58fb5ec16fd5ff49a2bd163e71d09adda73353bde18cea0eae9b2a41affc2a41", size = 1827509, upload-time = "2025-07-29T22:34:25.939Z" },
- { url = "https://files.pythonhosted.org/packages/3f/35/95d9a502fdc48ce1ef3a513ebd027488353441e15aa0448619abb3d09d32/oracledb-3.3.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d9adb74f837838e21898d938e3a725cf73099c65f98b0b34d77146b453e945e0", size = 3963945, upload-time = "2025-07-29T22:34:28.633Z" },
- { url = "https://files.pythonhosted.org/packages/16/a7/8f1ef447d995bb51d9fdc36356697afeceb603932f16410c12d52b2df1a4/oracledb-3.3.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4b063d1007882570f170ebde0f364e78d4a70c8f015735cc900663278b9ceef7", size = 2449385, upload-time = "2025-07-29T22:34:30.592Z" },
- { url = "https://files.pythonhosted.org/packages/b3/fa/6a78480450bc7d256808d0f38ade3385735fb5a90dab662167b4257dcf94/oracledb-3.3.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:187728f0a2d161676b8c581a9d8f15d9631a8fea1e628f6d0e9fa2f01280cd22", size = 2634943, upload-time = "2025-07-29T22:34:33.142Z" },
- { url = "https://files.pythonhosted.org/packages/5b/90/ea32b569a45fb99fac30b96f1ac0fb38b029eeebb78357bc6db4be9dde41/oracledb-3.3.0-cp311-cp311-win32.whl", hash = "sha256:920f14314f3402c5ab98f2efc5932e0547e9c0a4ca9338641357f73844e3e2b1", size = 1483549, upload-time = "2025-07-29T22:34:35.015Z" },
- { url = "https://files.pythonhosted.org/packages/81/55/ae60f72836eb8531b630299f9ed68df3fe7868c6da16f820a108155a21f9/oracledb-3.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:825edb97976468db1c7e52c78ba38d75ce7e2b71a2e88f8629bcf02be8e68a8a", size = 1834737, upload-time = "2025-07-29T22:34:36.824Z" },
- { url = "https://files.pythonhosted.org/packages/08/a8/f6b7809d70e98e113786d5a6f1294da81c046d2fa901ad656669fc5d7fae/oracledb-3.3.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9d25e37d640872731ac9b73f83cbc5fc4743cd744766bdb250488caf0d7696a8", size = 3943512, upload-time = "2025-07-29T22:34:39.237Z" },
- { url = "https://files.pythonhosted.org/packages/df/b9/8145ad8991f4864d3de4a911d439e5bc6cdbf14af448f3ab1e846a54210c/oracledb-3.3.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b0bf7cdc2b668f939aa364f552861bc7a149d7cd3f3794730d43ef07613b2bf9", size = 2276258, upload-time = "2025-07-29T22:34:41.547Z" },
- { url = "https://files.pythonhosted.org/packages/56/bf/f65635ad5df17d6e4a2083182750bb136ac663ff0e9996ce59d77d200f60/oracledb-3.3.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2fe20540fde64a6987046807ea47af93be918fd70b9766b3eb803c01e6d4202e", size = 2458811, upload-time = "2025-07-29T22:34:44.648Z" },
- { url = "https://files.pythonhosted.org/packages/7d/30/e0c130b6278c10b0e6cd77a3a1a29a785c083c549676cf701c5d180b8e63/oracledb-3.3.0-cp312-cp312-win32.whl", hash = "sha256:db080be9345cbf9506ffdaea3c13d5314605355e76d186ec4edfa49960ffb813", size = 1445525, upload-time = "2025-07-29T22:34:46.603Z" },
- { url = "https://files.pythonhosted.org/packages/1a/5c/7254f5e1a33a5d6b8bf6813d4f4fdcf5c4166ec8a7af932d987879d5595c/oracledb-3.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:be81e3afe79f6c8ece79a86d6067ad1572d2992ce1c590a086f3755a09535eb4", size = 1789976, upload-time = "2025-07-29T22:34:48.5Z" },
- { url = "https://files.pythonhosted.org/packages/3d/03/4d9fe4e8c6e54956be898e3caad4412de441e502a2679bb5ce8802db5078/oracledb-3.3.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6abc3e4432350839ecb98527707f4929bfb58959159ea440977f621e0db82ac6", size = 3918058, upload-time = "2025-07-29T22:34:51.661Z" },
- { url = "https://files.pythonhosted.org/packages/22/42/217c3b79c2e828c73435200f226128027e866ddb2e9124acf7e55b6ed16c/oracledb-3.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6770dabc441adce5c865c9f528992a7228b2e5e59924cbd8588eb159f548fc38", size = 2266909, upload-time = "2025-07-29T22:34:53.868Z" },
- { url = "https://files.pythonhosted.org/packages/a7/a8/755569f456abd62fb50ca4716cd5c8a7f4842899f587dba751108111ff1d/oracledb-3.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:55af5a49db7cbd03cef449ac51165d9aa30f26064481d68a653c81cc5a29ae80", size = 2449102, upload-time = "2025-07-29T22:34:55.969Z" },
- { url = "https://files.pythonhosted.org/packages/e0/2a/aaeef4f71cdfb0528f53af3a29a1235f243f23b46aadb9dbf4b95f5e4853/oracledb-3.3.0-cp313-cp313-win32.whl", hash = "sha256:5b4a68e4d783186cea9236fb0caa295f6da382ba1b80ca7f86d2d045cf29a993", size = 1448088, upload-time = "2025-07-29T22:34:57.766Z" },
- { url = "https://files.pythonhosted.org/packages/c8/ae/2ef3a3592360aaf9a3f816ccd814f9ad23966e100b06dabc40ea7cf01118/oracledb-3.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:ad63c0057d3f764cc2d96d4f6445b89a8ea59b42ed80f719d689292392ce62a3", size = 1789329, upload-time = "2025-07-29T22:34:59.581Z" },
- { url = "https://files.pythonhosted.org/packages/0c/a5/05347b113123245ead81501bcc25913ac8918c5b7c645deb1d6b9f32fbe3/oracledb-3.3.0-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:4c574a34a79934b9c6c3f5e4c715053ad3b46e18da38ec28d9c767e0541422ea", size = 3939747, upload-time = "2025-07-29T22:35:02.421Z" },
- { url = "https://files.pythonhosted.org/packages/4c/b9/11984a701960f1f8a3efd3980c4d50c8b56d3f3f338614a76521a6d5f61c/oracledb-3.3.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:172217e7511c58d8d3c09e9385f7d51696de27e639f336ba0a65d15009cd8cda", size = 2300535, upload-time = "2025-07-29T22:35:04.647Z" },
- { url = "https://files.pythonhosted.org/packages/b3/56/0eef985b490e7018f501dc39af12c0023360f18e3b9b0ae14809e95487e8/oracledb-3.3.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d450dcada7711007a9a8a2770f81b54c24ba1e1d2456643c3fae7a2ff26b3a29", size = 2458312, upload-time = "2025-07-29T22:35:06.725Z" },
- { url = "https://files.pythonhosted.org/packages/69/ed/83f786041a9ab8aee157156ce2526b332e603086f1ec2dfa3e8553c8204b/oracledb-3.3.0-cp314-cp314-win32.whl", hash = "sha256:b19ca41b3344dc77c53f74d31e0ca442734314593c4bec578a62efebdb1b59d7", size = 1469071, upload-time = "2025-07-29T22:35:08.76Z" },
- { url = "https://files.pythonhosted.org/packages/59/78/9627eb1630cb60b070889fce71b90e81ed276f678a1c4dfe2dccefab73f3/oracledb-3.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:a410dcf69b18ea607f3aed5cb4ecdebeb7bfb5f86e746c09a864c0f5bd563279", size = 1823668, upload-time = "2025-07-29T22:35:10.612Z" },
- { url = "https://files.pythonhosted.org/packages/cf/ae/5e44576e568395692f3819539137239b6b8ab13ee7ff072b8c64c296b203/oracledb-3.3.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2615f4f516a574fdf18e5aadca809bc90ac6ab37889d0293a9192c695fe07cd9", size = 3916715, upload-time = "2025-07-29T22:35:12.866Z" },
- { url = "https://files.pythonhosted.org/packages/02/0b/9d80aa547b97122005143a45abb5a8c8ee4d6d14fba781f4c9d1f3e07b76/oracledb-3.3.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ed608fee4e87319618be200d2befcdd17fa534e16f20cf60df6e9cbbfeadf58e", size = 2414025, upload-time = "2025-07-29T22:35:15.241Z" },
- { url = "https://files.pythonhosted.org/packages/8f/fc/d674acbcda75ed3302155b9d11f5890655f1e9577fed15afac43f36d6bfb/oracledb-3.3.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:35f6df7bec55314f56d4d87a53a1d5f6a0ded9ee106bc9346a5a4d4fe64aa667", size = 2602944, upload-time = "2025-07-29T22:35:17.024Z" },
- { url = "https://files.pythonhosted.org/packages/f4/54/0a9818a7f348ebd1ea89467b8ce11338815b5cab6bb9fa25ca13d75a444c/oracledb-3.3.0-cp39-cp39-win32.whl", hash = "sha256:0434f4ed7ded88120487b2ed3a13c37f89fc62b283960a72ddc051293e971244", size = 1488390, upload-time = "2025-07-29T22:35:18.62Z" },
- { url = "https://files.pythonhosted.org/packages/46/32/0e3084c846d12b70146e2f82031a3f17b6488bd15b6889f8cbbdabea3d46/oracledb-3.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:4c0e77e8dd1315f05f3d98d1f08df45f7bedd99612caccf315bb754cb768d692", size = 1830820, upload-time = "2025-07-29T22:35:20.624Z" },
-]
-
-[[package]]
-name = "orjson"
-version = "3.11.3"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/be/4d/8df5f83256a809c22c4d6792ce8d43bb503be0fb7a8e4da9025754b09658/orjson-3.11.3.tar.gz", hash = "sha256:1c0603b1d2ffcd43a411d64797a19556ef76958aef1c182f22dc30860152a98a", size = 5482394, upload-time = "2025-08-26T17:46:43.171Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/9b/64/4a3cef001c6cd9c64256348d4c13a7b09b857e3e1cbb5185917df67d8ced/orjson-3.11.3-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:29cb1f1b008d936803e2da3d7cba726fc47232c45df531b29edf0b232dd737e7", size = 238600, upload-time = "2025-08-26T17:44:36.875Z" },
- { url = "https://files.pythonhosted.org/packages/10/ce/0c8c87f54f79d051485903dc46226c4d3220b691a151769156054df4562b/orjson-3.11.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:97dceed87ed9139884a55db8722428e27bd8452817fbf1869c58b49fecab1120", size = 123526, upload-time = "2025-08-26T17:44:39.574Z" },
- { url = "https://files.pythonhosted.org/packages/ef/d0/249497e861f2d438f45b3ab7b7b361484237414945169aa285608f9f7019/orjson-3.11.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:58533f9e8266cb0ac298e259ed7b4d42ed3fa0b78ce76860626164de49e0d467", size = 128075, upload-time = "2025-08-26T17:44:40.672Z" },
- { url = "https://files.pythonhosted.org/packages/e5/64/00485702f640a0fd56144042a1ea196469f4a3ae93681871564bf74fa996/orjson-3.11.3-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0c212cfdd90512fe722fa9bd620de4d46cda691415be86b2e02243242ae81873", size = 130483, upload-time = "2025-08-26T17:44:41.788Z" },
- { url = "https://files.pythonhosted.org/packages/64/81/110d68dba3909171bf3f05619ad0cf187b430e64045ae4e0aa7ccfe25b15/orjson-3.11.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5ff835b5d3e67d9207343effb03760c00335f8b5285bfceefd4dc967b0e48f6a", size = 132539, upload-time = "2025-08-26T17:44:43.12Z" },
- { url = "https://files.pythonhosted.org/packages/79/92/dba25c22b0ddfafa1e6516a780a00abac28d49f49e7202eb433a53c3e94e/orjson-3.11.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f5aa4682912a450c2db89cbd92d356fef47e115dffba07992555542f344d301b", size = 135390, upload-time = "2025-08-26T17:44:44.199Z" },
- { url = "https://files.pythonhosted.org/packages/44/1d/ca2230fd55edbd87b58a43a19032d63a4b180389a97520cc62c535b726f9/orjson-3.11.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7d18dd34ea2e860553a579df02041845dee0af8985dff7f8661306f95504ddf", size = 132966, upload-time = "2025-08-26T17:44:45.719Z" },
- { url = "https://files.pythonhosted.org/packages/6e/b9/96bbc8ed3e47e52b487d504bd6861798977445fbc410da6e87e302dc632d/orjson-3.11.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d8b11701bc43be92ea42bd454910437b355dfb63696c06fe953ffb40b5f763b4", size = 131349, upload-time = "2025-08-26T17:44:46.862Z" },
- { url = "https://files.pythonhosted.org/packages/c4/3c/418fbd93d94b0df71cddf96b7fe5894d64a5d890b453ac365120daec30f7/orjson-3.11.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:90368277087d4af32d38bd55f9da2ff466d25325bf6167c8f382d8ee40cb2bbc", size = 404087, upload-time = "2025-08-26T17:44:48.079Z" },
- { url = "https://files.pythonhosted.org/packages/5b/a9/2bfd58817d736c2f63608dec0c34857339d423eeed30099b126562822191/orjson-3.11.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fd7ff459fb393358d3a155d25b275c60b07a2c83dcd7ea962b1923f5a1134569", size = 146067, upload-time = "2025-08-26T17:44:49.302Z" },
- { url = "https://files.pythonhosted.org/packages/33/ba/29023771f334096f564e48d82ed855a0ed3320389d6748a9c949e25be734/orjson-3.11.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f8d902867b699bcd09c176a280b1acdab57f924489033e53d0afe79817da37e6", size = 135506, upload-time = "2025-08-26T17:44:50.558Z" },
- { url = "https://files.pythonhosted.org/packages/39/62/b5a1eca83f54cb3aa11a9645b8a22f08d97dbd13f27f83aae7c6666a0a05/orjson-3.11.3-cp310-cp310-win32.whl", hash = "sha256:bb93562146120bb51e6b154962d3dadc678ed0fce96513fa6bc06599bb6f6edc", size = 136352, upload-time = "2025-08-26T17:44:51.698Z" },
- { url = "https://files.pythonhosted.org/packages/e3/c0/7ebfaa327d9a9ed982adc0d9420dbce9a3fec45b60ab32c6308f731333fa/orjson-3.11.3-cp310-cp310-win_amd64.whl", hash = "sha256:976c6f1975032cc327161c65d4194c549f2589d88b105a5e3499429a54479770", size = 131539, upload-time = "2025-08-26T17:44:52.974Z" },
- { url = "https://files.pythonhosted.org/packages/cd/8b/360674cd817faef32e49276187922a946468579fcaf37afdfb6c07046e92/orjson-3.11.3-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:9d2ae0cc6aeb669633e0124531f342a17d8e97ea999e42f12a5ad4adaa304c5f", size = 238238, upload-time = "2025-08-26T17:44:54.214Z" },
- { url = "https://files.pythonhosted.org/packages/05/3d/5fa9ea4b34c1a13be7d9046ba98d06e6feb1d8853718992954ab59d16625/orjson-3.11.3-cp311-cp311-macosx_15_0_arm64.whl", hash = "sha256:ba21dbb2493e9c653eaffdc38819b004b7b1b246fb77bfc93dc016fe664eac91", size = 127713, upload-time = "2025-08-26T17:44:55.596Z" },
- { url = "https://files.pythonhosted.org/packages/e5/5f/e18367823925e00b1feec867ff5f040055892fc474bf5f7875649ecfa586/orjson-3.11.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:00f1a271e56d511d1569937c0447d7dce5a99a33ea0dec76673706360a051904", size = 123241, upload-time = "2025-08-26T17:44:57.185Z" },
- { url = "https://files.pythonhosted.org/packages/0f/bd/3c66b91c4564759cf9f473251ac1650e446c7ba92a7c0f9f56ed54f9f0e6/orjson-3.11.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b67e71e47caa6680d1b6f075a396d04fa6ca8ca09aafb428731da9b3ea32a5a6", size = 127895, upload-time = "2025-08-26T17:44:58.349Z" },
- { url = "https://files.pythonhosted.org/packages/82/b5/dc8dcd609db4766e2967a85f63296c59d4722b39503e5b0bf7fd340d387f/orjson-3.11.3-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d7d012ebddffcce8c85734a6d9e5f08180cd3857c5f5a3ac70185b43775d043d", size = 130303, upload-time = "2025-08-26T17:44:59.491Z" },
- { url = "https://files.pythonhosted.org/packages/48/c2/d58ec5fd1270b2aa44c862171891adc2e1241bd7dab26c8f46eb97c6c6f1/orjson-3.11.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dd759f75d6b8d1b62012b7f5ef9461d03c804f94d539a5515b454ba3a6588038", size = 132366, upload-time = "2025-08-26T17:45:00.654Z" },
- { url = "https://files.pythonhosted.org/packages/73/87/0ef7e22eb8dd1ef940bfe3b9e441db519e692d62ed1aae365406a16d23d0/orjson-3.11.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6890ace0809627b0dff19cfad92d69d0fa3f089d3e359a2a532507bb6ba34efb", size = 135180, upload-time = "2025-08-26T17:45:02.424Z" },
- { url = "https://files.pythonhosted.org/packages/bb/6a/e5bf7b70883f374710ad74faf99bacfc4b5b5a7797c1d5e130350e0e28a3/orjson-3.11.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9d4a5e041ae435b815e568537755773d05dac031fee6a57b4ba70897a44d9d2", size = 132741, upload-time = "2025-08-26T17:45:03.663Z" },
- { url = "https://files.pythonhosted.org/packages/bd/0c/4577fd860b6386ffaa56440e792af01c7882b56d2766f55384b5b0e9d39b/orjson-3.11.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2d68bf97a771836687107abfca089743885fb664b90138d8761cce61d5625d55", size = 131104, upload-time = "2025-08-26T17:45:04.939Z" },
- { url = "https://files.pythonhosted.org/packages/66/4b/83e92b2d67e86d1c33f2ea9411742a714a26de63641b082bdbf3d8e481af/orjson-3.11.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:bfc27516ec46f4520b18ef645864cee168d2a027dbf32c5537cb1f3e3c22dac1", size = 403887, upload-time = "2025-08-26T17:45:06.228Z" },
- { url = "https://files.pythonhosted.org/packages/6d/e5/9eea6a14e9b5ceb4a271a1fd2e1dec5f2f686755c0fab6673dc6ff3433f4/orjson-3.11.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f66b001332a017d7945e177e282a40b6997056394e3ed7ddb41fb1813b83e824", size = 145855, upload-time = "2025-08-26T17:45:08.338Z" },
- { url = "https://files.pythonhosted.org/packages/45/78/8d4f5ad0c80ba9bf8ac4d0fc71f93a7d0dc0844989e645e2074af376c307/orjson-3.11.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:212e67806525d2561efbfe9e799633b17eb668b8964abed6b5319b2f1cfbae1f", size = 135361, upload-time = "2025-08-26T17:45:09.625Z" },
- { url = "https://files.pythonhosted.org/packages/0b/5f/16386970370178d7a9b438517ea3d704efcf163d286422bae3b37b88dbb5/orjson-3.11.3-cp311-cp311-win32.whl", hash = "sha256:6e8e0c3b85575a32f2ffa59de455f85ce002b8bdc0662d6b9c2ed6d80ab5d204", size = 136190, upload-time = "2025-08-26T17:45:10.962Z" },
- { url = "https://files.pythonhosted.org/packages/09/60/db16c6f7a41dd8ac9fb651f66701ff2aeb499ad9ebc15853a26c7c152448/orjson-3.11.3-cp311-cp311-win_amd64.whl", hash = "sha256:6be2f1b5d3dc99a5ce5ce162fc741c22ba9f3443d3dd586e6a1211b7bc87bc7b", size = 131389, upload-time = "2025-08-26T17:45:12.285Z" },
- { url = "https://files.pythonhosted.org/packages/3e/2a/bb811ad336667041dea9b8565c7c9faf2f59b47eb5ab680315eea612ef2e/orjson-3.11.3-cp311-cp311-win_arm64.whl", hash = "sha256:fafb1a99d740523d964b15c8db4eabbfc86ff29f84898262bf6e3e4c9e97e43e", size = 126120, upload-time = "2025-08-26T17:45:13.515Z" },
- { url = "https://files.pythonhosted.org/packages/3d/b0/a7edab2a00cdcb2688e1c943401cb3236323e7bfd2839815c6131a3742f4/orjson-3.11.3-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:8c752089db84333e36d754c4baf19c0e1437012242048439c7e80eb0e6426e3b", size = 238259, upload-time = "2025-08-26T17:45:15.093Z" },
- { url = "https://files.pythonhosted.org/packages/e1/c6/ff4865a9cc398a07a83342713b5932e4dc3cb4bf4bc04e8f83dedfc0d736/orjson-3.11.3-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:9b8761b6cf04a856eb544acdd82fc594b978f12ac3602d6374a7edb9d86fd2c2", size = 127633, upload-time = "2025-08-26T17:45:16.417Z" },
- { url = "https://files.pythonhosted.org/packages/6e/e6/e00bea2d9472f44fe8794f523e548ce0ad51eb9693cf538a753a27b8bda4/orjson-3.11.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b13974dc8ac6ba22feaa867fc19135a3e01a134b4f7c9c28162fed4d615008a", size = 123061, upload-time = "2025-08-26T17:45:17.673Z" },
- { url = "https://files.pythonhosted.org/packages/54/31/9fbb78b8e1eb3ac605467cb846e1c08d0588506028b37f4ee21f978a51d4/orjson-3.11.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f83abab5bacb76d9c821fd5c07728ff224ed0e52d7a71b7b3de822f3df04e15c", size = 127956, upload-time = "2025-08-26T17:45:19.172Z" },
- { url = "https://files.pythonhosted.org/packages/36/88/b0604c22af1eed9f98d709a96302006915cfd724a7ebd27d6dd11c22d80b/orjson-3.11.3-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e6fbaf48a744b94091a56c62897b27c31ee2da93d826aa5b207131a1e13d4064", size = 130790, upload-time = "2025-08-26T17:45:20.586Z" },
- { url = "https://files.pythonhosted.org/packages/0e/9d/1c1238ae9fffbfed51ba1e507731b3faaf6b846126a47e9649222b0fd06f/orjson-3.11.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bc779b4f4bba2847d0d2940081a7b6f7b5877e05408ffbb74fa1faf4a136c424", size = 132385, upload-time = "2025-08-26T17:45:22.036Z" },
- { url = "https://files.pythonhosted.org/packages/a3/b5/c06f1b090a1c875f337e21dd71943bc9d84087f7cdf8c6e9086902c34e42/orjson-3.11.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd4b909ce4c50faa2192da6bb684d9848d4510b736b0611b6ab4020ea6fd2d23", size = 135305, upload-time = "2025-08-26T17:45:23.4Z" },
- { url = "https://files.pythonhosted.org/packages/a0/26/5f028c7d81ad2ebbf84414ba6d6c9cac03f22f5cd0d01eb40fb2d6a06b07/orjson-3.11.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:524b765ad888dc5518bbce12c77c2e83dee1ed6b0992c1790cc5fb49bb4b6667", size = 132875, upload-time = "2025-08-26T17:45:25.182Z" },
- { url = "https://files.pythonhosted.org/packages/fe/d4/b8df70d9cfb56e385bf39b4e915298f9ae6c61454c8154a0f5fd7efcd42e/orjson-3.11.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:84fd82870b97ae3cdcea9d8746e592b6d40e1e4d4527835fc520c588d2ded04f", size = 130940, upload-time = "2025-08-26T17:45:27.209Z" },
- { url = "https://files.pythonhosted.org/packages/da/5e/afe6a052ebc1a4741c792dd96e9f65bf3939d2094e8b356503b68d48f9f5/orjson-3.11.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:fbecb9709111be913ae6879b07bafd4b0785b44c1eb5cac8ac76da048b3885a1", size = 403852, upload-time = "2025-08-26T17:45:28.478Z" },
- { url = "https://files.pythonhosted.org/packages/f8/90/7bbabafeb2ce65915e9247f14a56b29c9334003536009ef5b122783fe67e/orjson-3.11.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:9dba358d55aee552bd868de348f4736ca5a4086d9a62e2bfbbeeb5629fe8b0cc", size = 146293, upload-time = "2025-08-26T17:45:29.86Z" },
- { url = "https://files.pythonhosted.org/packages/27/b3/2d703946447da8b093350570644a663df69448c9d9330e5f1d9cce997f20/orjson-3.11.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:eabcf2e84f1d7105f84580e03012270c7e97ecb1fb1618bda395061b2a84a049", size = 135470, upload-time = "2025-08-26T17:45:31.243Z" },
- { url = "https://files.pythonhosted.org/packages/38/70/b14dcfae7aff0e379b0119c8a812f8396678919c431efccc8e8a0263e4d9/orjson-3.11.3-cp312-cp312-win32.whl", hash = "sha256:3782d2c60b8116772aea8d9b7905221437fdf53e7277282e8d8b07c220f96cca", size = 136248, upload-time = "2025-08-26T17:45:32.567Z" },
- { url = "https://files.pythonhosted.org/packages/35/b8/9e3127d65de7fff243f7f3e53f59a531bf6bb295ebe5db024c2503cc0726/orjson-3.11.3-cp312-cp312-win_amd64.whl", hash = "sha256:79b44319268af2eaa3e315b92298de9a0067ade6e6003ddaef72f8e0bedb94f1", size = 131437, upload-time = "2025-08-26T17:45:34.949Z" },
- { url = "https://files.pythonhosted.org/packages/51/92/a946e737d4d8a7fd84a606aba96220043dcc7d6988b9e7551f7f6d5ba5ad/orjson-3.11.3-cp312-cp312-win_arm64.whl", hash = "sha256:0e92a4e83341ef79d835ca21b8bd13e27c859e4e9e4d7b63defc6e58462a3710", size = 125978, upload-time = "2025-08-26T17:45:36.422Z" },
- { url = "https://files.pythonhosted.org/packages/fc/79/8932b27293ad35919571f77cb3693b5906cf14f206ef17546052a241fdf6/orjson-3.11.3-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:af40c6612fd2a4b00de648aa26d18186cd1322330bd3a3cc52f87c699e995810", size = 238127, upload-time = "2025-08-26T17:45:38.146Z" },
- { url = "https://files.pythonhosted.org/packages/1c/82/cb93cd8cf132cd7643b30b6c5a56a26c4e780c7a145db6f83de977b540ce/orjson-3.11.3-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:9f1587f26c235894c09e8b5b7636a38091a9e6e7fe4531937534749c04face43", size = 127494, upload-time = "2025-08-26T17:45:39.57Z" },
- { url = "https://files.pythonhosted.org/packages/a4/b8/2d9eb181a9b6bb71463a78882bcac1027fd29cf62c38a40cc02fc11d3495/orjson-3.11.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:61dcdad16da5bb486d7227a37a2e789c429397793a6955227cedbd7252eb5a27", size = 123017, upload-time = "2025-08-26T17:45:40.876Z" },
- { url = "https://files.pythonhosted.org/packages/b4/14/a0e971e72d03b509190232356d54c0f34507a05050bd026b8db2bf2c192c/orjson-3.11.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:11c6d71478e2cbea0a709e8a06365fa63da81da6498a53e4c4f065881d21ae8f", size = 127898, upload-time = "2025-08-26T17:45:42.188Z" },
- { url = "https://files.pythonhosted.org/packages/8e/af/dc74536722b03d65e17042cc30ae586161093e5b1f29bccda24765a6ae47/orjson-3.11.3-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff94112e0098470b665cb0ed06efb187154b63649403b8d5e9aedeb482b4548c", size = 130742, upload-time = "2025-08-26T17:45:43.511Z" },
- { url = "https://files.pythonhosted.org/packages/62/e6/7a3b63b6677bce089fe939353cda24a7679825c43a24e49f757805fc0d8a/orjson-3.11.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae8b756575aaa2a855a75192f356bbda11a89169830e1439cfb1a3e1a6dde7be", size = 132377, upload-time = "2025-08-26T17:45:45.525Z" },
- { url = "https://files.pythonhosted.org/packages/fc/cd/ce2ab93e2e7eaf518f0fd15e3068b8c43216c8a44ed82ac2b79ce5cef72d/orjson-3.11.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c9416cc19a349c167ef76135b2fe40d03cea93680428efee8771f3e9fb66079d", size = 135313, upload-time = "2025-08-26T17:45:46.821Z" },
- { url = "https://files.pythonhosted.org/packages/d0/b4/f98355eff0bd1a38454209bbc73372ce351ba29933cb3e2eba16c04b9448/orjson-3.11.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b822caf5b9752bc6f246eb08124c3d12bf2175b66ab74bac2ef3bbf9221ce1b2", size = 132908, upload-time = "2025-08-26T17:45:48.126Z" },
- { url = "https://files.pythonhosted.org/packages/eb/92/8f5182d7bc2a1bed46ed960b61a39af8389f0ad476120cd99e67182bfb6d/orjson-3.11.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:414f71e3bdd5573893bf5ecdf35c32b213ed20aa15536fe2f588f946c318824f", size = 130905, upload-time = "2025-08-26T17:45:49.414Z" },
- { url = "https://files.pythonhosted.org/packages/1a/60/c41ca753ce9ffe3d0f67b9b4c093bdd6e5fdb1bc53064f992f66bb99954d/orjson-3.11.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:828e3149ad8815dc14468f36ab2a4b819237c155ee1370341b91ea4c8672d2ee", size = 403812, upload-time = "2025-08-26T17:45:51.085Z" },
- { url = "https://files.pythonhosted.org/packages/dd/13/e4a4f16d71ce1868860db59092e78782c67082a8f1dc06a3788aef2b41bc/orjson-3.11.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ac9e05f25627ffc714c21f8dfe3a579445a5c392a9c8ae7ba1d0e9fb5333f56e", size = 146277, upload-time = "2025-08-26T17:45:52.851Z" },
- { url = "https://files.pythonhosted.org/packages/8d/8b/bafb7f0afef9344754a3a0597a12442f1b85a048b82108ef2c956f53babd/orjson-3.11.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e44fbe4000bd321d9f3b648ae46e0196d21577cf66ae684a96ff90b1f7c93633", size = 135418, upload-time = "2025-08-26T17:45:54.806Z" },
- { url = "https://files.pythonhosted.org/packages/60/d4/bae8e4f26afb2c23bea69d2f6d566132584d1c3a5fe89ee8c17b718cab67/orjson-3.11.3-cp313-cp313-win32.whl", hash = "sha256:2039b7847ba3eec1f5886e75e6763a16e18c68a63efc4b029ddf994821e2e66b", size = 136216, upload-time = "2025-08-26T17:45:57.182Z" },
- { url = "https://files.pythonhosted.org/packages/88/76/224985d9f127e121c8cad882cea55f0ebe39f97925de040b75ccd4b33999/orjson-3.11.3-cp313-cp313-win_amd64.whl", hash = "sha256:29be5ac4164aa8bdcba5fa0700a3c9c316b411d8ed9d39ef8a882541bd452fae", size = 131362, upload-time = "2025-08-26T17:45:58.56Z" },
- { url = "https://files.pythonhosted.org/packages/e2/cf/0dce7a0be94bd36d1346be5067ed65ded6adb795fdbe3abd234c8d576d01/orjson-3.11.3-cp313-cp313-win_arm64.whl", hash = "sha256:18bd1435cb1f2857ceb59cfb7de6f92593ef7b831ccd1b9bfb28ca530e539dce", size = 125989, upload-time = "2025-08-26T17:45:59.95Z" },
- { url = "https://files.pythonhosted.org/packages/ef/77/d3b1fef1fc6aaeed4cbf3be2b480114035f4df8fa1a99d2dac1d40d6e924/orjson-3.11.3-cp314-cp314-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:cf4b81227ec86935568c7edd78352a92e97af8da7bd70bdfdaa0d2e0011a1ab4", size = 238115, upload-time = "2025-08-26T17:46:01.669Z" },
- { url = "https://files.pythonhosted.org/packages/e4/6d/468d21d49bb12f900052edcfbf52c292022d0a323d7828dc6376e6319703/orjson-3.11.3-cp314-cp314-macosx_15_0_arm64.whl", hash = "sha256:bc8bc85b81b6ac9fc4dae393a8c159b817f4c2c9dee5d12b773bddb3b95fc07e", size = 127493, upload-time = "2025-08-26T17:46:03.466Z" },
- { url = "https://files.pythonhosted.org/packages/67/46/1e2588700d354aacdf9e12cc2d98131fb8ac6f31ca65997bef3863edb8ff/orjson-3.11.3-cp314-cp314-manylinux_2_34_aarch64.whl", hash = "sha256:88dcfc514cfd1b0de038443c7b3e6a9797ffb1b3674ef1fd14f701a13397f82d", size = 122998, upload-time = "2025-08-26T17:46:04.803Z" },
- { url = "https://files.pythonhosted.org/packages/3b/94/11137c9b6adb3779f1b34fd98be51608a14b430dbc02c6d41134fbba484c/orjson-3.11.3-cp314-cp314-manylinux_2_34_x86_64.whl", hash = "sha256:d61cd543d69715d5fc0a690c7c6f8dcc307bc23abef9738957981885f5f38229", size = 132915, upload-time = "2025-08-26T17:46:06.237Z" },
- { url = "https://files.pythonhosted.org/packages/10/61/dccedcf9e9bcaac09fdabe9eaee0311ca92115699500efbd31950d878833/orjson-3.11.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2b7b153ed90ababadbef5c3eb39549f9476890d339cf47af563aea7e07db2451", size = 130907, upload-time = "2025-08-26T17:46:07.581Z" },
- { url = "https://files.pythonhosted.org/packages/0e/fd/0e935539aa7b08b3ca0f817d73034f7eb506792aae5ecc3b7c6e679cdf5f/orjson-3.11.3-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:7909ae2460f5f494fecbcd10613beafe40381fd0316e35d6acb5f3a05bfda167", size = 403852, upload-time = "2025-08-26T17:46:08.982Z" },
- { url = "https://files.pythonhosted.org/packages/4a/2b/50ae1a5505cd1043379132fdb2adb8a05f37b3e1ebffe94a5073321966fd/orjson-3.11.3-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:2030c01cbf77bc67bee7eef1e7e31ecf28649353987775e3583062c752da0077", size = 146309, upload-time = "2025-08-26T17:46:10.576Z" },
- { url = "https://files.pythonhosted.org/packages/cd/1d/a473c158e380ef6f32753b5f39a69028b25ec5be331c2049a2201bde2e19/orjson-3.11.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:a0169ebd1cbd94b26c7a7ad282cf5c2744fce054133f959e02eb5265deae1872", size = 135424, upload-time = "2025-08-26T17:46:12.386Z" },
- { url = "https://files.pythonhosted.org/packages/da/09/17d9d2b60592890ff7382e591aa1d9afb202a266b180c3d4049b1ec70e4a/orjson-3.11.3-cp314-cp314-win32.whl", hash = "sha256:0c6d7328c200c349e3a4c6d8c83e0a5ad029bdc2d417f234152bf34842d0fc8d", size = 136266, upload-time = "2025-08-26T17:46:13.853Z" },
- { url = "https://files.pythonhosted.org/packages/15/58/358f6846410a6b4958b74734727e582ed971e13d335d6c7ce3e47730493e/orjson-3.11.3-cp314-cp314-win_amd64.whl", hash = "sha256:317bbe2c069bbc757b1a2e4105b64aacd3bc78279b66a6b9e51e846e4809f804", size = 131351, upload-time = "2025-08-26T17:46:15.27Z" },
- { url = "https://files.pythonhosted.org/packages/28/01/d6b274a0635be0468d4dbd9cafe80c47105937a0d42434e805e67cd2ed8b/orjson-3.11.3-cp314-cp314-win_arm64.whl", hash = "sha256:e8f6a7a27d7b7bec81bd5924163e9af03d49bbb63013f107b48eb5d16db711bc", size = 125985, upload-time = "2025-08-26T17:46:16.67Z" },
- { url = "https://files.pythonhosted.org/packages/99/a6/18d88ccf8e5d8f711310eba9b4f6562f4aa9d594258efdc4dcf8c1550090/orjson-3.11.3-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:56afaf1e9b02302ba636151cfc49929c1bb66b98794291afd0e5f20fecaf757c", size = 238221, upload-time = "2025-08-26T17:46:18.113Z" },
- { url = "https://files.pythonhosted.org/packages/ee/18/e210365a17bf984c89db40c8be65da164b4ce6a866a2a0ae1d6407c2630b/orjson-3.11.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:913f629adef31d2d350d41c051ce7e33cf0fd06a5d1cb28d49b1899b23b903aa", size = 123209, upload-time = "2025-08-26T17:46:19.688Z" },
- { url = "https://files.pythonhosted.org/packages/26/43/6b3f8ec15fa910726ed94bd2e618f86313ad1cae7c3c8c6b9b8a3a161814/orjson-3.11.3-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e0a23b41f8f98b4e61150a03f83e4f0d566880fe53519d445a962929a4d21045", size = 127881, upload-time = "2025-08-26T17:46:21.502Z" },
- { url = "https://files.pythonhosted.org/packages/4a/ed/f41d2406355ce67efdd4ab504732b27bea37b7dbdab3eb86314fe764f1b9/orjson-3.11.3-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3d721fee37380a44f9d9ce6c701b3960239f4fb3d5ceea7f31cbd43882edaa2f", size = 130306, upload-time = "2025-08-26T17:46:22.914Z" },
- { url = "https://files.pythonhosted.org/packages/3e/a1/1be02950f92c82e64602d3d284bd76d9fc82a6b92c9ce2a387e57a825a11/orjson-3.11.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73b92a5b69f31b1a58c0c7e31080aeaec49c6e01b9522e71ff38d08f15aa56de", size = 132383, upload-time = "2025-08-26T17:46:24.33Z" },
- { url = "https://files.pythonhosted.org/packages/39/49/46766ac00c68192b516a15ffc44c2a9789ca3468b8dc8a500422d99bf0dd/orjson-3.11.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d2489b241c19582b3f1430cc5d732caefc1aaf378d97e7fb95b9e56bed11725f", size = 135159, upload-time = "2025-08-26T17:46:25.741Z" },
- { url = "https://files.pythonhosted.org/packages/47/e1/27fd5e7600fdd82996329d48ee56f6e9e9ae4d31eadbc7f93fd2ff0d8214/orjson-3.11.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c5189a5dab8b0312eadaf9d58d3049b6a52c454256493a557405e77a3d67ab7f", size = 132690, upload-time = "2025-08-26T17:46:27.271Z" },
- { url = "https://files.pythonhosted.org/packages/d8/21/f57ef08799a68c36ef96fe561101afeef735caa80814636b2e18c234e405/orjson-3.11.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:9d8787bdfbb65a85ea76d0e96a3b1bed7bf0fbcb16d40408dc1172ad784a49d2", size = 131086, upload-time = "2025-08-26T17:46:33.067Z" },
- { url = "https://files.pythonhosted.org/packages/cd/84/a3a24306a9dc482e929232c65f5b8c69188136edd6005441d8cc4754f7ea/orjson-3.11.3-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:8e531abd745f51f8035e207e75e049553a86823d189a51809c078412cefb399a", size = 403884, upload-time = "2025-08-26T17:46:34.55Z" },
- { url = "https://files.pythonhosted.org/packages/11/98/fdae5b2c28bc358e6868e54c8eca7398c93d6a511f0436b61436ad1b04dc/orjson-3.11.3-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:8ab962931015f170b97a3dd7bd933399c1bae8ed8ad0fb2a7151a5654b6941c7", size = 145837, upload-time = "2025-08-26T17:46:36.46Z" },
- { url = "https://files.pythonhosted.org/packages/7d/a9/2fe5cd69ed231f3ed88b1ad36a6957e3d2c876eb4b2c6b17b8ae0a6681fc/orjson-3.11.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:124d5ba71fee9c9902c4a7baa9425e663f7f0aecf73d31d54fe3dd357d62c1a7", size = 135325, upload-time = "2025-08-26T17:46:38.03Z" },
- { url = "https://files.pythonhosted.org/packages/ac/a4/7d4c8aefb45f6c8d7d527d84559a3a7e394b9fd1d424a2b5bcaf75fa68e7/orjson-3.11.3-cp39-cp39-win32.whl", hash = "sha256:22724d80ee5a815a44fc76274bb7ba2e7464f5564aacb6ecddaa9970a83e3225", size = 136184, upload-time = "2025-08-26T17:46:39.542Z" },
- { url = "https://files.pythonhosted.org/packages/9a/1f/1d6a24d22001e96c0afcf1806b6eabee1109aebd2ef20ec6698f6a6012d7/orjson-3.11.3-cp39-cp39-win_amd64.whl", hash = "sha256:215c595c792a87d4407cb72dd5e0f6ee8e694ceeb7f9102b533c5a9bf2a916bb", size = 131373, upload-time = "2025-08-26T17:46:41.227Z" },
-]
-
-[[package]]
-name = "ormsgpack"
-version = "1.10.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/92/36/44eed5ef8ce93cded76a576780bab16425ce7876f10d3e2e6265e46c21ea/ormsgpack-1.10.0.tar.gz", hash = "sha256:7f7a27efd67ef22d7182ec3b7fa7e9d147c3ad9be2a24656b23c989077e08b16", size = 58629, upload-time = "2025-05-24T19:07:53.944Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/fc/74/c2dd5daf069e3798d09d5746000f9b210de04df83834e5cb47f0ace51892/ormsgpack-1.10.0-cp310-cp310-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:8a52c7ce7659459f3dc8dec9fd6a6c76f855a0a7e2b61f26090982ac10b95216", size = 376280, upload-time = "2025-05-24T19:06:51.3Z" },
- { url = "https://files.pythonhosted.org/packages/78/7b/30ff4bffb709e8a242005a8c4d65714fd96308ad640d31cff1b85c0d8cc4/ormsgpack-1.10.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:060f67fe927582f4f63a1260726d019204b72f460cf20930e6c925a1d129f373", size = 204335, upload-time = "2025-05-24T19:06:53.442Z" },
- { url = "https://files.pythonhosted.org/packages/8f/3f/c95b7d142819f801a0acdbd04280e8132e43b6e5a8920173e8eb92ea0e6a/ormsgpack-1.10.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e7058ef6092f995561bf9f71d6c9a4da867b6cc69d2e94cb80184f579a3ceed5", size = 215373, upload-time = "2025-05-24T19:06:55.153Z" },
- { url = "https://files.pythonhosted.org/packages/ef/1a/e30f4bcf386db2015d1686d1da6110c95110294d8ea04f86091dd5eb3361/ormsgpack-1.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10f6f3509c1b0e51b15552d314b1d409321718122e90653122ce4b997f01453a", size = 216469, upload-time = "2025-05-24T19:06:56.555Z" },
- { url = "https://files.pythonhosted.org/packages/96/fc/7e44aeade22b91883586f45b7278c118fd210834c069774891447f444fc9/ormsgpack-1.10.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:51c1edafd5c72b863b1f875ec31c529f09c872a5ff6fe473b9dfaf188ccc3227", size = 384590, upload-time = "2025-05-24T19:06:58.286Z" },
- { url = "https://files.pythonhosted.org/packages/ec/78/f92c24e8446697caa83c122f10b6cf5e155eddf81ce63905c8223a260482/ormsgpack-1.10.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:c780b44107a547a9e9327270f802fa4d6b0f6667c9c03c3338c0ce812259a0f7", size = 478891, upload-time = "2025-05-24T19:07:00.126Z" },
- { url = "https://files.pythonhosted.org/packages/5a/75/87449690253c64bea2b663c7c8f2dbc9ad39d73d0b38db74bdb0f3947b16/ormsgpack-1.10.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:137aab0d5cdb6df702da950a80405eb2b7038509585e32b4e16289604ac7cb84", size = 390121, upload-time = "2025-05-24T19:07:01.777Z" },
- { url = "https://files.pythonhosted.org/packages/69/cc/c83257faf3a5169ec29dd87121317a25711da9412ee8c1e82f2e1a00c0be/ormsgpack-1.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:3e666cb63030538fa5cd74b1e40cb55b6fdb6e2981f024997a288bf138ebad07", size = 121196, upload-time = "2025-05-24T19:07:03.47Z" },
- { url = "https://files.pythonhosted.org/packages/30/27/7da748bc0d7d567950a378dee5a32477ed5d15462ab186918b5f25cac1ad/ormsgpack-1.10.0-cp311-cp311-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:4bb7df307e17b36cbf7959cd642c47a7f2046ae19408c564e437f0ec323a7775", size = 376275, upload-time = "2025-05-24T19:07:05.128Z" },
- { url = "https://files.pythonhosted.org/packages/7b/65/c082cc8c74a914dbd05af0341c761c73c3d9960b7432bbf9b8e1e20811af/ormsgpack-1.10.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8817ae439c671779e1127ee62f0ac67afdeaeeacb5f0db45703168aa74a2e4af", size = 204335, upload-time = "2025-05-24T19:07:06.423Z" },
- { url = "https://files.pythonhosted.org/packages/46/62/17ef7e5d9766c79355b9c594cc9328c204f1677bc35da0595cc4e46449f0/ormsgpack-1.10.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2f345f81e852035d80232e64374d3a104139d60f8f43c6c5eade35c4bac5590e", size = 215372, upload-time = "2025-05-24T19:07:08.149Z" },
- { url = "https://files.pythonhosted.org/packages/4e/92/7c91e8115fc37e88d1a35e13200fda3054ff5d2e5adf017345e58cea4834/ormsgpack-1.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21de648a1c7ef692bdd287fb08f047bd5371d7462504c0a7ae1553c39fee35e3", size = 216470, upload-time = "2025-05-24T19:07:09.903Z" },
- { url = "https://files.pythonhosted.org/packages/2c/86/ce053c52e2517b90e390792d83e926a7a523c1bce5cc63d0a7cd05ce6cf6/ormsgpack-1.10.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3a7d844ae9cbf2112c16086dd931b2acefce14cefd163c57db161170c2bfa22b", size = 384591, upload-time = "2025-05-24T19:07:11.24Z" },
- { url = "https://files.pythonhosted.org/packages/07/e8/2ad59f2ab222c6029e500bc966bfd2fe5cb099f8ab6b7ebeb50ddb1a6fe5/ormsgpack-1.10.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:e4d80585403d86d7f800cf3d0aafac1189b403941e84e90dd5102bb2b92bf9d5", size = 478892, upload-time = "2025-05-24T19:07:13.147Z" },
- { url = "https://files.pythonhosted.org/packages/f4/73/f55e4b47b7b18fd8e7789680051bf830f1e39c03f1d9ed993cd0c3e97215/ormsgpack-1.10.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:da1de515a87e339e78a3ccf60e39f5fb740edac3e9e82d3c3d209e217a13ac08", size = 390122, upload-time = "2025-05-24T19:07:14.557Z" },
- { url = "https://files.pythonhosted.org/packages/f7/87/073251cdb93d4c6241748568b3ad1b2a76281fb2002eed16a3a4043d61cf/ormsgpack-1.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:57c4601812684024132cbb32c17a7d4bb46ffc7daf2fddf5b697391c2c4f142a", size = 121197, upload-time = "2025-05-24T19:07:15.981Z" },
- { url = "https://files.pythonhosted.org/packages/99/95/f3ab1a7638f6aa9362e87916bb96087fbbc5909db57e19f12ad127560e1e/ormsgpack-1.10.0-cp312-cp312-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:4e159d50cd4064d7540e2bc6a0ab66eab70b0cc40c618b485324ee17037527c0", size = 376806, upload-time = "2025-05-24T19:07:17.221Z" },
- { url = "https://files.pythonhosted.org/packages/6c/2b/42f559f13c0b0f647b09d749682851d47c1a7e48308c43612ae6833499c8/ormsgpack-1.10.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eeb47c85f3a866e29279d801115b554af0fefc409e2ed8aa90aabfa77efe5cc6", size = 204433, upload-time = "2025-05-24T19:07:18.569Z" },
- { url = "https://files.pythonhosted.org/packages/45/42/1ca0cb4d8c80340a89a4af9e6d8951fb8ba0d076a899d2084eadf536f677/ormsgpack-1.10.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c28249574934534c9bd5dce5485c52f21bcea0ee44d13ece3def6e3d2c3798b5", size = 215547, upload-time = "2025-05-24T19:07:20.245Z" },
- { url = "https://files.pythonhosted.org/packages/0a/38/184a570d7c44c0260bc576d1daaac35b2bfd465a50a08189518505748b9a/ormsgpack-1.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1957dcadbb16e6a981cd3f9caef9faf4c2df1125e2a1b702ee8236a55837ce07", size = 216746, upload-time = "2025-05-24T19:07:21.83Z" },
- { url = "https://files.pythonhosted.org/packages/69/2f/1aaffd08f6b7fdc2a57336a80bdfb8df24e6a65ada5aa769afecfcbc6cc6/ormsgpack-1.10.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3b29412558c740bf6bac156727aa85ac67f9952cd6f071318f29ee72e1a76044", size = 384783, upload-time = "2025-05-24T19:07:23.674Z" },
- { url = "https://files.pythonhosted.org/packages/a9/63/3e53d6f43bb35e00c98f2b8ab2006d5138089ad254bc405614fbf0213502/ormsgpack-1.10.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:6933f350c2041ec189fe739f0ba7d6117c8772f5bc81f45b97697a84d03020dd", size = 479076, upload-time = "2025-05-24T19:07:25.047Z" },
- { url = "https://files.pythonhosted.org/packages/b8/19/fa1121b03b61402bb4d04e35d164e2320ef73dfb001b57748110319dd014/ormsgpack-1.10.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9a86de06d368fcc2e58b79dece527dc8ca831e0e8b9cec5d6e633d2777ec93d0", size = 390447, upload-time = "2025-05-24T19:07:26.568Z" },
- { url = "https://files.pythonhosted.org/packages/b0/0d/73143ecb94ac4a5dcba223402139240a75dee0cc6ba8a543788a5646407a/ormsgpack-1.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:35fa9f81e5b9a0dab42e09a73f7339ecffdb978d6dbf9deb2ecf1e9fc7808722", size = 121401, upload-time = "2025-05-24T19:07:28.308Z" },
- { url = "https://files.pythonhosted.org/packages/61/f8/ec5f4e03268d0097545efaab2893aa63f171cf2959cb0ea678a5690e16a1/ormsgpack-1.10.0-cp313-cp313-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:8d816d45175a878993b7372bd5408e0f3ec5a40f48e2d5b9d8f1cc5d31b61f1f", size = 376806, upload-time = "2025-05-24T19:07:29.555Z" },
- { url = "https://files.pythonhosted.org/packages/c1/19/b3c53284aad1e90d4d7ed8c881a373d218e16675b8b38e3569d5b40cc9b8/ormsgpack-1.10.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a90345ccb058de0f35262893751c603b6376b05f02be2b6f6b7e05d9dd6d5643", size = 204433, upload-time = "2025-05-24T19:07:30.977Z" },
- { url = "https://files.pythonhosted.org/packages/09/0b/845c258f59df974a20a536c06cace593698491defdd3d026a8a5f9b6e745/ormsgpack-1.10.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:144b5e88f1999433e54db9d637bae6fe21e935888be4e3ac3daecd8260bd454e", size = 215549, upload-time = "2025-05-24T19:07:32.345Z" },
- { url = "https://files.pythonhosted.org/packages/61/56/57fce8fb34ca6c9543c026ebebf08344c64dbb7b6643d6ddd5355d37e724/ormsgpack-1.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2190b352509d012915921cca76267db136cd026ddee42f1b0d9624613cc7058c", size = 216747, upload-time = "2025-05-24T19:07:34.075Z" },
- { url = "https://files.pythonhosted.org/packages/b8/3f/655b5f6a2475c8d209f5348cfbaaf73ce26237b92d79ef2ad439407dd0fa/ormsgpack-1.10.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:86fd9c1737eaba43d3bb2730add9c9e8b5fbed85282433705dd1b1e88ea7e6fb", size = 384785, upload-time = "2025-05-24T19:07:35.83Z" },
- { url = "https://files.pythonhosted.org/packages/4b/94/687a0ad8afd17e4bce1892145d6a1111e58987ddb176810d02a1f3f18686/ormsgpack-1.10.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:33afe143a7b61ad21bb60109a86bb4e87fec70ef35db76b89c65b17e32da7935", size = 479076, upload-time = "2025-05-24T19:07:37.533Z" },
- { url = "https://files.pythonhosted.org/packages/c8/34/68925232e81e0e062a2f0ac678f62aa3b6f7009d6a759e19324dbbaebae7/ormsgpack-1.10.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f23d45080846a7b90feabec0d330a9cc1863dc956728412e4f7986c80ab3a668", size = 390446, upload-time = "2025-05-24T19:07:39.469Z" },
- { url = "https://files.pythonhosted.org/packages/12/ad/f4e1a36a6d1714afb7ffb74b3ababdcb96529cf4e7a216f9f7c8eda837b6/ormsgpack-1.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:534d18acb805c75e5fba09598bf40abe1851c853247e61dda0c01f772234da69", size = 121399, upload-time = "2025-05-24T19:07:40.854Z" },
- { url = "https://files.pythonhosted.org/packages/75/8f/bb80469db9d5b10708cba6997463d140486ca7053a5d18f99b5739cfecf7/ormsgpack-1.10.0-cp39-cp39-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:efdb25cf6d54085f7ae557268d59fd2d956f1a09a340856e282d2960fe929f32", size = 376272, upload-time = "2025-05-24T19:07:42.16Z" },
- { url = "https://files.pythonhosted.org/packages/08/9c/48f714ed3d5a153f25e3b490496e6ba214aee265a82be1b61e39019ea146/ormsgpack-1.10.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ddfcb30d4b1be2439836249d675f297947f4fb8efcd3eeb6fd83021d773cadc4", size = 204314, upload-time = "2025-05-24T19:07:43.444Z" },
- { url = "https://files.pythonhosted.org/packages/27/42/7f9edf6e5511120b5304c76c5d3a8b4719ff927555a6dba41b6f9d041b30/ormsgpack-1.10.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ee0944b6ccfd880beb1ca29f9442a774683c366f17f4207f8b81c5e24cadb453", size = 215386, upload-time = "2025-05-24T19:07:45.232Z" },
- { url = "https://files.pythonhosted.org/packages/40/87/41e14485857fbe4ed5a530677fe60dd6910a254825c0b1cb5b04baaa4be0/ormsgpack-1.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:35cdff6a0d3ba04e40a751129763c3b9b57a602c02944138e4b760ec99ae80a1", size = 216466, upload-time = "2025-05-24T19:07:46.548Z" },
- { url = "https://files.pythonhosted.org/packages/cb/68/769fa1c721d8aa6799c0ce98b1711ae57de3e6379b554ebf9a11be4c62ff/ormsgpack-1.10.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:599ccdabc19c618ef5de6e6f2e7f5d48c1f531a625fa6772313b8515bc710681", size = 384600, upload-time = "2025-05-24T19:07:47.945Z" },
- { url = "https://files.pythonhosted.org/packages/4e/f9/b57fd387fe16753783a3cea0ed2471c727bbed4356d8a08e3f0340251870/ormsgpack-1.10.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:bf46f57da9364bd5eefd92365c1b78797f56c6f780581eecd60cd7b367f9b4d3", size = 478888, upload-time = "2025-05-24T19:07:49.801Z" },
- { url = "https://files.pythonhosted.org/packages/3e/0f/464cdfa7f9ee817c2d94485880b6c3c4b9f22df9fcbf21c303bbfebcb3ed/ormsgpack-1.10.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:b796f64fdf823dedb1e35436a4a6f889cf78b1aa42d3097c66e5adfd8c3bd72d", size = 390118, upload-time = "2025-05-24T19:07:51.193Z" },
- { url = "https://files.pythonhosted.org/packages/ad/03/b9146dff5458def4c0a2b1e35c1c24e4d5e8083899aa0718b6eccba39317/ormsgpack-1.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:106253ac9dc08520951e556b3c270220fcb8b4fef0d30b71eedac4befa4de749", size = 121199, upload-time = "2025-05-24T19:07:52.639Z" },
-]
-
-[[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, upload-time = "2024-11-08T09:47:47.202Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451, upload-time = "2024-11-08T09:47:44.722Z" },
-]
-
-[[package]]
-name = "pandas"
-version = "2.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, upload-time = "2024-09-20T13:10:04.827Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/aa/70/c853aec59839bceed032d52010ff5f1b8d87dc3114b762e4ba2727661a3b/pandas-2.2.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1948ddde24197a0f7add2bdc4ca83bf2b1ef84a1bc8ccffd95eda17fd836ecb5", size = 12580827, upload-time = "2024-09-20T13:08:42.347Z" },
- { url = "https://files.pythonhosted.org/packages/99/f2/c4527768739ffa4469b2b4fff05aa3768a478aed89a2f271a79a40eee984/pandas-2.2.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:381175499d3802cde0eabbaf6324cce0c4f5d52ca6f8c377c29ad442f50f6348", size = 11303897, upload-time = "2024-09-20T13:08:45.807Z" },
- { url = "https://files.pythonhosted.org/packages/ed/12/86c1747ea27989d7a4064f806ce2bae2c6d575b950be087837bdfcabacc9/pandas-2.2.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d9c45366def9a3dd85a6454c0e7908f2b3b8e9c138f5dc38fed7ce720d8453ed", size = 66480908, upload-time = "2024-09-20T18:37:13.513Z" },
- { url = "https://files.pythonhosted.org/packages/44/50/7db2cd5e6373ae796f0ddad3675268c8d59fb6076e66f0c339d61cea886b/pandas-2.2.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86976a1c5b25ae3f8ccae3a5306e443569ee3c3faf444dfd0f41cda24667ad57", size = 13064210, upload-time = "2024-09-20T13:08:48.325Z" },
- { url = "https://files.pythonhosted.org/packages/61/61/a89015a6d5536cb0d6c3ba02cebed51a95538cf83472975275e28ebf7d0c/pandas-2.2.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b8661b0238a69d7aafe156b7fa86c44b881387509653fdf857bebc5e4008ad42", size = 16754292, upload-time = "2024-09-20T19:01:54.443Z" },
- { url = "https://files.pythonhosted.org/packages/ce/0d/4cc7b69ce37fac07645a94e1d4b0880b15999494372c1523508511b09e40/pandas-2.2.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:37e0aced3e8f539eccf2e099f65cdb9c8aa85109b0be6e93e2baff94264bdc6f", size = 14416379, upload-time = "2024-09-20T13:08:50.882Z" },
- { url = "https://files.pythonhosted.org/packages/31/9e/6ebb433de864a6cd45716af52a4d7a8c3c9aaf3a98368e61db9e69e69a9c/pandas-2.2.3-cp310-cp310-win_amd64.whl", hash = "sha256:56534ce0746a58afaf7942ba4863e0ef81c9c50d3f0ae93e9497d6a41a057645", size = 11598471, upload-time = "2024-09-20T13:08:53.332Z" },
- { url = "https://files.pythonhosted.org/packages/a8/44/d9502bf0ed197ba9bf1103c9867d5904ddcaf869e52329787fc54ed70cc8/pandas-2.2.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:66108071e1b935240e74525006034333f98bcdb87ea116de573a6a0dccb6c039", size = 12602222, upload-time = "2024-09-20T13:08:56.254Z" },
- { url = "https://files.pythonhosted.org/packages/52/11/9eac327a38834f162b8250aab32a6781339c69afe7574368fffe46387edf/pandas-2.2.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7c2875855b0ff77b2a64a0365e24455d9990730d6431b9e0ee18ad8acee13dbd", size = 11321274, upload-time = "2024-09-20T13:08:58.645Z" },
- { url = "https://files.pythonhosted.org/packages/45/fb/c4beeb084718598ba19aa9f5abbc8aed8b42f90930da861fcb1acdb54c3a/pandas-2.2.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cd8d0c3be0515c12fed0bdbae072551c8b54b7192c7b1fda0ba56059a0179698", size = 15579836, upload-time = "2024-09-20T19:01:57.571Z" },
- { url = "https://files.pythonhosted.org/packages/cd/5f/4dba1d39bb9c38d574a9a22548c540177f78ea47b32f99c0ff2ec499fac5/pandas-2.2.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c124333816c3a9b03fbeef3a9f230ba9a737e9e5bb4060aa2107a86cc0a497fc", size = 13058505, upload-time = "2024-09-20T13:09:01.501Z" },
- { url = "https://files.pythonhosted.org/packages/b9/57/708135b90391995361636634df1f1130d03ba456e95bcf576fada459115a/pandas-2.2.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:63cc132e40a2e084cf01adf0775b15ac515ba905d7dcca47e9a251819c575ef3", size = 16744420, upload-time = "2024-09-20T19:02:00.678Z" },
- { url = "https://files.pythonhosted.org/packages/86/4a/03ed6b7ee323cf30404265c284cee9c65c56a212e0a08d9ee06984ba2240/pandas-2.2.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:29401dbfa9ad77319367d36940cd8a0b3a11aba16063e39632d98b0e931ddf32", size = 14440457, upload-time = "2024-09-20T13:09:04.105Z" },
- { url = "https://files.pythonhosted.org/packages/ed/8c/87ddf1fcb55d11f9f847e3c69bb1c6f8e46e2f40ab1a2d2abadb2401b007/pandas-2.2.3-cp311-cp311-win_amd64.whl", hash = "sha256:3fc6873a41186404dad67245896a6e440baacc92f5b716ccd1bc9ed2995ab2c5", size = 11617166, upload-time = "2024-09-20T13:09:06.917Z" },
- { 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, upload-time = "2024-09-20T13:09:09.655Z" },
- { 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, upload-time = "2024-09-20T13:09:14.718Z" },
- { 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, upload-time = "2024-09-20T19:02:03.88Z" },
- { 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, upload-time = "2024-09-20T13:09:17.621Z" },
- { 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, upload-time = "2024-09-20T19:02:07.094Z" },
- { 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, upload-time = "2024-09-20T13:09:20.474Z" },
- { url = "https://files.pythonhosted.org/packages/29/d4/1244ab8edf173a10fd601f7e13b9566c1b525c4f365d6bee918e68381889/pandas-2.2.3-cp312-cp312-win_amd64.whl", hash = "sha256:59ef3764d0fe818125a5097d2ae867ca3fa64df032331b7e0917cf5d7bf66b13", size = 11504248, upload-time = "2024-09-20T13:09:23.137Z" },
- { 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, upload-time = "2024-09-20T13:09:25.522Z" },
- { 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, upload-time = "2024-09-20T13:09:28.012Z" },
- { 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, upload-time = "2024-09-20T19:02:10.451Z" },
- { 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, upload-time = "2024-09-20T13:09:30.814Z" },
- { 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, upload-time = "2024-09-20T19:02:13.825Z" },
- { 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, upload-time = "2024-09-20T13:09:33.462Z" },
- { url = "https://files.pythonhosted.org/packages/3b/bc/4b18e2b8c002572c5a441a64826252ce5da2aa738855747247a971988043/pandas-2.2.3-cp313-cp313-win_amd64.whl", hash = "sha256:61c5ad4043f791b61dd4752191d9f07f0ae412515d59ba8f005832a532f8736d", size = 11481175, upload-time = "2024-09-20T13:09:35.871Z" },
- { 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, upload-time = "2024-09-20T13:09:38.685Z" },
- { 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, upload-time = "2024-09-20T13:09:41.141Z" },
- { 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, upload-time = "2024-09-20T19:02:16.905Z" },
- { 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, upload-time = "2024-09-20T13:09:44.39Z" },
- { 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, upload-time = "2024-09-20T19:02:20.639Z" },
- { 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, upload-time = "2024-09-20T13:09:48.112Z" },
- { url = "https://files.pythonhosted.org/packages/ca/8c/8848a4c9b8fdf5a534fe2077af948bf53cd713d77ffbcd7bd15710348fd7/pandas-2.2.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bc6b93f9b966093cb0fd62ff1a7e4c09e6d546ad7c1de191767baffc57628f39", size = 12595535, upload-time = "2024-09-20T13:09:51.339Z" },
- { url = "https://files.pythonhosted.org/packages/9c/b9/5cead4f63b6d31bdefeb21a679bc5a7f4aaf262ca7e07e2bc1c341b68470/pandas-2.2.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5dbca4c1acd72e8eeef4753eeca07de9b1db4f398669d5994086f788a5d7cc30", size = 11319822, upload-time = "2024-09-20T13:09:54.31Z" },
- { url = "https://files.pythonhosted.org/packages/31/af/89e35619fb573366fa68dc26dad6ad2c08c17b8004aad6d98f1a31ce4bb3/pandas-2.2.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8cd6d7cc958a3910f934ea8dbdf17b2364827bb4dafc38ce6eef6bb3d65ff09c", size = 15625439, upload-time = "2024-09-20T19:02:23.689Z" },
- { url = "https://files.pythonhosted.org/packages/3d/dd/bed19c2974296661493d7acc4407b1d2db4e2a482197df100f8f965b6225/pandas-2.2.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99df71520d25fade9db7c1076ac94eb994f4d2673ef2aa2e86ee039b6746d20c", size = 13068928, upload-time = "2024-09-20T13:09:56.746Z" },
- { url = "https://files.pythonhosted.org/packages/31/a3/18508e10a31ea108d746c848b5a05c0711e0278fa0d6f1c52a8ec52b80a5/pandas-2.2.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:31d0ced62d4ea3e231a9f228366919a5ea0b07440d9d4dac345376fd8e1477ea", size = 16783266, upload-time = "2024-09-20T19:02:26.247Z" },
- { url = "https://files.pythonhosted.org/packages/c4/a5/3429bd13d82bebc78f4d78c3945efedef63a7cd0c15c17b2eeb838d1121f/pandas-2.2.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7eee9e7cea6adf3e3d24e304ac6b8300646e2a5d1cd3a3c2abed9101b0846761", size = 14450871, upload-time = "2024-09-20T13:09:59.779Z" },
- { url = "https://files.pythonhosted.org/packages/2f/49/5c30646e96c684570925b772eac4eb0a8cb0ca590fa978f56c5d3ae73ea1/pandas-2.2.3-cp39-cp39-win_amd64.whl", hash = "sha256:4850ba03528b6dd51d6c5d273c46f183f39a9baf3f0143e566b89450965b105e", size = 11618011, upload-time = "2024-09-20T13:10:02.351Z" },
-]
-
-[[package]]
-name = "parso"
-version = "0.8.5"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/d4/de/53e0bcf53d13e005bd8c92e7855142494f41171b34c2536b86187474184d/parso-0.8.5.tar.gz", hash = "sha256:034d7354a9a018bdce352f48b2a8a450f05e9d6ee85db84764e9b6bd96dafe5a", size = 401205, upload-time = "2025-08-23T15:15:28.028Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/16/32/f8e3c85d1d5250232a5d3477a2a28cc291968ff175caeadaf3cc19ce0e4a/parso-0.8.5-py2.py3-none-any.whl", hash = "sha256:646204b5ee239c396d040b90f9e272e9a8017c630092bf59980beb62fd033887", size = 106668, upload-time = "2025-08-23T15:15:25.663Z" },
-]
-
-[[package]]
-name = "pexpect"
-version = "4.9.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "ptyprocess" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/42/92/cc564bf6381ff43ce1f4d06852fc19a2f11d180f23dc32d9588bee2f149d/pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f", size = 166450, upload-time = "2023-11-25T09:07:26.339Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523", size = 63772, upload-time = "2023-11-25T06:56:14.81Z" },
-]
-
-[[package]]
-name = "pgvector"
-version = "0.3.6"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "numpy" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/7d/d8/fd6009cee3e03214667df488cdcf9609461d729968da94e4f95d6359d304/pgvector-0.3.6.tar.gz", hash = "sha256:31d01690e6ea26cea8a633cde5f0f55f5b246d9c8292d68efdef8c22ec994ade", size = 25421, upload-time = "2024-10-27T00:15:09.632Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/fb/81/f457d6d361e04d061bef413749a6e1ab04d98cfeec6d8abcfe40184750f3/pgvector-0.3.6-py3-none-any.whl", hash = "sha256:f6c269b3c110ccb7496bac87202148ed18f34b390a0189c783e351062400a75a", size = 24880, upload-time = "2024-10-27T00:15:08.045Z" },
-]
-
-[[package]]
-name = "pillow"
-version = "11.3.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/f3/0d/d0d6dea55cd152ce3d6767bb38a8fc10e33796ba4ba210cbab9354b6d238/pillow-11.3.0.tar.gz", hash = "sha256:3828ee7586cd0b2091b6209e5ad53e20d0649bbe87164a459d0676e035e8f523", size = 47113069, upload-time = "2025-07-01T09:16:30.666Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/4c/5d/45a3553a253ac8763f3561371432a90bdbe6000fbdcf1397ffe502aa206c/pillow-11.3.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:1b9c17fd4ace828b3003dfd1e30bff24863e0eb59b535e8f80194d9cc7ecf860", size = 5316554, upload-time = "2025-07-01T09:13:39.342Z" },
- { url = "https://files.pythonhosted.org/packages/7c/c8/67c12ab069ef586a25a4a79ced553586748fad100c77c0ce59bb4983ac98/pillow-11.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:65dc69160114cdd0ca0f35cb434633c75e8e7fad4cf855177a05bf38678f73ad", size = 4686548, upload-time = "2025-07-01T09:13:41.835Z" },
- { url = "https://files.pythonhosted.org/packages/2f/bd/6741ebd56263390b382ae4c5de02979af7f8bd9807346d068700dd6d5cf9/pillow-11.3.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7107195ddc914f656c7fc8e4a5e1c25f32e9236ea3ea860f257b0436011fddd0", size = 5859742, upload-time = "2025-07-03T13:09:47.439Z" },
- { url = "https://files.pythonhosted.org/packages/ca/0b/c412a9e27e1e6a829e6ab6c2dca52dd563efbedf4c9c6aa453d9a9b77359/pillow-11.3.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cc3e831b563b3114baac7ec2ee86819eb03caa1a2cef0b481a5675b59c4fe23b", size = 7633087, upload-time = "2025-07-03T13:09:51.796Z" },
- { url = "https://files.pythonhosted.org/packages/59/9d/9b7076aaf30f5dd17e5e5589b2d2f5a5d7e30ff67a171eb686e4eecc2adf/pillow-11.3.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f1f182ebd2303acf8c380a54f615ec883322593320a9b00438eb842c1f37ae50", size = 5963350, upload-time = "2025-07-01T09:13:43.865Z" },
- { url = "https://files.pythonhosted.org/packages/f0/16/1a6bf01fb622fb9cf5c91683823f073f053005c849b1f52ed613afcf8dae/pillow-11.3.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4445fa62e15936a028672fd48c4c11a66d641d2c05726c7ec1f8ba6a572036ae", size = 6631840, upload-time = "2025-07-01T09:13:46.161Z" },
- { url = "https://files.pythonhosted.org/packages/7b/e6/6ff7077077eb47fde78739e7d570bdcd7c10495666b6afcd23ab56b19a43/pillow-11.3.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:71f511f6b3b91dd543282477be45a033e4845a40278fa8dcdbfdb07109bf18f9", size = 6074005, upload-time = "2025-07-01T09:13:47.829Z" },
- { url = "https://files.pythonhosted.org/packages/c3/3a/b13f36832ea6d279a697231658199e0a03cd87ef12048016bdcc84131601/pillow-11.3.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:040a5b691b0713e1f6cbe222e0f4f74cd233421e105850ae3b3c0ceda520f42e", size = 6708372, upload-time = "2025-07-01T09:13:52.145Z" },
- { url = "https://files.pythonhosted.org/packages/6c/e4/61b2e1a7528740efbc70b3d581f33937e38e98ef3d50b05007267a55bcb2/pillow-11.3.0-cp310-cp310-win32.whl", hash = "sha256:89bd777bc6624fe4115e9fac3352c79ed60f3bb18651420635f26e643e3dd1f6", size = 6277090, upload-time = "2025-07-01T09:13:53.915Z" },
- { url = "https://files.pythonhosted.org/packages/a9/d3/60c781c83a785d6afbd6a326ed4d759d141de43aa7365725cbcd65ce5e54/pillow-11.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:19d2ff547c75b8e3ff46f4d9ef969a06c30ab2d4263a9e287733aa8b2429ce8f", size = 6985988, upload-time = "2025-07-01T09:13:55.699Z" },
- { url = "https://files.pythonhosted.org/packages/9f/28/4f4a0203165eefb3763939c6789ba31013a2e90adffb456610f30f613850/pillow-11.3.0-cp310-cp310-win_arm64.whl", hash = "sha256:819931d25e57b513242859ce1876c58c59dc31587847bf74cfe06b2e0cb22d2f", size = 2422899, upload-time = "2025-07-01T09:13:57.497Z" },
- { url = "https://files.pythonhosted.org/packages/db/26/77f8ed17ca4ffd60e1dcd220a6ec6d71210ba398cfa33a13a1cd614c5613/pillow-11.3.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:1cd110edf822773368b396281a2293aeb91c90a2db00d78ea43e7e861631b722", size = 5316531, upload-time = "2025-07-01T09:13:59.203Z" },
- { url = "https://files.pythonhosted.org/packages/cb/39/ee475903197ce709322a17a866892efb560f57900d9af2e55f86db51b0a5/pillow-11.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9c412fddd1b77a75aa904615ebaa6001f169b26fd467b4be93aded278266b288", size = 4686560, upload-time = "2025-07-01T09:14:01.101Z" },
- { url = "https://files.pythonhosted.org/packages/d5/90/442068a160fd179938ba55ec8c97050a612426fae5ec0a764e345839f76d/pillow-11.3.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7d1aa4de119a0ecac0a34a9c8bde33f34022e2e8f99104e47a3ca392fd60e37d", size = 5870978, upload-time = "2025-07-03T13:09:55.638Z" },
- { url = "https://files.pythonhosted.org/packages/13/92/dcdd147ab02daf405387f0218dcf792dc6dd5b14d2573d40b4caeef01059/pillow-11.3.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:91da1d88226663594e3f6b4b8c3c8d85bd504117d043740a8e0ec449087cc494", size = 7641168, upload-time = "2025-07-03T13:10:00.37Z" },
- { url = "https://files.pythonhosted.org/packages/6e/db/839d6ba7fd38b51af641aa904e2960e7a5644d60ec754c046b7d2aee00e5/pillow-11.3.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:643f189248837533073c405ec2f0bb250ba54598cf80e8c1e043381a60632f58", size = 5973053, upload-time = "2025-07-01T09:14:04.491Z" },
- { url = "https://files.pythonhosted.org/packages/f2/2f/d7675ecae6c43e9f12aa8d58b6012683b20b6edfbdac7abcb4e6af7a3784/pillow-11.3.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:106064daa23a745510dabce1d84f29137a37224831d88eb4ce94bb187b1d7e5f", size = 6640273, upload-time = "2025-07-01T09:14:06.235Z" },
- { url = "https://files.pythonhosted.org/packages/45/ad/931694675ede172e15b2ff03c8144a0ddaea1d87adb72bb07655eaffb654/pillow-11.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cd8ff254faf15591e724dc7c4ddb6bf4793efcbe13802a4ae3e863cd300b493e", size = 6082043, upload-time = "2025-07-01T09:14:07.978Z" },
- { url = "https://files.pythonhosted.org/packages/3a/04/ba8f2b11fc80d2dd462d7abec16351b45ec99cbbaea4387648a44190351a/pillow-11.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:932c754c2d51ad2b2271fd01c3d121daaa35e27efae2a616f77bf164bc0b3e94", size = 6715516, upload-time = "2025-07-01T09:14:10.233Z" },
- { url = "https://files.pythonhosted.org/packages/48/59/8cd06d7f3944cc7d892e8533c56b0acb68399f640786313275faec1e3b6f/pillow-11.3.0-cp311-cp311-win32.whl", hash = "sha256:b4b8f3efc8d530a1544e5962bd6b403d5f7fe8b9e08227c6b255f98ad82b4ba0", size = 6274768, upload-time = "2025-07-01T09:14:11.921Z" },
- { url = "https://files.pythonhosted.org/packages/f1/cc/29c0f5d64ab8eae20f3232da8f8571660aa0ab4b8f1331da5c2f5f9a938e/pillow-11.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:1a992e86b0dd7aeb1f053cd506508c0999d710a8f07b4c791c63843fc6a807ac", size = 6986055, upload-time = "2025-07-01T09:14:13.623Z" },
- { url = "https://files.pythonhosted.org/packages/c6/df/90bd886fabd544c25addd63e5ca6932c86f2b701d5da6c7839387a076b4a/pillow-11.3.0-cp311-cp311-win_arm64.whl", hash = "sha256:30807c931ff7c095620fe04448e2c2fc673fcbb1ffe2a7da3fb39613489b1ddd", size = 2423079, upload-time = "2025-07-01T09:14:15.268Z" },
- { url = "https://files.pythonhosted.org/packages/40/fe/1bc9b3ee13f68487a99ac9529968035cca2f0a51ec36892060edcc51d06a/pillow-11.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fdae223722da47b024b867c1ea0be64e0df702c5e0a60e27daad39bf960dd1e4", size = 5278800, upload-time = "2025-07-01T09:14:17.648Z" },
- { url = "https://files.pythonhosted.org/packages/2c/32/7e2ac19b5713657384cec55f89065fb306b06af008cfd87e572035b27119/pillow-11.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:921bd305b10e82b4d1f5e802b6850677f965d8394203d182f078873851dada69", size = 4686296, upload-time = "2025-07-01T09:14:19.828Z" },
- { url = "https://files.pythonhosted.org/packages/8e/1e/b9e12bbe6e4c2220effebc09ea0923a07a6da1e1f1bfbc8d7d29a01ce32b/pillow-11.3.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:eb76541cba2f958032d79d143b98a3a6b3ea87f0959bbe256c0b5e416599fd5d", size = 5871726, upload-time = "2025-07-03T13:10:04.448Z" },
- { url = "https://files.pythonhosted.org/packages/8d/33/e9200d2bd7ba00dc3ddb78df1198a6e80d7669cce6c2bdbeb2530a74ec58/pillow-11.3.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:67172f2944ebba3d4a7b54f2e95c786a3a50c21b88456329314caaa28cda70f6", size = 7644652, upload-time = "2025-07-03T13:10:10.391Z" },
- { url = "https://files.pythonhosted.org/packages/41/f1/6f2427a26fc683e00d985bc391bdd76d8dd4e92fac33d841127eb8fb2313/pillow-11.3.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97f07ed9f56a3b9b5f49d3661dc9607484e85c67e27f3e8be2c7d28ca032fec7", size = 5977787, upload-time = "2025-07-01T09:14:21.63Z" },
- { url = "https://files.pythonhosted.org/packages/e4/c9/06dd4a38974e24f932ff5f98ea3c546ce3f8c995d3f0985f8e5ba48bba19/pillow-11.3.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:676b2815362456b5b3216b4fd5bd89d362100dc6f4945154ff172e206a22c024", size = 6645236, upload-time = "2025-07-01T09:14:23.321Z" },
- { url = "https://files.pythonhosted.org/packages/40/e7/848f69fb79843b3d91241bad658e9c14f39a32f71a301bcd1d139416d1be/pillow-11.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3e184b2f26ff146363dd07bde8b711833d7b0202e27d13540bfe2e35a323a809", size = 6086950, upload-time = "2025-07-01T09:14:25.237Z" },
- { url = "https://files.pythonhosted.org/packages/0b/1a/7cff92e695a2a29ac1958c2a0fe4c0b2393b60aac13b04a4fe2735cad52d/pillow-11.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6be31e3fc9a621e071bc17bb7de63b85cbe0bfae91bb0363c893cbe67247780d", size = 6723358, upload-time = "2025-07-01T09:14:27.053Z" },
- { url = "https://files.pythonhosted.org/packages/26/7d/73699ad77895f69edff76b0f332acc3d497f22f5d75e5360f78cbcaff248/pillow-11.3.0-cp312-cp312-win32.whl", hash = "sha256:7b161756381f0918e05e7cb8a371fff367e807770f8fe92ecb20d905d0e1c149", size = 6275079, upload-time = "2025-07-01T09:14:30.104Z" },
- { url = "https://files.pythonhosted.org/packages/8c/ce/e7dfc873bdd9828f3b6e5c2bbb74e47a98ec23cc5c74fc4e54462f0d9204/pillow-11.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:a6444696fce635783440b7f7a9fc24b3ad10a9ea3f0ab66c5905be1c19ccf17d", size = 6986324, upload-time = "2025-07-01T09:14:31.899Z" },
- { url = "https://files.pythonhosted.org/packages/16/8f/b13447d1bf0b1f7467ce7d86f6e6edf66c0ad7cf44cf5c87a37f9bed9936/pillow-11.3.0-cp312-cp312-win_arm64.whl", hash = "sha256:2aceea54f957dd4448264f9bf40875da0415c83eb85f55069d89c0ed436e3542", size = 2423067, upload-time = "2025-07-01T09:14:33.709Z" },
- { url = "https://files.pythonhosted.org/packages/1e/93/0952f2ed8db3a5a4c7a11f91965d6184ebc8cd7cbb7941a260d5f018cd2d/pillow-11.3.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:1c627742b539bba4309df89171356fcb3cc5a9178355b2727d1b74a6cf155fbd", size = 2128328, upload-time = "2025-07-01T09:14:35.276Z" },
- { url = "https://files.pythonhosted.org/packages/4b/e8/100c3d114b1a0bf4042f27e0f87d2f25e857e838034e98ca98fe7b8c0a9c/pillow-11.3.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:30b7c02f3899d10f13d7a48163c8969e4e653f8b43416d23d13d1bbfdc93b9f8", size = 2170652, upload-time = "2025-07-01T09:14:37.203Z" },
- { url = "https://files.pythonhosted.org/packages/aa/86/3f758a28a6e381758545f7cdb4942e1cb79abd271bea932998fc0db93cb6/pillow-11.3.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:7859a4cc7c9295f5838015d8cc0a9c215b77e43d07a25e460f35cf516df8626f", size = 2227443, upload-time = "2025-07-01T09:14:39.344Z" },
- { url = "https://files.pythonhosted.org/packages/01/f4/91d5b3ffa718df2f53b0dc109877993e511f4fd055d7e9508682e8aba092/pillow-11.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ec1ee50470b0d050984394423d96325b744d55c701a439d2bd66089bff963d3c", size = 5278474, upload-time = "2025-07-01T09:14:41.843Z" },
- { url = "https://files.pythonhosted.org/packages/f9/0e/37d7d3eca6c879fbd9dba21268427dffda1ab00d4eb05b32923d4fbe3b12/pillow-11.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7db51d222548ccfd274e4572fdbf3e810a5e66b00608862f947b163e613b67dd", size = 4686038, upload-time = "2025-07-01T09:14:44.008Z" },
- { url = "https://files.pythonhosted.org/packages/ff/b0/3426e5c7f6565e752d81221af9d3676fdbb4f352317ceafd42899aaf5d8a/pillow-11.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2d6fcc902a24ac74495df63faad1884282239265c6839a0a6416d33faedfae7e", size = 5864407, upload-time = "2025-07-03T13:10:15.628Z" },
- { url = "https://files.pythonhosted.org/packages/fc/c1/c6c423134229f2a221ee53f838d4be9d82bab86f7e2f8e75e47b6bf6cd77/pillow-11.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f0f5d8f4a08090c6d6d578351a2b91acf519a54986c055af27e7a93feae6d3f1", size = 7639094, upload-time = "2025-07-03T13:10:21.857Z" },
- { url = "https://files.pythonhosted.org/packages/ba/c9/09e6746630fe6372c67c648ff9deae52a2bc20897d51fa293571977ceb5d/pillow-11.3.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c37d8ba9411d6003bba9e518db0db0c58a680ab9fe5179f040b0463644bc9805", size = 5973503, upload-time = "2025-07-01T09:14:45.698Z" },
- { url = "https://files.pythonhosted.org/packages/d5/1c/a2a29649c0b1983d3ef57ee87a66487fdeb45132df66ab30dd37f7dbe162/pillow-11.3.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:13f87d581e71d9189ab21fe0efb5a23e9f28552d5be6979e84001d3b8505abe8", size = 6642574, upload-time = "2025-07-01T09:14:47.415Z" },
- { url = "https://files.pythonhosted.org/packages/36/de/d5cc31cc4b055b6c6fd990e3e7f0f8aaf36229a2698501bcb0cdf67c7146/pillow-11.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:023f6d2d11784a465f09fd09a34b150ea4672e85fb3d05931d89f373ab14abb2", size = 6084060, upload-time = "2025-07-01T09:14:49.636Z" },
- { url = "https://files.pythonhosted.org/packages/d5/ea/502d938cbaeec836ac28a9b730193716f0114c41325db428e6b280513f09/pillow-11.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:45dfc51ac5975b938e9809451c51734124e73b04d0f0ac621649821a63852e7b", size = 6721407, upload-time = "2025-07-01T09:14:51.962Z" },
- { url = "https://files.pythonhosted.org/packages/45/9c/9c5e2a73f125f6cbc59cc7087c8f2d649a7ae453f83bd0362ff7c9e2aee2/pillow-11.3.0-cp313-cp313-win32.whl", hash = "sha256:a4d336baed65d50d37b88ca5b60c0fa9d81e3a87d4a7930d3880d1624d5b31f3", size = 6273841, upload-time = "2025-07-01T09:14:54.142Z" },
- { url = "https://files.pythonhosted.org/packages/23/85/397c73524e0cd212067e0c969aa245b01d50183439550d24d9f55781b776/pillow-11.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:0bce5c4fd0921f99d2e858dc4d4d64193407e1b99478bc5cacecba2311abde51", size = 6978450, upload-time = "2025-07-01T09:14:56.436Z" },
- { url = "https://files.pythonhosted.org/packages/17/d2/622f4547f69cd173955194b78e4d19ca4935a1b0f03a302d655c9f6aae65/pillow-11.3.0-cp313-cp313-win_arm64.whl", hash = "sha256:1904e1264881f682f02b7f8167935cce37bc97db457f8e7849dc3a6a52b99580", size = 2423055, upload-time = "2025-07-01T09:14:58.072Z" },
- { url = "https://files.pythonhosted.org/packages/dd/80/a8a2ac21dda2e82480852978416cfacd439a4b490a501a288ecf4fe2532d/pillow-11.3.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:4c834a3921375c48ee6b9624061076bc0a32a60b5532b322cc0ea64e639dd50e", size = 5281110, upload-time = "2025-07-01T09:14:59.79Z" },
- { url = "https://files.pythonhosted.org/packages/44/d6/b79754ca790f315918732e18f82a8146d33bcd7f4494380457ea89eb883d/pillow-11.3.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5e05688ccef30ea69b9317a9ead994b93975104a677a36a8ed8106be9260aa6d", size = 4689547, upload-time = "2025-07-01T09:15:01.648Z" },
- { url = "https://files.pythonhosted.org/packages/49/20/716b8717d331150cb00f7fdd78169c01e8e0c219732a78b0e59b6bdb2fd6/pillow-11.3.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1019b04af07fc0163e2810167918cb5add8d74674b6267616021ab558dc98ced", size = 5901554, upload-time = "2025-07-03T13:10:27.018Z" },
- { url = "https://files.pythonhosted.org/packages/74/cf/a9f3a2514a65bb071075063a96f0a5cf949c2f2fce683c15ccc83b1c1cab/pillow-11.3.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f944255db153ebb2b19c51fe85dd99ef0ce494123f21b9db4877ffdfc5590c7c", size = 7669132, upload-time = "2025-07-03T13:10:33.01Z" },
- { url = "https://files.pythonhosted.org/packages/98/3c/da78805cbdbee9cb43efe8261dd7cc0b4b93f2ac79b676c03159e9db2187/pillow-11.3.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1f85acb69adf2aaee8b7da124efebbdb959a104db34d3a2cb0f3793dbae422a8", size = 6005001, upload-time = "2025-07-01T09:15:03.365Z" },
- { url = "https://files.pythonhosted.org/packages/6c/fa/ce044b91faecf30e635321351bba32bab5a7e034c60187fe9698191aef4f/pillow-11.3.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:05f6ecbeff5005399bb48d198f098a9b4b6bdf27b8487c7f38ca16eeb070cd59", size = 6668814, upload-time = "2025-07-01T09:15:05.655Z" },
- { url = "https://files.pythonhosted.org/packages/7b/51/90f9291406d09bf93686434f9183aba27b831c10c87746ff49f127ee80cb/pillow-11.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a7bc6e6fd0395bc052f16b1a8670859964dbd7003bd0af2ff08342eb6e442cfe", size = 6113124, upload-time = "2025-07-01T09:15:07.358Z" },
- { url = "https://files.pythonhosted.org/packages/cd/5a/6fec59b1dfb619234f7636d4157d11fb4e196caeee220232a8d2ec48488d/pillow-11.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:83e1b0161c9d148125083a35c1c5a89db5b7054834fd4387499e06552035236c", size = 6747186, upload-time = "2025-07-01T09:15:09.317Z" },
- { url = "https://files.pythonhosted.org/packages/49/6b/00187a044f98255225f172de653941e61da37104a9ea60e4f6887717e2b5/pillow-11.3.0-cp313-cp313t-win32.whl", hash = "sha256:2a3117c06b8fb646639dce83694f2f9eac405472713fcb1ae887469c0d4f6788", size = 6277546, upload-time = "2025-07-01T09:15:11.311Z" },
- { url = "https://files.pythonhosted.org/packages/e8/5c/6caaba7e261c0d75bab23be79f1d06b5ad2a2ae49f028ccec801b0e853d6/pillow-11.3.0-cp313-cp313t-win_amd64.whl", hash = "sha256:857844335c95bea93fb39e0fa2726b4d9d758850b34075a7e3ff4f4fa3aa3b31", size = 6985102, upload-time = "2025-07-01T09:15:13.164Z" },
- { url = "https://files.pythonhosted.org/packages/f3/7e/b623008460c09a0cb38263c93b828c666493caee2eb34ff67f778b87e58c/pillow-11.3.0-cp313-cp313t-win_arm64.whl", hash = "sha256:8797edc41f3e8536ae4b10897ee2f637235c94f27404cac7297f7b607dd0716e", size = 2424803, upload-time = "2025-07-01T09:15:15.695Z" },
- { url = "https://files.pythonhosted.org/packages/73/f4/04905af42837292ed86cb1b1dabe03dce1edc008ef14c473c5c7e1443c5d/pillow-11.3.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:d9da3df5f9ea2a89b81bb6087177fb1f4d1c7146d583a3fe5c672c0d94e55e12", size = 5278520, upload-time = "2025-07-01T09:15:17.429Z" },
- { url = "https://files.pythonhosted.org/packages/41/b0/33d79e377a336247df6348a54e6d2a2b85d644ca202555e3faa0cf811ecc/pillow-11.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0b275ff9b04df7b640c59ec5a3cb113eefd3795a8df80bac69646ef699c6981a", size = 4686116, upload-time = "2025-07-01T09:15:19.423Z" },
- { url = "https://files.pythonhosted.org/packages/49/2d/ed8bc0ab219ae8768f529597d9509d184fe8a6c4741a6864fea334d25f3f/pillow-11.3.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0743841cabd3dba6a83f38a92672cccbd69af56e3e91777b0ee7f4dba4385632", size = 5864597, upload-time = "2025-07-03T13:10:38.404Z" },
- { url = "https://files.pythonhosted.org/packages/b5/3d/b932bb4225c80b58dfadaca9d42d08d0b7064d2d1791b6a237f87f661834/pillow-11.3.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2465a69cf967b8b49ee1b96d76718cd98c4e925414ead59fdf75cf0fd07df673", size = 7638246, upload-time = "2025-07-03T13:10:44.987Z" },
- { url = "https://files.pythonhosted.org/packages/09/b5/0487044b7c096f1b48f0d7ad416472c02e0e4bf6919541b111efd3cae690/pillow-11.3.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:41742638139424703b4d01665b807c6468e23e699e8e90cffefe291c5832b027", size = 5973336, upload-time = "2025-07-01T09:15:21.237Z" },
- { url = "https://files.pythonhosted.org/packages/a8/2d/524f9318f6cbfcc79fbc004801ea6b607ec3f843977652fdee4857a7568b/pillow-11.3.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:93efb0b4de7e340d99057415c749175e24c8864302369e05914682ba642e5d77", size = 6642699, upload-time = "2025-07-01T09:15:23.186Z" },
- { url = "https://files.pythonhosted.org/packages/6f/d2/a9a4f280c6aefedce1e8f615baaa5474e0701d86dd6f1dede66726462bbd/pillow-11.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7966e38dcd0fa11ca390aed7c6f20454443581d758242023cf36fcb319b1a874", size = 6083789, upload-time = "2025-07-01T09:15:25.1Z" },
- { url = "https://files.pythonhosted.org/packages/fe/54/86b0cd9dbb683a9d5e960b66c7379e821a19be4ac5810e2e5a715c09a0c0/pillow-11.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:98a9afa7b9007c67ed84c57c9e0ad86a6000da96eaa638e4f8abe5b65ff83f0a", size = 6720386, upload-time = "2025-07-01T09:15:27.378Z" },
- { url = "https://files.pythonhosted.org/packages/e7/95/88efcaf384c3588e24259c4203b909cbe3e3c2d887af9e938c2022c9dd48/pillow-11.3.0-cp314-cp314-win32.whl", hash = "sha256:02a723e6bf909e7cea0dac1b0e0310be9d7650cd66222a5f1c571455c0a45214", size = 6370911, upload-time = "2025-07-01T09:15:29.294Z" },
- { url = "https://files.pythonhosted.org/packages/2e/cc/934e5820850ec5eb107e7b1a72dd278140731c669f396110ebc326f2a503/pillow-11.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:a418486160228f64dd9e9efcd132679b7a02a5f22c982c78b6fc7dab3fefb635", size = 7117383, upload-time = "2025-07-01T09:15:31.128Z" },
- { url = "https://files.pythonhosted.org/packages/d6/e9/9c0a616a71da2a5d163aa37405e8aced9a906d574b4a214bede134e731bc/pillow-11.3.0-cp314-cp314-win_arm64.whl", hash = "sha256:155658efb5e044669c08896c0c44231c5e9abcaadbc5cd3648df2f7c0b96b9a6", size = 2511385, upload-time = "2025-07-01T09:15:33.328Z" },
- { url = "https://files.pythonhosted.org/packages/1a/33/c88376898aff369658b225262cd4f2659b13e8178e7534df9e6e1fa289f6/pillow-11.3.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:59a03cdf019efbfeeed910bf79c7c93255c3d54bc45898ac2a4140071b02b4ae", size = 5281129, upload-time = "2025-07-01T09:15:35.194Z" },
- { url = "https://files.pythonhosted.org/packages/1f/70/d376247fb36f1844b42910911c83a02d5544ebd2a8bad9efcc0f707ea774/pillow-11.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f8a5827f84d973d8636e9dc5764af4f0cf2318d26744b3d902931701b0d46653", size = 4689580, upload-time = "2025-07-01T09:15:37.114Z" },
- { url = "https://files.pythonhosted.org/packages/eb/1c/537e930496149fbac69efd2fc4329035bbe2e5475b4165439e3be9cb183b/pillow-11.3.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ee92f2fd10f4adc4b43d07ec5e779932b4eb3dbfbc34790ada5a6669bc095aa6", size = 5902860, upload-time = "2025-07-03T13:10:50.248Z" },
- { url = "https://files.pythonhosted.org/packages/bd/57/80f53264954dcefeebcf9dae6e3eb1daea1b488f0be8b8fef12f79a3eb10/pillow-11.3.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c96d333dcf42d01f47b37e0979b6bd73ec91eae18614864622d9b87bbd5bbf36", size = 7670694, upload-time = "2025-07-03T13:10:56.432Z" },
- { url = "https://files.pythonhosted.org/packages/70/ff/4727d3b71a8578b4587d9c276e90efad2d6fe0335fd76742a6da08132e8c/pillow-11.3.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4c96f993ab8c98460cd0c001447bff6194403e8b1d7e149ade5f00594918128b", size = 6005888, upload-time = "2025-07-01T09:15:39.436Z" },
- { url = "https://files.pythonhosted.org/packages/05/ae/716592277934f85d3be51d7256f3636672d7b1abfafdc42cf3f8cbd4b4c8/pillow-11.3.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:41342b64afeba938edb034d122b2dda5db2139b9a4af999729ba8818e0056477", size = 6670330, upload-time = "2025-07-01T09:15:41.269Z" },
- { url = "https://files.pythonhosted.org/packages/e7/bb/7fe6cddcc8827b01b1a9766f5fdeb7418680744f9082035bdbabecf1d57f/pillow-11.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:068d9c39a2d1b358eb9f245ce7ab1b5c3246c7c8c7d9ba58cfa5b43146c06e50", size = 6114089, upload-time = "2025-07-01T09:15:43.13Z" },
- { url = "https://files.pythonhosted.org/packages/8b/f5/06bfaa444c8e80f1a8e4bff98da9c83b37b5be3b1deaa43d27a0db37ef84/pillow-11.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a1bc6ba083b145187f648b667e05a2534ecc4b9f2784c2cbe3089e44868f2b9b", size = 6748206, upload-time = "2025-07-01T09:15:44.937Z" },
- { url = "https://files.pythonhosted.org/packages/f0/77/bc6f92a3e8e6e46c0ca78abfffec0037845800ea38c73483760362804c41/pillow-11.3.0-cp314-cp314t-win32.whl", hash = "sha256:118ca10c0d60b06d006be10a501fd6bbdfef559251ed31b794668ed569c87e12", size = 6377370, upload-time = "2025-07-01T09:15:46.673Z" },
- { url = "https://files.pythonhosted.org/packages/4a/82/3a721f7d69dca802befb8af08b7c79ebcab461007ce1c18bd91a5d5896f9/pillow-11.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:8924748b688aa210d79883357d102cd64690e56b923a186f35a82cbc10f997db", size = 7121500, upload-time = "2025-07-01T09:15:48.512Z" },
- { url = "https://files.pythonhosted.org/packages/89/c7/5572fa4a3f45740eaab6ae86fcdf7195b55beac1371ac8c619d880cfe948/pillow-11.3.0-cp314-cp314t-win_arm64.whl", hash = "sha256:79ea0d14d3ebad43ec77ad5272e6ff9bba5b679ef73375ea760261207fa8e0aa", size = 2512835, upload-time = "2025-07-01T09:15:50.399Z" },
- { url = "https://files.pythonhosted.org/packages/9e/8e/9c089f01677d1264ab8648352dcb7773f37da6ad002542760c80107da816/pillow-11.3.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:48d254f8a4c776de343051023eb61ffe818299eeac478da55227d96e241de53f", size = 5316478, upload-time = "2025-07-01T09:15:52.209Z" },
- { url = "https://files.pythonhosted.org/packages/b5/a9/5749930caf674695867eb56a581e78eb5f524b7583ff10b01b6e5048acb3/pillow-11.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7aee118e30a4cf54fdd873bd3a29de51e29105ab11f9aad8c32123f58c8f8081", size = 4686522, upload-time = "2025-07-01T09:15:54.162Z" },
- { url = "https://files.pythonhosted.org/packages/43/46/0b85b763eb292b691030795f9f6bb6fcaf8948c39413c81696a01c3577f7/pillow-11.3.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:23cff760a9049c502721bdb743a7cb3e03365fafcdfc2ef9784610714166e5a4", size = 5853376, upload-time = "2025-07-03T13:11:01.066Z" },
- { url = "https://files.pythonhosted.org/packages/5e/c6/1a230ec0067243cbd60bc2dad5dc3ab46a8a41e21c15f5c9b52b26873069/pillow-11.3.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6359a3bc43f57d5b375d1ad54a0074318a0844d11b76abccf478c37c986d3cfc", size = 7626020, upload-time = "2025-07-03T13:11:06.479Z" },
- { url = "https://files.pythonhosted.org/packages/63/dd/f296c27ffba447bfad76c6a0c44c1ea97a90cb9472b9304c94a732e8dbfb/pillow-11.3.0-cp39-cp39-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:092c80c76635f5ecb10f3f83d76716165c96f5229addbd1ec2bdbbda7d496e06", size = 5956732, upload-time = "2025-07-01T09:15:56.111Z" },
- { url = "https://files.pythonhosted.org/packages/a5/a0/98a3630f0b57f77bae67716562513d3032ae70414fcaf02750279c389a9e/pillow-11.3.0-cp39-cp39-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cadc9e0ea0a2431124cde7e1697106471fc4c1da01530e679b2391c37d3fbb3a", size = 6624404, upload-time = "2025-07-01T09:15:58.245Z" },
- { url = "https://files.pythonhosted.org/packages/de/e6/83dfba5646a290edd9a21964da07674409e410579c341fc5b8f7abd81620/pillow-11.3.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:6a418691000f2a418c9135a7cf0d797c1bb7d9a485e61fe8e7722845b95ef978", size = 6067760, upload-time = "2025-07-01T09:16:00.003Z" },
- { url = "https://files.pythonhosted.org/packages/bc/41/15ab268fe6ee9a2bc7391e2bbb20a98d3974304ab1a406a992dcb297a370/pillow-11.3.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:97afb3a00b65cc0804d1c7abddbf090a81eaac02768af58cbdcaaa0a931e0b6d", size = 6700534, upload-time = "2025-07-01T09:16:02.29Z" },
- { url = "https://files.pythonhosted.org/packages/64/79/6d4f638b288300bed727ff29f2a3cb63db054b33518a95f27724915e3fbc/pillow-11.3.0-cp39-cp39-win32.whl", hash = "sha256:ea944117a7974ae78059fcc1800e5d3295172bb97035c0c1d9345fca1419da71", size = 6277091, upload-time = "2025-07-01T09:16:04.4Z" },
- { url = "https://files.pythonhosted.org/packages/46/05/4106422f45a05716fd34ed21763f8ec182e8ea00af6e9cb05b93a247361a/pillow-11.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:e5c5858ad8ec655450a7c7df532e9842cf8df7cc349df7225c60d5d348c8aada", size = 6986091, upload-time = "2025-07-01T09:16:06.342Z" },
- { url = "https://files.pythonhosted.org/packages/63/c6/287fd55c2c12761d0591549d48885187579b7c257bef0c6660755b0b59ae/pillow-11.3.0-cp39-cp39-win_arm64.whl", hash = "sha256:6abdbfd3aea42be05702a8dd98832329c167ee84400a1d1f61ab11437f1717eb", size = 2422632, upload-time = "2025-07-01T09:16:08.142Z" },
- { url = "https://files.pythonhosted.org/packages/6f/8b/209bd6b62ce8367f47e68a218bffac88888fdf2c9fcf1ecadc6c3ec1ebc7/pillow-11.3.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:3cee80663f29e3843b68199b9d6f4f54bd1d4a6b59bdd91bceefc51238bcb967", size = 5270556, upload-time = "2025-07-01T09:16:09.961Z" },
- { url = "https://files.pythonhosted.org/packages/2e/e6/231a0b76070c2cfd9e260a7a5b504fb72da0a95279410fa7afd99d9751d6/pillow-11.3.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:b5f56c3f344f2ccaf0dd875d3e180f631dc60a51b314295a3e681fe8cf851fbe", size = 4654625, upload-time = "2025-07-01T09:16:11.913Z" },
- { url = "https://files.pythonhosted.org/packages/13/f4/10cf94fda33cb12765f2397fc285fa6d8eb9c29de7f3185165b702fc7386/pillow-11.3.0-pp310-pypy310_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e67d793d180c9df62f1f40aee3accca4829d3794c95098887edc18af4b8b780c", size = 4874207, upload-time = "2025-07-03T13:11:10.201Z" },
- { url = "https://files.pythonhosted.org/packages/72/c9/583821097dc691880c92892e8e2d41fe0a5a3d6021f4963371d2f6d57250/pillow-11.3.0-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d000f46e2917c705e9fb93a3606ee4a819d1e3aa7a9b442f6444f07e77cf5e25", size = 6583939, upload-time = "2025-07-03T13:11:15.68Z" },
- { url = "https://files.pythonhosted.org/packages/3b/8e/5c9d410f9217b12320efc7c413e72693f48468979a013ad17fd690397b9a/pillow-11.3.0-pp310-pypy310_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:527b37216b6ac3a12d7838dc3bd75208ec57c1c6d11ef01902266a5a0c14fc27", size = 4957166, upload-time = "2025-07-01T09:16:13.74Z" },
- { url = "https://files.pythonhosted.org/packages/62/bb/78347dbe13219991877ffb3a91bf09da8317fbfcd4b5f9140aeae020ad71/pillow-11.3.0-pp310-pypy310_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:be5463ac478b623b9dd3937afd7fb7ab3d79dd290a28e2b6df292dc75063eb8a", size = 5581482, upload-time = "2025-07-01T09:16:16.107Z" },
- { url = "https://files.pythonhosted.org/packages/d9/28/1000353d5e61498aaeaaf7f1e4b49ddb05f2c6575f9d4f9f914a3538b6e1/pillow-11.3.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:8dc70ca24c110503e16918a658b869019126ecfe03109b754c402daff12b3d9f", size = 6984596, upload-time = "2025-07-01T09:16:18.07Z" },
- { url = "https://files.pythonhosted.org/packages/9e/e3/6fa84033758276fb31da12e5fb66ad747ae83b93c67af17f8c6ff4cc8f34/pillow-11.3.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7c8ec7a017ad1bd562f93dbd8505763e688d388cde6e4a010ae1486916e713e6", size = 5270566, upload-time = "2025-07-01T09:16:19.801Z" },
- { url = "https://files.pythonhosted.org/packages/5b/ee/e8d2e1ab4892970b561e1ba96cbd59c0d28cf66737fc44abb2aec3795a4e/pillow-11.3.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:9ab6ae226de48019caa8074894544af5b53a117ccb9d3b3dcb2871464c829438", size = 4654618, upload-time = "2025-07-01T09:16:21.818Z" },
- { url = "https://files.pythonhosted.org/packages/f2/6d/17f80f4e1f0761f02160fc433abd4109fa1548dcfdca46cfdadaf9efa565/pillow-11.3.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fe27fb049cdcca11f11a7bfda64043c37b30e6b91f10cb5bab275806c32f6ab3", size = 4874248, upload-time = "2025-07-03T13:11:20.738Z" },
- { url = "https://files.pythonhosted.org/packages/de/5f/c22340acd61cef960130585bbe2120e2fd8434c214802f07e8c03596b17e/pillow-11.3.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:465b9e8844e3c3519a983d58b80be3f668e2a7a5db97f2784e7079fbc9f9822c", size = 6583963, upload-time = "2025-07-03T13:11:26.283Z" },
- { url = "https://files.pythonhosted.org/packages/31/5e/03966aedfbfcbb4d5f8aa042452d3361f325b963ebbadddac05b122e47dd/pillow-11.3.0-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5418b53c0d59b3824d05e029669efa023bbef0f3e92e75ec8428f3799487f361", size = 4957170, upload-time = "2025-07-01T09:16:23.762Z" },
- { url = "https://files.pythonhosted.org/packages/cc/2d/e082982aacc927fc2cab48e1e731bdb1643a1406acace8bed0900a61464e/pillow-11.3.0-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:504b6f59505f08ae014f724b6207ff6222662aab5cc9542577fb084ed0676ac7", size = 5581505, upload-time = "2025-07-01T09:16:25.593Z" },
- { url = "https://files.pythonhosted.org/packages/34/e7/ae39f538fd6844e982063c3a5e4598b8ced43b9633baa3a85ef33af8c05c/pillow-11.3.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:c84d689db21a1c397d001aa08241044aa2069e7587b398c8cc63020390b1c1b8", size = 6984598, upload-time = "2025-07-01T09:16:27.732Z" },
-]
-
-[[package]]
-name = "platformdirs"
-version = "4.4.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/23/e8/21db9c9987b0e728855bd57bff6984f67952bea55d6f75e055c46b5383e8/platformdirs-4.4.0.tar.gz", hash = "sha256:ca753cf4d81dc309bc67b0ea38fd15dc97bc30ce419a7f58d13eb3bf14c4febf", size = 21634, upload-time = "2025-08-26T14:32:04.268Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/40/4b/2028861e724d3bd36227adfa20d3fd24c3fc6d52032f4a93c133be5d17ce/platformdirs-4.4.0-py3-none-any.whl", hash = "sha256:abd01743f24e5287cd7a5db3752faf1a2d65353f38ec26d98e25a6db65958c85", size = 18654, upload-time = "2025-08-26T14:32:02.735Z" },
-]
-
-[[package]]
-name = "plotly"
-version = "6.3.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "narwhals" },
- { name = "packaging" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/a0/64/850de5076f4436410e1ce4f6a69f4313ef6215dfea155f3f6559335cad29/plotly-6.3.0.tar.gz", hash = "sha256:8840a184d18ccae0f9189c2b9a2943923fd5cae7717b723f36eef78f444e5a73", size = 6923926, upload-time = "2025-08-12T20:22:14.127Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/95/a9/12e2dc726ba1ba775a2c6922d5d5b4488ad60bdab0888c337c194c8e6de8/plotly-6.3.0-py3-none-any.whl", hash = "sha256:7ad806edce9d3cdd882eaebaf97c0c9e252043ed1ed3d382c3e3520ec07806d4", size = 9791257, upload-time = "2025-08-12T20:22:09.205Z" },
-]
-
-[[package]]
-name = "pluggy"
-version = "1.6.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" },
-]
-
-[[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, upload-time = "2025-01-20T18:31:48.681Z" }
-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, upload-time = "2025-01-20T18:31:47.319Z" },
-]
-
-[[package]]
-name = "progressbar2"
-version = "4.5.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "python-utils" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/19/24/3587e795fc590611434e4bcb9fbe0c3dddb5754ce1a20edfd86c587c0004/progressbar2-4.5.0.tar.gz", hash = "sha256:6662cb624886ed31eb94daf61e27583b5144ebc7383a17bae076f8f4f59088fb", size = 101449, upload-time = "2024-08-28T22:50:12.391Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/ee/94/448f037fb0ffd0e8a63b625cf9f5b13494b88d15573a987be8aaa735579d/progressbar2-4.5.0-py3-none-any.whl", hash = "sha256:625c94a54e63915b3959355e6d4aacd63a00219e5f3e2b12181b76867bf6f628", size = 57132, upload-time = "2024-08-28T22:50:10.264Z" },
-]
-
-[[package]]
-name = "prompt-toolkit"
-version = "3.0.52"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "wcwidth" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/a1/96/06e01a7b38dce6fe1db213e061a4602dd6032a8a97ef6c1a862537732421/prompt_toolkit-3.0.52.tar.gz", hash = "sha256:28cde192929c8e7321de85de1ddbe736f1375148b02f2e17edd840042b1be855", size = 434198, upload-time = "2025-08-27T15:24:02.057Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/84/03/0d3ce49e2505ae70cf43bc5bb3033955d2fc9f932163e84dc0779cc47f48/prompt_toolkit-3.0.52-py3-none-any.whl", hash = "sha256:9aac639a3bbd33284347de5ad8d68ecc044b91a762dc39b7c21095fcd6a19955", size = 391431, upload-time = "2025-08-27T15:23:59.498Z" },
-]
-
-[[package]]
-name = "propcache"
-version = "0.3.2"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/a6/16/43264e4a779dd8588c21a70f0709665ee8f611211bdd2c87d952cfa7c776/propcache-0.3.2.tar.gz", hash = "sha256:20d7d62e4e7ef05f221e0db2856b979540686342e7dd9973b815599c7057e168", size = 44139, upload-time = "2025-06-09T22:56:06.081Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/ab/14/510deed325e262afeb8b360043c5d7c960da7d3ecd6d6f9496c9c56dc7f4/propcache-0.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:22d9962a358aedbb7a2e36187ff273adeaab9743373a272976d2e348d08c7770", size = 73178, upload-time = "2025-06-09T22:53:40.126Z" },
- { url = "https://files.pythonhosted.org/packages/cd/4e/ad52a7925ff01c1325653a730c7ec3175a23f948f08626a534133427dcff/propcache-0.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0d0fda578d1dc3f77b6b5a5dce3b9ad69a8250a891760a548df850a5e8da87f3", size = 43133, upload-time = "2025-06-09T22:53:41.965Z" },
- { url = "https://files.pythonhosted.org/packages/63/7c/e9399ba5da7780871db4eac178e9c2e204c23dd3e7d32df202092a1ed400/propcache-0.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3def3da3ac3ce41562d85db655d18ebac740cb3fa4367f11a52b3da9d03a5cc3", size = 43039, upload-time = "2025-06-09T22:53:43.268Z" },
- { url = "https://files.pythonhosted.org/packages/22/e1/58da211eb8fdc6fc854002387d38f415a6ca5f5c67c1315b204a5d3e9d7a/propcache-0.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9bec58347a5a6cebf239daba9bda37dffec5b8d2ce004d9fe4edef3d2815137e", size = 201903, upload-time = "2025-06-09T22:53:44.872Z" },
- { url = "https://files.pythonhosted.org/packages/c4/0a/550ea0f52aac455cb90111c8bab995208443e46d925e51e2f6ebdf869525/propcache-0.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:55ffda449a507e9fbd4aca1a7d9aa6753b07d6166140e5a18d2ac9bc49eac220", size = 213362, upload-time = "2025-06-09T22:53:46.707Z" },
- { url = "https://files.pythonhosted.org/packages/5a/af/9893b7d878deda9bb69fcf54600b247fba7317761b7db11fede6e0f28bd0/propcache-0.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:64a67fb39229a8a8491dd42f864e5e263155e729c2e7ff723d6e25f596b1e8cb", size = 210525, upload-time = "2025-06-09T22:53:48.547Z" },
- { url = "https://files.pythonhosted.org/packages/7c/bb/38fd08b278ca85cde36d848091ad2b45954bc5f15cce494bb300b9285831/propcache-0.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9da1cf97b92b51253d5b68cf5a2b9e0dafca095e36b7f2da335e27dc6172a614", size = 198283, upload-time = "2025-06-09T22:53:50.067Z" },
- { url = "https://files.pythonhosted.org/packages/78/8c/9fe55bd01d362bafb413dfe508c48753111a1e269737fa143ba85693592c/propcache-0.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5f559e127134b07425134b4065be45b166183fdcb433cb6c24c8e4149056ad50", size = 191872, upload-time = "2025-06-09T22:53:51.438Z" },
- { url = "https://files.pythonhosted.org/packages/54/14/4701c33852937a22584e08abb531d654c8bcf7948a8f87ad0a4822394147/propcache-0.3.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:aff2e4e06435d61f11a428360a932138d0ec288b0a31dd9bd78d200bd4a2b339", size = 199452, upload-time = "2025-06-09T22:53:53.229Z" },
- { url = "https://files.pythonhosted.org/packages/16/44/447f2253d859602095356007657ee535e0093215ea0b3d1d6a41d16e5201/propcache-0.3.2-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:4927842833830942a5d0a56e6f4839bc484785b8e1ce8d287359794818633ba0", size = 191567, upload-time = "2025-06-09T22:53:54.541Z" },
- { url = "https://files.pythonhosted.org/packages/f2/b3/e4756258749bb2d3b46defcff606a2f47410bab82be5824a67e84015b267/propcache-0.3.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:6107ddd08b02654a30fb8ad7a132021759d750a82578b94cd55ee2772b6ebea2", size = 193015, upload-time = "2025-06-09T22:53:56.44Z" },
- { url = "https://files.pythonhosted.org/packages/1e/df/e6d3c7574233164b6330b9fd697beeac402afd367280e6dc377bb99b43d9/propcache-0.3.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:70bd8b9cd6b519e12859c99f3fc9a93f375ebd22a50296c3a295028bea73b9e7", size = 204660, upload-time = "2025-06-09T22:53:57.839Z" },
- { url = "https://files.pythonhosted.org/packages/b2/53/e4d31dd5170b4a0e2e6b730f2385a96410633b4833dc25fe5dffd1f73294/propcache-0.3.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2183111651d710d3097338dd1893fcf09c9f54e27ff1a8795495a16a469cc90b", size = 206105, upload-time = "2025-06-09T22:53:59.638Z" },
- { url = "https://files.pythonhosted.org/packages/7f/fe/74d54cf9fbe2a20ff786e5f7afcfde446588f0cf15fb2daacfbc267b866c/propcache-0.3.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:fb075ad271405dcad8e2a7ffc9a750a3bf70e533bd86e89f0603e607b93aa64c", size = 196980, upload-time = "2025-06-09T22:54:01.071Z" },
- { url = "https://files.pythonhosted.org/packages/22/ec/c469c9d59dada8a7679625e0440b544fe72e99311a4679c279562051f6fc/propcache-0.3.2-cp310-cp310-win32.whl", hash = "sha256:404d70768080d3d3bdb41d0771037da19d8340d50b08e104ca0e7f9ce55fce70", size = 37679, upload-time = "2025-06-09T22:54:03.003Z" },
- { url = "https://files.pythonhosted.org/packages/38/35/07a471371ac89d418f8d0b699c75ea6dca2041fbda360823de21f6a9ce0a/propcache-0.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:7435d766f978b4ede777002e6b3b6641dd229cd1da8d3d3106a45770365f9ad9", size = 41459, upload-time = "2025-06-09T22:54:04.134Z" },
- { url = "https://files.pythonhosted.org/packages/80/8d/e8b436717ab9c2cfc23b116d2c297305aa4cd8339172a456d61ebf5669b8/propcache-0.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0b8d2f607bd8f80ddc04088bc2a037fdd17884a6fcadc47a96e334d72f3717be", size = 74207, upload-time = "2025-06-09T22:54:05.399Z" },
- { url = "https://files.pythonhosted.org/packages/d6/29/1e34000e9766d112171764b9fa3226fa0153ab565d0c242c70e9945318a7/propcache-0.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:06766d8f34733416e2e34f46fea488ad5d60726bb9481d3cddf89a6fa2d9603f", size = 43648, upload-time = "2025-06-09T22:54:08.023Z" },
- { url = "https://files.pythonhosted.org/packages/46/92/1ad5af0df781e76988897da39b5f086c2bf0f028b7f9bd1f409bb05b6874/propcache-0.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a2dc1f4a1df4fecf4e6f68013575ff4af84ef6f478fe5344317a65d38a8e6dc9", size = 43496, upload-time = "2025-06-09T22:54:09.228Z" },
- { url = "https://files.pythonhosted.org/packages/b3/ce/e96392460f9fb68461fabab3e095cb00c8ddf901205be4eae5ce246e5b7e/propcache-0.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be29c4f4810c5789cf10ddf6af80b041c724e629fa51e308a7a0fb19ed1ef7bf", size = 217288, upload-time = "2025-06-09T22:54:10.466Z" },
- { url = "https://files.pythonhosted.org/packages/c5/2a/866726ea345299f7ceefc861a5e782b045545ae6940851930a6adaf1fca6/propcache-0.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59d61f6970ecbd8ff2e9360304d5c8876a6abd4530cb752c06586849ac8a9dc9", size = 227456, upload-time = "2025-06-09T22:54:11.828Z" },
- { url = "https://files.pythonhosted.org/packages/de/03/07d992ccb6d930398689187e1b3c718339a1c06b8b145a8d9650e4726166/propcache-0.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:62180e0b8dbb6b004baec00a7983e4cc52f5ada9cd11f48c3528d8cfa7b96a66", size = 225429, upload-time = "2025-06-09T22:54:13.823Z" },
- { url = "https://files.pythonhosted.org/packages/5d/e6/116ba39448753b1330f48ab8ba927dcd6cf0baea8a0ccbc512dfb49ba670/propcache-0.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c144ca294a204c470f18cf4c9d78887810d04a3e2fbb30eea903575a779159df", size = 213472, upload-time = "2025-06-09T22:54:15.232Z" },
- { url = "https://files.pythonhosted.org/packages/a6/85/f01f5d97e54e428885a5497ccf7f54404cbb4f906688a1690cd51bf597dc/propcache-0.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c5c2a784234c28854878d68978265617aa6dc0780e53d44b4d67f3651a17a9a2", size = 204480, upload-time = "2025-06-09T22:54:17.104Z" },
- { url = "https://files.pythonhosted.org/packages/e3/79/7bf5ab9033b8b8194cc3f7cf1aaa0e9c3256320726f64a3e1f113a812dce/propcache-0.3.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5745bc7acdafa978ca1642891b82c19238eadc78ba2aaa293c6863b304e552d7", size = 214530, upload-time = "2025-06-09T22:54:18.512Z" },
- { url = "https://files.pythonhosted.org/packages/31/0b/bd3e0c00509b609317df4a18e6b05a450ef2d9a963e1d8bc9c9415d86f30/propcache-0.3.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:c0075bf773d66fa8c9d41f66cc132ecc75e5bb9dd7cce3cfd14adc5ca184cb95", size = 205230, upload-time = "2025-06-09T22:54:19.947Z" },
- { url = "https://files.pythonhosted.org/packages/7a/23/fae0ff9b54b0de4e819bbe559508da132d5683c32d84d0dc2ccce3563ed4/propcache-0.3.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5f57aa0847730daceff0497f417c9de353c575d8da3579162cc74ac294c5369e", size = 206754, upload-time = "2025-06-09T22:54:21.716Z" },
- { url = "https://files.pythonhosted.org/packages/b7/7f/ad6a3c22630aaa5f618b4dc3c3598974a72abb4c18e45a50b3cdd091eb2f/propcache-0.3.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:eef914c014bf72d18efb55619447e0aecd5fb7c2e3fa7441e2e5d6099bddff7e", size = 218430, upload-time = "2025-06-09T22:54:23.17Z" },
- { url = "https://files.pythonhosted.org/packages/5b/2c/ba4f1c0e8a4b4c75910742f0d333759d441f65a1c7f34683b4a74c0ee015/propcache-0.3.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:2a4092e8549031e82facf3decdbc0883755d5bbcc62d3aea9d9e185549936dcf", size = 223884, upload-time = "2025-06-09T22:54:25.539Z" },
- { url = "https://files.pythonhosted.org/packages/88/e4/ebe30fc399e98572019eee82ad0caf512401661985cbd3da5e3140ffa1b0/propcache-0.3.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:85871b050f174bc0bfb437efbdb68aaf860611953ed12418e4361bc9c392749e", size = 211480, upload-time = "2025-06-09T22:54:26.892Z" },
- { url = "https://files.pythonhosted.org/packages/96/0a/7d5260b914e01d1d0906f7f38af101f8d8ed0dc47426219eeaf05e8ea7c2/propcache-0.3.2-cp311-cp311-win32.whl", hash = "sha256:36c8d9b673ec57900c3554264e630d45980fd302458e4ac801802a7fd2ef7897", size = 37757, upload-time = "2025-06-09T22:54:28.241Z" },
- { url = "https://files.pythonhosted.org/packages/e1/2d/89fe4489a884bc0da0c3278c552bd4ffe06a1ace559db5ef02ef24ab446b/propcache-0.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:e53af8cb6a781b02d2ea079b5b853ba9430fcbe18a8e3ce647d5982a3ff69f39", size = 41500, upload-time = "2025-06-09T22:54:29.4Z" },
- { url = "https://files.pythonhosted.org/packages/a8/42/9ca01b0a6f48e81615dca4765a8f1dd2c057e0540f6116a27dc5ee01dfb6/propcache-0.3.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8de106b6c84506b31c27168582cd3cb3000a6412c16df14a8628e5871ff83c10", size = 73674, upload-time = "2025-06-09T22:54:30.551Z" },
- { url = "https://files.pythonhosted.org/packages/af/6e/21293133beb550f9c901bbece755d582bfaf2176bee4774000bd4dd41884/propcache-0.3.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:28710b0d3975117239c76600ea351934ac7b5ff56e60953474342608dbbb6154", size = 43570, upload-time = "2025-06-09T22:54:32.296Z" },
- { url = "https://files.pythonhosted.org/packages/0c/c8/0393a0a3a2b8760eb3bde3c147f62b20044f0ddac81e9d6ed7318ec0d852/propcache-0.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce26862344bdf836650ed2487c3d724b00fbfec4233a1013f597b78c1cb73615", size = 43094, upload-time = "2025-06-09T22:54:33.929Z" },
- { url = "https://files.pythonhosted.org/packages/37/2c/489afe311a690399d04a3e03b069225670c1d489eb7b044a566511c1c498/propcache-0.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bca54bd347a253af2cf4544bbec232ab982f4868de0dd684246b67a51bc6b1db", size = 226958, upload-time = "2025-06-09T22:54:35.186Z" },
- { url = "https://files.pythonhosted.org/packages/9d/ca/63b520d2f3d418c968bf596839ae26cf7f87bead026b6192d4da6a08c467/propcache-0.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:55780d5e9a2ddc59711d727226bb1ba83a22dd32f64ee15594b9392b1f544eb1", size = 234894, upload-time = "2025-06-09T22:54:36.708Z" },
- { url = "https://files.pythonhosted.org/packages/11/60/1d0ed6fff455a028d678df30cc28dcee7af77fa2b0e6962ce1df95c9a2a9/propcache-0.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:035e631be25d6975ed87ab23153db6a73426a48db688070d925aa27e996fe93c", size = 233672, upload-time = "2025-06-09T22:54:38.062Z" },
- { url = "https://files.pythonhosted.org/packages/37/7c/54fd5301ef38505ab235d98827207176a5c9b2aa61939b10a460ca53e123/propcache-0.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee6f22b6eaa39297c751d0e80c0d3a454f112f5c6481214fcf4c092074cecd67", size = 224395, upload-time = "2025-06-09T22:54:39.634Z" },
- { url = "https://files.pythonhosted.org/packages/ee/1a/89a40e0846f5de05fdc6779883bf46ba980e6df4d2ff8fb02643de126592/propcache-0.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7ca3aee1aa955438c4dba34fc20a9f390e4c79967257d830f137bd5a8a32ed3b", size = 212510, upload-time = "2025-06-09T22:54:41.565Z" },
- { url = "https://files.pythonhosted.org/packages/5e/33/ca98368586c9566a6b8d5ef66e30484f8da84c0aac3f2d9aec6d31a11bd5/propcache-0.3.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7a4f30862869fa2b68380d677cc1c5fcf1e0f2b9ea0cf665812895c75d0ca3b8", size = 222949, upload-time = "2025-06-09T22:54:43.038Z" },
- { url = "https://files.pythonhosted.org/packages/ba/11/ace870d0aafe443b33b2f0b7efdb872b7c3abd505bfb4890716ad7865e9d/propcache-0.3.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:b77ec3c257d7816d9f3700013639db7491a434644c906a2578a11daf13176251", size = 217258, upload-time = "2025-06-09T22:54:44.376Z" },
- { url = "https://files.pythonhosted.org/packages/5b/d2/86fd6f7adffcfc74b42c10a6b7db721d1d9ca1055c45d39a1a8f2a740a21/propcache-0.3.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:cab90ac9d3f14b2d5050928483d3d3b8fb6b4018893fc75710e6aa361ecb2474", size = 213036, upload-time = "2025-06-09T22:54:46.243Z" },
- { url = "https://files.pythonhosted.org/packages/07/94/2d7d1e328f45ff34a0a284cf5a2847013701e24c2a53117e7c280a4316b3/propcache-0.3.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:0b504d29f3c47cf6b9e936c1852246c83d450e8e063d50562115a6be6d3a2535", size = 227684, upload-time = "2025-06-09T22:54:47.63Z" },
- { url = "https://files.pythonhosted.org/packages/b7/05/37ae63a0087677e90b1d14710e532ff104d44bc1efa3b3970fff99b891dc/propcache-0.3.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:ce2ac2675a6aa41ddb2a0c9cbff53780a617ac3d43e620f8fd77ba1c84dcfc06", size = 234562, upload-time = "2025-06-09T22:54:48.982Z" },
- { url = "https://files.pythonhosted.org/packages/a4/7c/3f539fcae630408d0bd8bf3208b9a647ccad10976eda62402a80adf8fc34/propcache-0.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:62b4239611205294cc433845b914131b2a1f03500ff3c1ed093ed216b82621e1", size = 222142, upload-time = "2025-06-09T22:54:50.424Z" },
- { url = "https://files.pythonhosted.org/packages/7c/d2/34b9eac8c35f79f8a962546b3e97e9d4b990c420ee66ac8255d5d9611648/propcache-0.3.2-cp312-cp312-win32.whl", hash = "sha256:df4a81b9b53449ebc90cc4deefb052c1dd934ba85012aa912c7ea7b7e38b60c1", size = 37711, upload-time = "2025-06-09T22:54:52.072Z" },
- { url = "https://files.pythonhosted.org/packages/19/61/d582be5d226cf79071681d1b46b848d6cb03d7b70af7063e33a2787eaa03/propcache-0.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:7046e79b989d7fe457bb755844019e10f693752d169076138abf17f31380800c", size = 41479, upload-time = "2025-06-09T22:54:53.234Z" },
- { url = "https://files.pythonhosted.org/packages/dc/d1/8c747fafa558c603c4ca19d8e20b288aa0c7cda74e9402f50f31eb65267e/propcache-0.3.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ca592ed634a73ca002967458187109265e980422116c0a107cf93d81f95af945", size = 71286, upload-time = "2025-06-09T22:54:54.369Z" },
- { url = "https://files.pythonhosted.org/packages/61/99/d606cb7986b60d89c36de8a85d58764323b3a5ff07770a99d8e993b3fa73/propcache-0.3.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9ecb0aad4020e275652ba3975740f241bd12a61f1a784df044cf7477a02bc252", size = 42425, upload-time = "2025-06-09T22:54:55.642Z" },
- { url = "https://files.pythonhosted.org/packages/8c/96/ef98f91bbb42b79e9bb82bdd348b255eb9d65f14dbbe3b1594644c4073f7/propcache-0.3.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7f08f1cc28bd2eade7a8a3d2954ccc673bb02062e3e7da09bc75d843386b342f", size = 41846, upload-time = "2025-06-09T22:54:57.246Z" },
- { url = "https://files.pythonhosted.org/packages/5b/ad/3f0f9a705fb630d175146cd7b1d2bf5555c9beaed54e94132b21aac098a6/propcache-0.3.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1a342c834734edb4be5ecb1e9fb48cb64b1e2320fccbd8c54bf8da8f2a84c33", size = 208871, upload-time = "2025-06-09T22:54:58.975Z" },
- { url = "https://files.pythonhosted.org/packages/3a/38/2085cda93d2c8b6ec3e92af2c89489a36a5886b712a34ab25de9fbca7992/propcache-0.3.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8a544caaae1ac73f1fecfae70ded3e93728831affebd017d53449e3ac052ac1e", size = 215720, upload-time = "2025-06-09T22:55:00.471Z" },
- { url = "https://files.pythonhosted.org/packages/61/c1/d72ea2dc83ac7f2c8e182786ab0fc2c7bd123a1ff9b7975bee671866fe5f/propcache-0.3.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:310d11aa44635298397db47a3ebce7db99a4cc4b9bbdfcf6c98a60c8d5261cf1", size = 215203, upload-time = "2025-06-09T22:55:01.834Z" },
- { url = "https://files.pythonhosted.org/packages/af/81/b324c44ae60c56ef12007105f1460d5c304b0626ab0cc6b07c8f2a9aa0b8/propcache-0.3.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c1396592321ac83157ac03a2023aa6cc4a3cc3cfdecb71090054c09e5a7cce3", size = 206365, upload-time = "2025-06-09T22:55:03.199Z" },
- { url = "https://files.pythonhosted.org/packages/09/73/88549128bb89e66d2aff242488f62869014ae092db63ccea53c1cc75a81d/propcache-0.3.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8cabf5b5902272565e78197edb682017d21cf3b550ba0460ee473753f28d23c1", size = 196016, upload-time = "2025-06-09T22:55:04.518Z" },
- { url = "https://files.pythonhosted.org/packages/b9/3f/3bdd14e737d145114a5eb83cb172903afba7242f67c5877f9909a20d948d/propcache-0.3.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0a2f2235ac46a7aa25bdeb03a9e7060f6ecbd213b1f9101c43b3090ffb971ef6", size = 205596, upload-time = "2025-06-09T22:55:05.942Z" },
- { url = "https://files.pythonhosted.org/packages/0f/ca/2f4aa819c357d3107c3763d7ef42c03980f9ed5c48c82e01e25945d437c1/propcache-0.3.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:92b69e12e34869a6970fd2f3da91669899994b47c98f5d430b781c26f1d9f387", size = 200977, upload-time = "2025-06-09T22:55:07.792Z" },
- { url = "https://files.pythonhosted.org/packages/cd/4a/e65276c7477533c59085251ae88505caf6831c0e85ff8b2e31ebcbb949b1/propcache-0.3.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:54e02207c79968ebbdffc169591009f4474dde3b4679e16634d34c9363ff56b4", size = 197220, upload-time = "2025-06-09T22:55:09.173Z" },
- { url = "https://files.pythonhosted.org/packages/7c/54/fc7152e517cf5578278b242396ce4d4b36795423988ef39bb8cd5bf274c8/propcache-0.3.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4adfb44cb588001f68c5466579d3f1157ca07f7504fc91ec87862e2b8e556b88", size = 210642, upload-time = "2025-06-09T22:55:10.62Z" },
- { url = "https://files.pythonhosted.org/packages/b9/80/abeb4a896d2767bf5f1ea7b92eb7be6a5330645bd7fb844049c0e4045d9d/propcache-0.3.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:fd3e6019dc1261cd0291ee8919dd91fbab7b169bb76aeef6c716833a3f65d206", size = 212789, upload-time = "2025-06-09T22:55:12.029Z" },
- { url = "https://files.pythonhosted.org/packages/b3/db/ea12a49aa7b2b6d68a5da8293dcf50068d48d088100ac016ad92a6a780e6/propcache-0.3.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4c181cad81158d71c41a2bce88edce078458e2dd5ffee7eddd6b05da85079f43", size = 205880, upload-time = "2025-06-09T22:55:13.45Z" },
- { url = "https://files.pythonhosted.org/packages/d1/e5/9076a0bbbfb65d1198007059c65639dfd56266cf8e477a9707e4b1999ff4/propcache-0.3.2-cp313-cp313-win32.whl", hash = "sha256:8a08154613f2249519e549de2330cf8e2071c2887309a7b07fb56098f5170a02", size = 37220, upload-time = "2025-06-09T22:55:15.284Z" },
- { url = "https://files.pythonhosted.org/packages/d3/f5/b369e026b09a26cd77aa88d8fffd69141d2ae00a2abaaf5380d2603f4b7f/propcache-0.3.2-cp313-cp313-win_amd64.whl", hash = "sha256:e41671f1594fc4ab0a6dec1351864713cb3a279910ae8b58f884a88a0a632c05", size = 40678, upload-time = "2025-06-09T22:55:16.445Z" },
- { url = "https://files.pythonhosted.org/packages/a4/3a/6ece377b55544941a08d03581c7bc400a3c8cd3c2865900a68d5de79e21f/propcache-0.3.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:9a3cf035bbaf035f109987d9d55dc90e4b0e36e04bbbb95af3055ef17194057b", size = 76560, upload-time = "2025-06-09T22:55:17.598Z" },
- { url = "https://files.pythonhosted.org/packages/0c/da/64a2bb16418740fa634b0e9c3d29edff1db07f56d3546ca2d86ddf0305e1/propcache-0.3.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:156c03d07dc1323d8dacaa221fbe028c5c70d16709cdd63502778e6c3ccca1b0", size = 44676, upload-time = "2025-06-09T22:55:18.922Z" },
- { url = "https://files.pythonhosted.org/packages/36/7b/f025e06ea51cb72c52fb87e9b395cced02786610b60a3ed51da8af017170/propcache-0.3.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:74413c0ba02ba86f55cf60d18daab219f7e531620c15f1e23d95563f505efe7e", size = 44701, upload-time = "2025-06-09T22:55:20.106Z" },
- { url = "https://files.pythonhosted.org/packages/a4/00/faa1b1b7c3b74fc277f8642f32a4c72ba1d7b2de36d7cdfb676db7f4303e/propcache-0.3.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f066b437bb3fa39c58ff97ab2ca351db465157d68ed0440abecb21715eb24b28", size = 276934, upload-time = "2025-06-09T22:55:21.5Z" },
- { url = "https://files.pythonhosted.org/packages/74/ab/935beb6f1756e0476a4d5938ff44bf0d13a055fed880caf93859b4f1baf4/propcache-0.3.2-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f1304b085c83067914721e7e9d9917d41ad87696bf70f0bc7dee450e9c71ad0a", size = 278316, upload-time = "2025-06-09T22:55:22.918Z" },
- { url = "https://files.pythonhosted.org/packages/f8/9d/994a5c1ce4389610838d1caec74bdf0e98b306c70314d46dbe4fcf21a3e2/propcache-0.3.2-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ab50cef01b372763a13333b4e54021bdcb291fc9a8e2ccb9c2df98be51bcde6c", size = 282619, upload-time = "2025-06-09T22:55:24.651Z" },
- { url = "https://files.pythonhosted.org/packages/2b/00/a10afce3d1ed0287cef2e09506d3be9822513f2c1e96457ee369adb9a6cd/propcache-0.3.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fad3b2a085ec259ad2c2842666b2a0a49dea8463579c606426128925af1ed725", size = 265896, upload-time = "2025-06-09T22:55:26.049Z" },
- { url = "https://files.pythonhosted.org/packages/2e/a8/2aa6716ffa566ca57c749edb909ad27884680887d68517e4be41b02299f3/propcache-0.3.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:261fa020c1c14deafd54c76b014956e2f86991af198c51139faf41c4d5e83892", size = 252111, upload-time = "2025-06-09T22:55:27.381Z" },
- { url = "https://files.pythonhosted.org/packages/36/4f/345ca9183b85ac29c8694b0941f7484bf419c7f0fea2d1e386b4f7893eed/propcache-0.3.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:46d7f8aa79c927e5f987ee3a80205c987717d3659f035c85cf0c3680526bdb44", size = 268334, upload-time = "2025-06-09T22:55:28.747Z" },
- { url = "https://files.pythonhosted.org/packages/3e/ca/fcd54f78b59e3f97b3b9715501e3147f5340167733d27db423aa321e7148/propcache-0.3.2-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:6d8f3f0eebf73e3c0ff0e7853f68be638b4043c65a70517bb575eff54edd8dbe", size = 255026, upload-time = "2025-06-09T22:55:30.184Z" },
- { url = "https://files.pythonhosted.org/packages/8b/95/8e6a6bbbd78ac89c30c225210a5c687790e532ba4088afb8c0445b77ef37/propcache-0.3.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:03c89c1b14a5452cf15403e291c0ccd7751d5b9736ecb2c5bab977ad6c5bcd81", size = 250724, upload-time = "2025-06-09T22:55:31.646Z" },
- { url = "https://files.pythonhosted.org/packages/ee/b0/0dd03616142baba28e8b2d14ce5df6631b4673850a3d4f9c0f9dd714a404/propcache-0.3.2-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:0cc17efde71e12bbaad086d679ce575268d70bc123a5a71ea7ad76f70ba30bba", size = 268868, upload-time = "2025-06-09T22:55:33.209Z" },
- { url = "https://files.pythonhosted.org/packages/c5/98/2c12407a7e4fbacd94ddd32f3b1e3d5231e77c30ef7162b12a60e2dd5ce3/propcache-0.3.2-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:acdf05d00696bc0447e278bb53cb04ca72354e562cf88ea6f9107df8e7fd9770", size = 271322, upload-time = "2025-06-09T22:55:35.065Z" },
- { url = "https://files.pythonhosted.org/packages/35/91/9cb56efbb428b006bb85db28591e40b7736847b8331d43fe335acf95f6c8/propcache-0.3.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4445542398bd0b5d32df908031cb1b30d43ac848e20470a878b770ec2dcc6330", size = 265778, upload-time = "2025-06-09T22:55:36.45Z" },
- { url = "https://files.pythonhosted.org/packages/9a/4c/b0fe775a2bdd01e176b14b574be679d84fc83958335790f7c9a686c1f468/propcache-0.3.2-cp313-cp313t-win32.whl", hash = "sha256:f86e5d7cd03afb3a1db8e9f9f6eff15794e79e791350ac48a8c924e6f439f394", size = 41175, upload-time = "2025-06-09T22:55:38.436Z" },
- { url = "https://files.pythonhosted.org/packages/a4/ff/47f08595e3d9b5e149c150f88d9714574f1a7cbd89fe2817158a952674bf/propcache-0.3.2-cp313-cp313t-win_amd64.whl", hash = "sha256:9704bedf6e7cbe3c65eca4379a9b53ee6a83749f047808cbb5044d40d7d72198", size = 44857, upload-time = "2025-06-09T22:55:39.687Z" },
- { url = "https://files.pythonhosted.org/packages/6c/39/8ea9bcfaaff16fd0b0fc901ee522e24c9ec44b4ca0229cfffb8066a06959/propcache-0.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a7fad897f14d92086d6b03fdd2eb844777b0c4d7ec5e3bac0fbae2ab0602bbe5", size = 74678, upload-time = "2025-06-09T22:55:41.227Z" },
- { url = "https://files.pythonhosted.org/packages/d3/85/cab84c86966e1d354cf90cdc4ba52f32f99a5bca92a1529d666d957d7686/propcache-0.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1f43837d4ca000243fd7fd6301947d7cb93360d03cd08369969450cc6b2ce3b4", size = 43829, upload-time = "2025-06-09T22:55:42.417Z" },
- { url = "https://files.pythonhosted.org/packages/23/f7/9cb719749152d8b26d63801b3220ce2d3931312b2744d2b3a088b0ee9947/propcache-0.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:261df2e9474a5949c46e962065d88eb9b96ce0f2bd30e9d3136bcde84befd8f2", size = 43729, upload-time = "2025-06-09T22:55:43.651Z" },
- { url = "https://files.pythonhosted.org/packages/a2/a2/0b2b5a210ff311260002a315f6f9531b65a36064dfb804655432b2f7d3e3/propcache-0.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e514326b79e51f0a177daab1052bc164d9d9e54133797a3a58d24c9c87a3fe6d", size = 204483, upload-time = "2025-06-09T22:55:45.327Z" },
- { url = "https://files.pythonhosted.org/packages/3f/e0/7aff5de0c535f783b0c8be5bdb750c305c1961d69fbb136939926e155d98/propcache-0.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d4a996adb6904f85894570301939afeee65f072b4fd265ed7e569e8d9058e4ec", size = 217425, upload-time = "2025-06-09T22:55:46.729Z" },
- { url = "https://files.pythonhosted.org/packages/92/1d/65fa889eb3b2a7d6e4ed3c2b568a9cb8817547a1450b572de7bf24872800/propcache-0.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:76cace5d6b2a54e55b137669b30f31aa15977eeed390c7cbfb1dafa8dfe9a701", size = 214723, upload-time = "2025-06-09T22:55:48.342Z" },
- { url = "https://files.pythonhosted.org/packages/9a/e2/eecf6989870988dfd731de408a6fa366e853d361a06c2133b5878ce821ad/propcache-0.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31248e44b81d59d6addbb182c4720f90b44e1efdc19f58112a3c3a1615fb47ef", size = 200166, upload-time = "2025-06-09T22:55:49.775Z" },
- { url = "https://files.pythonhosted.org/packages/12/06/c32be4950967f18f77489268488c7cdc78cbfc65a8ba8101b15e526b83dc/propcache-0.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abb7fa19dbf88d3857363e0493b999b8011eea856b846305d8c0512dfdf8fbb1", size = 194004, upload-time = "2025-06-09T22:55:51.335Z" },
- { url = "https://files.pythonhosted.org/packages/46/6c/17b521a6b3b7cbe277a4064ff0aa9129dd8c89f425a5a9b6b4dd51cc3ff4/propcache-0.3.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:d81ac3ae39d38588ad0549e321e6f773a4e7cc68e7751524a22885d5bbadf886", size = 203075, upload-time = "2025-06-09T22:55:52.681Z" },
- { url = "https://files.pythonhosted.org/packages/62/cb/3bdba2b736b3e45bc0e40f4370f745b3e711d439ffbffe3ae416393eece9/propcache-0.3.2-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:cc2782eb0f7a16462285b6f8394bbbd0e1ee5f928034e941ffc444012224171b", size = 195407, upload-time = "2025-06-09T22:55:54.048Z" },
- { url = "https://files.pythonhosted.org/packages/29/bd/760c5c6a60a4a2c55a421bc34a25ba3919d49dee411ddb9d1493bb51d46e/propcache-0.3.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:db429c19a6c7e8a1c320e6a13c99799450f411b02251fb1b75e6217cf4a14fcb", size = 196045, upload-time = "2025-06-09T22:55:55.485Z" },
- { url = "https://files.pythonhosted.org/packages/76/58/ced2757a46f55b8c84358d6ab8de4faf57cba831c51e823654da7144b13a/propcache-0.3.2-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:21d8759141a9e00a681d35a1f160892a36fb6caa715ba0b832f7747da48fb6ea", size = 208432, upload-time = "2025-06-09T22:55:56.884Z" },
- { url = "https://files.pythonhosted.org/packages/bb/ec/d98ea8d5a4d8fe0e372033f5254eddf3254344c0c5dc6c49ab84349e4733/propcache-0.3.2-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:2ca6d378f09adb13837614ad2754fa8afaee330254f404299611bce41a8438cb", size = 210100, upload-time = "2025-06-09T22:55:58.498Z" },
- { url = "https://files.pythonhosted.org/packages/56/84/b6d8a7ecf3f62d7dd09d9d10bbf89fad6837970ef868b35b5ffa0d24d9de/propcache-0.3.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:34a624af06c048946709f4278b4176470073deda88d91342665d95f7c6270fbe", size = 200712, upload-time = "2025-06-09T22:55:59.906Z" },
- { url = "https://files.pythonhosted.org/packages/bf/32/889f4903ddfe4a9dc61da71ee58b763758cf2d608fe1decede06e6467f8d/propcache-0.3.2-cp39-cp39-win32.whl", hash = "sha256:4ba3fef1c30f306b1c274ce0b8baaa2c3cdd91f645c48f06394068f37d3837a1", size = 38187, upload-time = "2025-06-09T22:56:01.212Z" },
- { url = "https://files.pythonhosted.org/packages/67/74/d666795fb9ba1dc139d30de64f3b6fd1ff9c9d3d96ccfdb992cd715ce5d2/propcache-0.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:7a2368eed65fc69a7a7a40b27f22e85e7627b74216f0846b04ba5c116e191ec9", size = 42025, upload-time = "2025-06-09T22:56:02.875Z" },
- { url = "https://files.pythonhosted.org/packages/cc/35/cc0aaecf278bb4575b8555f2b137de5ab821595ddae9da9d3cd1da4072c7/propcache-0.3.2-py3-none-any.whl", hash = "sha256:98f1ec44fb675f5052cccc8e609c46ed23a35a1cfd18545ad4e29002d858a43f", size = 12663, upload-time = "2025-06-09T22:56:04.484Z" },
-]
-
-[[package]]
-name = "proto-plus"
-version = "1.26.1"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "protobuf" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/f4/ac/87285f15f7cce6d4a008f33f1757fb5a13611ea8914eb58c3d0d26243468/proto_plus-1.26.1.tar.gz", hash = "sha256:21a515a4c4c0088a773899e23c7bbade3d18f9c66c73edd4c7ee3816bc96a012", size = 56142, upload-time = "2025-03-10T15:54:38.843Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/4e/6d/280c4c2ce28b1593a19ad5239c8b826871fc6ec275c21afc8e1820108039/proto_plus-1.26.1-py3-none-any.whl", hash = "sha256:13285478c2dcf2abb829db158e1047e2f1e8d63a077d94263c2b88b043c75a66", size = 50163, upload-time = "2025-03-10T15:54:37.335Z" },
-]
-
-[[package]]
-name = "protobuf"
-version = "5.29.5"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/43/29/d09e70352e4e88c9c7a198d5645d7277811448d76c23b00345670f7c8a38/protobuf-5.29.5.tar.gz", hash = "sha256:bc1463bafd4b0929216c35f437a8e28731a2b7fe3d98bb77a600efced5a15c84", size = 425226, upload-time = "2025-05-28T23:51:59.82Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/5f/11/6e40e9fc5bba02988a214c07cf324595789ca7820160bfd1f8be96e48539/protobuf-5.29.5-cp310-abi3-win32.whl", hash = "sha256:3f1c6468a2cfd102ff4703976138844f78ebd1fb45f49011afc5139e9e283079", size = 422963, upload-time = "2025-05-28T23:51:41.204Z" },
- { url = "https://files.pythonhosted.org/packages/81/7f/73cefb093e1a2a7c3ffd839e6f9fcafb7a427d300c7f8aef9c64405d8ac6/protobuf-5.29.5-cp310-abi3-win_amd64.whl", hash = "sha256:3f76e3a3675b4a4d867b52e4a5f5b78a2ef9565549d4037e06cf7b0942b1d3fc", size = 434818, upload-time = "2025-05-28T23:51:44.297Z" },
- { url = "https://files.pythonhosted.org/packages/dd/73/10e1661c21f139f2c6ad9b23040ff36fee624310dc28fba20d33fdae124c/protobuf-5.29.5-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:e38c5add5a311f2a6eb0340716ef9b039c1dfa428b28f25a7838ac329204a671", size = 418091, upload-time = "2025-05-28T23:51:45.907Z" },
- { url = "https://files.pythonhosted.org/packages/6c/04/98f6f8cf5b07ab1294c13f34b4e69b3722bb609c5b701d6c169828f9f8aa/protobuf-5.29.5-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:fa18533a299d7ab6c55a238bf8629311439995f2e7eca5caaff08663606e9015", size = 319824, upload-time = "2025-05-28T23:51:47.545Z" },
- { url = "https://files.pythonhosted.org/packages/85/e4/07c80521879c2d15f321465ac24c70efe2381378c00bf5e56a0f4fbac8cd/protobuf-5.29.5-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:63848923da3325e1bf7e9003d680ce6e14b07e55d0473253a690c3a8b8fd6e61", size = 319942, upload-time = "2025-05-28T23:51:49.11Z" },
- { url = "https://files.pythonhosted.org/packages/e5/59/ca89678bb0352f094fc92f2b358daa40e3acc91a93aa8f922b24762bf841/protobuf-5.29.5-cp39-cp39-win32.whl", hash = "sha256:6f642dc9a61782fa72b90878af134c5afe1917c89a568cd3476d758d3c3a0736", size = 423025, upload-time = "2025-05-28T23:51:54.003Z" },
- { url = "https://files.pythonhosted.org/packages/96/8b/2c62731fe3e92ddbbeca0174f78f0f8739197cdeb7c75ceb5aad3706963b/protobuf-5.29.5-cp39-cp39-win_amd64.whl", hash = "sha256:470f3af547ef17847a28e1f47200a1cbf0ba3ff57b7de50d22776607cd2ea353", size = 434906, upload-time = "2025-05-28T23:51:55.782Z" },
- { url = "https://files.pythonhosted.org/packages/7e/cc/7e77861000a0691aeea8f4566e5d3aa716f2b1dece4a24439437e41d3d25/protobuf-5.29.5-py3-none-any.whl", hash = "sha256:6cf42630262c59b2d8de33954443d94b746c952b01434fc58a417fdbd2e84bd5", size = 172823, upload-time = "2025-05-28T23:51:58.157Z" },
-]
-
-[[package]]
-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, upload-time = "2025-02-13T21:54:07.946Z" }
-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, upload-time = "2025-02-13T21:54:12.36Z" },
- { 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, upload-time = "2025-02-13T21:54:16.07Z" },
- { 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, upload-time = "2025-02-13T21:54:18.662Z" },
- { 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, upload-time = "2025-02-13T21:54:21.811Z" },
- { 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, upload-time = "2025-02-13T21:54:24.68Z" },
- { url = "https://files.pythonhosted.org/packages/50/e6/eecf58810b9d12e6427369784efe814a1eec0f492084ce8eb8f4d89d6d61/psutil-7.0.0-cp37-abi3-win32.whl", hash = "sha256:ba3fcef7523064a6c9da440fc4d6bd07da93ac726b5733c29027d7dc95b39d99", size = 241053, upload-time = "2025-02-13T21:54:34.31Z" },
- { url = "https://files.pythonhosted.org/packages/50/1b/6921afe68c74868b4c9fa424dad3be35b095e16687989ebbb50ce4fceb7c/psutil-7.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:4cf3d4eb1aa9b348dec30105c55cd9b7d4629285735a102beb4441e38db90553", size = 244885, upload-time = "2025-02-13T21:54:37.486Z" },
-]
-
-[[package]]
-name = "psycopg"
-version = "3.2.9"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "typing-extensions", marker = "python_full_version < '3.13'" },
- { name = "tzdata", marker = "sys_platform == 'win32'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/27/4a/93a6ab570a8d1a4ad171a1f4256e205ce48d828781312c0bbaff36380ecb/psycopg-3.2.9.tar.gz", hash = "sha256:2fbb46fcd17bc81f993f28c47f1ebea38d66ae97cc2dbc3cad73b37cefbff700", size = 158122, upload-time = "2025-05-13T16:11:15.533Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/44/b0/a73c195a56eb6b92e937a5ca58521a5c3346fb233345adc80fd3e2f542e2/psycopg-3.2.9-py3-none-any.whl", hash = "sha256:01a8dadccdaac2123c916208c96e06631641c0566b22005493f09663c7a8d3b6", size = 202705, upload-time = "2025-05-13T16:06:26.584Z" },
-]
-
-[package.optional-dependencies]
-binary = [
- { name = "psycopg-binary", marker = "implementation_name != 'pypy'" },
-]
-
-[[package]]
-name = "psycopg-binary"
-version = "3.2.9"
-source = { registry = "https://pypi.org/simple" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/b6/ce/d677bc51f9b180986e5515268603519cee682eb6b5e765ae46cdb8526579/psycopg_binary-3.2.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:528239bbf55728ba0eacbd20632342867590273a9bacedac7538ebff890f1093", size = 4033081, upload-time = "2025-05-13T16:06:29.666Z" },
- { url = "https://files.pythonhosted.org/packages/de/f4/b56263eb20dc36d71d7188622872098400536928edf86895736e28546b3c/psycopg_binary-3.2.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e4978c01ca4c208c9d6376bd585e2c0771986b76ff7ea518f6d2b51faece75e8", size = 4082141, upload-time = "2025-05-13T16:06:33.81Z" },
- { url = "https://files.pythonhosted.org/packages/68/47/5316c3b0a2b1ff5f1d440a27638250569994534874a2ce88bf24f5c51c0f/psycopg_binary-3.2.9-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ed2bab85b505d13e66a914d0f8cdfa9475c16d3491cf81394e0748b77729af2", size = 4678993, upload-time = "2025-05-13T16:06:36.309Z" },
- { url = "https://files.pythonhosted.org/packages/53/24/b2c667b59f07fd7d7805c0c2074351bf2b98a336c5030d961db316512ffb/psycopg_binary-3.2.9-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:799fa1179ab8a58d1557a95df28b492874c8f4135101b55133ec9c55fc9ae9d7", size = 4500117, upload-time = "2025-05-13T16:06:38.847Z" },
- { url = "https://files.pythonhosted.org/packages/ae/91/a08f8878b0fe0b34b083c149df950bce168bc1b18b2fe849fa42bf4378d4/psycopg_binary-3.2.9-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bb37ac3955d19e4996c3534abfa4f23181333974963826db9e0f00731274b695", size = 4766985, upload-time = "2025-05-13T16:06:42.502Z" },
- { url = "https://files.pythonhosted.org/packages/10/be/3a45d5b7d8f4c4332fd42465f2170b5aef4d28a7c79e79ac7e5e1dac74d7/psycopg_binary-3.2.9-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:001e986656f7e06c273dd4104e27f4b4e0614092e544d950c7c938d822b1a894", size = 4461990, upload-time = "2025-05-13T16:06:45.971Z" },
- { url = "https://files.pythonhosted.org/packages/03/ce/20682b9a4fc270d8dc644a0b16c1978732146c6ff0abbc48fbab2f4a70aa/psycopg_binary-3.2.9-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:fa5c80d8b4cbf23f338db88a7251cef8bb4b68e0f91cf8b6ddfa93884fdbb0c1", size = 3777947, upload-time = "2025-05-13T16:06:49.134Z" },
- { url = "https://files.pythonhosted.org/packages/07/5c/f6d486e00bcd8709908ccdd436b2a190d390dfd61e318de4060bc6ee2a1e/psycopg_binary-3.2.9-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:39a127e0cf9b55bd4734a8008adf3e01d1fd1cb36339c6a9e2b2cbb6007c50ee", size = 3337502, upload-time = "2025-05-13T16:06:51.378Z" },
- { url = "https://files.pythonhosted.org/packages/0b/a1/086508e929c0123a7f532840bb0a0c8a1ebd7e06aef3ee7fa44a3589bcdf/psycopg_binary-3.2.9-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fb7599e436b586e265bea956751453ad32eb98be6a6e694252f4691c31b16edb", size = 3440809, upload-time = "2025-05-13T16:06:54.552Z" },
- { url = "https://files.pythonhosted.org/packages/40/f2/3a347a0f894355a6b173fca2202eca279b6197727b24e4896cf83f4263ee/psycopg_binary-3.2.9-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5d2c9fe14fe42b3575a0b4e09b081713e83b762c8dc38a3771dd3265f8f110e7", size = 3497231, upload-time = "2025-05-13T16:06:58.858Z" },
- { url = "https://files.pythonhosted.org/packages/18/31/0845a385eb6f4521b398793293b5f746a101e80d5c43792990442d26bc2e/psycopg_binary-3.2.9-cp310-cp310-win_amd64.whl", hash = "sha256:7e4660fad2807612bb200de7262c88773c3483e85d981324b3c647176e41fdc8", size = 2936845, upload-time = "2025-05-13T16:07:02.712Z" },
- { url = "https://files.pythonhosted.org/packages/b6/84/259ea58aca48e03c3c793b4ccfe39ed63db7b8081ef784d039330d9eed96/psycopg_binary-3.2.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2504e9fd94eabe545d20cddcc2ff0da86ee55d76329e1ab92ecfcc6c0a8156c4", size = 4040785, upload-time = "2025-05-13T16:07:07.569Z" },
- { url = "https://files.pythonhosted.org/packages/25/22/ce58ffda2b7e36e45042b4d67f1bbd4dd2ccf4cfd2649696685c61046475/psycopg_binary-3.2.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:093a0c079dd6228a7f3c3d82b906b41964eaa062a9a8c19f45ab4984bf4e872b", size = 4087601, upload-time = "2025-05-13T16:07:11.75Z" },
- { url = "https://files.pythonhosted.org/packages/c6/4f/b043e85268650c245025e80039b79663d8986f857bc3d3a72b1de67f3550/psycopg_binary-3.2.9-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:387c87b51d72442708e7a853e7e7642717e704d59571da2f3b29e748be58c78a", size = 4676524, upload-time = "2025-05-13T16:07:17.038Z" },
- { url = "https://files.pythonhosted.org/packages/da/29/7afbfbd3740ea52fda488db190ef2ef2a9ff7379b85501a2142fb9f7dd56/psycopg_binary-3.2.9-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d9ac10a2ebe93a102a326415b330fff7512f01a9401406896e78a81d75d6eddc", size = 4495671, upload-time = "2025-05-13T16:07:21.709Z" },
- { url = "https://files.pythonhosted.org/packages/ea/eb/df69112d18a938cbb74efa1573082248437fa663ba66baf2cdba8a95a2d0/psycopg_binary-3.2.9-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:72fdbda5b4c2a6a72320857ef503a6589f56d46821592d4377c8c8604810342b", size = 4768132, upload-time = "2025-05-13T16:07:25.818Z" },
- { url = "https://files.pythonhosted.org/packages/76/fe/4803b20220c04f508f50afee9169268553f46d6eed99640a08c8c1e76409/psycopg_binary-3.2.9-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f34e88940833d46108f949fdc1fcfb74d6b5ae076550cd67ab59ef47555dba95", size = 4458394, upload-time = "2025-05-13T16:07:29.148Z" },
- { url = "https://files.pythonhosted.org/packages/0f/0f/5ecc64607ef6f62b04e610b7837b1a802ca6f7cb7211339f5d166d55f1dd/psycopg_binary-3.2.9-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a3e0f89fe35cb03ff1646ab663dabf496477bab2a072315192dbaa6928862891", size = 3776879, upload-time = "2025-05-13T16:07:32.503Z" },
- { url = "https://files.pythonhosted.org/packages/c8/d8/1c3d6e99b7db67946d0eac2cd15d10a79aa7b1e3222ce4aa8e7df72027f5/psycopg_binary-3.2.9-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:6afb3e62f2a3456f2180a4eef6b03177788df7ce938036ff7f09b696d418d186", size = 3333329, upload-time = "2025-05-13T16:07:35.555Z" },
- { url = "https://files.pythonhosted.org/packages/d7/02/a4e82099816559f558ccaf2b6945097973624dc58d5d1c91eb1e54e5a8e9/psycopg_binary-3.2.9-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:cc19ed5c7afca3f6b298bfc35a6baa27adb2019670d15c32d0bb8f780f7d560d", size = 3435683, upload-time = "2025-05-13T16:07:37.863Z" },
- { url = "https://files.pythonhosted.org/packages/91/e4/f27055290d58e8818bed8a297162a096ef7f8ecdf01d98772d4b02af46c4/psycopg_binary-3.2.9-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bc75f63653ce4ec764c8f8c8b0ad9423e23021e1c34a84eb5f4ecac8538a4a4a", size = 3497124, upload-time = "2025-05-13T16:07:40.567Z" },
- { url = "https://files.pythonhosted.org/packages/67/3d/17ed07579625529534605eeaeba34f0536754a5667dbf20ea2624fc80614/psycopg_binary-3.2.9-cp311-cp311-win_amd64.whl", hash = "sha256:3db3ba3c470801e94836ad78bf11fd5fab22e71b0c77343a1ee95d693879937a", size = 2939520, upload-time = "2025-05-13T16:07:45.467Z" },
- { url = "https://files.pythonhosted.org/packages/29/6f/ec9957e37a606cd7564412e03f41f1b3c3637a5be018d0849914cb06e674/psycopg_binary-3.2.9-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:be7d650a434921a6b1ebe3fff324dbc2364393eb29d7672e638ce3e21076974e", size = 4022205, upload-time = "2025-05-13T16:07:48.195Z" },
- { url = "https://files.pythonhosted.org/packages/6b/ba/497b8bea72b20a862ac95a94386967b745a472d9ddc88bc3f32d5d5f0d43/psycopg_binary-3.2.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6a76b4722a529390683c0304501f238b365a46b1e5fb6b7249dbc0ad6fea51a0", size = 4083795, upload-time = "2025-05-13T16:07:50.917Z" },
- { url = "https://files.pythonhosted.org/packages/42/07/af9503e8e8bdad3911fd88e10e6a29240f9feaa99f57d6fac4a18b16f5a0/psycopg_binary-3.2.9-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:96a551e4683f1c307cfc3d9a05fec62c00a7264f320c9962a67a543e3ce0d8ff", size = 4655043, upload-time = "2025-05-13T16:07:54.857Z" },
- { url = "https://files.pythonhosted.org/packages/28/ed/aff8c9850df1648cc6a5cc7a381f11ee78d98a6b807edd4a5ae276ad60ad/psycopg_binary-3.2.9-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:61d0a6ceed8f08c75a395bc28cb648a81cf8dee75ba4650093ad1a24a51c8724", size = 4477972, upload-time = "2025-05-13T16:07:57.925Z" },
- { url = "https://files.pythonhosted.org/packages/5c/bd/8e9d1b77ec1a632818fe2f457c3a65af83c68710c4c162d6866947d08cc5/psycopg_binary-3.2.9-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad280bbd409bf598683dda82232f5215cfc5f2b1bf0854e409b4d0c44a113b1d", size = 4737516, upload-time = "2025-05-13T16:08:01.616Z" },
- { url = "https://files.pythonhosted.org/packages/46/ec/222238f774cd5a0881f3f3b18fb86daceae89cc410f91ef6a9fb4556f236/psycopg_binary-3.2.9-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76eddaf7fef1d0994e3d536ad48aa75034663d3a07f6f7e3e601105ae73aeff6", size = 4436160, upload-time = "2025-05-13T16:08:04.278Z" },
- { url = "https://files.pythonhosted.org/packages/37/78/af5af2a1b296eeca54ea7592cd19284739a844974c9747e516707e7b3b39/psycopg_binary-3.2.9-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:52e239cd66c4158e412318fbe028cd94b0ef21b0707f56dcb4bdc250ee58fd40", size = 3753518, upload-time = "2025-05-13T16:08:07.567Z" },
- { url = "https://files.pythonhosted.org/packages/ec/ac/8a3ed39ea069402e9e6e6a2f79d81a71879708b31cc3454283314994b1ae/psycopg_binary-3.2.9-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:08bf9d5eabba160dd4f6ad247cf12f229cc19d2458511cab2eb9647f42fa6795", size = 3313598, upload-time = "2025-05-13T16:08:09.999Z" },
- { url = "https://files.pythonhosted.org/packages/da/43/26549af068347c808fbfe5f07d2fa8cef747cfff7c695136172991d2378b/psycopg_binary-3.2.9-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1b2cf018168cad87580e67bdde38ff5e51511112f1ce6ce9a8336871f465c19a", size = 3407289, upload-time = "2025-05-13T16:08:12.66Z" },
- { url = "https://files.pythonhosted.org/packages/67/55/ea8d227c77df8e8aec880ded398316735add8fda5eb4ff5cc96fac11e964/psycopg_binary-3.2.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:14f64d1ac6942ff089fc7e926440f7a5ced062e2ed0949d7d2d680dc5c00e2d4", size = 3472493, upload-time = "2025-05-13T16:08:15.672Z" },
- { url = "https://files.pythonhosted.org/packages/3c/02/6ff2a5bc53c3cd653d281666728e29121149179c73fddefb1e437024c192/psycopg_binary-3.2.9-cp312-cp312-win_amd64.whl", hash = "sha256:7a838852e5afb6b4126f93eb409516a8c02a49b788f4df8b6469a40c2157fa21", size = 2927400, upload-time = "2025-05-13T16:08:18.652Z" },
- { url = "https://files.pythonhosted.org/packages/28/0b/f61ff4e9f23396aca674ed4d5c9a5b7323738021d5d72d36d8b865b3deaf/psycopg_binary-3.2.9-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:98bbe35b5ad24a782c7bf267596638d78aa0e87abc7837bdac5b2a2ab954179e", size = 4017127, upload-time = "2025-05-13T16:08:21.391Z" },
- { url = "https://files.pythonhosted.org/packages/bc/00/7e181fb1179fbfc24493738b61efd0453d4b70a0c4b12728e2b82db355fd/psycopg_binary-3.2.9-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:72691a1615ebb42da8b636c5ca9f2b71f266be9e172f66209a361c175b7842c5", size = 4080322, upload-time = "2025-05-13T16:08:24.049Z" },
- { url = "https://files.pythonhosted.org/packages/58/fd/94fc267c1d1392c4211e54ccb943be96ea4032e761573cf1047951887494/psycopg_binary-3.2.9-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25ab464bfba8c401f5536d5aa95f0ca1dd8257b5202eede04019b4415f491351", size = 4655097, upload-time = "2025-05-13T16:08:27.376Z" },
- { url = "https://files.pythonhosted.org/packages/41/17/31b3acf43de0b2ba83eac5878ff0dea5a608ca2a5c5dd48067999503a9de/psycopg_binary-3.2.9-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e8aeefebe752f46e3c4b769e53f1d4ad71208fe1150975ef7662c22cca80fab", size = 4482114, upload-time = "2025-05-13T16:08:30.781Z" },
- { url = "https://files.pythonhosted.org/packages/85/78/b4d75e5fd5a85e17f2beb977abbba3389d11a4536b116205846b0e1cf744/psycopg_binary-3.2.9-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b7e4e4dd177a8665c9ce86bc9caae2ab3aa9360b7ce7ec01827ea1baea9ff748", size = 4737693, upload-time = "2025-05-13T16:08:34.625Z" },
- { url = "https://files.pythonhosted.org/packages/3b/95/7325a8550e3388b00b5e54f4ced5e7346b531eb4573bf054c3dbbfdc14fe/psycopg_binary-3.2.9-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7fc2915949e5c1ea27a851f7a472a7da7d0a40d679f0a31e42f1022f3c562e87", size = 4437423, upload-time = "2025-05-13T16:08:37.444Z" },
- { url = "https://files.pythonhosted.org/packages/1a/db/cef77d08e59910d483df4ee6da8af51c03bb597f500f1fe818f0f3b925d3/psycopg_binary-3.2.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a1fa38a4687b14f517f049477178093c39c2a10fdcced21116f47c017516498f", size = 3758667, upload-time = "2025-05-13T16:08:40.116Z" },
- { url = "https://files.pythonhosted.org/packages/95/3e/252fcbffb47189aa84d723b54682e1bb6d05c8875fa50ce1ada914ae6e28/psycopg_binary-3.2.9-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:5be8292d07a3ab828dc95b5ee6b69ca0a5b2e579a577b39671f4f5b47116dfd2", size = 3320576, upload-time = "2025-05-13T16:08:43.243Z" },
- { url = "https://files.pythonhosted.org/packages/1c/cd/9b5583936515d085a1bec32b45289ceb53b80d9ce1cea0fef4c782dc41a7/psycopg_binary-3.2.9-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:778588ca9897b6c6bab39b0d3034efff4c5438f5e3bd52fda3914175498202f9", size = 3411439, upload-time = "2025-05-13T16:08:47.321Z" },
- { url = "https://files.pythonhosted.org/packages/45/6b/6f1164ea1634c87956cdb6db759e0b8c5827f989ee3cdff0f5c70e8331f2/psycopg_binary-3.2.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f0d5b3af045a187aedbd7ed5fc513bd933a97aaff78e61c3745b330792c4345b", size = 3477477, upload-time = "2025-05-13T16:08:51.166Z" },
- { url = "https://files.pythonhosted.org/packages/7b/1d/bf54cfec79377929da600c16114f0da77a5f1670f45e0c3af9fcd36879bc/psycopg_binary-3.2.9-cp313-cp313-win_amd64.whl", hash = "sha256:2290bc146a1b6a9730350f695e8b670e1d1feb8446597bed0bbe7c3c30e0abcb", size = 2928009, upload-time = "2025-05-13T16:08:53.67Z" },
- { url = "https://files.pythonhosted.org/packages/0b/4a/e095884dd016b2bde2796043c61cd383b79e5d2a820c33e2c47293707ca8/psycopg_binary-3.2.9-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:587a3f19954d687a14e0c8202628844db692dbf00bba0e6d006659bf1ca91cbe", size = 4034274, upload-time = "2025-05-13T16:09:43.738Z" },
- { url = "https://files.pythonhosted.org/packages/11/e9/ab3fad6033de260a620f6481e66092417ce31fa194dbf9ac292ab8cb9fd0/psycopg_binary-3.2.9-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:791759138380df21d356ff991265fde7fe5997b0c924a502847a9f9141e68786", size = 4083015, upload-time = "2025-05-13T16:09:54.896Z" },
- { url = "https://files.pythonhosted.org/packages/ba/c8/6cd54a349d0b62b080761eb7bda43190003ecbbf17920d57254d5c780e11/psycopg_binary-3.2.9-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:95315b8c8ddfa2fdcb7fe3ddea8a595c1364524f512160c604e3be368be9dd07", size = 4679369, upload-time = "2025-05-13T16:10:00.545Z" },
- { url = "https://files.pythonhosted.org/packages/51/34/35c65ac413c485e9340d62f14adcb34420acae44425f77aee591d49e6647/psycopg_binary-3.2.9-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18ac08475c9b971237fcc395b0a6ee4e8580bb5cf6247bc9b8461644bef5d9f4", size = 4500889, upload-time = "2025-05-13T16:10:07.593Z" },
- { url = "https://files.pythonhosted.org/packages/77/a9/f691b8037b0bcef481b09ae4283beedbf048f79b6fe9bda1445dbb14ed18/psycopg_binary-3.2.9-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac2c04b6345e215e65ca6aef5c05cc689a960b16674eaa1f90a8f86dfaee8c04", size = 4769218, upload-time = "2025-05-13T16:10:23.076Z" },
- { url = "https://files.pythonhosted.org/packages/ee/38/25afc811c1dfb664b31d66d6f5c070326a1f89f768f1b673273a3abe6912/psycopg_binary-3.2.9-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c1ab25e3134774f1e476d4bb9050cdec25f10802e63e92153906ae934578734", size = 4462834, upload-time = "2025-05-13T16:10:30.442Z" },
- { url = "https://files.pythonhosted.org/packages/df/e2/eb4a8230e13f691d6e386e22b16d4b90f454839b78ac547be3f399562ee4/psycopg_binary-3.2.9-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:4bfec4a73e8447d8fe8854886ffa78df2b1c279a7592241c2eb393d4499a17e2", size = 3779527, upload-time = "2025-05-13T16:10:42.705Z" },
- { url = "https://files.pythonhosted.org/packages/26/39/0f79c7d42f0c5711861ce9db55c65e14e7f1e52bd40304b4d6e7cd505e61/psycopg_binary-3.2.9-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:166acc57af5d2ff0c0c342aed02e69a0cd5ff216cae8820c1059a6f3b7cf5f78", size = 3337958, upload-time = "2025-05-13T16:10:47.874Z" },
- { url = "https://files.pythonhosted.org/packages/11/ce/28b1d98aed9337a721b271778d07c5ac7f85730d96f0185cc6d22684536d/psycopg_binary-3.2.9-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:413f9e46259fe26d99461af8e1a2b4795a4e27cc8ac6f7919ec19bcee8945074", size = 3440567, upload-time = "2025-05-13T16:10:57.821Z" },
- { url = "https://files.pythonhosted.org/packages/24/54/40a3a8175566f8c1268af0bacf5d7b26371697b6cefa87352c1df4b435e1/psycopg_binary-3.2.9-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:354dea21137a316b6868ee41c2ae7cce001e104760cf4eab3ec85627aed9b6cd", size = 3498637, upload-time = "2025-05-13T16:11:02.854Z" },
- { url = "https://files.pythonhosted.org/packages/63/ee/51748bc8af0ba08e7415fcbbd00b7d069c068f8c08509e8dd0dd0a066394/psycopg_binary-3.2.9-cp39-cp39-win_amd64.whl", hash = "sha256:24ddb03c1ccfe12d000d950c9aba93a7297993c4e3905d9f2c9795bb0764d523", size = 2938614, upload-time = "2025-05-13T16:11:13.299Z" },
-]
-
-[[package]]
-name = "psycopg-pool"
-version = "3.2.6"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "typing-extensions" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/cf/13/1e7850bb2c69a63267c3dbf37387d3f71a00fd0e2fa55c5db14d64ba1af4/psycopg_pool-3.2.6.tar.gz", hash = "sha256:0f92a7817719517212fbfe2fd58b8c35c1850cdd2a80d36b581ba2085d9148e5", size = 29770, upload-time = "2025-02-26T12:03:47.129Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/47/fd/4feb52a55c1a4bd748f2acaed1903ab54a723c47f6d0242780f4d97104d4/psycopg_pool-3.2.6-py3-none-any.whl", hash = "sha256:5887318a9f6af906d041a0b1dc1c60f8f0dda8340c2572b74e10907b51ed5da7", size = 38252, upload-time = "2025-02-26T12:03:45.073Z" },
-]
-
-[[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, upload-time = "2024-10-16T11:24:58.126Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/7a/81/331257dbf2801cdb82105306042f7a1637cc752f65f2bb688188e0de5f0b/psycopg2_binary-2.9.10-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:0ea8e3d0ae83564f2fc554955d327fa081d065c8ca5cc6d2abb643e2c9c1200f", size = 3043397, upload-time = "2024-10-16T11:18:58.647Z" },
- { url = "https://files.pythonhosted.org/packages/e7/9a/7f4f2f031010bbfe6a02b4a15c01e12eb6b9b7b358ab33229f28baadbfc1/psycopg2_binary-2.9.10-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:3e9c76f0ac6f92ecfc79516a8034a544926430f7b080ec5a0537bca389ee0906", size = 3274806, upload-time = "2024-10-16T11:19:03.935Z" },
- { url = "https://files.pythonhosted.org/packages/e5/57/8ddd4b374fa811a0b0a0f49b6abad1cde9cb34df73ea3348cc283fcd70b4/psycopg2_binary-2.9.10-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ad26b467a405c798aaa1458ba09d7e2b6e5f96b1ce0ac15d82fd9f95dc38a92", size = 2851361, upload-time = "2024-10-16T11:19:07.277Z" },
- { url = "https://files.pythonhosted.org/packages/f9/66/d1e52c20d283f1f3a8e7e5c1e06851d432f123ef57b13043b4f9b21ffa1f/psycopg2_binary-2.9.10-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:270934a475a0e4b6925b5f804e3809dd5f90f8613621d062848dd82f9cd62007", size = 3080836, upload-time = "2024-10-16T11:19:11.033Z" },
- { url = "https://files.pythonhosted.org/packages/a0/cb/592d44a9546aba78f8a1249021fe7c59d3afb8a0ba51434d6610cc3462b6/psycopg2_binary-2.9.10-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:48b338f08d93e7be4ab2b5f1dbe69dc5e9ef07170fe1f86514422076d9c010d0", size = 3264552, upload-time = "2024-10-16T11:19:14.606Z" },
- { url = "https://files.pythonhosted.org/packages/64/33/c8548560b94b7617f203d7236d6cdf36fe1a5a3645600ada6efd79da946f/psycopg2_binary-2.9.10-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f4152f8f76d2023aac16285576a9ecd2b11a9895373a1f10fd9db54b3ff06b4", size = 3019789, upload-time = "2024-10-16T11:19:18.889Z" },
- { url = "https://files.pythonhosted.org/packages/b0/0e/c2da0db5bea88a3be52307f88b75eec72c4de62814cbe9ee600c29c06334/psycopg2_binary-2.9.10-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:32581b3020c72d7a421009ee1c6bf4a131ef5f0a968fab2e2de0c9d2bb4577f1", size = 2871776, upload-time = "2024-10-16T11:19:23.023Z" },
- { url = "https://files.pythonhosted.org/packages/15/d7/774afa1eadb787ddf41aab52d4c62785563e29949613c958955031408ae6/psycopg2_binary-2.9.10-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:2ce3e21dc3437b1d960521eca599d57408a695a0d3c26797ea0f72e834c7ffe5", size = 2820959, upload-time = "2024-10-16T11:19:26.906Z" },
- { url = "https://files.pythonhosted.org/packages/5e/ed/440dc3f5991a8c6172a1cde44850ead0e483a375277a1aef7cfcec00af07/psycopg2_binary-2.9.10-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:e984839e75e0b60cfe75e351db53d6db750b00de45644c5d1f7ee5d1f34a1ce5", size = 2919329, upload-time = "2024-10-16T11:19:30.027Z" },
- { url = "https://files.pythonhosted.org/packages/03/be/2cc8f4282898306732d2ae7b7378ae14e8df3c1231b53579efa056aae887/psycopg2_binary-2.9.10-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3c4745a90b78e51d9ba06e2088a2fe0c693ae19cc8cb051ccda44e8df8a6eb53", size = 2957659, upload-time = "2024-10-16T11:19:32.864Z" },
- { url = "https://files.pythonhosted.org/packages/d0/12/fb8e4f485d98c570e00dad5800e9a2349cfe0f71a767c856857160d343a5/psycopg2_binary-2.9.10-cp310-cp310-win32.whl", hash = "sha256:e5720a5d25e3b99cd0dc5c8a440570469ff82659bb09431c1439b92caf184d3b", size = 1024605, upload-time = "2024-10-16T11:19:35.462Z" },
- { url = "https://files.pythonhosted.org/packages/22/4f/217cd2471ecf45d82905dd09085e049af8de6cfdc008b6663c3226dc1c98/psycopg2_binary-2.9.10-cp310-cp310-win_amd64.whl", hash = "sha256:3c18f74eb4386bf35e92ab2354a12c17e5eb4d9798e4c0ad3a00783eae7cd9f1", size = 1163817, upload-time = "2024-10-16T11:19:37.384Z" },
- { url = "https://files.pythonhosted.org/packages/9c/8f/9feb01291d0d7a0a4c6a6bab24094135c2b59c6a81943752f632c75896d6/psycopg2_binary-2.9.10-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:04392983d0bb89a8717772a193cfaac58871321e3ec69514e1c4e0d4957b5aff", size = 3043397, upload-time = "2024-10-16T11:19:40.033Z" },
- { url = "https://files.pythonhosted.org/packages/15/30/346e4683532011561cd9c8dfeac6a8153dd96452fee0b12666058ab7893c/psycopg2_binary-2.9.10-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:1a6784f0ce3fec4edc64e985865c17778514325074adf5ad8f80636cd029ef7c", size = 3274806, upload-time = "2024-10-16T11:19:43.5Z" },
- { url = "https://files.pythonhosted.org/packages/66/6e/4efebe76f76aee7ec99166b6c023ff8abdc4e183f7b70913d7c047701b79/psycopg2_binary-2.9.10-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5f86c56eeb91dc3135b3fd8a95dc7ae14c538a2f3ad77a19645cf55bab1799c", size = 2851370, upload-time = "2024-10-16T11:19:46.986Z" },
- { url = "https://files.pythonhosted.org/packages/7f/fd/ff83313f86b50f7ca089b161b8e0a22bb3c319974096093cd50680433fdb/psycopg2_binary-2.9.10-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2b3d2491d4d78b6b14f76881905c7a8a8abcf974aad4a8a0b065273a0ed7a2cb", size = 3080780, upload-time = "2024-10-16T11:19:50.242Z" },
- { url = "https://files.pythonhosted.org/packages/e6/c4/bfadd202dcda8333a7ccafdc51c541dbdfce7c2c7cda89fa2374455d795f/psycopg2_binary-2.9.10-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2286791ececda3a723d1910441c793be44625d86d1a4e79942751197f4d30341", size = 3264583, upload-time = "2024-10-16T11:19:54.424Z" },
- { url = "https://files.pythonhosted.org/packages/5d/f1/09f45ac25e704ac954862581f9f9ae21303cc5ded3d0b775532b407f0e90/psycopg2_binary-2.9.10-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:512d29bb12608891e349af6a0cccedce51677725a921c07dba6342beaf576f9a", size = 3019831, upload-time = "2024-10-16T11:19:57.762Z" },
- { url = "https://files.pythonhosted.org/packages/9e/2e/9beaea078095cc558f215e38f647c7114987d9febfc25cb2beed7c3582a5/psycopg2_binary-2.9.10-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5a507320c58903967ef7384355a4da7ff3f28132d679aeb23572753cbf2ec10b", size = 2871822, upload-time = "2024-10-16T11:20:04.693Z" },
- { url = "https://files.pythonhosted.org/packages/01/9e/ef93c5d93f3dc9fc92786ffab39e323b9aed066ba59fdc34cf85e2722271/psycopg2_binary-2.9.10-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:6d4fa1079cab9018f4d0bd2db307beaa612b0d13ba73b5c6304b9fe2fb441ff7", size = 2820975, upload-time = "2024-10-16T11:20:11.401Z" },
- { url = "https://files.pythonhosted.org/packages/a5/f0/049e9631e3268fe4c5a387f6fc27e267ebe199acf1bc1bc9cbde4bd6916c/psycopg2_binary-2.9.10-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:851485a42dbb0bdc1edcdabdb8557c09c9655dfa2ca0460ff210522e073e319e", size = 2919320, upload-time = "2024-10-16T11:20:17.959Z" },
- { url = "https://files.pythonhosted.org/packages/dc/9a/bcb8773b88e45fb5a5ea8339e2104d82c863a3b8558fbb2aadfe66df86b3/psycopg2_binary-2.9.10-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:35958ec9e46432d9076286dda67942ed6d968b9c3a6a2fd62b48939d1d78bf68", size = 2957617, upload-time = "2024-10-16T11:20:24.711Z" },
- { url = "https://files.pythonhosted.org/packages/e2/6b/144336a9bf08a67d217b3af3246abb1d027095dab726f0687f01f43e8c03/psycopg2_binary-2.9.10-cp311-cp311-win32.whl", hash = "sha256:ecced182e935529727401b24d76634a357c71c9275b356efafd8a2a91ec07392", size = 1024618, upload-time = "2024-10-16T11:20:27.718Z" },
- { url = "https://files.pythonhosted.org/packages/61/69/3b3d7bd583c6d3cbe5100802efa5beacaacc86e37b653fc708bf3d6853b8/psycopg2_binary-2.9.10-cp311-cp311-win_amd64.whl", hash = "sha256:ee0e8c683a7ff25d23b55b11161c2663d4b099770f6085ff0a20d4505778d6b4", size = 1163816, upload-time = "2024-10-16T11:20:30.777Z" },
- { 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, upload-time = "2024-10-16T11:20:35.234Z" },
- { 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, upload-time = "2024-10-16T11:20:38.742Z" },
- { 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, upload-time = "2024-10-16T11:20:42.145Z" },
- { 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, upload-time = "2024-10-16T11:20:46.185Z" },
- { 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, upload-time = "2024-10-16T11:20:50.879Z" },
- { 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, upload-time = "2024-10-16T11:20:56.819Z" },
- { 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, upload-time = "2024-10-16T11:21:02.411Z" },
- { 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, upload-time = "2024-10-16T11:21:09.01Z" },
- { 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, upload-time = "2024-10-16T11:21:16.339Z" },
- { 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, upload-time = "2024-10-16T11:21:25.584Z" },
- { url = "https://files.pythonhosted.org/packages/d3/bd/213e59854fafe87ba47814bf413ace0dcee33a89c8c8c814faca6bc7cf3c/psycopg2_binary-2.9.10-cp312-cp312-win32.whl", hash = "sha256:ec8a77f521a17506a24a5f626cb2aee7850f9b69a0afe704586f63a464f3cd64", size = 1025191, upload-time = "2024-10-16T11:21:29.912Z" },
- { url = "https://files.pythonhosted.org/packages/92/29/06261ea000e2dc1e22907dbbc483a1093665509ea586b29b8986a0e56733/psycopg2_binary-2.9.10-cp312-cp312-win_amd64.whl", hash = "sha256:18c5ee682b9c6dd3696dad6e54cc7ff3a1a9020df6a5c0f861ef8bfd338c3ca0", size = 1164031, upload-time = "2024-10-16T11:21:34.211Z" },
- { 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, upload-time = "2024-10-16T11:21:42.841Z" },
- { 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, upload-time = "2024-10-16T11:21:51.989Z" },
- { 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, upload-time = "2024-10-16T11:21:57.584Z" },
- { 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, upload-time = "2024-10-16T11:22:02.005Z" },
- { 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, upload-time = "2024-10-16T11:22:06.412Z" },
- { 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, upload-time = "2024-10-16T11:22:11.583Z" },
- { 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, upload-time = "2024-10-16T11:22:16.406Z" },
- { 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, upload-time = "2024-10-16T11:22:21.366Z" },
- { 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, upload-time = "2024-10-16T11:22:25.684Z" },
- { 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, upload-time = "2024-10-16T11:22:30.562Z" },
- { url = "https://files.pythonhosted.org/packages/08/50/d13ea0a054189ae1bc21af1d85b6f8bb9bbc5572991055d70ad9006fe2d6/psycopg2_binary-2.9.10-cp313-cp313-win_amd64.whl", hash = "sha256:27422aa5f11fbcd9b18da48373eb67081243662f9b46e6fd07c3eb46e4535142", size = 2569224, upload-time = "2025-01-04T20:09:19.234Z" },
- { url = "https://files.pythonhosted.org/packages/a2/bc/e77648009b6e61af327c607543f65fdf25bcfb4100f5a6f3bdb62ddac03c/psycopg2_binary-2.9.10-cp39-cp39-macosx_12_0_x86_64.whl", hash = "sha256:7a813c8bdbaaaab1f078014b9b0b13f5de757e2b5d9be6403639b298a04d218b", size = 3043437, upload-time = "2024-10-16T11:23:42.946Z" },
- { url = "https://files.pythonhosted.org/packages/e0/e8/5a12211a1f5b959f3e3ccd342eace60c1f26422f53e06d687821dc268780/psycopg2_binary-2.9.10-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d00924255d7fc916ef66e4bf22f354a940c67179ad3fd7067d7a0a9c84d2fbfc", size = 2851340, upload-time = "2024-10-16T11:23:50.038Z" },
- { url = "https://files.pythonhosted.org/packages/47/ed/5932b0458a7fc61237b653df050513c8d18a6f4083cc7f90dcef967f7bce/psycopg2_binary-2.9.10-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7559bce4b505762d737172556a4e6ea8a9998ecac1e39b5233465093e8cee697", size = 3080905, upload-time = "2024-10-16T11:23:57.932Z" },
- { url = "https://files.pythonhosted.org/packages/71/df/8047d85c3d23864aca4613c3be1ea0fe61dbe4e050a89ac189f9dce4403e/psycopg2_binary-2.9.10-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e8b58f0a96e7a1e341fc894f62c1177a7c83febebb5ff9123b579418fdc8a481", size = 3264640, upload-time = "2024-10-16T11:24:06.122Z" },
- { url = "https://files.pythonhosted.org/packages/f3/de/6157e4ef242920e8f2749f7708d5cc8815414bdd4a27a91996e7cd5c80df/psycopg2_binary-2.9.10-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b269105e59ac96aba877c1707c600ae55711d9dcd3fc4b5012e4af68e30c648", size = 3019812, upload-time = "2024-10-16T11:24:17.025Z" },
- { url = "https://files.pythonhosted.org/packages/25/f9/0fc49efd2d4d6db3a8d0a3f5749b33a0d3fdd872cad49fbf5bfce1c50027/psycopg2_binary-2.9.10-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:79625966e176dc97ddabc142351e0409e28acf4660b88d1cf6adb876d20c490d", size = 2871933, upload-time = "2024-10-16T11:24:24.858Z" },
- { url = "https://files.pythonhosted.org/packages/57/bc/2ed1bd182219065692ed458d218d311b0b220b20662d25d913bc4e8d3549/psycopg2_binary-2.9.10-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:8aabf1c1a04584c168984ac678a668094d831f152859d06e055288fa515e4d30", size = 2820990, upload-time = "2024-10-16T11:24:29.571Z" },
- { url = "https://files.pythonhosted.org/packages/71/2a/43f77a9b8ee0b10e2de784d97ddc099d9fe0d9eec462a006e4d2cc74756d/psycopg2_binary-2.9.10-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:19721ac03892001ee8fdd11507e6a2e01f4e37014def96379411ca99d78aeb2c", size = 2919352, upload-time = "2024-10-16T11:24:36.906Z" },
- { url = "https://files.pythonhosted.org/packages/57/86/d2943df70469e6afab3b5b8e1367fccc61891f46de436b24ddee6f2c8404/psycopg2_binary-2.9.10-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7f5d859928e635fa3ce3477704acee0f667b3a3d3e4bb109f2b18d4005f38287", size = 2957614, upload-time = "2024-10-16T11:24:44.423Z" },
- { url = "https://files.pythonhosted.org/packages/85/21/195d69371330983aa16139e60ba855d0a18164c9295f3a3696be41bbcd54/psycopg2_binary-2.9.10-cp39-cp39-win32.whl", hash = "sha256:3216ccf953b3f267691c90c6fe742e45d890d8272326b4a8b20850a03d05b7b8", size = 1025341, upload-time = "2024-10-16T11:24:48.056Z" },
- { url = "https://files.pythonhosted.org/packages/ad/53/73196ebc19d6fbfc22427b982fbc98698b7b9c361e5e7707e3a3247cf06d/psycopg2_binary-2.9.10-cp39-cp39-win_amd64.whl", hash = "sha256:30e34c4e97964805f715206c7b789d54a78b70f3ff19fbe590104b71c45600e5", size = 1163958, upload-time = "2024-10-16T11:24:51.882Z" },
-]
-
-[[package]]
-name = "ptyprocess"
-version = "0.7.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/20/e5/16ff212c1e452235a90aeb09066144d0c5a6a8c0834397e03f5224495c4e/ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220", size = 70762, upload-time = "2020-12-28T15:15:30.155Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35", size = 13993, upload-time = "2020-12-28T15:15:28.35Z" },
-]
-
-[[package]]
-name = "pure-eval"
-version = "0.2.3"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/cd/05/0a34433a064256a578f1783a10da6df098ceaa4a57bbeaa96a6c0352786b/pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42", size = 19752, upload-time = "2024-07-21T12:58:21.801Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0", size = 11842, upload-time = "2024-07-21T12:58:20.04Z" },
-]
-
-[[package]]
-name = "pyarrow"
-version = "21.0.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/ef/c2/ea068b8f00905c06329a3dfcd40d0fcc2b7d0f2e355bdb25b65e0a0e4cd4/pyarrow-21.0.0.tar.gz", hash = "sha256:5051f2dccf0e283ff56335760cbc8622cf52264d67e359d5569541ac11b6d5bc", size = 1133487, upload-time = "2025-07-18T00:57:31.761Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/17/d9/110de31880016e2afc52d8580b397dbe47615defbf09ca8cf55f56c62165/pyarrow-21.0.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:e563271e2c5ff4d4a4cbeb2c83d5cf0d4938b891518e676025f7268c6fe5fe26", size = 31196837, upload-time = "2025-07-18T00:54:34.755Z" },
- { url = "https://files.pythonhosted.org/packages/df/5f/c1c1997613abf24fceb087e79432d24c19bc6f7259cab57c2c8e5e545fab/pyarrow-21.0.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:fee33b0ca46f4c85443d6c450357101e47d53e6c3f008d658c27a2d020d44c79", size = 32659470, upload-time = "2025-07-18T00:54:38.329Z" },
- { url = "https://files.pythonhosted.org/packages/3e/ed/b1589a777816ee33ba123ba1e4f8f02243a844fed0deec97bde9fb21a5cf/pyarrow-21.0.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:7be45519b830f7c24b21d630a31d48bcebfd5d4d7f9d3bdb49da9cdf6d764edb", size = 41055619, upload-time = "2025-07-18T00:54:42.172Z" },
- { url = "https://files.pythonhosted.org/packages/44/28/b6672962639e85dc0ac36f71ab3a8f5f38e01b51343d7aa372a6b56fa3f3/pyarrow-21.0.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:26bfd95f6bff443ceae63c65dc7e048670b7e98bc892210acba7e4995d3d4b51", size = 42733488, upload-time = "2025-07-18T00:54:47.132Z" },
- { url = "https://files.pythonhosted.org/packages/f8/cc/de02c3614874b9089c94eac093f90ca5dfa6d5afe45de3ba847fd950fdf1/pyarrow-21.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:bd04ec08f7f8bd113c55868bd3fc442a9db67c27af098c5f814a3091e71cc61a", size = 43329159, upload-time = "2025-07-18T00:54:51.686Z" },
- { url = "https://files.pythonhosted.org/packages/a6/3e/99473332ac40278f196e105ce30b79ab8affab12f6194802f2593d6b0be2/pyarrow-21.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:9b0b14b49ac10654332a805aedfc0147fb3469cbf8ea951b3d040dab12372594", size = 45050567, upload-time = "2025-07-18T00:54:56.679Z" },
- { url = "https://files.pythonhosted.org/packages/7b/f5/c372ef60593d713e8bfbb7e0c743501605f0ad00719146dc075faf11172b/pyarrow-21.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:9d9f8bcb4c3be7738add259738abdeddc363de1b80e3310e04067aa1ca596634", size = 26217959, upload-time = "2025-07-18T00:55:00.482Z" },
- { url = "https://files.pythonhosted.org/packages/94/dc/80564a3071a57c20b7c32575e4a0120e8a330ef487c319b122942d665960/pyarrow-21.0.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:c077f48aab61738c237802836fc3844f85409a46015635198761b0d6a688f87b", size = 31243234, upload-time = "2025-07-18T00:55:03.812Z" },
- { url = "https://files.pythonhosted.org/packages/ea/cc/3b51cb2db26fe535d14f74cab4c79b191ed9a8cd4cbba45e2379b5ca2746/pyarrow-21.0.0-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:689f448066781856237eca8d1975b98cace19b8dd2ab6145bf49475478bcaa10", size = 32714370, upload-time = "2025-07-18T00:55:07.495Z" },
- { url = "https://files.pythonhosted.org/packages/24/11/a4431f36d5ad7d83b87146f515c063e4d07ef0b7240876ddb885e6b44f2e/pyarrow-21.0.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:479ee41399fcddc46159a551705b89c05f11e8b8cb8e968f7fec64f62d91985e", size = 41135424, upload-time = "2025-07-18T00:55:11.461Z" },
- { url = "https://files.pythonhosted.org/packages/74/dc/035d54638fc5d2971cbf1e987ccd45f1091c83bcf747281cf6cc25e72c88/pyarrow-21.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:40ebfcb54a4f11bcde86bc586cbd0272bac0d516cfa539c799c2453768477569", size = 42823810, upload-time = "2025-07-18T00:55:16.301Z" },
- { url = "https://files.pythonhosted.org/packages/2e/3b/89fced102448a9e3e0d4dded1f37fa3ce4700f02cdb8665457fcc8015f5b/pyarrow-21.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8d58d8497814274d3d20214fbb24abcad2f7e351474357d552a8d53bce70c70e", size = 43391538, upload-time = "2025-07-18T00:55:23.82Z" },
- { url = "https://files.pythonhosted.org/packages/fb/bb/ea7f1bd08978d39debd3b23611c293f64a642557e8141c80635d501e6d53/pyarrow-21.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:585e7224f21124dd57836b1530ac8f2df2afc43c861d7bf3d58a4870c42ae36c", size = 45120056, upload-time = "2025-07-18T00:55:28.231Z" },
- { url = "https://files.pythonhosted.org/packages/6e/0b/77ea0600009842b30ceebc3337639a7380cd946061b620ac1a2f3cb541e2/pyarrow-21.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:555ca6935b2cbca2c0e932bedd853e9bc523098c39636de9ad4693b5b1df86d6", size = 26220568, upload-time = "2025-07-18T00:55:32.122Z" },
- { url = "https://files.pythonhosted.org/packages/ca/d4/d4f817b21aacc30195cf6a46ba041dd1be827efa4a623cc8bf39a1c2a0c0/pyarrow-21.0.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:3a302f0e0963db37e0a24a70c56cf91a4faa0bca51c23812279ca2e23481fccd", size = 31160305, upload-time = "2025-07-18T00:55:35.373Z" },
- { url = "https://files.pythonhosted.org/packages/a2/9c/dcd38ce6e4b4d9a19e1d36914cb8e2b1da4e6003dd075474c4cfcdfe0601/pyarrow-21.0.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:b6b27cf01e243871390474a211a7922bfbe3bda21e39bc9160daf0da3fe48876", size = 32684264, upload-time = "2025-07-18T00:55:39.303Z" },
- { url = "https://files.pythonhosted.org/packages/4f/74/2a2d9f8d7a59b639523454bec12dba35ae3d0a07d8ab529dc0809f74b23c/pyarrow-21.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:e72a8ec6b868e258a2cd2672d91f2860ad532d590ce94cdf7d5e7ec674ccf03d", size = 41108099, upload-time = "2025-07-18T00:55:42.889Z" },
- { url = "https://files.pythonhosted.org/packages/ad/90/2660332eeb31303c13b653ea566a9918484b6e4d6b9d2d46879a33ab0622/pyarrow-21.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:b7ae0bbdc8c6674259b25bef5d2a1d6af5d39d7200c819cf99e07f7dfef1c51e", size = 42829529, upload-time = "2025-07-18T00:55:47.069Z" },
- { url = "https://files.pythonhosted.org/packages/33/27/1a93a25c92717f6aa0fca06eb4700860577d016cd3ae51aad0e0488ac899/pyarrow-21.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:58c30a1729f82d201627c173d91bd431db88ea74dcaa3885855bc6203e433b82", size = 43367883, upload-time = "2025-07-18T00:55:53.069Z" },
- { url = "https://files.pythonhosted.org/packages/05/d9/4d09d919f35d599bc05c6950095e358c3e15148ead26292dfca1fb659b0c/pyarrow-21.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:072116f65604b822a7f22945a7a6e581cfa28e3454fdcc6939d4ff6090126623", size = 45133802, upload-time = "2025-07-18T00:55:57.714Z" },
- { url = "https://files.pythonhosted.org/packages/71/30/f3795b6e192c3ab881325ffe172e526499eb3780e306a15103a2764916a2/pyarrow-21.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:cf56ec8b0a5c8c9d7021d6fd754e688104f9ebebf1bf4449613c9531f5346a18", size = 26203175, upload-time = "2025-07-18T00:56:01.364Z" },
- { url = "https://files.pythonhosted.org/packages/16/ca/c7eaa8e62db8fb37ce942b1ea0c6d7abfe3786ca193957afa25e71b81b66/pyarrow-21.0.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:e99310a4ebd4479bcd1964dff9e14af33746300cb014aa4a3781738ac63baf4a", size = 31154306, upload-time = "2025-07-18T00:56:04.42Z" },
- { url = "https://files.pythonhosted.org/packages/ce/e8/e87d9e3b2489302b3a1aea709aaca4b781c5252fcb812a17ab6275a9a484/pyarrow-21.0.0-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:d2fe8e7f3ce329a71b7ddd7498b3cfac0eeb200c2789bd840234f0dc271a8efe", size = 32680622, upload-time = "2025-07-18T00:56:07.505Z" },
- { url = "https://files.pythonhosted.org/packages/84/52/79095d73a742aa0aba370c7942b1b655f598069489ab387fe47261a849e1/pyarrow-21.0.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:f522e5709379d72fb3da7785aa489ff0bb87448a9dc5a75f45763a795a089ebd", size = 41104094, upload-time = "2025-07-18T00:56:10.994Z" },
- { url = "https://files.pythonhosted.org/packages/89/4b/7782438b551dbb0468892a276b8c789b8bbdb25ea5c5eb27faadd753e037/pyarrow-21.0.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:69cbbdf0631396e9925e048cfa5bce4e8c3d3b41562bbd70c685a8eb53a91e61", size = 42825576, upload-time = "2025-07-18T00:56:15.569Z" },
- { url = "https://files.pythonhosted.org/packages/b3/62/0f29de6e0a1e33518dec92c65be0351d32d7ca351e51ec5f4f837a9aab91/pyarrow-21.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:731c7022587006b755d0bdb27626a1a3bb004bb56b11fb30d98b6c1b4718579d", size = 43368342, upload-time = "2025-07-18T00:56:19.531Z" },
- { url = "https://files.pythonhosted.org/packages/90/c7/0fa1f3f29cf75f339768cc698c8ad4ddd2481c1742e9741459911c9ac477/pyarrow-21.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:dc56bc708f2d8ac71bd1dcb927e458c93cec10b98eb4120206a4091db7b67b99", size = 45131218, upload-time = "2025-07-18T00:56:23.347Z" },
- { url = "https://files.pythonhosted.org/packages/01/63/581f2076465e67b23bc5a37d4a2abff8362d389d29d8105832e82c9c811c/pyarrow-21.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:186aa00bca62139f75b7de8420f745f2af12941595bbbfa7ed3870ff63e25636", size = 26087551, upload-time = "2025-07-18T00:56:26.758Z" },
- { url = "https://files.pythonhosted.org/packages/c9/ab/357d0d9648bb8241ee7348e564f2479d206ebe6e1c47ac5027c2e31ecd39/pyarrow-21.0.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:a7a102574faa3f421141a64c10216e078df467ab9576684d5cd696952546e2da", size = 31290064, upload-time = "2025-07-18T00:56:30.214Z" },
- { url = "https://files.pythonhosted.org/packages/3f/8a/5685d62a990e4cac2043fc76b4661bf38d06efed55cf45a334b455bd2759/pyarrow-21.0.0-cp313-cp313t-macosx_12_0_x86_64.whl", hash = "sha256:1e005378c4a2c6db3ada3ad4c217b381f6c886f0a80d6a316fe586b90f77efd7", size = 32727837, upload-time = "2025-07-18T00:56:33.935Z" },
- { url = "https://files.pythonhosted.org/packages/fc/de/c0828ee09525c2bafefd3e736a248ebe764d07d0fd762d4f0929dbc516c9/pyarrow-21.0.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:65f8e85f79031449ec8706b74504a316805217b35b6099155dd7e227eef0d4b6", size = 41014158, upload-time = "2025-07-18T00:56:37.528Z" },
- { url = "https://files.pythonhosted.org/packages/6e/26/a2865c420c50b7a3748320b614f3484bfcde8347b2639b2b903b21ce6a72/pyarrow-21.0.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:3a81486adc665c7eb1a2bde0224cfca6ceaba344a82a971ef059678417880eb8", size = 42667885, upload-time = "2025-07-18T00:56:41.483Z" },
- { url = "https://files.pythonhosted.org/packages/0a/f9/4ee798dc902533159250fb4321267730bc0a107d8c6889e07c3add4fe3a5/pyarrow-21.0.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:fc0d2f88b81dcf3ccf9a6ae17f89183762c8a94a5bdcfa09e05cfe413acf0503", size = 43276625, upload-time = "2025-07-18T00:56:48.002Z" },
- { url = "https://files.pythonhosted.org/packages/5a/da/e02544d6997037a4b0d22d8e5f66bc9315c3671371a8b18c79ade1cefe14/pyarrow-21.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6299449adf89df38537837487a4f8d3bd91ec94354fdd2a7d30bc11c48ef6e79", size = 44951890, upload-time = "2025-07-18T00:56:52.568Z" },
- { url = "https://files.pythonhosted.org/packages/e5/4e/519c1bc1876625fe6b71e9a28287c43ec2f20f73c658b9ae1d485c0c206e/pyarrow-21.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:222c39e2c70113543982c6b34f3077962b44fca38c0bd9e68bb6781534425c10", size = 26371006, upload-time = "2025-07-18T00:56:56.379Z" },
- { url = "https://files.pythonhosted.org/packages/3e/cc/ce4939f4b316457a083dc5718b3982801e8c33f921b3c98e7a93b7c7491f/pyarrow-21.0.0-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:a7f6524e3747e35f80744537c78e7302cd41deee8baa668d56d55f77d9c464b3", size = 31211248, upload-time = "2025-07-18T00:56:59.7Z" },
- { url = "https://files.pythonhosted.org/packages/1f/c2/7a860931420d73985e2f340f06516b21740c15b28d24a0e99a900bb27d2b/pyarrow-21.0.0-cp39-cp39-macosx_12_0_x86_64.whl", hash = "sha256:203003786c9fd253ebcafa44b03c06983c9c8d06c3145e37f1b76a1f317aeae1", size = 32676896, upload-time = "2025-07-18T00:57:03.884Z" },
- { url = "https://files.pythonhosted.org/packages/68/a8/197f989b9a75e59b4ca0db6a13c56f19a0ad8a298c68da9cc28145e0bb97/pyarrow-21.0.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:3b4d97e297741796fead24867a8dabf86c87e4584ccc03167e4a811f50fdf74d", size = 41067862, upload-time = "2025-07-18T00:57:07.587Z" },
- { url = "https://files.pythonhosted.org/packages/fa/82/6ecfa89487b35aa21accb014b64e0a6b814cc860d5e3170287bf5135c7d8/pyarrow-21.0.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:898afce396b80fdda05e3086b4256f8677c671f7b1d27a6976fa011d3fd0a86e", size = 42747508, upload-time = "2025-07-18T00:57:13.917Z" },
- { url = "https://files.pythonhosted.org/packages/3b/b7/ba252f399bbf3addc731e8643c05532cf32e74cebb5e32f8f7409bc243cf/pyarrow-21.0.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:067c66ca29aaedae08218569a114e413b26e742171f526e828e1064fcdec13f4", size = 43345293, upload-time = "2025-07-18T00:57:19.828Z" },
- { url = "https://files.pythonhosted.org/packages/ff/0a/a20819795bd702b9486f536a8eeb70a6aa64046fce32071c19ec8230dbaa/pyarrow-21.0.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:0c4e75d13eb76295a49e0ea056eb18dbd87d81450bfeb8afa19a7e5a75ae2ad7", size = 45060670, upload-time = "2025-07-18T00:57:24.477Z" },
- { url = "https://files.pythonhosted.org/packages/10/15/6b30e77872012bbfe8265d42a01d5b3c17ef0ac0f2fae531ad91b6a6c02e/pyarrow-21.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:cdc4c17afda4dab2a9c0b79148a43a7f4e1094916b3e18d8975bfd6d6d52241f", size = 26227521, upload-time = "2025-07-18T00:57:29.119Z" },
-]
-
-[[package]]
-name = "pyasn1"
-version = "0.6.1"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/ba/e9/01f1a64245b89f039897cb0130016d79f77d52669aae6ee7b159a6c4c018/pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034", size = 145322, upload-time = "2024-09-10T22:41:42.55Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/c8/f1/d6a797abb14f6283c0ddff96bbdd46937f64122b8c925cab503dd37f8214/pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629", size = 83135, upload-time = "2024-09-11T16:00:36.122Z" },
-]
-
-[[package]]
-name = "pyasn1-modules"
-version = "0.4.2"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "pyasn1" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/e9/e6/78ebbb10a8c8e4b61a59249394a4a594c1a7af95593dc933a349c8d00964/pyasn1_modules-0.4.2.tar.gz", hash = "sha256:677091de870a80aae844b1ca6134f54652fa2c8c5a52aa396440ac3106e941e6", size = 307892, upload-time = "2025-03-28T02:41:22.17Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/47/8d/d529b5d697919ba8c11ad626e835d4039be708a35b0d22de83a269a6682c/pyasn1_modules-0.4.2-py3-none-any.whl", hash = "sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a", size = 181259, upload-time = "2025-03-28T02:41:19.028Z" },
-]
-
-[[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, upload-time = "2024-03-30T13:22:22.564Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/13/a3/a812df4e2dd5696d1f351d58b8fe16a405b234ad2886a0dab9183fb78109/pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc", size = 117552, upload-time = "2024-03-30T13:22:20.476Z" },
-]
-
-[[package]]
-name = "pydantic"
-version = "2.11.7"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "annotated-types" },
- { name = "pydantic-core" },
- { name = "typing-extensions" },
- { name = "typing-inspection" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/00/dd/4325abf92c39ba8623b5af936ddb36ffcfe0beae70405d456ab1fb2f5b8c/pydantic-2.11.7.tar.gz", hash = "sha256:d989c3c6cb79469287b1569f7447a17848c998458d49ebe294e975b9baf0f0db", size = 788350, upload-time = "2025-06-14T08:33:17.137Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/6a/c0/ec2b1c8712ca690e5d61979dee872603e92b8a32f94cc1b72d53beab008a/pydantic-2.11.7-py3-none-any.whl", hash = "sha256:dde5df002701f6de26248661f6835bbe296a47bf73990135c7d07ce741b9623b", size = 444782, upload-time = "2025-06-14T08:33:14.905Z" },
-]
-
-[[package]]
-name = "pydantic-core"
-version = "2.33.2"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "typing-extensions" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/ad/88/5f2260bdfae97aabf98f1778d43f69574390ad787afb646292a638c923d4/pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc", size = 435195, upload-time = "2025-04-23T18:33:52.104Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/e5/92/b31726561b5dae176c2d2c2dc43a9c5bfba5d32f96f8b4c0a600dd492447/pydantic_core-2.33.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2b3d326aaef0c0399d9afffeb6367d5e26ddc24d351dbc9c636840ac355dc5d8", size = 2028817, upload-time = "2025-04-23T18:30:43.919Z" },
- { url = "https://files.pythonhosted.org/packages/a3/44/3f0b95fafdaca04a483c4e685fe437c6891001bf3ce8b2fded82b9ea3aa1/pydantic_core-2.33.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e5b2671f05ba48b94cb90ce55d8bdcaaedb8ba00cc5359f6810fc918713983d", size = 1861357, upload-time = "2025-04-23T18:30:46.372Z" },
- { url = "https://files.pythonhosted.org/packages/30/97/e8f13b55766234caae05372826e8e4b3b96e7b248be3157f53237682e43c/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0069c9acc3f3981b9ff4cdfaf088e98d83440a4c7ea1bc07460af3d4dc22e72d", size = 1898011, upload-time = "2025-04-23T18:30:47.591Z" },
- { url = "https://files.pythonhosted.org/packages/9b/a3/99c48cf7bafc991cc3ee66fd544c0aae8dc907b752f1dad2d79b1b5a471f/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d53b22f2032c42eaaf025f7c40c2e3b94568ae077a606f006d206a463bc69572", size = 1982730, upload-time = "2025-04-23T18:30:49.328Z" },
- { url = "https://files.pythonhosted.org/packages/de/8e/a5b882ec4307010a840fb8b58bd9bf65d1840c92eae7534c7441709bf54b/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0405262705a123b7ce9f0b92f123334d67b70fd1f20a9372b907ce1080c7ba02", size = 2136178, upload-time = "2025-04-23T18:30:50.907Z" },
- { url = "https://files.pythonhosted.org/packages/e4/bb/71e35fc3ed05af6834e890edb75968e2802fe98778971ab5cba20a162315/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4b25d91e288e2c4e0662b8038a28c6a07eaac3e196cfc4ff69de4ea3db992a1b", size = 2736462, upload-time = "2025-04-23T18:30:52.083Z" },
- { url = "https://files.pythonhosted.org/packages/31/0d/c8f7593e6bc7066289bbc366f2235701dcbebcd1ff0ef8e64f6f239fb47d/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bdfe4b3789761f3bcb4b1ddf33355a71079858958e3a552f16d5af19768fef2", size = 2005652, upload-time = "2025-04-23T18:30:53.389Z" },
- { url = "https://files.pythonhosted.org/packages/d2/7a/996d8bd75f3eda405e3dd219ff5ff0a283cd8e34add39d8ef9157e722867/pydantic_core-2.33.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:efec8db3266b76ef9607c2c4c419bdb06bf335ae433b80816089ea7585816f6a", size = 2113306, upload-time = "2025-04-23T18:30:54.661Z" },
- { url = "https://files.pythonhosted.org/packages/ff/84/daf2a6fb2db40ffda6578a7e8c5a6e9c8affb251a05c233ae37098118788/pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:031c57d67ca86902726e0fae2214ce6770bbe2f710dc33063187a68744a5ecac", size = 2073720, upload-time = "2025-04-23T18:30:56.11Z" },
- { url = "https://files.pythonhosted.org/packages/77/fb/2258da019f4825128445ae79456a5499c032b55849dbd5bed78c95ccf163/pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:f8de619080e944347f5f20de29a975c2d815d9ddd8be9b9b7268e2e3ef68605a", size = 2244915, upload-time = "2025-04-23T18:30:57.501Z" },
- { url = "https://files.pythonhosted.org/packages/d8/7a/925ff73756031289468326e355b6fa8316960d0d65f8b5d6b3a3e7866de7/pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:73662edf539e72a9440129f231ed3757faab89630d291b784ca99237fb94db2b", size = 2241884, upload-time = "2025-04-23T18:30:58.867Z" },
- { url = "https://files.pythonhosted.org/packages/0b/b0/249ee6d2646f1cdadcb813805fe76265745c4010cf20a8eba7b0e639d9b2/pydantic_core-2.33.2-cp310-cp310-win32.whl", hash = "sha256:0a39979dcbb70998b0e505fb1556a1d550a0781463ce84ebf915ba293ccb7e22", size = 1910496, upload-time = "2025-04-23T18:31:00.078Z" },
- { url = "https://files.pythonhosted.org/packages/66/ff/172ba8f12a42d4b552917aa65d1f2328990d3ccfc01d5b7c943ec084299f/pydantic_core-2.33.2-cp310-cp310-win_amd64.whl", hash = "sha256:b0379a2b24882fef529ec3b4987cb5d003b9cda32256024e6fe1586ac45fc640", size = 1955019, upload-time = "2025-04-23T18:31:01.335Z" },
- { url = "https://files.pythonhosted.org/packages/3f/8d/71db63483d518cbbf290261a1fc2839d17ff89fce7089e08cad07ccfce67/pydantic_core-2.33.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4c5b0a576fb381edd6d27f0a85915c6daf2f8138dc5c267a57c08a62900758c7", size = 2028584, upload-time = "2025-04-23T18:31:03.106Z" },
- { url = "https://files.pythonhosted.org/packages/24/2f/3cfa7244ae292dd850989f328722d2aef313f74ffc471184dc509e1e4e5a/pydantic_core-2.33.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e799c050df38a639db758c617ec771fd8fb7a5f8eaaa4b27b101f266b216a246", size = 1855071, upload-time = "2025-04-23T18:31:04.621Z" },
- { url = "https://files.pythonhosted.org/packages/b3/d3/4ae42d33f5e3f50dd467761304be2fa0a9417fbf09735bc2cce003480f2a/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc46a01bf8d62f227d5ecee74178ffc448ff4e5197c756331f71efcc66dc980f", size = 1897823, upload-time = "2025-04-23T18:31:06.377Z" },
- { url = "https://files.pythonhosted.org/packages/f4/f3/aa5976e8352b7695ff808599794b1fba2a9ae2ee954a3426855935799488/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a144d4f717285c6d9234a66778059f33a89096dfb9b39117663fd8413d582dcc", size = 1983792, upload-time = "2025-04-23T18:31:07.93Z" },
- { url = "https://files.pythonhosted.org/packages/d5/7a/cda9b5a23c552037717f2b2a5257e9b2bfe45e687386df9591eff7b46d28/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73cf6373c21bc80b2e0dc88444f41ae60b2f070ed02095754eb5a01df12256de", size = 2136338, upload-time = "2025-04-23T18:31:09.283Z" },
- { url = "https://files.pythonhosted.org/packages/2b/9f/b8f9ec8dd1417eb9da784e91e1667d58a2a4a7b7b34cf4af765ef663a7e5/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3dc625f4aa79713512d1976fe9f0bc99f706a9dee21dfd1810b4bbbf228d0e8a", size = 2730998, upload-time = "2025-04-23T18:31:11.7Z" },
- { url = "https://files.pythonhosted.org/packages/47/bc/cd720e078576bdb8255d5032c5d63ee5c0bf4b7173dd955185a1d658c456/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b21b5549499972441da4758d662aeea93f1923f953e9cbaff14b8b9565aef", size = 2003200, upload-time = "2025-04-23T18:31:13.536Z" },
- { url = "https://files.pythonhosted.org/packages/ca/22/3602b895ee2cd29d11a2b349372446ae9727c32e78a94b3d588a40fdf187/pydantic_core-2.33.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bdc25f3681f7b78572699569514036afe3c243bc3059d3942624e936ec93450e", size = 2113890, upload-time = "2025-04-23T18:31:15.011Z" },
- { url = "https://files.pythonhosted.org/packages/ff/e6/e3c5908c03cf00d629eb38393a98fccc38ee0ce8ecce32f69fc7d7b558a7/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fe5b32187cbc0c862ee201ad66c30cf218e5ed468ec8dc1cf49dec66e160cc4d", size = 2073359, upload-time = "2025-04-23T18:31:16.393Z" },
- { url = "https://files.pythonhosted.org/packages/12/e7/6a36a07c59ebefc8777d1ffdaf5ae71b06b21952582e4b07eba88a421c79/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:bc7aee6f634a6f4a95676fcb5d6559a2c2a390330098dba5e5a5f28a2e4ada30", size = 2245883, upload-time = "2025-04-23T18:31:17.892Z" },
- { url = "https://files.pythonhosted.org/packages/16/3f/59b3187aaa6cc0c1e6616e8045b284de2b6a87b027cce2ffcea073adf1d2/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:235f45e5dbcccf6bd99f9f472858849f73d11120d76ea8707115415f8e5ebebf", size = 2241074, upload-time = "2025-04-23T18:31:19.205Z" },
- { url = "https://files.pythonhosted.org/packages/e0/ed/55532bb88f674d5d8f67ab121a2a13c385df382de2a1677f30ad385f7438/pydantic_core-2.33.2-cp311-cp311-win32.whl", hash = "sha256:6368900c2d3ef09b69cb0b913f9f8263b03786e5b2a387706c5afb66800efd51", size = 1910538, upload-time = "2025-04-23T18:31:20.541Z" },
- { url = "https://files.pythonhosted.org/packages/fe/1b/25b7cccd4519c0b23c2dd636ad39d381abf113085ce4f7bec2b0dc755eb1/pydantic_core-2.33.2-cp311-cp311-win_amd64.whl", hash = "sha256:1e063337ef9e9820c77acc768546325ebe04ee38b08703244c1309cccc4f1bab", size = 1952909, upload-time = "2025-04-23T18:31:22.371Z" },
- { url = "https://files.pythonhosted.org/packages/49/a9/d809358e49126438055884c4366a1f6227f0f84f635a9014e2deb9b9de54/pydantic_core-2.33.2-cp311-cp311-win_arm64.whl", hash = "sha256:6b99022f1d19bc32a4c2a0d544fc9a76e3be90f0b3f4af413f87d38749300e65", size = 1897786, upload-time = "2025-04-23T18:31:24.161Z" },
- { url = "https://files.pythonhosted.org/packages/18/8a/2b41c97f554ec8c71f2a8a5f85cb56a8b0956addfe8b0efb5b3d77e8bdc3/pydantic_core-2.33.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a7ec89dc587667f22b6a0b6579c249fca9026ce7c333fc142ba42411fa243cdc", size = 2009000, upload-time = "2025-04-23T18:31:25.863Z" },
- { url = "https://files.pythonhosted.org/packages/a1/02/6224312aacb3c8ecbaa959897af57181fb6cf3a3d7917fd44d0f2917e6f2/pydantic_core-2.33.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3c6db6e52c6d70aa0d00d45cdb9b40f0433b96380071ea80b09277dba021ddf7", size = 1847996, upload-time = "2025-04-23T18:31:27.341Z" },
- { url = "https://files.pythonhosted.org/packages/d6/46/6dcdf084a523dbe0a0be59d054734b86a981726f221f4562aed313dbcb49/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e61206137cbc65e6d5256e1166f88331d3b6238e082d9f74613b9b765fb9025", size = 1880957, upload-time = "2025-04-23T18:31:28.956Z" },
- { url = "https://files.pythonhosted.org/packages/ec/6b/1ec2c03837ac00886ba8160ce041ce4e325b41d06a034adbef11339ae422/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb8c529b2819c37140eb51b914153063d27ed88e3bdc31b71198a198e921e011", size = 1964199, upload-time = "2025-04-23T18:31:31.025Z" },
- { url = "https://files.pythonhosted.org/packages/2d/1d/6bf34d6adb9debd9136bd197ca72642203ce9aaaa85cfcbfcf20f9696e83/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c52b02ad8b4e2cf14ca7b3d918f3eb0ee91e63b3167c32591e57c4317e134f8f", size = 2120296, upload-time = "2025-04-23T18:31:32.514Z" },
- { url = "https://files.pythonhosted.org/packages/e0/94/2bd0aaf5a591e974b32a9f7123f16637776c304471a0ab33cf263cf5591a/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:96081f1605125ba0855dfda83f6f3df5ec90c61195421ba72223de35ccfb2f88", size = 2676109, upload-time = "2025-04-23T18:31:33.958Z" },
- { url = "https://files.pythonhosted.org/packages/f9/41/4b043778cf9c4285d59742281a769eac371b9e47e35f98ad321349cc5d61/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f57a69461af2a5fa6e6bbd7a5f60d3b7e6cebb687f55106933188e79ad155c1", size = 2002028, upload-time = "2025-04-23T18:31:39.095Z" },
- { url = "https://files.pythonhosted.org/packages/cb/d5/7bb781bf2748ce3d03af04d5c969fa1308880e1dca35a9bd94e1a96a922e/pydantic_core-2.33.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:572c7e6c8bb4774d2ac88929e3d1f12bc45714ae5ee6d9a788a9fb35e60bb04b", size = 2100044, upload-time = "2025-04-23T18:31:41.034Z" },
- { url = "https://files.pythonhosted.org/packages/fe/36/def5e53e1eb0ad896785702a5bbfd25eed546cdcf4087ad285021a90ed53/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:db4b41f9bd95fbe5acd76d89920336ba96f03e149097365afe1cb092fceb89a1", size = 2058881, upload-time = "2025-04-23T18:31:42.757Z" },
- { url = "https://files.pythonhosted.org/packages/01/6c/57f8d70b2ee57fc3dc8b9610315949837fa8c11d86927b9bb044f8705419/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:fa854f5cf7e33842a892e5c73f45327760bc7bc516339fda888c75ae60edaeb6", size = 2227034, upload-time = "2025-04-23T18:31:44.304Z" },
- { url = "https://files.pythonhosted.org/packages/27/b9/9c17f0396a82b3d5cbea4c24d742083422639e7bb1d5bf600e12cb176a13/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5f483cfb75ff703095c59e365360cb73e00185e01aaea067cd19acffd2ab20ea", size = 2234187, upload-time = "2025-04-23T18:31:45.891Z" },
- { url = "https://files.pythonhosted.org/packages/b0/6a/adf5734ffd52bf86d865093ad70b2ce543415e0e356f6cacabbc0d9ad910/pydantic_core-2.33.2-cp312-cp312-win32.whl", hash = "sha256:9cb1da0f5a471435a7bc7e439b8a728e8b61e59784b2af70d7c169f8dd8ae290", size = 1892628, upload-time = "2025-04-23T18:31:47.819Z" },
- { url = "https://files.pythonhosted.org/packages/43/e4/5479fecb3606c1368d496a825d8411e126133c41224c1e7238be58b87d7e/pydantic_core-2.33.2-cp312-cp312-win_amd64.whl", hash = "sha256:f941635f2a3d96b2973e867144fde513665c87f13fe0e193c158ac51bfaaa7b2", size = 1955866, upload-time = "2025-04-23T18:31:49.635Z" },
- { url = "https://files.pythonhosted.org/packages/0d/24/8b11e8b3e2be9dd82df4b11408a67c61bb4dc4f8e11b5b0fc888b38118b5/pydantic_core-2.33.2-cp312-cp312-win_arm64.whl", hash = "sha256:cca3868ddfaccfbc4bfb1d608e2ccaaebe0ae628e1416aeb9c4d88c001bb45ab", size = 1888894, upload-time = "2025-04-23T18:31:51.609Z" },
- { url = "https://files.pythonhosted.org/packages/46/8c/99040727b41f56616573a28771b1bfa08a3d3fe74d3d513f01251f79f172/pydantic_core-2.33.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1082dd3e2d7109ad8b7da48e1d4710c8d06c253cbc4a27c1cff4fbcaa97a9e3f", size = 2015688, upload-time = "2025-04-23T18:31:53.175Z" },
- { url = "https://files.pythonhosted.org/packages/3a/cc/5999d1eb705a6cefc31f0b4a90e9f7fc400539b1a1030529700cc1b51838/pydantic_core-2.33.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f517ca031dfc037a9c07e748cefd8d96235088b83b4f4ba8939105d20fa1dcd6", size = 1844808, upload-time = "2025-04-23T18:31:54.79Z" },
- { url = "https://files.pythonhosted.org/packages/6f/5e/a0a7b8885c98889a18b6e376f344da1ef323d270b44edf8174d6bce4d622/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a9f2c9dd19656823cb8250b0724ee9c60a82f3cdf68a080979d13092a3b0fef", size = 1885580, upload-time = "2025-04-23T18:31:57.393Z" },
- { url = "https://files.pythonhosted.org/packages/3b/2a/953581f343c7d11a304581156618c3f592435523dd9d79865903272c256a/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b0a451c263b01acebe51895bfb0e1cc842a5c666efe06cdf13846c7418caa9a", size = 1973859, upload-time = "2025-04-23T18:31:59.065Z" },
- { url = "https://files.pythonhosted.org/packages/e6/55/f1a813904771c03a3f97f676c62cca0c0a4138654107c1b61f19c644868b/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ea40a64d23faa25e62a70ad163571c0b342b8bf66d5fa612ac0dec4f069d916", size = 2120810, upload-time = "2025-04-23T18:32:00.78Z" },
- { url = "https://files.pythonhosted.org/packages/aa/c3/053389835a996e18853ba107a63caae0b9deb4a276c6b472931ea9ae6e48/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb2d542b4d66f9470e8065c5469ec676978d625a8b7a363f07d9a501a9cb36a", size = 2676498, upload-time = "2025-04-23T18:32:02.418Z" },
- { url = "https://files.pythonhosted.org/packages/eb/3c/f4abd740877a35abade05e437245b192f9d0ffb48bbbbd708df33d3cda37/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdac5d6ffa1b5a83bca06ffe7583f5576555e6c8b3a91fbd25ea7780f825f7d", size = 2000611, upload-time = "2025-04-23T18:32:04.152Z" },
- { url = "https://files.pythonhosted.org/packages/59/a7/63ef2fed1837d1121a894d0ce88439fe3e3b3e48c7543b2a4479eb99c2bd/pydantic_core-2.33.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04a1a413977ab517154eebb2d326da71638271477d6ad87a769102f7c2488c56", size = 2107924, upload-time = "2025-04-23T18:32:06.129Z" },
- { url = "https://files.pythonhosted.org/packages/04/8f/2551964ef045669801675f1cfc3b0d74147f4901c3ffa42be2ddb1f0efc4/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c8e7af2f4e0194c22b5b37205bfb293d166a7344a5b0d0eaccebc376546d77d5", size = 2063196, upload-time = "2025-04-23T18:32:08.178Z" },
- { url = "https://files.pythonhosted.org/packages/26/bd/d9602777e77fc6dbb0c7db9ad356e9a985825547dce5ad1d30ee04903918/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:5c92edd15cd58b3c2d34873597a1e20f13094f59cf88068adb18947df5455b4e", size = 2236389, upload-time = "2025-04-23T18:32:10.242Z" },
- { url = "https://files.pythonhosted.org/packages/42/db/0e950daa7e2230423ab342ae918a794964b053bec24ba8af013fc7c94846/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:65132b7b4a1c0beded5e057324b7e16e10910c106d43675d9bd87d4f38dde162", size = 2239223, upload-time = "2025-04-23T18:32:12.382Z" },
- { url = "https://files.pythonhosted.org/packages/58/4d/4f937099c545a8a17eb52cb67fe0447fd9a373b348ccfa9a87f141eeb00f/pydantic_core-2.33.2-cp313-cp313-win32.whl", hash = "sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849", size = 1900473, upload-time = "2025-04-23T18:32:14.034Z" },
- { url = "https://files.pythonhosted.org/packages/a0/75/4a0a9bac998d78d889def5e4ef2b065acba8cae8c93696906c3a91f310ca/pydantic_core-2.33.2-cp313-cp313-win_amd64.whl", hash = "sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9", size = 1955269, upload-time = "2025-04-23T18:32:15.783Z" },
- { url = "https://files.pythonhosted.org/packages/f9/86/1beda0576969592f1497b4ce8e7bc8cbdf614c352426271b1b10d5f0aa64/pydantic_core-2.33.2-cp313-cp313-win_arm64.whl", hash = "sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9", size = 1893921, upload-time = "2025-04-23T18:32:18.473Z" },
- { url = "https://files.pythonhosted.org/packages/a4/7d/e09391c2eebeab681df2b74bfe6c43422fffede8dc74187b2b0bf6fd7571/pydantic_core-2.33.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac", size = 1806162, upload-time = "2025-04-23T18:32:20.188Z" },
- { url = "https://files.pythonhosted.org/packages/f1/3d/847b6b1fed9f8ed3bb95a9ad04fbd0b212e832d4f0f50ff4d9ee5a9f15cf/pydantic_core-2.33.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5", size = 1981560, upload-time = "2025-04-23T18:32:22.354Z" },
- { url = "https://files.pythonhosted.org/packages/6f/9a/e73262f6c6656262b5fdd723ad90f518f579b7bc8622e43a942eec53c938/pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9", size = 1935777, upload-time = "2025-04-23T18:32:25.088Z" },
- { url = "https://files.pythonhosted.org/packages/53/ea/bbe9095cdd771987d13c82d104a9c8559ae9aec1e29f139e286fd2e9256e/pydantic_core-2.33.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a2b911a5b90e0374d03813674bf0a5fbbb7741570dcd4b4e85a2e48d17def29d", size = 2028677, upload-time = "2025-04-23T18:32:27.227Z" },
- { url = "https://files.pythonhosted.org/packages/49/1d/4ac5ed228078737d457a609013e8f7edc64adc37b91d619ea965758369e5/pydantic_core-2.33.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6fa6dfc3e4d1f734a34710f391ae822e0a8eb8559a85c6979e14e65ee6ba2954", size = 1864735, upload-time = "2025-04-23T18:32:29.019Z" },
- { url = "https://files.pythonhosted.org/packages/23/9a/2e70d6388d7cda488ae38f57bc2f7b03ee442fbcf0d75d848304ac7e405b/pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c54c939ee22dc8e2d545da79fc5381f1c020d6d3141d3bd747eab59164dc89fb", size = 1898467, upload-time = "2025-04-23T18:32:31.119Z" },
- { url = "https://files.pythonhosted.org/packages/ff/2e/1568934feb43370c1ffb78a77f0baaa5a8b6897513e7a91051af707ffdc4/pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:53a57d2ed685940a504248187d5685e49eb5eef0f696853647bf37c418c538f7", size = 1983041, upload-time = "2025-04-23T18:32:33.655Z" },
- { url = "https://files.pythonhosted.org/packages/01/1a/1a1118f38ab64eac2f6269eb8c120ab915be30e387bb561e3af904b12499/pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09fb9dd6571aacd023fe6aaca316bd01cf60ab27240d7eb39ebd66a3a15293b4", size = 2136503, upload-time = "2025-04-23T18:32:35.519Z" },
- { url = "https://files.pythonhosted.org/packages/5c/da/44754d1d7ae0f22d6d3ce6c6b1486fc07ac2c524ed8f6eca636e2e1ee49b/pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0e6116757f7959a712db11f3e9c0a99ade00a5bbedae83cb801985aa154f071b", size = 2736079, upload-time = "2025-04-23T18:32:37.659Z" },
- { url = "https://files.pythonhosted.org/packages/4d/98/f43cd89172220ec5aa86654967b22d862146bc4d736b1350b4c41e7c9c03/pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d55ab81c57b8ff8548c3e4947f119551253f4e3787a7bbc0b6b3ca47498a9d3", size = 2006508, upload-time = "2025-04-23T18:32:39.637Z" },
- { url = "https://files.pythonhosted.org/packages/2b/cc/f77e8e242171d2158309f830f7d5d07e0531b756106f36bc18712dc439df/pydantic_core-2.33.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c20c462aa4434b33a2661701b861604913f912254e441ab8d78d30485736115a", size = 2113693, upload-time = "2025-04-23T18:32:41.818Z" },
- { url = "https://files.pythonhosted.org/packages/54/7a/7be6a7bd43e0a47c147ba7fbf124fe8aaf1200bc587da925509641113b2d/pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:44857c3227d3fb5e753d5fe4a3420d6376fa594b07b621e220cd93703fe21782", size = 2074224, upload-time = "2025-04-23T18:32:44.033Z" },
- { url = "https://files.pythonhosted.org/packages/2a/07/31cf8fadffbb03be1cb520850e00a8490c0927ec456e8293cafda0726184/pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:eb9b459ca4df0e5c87deb59d37377461a538852765293f9e6ee834f0435a93b9", size = 2245403, upload-time = "2025-04-23T18:32:45.836Z" },
- { url = "https://files.pythonhosted.org/packages/b6/8d/bbaf4c6721b668d44f01861f297eb01c9b35f612f6b8e14173cb204e6240/pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9fcd347d2cc5c23b06de6d3b7b8275be558a0c90549495c699e379a80bf8379e", size = 2242331, upload-time = "2025-04-23T18:32:47.618Z" },
- { url = "https://files.pythonhosted.org/packages/bb/93/3cc157026bca8f5006250e74515119fcaa6d6858aceee8f67ab6dc548c16/pydantic_core-2.33.2-cp39-cp39-win32.whl", hash = "sha256:83aa99b1285bc8f038941ddf598501a86f1536789740991d7d8756e34f1e74d9", size = 1910571, upload-time = "2025-04-23T18:32:49.401Z" },
- { url = "https://files.pythonhosted.org/packages/5b/90/7edc3b2a0d9f0dda8806c04e511a67b0b7a41d2187e2003673a996fb4310/pydantic_core-2.33.2-cp39-cp39-win_amd64.whl", hash = "sha256:f481959862f57f29601ccced557cc2e817bce7533ab8e01a797a48b49c9692b3", size = 1956504, upload-time = "2025-04-23T18:32:51.287Z" },
- { url = "https://files.pythonhosted.org/packages/30/68/373d55e58b7e83ce371691f6eaa7175e3a24b956c44628eb25d7da007917/pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5c4aa4e82353f65e548c476b37e64189783aa5384903bfea4f41580f255fddfa", size = 2023982, upload-time = "2025-04-23T18:32:53.14Z" },
- { url = "https://files.pythonhosted.org/packages/a4/16/145f54ac08c96a63d8ed6442f9dec17b2773d19920b627b18d4f10a061ea/pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d946c8bf0d5c24bf4fe333af284c59a19358aa3ec18cb3dc4370080da1e8ad29", size = 1858412, upload-time = "2025-04-23T18:32:55.52Z" },
- { url = "https://files.pythonhosted.org/packages/41/b1/c6dc6c3e2de4516c0bb2c46f6a373b91b5660312342a0cf5826e38ad82fa/pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87b31b6846e361ef83fedb187bb5b4372d0da3f7e28d85415efa92d6125d6e6d", size = 1892749, upload-time = "2025-04-23T18:32:57.546Z" },
- { url = "https://files.pythonhosted.org/packages/12/73/8cd57e20afba760b21b742106f9dbdfa6697f1570b189c7457a1af4cd8a0/pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa9d91b338f2df0508606f7009fde642391425189bba6d8c653afd80fd6bb64e", size = 2067527, upload-time = "2025-04-23T18:32:59.771Z" },
- { url = "https://files.pythonhosted.org/packages/e3/d5/0bb5d988cc019b3cba4a78f2d4b3854427fc47ee8ec8e9eaabf787da239c/pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2058a32994f1fde4ca0480ab9d1e75a0e8c87c22b53a3ae66554f9af78f2fe8c", size = 2108225, upload-time = "2025-04-23T18:33:04.51Z" },
- { url = "https://files.pythonhosted.org/packages/f1/c5/00c02d1571913d496aabf146106ad8239dc132485ee22efe08085084ff7c/pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:0e03262ab796d986f978f79c943fc5f620381be7287148b8010b4097f79a39ec", size = 2069490, upload-time = "2025-04-23T18:33:06.391Z" },
- { url = "https://files.pythonhosted.org/packages/22/a8/dccc38768274d3ed3a59b5d06f59ccb845778687652daa71df0cab4040d7/pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:1a8695a8d00c73e50bff9dfda4d540b7dee29ff9b8053e38380426a85ef10052", size = 2237525, upload-time = "2025-04-23T18:33:08.44Z" },
- { url = "https://files.pythonhosted.org/packages/d4/e7/4f98c0b125dda7cf7ccd14ba936218397b44f50a56dd8c16a3091df116c3/pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:fa754d1850735a0b0e03bcffd9d4b4343eb417e47196e4485d9cca326073a42c", size = 2238446, upload-time = "2025-04-23T18:33:10.313Z" },
- { url = "https://files.pythonhosted.org/packages/ce/91/2ec36480fdb0b783cd9ef6795753c1dea13882f2e68e73bce76ae8c21e6a/pydantic_core-2.33.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a11c8d26a50bfab49002947d3d237abe4d9e4b5bdc8846a63537b6488e197808", size = 2066678, upload-time = "2025-04-23T18:33:12.224Z" },
- { url = "https://files.pythonhosted.org/packages/7b/27/d4ae6487d73948d6f20dddcd94be4ea43e74349b56eba82e9bdee2d7494c/pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:dd14041875d09cc0f9308e37a6f8b65f5585cf2598a53aa0123df8b129d481f8", size = 2025200, upload-time = "2025-04-23T18:33:14.199Z" },
- { url = "https://files.pythonhosted.org/packages/f1/b8/b3cb95375f05d33801024079b9392a5ab45267a63400bf1866e7ce0f0de4/pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d87c561733f66531dced0da6e864f44ebf89a8fba55f31407b00c2f7f9449593", size = 1859123, upload-time = "2025-04-23T18:33:16.555Z" },
- { url = "https://files.pythonhosted.org/packages/05/bc/0d0b5adeda59a261cd30a1235a445bf55c7e46ae44aea28f7bd6ed46e091/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f82865531efd18d6e07a04a17331af02cb7a651583c418df8266f17a63c6612", size = 1892852, upload-time = "2025-04-23T18:33:18.513Z" },
- { url = "https://files.pythonhosted.org/packages/3e/11/d37bdebbda2e449cb3f519f6ce950927b56d62f0b84fd9cb9e372a26a3d5/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bfb5112df54209d820d7bf9317c7a6c9025ea52e49f46b6a2060104bba37de7", size = 2067484, upload-time = "2025-04-23T18:33:20.475Z" },
- { url = "https://files.pythonhosted.org/packages/8c/55/1f95f0a05ce72ecb02a8a8a1c3be0579bbc29b1d5ab68f1378b7bebc5057/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:64632ff9d614e5eecfb495796ad51b0ed98c453e447a76bcbeeb69615079fc7e", size = 2108896, upload-time = "2025-04-23T18:33:22.501Z" },
- { url = "https://files.pythonhosted.org/packages/53/89/2b2de6c81fa131f423246a9109d7b2a375e83968ad0800d6e57d0574629b/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:f889f7a40498cc077332c7ab6b4608d296d852182211787d4f3ee377aaae66e8", size = 2069475, upload-time = "2025-04-23T18:33:24.528Z" },
- { url = "https://files.pythonhosted.org/packages/b8/e9/1f7efbe20d0b2b10f6718944b5d8ece9152390904f29a78e68d4e7961159/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:de4b83bb311557e439b9e186f733f6c645b9417c84e2eb8203f3f820a4b988bf", size = 2239013, upload-time = "2025-04-23T18:33:26.621Z" },
- { url = "https://files.pythonhosted.org/packages/3c/b2/5309c905a93811524a49b4e031e9851a6b00ff0fb668794472ea7746b448/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:82f68293f055f51b51ea42fafc74b6aad03e70e191799430b90c13d643059ebb", size = 2238715, upload-time = "2025-04-23T18:33:28.656Z" },
- { url = "https://files.pythonhosted.org/packages/32/56/8a7ca5d2cd2cda1d245d34b1c9a942920a718082ae8e54e5f3e5a58b7add/pydantic_core-2.33.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:329467cecfb529c925cf2bbd4d60d2c509bc2fb52a20c1045bf09bb70971a9c1", size = 2066757, upload-time = "2025-04-23T18:33:30.645Z" },
- { url = "https://files.pythonhosted.org/packages/08/98/dbf3fdfabaf81cda5622154fda78ea9965ac467e3239078e0dcd6df159e7/pydantic_core-2.33.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:87acbfcf8e90ca885206e98359d7dca4bcbb35abdc0ff66672a293e1d7a19101", size = 2024034, upload-time = "2025-04-23T18:33:32.843Z" },
- { url = "https://files.pythonhosted.org/packages/8d/99/7810aa9256e7f2ccd492590f86b79d370df1e9292f1f80b000b6a75bd2fb/pydantic_core-2.33.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:7f92c15cd1e97d4b12acd1cc9004fa092578acfa57b67ad5e43a197175d01a64", size = 1858578, upload-time = "2025-04-23T18:33:34.912Z" },
- { url = "https://files.pythonhosted.org/packages/d8/60/bc06fa9027c7006cc6dd21e48dbf39076dc39d9abbaf718a1604973a9670/pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3f26877a748dc4251cfcfda9dfb5f13fcb034f5308388066bcfe9031b63ae7d", size = 1892858, upload-time = "2025-04-23T18:33:36.933Z" },
- { url = "https://files.pythonhosted.org/packages/f2/40/9d03997d9518816c68b4dfccb88969756b9146031b61cd37f781c74c9b6a/pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dac89aea9af8cd672fa7b510e7b8c33b0bba9a43186680550ccf23020f32d535", size = 2068498, upload-time = "2025-04-23T18:33:38.997Z" },
- { url = "https://files.pythonhosted.org/packages/d8/62/d490198d05d2d86672dc269f52579cad7261ced64c2df213d5c16e0aecb1/pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:970919794d126ba8645f3837ab6046fb4e72bbc057b3709144066204c19a455d", size = 2108428, upload-time = "2025-04-23T18:33:41.18Z" },
- { url = "https://files.pythonhosted.org/packages/9a/ec/4cd215534fd10b8549015f12ea650a1a973da20ce46430b68fc3185573e8/pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:3eb3fe62804e8f859c49ed20a8451342de53ed764150cb14ca71357c765dc2a6", size = 2069854, upload-time = "2025-04-23T18:33:43.446Z" },
- { url = "https://files.pythonhosted.org/packages/1a/1a/abbd63d47e1d9b0d632fee6bb15785d0889c8a6e0a6c3b5a8e28ac1ec5d2/pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:3abcd9392a36025e3bd55f9bd38d908bd17962cc49bc6da8e7e96285336e2bca", size = 2237859, upload-time = "2025-04-23T18:33:45.56Z" },
- { url = "https://files.pythonhosted.org/packages/80/1c/fa883643429908b1c90598fd2642af8839efd1d835b65af1f75fba4d94fe/pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:3a1c81334778f9e3af2f8aeb7a960736e5cab1dfebfb26aabca09afd2906c039", size = 2239059, upload-time = "2025-04-23T18:33:47.735Z" },
- { url = "https://files.pythonhosted.org/packages/d4/29/3cade8a924a61f60ccfa10842f75eb12787e1440e2b8660ceffeb26685e7/pydantic_core-2.33.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2807668ba86cb38c6817ad9bc66215ab8584d1d304030ce4f0887336f28a5e27", size = 2066661, upload-time = "2025-04-23T18:33:49.995Z" },
-]
-
-[[package]]
-name = "pydantic-settings"
-version = "2.10.1"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "pydantic" },
- { name = "python-dotenv" },
- { name = "typing-inspection" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/68/85/1ea668bbab3c50071ca613c6ab30047fb36ab0da1b92fa8f17bbc38fd36c/pydantic_settings-2.10.1.tar.gz", hash = "sha256:06f0062169818d0f5524420a360d632d5857b83cffd4d42fe29597807a1614ee", size = 172583, upload-time = "2025-06-24T13:26:46.841Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/58/f0/427018098906416f580e3cf1366d3b1abfb408a0652e9f31600c24a1903c/pydantic_settings-2.10.1-py3-none-any.whl", hash = "sha256:a60952460b99cf661dc25c29c0ef171721f98bfcb52ef8d9ea4c943d7c8cc796", size = 45235, upload-time = "2025-06-24T13:26:45.485Z" },
-]
-
-[[package]]
-name = "pydeck"
-version = "0.9.1"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "jinja2" },
- { name = "numpy" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/a1/ca/40e14e196864a0f61a92abb14d09b3d3da98f94ccb03b49cf51688140dab/pydeck-0.9.1.tar.gz", hash = "sha256:f74475ae637951d63f2ee58326757f8d4f9cd9f2a457cf42950715003e2cb605", size = 3832240, upload-time = "2024-05-10T15:36:21.153Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/ab/4c/b888e6cf58bd9db9c93f40d1c6be8283ff49d88919231afe93a6bcf61626/pydeck-0.9.1-py2.py3-none-any.whl", hash = "sha256:b3f75ba0d273fc917094fa61224f3f6076ca8752b93d46faf3bcfd9f9d59b038", size = 6900403, upload-time = "2024-05-10T15:36:17.36Z" },
-]
-
-[[package]]
-name = "pygments"
-version = "2.19.2"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" },
-]
-
-[[package]]
-name = "pyhive"
-version = "0.7.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "future" },
- { name = "python-dateutil" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/f6/ec/5c658b3a4d99a6d9145030cc8e003c3f7efc668d866e88544812ab0af310/PyHive-0.7.0.tar.gz", hash = "sha256:585beff9578a61b99aed47140fec97e26323e8c685a5b5d0c8550a8ebf8a24e0", size = 46479, upload-time = "2023-08-17T23:29:25.26Z" }
-
-[[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, upload-time = "2024-11-28T03:43:29.933Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/61/ad/689f02752eeec26aed679477e80e632ef1b682313be70793d798c1d5fc8f/PyJWT-2.10.1-py3-none-any.whl", hash = "sha256:dcdd193e30abefd5debf142f9adfcdd2b58004e644f25406ffaebd50bd98dacb", size = 22997, upload-time = "2024-11-28T03:43:27.893Z" },
-]
-
-[[package]]
-name = "pyodbc"
-version = "5.2.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/a0/36/a1ac7d23a1611e7ccd4d27df096f3794e8d1e7faa040260d9d41b6fc3185/pyodbc-5.2.0.tar.gz", hash = "sha256:de8be39809c8ddeeee26a4b876a6463529cd487a60d1393eb2a93e9bcd44a8f5", size = 116908, upload-time = "2024-10-16T01:40:13.425Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/30/01/05c4a4ec122c4a8a37fa1be5bdbf6fb23724a2ee3b1b771bb46f710158a9/pyodbc-5.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:eb0850e3e3782f57457feed297e220bb20c3e8fd7550d7a6b6bb96112bd9b6fe", size = 72483, upload-time = "2024-10-16T01:39:23.697Z" },
- { url = "https://files.pythonhosted.org/packages/73/22/ba718cc5508bdfbb53e1906018d7f597be37241c769dda8a48f52af96fe3/pyodbc-5.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0dae0fb86078c87acf135dbe5afd3c7d15d52ab0db5965c44159e84058c3e2fb", size = 71794, upload-time = "2024-10-16T01:39:25.372Z" },
- { url = "https://files.pythonhosted.org/packages/24/e4/9d859ea3642059c10a6644a00ccb1f8b8e02c1e4f49ab34250db1273c2c5/pyodbc-5.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6493b9c7506ca964b80ad638d0dc82869df7058255d71f04fdd1405e88bcb36b", size = 332850, upload-time = "2024-10-16T01:39:27.789Z" },
- { url = "https://files.pythonhosted.org/packages/b9/a7/98c3555c10cfeb343ec7eea69ecb17476aa3ace72131ea8a4a1f8250318c/pyodbc-5.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e04de873607fb960e71953c164c83e8e5d9291ce0d69e688e54947b254b04902", size = 336009, upload-time = "2024-10-16T01:39:29.694Z" },
- { url = "https://files.pythonhosted.org/packages/24/c1/d5b16dd62eb70f281bc90cdc1e3c46af7acda3f0f6afb34553206506ccb2/pyodbc-5.2.0-cp310-cp310-win32.whl", hash = "sha256:74135cb10c1dcdbd99fe429c61539c232140e62939fa7c69b0a373cc552e4a08", size = 62407, upload-time = "2024-10-16T01:39:31.894Z" },
- { url = "https://files.pythonhosted.org/packages/f5/12/22c83669abee4ca5915aa89172cf1673b58ca05f44dabeb8b0bac9b7fecc/pyodbc-5.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:d287121eeaa562b9ab3d4c52fa77c793dfedd127049273eb882a05d3d67a8ce8", size = 68874, upload-time = "2024-10-16T01:39:33.325Z" },
- { url = "https://files.pythonhosted.org/packages/8f/a2/5907ce319a571eb1e271d6a475920edfeacd92da1021bb2a15ed1b7f6ac1/pyodbc-5.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4627779f0a608b51ce2d2fe6d1d395384e65ca36248bf9dbb6d7cf2c8fda1cab", size = 72536, upload-time = "2024-10-16T01:39:34.715Z" },
- { url = "https://files.pythonhosted.org/packages/e1/b8/bd438ab2bb9481615142784b0c9778079a87ae1bca7a0fe8aabfc088aa9f/pyodbc-5.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4d997d3b6551273647825c734158ca8a6f682df269f6b3975f2499c01577ddec", size = 71825, upload-time = "2024-10-16T01:39:36.343Z" },
- { url = "https://files.pythonhosted.org/packages/8b/82/cf71ae99b511a7f20c380ce470de233a0291fa3798afa74e0adc8fad1675/pyodbc-5.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5102007a8c78dd2fc1c1b6f6147de8cfc020f81013e4b46c33e66aaa7d1bf7b1", size = 342304, upload-time = "2024-10-16T01:39:37.82Z" },
- { url = "https://files.pythonhosted.org/packages/43/ea/03fe042f4a390df05e753ddd21c6cab006baae1eee71ce230f6e2a883944/pyodbc-5.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e3cbc7075a46c411b531ada557c4aef13d034060a70077717124cabc1717e2d", size = 346186, upload-time = "2024-10-16T01:39:39.3Z" },
- { url = "https://files.pythonhosted.org/packages/f9/80/48178bb50990147adb72ec9e377e94517a0dfaf2f2a6e3fe477d9a33671f/pyodbc-5.2.0-cp311-cp311-win32.whl", hash = "sha256:de1ee7ec2eb326b7be5e2c4ce20d472c5ef1a6eb838d126d1d26779ff5486e49", size = 62418, upload-time = "2024-10-16T01:39:40.797Z" },
- { url = "https://files.pythonhosted.org/packages/7c/6b/f0ad7d8a535d58f35f375ffbf367c68d0ec54452a431d23b0ebee4cd44c6/pyodbc-5.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:113f904b9852c12f10c7a3288f5a3563ecdbbefe3ccc829074a9eb8255edcd29", size = 68871, upload-time = "2024-10-16T01:39:41.997Z" },
- { url = "https://files.pythonhosted.org/packages/26/26/104525b728fedfababd3143426b9d0008c70f0d604a3bf5d4773977d83f4/pyodbc-5.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:be43d1ece4f2cf4d430996689d89a1a15aeb3a8da8262527e5ced5aee27e89c3", size = 73014, upload-time = "2024-10-16T01:39:43.332Z" },
- { url = "https://files.pythonhosted.org/packages/4f/7d/bb632488b603bcd2a6753b858e8bc7dd56146dd19bd72003cc09ae6e3fc0/pyodbc-5.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9f7badd0055221a744d76c11440c0856fd2846ed53b6555cf8f0a8893a3e4b03", size = 72515, upload-time = "2024-10-16T01:39:44.506Z" },
- { url = "https://files.pythonhosted.org/packages/ab/38/a1b9bfe5a7062672268553c2d6ff93676173b0fb4bd583e8c4f74a0e296f/pyodbc-5.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad633c52f4f4e7691daaa2278d6e6ebb2fe4ae7709e610e22c7dd1a1d620cf8b", size = 348561, upload-time = "2024-10-16T01:39:45.986Z" },
- { url = "https://files.pythonhosted.org/packages/71/82/ddb1c41c682550116f391aa6cab2052910046a30d63014bbe6d09c4958f4/pyodbc-5.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97d086a8f7a302b74c9c2e77bedf954a603b19168af900d4d3a97322e773df63", size = 353962, upload-time = "2024-10-16T01:39:47.254Z" },
- { url = "https://files.pythonhosted.org/packages/e5/29/fec0e739d0c1cab155843ed71d0717f5e1694effe3f28d397168f48bcd92/pyodbc-5.2.0-cp312-cp312-win32.whl", hash = "sha256:0e4412f8e608db2a4be5bcc75f9581f386ed6a427dbcb5eac795049ba6fc205e", size = 63050, upload-time = "2024-10-16T01:39:48.8Z" },
- { url = "https://files.pythonhosted.org/packages/21/7f/3a47e022a97b017ffb73351a1061e4401bcb5aa4fc0162d04f4e5452e4fc/pyodbc-5.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:b1f5686b142759c5b2bdbeaa0692622c2ebb1f10780eb3c174b85f5607fbcf55", size = 69485, upload-time = "2024-10-16T01:39:49.732Z" },
- { url = "https://files.pythonhosted.org/packages/90/be/e5f8022ec57a7ea6aa3717a3f307a44c3b012fce7ad6ec91aad3e2a56978/pyodbc-5.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:26844d780045bbc3514d5c2f0d89e7fda7df7db0bd24292eb6902046f5730885", size = 72982, upload-time = "2024-10-16T01:39:50.738Z" },
- { url = "https://files.pythonhosted.org/packages/5c/0e/71111e4f53936b0b99731d9b6acfc8fc95660533a1421447a63d6e519112/pyodbc-5.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:26d2d8fd53b71204c755abc53b0379df4e23fd9a40faf211e1cb87e8a32470f0", size = 72515, upload-time = "2024-10-16T01:39:51.86Z" },
- { url = "https://files.pythonhosted.org/packages/a5/09/3c06bbc1ebb9ae15f53cefe10774809b67da643883287ba1c44ba053816a/pyodbc-5.2.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a27996b6d27e275dfb5fe8a34087ba1cacadfd1439e636874ef675faea5149d9", size = 347470, upload-time = "2024-10-16T01:39:53.594Z" },
- { url = "https://files.pythonhosted.org/packages/a4/35/1c7efd4665e7983169d20175014f68578e0edfcbc4602b0bafcefa522c4a/pyodbc-5.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eaf42c4bd323b8fd01f1cd900cca2d09232155f9b8f0b9bcd0be66763588ce64", size = 353025, upload-time = "2024-10-16T01:39:55.124Z" },
- { url = "https://files.pythonhosted.org/packages/6d/c9/736d07fa33572abdc50d858fd9e527d2c8281f3acbb90dff4999a3662edd/pyodbc-5.2.0-cp313-cp313-win32.whl", hash = "sha256:207f16b7e9bf09c591616429ebf2b47127e879aad21167ac15158910dc9bbcda", size = 63052, upload-time = "2024-10-16T01:39:56.565Z" },
- { url = "https://files.pythonhosted.org/packages/73/2a/3219c8b7fa3788fc9f27b5fc2244017223cf070e5ab370f71c519adf9120/pyodbc-5.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:96d3127f28c0dacf18da7ae009cd48eac532d3dcc718a334b86a3c65f6a5ef5c", size = 69486, upload-time = "2024-10-16T01:39:57.57Z" },
- { url = "https://files.pythonhosted.org/packages/7c/1a/bec4dd9f65a7c0c1a75641119351f0f402c721bbcea3c4eb684868259467/pyodbc-5.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9e8f4ee2c523bbe85124540ffad62a3b62ae481f012e390ef93e0602b6302e5e", size = 72440, upload-time = "2024-10-16T01:40:05.42Z" },
- { url = "https://files.pythonhosted.org/packages/df/2f/62cce82e4547dc8c4ac3174403e24ed31bdb10bb69ad30e1bb362b960877/pyodbc-5.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:057b8ede91b21d9f0ef58210d1ca1aad704e641ca68ac6b02f109d86b61d7402", size = 71902, upload-time = "2024-10-16T01:40:06.379Z" },
- { url = "https://files.pythonhosted.org/packages/f0/1a/54d9595f0471c15b1de4766ec3436763aeef980740d484d629afa778c506/pyodbc-5.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f0ecbc7067467df95c9b8bd38fb2682c4a13a3402d77dccaddf1e145cea8cc0", size = 329596, upload-time = "2024-10-16T01:40:07.948Z" },
- { url = "https://files.pythonhosted.org/packages/2d/3a/88bc3bb8c15aefaf98bfadd51dae2fe492486daeb04911d8cf0a6d8dd884/pyodbc-5.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26b7f8324fa01c09fe4843ad8adb0b131299ef263a1fb9e63830c9cd1d5c45e4", size = 333575, upload-time = "2024-10-16T01:40:09.898Z" },
- { url = "https://files.pythonhosted.org/packages/60/75/aedf6d10f66b22302dc3f0181cbef0cc5789f2c2a658343f10ae72f51190/pyodbc-5.2.0-cp39-cp39-win32.whl", hash = "sha256:600ef6f562f609f5612ffaa8a93827249150aa3030c867937c87b24a1608967e", size = 62379, upload-time = "2024-10-16T01:40:11.412Z" },
- { url = "https://files.pythonhosted.org/packages/d9/9c/b1e367b07904a52f22b8707979bcbda1b5a6056c46e67e0a66241d8138aa/pyodbc-5.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:b77556349746fb90416a48bd114cd7323f7e2559a4b263dada935f9b406ba59b", size = 68951, upload-time = "2024-10-16T01:40:12.374Z" },
-]
-
-[[package]]
-name = "pyopenssl"
-version = "25.1.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "cryptography" },
- { name = "typing-extensions", marker = "python_full_version < '3.13'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/04/8c/cd89ad05804f8e3c17dea8f178c3f40eeab5694c30e0c9f5bcd49f576fc3/pyopenssl-25.1.0.tar.gz", hash = "sha256:8d031884482e0c67ee92bf9a4d8cceb08d92aba7136432ffb0703c5280fc205b", size = 179937, upload-time = "2025-05-17T16:28:31.31Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/80/28/2659c02301b9500751f8d42f9a6632e1508aa5120de5e43042b8b30f8d5d/pyopenssl-25.1.0-py3-none-any.whl", hash = "sha256:2b11f239acc47ac2e5aca04fd7fa829800aeee22a2eb30d744572a157bd8a1ab", size = 56771, upload-time = "2025-05-17T16:28:29.197Z" },
-]
-
-[[package]]
-name = "pyparsing"
-version = "3.2.3"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/bb/22/f1129e69d94ffff626bdb5c835506b3a5b4f3d070f17ea295e12c2c6f60f/pyparsing-3.2.3.tar.gz", hash = "sha256:b9c13f1ab8b3b542f72e28f634bad4de758ab3ce4546e4301970ad6fa77c38be", size = 1088608, upload-time = "2025-03-25T05:01:28.114Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/05/e7/df2285f3d08fee213f2d041540fa4fc9ca6c2d44cf36d3a035bf2a8d2bcc/pyparsing-3.2.3-py3-none-any.whl", hash = "sha256:a749938e02d6fd0b59b356ca504a24982314bb090c383e3cf201c95ef7e2bfcf", size = 111120, upload-time = "2025-03-25T05:01:24.908Z" },
-]
-
-[[package]]
-name = "pyreadline3"
-version = "3.5.4"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/0f/49/4cea918a08f02817aabae639e3d0ac046fef9f9180518a3ad394e22da148/pyreadline3-3.5.4.tar.gz", hash = "sha256:8d57d53039a1c75adba8e50dd3d992b28143480816187ea5efbd5c78e6c885b7", size = 99839, upload-time = "2024-09-19T02:40:10.062Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/5a/dc/491b7661614ab97483abf2056be1deee4dc2490ecbf7bff9ab5cdbac86e1/pyreadline3-3.5.4-py3-none-any.whl", hash = "sha256:eaf8e6cc3c49bcccf145fc6067ba8643d1df34d604a1ec0eccbf7a18e6d3fae6", size = 83178, upload-time = "2024-09-19T02:40:08.598Z" },
-]
-
-[[package]]
-name = "pytest"
-version = "8.4.1"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "colorama", marker = "sys_platform == 'win32'" },
- { name = "exceptiongroup", marker = "python_full_version < '3.11'" },
- { name = "iniconfig" },
- { name = "packaging" },
- { name = "pluggy" },
- { name = "pygments" },
- { name = "tomli", marker = "python_full_version < '3.11'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/08/ba/45911d754e8eba3d5a841a5ce61a65a685ff1798421ac054f85aa8747dfb/pytest-8.4.1.tar.gz", hash = "sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c", size = 1517714, upload-time = "2025-06-18T05:48:06.109Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/29/16/c8a903f4c4dffe7a12843191437d7cd8e32751d5de349d45d3fe69544e87/pytest-8.4.1-py3-none-any.whl", hash = "sha256:539c70ba6fcead8e78eebbf1115e8b589e7565830d7d006a8723f19ac8a0afb7", size = 365474, upload-time = "2025-06-18T05:48:03.955Z" },
-]
-
-[[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, upload-time = "2024-03-01T18:36:20.211Z" }
-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, upload-time = "2024-03-01T18:36:18.57Z" },
-]
-
-[[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, upload-time = "2024-01-23T06:33:00.505Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/6a/3e/b68c118422ec867fa7ab88444e1274aa40681c606d59ac27de5a5588f082/python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a", size = 19863, upload-time = "2024-01-23T06:32:58.246Z" },
-]
-
-[[package]]
-name = "python-utils"
-version = "3.9.1"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "typing-extensions" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/13/4c/ef8b7b1046d65c1f18ca31e5235c7d6627ca2b3f389ab1d44a74d22f5cc9/python_utils-3.9.1.tar.gz", hash = "sha256:eb574b4292415eb230f094cbf50ab5ef36e3579b8f09e9f2ba74af70891449a0", size = 35403, upload-time = "2024-11-26T00:38:58.736Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/d4/69/31c82567719b34d8f6b41077732589104883771d182a9f4ff3e71430999a/python_utils-3.9.1-py2.py3-none-any.whl", hash = "sha256:0273d7363c7ad4b70999b2791d5ba6b55333d6f7a4e4c8b6b39fb82b5fab4613", size = 32078, upload-time = "2024-11-26T00:38:57.488Z" },
-]
-
-[[package]]
-name = "pytz"
-version = "2025.2"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/f8/bf/abbd3cdfb8fbc7fb3d4d38d320f2441b1e7cbe29be4f23797b4a2b5d8aac/pytz-2025.2.tar.gz", hash = "sha256:360b9e3dbb49a209c21ad61809c7fb453643e048b38924c765813546746e81c3", size = 320884, upload-time = "2025-03-25T02:25:00.538Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.py3-none-any.whl", hash = "sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00", size = 509225, upload-time = "2025-03-25T02:24:58.468Z" },
-]
-
-[[package]]
-name = "pywin32"
-version = "311"
-source = { registry = "https://pypi.org/simple" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/7b/40/44efbb0dfbd33aca6a6483191dae0716070ed99e2ecb0c53683f400a0b4f/pywin32-311-cp310-cp310-win32.whl", hash = "sha256:d03ff496d2a0cd4a5893504789d4a15399133fe82517455e78bad62efbb7f0a3", size = 8760432, upload-time = "2025-07-14T20:13:05.9Z" },
- { url = "https://files.pythonhosted.org/packages/5e/bf/360243b1e953bd254a82f12653974be395ba880e7ec23e3731d9f73921cc/pywin32-311-cp310-cp310-win_amd64.whl", hash = "sha256:797c2772017851984b97180b0bebe4b620bb86328e8a884bb626156295a63b3b", size = 9590103, upload-time = "2025-07-14T20:13:07.698Z" },
- { url = "https://files.pythonhosted.org/packages/57/38/d290720e6f138086fb3d5ffe0b6caa019a791dd57866940c82e4eeaf2012/pywin32-311-cp310-cp310-win_arm64.whl", hash = "sha256:0502d1facf1fed4839a9a51ccbcc63d952cf318f78ffc00a7e78528ac27d7a2b", size = 8778557, upload-time = "2025-07-14T20:13:11.11Z" },
- { url = "https://files.pythonhosted.org/packages/7c/af/449a6a91e5d6db51420875c54f6aff7c97a86a3b13a0b4f1a5c13b988de3/pywin32-311-cp311-cp311-win32.whl", hash = "sha256:184eb5e436dea364dcd3d2316d577d625c0351bf237c4e9a5fabbcfa5a58b151", size = 8697031, upload-time = "2025-07-14T20:13:13.266Z" },
- { url = "https://files.pythonhosted.org/packages/51/8f/9bb81dd5bb77d22243d33c8397f09377056d5c687aa6d4042bea7fbf8364/pywin32-311-cp311-cp311-win_amd64.whl", hash = "sha256:3ce80b34b22b17ccbd937a6e78e7225d80c52f5ab9940fe0506a1a16f3dab503", size = 9508308, upload-time = "2025-07-14T20:13:15.147Z" },
- { url = "https://files.pythonhosted.org/packages/44/7b/9c2ab54f74a138c491aba1b1cd0795ba61f144c711daea84a88b63dc0f6c/pywin32-311-cp311-cp311-win_arm64.whl", hash = "sha256:a733f1388e1a842abb67ffa8e7aad0e70ac519e09b0f6a784e65a136ec7cefd2", size = 8703930, upload-time = "2025-07-14T20:13:16.945Z" },
- { url = "https://files.pythonhosted.org/packages/e7/ab/01ea1943d4eba0f850c3c61e78e8dd59757ff815ff3ccd0a84de5f541f42/pywin32-311-cp312-cp312-win32.whl", hash = "sha256:750ec6e621af2b948540032557b10a2d43b0cee2ae9758c54154d711cc852d31", size = 8706543, upload-time = "2025-07-14T20:13:20.765Z" },
- { url = "https://files.pythonhosted.org/packages/d1/a8/a0e8d07d4d051ec7502cd58b291ec98dcc0c3fff027caad0470b72cfcc2f/pywin32-311-cp312-cp312-win_amd64.whl", hash = "sha256:b8c095edad5c211ff31c05223658e71bf7116daa0ecf3ad85f3201ea3190d067", size = 9495040, upload-time = "2025-07-14T20:13:22.543Z" },
- { url = "https://files.pythonhosted.org/packages/ba/3a/2ae996277b4b50f17d61f0603efd8253cb2d79cc7ae159468007b586396d/pywin32-311-cp312-cp312-win_arm64.whl", hash = "sha256:e286f46a9a39c4a18b319c28f59b61de793654af2f395c102b4f819e584b5852", size = 8710102, upload-time = "2025-07-14T20:13:24.682Z" },
- { url = "https://files.pythonhosted.org/packages/a5/be/3fd5de0979fcb3994bfee0d65ed8ca9506a8a1260651b86174f6a86f52b3/pywin32-311-cp313-cp313-win32.whl", hash = "sha256:f95ba5a847cba10dd8c4d8fefa9f2a6cf283b8b88ed6178fa8a6c1ab16054d0d", size = 8705700, upload-time = "2025-07-14T20:13:26.471Z" },
- { url = "https://files.pythonhosted.org/packages/e3/28/e0a1909523c6890208295a29e05c2adb2126364e289826c0a8bc7297bd5c/pywin32-311-cp313-cp313-win_amd64.whl", hash = "sha256:718a38f7e5b058e76aee1c56ddd06908116d35147e133427e59a3983f703a20d", size = 9494700, upload-time = "2025-07-14T20:13:28.243Z" },
- { url = "https://files.pythonhosted.org/packages/04/bf/90339ac0f55726dce7d794e6d79a18a91265bdf3aa70b6b9ca52f35e022a/pywin32-311-cp313-cp313-win_arm64.whl", hash = "sha256:7b4075d959648406202d92a2310cb990fea19b535c7f4a78d3f5e10b926eeb8a", size = 8709318, upload-time = "2025-07-14T20:13:30.348Z" },
- { url = "https://files.pythonhosted.org/packages/c9/31/097f2e132c4f16d99a22bfb777e0fd88bd8e1c634304e102f313af69ace5/pywin32-311-cp314-cp314-win32.whl", hash = "sha256:b7a2c10b93f8986666d0c803ee19b5990885872a7de910fc460f9b0c2fbf92ee", size = 8840714, upload-time = "2025-07-14T20:13:32.449Z" },
- { url = "https://files.pythonhosted.org/packages/90/4b/07c77d8ba0e01349358082713400435347df8426208171ce297da32c313d/pywin32-311-cp314-cp314-win_amd64.whl", hash = "sha256:3aca44c046bd2ed8c90de9cb8427f581c479e594e99b5c0bb19b29c10fd6cb87", size = 9656800, upload-time = "2025-07-14T20:13:34.312Z" },
- { url = "https://files.pythonhosted.org/packages/c0/d2/21af5c535501a7233e734b8af901574572da66fcc254cb35d0609c9080dd/pywin32-311-cp314-cp314-win_arm64.whl", hash = "sha256:a508e2d9025764a8270f93111a970e1d0fbfc33f4153b388bb649b7eec4f9b42", size = 8932540, upload-time = "2025-07-14T20:13:36.379Z" },
- { url = "https://files.pythonhosted.org/packages/59/42/b86689aac0cdaee7ae1c58d464b0ff04ca909c19bb6502d4973cdd9f9544/pywin32-311-cp39-cp39-win32.whl", hash = "sha256:aba8f82d551a942cb20d4a83413ccbac30790b50efb89a75e4f586ac0bb8056b", size = 8760837, upload-time = "2025-07-14T20:12:59.59Z" },
- { url = "https://files.pythonhosted.org/packages/9f/8a/1403d0353f8c5a2f0829d2b1c4becbf9da2f0a4d040886404fc4a5431e4d/pywin32-311-cp39-cp39-win_amd64.whl", hash = "sha256:e0c4cfb0621281fe40387df582097fd796e80430597cb9944f0ae70447bacd91", size = 9590187, upload-time = "2025-07-14T20:13:01.419Z" },
- { url = "https://files.pythonhosted.org/packages/60/22/e0e8d802f124772cec9c75430b01a212f86f9de7546bda715e54140d5aeb/pywin32-311-cp39-cp39-win_arm64.whl", hash = "sha256:62ea666235135fee79bb154e695f3ff67370afefd71bd7fea7512fc70ef31e3d", size = 8778162, upload-time = "2025-07-14T20:13:03.544Z" },
-]
-
-[[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, upload-time = "2024-08-06T20:33:50.674Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/9b/95/a3fac87cb7158e231b5a6012e438c647e1a87f09f8e0d123acec8ab8bf71/PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086", size = 184199, upload-time = "2024-08-06T20:31:40.178Z" },
- { url = "https://files.pythonhosted.org/packages/c7/7a/68bd47624dab8fd4afbfd3c48e3b79efe09098ae941de5b58abcbadff5cb/PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf", size = 171758, upload-time = "2024-08-06T20:31:42.173Z" },
- { url = "https://files.pythonhosted.org/packages/49/ee/14c54df452143b9ee9f0f29074d7ca5516a36edb0b4cc40c3f280131656f/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237", size = 718463, upload-time = "2024-08-06T20:31:44.263Z" },
- { url = "https://files.pythonhosted.org/packages/4d/61/de363a97476e766574650d742205be468921a7b532aa2499fcd886b62530/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b", size = 719280, upload-time = "2024-08-06T20:31:50.199Z" },
- { url = "https://files.pythonhosted.org/packages/6b/4e/1523cb902fd98355e2e9ea5e5eb237cbc5f3ad5f3075fa65087aa0ecb669/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed", size = 751239, upload-time = "2024-08-06T20:31:52.292Z" },
- { url = "https://files.pythonhosted.org/packages/b7/33/5504b3a9a4464893c32f118a9cc045190a91637b119a9c881da1cf6b7a72/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180", size = 695802, upload-time = "2024-08-06T20:31:53.836Z" },
- { url = "https://files.pythonhosted.org/packages/5c/20/8347dcabd41ef3a3cdc4f7b7a2aff3d06598c8779faa189cdbf878b626a4/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68", size = 720527, upload-time = "2024-08-06T20:31:55.565Z" },
- { url = "https://files.pythonhosted.org/packages/be/aa/5afe99233fb360d0ff37377145a949ae258aaab831bde4792b32650a4378/PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99", size = 144052, upload-time = "2024-08-06T20:31:56.914Z" },
- { url = "https://files.pythonhosted.org/packages/b5/84/0fa4b06f6d6c958d207620fc60005e241ecedceee58931bb20138e1e5776/PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e", size = 161774, upload-time = "2024-08-06T20:31:58.304Z" },
- { url = "https://files.pythonhosted.org/packages/f8/aa/7af4e81f7acba21a4c6be026da38fd2b872ca46226673c89a758ebdc4fd2/PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", size = 184612, upload-time = "2024-08-06T20:32:03.408Z" },
- { url = "https://files.pythonhosted.org/packages/8b/62/b9faa998fd185f65c1371643678e4d58254add437edb764a08c5a98fb986/PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", size = 172040, upload-time = "2024-08-06T20:32:04.926Z" },
- { url = "https://files.pythonhosted.org/packages/ad/0c/c804f5f922a9a6563bab712d8dcc70251e8af811fce4524d57c2c0fd49a4/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", size = 736829, upload-time = "2024-08-06T20:32:06.459Z" },
- { url = "https://files.pythonhosted.org/packages/51/16/6af8d6a6b210c8e54f1406a6b9481febf9c64a3109c541567e35a49aa2e7/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317", size = 764167, upload-time = "2024-08-06T20:32:08.338Z" },
- { url = "https://files.pythonhosted.org/packages/75/e4/2c27590dfc9992f73aabbeb9241ae20220bd9452df27483b6e56d3975cc5/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85", size = 762952, upload-time = "2024-08-06T20:32:14.124Z" },
- { url = "https://files.pythonhosted.org/packages/9b/97/ecc1abf4a823f5ac61941a9c00fe501b02ac3ab0e373c3857f7d4b83e2b6/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4", size = 735301, upload-time = "2024-08-06T20:32:16.17Z" },
- { url = "https://files.pythonhosted.org/packages/45/73/0f49dacd6e82c9430e46f4a027baa4ca205e8b0a9dce1397f44edc23559d/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", size = 756638, upload-time = "2024-08-06T20:32:18.555Z" },
- { url = "https://files.pythonhosted.org/packages/22/5f/956f0f9fc65223a58fbc14459bf34b4cc48dec52e00535c79b8db361aabd/PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", size = 143850, upload-time = "2024-08-06T20:32:19.889Z" },
- { url = "https://files.pythonhosted.org/packages/ed/23/8da0bbe2ab9dcdd11f4f4557ccaf95c10b9811b13ecced089d43ce59c3c8/PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", size = 161980, upload-time = "2024-08-06T20:32:21.273Z" },
- { 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, upload-time = "2024-08-06T20:32:25.131Z" },
- { 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, upload-time = "2024-08-06T20:32:26.511Z" },
- { 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, upload-time = "2024-08-06T20:32:28.363Z" },
- { 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, upload-time = "2024-08-06T20:32:30.058Z" },
- { 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, upload-time = "2024-08-06T20:32:31.881Z" },
- { 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, upload-time = "2024-08-06T20:32:37.083Z" },
- { 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, upload-time = "2024-08-06T20:32:38.898Z" },
- { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591, upload-time = "2024-08-06T20:32:40.241Z" },
- { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338, upload-time = "2024-08-06T20:32:41.93Z" },
- { 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, upload-time = "2024-08-06T20:32:43.4Z" },
- { 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, upload-time = "2024-08-06T20:32:44.801Z" },
- { 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, upload-time = "2024-08-06T20:32:46.432Z" },
- { 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, upload-time = "2024-08-06T20:32:51.188Z" },
- { 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, upload-time = "2024-08-06T20:32:53.019Z" },
- { 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, upload-time = "2024-08-06T20:32:54.708Z" },
- { 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, upload-time = "2024-08-06T20:32:56.985Z" },
- { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527, upload-time = "2024-08-06T20:33:03.001Z" },
- { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446, upload-time = "2024-08-06T20:33:04.33Z" },
- { url = "https://files.pythonhosted.org/packages/65/d8/b7a1db13636d7fb7d4ff431593c510c8b8fca920ade06ca8ef20015493c5/PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d", size = 184777, upload-time = "2024-08-06T20:33:25.896Z" },
- { url = "https://files.pythonhosted.org/packages/0a/02/6ec546cd45143fdf9840b2c6be8d875116a64076218b61d68e12548e5839/PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f", size = 172318, upload-time = "2024-08-06T20:33:27.212Z" },
- { url = "https://files.pythonhosted.org/packages/0e/9a/8cc68be846c972bda34f6c2a93abb644fb2476f4dcc924d52175786932c9/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290", size = 720891, upload-time = "2024-08-06T20:33:28.974Z" },
- { url = "https://files.pythonhosted.org/packages/e9/6c/6e1b7f40181bc4805e2e07f4abc10a88ce4648e7e95ff1abe4ae4014a9b2/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12", size = 722614, upload-time = "2024-08-06T20:33:34.157Z" },
- { url = "https://files.pythonhosted.org/packages/3d/32/e7bd8535d22ea2874cef6a81021ba019474ace0d13a4819c2a4bce79bd6a/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19", size = 737360, upload-time = "2024-08-06T20:33:35.84Z" },
- { url = "https://files.pythonhosted.org/packages/d7/12/7322c1e30b9be969670b672573d45479edef72c9a0deac3bb2868f5d7469/PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e", size = 699006, upload-time = "2024-08-06T20:33:37.501Z" },
- { url = "https://files.pythonhosted.org/packages/82/72/04fcad41ca56491995076630c3ec1e834be241664c0c09a64c9a2589b507/PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725", size = 723577, upload-time = "2024-08-06T20:33:39.389Z" },
- { url = "https://files.pythonhosted.org/packages/ed/5e/46168b1f2757f1fcd442bc3029cd8767d88a98c9c05770d8b420948743bb/PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631", size = 144593, upload-time = "2024-08-06T20:33:46.63Z" },
- { url = "https://files.pythonhosted.org/packages/19/87/5124b1c1f2412bb95c59ec481eaf936cd32f0fe2a7b16b97b81c4c017a6a/PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8", size = 162312, upload-time = "2024-08-06T20:33:49.073Z" },
-]
-
-[[package]]
-name = "referencing"
-version = "0.36.2"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "attrs" },
- { name = "rpds-py" },
- { name = "typing-extensions", marker = "python_full_version < '3.13'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/2f/db/98b5c277be99dd18bfd91dd04e1b759cad18d1a338188c936e92f921c7e2/referencing-0.36.2.tar.gz", hash = "sha256:df2e89862cd09deabbdba16944cc3f10feb6b3e6f18e902f7cc25609a34775aa", size = 74744, upload-time = "2025-01-25T08:48:16.138Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/c1/b1/3baf80dc6d2b7bc27a95a67752d0208e410351e3feb4eb78de5f77454d8d/referencing-0.36.2-py3-none-any.whl", hash = "sha256:e8699adbbf8b5c7de96d8ffa0eb5c158b3beafce084968e2ea8bb08c6794dcd0", size = 26775, upload-time = "2025-01-25T08:48:14.241Z" },
-]
-
-[[package]]
-name = "regex"
-version = "2025.8.29"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/e4/10/2d333227cf5198eb3252f2d50c8ade5cd2015f11c22403f0c9e3d529e81a/regex-2025.8.29.tar.gz", hash = "sha256:731ddb27a0900fa227dfba976b4efccec8c1c6fba147829bb52e71d49e91a5d7", size = 400817, upload-time = "2025-08-29T22:43:36.985Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/a4/44/b29ab748d9a8fddd4b6165f7a78e95bcfc7ce73b777cd9f5843a7c9c0326/regex-2025.8.29-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a367dbb66842a08744f49c64ba1aab23e4cbcc924bae8ef40870f2c51d6cb240", size = 484656, upload-time = "2025-08-29T22:40:38.918Z" },
- { url = "https://files.pythonhosted.org/packages/fd/8e/ddca226a60d0b0002aced9f1f7b08b651a22575326e3b775e124922a6d9a/regex-2025.8.29-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:090d20a6f308c1cd3c33824e892666089d9719ff88e139d4b63623e881d3945c", size = 289363, upload-time = "2025-08-29T22:40:42.61Z" },
- { url = "https://files.pythonhosted.org/packages/1e/cf/036d79ef8a8ad94ec921afaa4ac399ba8856df7d0a774a8a9472ba4b6712/regex-2025.8.29-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:86e7ee69fdc9daf6aa98693b0db27a76e3d960c80d87c695af262c2608ccfc6a", size = 286006, upload-time = "2025-08-29T22:40:44.645Z" },
- { url = "https://files.pythonhosted.org/packages/35/5c/90a965e4f1332f0e944dd7eff57d9e8b803f80bc2220dc97aed4869f88c2/regex-2025.8.29-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:50628bc413193041838001b3926570629369d675b92badd6962c402aa09ed4c4", size = 780435, upload-time = "2025-08-29T22:40:46.739Z" },
- { url = "https://files.pythonhosted.org/packages/b0/21/ef1e15ef2188d40b67f48d99bdf452d0f4e0c48246a137840c6302dcb169/regex-2025.8.29-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:fadf22d84901f1b6cc6b27439d98688a33cefb83e70c885791c2c27524907ed4", size = 849251, upload-time = "2025-08-29T22:40:48.547Z" },
- { url = "https://files.pythonhosted.org/packages/7c/29/fbbff8f0285a1a8b014d962d8b5b14803aa52c78d79555d45b5d5c713cf2/regex-2025.8.29-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e3948db57ebe3c4bfb7e05765411ce6186820cafa27e5c737d72dbc5249010b3", size = 897295, upload-time = "2025-08-29T22:40:51.751Z" },
- { url = "https://files.pythonhosted.org/packages/96/f0/4bcc714f251e991e13bcc462af25b85ec1f300eeface928f8b0d744be70e/regex-2025.8.29-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0c42fbffe25ac6291f8dd00176d1916165550aa649d14e9c4668d6a3d6a5c900", size = 789904, upload-time = "2025-08-29T22:40:53.154Z" },
- { url = "https://files.pythonhosted.org/packages/e0/36/6f1d93acf9d96f0754669fcd5348f32824ffd3efb54695afa72bc84d862b/regex-2025.8.29-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d1f3498dcc96266b8db76512ffb2432bab2587df5e8ebfdceba5e737378e2bd1", size = 780740, upload-time = "2025-08-29T22:40:54.91Z" },
- { url = "https://files.pythonhosted.org/packages/35/75/e5a32207a38608e390e60a031524e5da27ad9480e1ec504ad66335d4d85e/regex-2025.8.29-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:2dadb4ecaad42562771697685a381e3f723bd4d522e357c07ae4a541ebf5753c", size = 773586, upload-time = "2025-08-29T22:40:56.764Z" },
- { url = "https://files.pythonhosted.org/packages/cb/ee/6ff1375398b101f9e132277220551db213db0d72f82018e206353d3b3e59/regex-2025.8.29-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:bc94bccb0482a1eceb34961e3c46e25a3746633fa19f93c93a42ff4b231ee6c3", size = 844064, upload-time = "2025-08-29T22:40:58.442Z" },
- { url = "https://files.pythonhosted.org/packages/17/78/6aca9854aebeaf7707e07d4426c15f861dd910bd64f1c41dd6417feb8746/regex-2025.8.29-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:96adc63fd63c05e2feb9c6b8a7212e2b9f52ccb1fa1f18eaed4f9e0ac2cbd186", size = 834749, upload-time = "2025-08-29T22:41:00.77Z" },
- { url = "https://files.pythonhosted.org/packages/50/d9/07d7361028c87aac0a0cdcbf83faf2e87518b6cc88ecb20aa0586076cea8/regex-2025.8.29-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:145fb4ca5a85e26c330b464fc71bbe0e92523ec5d295c6de9a1e31b06ebccf25", size = 778495, upload-time = "2025-08-29T22:41:02.618Z" },
- { url = "https://files.pythonhosted.org/packages/db/76/30f00296af393de079f86768a5040d1e857316d088c137de1d94269898aa/regex-2025.8.29-cp310-cp310-win32.whl", hash = "sha256:119a0e930916bb26fe028ef5098c6cad66d7a298560cacbc6942e834580dfba5", size = 264074, upload-time = "2025-08-29T22:41:05.261Z" },
- { url = "https://files.pythonhosted.org/packages/20/53/11149800770db8f45b9712571c47cb629f0bc8f76f32e529a7c7709c8434/regex-2025.8.29-cp310-cp310-win_amd64.whl", hash = "sha256:e8f709146e0f3dafdb4315884de1490ab59f1b93ecf7f9c6c8b0f655f437e593", size = 276099, upload-time = "2025-08-29T22:41:06.635Z" },
- { url = "https://files.pythonhosted.org/packages/24/29/d43a2f6786987784d26d6cfd9818086cfd30fa398446a729191b752a4583/regex-2025.8.29-cp310-cp310-win_arm64.whl", hash = "sha256:dc12259599d953bc25bc01f19b056b9115a96cd3cfe05f154d4570c9649800b0", size = 268428, upload-time = "2025-08-29T22:41:08.489Z" },
- { url = "https://files.pythonhosted.org/packages/ef/a2/e9b9ce5407af9147dc39a7de4f161fd72804c095ea398ab472e8dbc65533/regex-2025.8.29-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:156f711019968ffb3512723a38b06d94d379675c296bdb6104d1abb6e57374c6", size = 484663, upload-time = "2025-08-29T22:41:10.425Z" },
- { url = "https://files.pythonhosted.org/packages/f1/7c/5b2cf5f1350c1c218542fb0be89cf28d8375ebe240cb5769f108325eb285/regex-2025.8.29-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9082c0db8d43c696fac70b5b0592934f21533940f0118239b5c32fa23e51ed1a", size = 289365, upload-time = "2025-08-29T22:41:14.439Z" },
- { url = "https://files.pythonhosted.org/packages/1c/27/44733d2aa3b0c9532580872e9ed2df6a86fe7b975b75dc1f1733f6751e55/regex-2025.8.29-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9b3535b9a69a818735ebac392876dae4b215fe28c13b145353a2dac468ebae16", size = 286007, upload-time = "2025-08-29T22:41:16.243Z" },
- { url = "https://files.pythonhosted.org/packages/b9/ac/2d4f6904422b95f22d1548d8655b288837f3218b54853c6050de61a87b7e/regex-2025.8.29-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0c460628f6098cf8916b2d62fb39a37a39e49cca0279ac301ff9d94f7e75033e", size = 792412, upload-time = "2025-08-29T22:41:18.618Z" },
- { url = "https://files.pythonhosted.org/packages/a1/61/8f67415c0ad59abf8f4dd24ad9de504eb37c363318f757be35c42b537d66/regex-2025.8.29-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8dad3ce46390fe3d81ae1c131e29179f010925fa164e15b918fb037effdb7ad9", size = 858682, upload-time = "2025-08-29T22:41:21.519Z" },
- { url = "https://files.pythonhosted.org/packages/fb/31/c3552278e507ab255c51dce4dda0072252e78c801a16697085e71595b1c7/regex-2025.8.29-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f89e5beb3012d3c36c526fd4af163ada24011a0b417378f726b17c2fb382a35d", size = 905855, upload-time = "2025-08-29T22:41:23.367Z" },
- { url = "https://files.pythonhosted.org/packages/ab/84/5150fdffe83df17a7b869930c06d8007b890be3fdf6eb509b849431cabeb/regex-2025.8.29-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:40eeff06bbcfa69201b60488f3f3aa38ad3c92c7c0ab2cfc7c9599abfdf24262", size = 798943, upload-time = "2025-08-29T22:41:25.511Z" },
- { url = "https://files.pythonhosted.org/packages/89/bc/695f94a6fada1838adc75312512843f8d9d94eda71c253958fb40bba5083/regex-2025.8.29-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d7a9bc68610d22735b6ac01a3c3ef5b03d9303a18bd3e2249340213389f273dc", size = 781859, upload-time = "2025-08-29T22:41:27.178Z" },
- { url = "https://files.pythonhosted.org/packages/11/8e/641b228837f551c129bc03005a158c48aebb353a1f6a34dfcea025b5e4bc/regex-2025.8.29-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e785e40f7edfc19ff0b81b27f25eefdb0251cfd2ac4a9fa1eea03f5129e93758", size = 852914, upload-time = "2025-08-29T22:41:29.292Z" },
- { url = "https://files.pythonhosted.org/packages/0c/49/b8d55dffd138369ee8378830b3bad4f7b815517df5ad16212031521f966f/regex-2025.8.29-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:ba1deae2ceaa0b181ac9fd4cb8f04d6ba1494f3c8d053c8999f7c0dadb93497b", size = 844314, upload-time = "2025-08-29T22:41:31.244Z" },
- { url = "https://files.pythonhosted.org/packages/f7/73/48b6b616fdc1b6dc75a00c2670da7038400796c855b7bd0fbd4dad18c26c/regex-2025.8.29-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:15869e4f36de7091342e1dae90216aafa3746e3a069f30b34503a36931036f95", size = 787215, upload-time = "2025-08-29T22:41:33.315Z" },
- { url = "https://files.pythonhosted.org/packages/65/af/38af20de8ea862c5275da67d5a0e63023a92cc5df344ad9a80fc1fcd448e/regex-2025.8.29-cp311-cp311-win32.whl", hash = "sha256:aef62e0b08b0e3c2616783a9f75a02f001254695a0a1d28b829dc9fb6a3603e4", size = 264088, upload-time = "2025-08-29T22:41:35.263Z" },
- { url = "https://files.pythonhosted.org/packages/84/d9/f765e5d9eaaa67e10267662002aea786334176c2b22066437df6d73a6424/regex-2025.8.29-cp311-cp311-win_amd64.whl", hash = "sha256:fd347592a4811ba1d246f99fb53db82a1898a5aebb511281ac0c2d81632e1789", size = 276119, upload-time = "2025-08-29T22:41:37.933Z" },
- { url = "https://files.pythonhosted.org/packages/87/cd/44da9fae9a0c1af09f7171facc8d6313b1cbdfeea9f3526607495a28bdd7/regex-2025.8.29-cp311-cp311-win_arm64.whl", hash = "sha256:d93801012bb23901df403ae0adf528abfd50041c9e1136a303937d45c14466e0", size = 268429, upload-time = "2025-08-29T22:41:39.571Z" },
- { url = "https://files.pythonhosted.org/packages/e3/a0/8c37d276a80ffda94f7e019e50cc88f898015512c7f104e49f1a0a6d3c59/regex-2025.8.29-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:dd61f18dc4446bc3a2904559a61f32e98091cef7fb796e06fa35b9bfefe4c0c5", size = 485565, upload-time = "2025-08-29T22:41:41.069Z" },
- { url = "https://files.pythonhosted.org/packages/5d/34/baf5963bec36ac250fa242f0f0e7670f013de5004db6caa31c872981df42/regex-2025.8.29-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f21b416be10a8348a7313ba8c610569a1ab4bf8ec70731750540842a4551cd3d", size = 290073, upload-time = "2025-08-29T22:41:42.686Z" },
- { url = "https://files.pythonhosted.org/packages/24/29/c5c18143cd60b736d7ff8acece126118fe5649f45a7a8db18e308f5f813d/regex-2025.8.29-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:008947a7fa92f4cb3b28201c9aa7becc0a44c31a7c2fcb934356e1877baccc09", size = 286144, upload-time = "2025-08-29T22:41:44.364Z" },
- { url = "https://files.pythonhosted.org/packages/86/7c/0d90b687d2a33fe28b201f85ddfde6b378bf41677aedbe23eb7dc79385aa/regex-2025.8.29-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e78ab1b3e68b890d7ebd69218cfbfe4a09dc00b8a47be8648510b81b932d55ff", size = 797417, upload-time = "2025-08-29T22:41:47.224Z" },
- { url = "https://files.pythonhosted.org/packages/fb/67/c391c899e5ef274c4dd4ede029ffb853ddf5ba77aa251be02cfe3810574c/regex-2025.8.29-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a848368797515bc141d3fad5fd2d81bf9e8a6a22d9ac1a4be4690dd22e997854", size = 862630, upload-time = "2025-08-29T22:41:48.891Z" },
- { url = "https://files.pythonhosted.org/packages/08/20/ae749a68da3496a133836c8724649bd2e004fc176c7c6647d9cb269cc975/regex-2025.8.29-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8eaf3ea6631f804efcf0f5bd0e4ab62ba984fd9b70e3aef44b05cc6b951cc728", size = 910837, upload-time = "2025-08-29T22:41:50.592Z" },
- { url = "https://files.pythonhosted.org/packages/e2/80/bc4244ec79fba4185fd3a29d79f77f79b3b0dc12ee426687501b0b077e2a/regex-2025.8.29-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4561aeb36b0bf3bb44826e4b61a80c6ace0d8839bf4914d78f061f9ba61444b4", size = 801968, upload-time = "2025-08-29T22:41:54.239Z" },
- { url = "https://files.pythonhosted.org/packages/ef/bd/a2d75042bb1d3c9997e22bc0051cb9791a405589d6293c874f7c2ba487e7/regex-2025.8.29-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:93e077d1fbd24033fa427eab43d80ad47e449d25700cda78e8cac821a30090bf", size = 786626, upload-time = "2025-08-29T22:41:56.158Z" },
- { url = "https://files.pythonhosted.org/packages/24/ab/19cec75bf7d335cc7595d4857591455de118f6bfb563e6731c31f4fe33c3/regex-2025.8.29-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:d92379e53d782bdb773988687300e3bccb91ad38157b754b04b1857aaeea16a3", size = 856532, upload-time = "2025-08-29T22:41:58.057Z" },
- { url = "https://files.pythonhosted.org/packages/b6/3d/517cd0b0f4b8330164d03ef0eafdd61ee839f82b891fcd8c571d5c727117/regex-2025.8.29-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:d41726de2040c2a487bbac70fdd6e3ff2f1aa47dc91f0a29f6955a6dfa0f06b6", size = 848977, upload-time = "2025-08-29T22:42:00.346Z" },
- { url = "https://files.pythonhosted.org/packages/ae/fc/b57e2644d87d038d7302f359f4042bf7092bd8259a3ae999adf236e6fbc0/regex-2025.8.29-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1915dfda52bd4d466f3a66b66988db1f647ee1d9c605858640ceeb779cffd908", size = 788112, upload-time = "2025-08-29T22:42:02.008Z" },
- { url = "https://files.pythonhosted.org/packages/a9/2f/70737feddbd33ec9f3f0cb8b38e7fc89304eccc80fd693d79a6f336e2282/regex-2025.8.29-cp312-cp312-win32.whl", hash = "sha256:e2ef0087ad6949918836f215480a9331f6c59ad54912a9a412f08ab1c9ccbc98", size = 264487, upload-time = "2025-08-29T22:42:04.401Z" },
- { url = "https://files.pythonhosted.org/packages/2f/f5/8832d05ecc5a7f80043e7521ea55adfa2d9b9ac0e646474153e7e13722c2/regex-2025.8.29-cp312-cp312-win_amd64.whl", hash = "sha256:c15d361fe9800bf38ef69c2e0c4b8b961ae4ce2f076fcf4f28e1fc9ea127f55a", size = 275455, upload-time = "2025-08-29T22:42:06.312Z" },
- { url = "https://files.pythonhosted.org/packages/a5/f9/f10ae0c4e5e22db75dda155d83056e2b70c4e87b04ad9838723ff5057e90/regex-2025.8.29-cp312-cp312-win_arm64.whl", hash = "sha256:305577fab545e64fb84d9a24269aa3132dbe05e1d7fa74b3614e93ec598fe6e6", size = 268558, upload-time = "2025-08-29T22:42:08.062Z" },
- { url = "https://files.pythonhosted.org/packages/42/db/2f0e1fbca855f3c519f3f8198817d14a9569ca939bc0cc86efd4da196d3e/regex-2025.8.29-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:eed02e5c39f91268ea4ddf68ee19eed189d57c605530b7d32960f54325c52e7a", size = 485405, upload-time = "2025-08-29T22:42:10.138Z" },
- { url = "https://files.pythonhosted.org/packages/15/ed/52afe839607719750acc87d144ec3db699adb9c1f40ecb6fa9f3700437b6/regex-2025.8.29-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:630d5c7e0a490db2fee3c7b282c8db973abcbb036a6e4e6dc06c4270965852be", size = 290014, upload-time = "2025-08-29T22:42:12.38Z" },
- { url = "https://files.pythonhosted.org/packages/da/84/beb3becb129e41ae3e6bacd737aa751228ec0c17c707b9999648f050968c/regex-2025.8.29-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2206d3a30469e8fc8848139884168127f456efbaca8ae14809c26b98d2be15c6", size = 286059, upload-time = "2025-08-29T22:42:14.009Z" },
- { url = "https://files.pythonhosted.org/packages/44/31/74476ac68cd5ed46634683cba634ab0885e917624d620c5959f67835554b/regex-2025.8.29-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:394c492c398a9f9e17545e19f770c58b97e65963eedaa25bb879e80a03e2b327", size = 797490, upload-time = "2025-08-29T22:42:15.864Z" },
- { url = "https://files.pythonhosted.org/packages/3f/97/1a8d109f891c4af31f43295304a51b76bc7aef4ce6d7953e4832f86c85f0/regex-2025.8.29-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:db8b0e05af08ff38d78544950e844b5f159032b66dedda19b3f9b17297248be7", size = 862562, upload-time = "2025-08-29T22:42:17.557Z" },
- { url = "https://files.pythonhosted.org/packages/1b/a8/13d6ea4b8a0c7eed0e528dcb25cbdc3bc53e26b0928dc48d6c0381516c4a/regex-2025.8.29-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:cd7c1821eff911917c476d41030b422791ce282c23ee9e1b8f7681fd0993f1e4", size = 910790, upload-time = "2025-08-29T22:42:19.268Z" },
- { url = "https://files.pythonhosted.org/packages/10/b3/1c7320c1fdc6569a086949d2c5b7b742696098c28a6c83ca909b8d36d17b/regex-2025.8.29-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0b4d8a7f75da748a2d0c045600259f1899c9dd8dd9d3da1daa50bf534c3fa5ba", size = 802016, upload-time = "2025-08-29T22:42:21.268Z" },
- { url = "https://files.pythonhosted.org/packages/7a/b5/f3613b70a569b6309cd2a61ae869407b45cff25c9734f5ff179b416e9615/regex-2025.8.29-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5cd74545c32e0da0d489c2293101a82f4a1b88050c235e45509e4123017673b2", size = 786740, upload-time = "2025-08-29T22:42:23.538Z" },
- { url = "https://files.pythonhosted.org/packages/e0/8a/9f16babae23011acbd27f886c4817159508f4f3209bcfce4bc2b8f12f2ba/regex-2025.8.29-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:97b98ea38fc3c1034f3d7bd30288d2c5b3be8cdcd69e2061d1c86cb14644a27b", size = 856533, upload-time = "2025-08-29T22:42:26.055Z" },
- { url = "https://files.pythonhosted.org/packages/4d/d0/adca6eec8ed79541edadecf8b512d7a3960c2ba983d2e5baf68dbddd7a90/regex-2025.8.29-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:8decb26f271b989d612c5d99db5f8f741dcd63ece51c59029840070f5f9778bf", size = 849083, upload-time = "2025-08-29T22:42:27.762Z" },
- { url = "https://files.pythonhosted.org/packages/46/cc/37fddb2a17cefffb43b9dfd5f585a6cd6f90ee5b32c821886d0c0c3bc243/regex-2025.8.29-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:62141843d1ec079cd66604424af566e542e7e072b2d9e37165d414d2e6e271dd", size = 788177, upload-time = "2025-08-29T22:42:31.121Z" },
- { url = "https://files.pythonhosted.org/packages/f5/ea/413fe88ce5ac2418223434aa1603d92134b74deed6007dc6e4c37d83bbcd/regex-2025.8.29-cp313-cp313-win32.whl", hash = "sha256:dd23006c90d9ff0c2e4e5f3eaf8233dcefe45684f2acb330869ec5c2aa02b1fb", size = 264473, upload-time = "2025-08-29T22:42:32.706Z" },
- { url = "https://files.pythonhosted.org/packages/5a/73/d07bc1d1969e41bf1637a8aad4228da506747f4c94415ef03c534c7d68d6/regex-2025.8.29-cp313-cp313-win_amd64.whl", hash = "sha256:d41a71342819bdfe87c701f073a14ea4bd3f847333d696c7344e9ff3412b7f70", size = 275438, upload-time = "2025-08-29T22:42:34.35Z" },
- { url = "https://files.pythonhosted.org/packages/86/cd/2e05fc85ebee6fe6c5073c9b0c737a473c226422d75e93903810b247a9fe/regex-2025.8.29-cp313-cp313-win_arm64.whl", hash = "sha256:54018e66344d60b214f4aa151c046e0fa528221656f4f7eba5a787ccc7057312", size = 268553, upload-time = "2025-08-29T22:42:35.874Z" },
- { url = "https://files.pythonhosted.org/packages/2e/2d/2aa4b98231017994ea52d05c13997778af415f5d7faa7f90988a640dac44/regex-2025.8.29-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:c03308757831a8d89e7c007abb75d1d4c9fbca003b5fb32755d4475914535f08", size = 485447, upload-time = "2025-08-29T22:42:37.429Z" },
- { url = "https://files.pythonhosted.org/packages/b7/b4/ed3241bb99a0783fe650d8511924c7c43f704b720fab3e353393bea8c96a/regex-2025.8.29-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:0d4b71791975fc203e0e6c50db974abb23a8df30729c1ac4fd68c9f2bb8c9358", size = 289862, upload-time = "2025-08-29T22:42:39.71Z" },
- { url = "https://files.pythonhosted.org/packages/ba/f6/5237a7d0b2bd64bb216d06470549bc4cc33de57033772e3018708636a027/regex-2025.8.29-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:284fcd2dcb613e8b89b22a30cf42998c9a73ee360b8a24db8457d24f5c42282e", size = 286211, upload-time = "2025-08-29T22:42:41.266Z" },
- { url = "https://files.pythonhosted.org/packages/58/eb/05568fdc4028d1b339fb950fe6b92ade2613edd6423291939c8e29b21e8a/regex-2025.8.29-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b394b5157701b22cf63699c792bfeed65fbfeacbd94fea717a9e2036a51148ab", size = 797826, upload-time = "2025-08-29T22:42:42.911Z" },
- { url = "https://files.pythonhosted.org/packages/3d/2a/a3c1c209faa1f6a218e64c5a235e06f6f36c45b5aa924c6bf75241a996f7/regex-2025.8.29-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ea197ac22396faf5e70c87836bb89f94ed5b500e1b407646a4e5f393239611f1", size = 863338, upload-time = "2025-08-29T22:42:44.831Z" },
- { url = "https://files.pythonhosted.org/packages/dd/66/5e96f217662387742c0d9732e97129850bd3243e019309c1fbdcd62b5421/regex-2025.8.29-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:decd84f195c08b3d9d0297a7e310379aae13ca7e166473534508c81b95c74bba", size = 910176, upload-time = "2025-08-29T22:42:46.997Z" },
- { url = "https://files.pythonhosted.org/packages/fc/f2/975e77333267f9652bc2cc926382d8c9d86683eb84d1989459e644ac818b/regex-2025.8.29-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ebaf81f7344dbf1a2b383e35923648de8f78fb262cf04154c82853887ac3e684", size = 801784, upload-time = "2025-08-29T22:42:48.786Z" },
- { url = "https://files.pythonhosted.org/packages/75/d9/b25dbf9729b5a5958a804e91b376fe8e829ec10c0d7edb4b1ad91070132b/regex-2025.8.29-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d82fb8a97e5ed8f1d3ed7f8e0e7fe1760faa95846c0d38b314284dfdbe86b229", size = 786799, upload-time = "2025-08-29T22:42:50.868Z" },
- { url = "https://files.pythonhosted.org/packages/1d/0a/7f8de7ea41d7a3a21dfcb9dcea7b727fdde9e35d74a23e16ef5edcd68005/regex-2025.8.29-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:1dcec2448ed0062f63e82ca02d1d05f74d4127cb6a9d76a73df60e81298d380b", size = 857380, upload-time = "2025-08-29T22:42:52.992Z" },
- { url = "https://files.pythonhosted.org/packages/f8/40/494600424c394a507070b41fc0666ceaa7dccf62c3220a76833eb11de647/regex-2025.8.29-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:d0ffe4a3257a235f9d39b99c6f1bc53c7a4b11f28565726b1aa00a5787950d60", size = 848570, upload-time = "2025-08-29T22:42:54.857Z" },
- { url = "https://files.pythonhosted.org/packages/be/d0/6988feb7c15bb3df7b944a10b3b58fb238c94987c70a991ba87e3685e1cd/regex-2025.8.29-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5421a2d2026e8189500f12375cfd80a9a1914466d446edd28b37eb33c1953b39", size = 787926, upload-time = "2025-08-29T22:42:57.025Z" },
- { url = "https://files.pythonhosted.org/packages/98/16/d719b131b0577a2a975376b3e673fc7f89b9998d54753f0419d59d33b3a1/regex-2025.8.29-cp314-cp314-win32.whl", hash = "sha256:ceeeaab602978c8eac3b25b8707f21a69c0bcd179d9af72519da93ef3966158f", size = 269805, upload-time = "2025-08-29T22:42:59.241Z" },
- { url = "https://files.pythonhosted.org/packages/a5/b7/50d3bb5df25ae73e7aee186a2f1e4f1ed5e4d54006bdf5abd558c1ce9e7a/regex-2025.8.29-cp314-cp314-win_amd64.whl", hash = "sha256:5ba4f8b0d5b88c33fe4060e6def58001fd8334b03c7ce2126964fa8851ab5d1b", size = 278710, upload-time = "2025-08-29T22:43:00.84Z" },
- { url = "https://files.pythonhosted.org/packages/0f/34/c723ebe214c33000b53e0eebdc63ad3697d5611c7fa9b388eef2113a5e82/regex-2025.8.29-cp314-cp314-win_arm64.whl", hash = "sha256:7b4a3dc155984f09a55c64b90923cb136cd0dad21ca0168aba2382d90ea4c546", size = 271832, upload-time = "2025-08-29T22:43:02.777Z" },
- { url = "https://files.pythonhosted.org/packages/35/c7/6d2cfc16ad1d7cd5306d4eb7a40a176c4af77082e3470d77d326b05a72ea/regex-2025.8.29-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:4d6dbdfdb4de3a77d1b2f9ec6bded2e056081407923d69236e13457924cf5fd7", size = 484668, upload-time = "2025-08-29T22:43:04.736Z" },
- { url = "https://files.pythonhosted.org/packages/24/4e/982712f158e97bfef674d559dd14e44dbec15e1571e46ad921efa41d88c6/regex-2025.8.29-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3f747541fd1ad1dcf859ce221749a5d26d7dbe6d928efdd407c97a2d27c8f434", size = 289362, upload-time = "2025-08-29T22:43:07.225Z" },
- { url = "https://files.pythonhosted.org/packages/45/17/acd7b31fb2388567fbaa00b253d243a3f661616604e074d3d237a43c0579/regex-2025.8.29-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:90c37a24d9a809ff1898e74f3318a4e21f8bb3db9975a560fa3722e42c370285", size = 286007, upload-time = "2025-08-29T22:43:09.24Z" },
- { url = "https://files.pythonhosted.org/packages/c4/8e/b27adc3d1449f72eb8a3c67c84d1b1fe43123ab32f20dd8493f01517b4bb/regex-2025.8.29-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:470138c8882d66493969f45fad2f8e20f35e381b9f96a37f59a5ac786e653cf6", size = 779857, upload-time = "2025-08-29T22:43:11.888Z" },
- { url = "https://files.pythonhosted.org/packages/f0/89/d35f7915992dd2c171a60107f4f0feba8122b5244c9de6c609f194dffa68/regex-2025.8.29-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:dc8c7fc96c9eb18b6690c96ec9c8fb63ea2fa78c6df4258fd76b59d4fbf46645", size = 848911, upload-time = "2025-08-29T22:43:13.94Z" },
- { url = "https://files.pythonhosted.org/packages/81/cc/5893414a23cdc83767d45431175cd14db4beb0678e38e1b0e41be47ecef4/regex-2025.8.29-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:33a26d4b2dc639868d73b9ec4ff8a89eb295797170125e4d4810ad23228f93c8", size = 896723, upload-time = "2025-08-29T22:43:15.696Z" },
- { url = "https://files.pythonhosted.org/packages/f1/fd/35079103a56d3dbce5db73c04d49efeec53f8834262c64cc1db003ca6b39/regex-2025.8.29-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b839268539b44a965f3ed680fda6270337a05bd425cc80542e0c6808efdc9a7e", size = 789441, upload-time = "2025-08-29T22:43:17.646Z" },
- { url = "https://files.pythonhosted.org/packages/f8/1b/34e715bbe1299e60da29afbff25d12d4574fef7133a607140f02a45d4e0c/regex-2025.8.29-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:16b5ca6570c71b1ee61dd30f24a1944eb82a372364e37f58f9b9731636cc6ba9", size = 780102, upload-time = "2025-08-29T22:43:19.798Z" },
- { url = "https://files.pythonhosted.org/packages/22/b1/d5ab50bf719f2573076af67975cf5d2cac21fbfeb78049ce83f3fbe5b2e6/regex-2025.8.29-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:421b6ccd037ad551e1ef1bc31debc3a914b579c27c0807f35c85f13b0eccbff3", size = 773115, upload-time = "2025-08-29T22:43:22.668Z" },
- { url = "https://files.pythonhosted.org/packages/b0/d2/e47a6ca0b4e26fdae186c44799feaabfa4c8cba203cd24cc3e47897a1e8c/regex-2025.8.29-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:d8cb77df92d1a204a0c218d93c5fb14945e2a7b40da2d9f15b05c9ddae393b43", size = 843514, upload-time = "2025-08-29T22:43:25.312Z" },
- { url = "https://files.pythonhosted.org/packages/43/45/ed0ccec667f44e5d3e4a65051ba5d96326ba08d0be3ac2f8544564215a6f/regex-2025.8.29-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:dd7df4ae4ea0efe0d378535e9825bd20e3be8d57eb3d55291d8094d61c9ccd9e", size = 834128, upload-time = "2025-08-29T22:43:27.171Z" },
- { url = "https://files.pythonhosted.org/packages/78/0c/4a6d0d5c2b68f94e8efcace2493533002f5b8927726160d2b99975623769/regex-2025.8.29-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:348cbcdf2d9dd0d09f05a78218776a33779e95aa57d553065a00429a96c553d3", size = 777982, upload-time = "2025-08-29T22:43:29.418Z" },
- { url = "https://files.pythonhosted.org/packages/0b/a5/40d0ebb19e2fef65da401c0523efc9d0d1677af73a6c2ed51fd028b790d0/regex-2025.8.29-cp39-cp39-win32.whl", hash = "sha256:590de47e6c390a42e6bfb1bdbe2148456827a6b28464c6e387f51b4bbe1f83e2", size = 264108, upload-time = "2025-08-29T22:43:31.581Z" },
- { url = "https://files.pythonhosted.org/packages/50/d7/7b884decda73e186c4d4b5443f6b741e40cfe51d873df80bc5a45647a1e1/regex-2025.8.29-cp39-cp39-win_amd64.whl", hash = "sha256:df8deeb34e06c8ba196beabbcf2810d5ecd8cf71cfe69899e93806244610f7ae", size = 276188, upload-time = "2025-08-29T22:43:33.276Z" },
- { url = "https://files.pythonhosted.org/packages/62/33/8bdd193da393a64bf462da97152f88b40297b221c19d6ecf99b9c17404cf/regex-2025.8.29-cp39-cp39-win_arm64.whl", hash = "sha256:fbabdb18fdd1fc4b0740f4e6b3070d7f41f98a88b8c38cf1962b6dcb3e745e56", size = 268461, upload-time = "2025-08-29T22:43:35.008Z" },
-]
-
-[[package]]
-name = "requests"
-version = "2.32.5"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "certifi" },
- { name = "charset-normalizer" },
- { name = "idna" },
- { name = "urllib3", version = "1.26.20", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
- { name = "urllib3", version = "2.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" },
-]
-
-[[package]]
-name = "requests-file"
-version = "2.1.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "requests" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/72/97/bf44e6c6bd8ddbb99943baf7ba8b1a8485bcd2fe0e55e5708d7fee4ff1ae/requests_file-2.1.0.tar.gz", hash = "sha256:0f549a3f3b0699415ac04d167e9cb39bccfb730cb832b4d20be3d9867356e658", size = 6891, upload-time = "2024-05-21T16:28:00.24Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/d7/25/dd878a121fcfdf38f52850f11c512e13ec87c2ea72385933818e5b6c15ce/requests_file-2.1.0-py2.py3-none-any.whl", hash = "sha256:cf270de5a4c5874e84599fc5778303d496c10ae5e870bfa378818f35d21bda5c", size = 4244, upload-time = "2024-05-21T16:27:57.733Z" },
-]
-
-[[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, upload-time = "2023-05-01T04:11:33.229Z" }
-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, upload-time = "2023-05-01T04:11:28.427Z" },
-]
-
-[[package]]
-name = "rich"
-version = "13.9.4"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "markdown-it-py", version = "3.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
- { name = "markdown-it-py", version = "4.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
- { name = "pygments" },
- { name = "typing-extensions", marker = "python_full_version < '3.11'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/ab/3a/0316b28d0761c6734d6bc14e770d85506c986c85ffb239e688eeaab2c2bc/rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098", size = 223149, upload-time = "2024-11-01T16:43:57.873Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/19/71/39c7c0d87f8d4e6c020a393182060eaefeeae6c01dab6a84ec346f2567df/rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90", size = 242424, upload-time = "2024-11-01T16:43:55.817Z" },
-]
-
-[[package]]
-name = "rpds-py"
-version = "0.27.1"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/e9/dd/2c0cbe774744272b0ae725f44032c77bdcab6e8bcf544bffa3b6e70c8dba/rpds_py-0.27.1.tar.gz", hash = "sha256:26a1c73171d10b7acccbded82bf6a586ab8203601e565badc74bbbf8bc5a10f8", size = 27479, upload-time = "2025-08-27T12:16:36.024Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/a5/ed/3aef893e2dd30e77e35d20d4ddb45ca459db59cead748cad9796ad479411/rpds_py-0.27.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:68afeec26d42ab3b47e541b272166a0b4400313946871cba3ed3a4fc0cab1cef", size = 371606, upload-time = "2025-08-27T12:12:25.189Z" },
- { url = "https://files.pythonhosted.org/packages/6d/82/9818b443e5d3eb4c83c3994561387f116aae9833b35c484474769c4a8faf/rpds_py-0.27.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:74e5b2f7bb6fa38b1b10546d27acbacf2a022a8b5543efb06cfebc72a59c85be", size = 353452, upload-time = "2025-08-27T12:12:27.433Z" },
- { url = "https://files.pythonhosted.org/packages/99/c7/d2a110ffaaa397fc6793a83c7bd3545d9ab22658b7cdff05a24a4535cc45/rpds_py-0.27.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9024de74731df54546fab0bfbcdb49fae19159ecaecfc8f37c18d2c7e2c0bd61", size = 381519, upload-time = "2025-08-27T12:12:28.719Z" },
- { url = "https://files.pythonhosted.org/packages/5a/bc/e89581d1f9d1be7d0247eaef602566869fdc0d084008ba139e27e775366c/rpds_py-0.27.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:31d3ebadefcd73b73928ed0b2fd696f7fefda8629229f81929ac9c1854d0cffb", size = 394424, upload-time = "2025-08-27T12:12:30.207Z" },
- { url = "https://files.pythonhosted.org/packages/ac/2e/36a6861f797530e74bb6ed53495f8741f1ef95939eed01d761e73d559067/rpds_py-0.27.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b2e7f8f169d775dd9092a1743768d771f1d1300453ddfe6325ae3ab5332b4657", size = 523467, upload-time = "2025-08-27T12:12:31.808Z" },
- { url = "https://files.pythonhosted.org/packages/c4/59/c1bc2be32564fa499f988f0a5c6505c2f4746ef96e58e4d7de5cf923d77e/rpds_py-0.27.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d905d16f77eb6ab2e324e09bfa277b4c8e5e6b8a78a3e7ff8f3cdf773b4c013", size = 402660, upload-time = "2025-08-27T12:12:33.444Z" },
- { url = "https://files.pythonhosted.org/packages/0a/ec/ef8bf895f0628dd0a59e54d81caed6891663cb9c54a0f4bb7da918cb88cf/rpds_py-0.27.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50c946f048209e6362e22576baea09193809f87687a95a8db24e5fbdb307b93a", size = 384062, upload-time = "2025-08-27T12:12:34.857Z" },
- { url = "https://files.pythonhosted.org/packages/69/f7/f47ff154be8d9a5e691c083a920bba89cef88d5247c241c10b9898f595a1/rpds_py-0.27.1-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:3deab27804d65cd8289eb814c2c0e807c4b9d9916c9225e363cb0cf875eb67c1", size = 401289, upload-time = "2025-08-27T12:12:36.085Z" },
- { url = "https://files.pythonhosted.org/packages/3b/d9/ca410363efd0615814ae579f6829cafb39225cd63e5ea5ed1404cb345293/rpds_py-0.27.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8b61097f7488de4be8244c89915da8ed212832ccf1e7c7753a25a394bf9b1f10", size = 417718, upload-time = "2025-08-27T12:12:37.401Z" },
- { url = "https://files.pythonhosted.org/packages/e3/a0/8cb5c2ff38340f221cc067cc093d1270e10658ba4e8d263df923daa18e86/rpds_py-0.27.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8a3f29aba6e2d7d90528d3c792555a93497fe6538aa65eb675b44505be747808", size = 558333, upload-time = "2025-08-27T12:12:38.672Z" },
- { url = "https://files.pythonhosted.org/packages/6f/8c/1b0de79177c5d5103843774ce12b84caa7164dfc6cd66378768d37db11bf/rpds_py-0.27.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:dd6cd0485b7d347304067153a6dc1d73f7d4fd995a396ef32a24d24b8ac63ac8", size = 589127, upload-time = "2025-08-27T12:12:41.48Z" },
- { url = "https://files.pythonhosted.org/packages/c8/5e/26abb098d5e01266b0f3a2488d299d19ccc26849735d9d2b95c39397e945/rpds_py-0.27.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:6f4461bf931108c9fa226ffb0e257c1b18dc2d44cd72b125bec50ee0ab1248a9", size = 554899, upload-time = "2025-08-27T12:12:42.925Z" },
- { url = "https://files.pythonhosted.org/packages/de/41/905cc90ced13550db017f8f20c6d8e8470066c5738ba480d7ba63e3d136b/rpds_py-0.27.1-cp310-cp310-win32.whl", hash = "sha256:ee5422d7fb21f6a00c1901bf6559c49fee13a5159d0288320737bbf6585bd3e4", size = 217450, upload-time = "2025-08-27T12:12:44.813Z" },
- { url = "https://files.pythonhosted.org/packages/75/3d/6bef47b0e253616ccdf67c283e25f2d16e18ccddd38f92af81d5a3420206/rpds_py-0.27.1-cp310-cp310-win_amd64.whl", hash = "sha256:3e039aabf6d5f83c745d5f9a0a381d031e9ed871967c0a5c38d201aca41f3ba1", size = 228447, upload-time = "2025-08-27T12:12:46.204Z" },
- { url = "https://files.pythonhosted.org/packages/b5/c1/7907329fbef97cbd49db6f7303893bd1dd5a4a3eae415839ffdfb0762cae/rpds_py-0.27.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:be898f271f851f68b318872ce6ebebbc62f303b654e43bf72683dbdc25b7c881", size = 371063, upload-time = "2025-08-27T12:12:47.856Z" },
- { url = "https://files.pythonhosted.org/packages/11/94/2aab4bc86228bcf7c48760990273653a4900de89c7537ffe1b0d6097ed39/rpds_py-0.27.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:62ac3d4e3e07b58ee0ddecd71d6ce3b1637de2d373501412df395a0ec5f9beb5", size = 353210, upload-time = "2025-08-27T12:12:49.187Z" },
- { url = "https://files.pythonhosted.org/packages/3a/57/f5eb3ecf434342f4f1a46009530e93fd201a0b5b83379034ebdb1d7c1a58/rpds_py-0.27.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4708c5c0ceb2d034f9991623631d3d23cb16e65c83736ea020cdbe28d57c0a0e", size = 381636, upload-time = "2025-08-27T12:12:50.492Z" },
- { url = "https://files.pythonhosted.org/packages/ae/f4/ef95c5945e2ceb5119571b184dd5a1cc4b8541bbdf67461998cfeac9cb1e/rpds_py-0.27.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:abfa1171a9952d2e0002aba2ad3780820b00cc3d9c98c6630f2e93271501f66c", size = 394341, upload-time = "2025-08-27T12:12:52.024Z" },
- { url = "https://files.pythonhosted.org/packages/5a/7e/4bd610754bf492d398b61725eb9598ddd5eb86b07d7d9483dbcd810e20bc/rpds_py-0.27.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4b507d19f817ebaca79574b16eb2ae412e5c0835542c93fe9983f1e432aca195", size = 523428, upload-time = "2025-08-27T12:12:53.779Z" },
- { url = "https://files.pythonhosted.org/packages/9f/e5/059b9f65a8c9149361a8b75094864ab83b94718344db511fd6117936ed2a/rpds_py-0.27.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:168b025f8fd8d8d10957405f3fdcef3dc20f5982d398f90851f4abc58c566c52", size = 402923, upload-time = "2025-08-27T12:12:55.15Z" },
- { url = "https://files.pythonhosted.org/packages/f5/48/64cabb7daced2968dd08e8a1b7988bf358d7bd5bcd5dc89a652f4668543c/rpds_py-0.27.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb56c6210ef77caa58e16e8c17d35c63fe3f5b60fd9ba9d424470c3400bcf9ed", size = 384094, upload-time = "2025-08-27T12:12:57.194Z" },
- { url = "https://files.pythonhosted.org/packages/ae/e1/dc9094d6ff566bff87add8a510c89b9e158ad2ecd97ee26e677da29a9e1b/rpds_py-0.27.1-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:d252f2d8ca0195faa707f8eb9368955760880b2b42a8ee16d382bf5dd807f89a", size = 401093, upload-time = "2025-08-27T12:12:58.985Z" },
- { url = "https://files.pythonhosted.org/packages/37/8e/ac8577e3ecdd5593e283d46907d7011618994e1d7ab992711ae0f78b9937/rpds_py-0.27.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6e5e54da1e74b91dbc7996b56640f79b195d5925c2b78efaa8c5d53e1d88edde", size = 417969, upload-time = "2025-08-27T12:13:00.367Z" },
- { url = "https://files.pythonhosted.org/packages/66/6d/87507430a8f74a93556fe55c6485ba9c259949a853ce407b1e23fea5ba31/rpds_py-0.27.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ffce0481cc6e95e5b3f0a47ee17ffbd234399e6d532f394c8dce320c3b089c21", size = 558302, upload-time = "2025-08-27T12:13:01.737Z" },
- { url = "https://files.pythonhosted.org/packages/3a/bb/1db4781ce1dda3eecc735e3152659a27b90a02ca62bfeea17aee45cc0fbc/rpds_py-0.27.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:a205fdfe55c90c2cd8e540ca9ceba65cbe6629b443bc05db1f590a3db8189ff9", size = 589259, upload-time = "2025-08-27T12:13:03.127Z" },
- { url = "https://files.pythonhosted.org/packages/7b/0e/ae1c8943d11a814d01b482e1f8da903f88047a962dff9bbdadf3bd6e6fd1/rpds_py-0.27.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:689fb5200a749db0415b092972e8eba85847c23885c8543a8b0f5c009b1a5948", size = 554983, upload-time = "2025-08-27T12:13:04.516Z" },
- { url = "https://files.pythonhosted.org/packages/b2/d5/0b2a55415931db4f112bdab072443ff76131b5ac4f4dc98d10d2d357eb03/rpds_py-0.27.1-cp311-cp311-win32.whl", hash = "sha256:3182af66048c00a075010bc7f4860f33913528a4b6fc09094a6e7598e462fe39", size = 217154, upload-time = "2025-08-27T12:13:06.278Z" },
- { url = "https://files.pythonhosted.org/packages/24/75/3b7ffe0d50dc86a6a964af0d1cc3a4a2cdf437cb7b099a4747bbb96d1819/rpds_py-0.27.1-cp311-cp311-win_amd64.whl", hash = "sha256:b4938466c6b257b2f5c4ff98acd8128ec36b5059e5c8f8372d79316b1c36bb15", size = 228627, upload-time = "2025-08-27T12:13:07.625Z" },
- { url = "https://files.pythonhosted.org/packages/8d/3f/4fd04c32abc02c710f09a72a30c9a55ea3cc154ef8099078fd50a0596f8e/rpds_py-0.27.1-cp311-cp311-win_arm64.whl", hash = "sha256:2f57af9b4d0793e53266ee4325535a31ba48e2f875da81a9177c9926dfa60746", size = 220998, upload-time = "2025-08-27T12:13:08.972Z" },
- { url = "https://files.pythonhosted.org/packages/bd/fe/38de28dee5df58b8198c743fe2bea0c785c6d40941b9950bac4cdb71a014/rpds_py-0.27.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:ae2775c1973e3c30316892737b91f9283f9908e3cc7625b9331271eaaed7dc90", size = 361887, upload-time = "2025-08-27T12:13:10.233Z" },
- { url = "https://files.pythonhosted.org/packages/7c/9a/4b6c7eedc7dd90986bf0fab6ea2a091ec11c01b15f8ba0a14d3f80450468/rpds_py-0.27.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2643400120f55c8a96f7c9d858f7be0c88d383cd4653ae2cf0d0c88f668073e5", size = 345795, upload-time = "2025-08-27T12:13:11.65Z" },
- { url = "https://files.pythonhosted.org/packages/6f/0e/e650e1b81922847a09cca820237b0edee69416a01268b7754d506ade11ad/rpds_py-0.27.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:16323f674c089b0360674a4abd28d5042947d54ba620f72514d69be4ff64845e", size = 385121, upload-time = "2025-08-27T12:13:13.008Z" },
- { url = "https://files.pythonhosted.org/packages/1b/ea/b306067a712988e2bff00dcc7c8f31d26c29b6d5931b461aa4b60a013e33/rpds_py-0.27.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9a1f4814b65eacac94a00fc9a526e3fdafd78e439469644032032d0d63de4881", size = 398976, upload-time = "2025-08-27T12:13:14.368Z" },
- { url = "https://files.pythonhosted.org/packages/2c/0a/26dc43c8840cb8fe239fe12dbc8d8de40f2365e838f3d395835dde72f0e5/rpds_py-0.27.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ba32c16b064267b22f1850a34051121d423b6f7338a12b9459550eb2096e7ec", size = 525953, upload-time = "2025-08-27T12:13:15.774Z" },
- { url = "https://files.pythonhosted.org/packages/22/14/c85e8127b573aaf3a0cbd7fbb8c9c99e735a4a02180c84da2a463b766e9e/rpds_py-0.27.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5c20f33fd10485b80f65e800bbe5f6785af510b9f4056c5a3c612ebc83ba6cb", size = 407915, upload-time = "2025-08-27T12:13:17.379Z" },
- { url = "https://files.pythonhosted.org/packages/ed/7b/8f4fee9ba1fb5ec856eb22d725a4efa3deb47f769597c809e03578b0f9d9/rpds_py-0.27.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:466bfe65bd932da36ff279ddd92de56b042f2266d752719beb97b08526268ec5", size = 386883, upload-time = "2025-08-27T12:13:18.704Z" },
- { url = "https://files.pythonhosted.org/packages/86/47/28fa6d60f8b74fcdceba81b272f8d9836ac0340570f68f5df6b41838547b/rpds_py-0.27.1-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:41e532bbdcb57c92ba3be62c42e9f096431b4cf478da9bc3bc6ce5c38ab7ba7a", size = 405699, upload-time = "2025-08-27T12:13:20.089Z" },
- { url = "https://files.pythonhosted.org/packages/d0/fd/c5987b5e054548df56953a21fe2ebed51fc1ec7c8f24fd41c067b68c4a0a/rpds_py-0.27.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f149826d742b406579466283769a8ea448eed82a789af0ed17b0cd5770433444", size = 423713, upload-time = "2025-08-27T12:13:21.436Z" },
- { url = "https://files.pythonhosted.org/packages/ac/ba/3c4978b54a73ed19a7d74531be37a8bcc542d917c770e14d372b8daea186/rpds_py-0.27.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:80c60cfb5310677bd67cb1e85a1e8eb52e12529545441b43e6f14d90b878775a", size = 562324, upload-time = "2025-08-27T12:13:22.789Z" },
- { url = "https://files.pythonhosted.org/packages/b5/6c/6943a91768fec16db09a42b08644b960cff540c66aab89b74be6d4a144ba/rpds_py-0.27.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:7ee6521b9baf06085f62ba9c7a3e5becffbc32480d2f1b351559c001c38ce4c1", size = 593646, upload-time = "2025-08-27T12:13:24.122Z" },
- { url = "https://files.pythonhosted.org/packages/11/73/9d7a8f4be5f4396f011a6bb7a19fe26303a0dac9064462f5651ced2f572f/rpds_py-0.27.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a512c8263249a9d68cac08b05dd59d2b3f2061d99b322813cbcc14c3c7421998", size = 558137, upload-time = "2025-08-27T12:13:25.557Z" },
- { url = "https://files.pythonhosted.org/packages/6e/96/6772cbfa0e2485bcceef8071de7821f81aeac8bb45fbfd5542a3e8108165/rpds_py-0.27.1-cp312-cp312-win32.whl", hash = "sha256:819064fa048ba01b6dadc5116f3ac48610435ac9a0058bbde98e569f9e785c39", size = 221343, upload-time = "2025-08-27T12:13:26.967Z" },
- { url = "https://files.pythonhosted.org/packages/67/b6/c82f0faa9af1c6a64669f73a17ee0eeef25aff30bb9a1c318509efe45d84/rpds_py-0.27.1-cp312-cp312-win_amd64.whl", hash = "sha256:d9199717881f13c32c4046a15f024971a3b78ad4ea029e8da6b86e5aa9cf4594", size = 232497, upload-time = "2025-08-27T12:13:28.326Z" },
- { url = "https://files.pythonhosted.org/packages/e1/96/2817b44bd2ed11aebacc9251da03689d56109b9aba5e311297b6902136e2/rpds_py-0.27.1-cp312-cp312-win_arm64.whl", hash = "sha256:33aa65b97826a0e885ef6e278fbd934e98cdcfed80b63946025f01e2f5b29502", size = 222790, upload-time = "2025-08-27T12:13:29.71Z" },
- { url = "https://files.pythonhosted.org/packages/cc/77/610aeee8d41e39080c7e14afa5387138e3c9fa9756ab893d09d99e7d8e98/rpds_py-0.27.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:e4b9fcfbc021633863a37e92571d6f91851fa656f0180246e84cbd8b3f6b329b", size = 361741, upload-time = "2025-08-27T12:13:31.039Z" },
- { url = "https://files.pythonhosted.org/packages/3a/fc/c43765f201c6a1c60be2043cbdb664013def52460a4c7adace89d6682bf4/rpds_py-0.27.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1441811a96eadca93c517d08df75de45e5ffe68aa3089924f963c782c4b898cf", size = 345574, upload-time = "2025-08-27T12:13:32.902Z" },
- { url = "https://files.pythonhosted.org/packages/20/42/ee2b2ca114294cd9847d0ef9c26d2b0851b2e7e00bf14cc4c0b581df0fc3/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55266dafa22e672f5a4f65019015f90336ed31c6383bd53f5e7826d21a0e0b83", size = 385051, upload-time = "2025-08-27T12:13:34.228Z" },
- { url = "https://files.pythonhosted.org/packages/fd/e8/1e430fe311e4799e02e2d1af7c765f024e95e17d651612425b226705f910/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d78827d7ac08627ea2c8e02c9e5b41180ea5ea1f747e9db0915e3adf36b62dcf", size = 398395, upload-time = "2025-08-27T12:13:36.132Z" },
- { url = "https://files.pythonhosted.org/packages/82/95/9dc227d441ff2670651c27a739acb2535ccaf8b351a88d78c088965e5996/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae92443798a40a92dc5f0b01d8a7c93adde0c4dc965310a29ae7c64d72b9fad2", size = 524334, upload-time = "2025-08-27T12:13:37.562Z" },
- { url = "https://files.pythonhosted.org/packages/87/01/a670c232f401d9ad461d9a332aa4080cd3cb1d1df18213dbd0d2a6a7ab51/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c46c9dd2403b66a2a3b9720ec4b74d4ab49d4fabf9f03dfdce2d42af913fe8d0", size = 407691, upload-time = "2025-08-27T12:13:38.94Z" },
- { url = "https://files.pythonhosted.org/packages/03/36/0a14aebbaa26fe7fab4780c76f2239e76cc95a0090bdb25e31d95c492fcd/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2efe4eb1d01b7f5f1939f4ef30ecea6c6b3521eec451fb93191bf84b2a522418", size = 386868, upload-time = "2025-08-27T12:13:40.192Z" },
- { url = "https://files.pythonhosted.org/packages/3b/03/8c897fb8b5347ff6c1cc31239b9611c5bf79d78c984430887a353e1409a1/rpds_py-0.27.1-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:15d3b4d83582d10c601f481eca29c3f138d44c92187d197aff663a269197c02d", size = 405469, upload-time = "2025-08-27T12:13:41.496Z" },
- { url = "https://files.pythonhosted.org/packages/da/07/88c60edc2df74850d496d78a1fdcdc7b54360a7f610a4d50008309d41b94/rpds_py-0.27.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4ed2e16abbc982a169d30d1a420274a709949e2cbdef119fe2ec9d870b42f274", size = 422125, upload-time = "2025-08-27T12:13:42.802Z" },
- { url = "https://files.pythonhosted.org/packages/6b/86/5f4c707603e41b05f191a749984f390dabcbc467cf833769b47bf14ba04f/rpds_py-0.27.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a75f305c9b013289121ec0f1181931975df78738cdf650093e6b86d74aa7d8dd", size = 562341, upload-time = "2025-08-27T12:13:44.472Z" },
- { url = "https://files.pythonhosted.org/packages/b2/92/3c0cb2492094e3cd9baf9e49bbb7befeceb584ea0c1a8b5939dca4da12e5/rpds_py-0.27.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:67ce7620704745881a3d4b0ada80ab4d99df390838839921f99e63c474f82cf2", size = 592511, upload-time = "2025-08-27T12:13:45.898Z" },
- { url = "https://files.pythonhosted.org/packages/10/bb/82e64fbb0047c46a168faa28d0d45a7851cd0582f850b966811d30f67ad8/rpds_py-0.27.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9d992ac10eb86d9b6f369647b6a3f412fc0075cfd5d799530e84d335e440a002", size = 557736, upload-time = "2025-08-27T12:13:47.408Z" },
- { url = "https://files.pythonhosted.org/packages/00/95/3c863973d409210da7fb41958172c6b7dbe7fc34e04d3cc1f10bb85e979f/rpds_py-0.27.1-cp313-cp313-win32.whl", hash = "sha256:4f75e4bd8ab8db624e02c8e2fc4063021b58becdbe6df793a8111d9343aec1e3", size = 221462, upload-time = "2025-08-27T12:13:48.742Z" },
- { url = "https://files.pythonhosted.org/packages/ce/2c/5867b14a81dc217b56d95a9f2a40fdbc56a1ab0181b80132beeecbd4b2d6/rpds_py-0.27.1-cp313-cp313-win_amd64.whl", hash = "sha256:f9025faafc62ed0b75a53e541895ca272815bec18abe2249ff6501c8f2e12b83", size = 232034, upload-time = "2025-08-27T12:13:50.11Z" },
- { url = "https://files.pythonhosted.org/packages/c7/78/3958f3f018c01923823f1e47f1cc338e398814b92d83cd278364446fac66/rpds_py-0.27.1-cp313-cp313-win_arm64.whl", hash = "sha256:ed10dc32829e7d222b7d3b93136d25a406ba9788f6a7ebf6809092da1f4d279d", size = 222392, upload-time = "2025-08-27T12:13:52.587Z" },
- { url = "https://files.pythonhosted.org/packages/01/76/1cdf1f91aed5c3a7bf2eba1f1c4e4d6f57832d73003919a20118870ea659/rpds_py-0.27.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:92022bbbad0d4426e616815b16bc4127f83c9a74940e1ccf3cfe0b387aba0228", size = 358355, upload-time = "2025-08-27T12:13:54.012Z" },
- { url = "https://files.pythonhosted.org/packages/c3/6f/bf142541229374287604caf3bb2a4ae17f0a580798fd72d3b009b532db4e/rpds_py-0.27.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:47162fdab9407ec3f160805ac3e154df042e577dd53341745fc7fb3f625e6d92", size = 342138, upload-time = "2025-08-27T12:13:55.791Z" },
- { url = "https://files.pythonhosted.org/packages/1a/77/355b1c041d6be40886c44ff5e798b4e2769e497b790f0f7fd1e78d17e9a8/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb89bec23fddc489e5d78b550a7b773557c9ab58b7946154a10a6f7a214a48b2", size = 380247, upload-time = "2025-08-27T12:13:57.683Z" },
- { url = "https://files.pythonhosted.org/packages/d6/a4/d9cef5c3946ea271ce2243c51481971cd6e34f21925af2783dd17b26e815/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e48af21883ded2b3e9eb48cb7880ad8598b31ab752ff3be6457001d78f416723", size = 390699, upload-time = "2025-08-27T12:13:59.137Z" },
- { url = "https://files.pythonhosted.org/packages/3a/06/005106a7b8c6c1a7e91b73169e49870f4af5256119d34a361ae5240a0c1d/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6f5b7bd8e219ed50299e58551a410b64daafb5017d54bbe822e003856f06a802", size = 521852, upload-time = "2025-08-27T12:14:00.583Z" },
- { url = "https://files.pythonhosted.org/packages/e5/3e/50fb1dac0948e17a02eb05c24510a8fe12d5ce8561c6b7b7d1339ab7ab9c/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:08f1e20bccf73b08d12d804d6e1c22ca5530e71659e6673bce31a6bb71c1e73f", size = 402582, upload-time = "2025-08-27T12:14:02.034Z" },
- { url = "https://files.pythonhosted.org/packages/cb/b0/f4e224090dc5b0ec15f31a02d746ab24101dd430847c4d99123798661bfc/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0dc5dceeaefcc96dc192e3a80bbe1d6c410c469e97bdd47494a7d930987f18b2", size = 384126, upload-time = "2025-08-27T12:14:03.437Z" },
- { url = "https://files.pythonhosted.org/packages/54/77/ac339d5f82b6afff1df8f0fe0d2145cc827992cb5f8eeb90fc9f31ef7a63/rpds_py-0.27.1-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:d76f9cc8665acdc0c9177043746775aa7babbf479b5520b78ae4002d889f5c21", size = 399486, upload-time = "2025-08-27T12:14:05.443Z" },
- { url = "https://files.pythonhosted.org/packages/d6/29/3e1c255eee6ac358c056a57d6d6869baa00a62fa32eea5ee0632039c50a3/rpds_py-0.27.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:134fae0e36022edad8290a6661edf40c023562964efea0cc0ec7f5d392d2aaef", size = 414832, upload-time = "2025-08-27T12:14:06.902Z" },
- { url = "https://files.pythonhosted.org/packages/3f/db/6d498b844342deb3fa1d030598db93937a9964fcf5cb4da4feb5f17be34b/rpds_py-0.27.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:eb11a4f1b2b63337cfd3b4d110af778a59aae51c81d195768e353d8b52f88081", size = 557249, upload-time = "2025-08-27T12:14:08.37Z" },
- { url = "https://files.pythonhosted.org/packages/60/f3/690dd38e2310b6f68858a331399b4d6dbb9132c3e8ef8b4333b96caf403d/rpds_py-0.27.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:13e608ac9f50a0ed4faec0e90ece76ae33b34c0e8656e3dceb9a7db994c692cd", size = 587356, upload-time = "2025-08-27T12:14:10.034Z" },
- { url = "https://files.pythonhosted.org/packages/86/e3/84507781cccd0145f35b1dc32c72675200c5ce8d5b30f813e49424ef68fc/rpds_py-0.27.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dd2135527aa40f061350c3f8f89da2644de26cd73e4de458e79606384f4f68e7", size = 555300, upload-time = "2025-08-27T12:14:11.783Z" },
- { url = "https://files.pythonhosted.org/packages/e5/ee/375469849e6b429b3516206b4580a79e9ef3eb12920ddbd4492b56eaacbe/rpds_py-0.27.1-cp313-cp313t-win32.whl", hash = "sha256:3020724ade63fe320a972e2ffd93b5623227e684315adce194941167fee02688", size = 216714, upload-time = "2025-08-27T12:14:13.629Z" },
- { url = "https://files.pythonhosted.org/packages/21/87/3fc94e47c9bd0742660e84706c311a860dcae4374cf4a03c477e23ce605a/rpds_py-0.27.1-cp313-cp313t-win_amd64.whl", hash = "sha256:8ee50c3e41739886606388ba3ab3ee2aae9f35fb23f833091833255a31740797", size = 228943, upload-time = "2025-08-27T12:14:14.937Z" },
- { url = "https://files.pythonhosted.org/packages/70/36/b6e6066520a07cf029d385de869729a895917b411e777ab1cde878100a1d/rpds_py-0.27.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:acb9aafccaae278f449d9c713b64a9e68662e7799dbd5859e2c6b3c67b56d334", size = 362472, upload-time = "2025-08-27T12:14:16.333Z" },
- { url = "https://files.pythonhosted.org/packages/af/07/b4646032e0dcec0df9c73a3bd52f63bc6c5f9cda992f06bd0e73fe3fbebd/rpds_py-0.27.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:b7fb801aa7f845ddf601c49630deeeccde7ce10065561d92729bfe81bd21fb33", size = 345676, upload-time = "2025-08-27T12:14:17.764Z" },
- { url = "https://files.pythonhosted.org/packages/b0/16/2f1003ee5d0af4bcb13c0cf894957984c32a6751ed7206db2aee7379a55e/rpds_py-0.27.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fe0dd05afb46597b9a2e11c351e5e4283c741237e7f617ffb3252780cca9336a", size = 385313, upload-time = "2025-08-27T12:14:19.829Z" },
- { url = "https://files.pythonhosted.org/packages/05/cd/7eb6dd7b232e7f2654d03fa07f1414d7dfc980e82ba71e40a7c46fd95484/rpds_py-0.27.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b6dfb0e058adb12d8b1d1b25f686e94ffa65d9995a5157afe99743bf7369d62b", size = 399080, upload-time = "2025-08-27T12:14:21.531Z" },
- { url = "https://files.pythonhosted.org/packages/20/51/5829afd5000ec1cb60f304711f02572d619040aa3ec033d8226817d1e571/rpds_py-0.27.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ed090ccd235f6fa8bb5861684567f0a83e04f52dfc2e5c05f2e4b1309fcf85e7", size = 523868, upload-time = "2025-08-27T12:14:23.485Z" },
- { url = "https://files.pythonhosted.org/packages/05/2c/30eebca20d5db95720ab4d2faec1b5e4c1025c473f703738c371241476a2/rpds_py-0.27.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bf876e79763eecf3e7356f157540d6a093cef395b65514f17a356f62af6cc136", size = 408750, upload-time = "2025-08-27T12:14:24.924Z" },
- { url = "https://files.pythonhosted.org/packages/90/1a/cdb5083f043597c4d4276eae4e4c70c55ab5accec078da8611f24575a367/rpds_py-0.27.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:12ed005216a51b1d6e2b02a7bd31885fe317e45897de81d86dcce7d74618ffff", size = 387688, upload-time = "2025-08-27T12:14:27.537Z" },
- { url = "https://files.pythonhosted.org/packages/7c/92/cf786a15320e173f945d205ab31585cc43969743bb1a48b6888f7a2b0a2d/rpds_py-0.27.1-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:ee4308f409a40e50593c7e3bb8cbe0b4d4c66d1674a316324f0c2f5383b486f9", size = 407225, upload-time = "2025-08-27T12:14:28.981Z" },
- { url = "https://files.pythonhosted.org/packages/33/5c/85ee16df5b65063ef26017bef33096557a4c83fbe56218ac7cd8c235f16d/rpds_py-0.27.1-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0b08d152555acf1f455154d498ca855618c1378ec810646fcd7c76416ac6dc60", size = 423361, upload-time = "2025-08-27T12:14:30.469Z" },
- { url = "https://files.pythonhosted.org/packages/4b/8e/1c2741307fcabd1a334ecf008e92c4f47bb6f848712cf15c923becfe82bb/rpds_py-0.27.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:dce51c828941973a5684d458214d3a36fcd28da3e1875d659388f4f9f12cc33e", size = 562493, upload-time = "2025-08-27T12:14:31.987Z" },
- { url = "https://files.pythonhosted.org/packages/04/03/5159321baae9b2222442a70c1f988cbbd66b9be0675dd3936461269be360/rpds_py-0.27.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:c1476d6f29eb81aa4151c9a31219b03f1f798dc43d8af1250a870735516a1212", size = 592623, upload-time = "2025-08-27T12:14:33.543Z" },
- { url = "https://files.pythonhosted.org/packages/ff/39/c09fd1ad28b85bc1d4554a8710233c9f4cefd03d7717a1b8fbfd171d1167/rpds_py-0.27.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:3ce0cac322b0d69b63c9cdb895ee1b65805ec9ffad37639f291dd79467bee675", size = 558800, upload-time = "2025-08-27T12:14:35.436Z" },
- { url = "https://files.pythonhosted.org/packages/c5/d6/99228e6bbcf4baa764b18258f519a9035131d91b538d4e0e294313462a98/rpds_py-0.27.1-cp314-cp314-win32.whl", hash = "sha256:dfbfac137d2a3d0725758cd141f878bf4329ba25e34979797c89474a89a8a3a3", size = 221943, upload-time = "2025-08-27T12:14:36.898Z" },
- { url = "https://files.pythonhosted.org/packages/be/07/c802bc6b8e95be83b79bdf23d1aa61d68324cb1006e245d6c58e959e314d/rpds_py-0.27.1-cp314-cp314-win_amd64.whl", hash = "sha256:a6e57b0abfe7cc513450fcf529eb486b6e4d3f8aee83e92eb5f1ef848218d456", size = 233739, upload-time = "2025-08-27T12:14:38.386Z" },
- { url = "https://files.pythonhosted.org/packages/c8/89/3e1b1c16d4c2d547c5717377a8df99aee8099ff050f87c45cb4d5fa70891/rpds_py-0.27.1-cp314-cp314-win_arm64.whl", hash = "sha256:faf8d146f3d476abfee026c4ae3bdd9ca14236ae4e4c310cbd1cf75ba33d24a3", size = 223120, upload-time = "2025-08-27T12:14:39.82Z" },
- { url = "https://files.pythonhosted.org/packages/62/7e/dc7931dc2fa4a6e46b2a4fa744a9fe5c548efd70e0ba74f40b39fa4a8c10/rpds_py-0.27.1-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:ba81d2b56b6d4911ce735aad0a1d4495e808b8ee4dc58715998741a26874e7c2", size = 358944, upload-time = "2025-08-27T12:14:41.199Z" },
- { url = "https://files.pythonhosted.org/packages/e6/22/4af76ac4e9f336bfb1a5f240d18a33c6b2fcaadb7472ac7680576512b49a/rpds_py-0.27.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:84f7d509870098de0e864cad0102711c1e24e9b1a50ee713b65928adb22269e4", size = 342283, upload-time = "2025-08-27T12:14:42.699Z" },
- { url = "https://files.pythonhosted.org/packages/1c/15/2a7c619b3c2272ea9feb9ade67a45c40b3eeb500d503ad4c28c395dc51b4/rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9e960fc78fecd1100539f14132425e1d5fe44ecb9239f8f27f079962021523e", size = 380320, upload-time = "2025-08-27T12:14:44.157Z" },
- { url = "https://files.pythonhosted.org/packages/a2/7d/4c6d243ba4a3057e994bb5bedd01b5c963c12fe38dde707a52acdb3849e7/rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:62f85b665cedab1a503747617393573995dac4600ff51869d69ad2f39eb5e817", size = 391760, upload-time = "2025-08-27T12:14:45.845Z" },
- { url = "https://files.pythonhosted.org/packages/b4/71/b19401a909b83bcd67f90221330bc1ef11bc486fe4e04c24388d28a618ae/rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fed467af29776f6556250c9ed85ea5a4dd121ab56a5f8b206e3e7a4c551e48ec", size = 522476, upload-time = "2025-08-27T12:14:47.364Z" },
- { url = "https://files.pythonhosted.org/packages/e4/44/1a3b9715c0455d2e2f0f6df5ee6d6f5afdc423d0773a8a682ed2b43c566c/rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2729615f9d430af0ae6b36cf042cb55c0936408d543fb691e1a9e36648fd35a", size = 403418, upload-time = "2025-08-27T12:14:49.991Z" },
- { url = "https://files.pythonhosted.org/packages/1c/4b/fb6c4f14984eb56673bc868a66536f53417ddb13ed44b391998100a06a96/rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1b207d881a9aef7ba753d69c123a35d96ca7cb808056998f6b9e8747321f03b8", size = 384771, upload-time = "2025-08-27T12:14:52.159Z" },
- { url = "https://files.pythonhosted.org/packages/c0/56/d5265d2d28b7420d7b4d4d85cad8ef891760f5135102e60d5c970b976e41/rpds_py-0.27.1-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:639fd5efec029f99b79ae47e5d7e00ad8a773da899b6309f6786ecaf22948c48", size = 400022, upload-time = "2025-08-27T12:14:53.859Z" },
- { url = "https://files.pythonhosted.org/packages/8f/e9/9f5fc70164a569bdd6ed9046486c3568d6926e3a49bdefeeccfb18655875/rpds_py-0.27.1-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fecc80cb2a90e28af8a9b366edacf33d7a91cbfe4c2c4544ea1246e949cfebeb", size = 416787, upload-time = "2025-08-27T12:14:55.673Z" },
- { url = "https://files.pythonhosted.org/packages/d4/64/56dd03430ba491db943a81dcdef115a985aac5f44f565cd39a00c766d45c/rpds_py-0.27.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:42a89282d711711d0a62d6f57d81aa43a1368686c45bc1c46b7f079d55692734", size = 557538, upload-time = "2025-08-27T12:14:57.245Z" },
- { url = "https://files.pythonhosted.org/packages/3f/36/92cc885a3129993b1d963a2a42ecf64e6a8e129d2c7cc980dbeba84e55fb/rpds_py-0.27.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:cf9931f14223de59551ab9d38ed18d92f14f055a5f78c1d8ad6493f735021bbb", size = 588512, upload-time = "2025-08-27T12:14:58.728Z" },
- { url = "https://files.pythonhosted.org/packages/dd/10/6b283707780a81919f71625351182b4f98932ac89a09023cb61865136244/rpds_py-0.27.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f39f58a27cc6e59f432b568ed8429c7e1641324fbe38131de852cd77b2d534b0", size = 555813, upload-time = "2025-08-27T12:15:00.334Z" },
- { url = "https://files.pythonhosted.org/packages/04/2e/30b5ea18c01379da6272a92825dd7e53dc9d15c88a19e97932d35d430ef7/rpds_py-0.27.1-cp314-cp314t-win32.whl", hash = "sha256:d5fa0ee122dc09e23607a28e6d7b150da16c662e66409bbe85230e4c85bb528a", size = 217385, upload-time = "2025-08-27T12:15:01.937Z" },
- { url = "https://files.pythonhosted.org/packages/32/7d/97119da51cb1dd3f2f3c0805f155a3aa4a95fa44fe7d78ae15e69edf4f34/rpds_py-0.27.1-cp314-cp314t-win_amd64.whl", hash = "sha256:6567d2bb951e21232c2f660c24cf3470bb96de56cdcb3f071a83feeaff8a2772", size = 230097, upload-time = "2025-08-27T12:15:03.961Z" },
- { url = "https://files.pythonhosted.org/packages/7f/6c/252e83e1ce7583c81f26d1d884b2074d40a13977e1b6c9c50bbf9a7f1f5a/rpds_py-0.27.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:c918c65ec2e42c2a78d19f18c553d77319119bf43aa9e2edf7fb78d624355527", size = 372140, upload-time = "2025-08-27T12:15:05.441Z" },
- { url = "https://files.pythonhosted.org/packages/9d/71/949c195d927c5aeb0d0629d329a20de43a64c423a6aa53836290609ef7ec/rpds_py-0.27.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1fea2b1a922c47c51fd07d656324531adc787e415c8b116530a1d29c0516c62d", size = 354086, upload-time = "2025-08-27T12:15:07.404Z" },
- { url = "https://files.pythonhosted.org/packages/9f/02/e43e332ad8ce4f6c4342d151a471a7f2900ed1d76901da62eb3762663a71/rpds_py-0.27.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbf94c58e8e0cd6b6f38d8de67acae41b3a515c26169366ab58bdca4a6883bb8", size = 382117, upload-time = "2025-08-27T12:15:09.275Z" },
- { url = "https://files.pythonhosted.org/packages/d0/05/b0fdeb5b577197ad72812bbdfb72f9a08fa1e64539cc3940b1b781cd3596/rpds_py-0.27.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c2a8fed130ce946d5c585eddc7c8eeef0051f58ac80a8ee43bd17835c144c2cc", size = 394520, upload-time = "2025-08-27T12:15:10.727Z" },
- { url = "https://files.pythonhosted.org/packages/67/1f/4cfef98b2349a7585181e99294fa2a13f0af06902048a5d70f431a66d0b9/rpds_py-0.27.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:037a2361db72ee98d829bc2c5b7cc55598ae0a5e0ec1823a56ea99374cfd73c1", size = 522657, upload-time = "2025-08-27T12:15:12.613Z" },
- { url = "https://files.pythonhosted.org/packages/44/55/ccf37ddc4c6dce7437b335088b5ca18da864b334890e2fe9aa6ddc3f79a9/rpds_py-0.27.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5281ed1cc1d49882f9997981c88df1a22e140ab41df19071222f7e5fc4e72125", size = 402967, upload-time = "2025-08-27T12:15:14.113Z" },
- { url = "https://files.pythonhosted.org/packages/74/e5/5903f92e41e293b07707d5bf00ef39a0eb2af7190aff4beaf581a6591510/rpds_py-0.27.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fd50659a069c15eef8aa3d64bbef0d69fd27bb4a50c9ab4f17f83a16cbf8905", size = 384372, upload-time = "2025-08-27T12:15:15.842Z" },
- { url = "https://files.pythonhosted.org/packages/8f/e3/fbb409e18aeefc01e49f5922ac63d2d914328430e295c12183ce56ebf76b/rpds_py-0.27.1-cp39-cp39-manylinux_2_31_riscv64.whl", hash = "sha256:c4b676c4ae3921649a15d28ed10025548e9b561ded473aa413af749503c6737e", size = 401264, upload-time = "2025-08-27T12:15:17.388Z" },
- { url = "https://files.pythonhosted.org/packages/55/79/529ad07794e05cb0f38e2f965fc5bb20853d523976719400acecc447ec9d/rpds_py-0.27.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:079bc583a26db831a985c5257797b2b5d3affb0386e7ff886256762f82113b5e", size = 418691, upload-time = "2025-08-27T12:15:19.144Z" },
- { url = "https://files.pythonhosted.org/packages/33/39/6554a7fd6d9906fda2521c6d52f5d723dca123529fb719a5b5e074c15e01/rpds_py-0.27.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:4e44099bd522cba71a2c6b97f68e19f40e7d85399de899d66cdb67b32d7cb786", size = 558989, upload-time = "2025-08-27T12:15:21.087Z" },
- { url = "https://files.pythonhosted.org/packages/19/b2/76fa15173b6f9f445e5ef15120871b945fb8dd9044b6b8c7abe87e938416/rpds_py-0.27.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e202e6d4188e53c6661af813b46c37ca2c45e497fc558bacc1a7630ec2695aec", size = 589835, upload-time = "2025-08-27T12:15:22.696Z" },
- { url = "https://files.pythonhosted.org/packages/ee/9e/5560a4b39bab780405bed8a88ee85b30178061d189558a86003548dea045/rpds_py-0.27.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:f41f814b8eaa48768d1bb551591f6ba45f87ac76899453e8ccd41dba1289b04b", size = 555227, upload-time = "2025-08-27T12:15:24.278Z" },
- { url = "https://files.pythonhosted.org/packages/52/d7/cd9c36215111aa65724c132bf709c6f35175973e90b32115dedc4ced09cb/rpds_py-0.27.1-cp39-cp39-win32.whl", hash = "sha256:9e71f5a087ead99563c11fdaceee83ee982fd39cf67601f4fd66cb386336ee52", size = 217899, upload-time = "2025-08-27T12:15:25.926Z" },
- { url = "https://files.pythonhosted.org/packages/5b/e0/d75ab7b4dd8ba777f6b365adbdfc7614bbfe7c5f05703031dfa4b61c3d6c/rpds_py-0.27.1-cp39-cp39-win_amd64.whl", hash = "sha256:71108900c9c3c8590697244b9519017a400d9ba26a36c48381b3f64743a44aab", size = 228725, upload-time = "2025-08-27T12:15:27.398Z" },
- { url = "https://files.pythonhosted.org/packages/d5/63/b7cc415c345625d5e62f694ea356c58fb964861409008118f1245f8c3347/rpds_py-0.27.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:7ba22cb9693df986033b91ae1d7a979bc399237d45fccf875b76f62bb9e52ddf", size = 371360, upload-time = "2025-08-27T12:15:29.218Z" },
- { url = "https://files.pythonhosted.org/packages/e5/8c/12e1b24b560cf378b8ffbdb9dc73abd529e1adcfcf82727dfd29c4a7b88d/rpds_py-0.27.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:5b640501be9288c77738b5492b3fd3abc4ba95c50c2e41273c8a1459f08298d3", size = 353933, upload-time = "2025-08-27T12:15:30.837Z" },
- { url = "https://files.pythonhosted.org/packages/9b/85/1bb2210c1f7a1b99e91fea486b9f0f894aa5da3a5ec7097cbad7dec6d40f/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb08b65b93e0c6dd70aac7f7890a9c0938d5ec71d5cb32d45cf844fb8ae47636", size = 382962, upload-time = "2025-08-27T12:15:32.348Z" },
- { url = "https://files.pythonhosted.org/packages/cc/c9/a839b9f219cf80ed65f27a7f5ddbb2809c1b85c966020ae2dff490e0b18e/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d7ff07d696a7a38152ebdb8212ca9e5baab56656749f3d6004b34ab726b550b8", size = 394412, upload-time = "2025-08-27T12:15:33.839Z" },
- { url = "https://files.pythonhosted.org/packages/02/2d/b1d7f928b0b1f4fc2e0133e8051d199b01d7384875adc63b6ddadf3de7e5/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fb7c72262deae25366e3b6c0c0ba46007967aea15d1eea746e44ddba8ec58dcc", size = 523972, upload-time = "2025-08-27T12:15:35.377Z" },
- { url = "https://files.pythonhosted.org/packages/a9/af/2cbf56edd2d07716df1aec8a726b3159deb47cb5c27e1e42b71d705a7c2f/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7b002cab05d6339716b03a4a3a2ce26737f6231d7b523f339fa061d53368c9d8", size = 403273, upload-time = "2025-08-27T12:15:37.051Z" },
- { url = "https://files.pythonhosted.org/packages/c0/93/425e32200158d44ff01da5d9612c3b6711fe69f606f06e3895511f17473b/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23f6b69d1c26c4704fec01311963a41d7de3ee0570a84ebde4d544e5a1859ffc", size = 385278, upload-time = "2025-08-27T12:15:38.571Z" },
- { url = "https://files.pythonhosted.org/packages/eb/1a/1a04a915ecd0551bfa9e77b7672d1937b4b72a0fc204a17deef76001cfb2/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:530064db9146b247351f2a0250b8f00b289accea4596a033e94be2389977de71", size = 402084, upload-time = "2025-08-27T12:15:40.529Z" },
- { url = "https://files.pythonhosted.org/packages/51/f7/66585c0fe5714368b62951d2513b684e5215beaceab2c6629549ddb15036/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7b90b0496570bd6b0321724a330d8b545827c4df2034b6ddfc5f5275f55da2ad", size = 419041, upload-time = "2025-08-27T12:15:42.191Z" },
- { url = "https://files.pythonhosted.org/packages/8e/7e/83a508f6b8e219bba2d4af077c35ba0e0cdd35a751a3be6a7cba5a55ad71/rpds_py-0.27.1-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:879b0e14a2da6a1102a3fc8af580fc1ead37e6d6692a781bd8c83da37429b5ab", size = 560084, upload-time = "2025-08-27T12:15:43.839Z" },
- { url = "https://files.pythonhosted.org/packages/66/66/bb945683b958a1b19eb0fe715594630d0f36396ebdef4d9b89c2fa09aa56/rpds_py-0.27.1-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:0d807710df3b5faa66c731afa162ea29717ab3be17bdc15f90f2d9f183da4059", size = 590115, upload-time = "2025-08-27T12:15:46.647Z" },
- { url = "https://files.pythonhosted.org/packages/12/00/ccfaafaf7db7e7adace915e5c2f2c2410e16402561801e9c7f96683002d3/rpds_py-0.27.1-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:3adc388fc3afb6540aec081fa59e6e0d3908722771aa1e37ffe22b220a436f0b", size = 556561, upload-time = "2025-08-27T12:15:48.219Z" },
- { url = "https://files.pythonhosted.org/packages/e1/b7/92b6ed9aad103bfe1c45df98453dfae40969eef2cb6c6239c58d7e96f1b3/rpds_py-0.27.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c796c0c1cc68cb08b0284db4229f5af76168172670c74908fdbd4b7d7f515819", size = 229125, upload-time = "2025-08-27T12:15:49.956Z" },
- { url = "https://files.pythonhosted.org/packages/0c/ed/e1fba02de17f4f76318b834425257c8ea297e415e12c68b4361f63e8ae92/rpds_py-0.27.1-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:cdfe4bb2f9fe7458b7453ad3c33e726d6d1c7c0a72960bcc23800d77384e42df", size = 371402, upload-time = "2025-08-27T12:15:51.561Z" },
- { url = "https://files.pythonhosted.org/packages/af/7c/e16b959b316048b55585a697e94add55a4ae0d984434d279ea83442e460d/rpds_py-0.27.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:8fabb8fd848a5f75a2324e4a84501ee3a5e3c78d8603f83475441866e60b94a3", size = 354084, upload-time = "2025-08-27T12:15:53.219Z" },
- { url = "https://files.pythonhosted.org/packages/de/c1/ade645f55de76799fdd08682d51ae6724cb46f318573f18be49b1e040428/rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eda8719d598f2f7f3e0f885cba8646644b55a187762bec091fa14a2b819746a9", size = 383090, upload-time = "2025-08-27T12:15:55.158Z" },
- { url = "https://files.pythonhosted.org/packages/1f/27/89070ca9b856e52960da1472efcb6c20ba27cfe902f4f23ed095b9cfc61d/rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3c64d07e95606ec402a0a1c511fe003873fa6af630bda59bac77fac8b4318ebc", size = 394519, upload-time = "2025-08-27T12:15:57.238Z" },
- { url = "https://files.pythonhosted.org/packages/b3/28/be120586874ef906aa5aeeae95ae8df4184bc757e5b6bd1c729ccff45ed5/rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:93a2ed40de81bcff59aabebb626562d48332f3d028ca2036f1d23cbb52750be4", size = 523817, upload-time = "2025-08-27T12:15:59.237Z" },
- { url = "https://files.pythonhosted.org/packages/a8/ef/70cc197bc11cfcde02a86f36ac1eed15c56667c2ebddbdb76a47e90306da/rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:387ce8c44ae94e0ec50532d9cb0edce17311024c9794eb196b90e1058aadeb66", size = 403240, upload-time = "2025-08-27T12:16:00.923Z" },
- { url = "https://files.pythonhosted.org/packages/cf/35/46936cca449f7f518f2f4996e0e8344db4b57e2081e752441154089d2a5f/rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aaf94f812c95b5e60ebaf8bfb1898a7d7cb9c1af5744d4a67fa47796e0465d4e", size = 385194, upload-time = "2025-08-27T12:16:02.802Z" },
- { url = "https://files.pythonhosted.org/packages/e1/62/29c0d3e5125c3270b51415af7cbff1ec587379c84f55a5761cc9efa8cd06/rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:4848ca84d6ded9b58e474dfdbad4b8bfb450344c0551ddc8d958bf4b36aa837c", size = 402086, upload-time = "2025-08-27T12:16:04.806Z" },
- { url = "https://files.pythonhosted.org/packages/8f/66/03e1087679227785474466fdd04157fb793b3b76e3fcf01cbf4c693c1949/rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2bde09cbcf2248b73c7c323be49b280180ff39fadcfe04e7b6f54a678d02a7cf", size = 419272, upload-time = "2025-08-27T12:16:06.471Z" },
- { url = "https://files.pythonhosted.org/packages/6a/24/e3e72d265121e00b063aef3e3501e5b2473cf1b23511d56e529531acf01e/rpds_py-0.27.1-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:94c44ee01fd21c9058f124d2d4f0c9dc7634bec93cd4b38eefc385dabe71acbf", size = 560003, upload-time = "2025-08-27T12:16:08.06Z" },
- { url = "https://files.pythonhosted.org/packages/26/ca/f5a344c534214cc2d41118c0699fffbdc2c1bc7046f2a2b9609765ab9c92/rpds_py-0.27.1-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:df8b74962e35c9249425d90144e721eed198e6555a0e22a563d29fe4486b51f6", size = 590482, upload-time = "2025-08-27T12:16:10.137Z" },
- { url = "https://files.pythonhosted.org/packages/ce/08/4349bdd5c64d9d193c360aa9db89adeee6f6682ab8825dca0a3f535f434f/rpds_py-0.27.1-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:dc23e6820e3b40847e2f4a7726462ba0cf53089512abe9ee16318c366494c17a", size = 556523, upload-time = "2025-08-27T12:16:12.188Z" },
- { url = "https://files.pythonhosted.org/packages/4e/ea/5463cd5048a7a2fcdae308b6e96432802132c141bfb9420260142632a0f1/rpds_py-0.27.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:aa8933159edc50be265ed22b401125c9eebff3171f570258854dbce3ecd55475", size = 371778, upload-time = "2025-08-27T12:16:13.851Z" },
- { url = "https://files.pythonhosted.org/packages/0d/c8/f38c099db07f5114029c1467649d308543906933eebbc226d4527a5f4693/rpds_py-0.27.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:a50431bf02583e21bf273c71b89d710e7a710ad5e39c725b14e685610555926f", size = 354394, upload-time = "2025-08-27T12:16:15.609Z" },
- { url = "https://files.pythonhosted.org/packages/7d/79/b76f97704d9dd8ddbd76fed4c4048153a847c5d6003afe20a6b5c3339065/rpds_py-0.27.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78af06ddc7fe5cc0e967085a9115accee665fb912c22a3f54bad70cc65b05fe6", size = 382348, upload-time = "2025-08-27T12:16:17.251Z" },
- { url = "https://files.pythonhosted.org/packages/8a/3f/ef23d3c1be1b837b648a3016d5bbe7cfe711422ad110b4081c0a90ef5a53/rpds_py-0.27.1-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:70d0738ef8fee13c003b100c2fbd667ec4f133468109b3472d249231108283a3", size = 394159, upload-time = "2025-08-27T12:16:19.251Z" },
- { url = "https://files.pythonhosted.org/packages/74/8a/9e62693af1a34fd28b1a190d463d12407bd7cf561748cb4745845d9548d3/rpds_py-0.27.1-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e2f6fd8a1cea5bbe599b6e78a6e5ee08db434fc8ffea51ff201c8765679698b3", size = 522775, upload-time = "2025-08-27T12:16:20.929Z" },
- { url = "https://files.pythonhosted.org/packages/36/0d/8d5bb122bf7a60976b54c5c99a739a3819f49f02d69df3ea2ca2aff47d5c/rpds_py-0.27.1-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8177002868d1426305bb5de1e138161c2ec9eb2d939be38291d7c431c4712df8", size = 402633, upload-time = "2025-08-27T12:16:22.548Z" },
- { url = "https://files.pythonhosted.org/packages/0f/0e/237948c1f425e23e0cf5a566d702652a6e55c6f8fbd332a1792eb7043daf/rpds_py-0.27.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:008b839781d6c9bf3b6a8984d1d8e56f0ec46dc56df61fd669c49b58ae800400", size = 384867, upload-time = "2025-08-27T12:16:24.29Z" },
- { url = "https://files.pythonhosted.org/packages/d6/0a/da0813efcd998d260cbe876d97f55b0f469ada8ba9cbc47490a132554540/rpds_py-0.27.1-pp39-pypy39_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:a55b9132bb1ade6c734ddd2759c8dc132aa63687d259e725221f106b83a0e485", size = 401791, upload-time = "2025-08-27T12:16:25.954Z" },
- { url = "https://files.pythonhosted.org/packages/51/78/c6c9e8a8aaca416a6f0d1b6b4a6ee35b88fe2c5401d02235d0a056eceed2/rpds_py-0.27.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a46fdec0083a26415f11d5f236b79fa1291c32aaa4a17684d82f7017a1f818b1", size = 419525, upload-time = "2025-08-27T12:16:27.659Z" },
- { url = "https://files.pythonhosted.org/packages/a3/69/5af37e1d71487cf6d56dd1420dc7e0c2732c1b6ff612aa7a88374061c0a8/rpds_py-0.27.1-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:8a63b640a7845f2bdd232eb0d0a4a2dd939bcdd6c57e6bb134526487f3160ec5", size = 559255, upload-time = "2025-08-27T12:16:29.343Z" },
- { url = "https://files.pythonhosted.org/packages/40/7f/8b7b136069ef7ac3960eda25d832639bdb163018a34c960ed042dd1707c8/rpds_py-0.27.1-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:7e32721e5d4922deaaf963469d795d5bde6093207c52fec719bd22e5d1bedbc4", size = 590384, upload-time = "2025-08-27T12:16:31.005Z" },
- { url = "https://files.pythonhosted.org/packages/d8/06/c316d3f6ff03f43ccb0eba7de61376f8ec4ea850067dddfafe98274ae13c/rpds_py-0.27.1-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:2c426b99a068601b5f4623573df7a7c3d72e87533a2dd2253353a03e7502566c", size = 555959, upload-time = "2025-08-27T12:16:32.73Z" },
- { url = "https://files.pythonhosted.org/packages/60/94/384cf54c430b9dac742bbd2ec26c23feb78ded0d43d6d78563a281aec017/rpds_py-0.27.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:4fc9b7fe29478824361ead6e14e4f5aed570d477e06088826537e202d25fe859", size = 228784, upload-time = "2025-08-27T12:16:34.428Z" },
-]
-
-[[package]]
-name = "rsa"
-version = "4.9.1"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "pyasn1" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/da/8a/22b7beea3ee0d44b1916c0c1cb0ee3af23b700b6da9f04991899d0c555d4/rsa-4.9.1.tar.gz", hash = "sha256:e7bdbfdb5497da4c07dfd35530e1a902659db6ff241e39d9953cad06ebd0ae75", size = 29034, upload-time = "2025-04-16T09:51:18.218Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/64/8d/0133e4eb4beed9e425d9a98ed6e081a55d195481b7632472be1af08d2f6b/rsa-4.9.1-py3-none-any.whl", hash = "sha256:68635866661c6836b8d39430f97a996acbd61bfa49406748ea243539fe239762", size = 34696, upload-time = "2025-04-16T09:51:17.142Z" },
-]
-
-[[package]]
-name = "ruamel-yaml"
-version = "0.18.15"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "ruamel-yaml-clib", marker = "python_full_version < '3.14' and platform_python_implementation == 'CPython'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/3e/db/f3950f5e5031b618aae9f423a39bf81a55c148aecd15a34527898e752cf4/ruamel.yaml-0.18.15.tar.gz", hash = "sha256:dbfca74b018c4c3fba0b9cc9ee33e53c371194a9000e694995e620490fd40700", size = 146865, upload-time = "2025-08-19T11:15:10.694Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/d1/e5/f2a0621f1781b76a38194acae72f01e37b1941470407345b6e8653ad7640/ruamel.yaml-0.18.15-py3-none-any.whl", hash = "sha256:148f6488d698b7a5eded5ea793a025308b25eca97208181b6a026037f391f701", size = 119702, upload-time = "2025-08-19T11:15:07.696Z" },
-]
-
-[[package]]
-name = "ruamel-yaml-clib"
-version = "0.2.12"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/20/84/80203abff8ea4993a87d823a5f632e4d92831ef75d404c9fc78d0176d2b5/ruamel.yaml.clib-0.2.12.tar.gz", hash = "sha256:6c8fbb13ec503f99a91901ab46e0b07ae7941cd527393187039aec586fdfd36f", size = 225315, upload-time = "2024-10-20T10:10:56.22Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/70/57/40a958e863e299f0c74ef32a3bde9f2d1ea8d69669368c0c502a0997f57f/ruamel.yaml.clib-0.2.12-cp310-cp310-macosx_13_0_arm64.whl", hash = "sha256:11f891336688faf5156a36293a9c362bdc7c88f03a8a027c2c1d8e0bcde998e5", size = 131301, upload-time = "2024-10-20T10:12:35.876Z" },
- { url = "https://files.pythonhosted.org/packages/98/a8/29a3eb437b12b95f50a6bcc3d7d7214301c6c529d8fdc227247fa84162b5/ruamel.yaml.clib-0.2.12-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:a606ef75a60ecf3d924613892cc603b154178ee25abb3055db5062da811fd969", size = 633728, upload-time = "2024-10-20T10:12:37.858Z" },
- { url = "https://files.pythonhosted.org/packages/35/6d/ae05a87a3ad540259c3ad88d71275cbd1c0f2d30ae04c65dcbfb6dcd4b9f/ruamel.yaml.clib-0.2.12-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd5415dded15c3822597455bc02bcd66e81ef8b7a48cb71a33628fc9fdde39df", size = 722230, upload-time = "2024-10-20T10:12:39.457Z" },
- { url = "https://files.pythonhosted.org/packages/7f/b7/20c6f3c0b656fe609675d69bc135c03aac9e3865912444be6339207b6648/ruamel.yaml.clib-0.2.12-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f66efbc1caa63c088dead1c4170d148eabc9b80d95fb75b6c92ac0aad2437d76", size = 686712, upload-time = "2024-10-20T10:12:41.119Z" },
- { url = "https://files.pythonhosted.org/packages/cd/11/d12dbf683471f888d354dac59593873c2b45feb193c5e3e0f2ebf85e68b9/ruamel.yaml.clib-0.2.12-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:22353049ba4181685023b25b5b51a574bce33e7f51c759371a7422dcae5402a6", size = 663936, upload-time = "2024-10-21T11:26:37.419Z" },
- { url = "https://files.pythonhosted.org/packages/72/14/4c268f5077db5c83f743ee1daeb236269fa8577133a5cfa49f8b382baf13/ruamel.yaml.clib-0.2.12-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:932205970b9f9991b34f55136be327501903f7c66830e9760a8ffb15b07f05cd", size = 696580, upload-time = "2024-10-21T11:26:39.503Z" },
- { url = "https://files.pythonhosted.org/packages/30/fc/8cd12f189c6405a4c1cf37bd633aa740a9538c8e40497c231072d0fef5cf/ruamel.yaml.clib-0.2.12-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a52d48f4e7bf9005e8f0a89209bf9a73f7190ddf0489eee5eb51377385f59f2a", size = 663393, upload-time = "2024-12-11T19:58:13.873Z" },
- { url = "https://files.pythonhosted.org/packages/80/29/c0a017b704aaf3cbf704989785cd9c5d5b8ccec2dae6ac0c53833c84e677/ruamel.yaml.clib-0.2.12-cp310-cp310-win32.whl", hash = "sha256:3eac5a91891ceb88138c113f9db04f3cebdae277f5d44eaa3651a4f573e6a5da", size = 100326, upload-time = "2024-10-20T10:12:42.967Z" },
- { url = "https://files.pythonhosted.org/packages/3a/65/fa39d74db4e2d0cd252355732d966a460a41cd01c6353b820a0952432839/ruamel.yaml.clib-0.2.12-cp310-cp310-win_amd64.whl", hash = "sha256:ab007f2f5a87bd08ab1499bdf96f3d5c6ad4dcfa364884cb4549aa0154b13a28", size = 118079, upload-time = "2024-10-20T10:12:44.117Z" },
- { url = "https://files.pythonhosted.org/packages/fb/8f/683c6ad562f558cbc4f7c029abcd9599148c51c54b5ef0f24f2638da9fbb/ruamel.yaml.clib-0.2.12-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:4a6679521a58256a90b0d89e03992c15144c5f3858f40d7c18886023d7943db6", size = 132224, upload-time = "2024-10-20T10:12:45.162Z" },
- { url = "https://files.pythonhosted.org/packages/3c/d2/b79b7d695e2f21da020bd44c782490578f300dd44f0a4c57a92575758a76/ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:d84318609196d6bd6da0edfa25cedfbabd8dbde5140a0a23af29ad4b8f91fb1e", size = 641480, upload-time = "2024-10-20T10:12:46.758Z" },
- { url = "https://files.pythonhosted.org/packages/68/6e/264c50ce2a31473a9fdbf4fa66ca9b2b17c7455b31ef585462343818bd6c/ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb43a269eb827806502c7c8efb7ae7e9e9d0573257a46e8e952f4d4caba4f31e", size = 739068, upload-time = "2024-10-20T10:12:48.605Z" },
- { url = "https://files.pythonhosted.org/packages/86/29/88c2567bc893c84d88b4c48027367c3562ae69121d568e8a3f3a8d363f4d/ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:811ea1594b8a0fb466172c384267a4e5e367298af6b228931f273b111f17ef52", size = 703012, upload-time = "2024-10-20T10:12:51.124Z" },
- { url = "https://files.pythonhosted.org/packages/11/46/879763c619b5470820f0cd6ca97d134771e502776bc2b844d2adb6e37753/ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:cf12567a7b565cbf65d438dec6cfbe2917d3c1bdddfce84a9930b7d35ea59642", size = 704352, upload-time = "2024-10-21T11:26:41.438Z" },
- { url = "https://files.pythonhosted.org/packages/02/80/ece7e6034256a4186bbe50dee28cd032d816974941a6abf6a9d65e4228a7/ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7dd5adc8b930b12c8fc5b99e2d535a09889941aa0d0bd06f4749e9a9397c71d2", size = 737344, upload-time = "2024-10-21T11:26:43.62Z" },
- { url = "https://files.pythonhosted.org/packages/f0/ca/e4106ac7e80efbabdf4bf91d3d32fc424e41418458251712f5672eada9ce/ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1492a6051dab8d912fc2adeef0e8c72216b24d57bd896ea607cb90bb0c4981d3", size = 714498, upload-time = "2024-12-11T19:58:15.592Z" },
- { url = "https://files.pythonhosted.org/packages/67/58/b1f60a1d591b771298ffa0428237afb092c7f29ae23bad93420b1eb10703/ruamel.yaml.clib-0.2.12-cp311-cp311-win32.whl", hash = "sha256:bd0a08f0bab19093c54e18a14a10b4322e1eacc5217056f3c063bd2f59853ce4", size = 100205, upload-time = "2024-10-20T10:12:52.865Z" },
- { url = "https://files.pythonhosted.org/packages/b4/4f/b52f634c9548a9291a70dfce26ca7ebce388235c93588a1068028ea23fcc/ruamel.yaml.clib-0.2.12-cp311-cp311-win_amd64.whl", hash = "sha256:a274fb2cb086c7a3dea4322ec27f4cb5cc4b6298adb583ab0e211a4682f241eb", size = 118185, upload-time = "2024-10-20T10:12:54.652Z" },
- { url = "https://files.pythonhosted.org/packages/48/41/e7a405afbdc26af961678474a55373e1b323605a4f5e2ddd4a80ea80f628/ruamel.yaml.clib-0.2.12-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:20b0f8dc160ba83b6dcc0e256846e1a02d044e13f7ea74a3d1d56ede4e48c632", size = 133433, upload-time = "2024-10-20T10:12:55.657Z" },
- { url = "https://files.pythonhosted.org/packages/ec/b0/b850385604334c2ce90e3ee1013bd911aedf058a934905863a6ea95e9eb4/ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:943f32bc9dedb3abff9879edc134901df92cfce2c3d5c9348f172f62eb2d771d", size = 647362, upload-time = "2024-10-20T10:12:57.155Z" },
- { url = "https://files.pythonhosted.org/packages/44/d0/3f68a86e006448fb6c005aee66565b9eb89014a70c491d70c08de597f8e4/ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95c3829bb364fdb8e0332c9931ecf57d9be3519241323c5274bd82f709cebc0c", size = 754118, upload-time = "2024-10-20T10:12:58.501Z" },
- { url = "https://files.pythonhosted.org/packages/52/a9/d39f3c5ada0a3bb2870d7db41901125dbe2434fa4f12ca8c5b83a42d7c53/ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:749c16fcc4a2b09f28843cda5a193e0283e47454b63ec4b81eaa2242f50e4ccd", size = 706497, upload-time = "2024-10-20T10:13:00.211Z" },
- { url = "https://files.pythonhosted.org/packages/b0/fa/097e38135dadd9ac25aecf2a54be17ddf6e4c23e43d538492a90ab3d71c6/ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bf165fef1f223beae7333275156ab2022cffe255dcc51c27f066b4370da81e31", size = 698042, upload-time = "2024-10-21T11:26:46.038Z" },
- { url = "https://files.pythonhosted.org/packages/ec/d5/a659ca6f503b9379b930f13bc6b130c9f176469b73b9834296822a83a132/ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:32621c177bbf782ca5a18ba4d7af0f1082a3f6e517ac2a18b3974d4edf349680", size = 745831, upload-time = "2024-10-21T11:26:47.487Z" },
- { url = "https://files.pythonhosted.org/packages/db/5d/36619b61ffa2429eeaefaab4f3374666adf36ad8ac6330d855848d7d36fd/ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b82a7c94a498853aa0b272fd5bc67f29008da798d4f93a2f9f289feb8426a58d", size = 715692, upload-time = "2024-12-11T19:58:17.252Z" },
- { url = "https://files.pythonhosted.org/packages/b1/82/85cb92f15a4231c89b95dfe08b09eb6adca929ef7df7e17ab59902b6f589/ruamel.yaml.clib-0.2.12-cp312-cp312-win32.whl", hash = "sha256:e8c4ebfcfd57177b572e2040777b8abc537cdef58a2120e830124946aa9b42c5", size = 98777, upload-time = "2024-10-20T10:13:01.395Z" },
- { url = "https://files.pythonhosted.org/packages/d7/8f/c3654f6f1ddb75daf3922c3d8fc6005b1ab56671ad56ffb874d908bfa668/ruamel.yaml.clib-0.2.12-cp312-cp312-win_amd64.whl", hash = "sha256:0467c5965282c62203273b838ae77c0d29d7638c8a4e3a1c8bdd3602c10904e4", size = 115523, upload-time = "2024-10-20T10:13:02.768Z" },
- { url = "https://files.pythonhosted.org/packages/29/00/4864119668d71a5fa45678f380b5923ff410701565821925c69780356ffa/ruamel.yaml.clib-0.2.12-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:4c8c5d82f50bb53986a5e02d1b3092b03622c02c2eb78e29bec33fd9593bae1a", size = 132011, upload-time = "2024-10-20T10:13:04.377Z" },
- { url = "https://files.pythonhosted.org/packages/7f/5e/212f473a93ae78c669ffa0cb051e3fee1139cb2d385d2ae1653d64281507/ruamel.yaml.clib-0.2.12-cp313-cp313-manylinux2014_aarch64.whl", hash = "sha256:e7e3736715fbf53e9be2a79eb4db68e4ed857017344d697e8b9749444ae57475", size = 642488, upload-time = "2024-10-20T10:13:05.906Z" },
- { url = "https://files.pythonhosted.org/packages/1f/8f/ecfbe2123ade605c49ef769788f79c38ddb1c8fa81e01f4dbf5cf1a44b16/ruamel.yaml.clib-0.2.12-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b7e75b4965e1d4690e93021adfcecccbca7d61c7bddd8e22406ef2ff20d74ef", size = 745066, upload-time = "2024-10-20T10:13:07.26Z" },
- { url = "https://files.pythonhosted.org/packages/e2/a9/28f60726d29dfc01b8decdb385de4ced2ced9faeb37a847bd5cf26836815/ruamel.yaml.clib-0.2.12-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96777d473c05ee3e5e3c3e999f5d23c6f4ec5b0c38c098b3a5229085f74236c6", size = 701785, upload-time = "2024-10-20T10:13:08.504Z" },
- { url = "https://files.pythonhosted.org/packages/84/7e/8e7ec45920daa7f76046578e4f677a3215fe8f18ee30a9cb7627a19d9b4c/ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:3bc2a80e6420ca8b7d3590791e2dfc709c88ab9152c00eeb511c9875ce5778bf", size = 693017, upload-time = "2024-10-21T11:26:48.866Z" },
- { url = "https://files.pythonhosted.org/packages/c5/b3/d650eaade4ca225f02a648321e1ab835b9d361c60d51150bac49063b83fa/ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:e188d2699864c11c36cdfdada94d781fd5d6b0071cd9c427bceb08ad3d7c70e1", size = 741270, upload-time = "2024-10-21T11:26:50.213Z" },
- { url = "https://files.pythonhosted.org/packages/87/b8/01c29b924dcbbed75cc45b30c30d565d763b9c4d540545a0eeecffb8f09c/ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4f6f3eac23941b32afccc23081e1f50612bdbe4e982012ef4f5797986828cd01", size = 709059, upload-time = "2024-12-11T19:58:18.846Z" },
- { url = "https://files.pythonhosted.org/packages/30/8c/ed73f047a73638257aa9377ad356bea4d96125b305c34a28766f4445cc0f/ruamel.yaml.clib-0.2.12-cp313-cp313-win32.whl", hash = "sha256:6442cb36270b3afb1b4951f060eccca1ce49f3d087ca1ca4563a6eb479cb3de6", size = 98583, upload-time = "2024-10-20T10:13:09.658Z" },
- { url = "https://files.pythonhosted.org/packages/b0/85/e8e751d8791564dd333d5d9a4eab0a7a115f7e349595417fd50ecae3395c/ruamel.yaml.clib-0.2.12-cp313-cp313-win_amd64.whl", hash = "sha256:e5b8daf27af0b90da7bb903a876477a9e6d7270be6146906b276605997c7e9a3", size = 115190, upload-time = "2024-10-20T10:13:10.66Z" },
- { url = "https://files.pythonhosted.org/packages/e5/46/ccdef7a84ad745c37cb3d9a81790f28fbc9adf9c237dba682017b123294e/ruamel.yaml.clib-0.2.12-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:fc4b630cd3fa2cf7fce38afa91d7cfe844a9f75d7f0f36393fa98815e911d987", size = 131834, upload-time = "2024-10-20T10:13:11.72Z" },
- { url = "https://files.pythonhosted.org/packages/29/09/932360f30ad1b7b79f08757e0a6fb8c5392a52cdcc182779158fe66d25ac/ruamel.yaml.clib-0.2.12-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:bc5f1e1c28e966d61d2519f2a3d451ba989f9ea0f2307de7bc45baa526de9e45", size = 636120, upload-time = "2024-10-20T10:13:12.84Z" },
- { url = "https://files.pythonhosted.org/packages/a2/2a/5b27602e7a4344c1334e26bf4739746206b7a60a8acdba33a61473468b73/ruamel.yaml.clib-0.2.12-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a0e060aace4c24dcaf71023bbd7d42674e3b230f7e7b97317baf1e953e5b519", size = 724914, upload-time = "2024-10-20T10:13:14.605Z" },
- { url = "https://files.pythonhosted.org/packages/da/1c/23497017c554fc06ff5701b29355522cff850f626337fff35d9ab352cb18/ruamel.yaml.clib-0.2.12-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e2f1c3765db32be59d18ab3953f43ab62a761327aafc1594a2a1fbe038b8b8a7", size = 689072, upload-time = "2024-10-20T10:13:15.939Z" },
- { url = "https://files.pythonhosted.org/packages/68/e6/f3d4ff3223f9ea49c3b7169ec0268e42bd49f87c70c0e3e853895e4a7ae2/ruamel.yaml.clib-0.2.12-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:d85252669dc32f98ebcd5d36768f5d4faeaeaa2d655ac0473be490ecdae3c285", size = 667091, upload-time = "2024-10-21T11:26:52.274Z" },
- { url = "https://files.pythonhosted.org/packages/84/62/ead07043527642491e5011b143f44b81ef80f1025a96069b7210e0f2f0f3/ruamel.yaml.clib-0.2.12-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e143ada795c341b56de9418c58d028989093ee611aa27ffb9b7f609c00d813ed", size = 699111, upload-time = "2024-10-21T11:26:54.294Z" },
- { url = "https://files.pythonhosted.org/packages/52/b3/fe4d84446f7e4887e3bea7ceff0a7df23790b5ed625f830e79ace88ebefb/ruamel.yaml.clib-0.2.12-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2c59aa6170b990d8d2719323e628aaf36f3bfbc1c26279c0eeeb24d05d2d11c7", size = 666365, upload-time = "2024-12-11T19:58:20.444Z" },
- { url = "https://files.pythonhosted.org/packages/6e/b3/7feb99a00bfaa5c6868617bb7651308afde85e5a0b23cd187fe5de65feeb/ruamel.yaml.clib-0.2.12-cp39-cp39-win32.whl", hash = "sha256:beffaed67936fbbeffd10966a4eb53c402fafd3d6833770516bf7314bc6ffa12", size = 100863, upload-time = "2024-10-20T10:13:17.244Z" },
- { url = "https://files.pythonhosted.org/packages/93/07/de635108684b7a5bb06e432b0930c5a04b6c59efe73bd966d8db3cc208f2/ruamel.yaml.clib-0.2.12-cp39-cp39-win_amd64.whl", hash = "sha256:040ae85536960525ea62868b642bdb0c2cc6021c9f9d507810c0c604e66f5a7b", size = 118653, upload-time = "2024-10-20T10:13:18.289Z" },
-]
-
-[[package]]
-name = "s3transfer"
-version = "0.13.1"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "botocore" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/6d/05/d52bf1e65044b4e5e27d4e63e8d1579dbdec54fce685908ae09bc3720030/s3transfer-0.13.1.tar.gz", hash = "sha256:c3fdba22ba1bd367922f27ec8032d6a1cf5f10c934fb5d68cf60fd5a23d936cf", size = 150589, upload-time = "2025-07-18T19:22:42.31Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/6d/4f/d073e09df851cfa251ef7840007d04db3293a0482ce607d2b993926089be/s3transfer-0.13.1-py3-none-any.whl", hash = "sha256:a981aa7429be23fe6dfc13e80e4020057cbab622b08c0315288758d67cabc724", size = 85308, upload-time = "2025-07-18T19:22:40.947Z" },
-]
-
-[[package]]
-name = "safetensors"
-version = "0.6.2"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/ac/cc/738f3011628920e027a11754d9cae9abec1aed00f7ae860abbf843755233/safetensors-0.6.2.tar.gz", hash = "sha256:43ff2aa0e6fa2dc3ea5524ac7ad93a9839256b8703761e76e2d0b2a3fa4f15d9", size = 197968, upload-time = "2025-08-08T13:13:58.654Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/4d/b1/3f5fd73c039fc87dba3ff8b5d528bfc5a32b597fea8e7a6a4800343a17c7/safetensors-0.6.2-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:9c85ede8ec58f120bad982ec47746981e210492a6db876882aa021446af8ffba", size = 454797, upload-time = "2025-08-08T13:13:52.066Z" },
- { url = "https://files.pythonhosted.org/packages/8c/c9/bb114c158540ee17907ec470d01980957fdaf87b4aa07914c24eba87b9c6/safetensors-0.6.2-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:d6675cf4b39c98dbd7d940598028f3742e0375a6b4d4277e76beb0c35f4b843b", size = 432206, upload-time = "2025-08-08T13:13:50.931Z" },
- { url = "https://files.pythonhosted.org/packages/d3/8e/f70c34e47df3110e8e0bb268d90db8d4be8958a54ab0336c9be4fe86dac8/safetensors-0.6.2-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d2d2b3ce1e2509c68932ca03ab8f20570920cd9754b05063d4368ee52833ecd", size = 473261, upload-time = "2025-08-08T13:13:41.259Z" },
- { url = "https://files.pythonhosted.org/packages/2a/f5/be9c6a7c7ef773e1996dc214e73485286df1836dbd063e8085ee1976f9cb/safetensors-0.6.2-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:93de35a18f46b0f5a6a1f9e26d91b442094f2df02e9fd7acf224cfec4238821a", size = 485117, upload-time = "2025-08-08T13:13:43.506Z" },
- { url = "https://files.pythonhosted.org/packages/c9/55/23f2d0a2c96ed8665bf17a30ab4ce5270413f4d74b6d87dd663258b9af31/safetensors-0.6.2-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:89a89b505f335640f9120fac65ddeb83e40f1fd081cb8ed88b505bdccec8d0a1", size = 616154, upload-time = "2025-08-08T13:13:45.096Z" },
- { url = "https://files.pythonhosted.org/packages/98/c6/affb0bd9ce02aa46e7acddbe087912a04d953d7a4d74b708c91b5806ef3f/safetensors-0.6.2-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fc4d0d0b937e04bdf2ae6f70cd3ad51328635fe0e6214aa1fc811f3b576b3bda", size = 520713, upload-time = "2025-08-08T13:13:46.25Z" },
- { url = "https://files.pythonhosted.org/packages/fe/5d/5a514d7b88e310c8b146e2404e0dc161282e78634d9358975fd56dfd14be/safetensors-0.6.2-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8045db2c872db8f4cbe3faa0495932d89c38c899c603f21e9b6486951a5ecb8f", size = 485835, upload-time = "2025-08-08T13:13:49.373Z" },
- { url = "https://files.pythonhosted.org/packages/7a/7b/4fc3b2ba62c352b2071bea9cfbad330fadda70579f617506ae1a2f129cab/safetensors-0.6.2-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:81e67e8bab9878bb568cffbc5f5e655adb38d2418351dc0859ccac158f753e19", size = 521503, upload-time = "2025-08-08T13:13:47.651Z" },
- { url = "https://files.pythonhosted.org/packages/5a/50/0057e11fe1f3cead9254315a6c106a16dd4b1a19cd247f7cc6414f6b7866/safetensors-0.6.2-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:b0e4d029ab0a0e0e4fdf142b194514695b1d7d3735503ba700cf36d0fc7136ce", size = 652256, upload-time = "2025-08-08T13:13:53.167Z" },
- { url = "https://files.pythonhosted.org/packages/e9/29/473f789e4ac242593ac1656fbece6e1ecd860bb289e635e963667807afe3/safetensors-0.6.2-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:fa48268185c52bfe8771e46325a1e21d317207bcabcb72e65c6e28e9ffeb29c7", size = 747281, upload-time = "2025-08-08T13:13:54.656Z" },
- { url = "https://files.pythonhosted.org/packages/68/52/f7324aad7f2df99e05525c84d352dc217e0fa637a4f603e9f2eedfbe2c67/safetensors-0.6.2-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:d83c20c12c2d2f465997c51b7ecb00e407e5f94d7dec3ea0cc11d86f60d3fde5", size = 692286, upload-time = "2025-08-08T13:13:55.884Z" },
- { url = "https://files.pythonhosted.org/packages/ad/fe/cad1d9762868c7c5dc70c8620074df28ebb1a8e4c17d4c0cb031889c457e/safetensors-0.6.2-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:d944cea65fad0ead848b6ec2c37cc0b197194bec228f8020054742190e9312ac", size = 655957, upload-time = "2025-08-08T13:13:57.029Z" },
- { url = "https://files.pythonhosted.org/packages/59/a7/e2158e17bbe57d104f0abbd95dff60dda916cf277c9f9663b4bf9bad8b6e/safetensors-0.6.2-cp38-abi3-win32.whl", hash = "sha256:cab75ca7c064d3911411461151cb69380c9225798a20e712b102edda2542ddb1", size = 308926, upload-time = "2025-08-08T13:14:01.095Z" },
- { url = "https://files.pythonhosted.org/packages/2c/c3/c0be1135726618dc1e28d181b8c442403d8dbb9e273fd791de2d4384bcdd/safetensors-0.6.2-cp38-abi3-win_amd64.whl", hash = "sha256:c7b214870df923cbc1593c3faee16bec59ea462758699bd3fee399d00aac072c", size = 320192, upload-time = "2025-08-08T13:13:59.467Z" },
-]
-
-[[package]]
-name = "scikit-learn"
-version = "1.6.1"
-source = { registry = "https://pypi.org/simple" }
-resolution-markers = [
- "python_full_version < '3.10'",
-]
-dependencies = [
- { name = "joblib", marker = "python_full_version < '3.10'" },
- { name = "numpy", marker = "python_full_version < '3.10'" },
- { name = "scipy", version = "1.13.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
- { name = "threadpoolctl", marker = "python_full_version < '3.10'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/9e/a5/4ae3b3a0755f7b35a280ac90b28817d1f380318973cff14075ab41ef50d9/scikit_learn-1.6.1.tar.gz", hash = "sha256:b4fc2525eca2c69a59260f583c56a7557c6ccdf8deafdba6e060f94c1c59738e", size = 7068312, upload-time = "2025-01-10T08:07:55.348Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/2e/3a/f4597eb41049110b21ebcbb0bcb43e4035017545daa5eedcfeb45c08b9c5/scikit_learn-1.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d056391530ccd1e501056160e3c9673b4da4805eb67eb2bdf4e983e1f9c9204e", size = 12067702, upload-time = "2025-01-10T08:05:56.515Z" },
- { url = "https://files.pythonhosted.org/packages/37/19/0423e5e1fd1c6ec5be2352ba05a537a473c1677f8188b9306097d684b327/scikit_learn-1.6.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:0c8d036eb937dbb568c6242fa598d551d88fb4399c0344d95c001980ec1c7d36", size = 11112765, upload-time = "2025-01-10T08:06:00.272Z" },
- { url = "https://files.pythonhosted.org/packages/70/95/d5cb2297a835b0f5fc9a77042b0a2d029866379091ab8b3f52cc62277808/scikit_learn-1.6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8634c4bd21a2a813e0a7e3900464e6d593162a29dd35d25bdf0103b3fce60ed5", size = 12643991, upload-time = "2025-01-10T08:06:04.813Z" },
- { url = "https://files.pythonhosted.org/packages/b7/91/ab3c697188f224d658969f678be86b0968ccc52774c8ab4a86a07be13c25/scikit_learn-1.6.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:775da975a471c4f6f467725dff0ced5c7ac7bda5e9316b260225b48475279a1b", size = 13497182, upload-time = "2025-01-10T08:06:08.42Z" },
- { url = "https://files.pythonhosted.org/packages/17/04/d5d556b6c88886c092cc989433b2bab62488e0f0dafe616a1d5c9cb0efb1/scikit_learn-1.6.1-cp310-cp310-win_amd64.whl", hash = "sha256:8a600c31592bd7dab31e1c61b9bbd6dea1b3433e67d264d17ce1017dbdce8002", size = 11125517, upload-time = "2025-01-10T08:06:12.783Z" },
- { url = "https://files.pythonhosted.org/packages/6c/2a/e291c29670795406a824567d1dfc91db7b699799a002fdaa452bceea8f6e/scikit_learn-1.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:72abc587c75234935e97d09aa4913a82f7b03ee0b74111dcc2881cba3c5a7b33", size = 12102620, upload-time = "2025-01-10T08:06:16.675Z" },
- { url = "https://files.pythonhosted.org/packages/25/92/ee1d7a00bb6b8c55755d4984fd82608603a3cc59959245068ce32e7fb808/scikit_learn-1.6.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:b3b00cdc8f1317b5f33191df1386c0befd16625f49d979fe77a8d44cae82410d", size = 11116234, upload-time = "2025-01-10T08:06:21.83Z" },
- { url = "https://files.pythonhosted.org/packages/30/cd/ed4399485ef364bb25f388ab438e3724e60dc218c547a407b6e90ccccaef/scikit_learn-1.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc4765af3386811c3ca21638f63b9cf5ecf66261cc4815c1db3f1e7dc7b79db2", size = 12592155, upload-time = "2025-01-10T08:06:27.309Z" },
- { url = "https://files.pythonhosted.org/packages/a8/f3/62fc9a5a659bb58a03cdd7e258956a5824bdc9b4bb3c5d932f55880be569/scikit_learn-1.6.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:25fc636bdaf1cc2f4a124a116312d837148b5e10872147bdaf4887926b8c03d8", size = 13497069, upload-time = "2025-01-10T08:06:32.515Z" },
- { url = "https://files.pythonhosted.org/packages/a1/a6/c5b78606743a1f28eae8f11973de6613a5ee87366796583fb74c67d54939/scikit_learn-1.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:fa909b1a36e000a03c382aade0bd2063fd5680ff8b8e501660c0f59f021a6415", size = 11139809, upload-time = "2025-01-10T08:06:35.514Z" },
- { url = "https://files.pythonhosted.org/packages/0a/18/c797c9b8c10380d05616db3bfb48e2a3358c767affd0857d56c2eb501caa/scikit_learn-1.6.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:926f207c804104677af4857b2c609940b743d04c4c35ce0ddc8ff4f053cddc1b", size = 12104516, upload-time = "2025-01-10T08:06:40.009Z" },
- { url = "https://files.pythonhosted.org/packages/c4/b7/2e35f8e289ab70108f8cbb2e7a2208f0575dc704749721286519dcf35f6f/scikit_learn-1.6.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:2c2cae262064e6a9b77eee1c8e768fc46aa0b8338c6a8297b9b6759720ec0ff2", size = 11167837, upload-time = "2025-01-10T08:06:43.305Z" },
- { url = "https://files.pythonhosted.org/packages/a4/f6/ff7beaeb644bcad72bcfd5a03ff36d32ee4e53a8b29a639f11bcb65d06cd/scikit_learn-1.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1061b7c028a8663fb9a1a1baf9317b64a257fcb036dae5c8752b2abef31d136f", size = 12253728, upload-time = "2025-01-10T08:06:47.618Z" },
- { url = "https://files.pythonhosted.org/packages/29/7a/8bce8968883e9465de20be15542f4c7e221952441727c4dad24d534c6d99/scikit_learn-1.6.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e69fab4ebfc9c9b580a7a80111b43d214ab06250f8a7ef590a4edf72464dd86", size = 13147700, upload-time = "2025-01-10T08:06:50.888Z" },
- { url = "https://files.pythonhosted.org/packages/62/27/585859e72e117fe861c2079bcba35591a84f801e21bc1ab85bce6ce60305/scikit_learn-1.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:70b1d7e85b1c96383f872a519b3375f92f14731e279a7b4c6cfd650cf5dffc52", size = 11110613, upload-time = "2025-01-10T08:06:54.115Z" },
- { url = "https://files.pythonhosted.org/packages/2e/59/8eb1872ca87009bdcdb7f3cdc679ad557b992c12f4b61f9250659e592c63/scikit_learn-1.6.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2ffa1e9e25b3d93990e74a4be2c2fc61ee5af85811562f1288d5d055880c4322", size = 12010001, upload-time = "2025-01-10T08:06:58.613Z" },
- { url = "https://files.pythonhosted.org/packages/9d/05/f2fc4effc5b32e525408524c982c468c29d22f828834f0625c5ef3d601be/scikit_learn-1.6.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:dc5cf3d68c5a20ad6d571584c0750ec641cc46aeef1c1507be51300e6003a7e1", size = 11096360, upload-time = "2025-01-10T08:07:01.556Z" },
- { url = "https://files.pythonhosted.org/packages/c8/e4/4195d52cf4f113573fb8ebc44ed5a81bd511a92c0228889125fac2f4c3d1/scikit_learn-1.6.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c06beb2e839ecc641366000ca84f3cf6fa9faa1777e29cf0c04be6e4d096a348", size = 12209004, upload-time = "2025-01-10T08:07:06.931Z" },
- { url = "https://files.pythonhosted.org/packages/94/be/47e16cdd1e7fcf97d95b3cb08bde1abb13e627861af427a3651fcb80b517/scikit_learn-1.6.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e8ca8cb270fee8f1f76fa9bfd5c3507d60c6438bbee5687f81042e2bb98e5a97", size = 13171776, upload-time = "2025-01-10T08:07:11.715Z" },
- { url = "https://files.pythonhosted.org/packages/34/b0/ca92b90859070a1487827dbc672f998da95ce83edce1270fc23f96f1f61a/scikit_learn-1.6.1-cp313-cp313-win_amd64.whl", hash = "sha256:7a1c43c8ec9fde528d664d947dc4c0789be4077a3647f232869f41d9bf50e0fb", size = 11071865, upload-time = "2025-01-10T08:07:16.088Z" },
- { url = "https://files.pythonhosted.org/packages/12/ae/993b0fb24a356e71e9a894e42b8a9eec528d4c70217353a1cd7a48bc25d4/scikit_learn-1.6.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a17c1dea1d56dcda2fac315712f3651a1fea86565b64b48fa1bc090249cbf236", size = 11955804, upload-time = "2025-01-10T08:07:20.385Z" },
- { url = "https://files.pythonhosted.org/packages/d6/54/32fa2ee591af44507eac86406fa6bba968d1eb22831494470d0a2e4a1eb1/scikit_learn-1.6.1-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:6a7aa5f9908f0f28f4edaa6963c0a6183f1911e63a69aa03782f0d924c830a35", size = 11100530, upload-time = "2025-01-10T08:07:23.675Z" },
- { url = "https://files.pythonhosted.org/packages/3f/58/55856da1adec655bdce77b502e94a267bf40a8c0b89f8622837f89503b5a/scikit_learn-1.6.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0650e730afb87402baa88afbf31c07b84c98272622aaba002559b614600ca691", size = 12433852, upload-time = "2025-01-10T08:07:26.817Z" },
- { url = "https://files.pythonhosted.org/packages/ff/4f/c83853af13901a574f8f13b645467285a48940f185b690936bb700a50863/scikit_learn-1.6.1-cp313-cp313t-win_amd64.whl", hash = "sha256:3f59fe08dc03ea158605170eb52b22a105f238a5d512c4470ddeca71feae8e5f", size = 11337256, upload-time = "2025-01-10T08:07:31.084Z" },
- { url = "https://files.pythonhosted.org/packages/d2/37/b305b759cc65829fe1b8853ff3e308b12cdd9d8884aa27840835560f2b42/scikit_learn-1.6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6849dd3234e87f55dce1db34c89a810b489ead832aaf4d4550b7ea85628be6c1", size = 12101868, upload-time = "2025-01-10T08:07:34.189Z" },
- { url = "https://files.pythonhosted.org/packages/83/74/f64379a4ed5879d9db744fe37cfe1978c07c66684d2439c3060d19a536d8/scikit_learn-1.6.1-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:e7be3fa5d2eb9be7d77c3734ff1d599151bb523674be9b834e8da6abe132f44e", size = 11144062, upload-time = "2025-01-10T08:07:37.67Z" },
- { url = "https://files.pythonhosted.org/packages/fd/dc/d5457e03dc9c971ce2b0d750e33148dd060fefb8b7dc71acd6054e4bb51b/scikit_learn-1.6.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:44a17798172df1d3c1065e8fcf9019183f06c87609b49a124ebdf57ae6cb0107", size = 12693173, upload-time = "2025-01-10T08:07:42.713Z" },
- { url = "https://files.pythonhosted.org/packages/79/35/b1d2188967c3204c78fa79c9263668cf1b98060e8e58d1a730fe5b2317bb/scikit_learn-1.6.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8b7a3b86e411e4bce21186e1c180d792f3d99223dcfa3b4f597ecc92fa1a422", size = 13518605, upload-time = "2025-01-10T08:07:46.551Z" },
- { url = "https://files.pythonhosted.org/packages/fb/d8/8d603bdd26601f4b07e2363032b8565ab82eb857f93d86d0f7956fcf4523/scikit_learn-1.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:7a73d457070e3318e32bdb3aa79a8d990474f19035464dfd8bede2883ab5dc3b", size = 11155078, upload-time = "2025-01-10T08:07:51.376Z" },
-]
-
-[[package]]
-name = "scikit-learn"
-version = "1.7.1"
-source = { registry = "https://pypi.org/simple" }
-resolution-markers = [
- "python_full_version >= '3.13'",
- "python_full_version >= '3.12.4' and python_full_version < '3.13'",
- "python_full_version >= '3.12' and python_full_version < '3.12.4'",
- "python_full_version == '3.11.*'",
- "python_full_version == '3.10.*'",
-]
-dependencies = [
- { name = "joblib", marker = "python_full_version >= '3.10'" },
- { name = "numpy", marker = "python_full_version >= '3.10'" },
- { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" },
- { name = "scipy", version = "1.16.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
- { name = "threadpoolctl", marker = "python_full_version >= '3.10'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/41/84/5f4af978fff619706b8961accac84780a6d298d82a8873446f72edb4ead0/scikit_learn-1.7.1.tar.gz", hash = "sha256:24b3f1e976a4665aa74ee0fcaac2b8fccc6ae77c8e07ab25da3ba6d3292b9802", size = 7190445, upload-time = "2025-07-18T08:01:54.5Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/74/88/0dd5be14ef19f2d80a77780be35a33aa94e8a3b3223d80bee8892a7832b4/scikit_learn-1.7.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:406204dd4004f0517f0b23cf4b28c6245cbd51ab1b6b78153bc784def214946d", size = 9338868, upload-time = "2025-07-18T08:01:00.25Z" },
- { url = "https://files.pythonhosted.org/packages/fd/52/3056b6adb1ac58a0bc335fc2ed2fcf599974d908855e8cb0ca55f797593c/scikit_learn-1.7.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:16af2e44164f05d04337fd1fc3ae7c4ea61fd9b0d527e22665346336920fe0e1", size = 8655943, upload-time = "2025-07-18T08:01:02.974Z" },
- { url = "https://files.pythonhosted.org/packages/fb/a4/e488acdece6d413f370a9589a7193dac79cd486b2e418d3276d6ea0b9305/scikit_learn-1.7.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2f2e78e56a40c7587dea9a28dc4a49500fa2ead366869418c66f0fd75b80885c", size = 9652056, upload-time = "2025-07-18T08:01:04.978Z" },
- { url = "https://files.pythonhosted.org/packages/18/41/bceacec1285b94eb9e4659b24db46c23346d7e22cf258d63419eb5dec6f7/scikit_learn-1.7.1-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b62b76ad408a821475b43b7bb90a9b1c9a4d8d125d505c2df0539f06d6e631b1", size = 9473691, upload-time = "2025-07-18T08:01:07.006Z" },
- { url = "https://files.pythonhosted.org/packages/12/7b/e1ae4b7e1dd85c4ca2694ff9cc4a9690970fd6150d81b975e6c5c6f8ee7c/scikit_learn-1.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:9963b065677a4ce295e8ccdee80a1dd62b37249e667095039adcd5bce6e90deb", size = 8900873, upload-time = "2025-07-18T08:01:09.332Z" },
- { url = "https://files.pythonhosted.org/packages/b4/bd/a23177930abd81b96daffa30ef9c54ddbf544d3226b8788ce4c3ef1067b4/scikit_learn-1.7.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:90c8494ea23e24c0fb371afc474618c1019dc152ce4a10e4607e62196113851b", size = 9334838, upload-time = "2025-07-18T08:01:11.239Z" },
- { url = "https://files.pythonhosted.org/packages/8d/a1/d3a7628630a711e2ac0d1a482910da174b629f44e7dd8cfcd6924a4ef81a/scikit_learn-1.7.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:bb870c0daf3bf3be145ec51df8ac84720d9972170786601039f024bf6d61a518", size = 8651241, upload-time = "2025-07-18T08:01:13.234Z" },
- { url = "https://files.pythonhosted.org/packages/26/92/85ec172418f39474c1cd0221d611345d4f433fc4ee2fc68e01f524ccc4e4/scikit_learn-1.7.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:40daccd1b5623f39e8943ab39735cadf0bdce80e67cdca2adcb5426e987320a8", size = 9718677, upload-time = "2025-07-18T08:01:15.649Z" },
- { url = "https://files.pythonhosted.org/packages/df/ce/abdb1dcbb1d2b66168ec43b23ee0cee356b4cc4100ddee3943934ebf1480/scikit_learn-1.7.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:30d1f413cfc0aa5a99132a554f1d80517563c34a9d3e7c118fde2d273c6fe0f7", size = 9511189, upload-time = "2025-07-18T08:01:18.013Z" },
- { url = "https://files.pythonhosted.org/packages/b2/3b/47b5eaee01ef2b5a80ba3f7f6ecf79587cb458690857d4777bfd77371c6f/scikit_learn-1.7.1-cp311-cp311-win_amd64.whl", hash = "sha256:c711d652829a1805a95d7fe96654604a8f16eab5a9e9ad87b3e60173415cb650", size = 8914794, upload-time = "2025-07-18T08:01:20.357Z" },
- { url = "https://files.pythonhosted.org/packages/cb/16/57f176585b35ed865f51b04117947fe20f130f78940c6477b6d66279c9c2/scikit_learn-1.7.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3cee419b49b5bbae8796ecd690f97aa412ef1674410c23fc3257c6b8b85b8087", size = 9260431, upload-time = "2025-07-18T08:01:22.77Z" },
- { url = "https://files.pythonhosted.org/packages/67/4e/899317092f5efcab0e9bc929e3391341cec8fb0e816c4789686770024580/scikit_learn-1.7.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:2fd8b8d35817b0d9ebf0b576f7d5ffbbabdb55536b0655a8aaae629d7ffd2e1f", size = 8637191, upload-time = "2025-07-18T08:01:24.731Z" },
- { url = "https://files.pythonhosted.org/packages/f3/1b/998312db6d361ded1dd56b457ada371a8d8d77ca2195a7d18fd8a1736f21/scikit_learn-1.7.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:588410fa19a96a69763202f1d6b7b91d5d7a5d73be36e189bc6396bfb355bd87", size = 9486346, upload-time = "2025-07-18T08:01:26.713Z" },
- { url = "https://files.pythonhosted.org/packages/ad/09/a2aa0b4e644e5c4ede7006748f24e72863ba2ae71897fecfd832afea01b4/scikit_learn-1.7.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e3142f0abe1ad1d1c31a2ae987621e41f6b578144a911ff4ac94781a583adad7", size = 9290988, upload-time = "2025-07-18T08:01:28.938Z" },
- { url = "https://files.pythonhosted.org/packages/15/fa/c61a787e35f05f17fc10523f567677ec4eeee5f95aa4798dbbbcd9625617/scikit_learn-1.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:3ddd9092c1bd469acab337d87930067c87eac6bd544f8d5027430983f1e1ae88", size = 8735568, upload-time = "2025-07-18T08:01:30.936Z" },
- { url = "https://files.pythonhosted.org/packages/52/f8/e0533303f318a0f37b88300d21f79b6ac067188d4824f1047a37214ab718/scikit_learn-1.7.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b7839687fa46d02e01035ad775982f2470be2668e13ddd151f0f55a5bf123bae", size = 9213143, upload-time = "2025-07-18T08:01:32.942Z" },
- { url = "https://files.pythonhosted.org/packages/71/f3/f1df377d1bdfc3e3e2adc9c119c238b182293e6740df4cbeac6de2cc3e23/scikit_learn-1.7.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:a10f276639195a96c86aa572ee0698ad64ee939a7b042060b98bd1930c261d10", size = 8591977, upload-time = "2025-07-18T08:01:34.967Z" },
- { url = "https://files.pythonhosted.org/packages/99/72/c86a4cd867816350fe8dee13f30222340b9cd6b96173955819a5561810c5/scikit_learn-1.7.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:13679981fdaebc10cc4c13c43344416a86fcbc61449cb3e6517e1df9d12c8309", size = 9436142, upload-time = "2025-07-18T08:01:37.397Z" },
- { url = "https://files.pythonhosted.org/packages/e8/66/277967b29bd297538dc7a6ecfb1a7dce751beabd0d7f7a2233be7a4f7832/scikit_learn-1.7.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4f1262883c6a63f067a980a8cdd2d2e7f2513dddcef6a9eaada6416a7a7cbe43", size = 9282996, upload-time = "2025-07-18T08:01:39.721Z" },
- { url = "https://files.pythonhosted.org/packages/e2/47/9291cfa1db1dae9880420d1e07dbc7e8dd4a7cdbc42eaba22512e6bde958/scikit_learn-1.7.1-cp313-cp313-win_amd64.whl", hash = "sha256:ca6d31fb10e04d50bfd2b50d66744729dbb512d4efd0223b864e2fdbfc4cee11", size = 8707418, upload-time = "2025-07-18T08:01:42.124Z" },
- { url = "https://files.pythonhosted.org/packages/61/95/45726819beccdaa34d3362ea9b2ff9f2b5d3b8bf721bd632675870308ceb/scikit_learn-1.7.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:781674d096303cfe3d351ae6963ff7c958db61cde3421cd490e3a5a58f2a94ae", size = 9561466, upload-time = "2025-07-18T08:01:44.195Z" },
- { url = "https://files.pythonhosted.org/packages/ee/1c/6f4b3344805de783d20a51eb24d4c9ad4b11a7f75c1801e6ec6d777361fd/scikit_learn-1.7.1-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:10679f7f125fe7ecd5fad37dd1aa2daae7e3ad8df7f3eefa08901b8254b3e12c", size = 9040467, upload-time = "2025-07-18T08:01:46.671Z" },
- { url = "https://files.pythonhosted.org/packages/6f/80/abe18fe471af9f1d181904203d62697998b27d9b62124cd281d740ded2f9/scikit_learn-1.7.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1f812729e38c8cb37f760dce71a9b83ccfb04f59b3dca7c6079dcdc60544fa9e", size = 9532052, upload-time = "2025-07-18T08:01:48.676Z" },
- { url = "https://files.pythonhosted.org/packages/14/82/b21aa1e0c4cee7e74864d3a5a721ab8fcae5ca55033cb6263dca297ed35b/scikit_learn-1.7.1-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:88e1a20131cf741b84b89567e1717f27a2ced228e0f29103426102bc2e3b8ef7", size = 9361575, upload-time = "2025-07-18T08:01:50.639Z" },
- { url = "https://files.pythonhosted.org/packages/f2/20/f4777fcd5627dc6695fa6b92179d0edb7a3ac1b91bcd9a1c7f64fa7ade23/scikit_learn-1.7.1-cp313-cp313t-win_amd64.whl", hash = "sha256:b1bd1d919210b6a10b7554b717c9000b5485aa95a1d0f177ae0d7ee8ec750da5", size = 9277310, upload-time = "2025-07-18T08:01:52.547Z" },
-]
-
-[[package]]
-name = "scipy"
-version = "1.13.1"
-source = { registry = "https://pypi.org/simple" }
-resolution-markers = [
- "python_full_version < '3.10'",
-]
-dependencies = [
- { name = "numpy", marker = "python_full_version < '3.10'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/ae/00/48c2f661e2816ccf2ecd77982f6605b2950afe60f60a52b4cbbc2504aa8f/scipy-1.13.1.tar.gz", hash = "sha256:095a87a0312b08dfd6a6155cbbd310a8c51800fc931b8c0b84003014b874ed3c", size = 57210720, upload-time = "2024-05-23T03:29:26.079Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/33/59/41b2529908c002ade869623b87eecff3e11e3ce62e996d0bdcb536984187/scipy-1.13.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:20335853b85e9a49ff7572ab453794298bcf0354d8068c5f6775a0eabf350aca", size = 39328076, upload-time = "2024-05-23T03:19:01.687Z" },
- { url = "https://files.pythonhosted.org/packages/d5/33/f1307601f492f764062ce7dd471a14750f3360e33cd0f8c614dae208492c/scipy-1.13.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:d605e9c23906d1994f55ace80e0125c587f96c020037ea6aa98d01b4bd2e222f", size = 30306232, upload-time = "2024-05-23T03:19:09.089Z" },
- { url = "https://files.pythonhosted.org/packages/c0/66/9cd4f501dd5ea03e4a4572ecd874936d0da296bd04d1c45ae1a4a75d9c3a/scipy-1.13.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cfa31f1def5c819b19ecc3a8b52d28ffdcc7ed52bb20c9a7589669dd3c250989", size = 33743202, upload-time = "2024-05-23T03:19:15.138Z" },
- { url = "https://files.pythonhosted.org/packages/a3/ba/7255e5dc82a65adbe83771c72f384d99c43063648456796436c9a5585ec3/scipy-1.13.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f26264b282b9da0952a024ae34710c2aff7d27480ee91a2e82b7b7073c24722f", size = 38577335, upload-time = "2024-05-23T03:19:21.984Z" },
- { url = "https://files.pythonhosted.org/packages/49/a5/bb9ded8326e9f0cdfdc412eeda1054b914dfea952bda2097d174f8832cc0/scipy-1.13.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:eccfa1906eacc02de42d70ef4aecea45415f5be17e72b61bafcfd329bdc52e94", size = 38820728, upload-time = "2024-05-23T03:19:28.225Z" },
- { url = "https://files.pythonhosted.org/packages/12/30/df7a8fcc08f9b4a83f5f27cfaaa7d43f9a2d2ad0b6562cced433e5b04e31/scipy-1.13.1-cp310-cp310-win_amd64.whl", hash = "sha256:2831f0dc9c5ea9edd6e51e6e769b655f08ec6db6e2e10f86ef39bd32eb11da54", size = 46210588, upload-time = "2024-05-23T03:19:35.661Z" },
- { url = "https://files.pythonhosted.org/packages/b4/15/4a4bb1b15bbd2cd2786c4f46e76b871b28799b67891f23f455323a0cdcfb/scipy-1.13.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:27e52b09c0d3a1d5b63e1105f24177e544a222b43611aaf5bc44d4a0979e32f9", size = 39333805, upload-time = "2024-05-23T03:19:43.081Z" },
- { url = "https://files.pythonhosted.org/packages/ba/92/42476de1af309c27710004f5cdebc27bec62c204db42e05b23a302cb0c9a/scipy-1.13.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:54f430b00f0133e2224c3ba42b805bfd0086fe488835effa33fa291561932326", size = 30317687, upload-time = "2024-05-23T03:19:48.799Z" },
- { url = "https://files.pythonhosted.org/packages/80/ba/8be64fe225360a4beb6840f3cbee494c107c0887f33350d0a47d55400b01/scipy-1.13.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e89369d27f9e7b0884ae559a3a956e77c02114cc60a6058b4e5011572eea9299", size = 33694638, upload-time = "2024-05-23T03:19:55.104Z" },
- { url = "https://files.pythonhosted.org/packages/36/07/035d22ff9795129c5a847c64cb43c1fa9188826b59344fee28a3ab02e283/scipy-1.13.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a78b4b3345f1b6f68a763c6e25c0c9a23a9fd0f39f5f3d200efe8feda560a5fa", size = 38569931, upload-time = "2024-05-23T03:20:01.82Z" },
- { url = "https://files.pythonhosted.org/packages/d9/10/f9b43de37e5ed91facc0cfff31d45ed0104f359e4f9a68416cbf4e790241/scipy-1.13.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:45484bee6d65633752c490404513b9ef02475b4284c4cfab0ef946def50b3f59", size = 38838145, upload-time = "2024-05-23T03:20:09.173Z" },
- { url = "https://files.pythonhosted.org/packages/4a/48/4513a1a5623a23e95f94abd675ed91cfb19989c58e9f6f7d03990f6caf3d/scipy-1.13.1-cp311-cp311-win_amd64.whl", hash = "sha256:5713f62f781eebd8d597eb3f88b8bf9274e79eeabf63afb4a737abc6c84ad37b", size = 46196227, upload-time = "2024-05-23T03:20:16.433Z" },
- { url = "https://files.pythonhosted.org/packages/f2/7b/fb6b46fbee30fc7051913068758414f2721003a89dd9a707ad49174e3843/scipy-1.13.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5d72782f39716b2b3509cd7c33cdc08c96f2f4d2b06d51e52fb45a19ca0c86a1", size = 39357301, upload-time = "2024-05-23T03:20:23.538Z" },
- { url = "https://files.pythonhosted.org/packages/dc/5a/2043a3bde1443d94014aaa41e0b50c39d046dda8360abd3b2a1d3f79907d/scipy-1.13.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:017367484ce5498445aade74b1d5ab377acdc65e27095155e448c88497755a5d", size = 30363348, upload-time = "2024-05-23T03:20:29.885Z" },
- { url = "https://files.pythonhosted.org/packages/e7/cb/26e4a47364bbfdb3b7fb3363be6d8a1c543bcd70a7753ab397350f5f189a/scipy-1.13.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:949ae67db5fa78a86e8fa644b9a6b07252f449dcf74247108c50e1d20d2b4627", size = 33406062, upload-time = "2024-05-23T03:20:36.012Z" },
- { url = "https://files.pythonhosted.org/packages/88/ab/6ecdc526d509d33814835447bbbeedbebdec7cca46ef495a61b00a35b4bf/scipy-1.13.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de3ade0e53bc1f21358aa74ff4830235d716211d7d077e340c7349bc3542e884", size = 38218311, upload-time = "2024-05-23T03:20:42.086Z" },
- { url = "https://files.pythonhosted.org/packages/0b/00/9f54554f0f8318100a71515122d8f4f503b1a2c4b4cfab3b4b68c0eb08fa/scipy-1.13.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2ac65fb503dad64218c228e2dc2d0a0193f7904747db43014645ae139c8fad16", size = 38442493, upload-time = "2024-05-23T03:20:48.292Z" },
- { url = "https://files.pythonhosted.org/packages/3e/df/963384e90733e08eac978cd103c34df181d1fec424de383cdc443f418dd4/scipy-1.13.1-cp312-cp312-win_amd64.whl", hash = "sha256:cdd7dacfb95fea358916410ec61bbc20440f7860333aee6d882bb8046264e949", size = 45910955, upload-time = "2024-05-23T03:20:55.091Z" },
- { url = "https://files.pythonhosted.org/packages/7f/29/c2ea58c9731b9ecb30b6738113a95d147e83922986b34c685b8f6eefde21/scipy-1.13.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:436bbb42a94a8aeef855d755ce5a465479c721e9d684de76bf61a62e7c2b81d5", size = 39352927, upload-time = "2024-05-23T03:21:01.95Z" },
- { url = "https://files.pythonhosted.org/packages/5c/c0/e71b94b20ccf9effb38d7147c0064c08c622309fd487b1b677771a97d18c/scipy-1.13.1-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:8335549ebbca860c52bf3d02f80784e91a004b71b059e3eea9678ba994796a24", size = 30324538, upload-time = "2024-05-23T03:21:07.634Z" },
- { url = "https://files.pythonhosted.org/packages/6d/0f/aaa55b06d474817cea311e7b10aab2ea1fd5d43bc6a2861ccc9caec9f418/scipy-1.13.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d533654b7d221a6a97304ab63c41c96473ff04459e404b83275b60aa8f4b7004", size = 33732190, upload-time = "2024-05-23T03:21:14.41Z" },
- { url = "https://files.pythonhosted.org/packages/35/f5/d0ad1a96f80962ba65e2ce1de6a1e59edecd1f0a7b55990ed208848012e0/scipy-1.13.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:637e98dcf185ba7f8e663e122ebf908c4702420477ae52a04f9908707456ba4d", size = 38612244, upload-time = "2024-05-23T03:21:21.827Z" },
- { url = "https://files.pythonhosted.org/packages/8d/02/1165905f14962174e6569076bcc3315809ae1291ed14de6448cc151eedfd/scipy-1.13.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a014c2b3697bde71724244f63de2476925596c24285c7a637364761f8710891c", size = 38845637, upload-time = "2024-05-23T03:21:28.729Z" },
- { url = "https://files.pythonhosted.org/packages/3e/77/dab54fe647a08ee4253963bcd8f9cf17509c8ca64d6335141422fe2e2114/scipy-1.13.1-cp39-cp39-win_amd64.whl", hash = "sha256:392e4ec766654852c25ebad4f64e4e584cf19820b980bc04960bca0b0cd6eaa2", size = 46227440, upload-time = "2024-05-23T03:21:35.888Z" },
-]
-
-[[package]]
-name = "scipy"
-version = "1.15.3"
-source = { registry = "https://pypi.org/simple" }
-resolution-markers = [
- "python_full_version == '3.10.*'",
-]
-dependencies = [
- { name = "numpy", marker = "python_full_version == '3.10.*'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/0f/37/6964b830433e654ec7485e45a00fc9a27cf868d622838f6b6d9c5ec0d532/scipy-1.15.3.tar.gz", hash = "sha256:eae3cf522bc7df64b42cad3925c876e1b0b6c35c1337c93e12c0f366f55b0eaf", size = 59419214, upload-time = "2025-05-08T16:13:05.955Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/78/2f/4966032c5f8cc7e6a60f1b2e0ad686293b9474b65246b0c642e3ef3badd0/scipy-1.15.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:a345928c86d535060c9c2b25e71e87c39ab2f22fc96e9636bd74d1dbf9de448c", size = 38702770, upload-time = "2025-05-08T16:04:20.849Z" },
- { url = "https://files.pythonhosted.org/packages/a0/6e/0c3bf90fae0e910c274db43304ebe25a6b391327f3f10b5dcc638c090795/scipy-1.15.3-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:ad3432cb0f9ed87477a8d97f03b763fd1d57709f1bbde3c9369b1dff5503b253", size = 30094511, upload-time = "2025-05-08T16:04:27.103Z" },
- { url = "https://files.pythonhosted.org/packages/ea/b1/4deb37252311c1acff7f101f6453f0440794f51b6eacb1aad4459a134081/scipy-1.15.3-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:aef683a9ae6eb00728a542b796f52a5477b78252edede72b8327a886ab63293f", size = 22368151, upload-time = "2025-05-08T16:04:31.731Z" },
- { url = "https://files.pythonhosted.org/packages/38/7d/f457626e3cd3c29b3a49ca115a304cebb8cc6f31b04678f03b216899d3c6/scipy-1.15.3-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:1c832e1bd78dea67d5c16f786681b28dd695a8cb1fb90af2e27580d3d0967e92", size = 25121732, upload-time = "2025-05-08T16:04:36.596Z" },
- { url = "https://files.pythonhosted.org/packages/db/0a/92b1de4a7adc7a15dcf5bddc6e191f6f29ee663b30511ce20467ef9b82e4/scipy-1.15.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:263961f658ce2165bbd7b99fa5135195c3a12d9bef045345016b8b50c315cb82", size = 35547617, upload-time = "2025-05-08T16:04:43.546Z" },
- { url = "https://files.pythonhosted.org/packages/8e/6d/41991e503e51fc1134502694c5fa7a1671501a17ffa12716a4a9151af3df/scipy-1.15.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e2abc762b0811e09a0d3258abee2d98e0c703eee49464ce0069590846f31d40", size = 37662964, upload-time = "2025-05-08T16:04:49.431Z" },
- { url = "https://files.pythonhosted.org/packages/25/e1/3df8f83cb15f3500478c889be8fb18700813b95e9e087328230b98d547ff/scipy-1.15.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ed7284b21a7a0c8f1b6e5977ac05396c0d008b89e05498c8b7e8f4a1423bba0e", size = 37238749, upload-time = "2025-05-08T16:04:55.215Z" },
- { url = "https://files.pythonhosted.org/packages/93/3e/b3257cf446f2a3533ed7809757039016b74cd6f38271de91682aa844cfc5/scipy-1.15.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5380741e53df2c566f4d234b100a484b420af85deb39ea35a1cc1be84ff53a5c", size = 40022383, upload-time = "2025-05-08T16:05:01.914Z" },
- { url = "https://files.pythonhosted.org/packages/d1/84/55bc4881973d3f79b479a5a2e2df61c8c9a04fcb986a213ac9c02cfb659b/scipy-1.15.3-cp310-cp310-win_amd64.whl", hash = "sha256:9d61e97b186a57350f6d6fd72640f9e99d5a4a2b8fbf4b9ee9a841eab327dc13", size = 41259201, upload-time = "2025-05-08T16:05:08.166Z" },
- { url = "https://files.pythonhosted.org/packages/96/ab/5cc9f80f28f6a7dff646c5756e559823614a42b1939d86dd0ed550470210/scipy-1.15.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:993439ce220d25e3696d1b23b233dd010169b62f6456488567e830654ee37a6b", size = 38714255, upload-time = "2025-05-08T16:05:14.596Z" },
- { url = "https://files.pythonhosted.org/packages/4a/4a/66ba30abe5ad1a3ad15bfb0b59d22174012e8056ff448cb1644deccbfed2/scipy-1.15.3-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:34716e281f181a02341ddeaad584205bd2fd3c242063bd3423d61ac259ca7eba", size = 30111035, upload-time = "2025-05-08T16:05:20.152Z" },
- { url = "https://files.pythonhosted.org/packages/4b/fa/a7e5b95afd80d24313307f03624acc65801846fa75599034f8ceb9e2cbf6/scipy-1.15.3-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3b0334816afb8b91dab859281b1b9786934392aa3d527cd847e41bb6f45bee65", size = 22384499, upload-time = "2025-05-08T16:05:24.494Z" },
- { url = "https://files.pythonhosted.org/packages/17/99/f3aaddccf3588bb4aea70ba35328c204cadd89517a1612ecfda5b2dd9d7a/scipy-1.15.3-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:6db907c7368e3092e24919b5e31c76998b0ce1684d51a90943cb0ed1b4ffd6c1", size = 25152602, upload-time = "2025-05-08T16:05:29.313Z" },
- { url = "https://files.pythonhosted.org/packages/56/c5/1032cdb565f146109212153339f9cb8b993701e9fe56b1c97699eee12586/scipy-1.15.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:721d6b4ef5dc82ca8968c25b111e307083d7ca9091bc38163fb89243e85e3889", size = 35503415, upload-time = "2025-05-08T16:05:34.699Z" },
- { url = "https://files.pythonhosted.org/packages/bd/37/89f19c8c05505d0601ed5650156e50eb881ae3918786c8fd7262b4ee66d3/scipy-1.15.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39cb9c62e471b1bb3750066ecc3a3f3052b37751c7c3dfd0fd7e48900ed52982", size = 37652622, upload-time = "2025-05-08T16:05:40.762Z" },
- { url = "https://files.pythonhosted.org/packages/7e/31/be59513aa9695519b18e1851bb9e487de66f2d31f835201f1b42f5d4d475/scipy-1.15.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:795c46999bae845966368a3c013e0e00947932d68e235702b5c3f6ea799aa8c9", size = 37244796, upload-time = "2025-05-08T16:05:48.119Z" },
- { url = "https://files.pythonhosted.org/packages/10/c0/4f5f3eeccc235632aab79b27a74a9130c6c35df358129f7ac8b29f562ac7/scipy-1.15.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:18aaacb735ab38b38db42cb01f6b92a2d0d4b6aabefeb07f02849e47f8fb3594", size = 40047684, upload-time = "2025-05-08T16:05:54.22Z" },
- { url = "https://files.pythonhosted.org/packages/ab/a7/0ddaf514ce8a8714f6ed243a2b391b41dbb65251affe21ee3077ec45ea9a/scipy-1.15.3-cp311-cp311-win_amd64.whl", hash = "sha256:ae48a786a28412d744c62fd7816a4118ef97e5be0bee968ce8f0a2fba7acf3bb", size = 41246504, upload-time = "2025-05-08T16:06:00.437Z" },
- { url = "https://files.pythonhosted.org/packages/37/4b/683aa044c4162e10ed7a7ea30527f2cbd92e6999c10a8ed8edb253836e9c/scipy-1.15.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6ac6310fdbfb7aa6612408bd2f07295bcbd3fda00d2d702178434751fe48e019", size = 38766735, upload-time = "2025-05-08T16:06:06.471Z" },
- { url = "https://files.pythonhosted.org/packages/7b/7e/f30be3d03de07f25dc0ec926d1681fed5c732d759ac8f51079708c79e680/scipy-1.15.3-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:185cd3d6d05ca4b44a8f1595af87f9c372bb6acf9c808e99aa3e9aa03bd98cf6", size = 30173284, upload-time = "2025-05-08T16:06:11.686Z" },
- { url = "https://files.pythonhosted.org/packages/07/9c/0ddb0d0abdabe0d181c1793db51f02cd59e4901da6f9f7848e1f96759f0d/scipy-1.15.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:05dc6abcd105e1a29f95eada46d4a3f251743cfd7d3ae8ddb4088047f24ea477", size = 22446958, upload-time = "2025-05-08T16:06:15.97Z" },
- { url = "https://files.pythonhosted.org/packages/af/43/0bce905a965f36c58ff80d8bea33f1f9351b05fad4beaad4eae34699b7a1/scipy-1.15.3-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:06efcba926324df1696931a57a176c80848ccd67ce6ad020c810736bfd58eb1c", size = 25242454, upload-time = "2025-05-08T16:06:20.394Z" },
- { url = "https://files.pythonhosted.org/packages/56/30/a6f08f84ee5b7b28b4c597aca4cbe545535c39fe911845a96414700b64ba/scipy-1.15.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c05045d8b9bfd807ee1b9f38761993297b10b245f012b11b13b91ba8945f7e45", size = 35210199, upload-time = "2025-05-08T16:06:26.159Z" },
- { url = "https://files.pythonhosted.org/packages/0b/1f/03f52c282437a168ee2c7c14a1a0d0781a9a4a8962d84ac05c06b4c5b555/scipy-1.15.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:271e3713e645149ea5ea3e97b57fdab61ce61333f97cfae392c28ba786f9bb49", size = 37309455, upload-time = "2025-05-08T16:06:32.778Z" },
- { url = "https://files.pythonhosted.org/packages/89/b1/fbb53137f42c4bf630b1ffdfc2151a62d1d1b903b249f030d2b1c0280af8/scipy-1.15.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6cfd56fc1a8e53f6e89ba3a7a7251f7396412d655bca2aa5611c8ec9a6784a1e", size = 36885140, upload-time = "2025-05-08T16:06:39.249Z" },
- { url = "https://files.pythonhosted.org/packages/2e/2e/025e39e339f5090df1ff266d021892694dbb7e63568edcfe43f892fa381d/scipy-1.15.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0ff17c0bb1cb32952c09217d8d1eed9b53d1463e5f1dd6052c7857f83127d539", size = 39710549, upload-time = "2025-05-08T16:06:45.729Z" },
- { url = "https://files.pythonhosted.org/packages/e6/eb/3bf6ea8ab7f1503dca3a10df2e4b9c3f6b3316df07f6c0ded94b281c7101/scipy-1.15.3-cp312-cp312-win_amd64.whl", hash = "sha256:52092bc0472cfd17df49ff17e70624345efece4e1a12b23783a1ac59a1b728ed", size = 40966184, upload-time = "2025-05-08T16:06:52.623Z" },
- { url = "https://files.pythonhosted.org/packages/73/18/ec27848c9baae6e0d6573eda6e01a602e5649ee72c27c3a8aad673ebecfd/scipy-1.15.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2c620736bcc334782e24d173c0fdbb7590a0a436d2fdf39310a8902505008759", size = 38728256, upload-time = "2025-05-08T16:06:58.696Z" },
- { url = "https://files.pythonhosted.org/packages/74/cd/1aef2184948728b4b6e21267d53b3339762c285a46a274ebb7863c9e4742/scipy-1.15.3-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:7e11270a000969409d37ed399585ee530b9ef6aa99d50c019de4cb01e8e54e62", size = 30109540, upload-time = "2025-05-08T16:07:04.209Z" },
- { url = "https://files.pythonhosted.org/packages/5b/d8/59e452c0a255ec352bd0a833537a3bc1bfb679944c4938ab375b0a6b3a3e/scipy-1.15.3-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:8c9ed3ba2c8a2ce098163a9bdb26f891746d02136995df25227a20e71c396ebb", size = 22383115, upload-time = "2025-05-08T16:07:08.998Z" },
- { url = "https://files.pythonhosted.org/packages/08/f5/456f56bbbfccf696263b47095291040655e3cbaf05d063bdc7c7517f32ac/scipy-1.15.3-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:0bdd905264c0c9cfa74a4772cdb2070171790381a5c4d312c973382fc6eaf730", size = 25163884, upload-time = "2025-05-08T16:07:14.091Z" },
- { url = "https://files.pythonhosted.org/packages/a2/66/a9618b6a435a0f0c0b8a6d0a2efb32d4ec5a85f023c2b79d39512040355b/scipy-1.15.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79167bba085c31f38603e11a267d862957cbb3ce018d8b38f79ac043bc92d825", size = 35174018, upload-time = "2025-05-08T16:07:19.427Z" },
- { url = "https://files.pythonhosted.org/packages/b5/09/c5b6734a50ad4882432b6bb7c02baf757f5b2f256041da5df242e2d7e6b6/scipy-1.15.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9deabd6d547aee2c9a81dee6cc96c6d7e9a9b1953f74850c179f91fdc729cb7", size = 37269716, upload-time = "2025-05-08T16:07:25.712Z" },
- { url = "https://files.pythonhosted.org/packages/77/0a/eac00ff741f23bcabd352731ed9b8995a0a60ef57f5fd788d611d43d69a1/scipy-1.15.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:dde4fc32993071ac0c7dd2d82569e544f0bdaff66269cb475e0f369adad13f11", size = 36872342, upload-time = "2025-05-08T16:07:31.468Z" },
- { url = "https://files.pythonhosted.org/packages/fe/54/4379be86dd74b6ad81551689107360d9a3e18f24d20767a2d5b9253a3f0a/scipy-1.15.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f77f853d584e72e874d87357ad70f44b437331507d1c311457bed8ed2b956126", size = 39670869, upload-time = "2025-05-08T16:07:38.002Z" },
- { url = "https://files.pythonhosted.org/packages/87/2e/892ad2862ba54f084ffe8cc4a22667eaf9c2bcec6d2bff1d15713c6c0703/scipy-1.15.3-cp313-cp313-win_amd64.whl", hash = "sha256:b90ab29d0c37ec9bf55424c064312930ca5f4bde15ee8619ee44e69319aab163", size = 40988851, upload-time = "2025-05-08T16:08:33.671Z" },
- { url = "https://files.pythonhosted.org/packages/1b/e9/7a879c137f7e55b30d75d90ce3eb468197646bc7b443ac036ae3fe109055/scipy-1.15.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:3ac07623267feb3ae308487c260ac684b32ea35fd81e12845039952f558047b8", size = 38863011, upload-time = "2025-05-08T16:07:44.039Z" },
- { url = "https://files.pythonhosted.org/packages/51/d1/226a806bbd69f62ce5ef5f3ffadc35286e9fbc802f606a07eb83bf2359de/scipy-1.15.3-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:6487aa99c2a3d509a5227d9a5e889ff05830a06b2ce08ec30df6d79db5fcd5c5", size = 30266407, upload-time = "2025-05-08T16:07:49.891Z" },
- { url = "https://files.pythonhosted.org/packages/e5/9b/f32d1d6093ab9eeabbd839b0f7619c62e46cc4b7b6dbf05b6e615bbd4400/scipy-1.15.3-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:50f9e62461c95d933d5c5ef4a1f2ebf9a2b4e83b0db374cb3f1de104d935922e", size = 22540030, upload-time = "2025-05-08T16:07:54.121Z" },
- { url = "https://files.pythonhosted.org/packages/e7/29/c278f699b095c1a884f29fda126340fcc201461ee8bfea5c8bdb1c7c958b/scipy-1.15.3-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:14ed70039d182f411ffc74789a16df3835e05dc469b898233a245cdfd7f162cb", size = 25218709, upload-time = "2025-05-08T16:07:58.506Z" },
- { url = "https://files.pythonhosted.org/packages/24/18/9e5374b617aba742a990581373cd6b68a2945d65cc588482749ef2e64467/scipy-1.15.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a769105537aa07a69468a0eefcd121be52006db61cdd8cac8a0e68980bbb723", size = 34809045, upload-time = "2025-05-08T16:08:03.929Z" },
- { url = "https://files.pythonhosted.org/packages/e1/fe/9c4361e7ba2927074360856db6135ef4904d505e9b3afbbcb073c4008328/scipy-1.15.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9db984639887e3dffb3928d118145ffe40eff2fa40cb241a306ec57c219ebbbb", size = 36703062, upload-time = "2025-05-08T16:08:09.558Z" },
- { url = "https://files.pythonhosted.org/packages/b7/8e/038ccfe29d272b30086b25a4960f757f97122cb2ec42e62b460d02fe98e9/scipy-1.15.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:40e54d5c7e7ebf1aa596c374c49fa3135f04648a0caabcb66c52884b943f02b4", size = 36393132, upload-time = "2025-05-08T16:08:15.34Z" },
- { url = "https://files.pythonhosted.org/packages/10/7e/5c12285452970be5bdbe8352c619250b97ebf7917d7a9a9e96b8a8140f17/scipy-1.15.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:5e721fed53187e71d0ccf382b6bf977644c533e506c4d33c3fb24de89f5c3ed5", size = 38979503, upload-time = "2025-05-08T16:08:21.513Z" },
- { url = "https://files.pythonhosted.org/packages/81/06/0a5e5349474e1cbc5757975b21bd4fad0e72ebf138c5592f191646154e06/scipy-1.15.3-cp313-cp313t-win_amd64.whl", hash = "sha256:76ad1fb5f8752eabf0fa02e4cc0336b4e8f021e2d5f061ed37d6d264db35e3ca", size = 40308097, upload-time = "2025-05-08T16:08:27.627Z" },
-]
-
-[[package]]
-name = "scipy"
-version = "1.16.1"
-source = { registry = "https://pypi.org/simple" }
-resolution-markers = [
- "python_full_version >= '3.13'",
- "python_full_version >= '3.12.4' and python_full_version < '3.13'",
- "python_full_version >= '3.12' and python_full_version < '3.12.4'",
- "python_full_version == '3.11.*'",
-]
-dependencies = [
- { name = "numpy", marker = "python_full_version >= '3.11'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/f5/4a/b927028464795439faec8eaf0b03b011005c487bb2d07409f28bf30879c4/scipy-1.16.1.tar.gz", hash = "sha256:44c76f9e8b6e8e488a586190ab38016e4ed2f8a038af7cd3defa903c0a2238b3", size = 30580861, upload-time = "2025-07-27T16:33:30.834Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/da/91/812adc6f74409b461e3a5fa97f4f74c769016919203138a3bf6fc24ba4c5/scipy-1.16.1-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:c033fa32bab91dc98ca59d0cf23bb876454e2bb02cbe592d5023138778f70030", size = 36552519, upload-time = "2025-07-27T16:26:29.658Z" },
- { url = "https://files.pythonhosted.org/packages/47/18/8e355edcf3b71418d9e9f9acd2708cc3a6c27e8f98fde0ac34b8a0b45407/scipy-1.16.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:6e5c2f74e5df33479b5cd4e97a9104c511518fbd979aa9b8f6aec18b2e9ecae7", size = 28638010, upload-time = "2025-07-27T16:26:38.196Z" },
- { url = "https://files.pythonhosted.org/packages/d9/eb/e931853058607bdfbc11b86df19ae7a08686121c203483f62f1ecae5989c/scipy-1.16.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:0a55ffe0ba0f59666e90951971a884d1ff6f4ec3275a48f472cfb64175570f77", size = 20909790, upload-time = "2025-07-27T16:26:43.93Z" },
- { url = "https://files.pythonhosted.org/packages/45/0c/be83a271d6e96750cd0be2e000f35ff18880a46f05ce8b5d3465dc0f7a2a/scipy-1.16.1-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:f8a5d6cd147acecc2603fbd382fed6c46f474cccfcf69ea32582e033fb54dcfe", size = 23513352, upload-time = "2025-07-27T16:26:50.017Z" },
- { url = "https://files.pythonhosted.org/packages/7c/bf/fe6eb47e74f762f933cca962db7f2c7183acfdc4483bd1c3813cfe83e538/scipy-1.16.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cb18899127278058bcc09e7b9966d41a5a43740b5bb8dcba401bd983f82e885b", size = 33534643, upload-time = "2025-07-27T16:26:57.503Z" },
- { url = "https://files.pythonhosted.org/packages/bb/ba/63f402e74875486b87ec6506a4f93f6d8a0d94d10467280f3d9d7837ce3a/scipy-1.16.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:adccd93a2fa937a27aae826d33e3bfa5edf9aa672376a4852d23a7cd67a2e5b7", size = 35376776, upload-time = "2025-07-27T16:27:06.639Z" },
- { url = "https://files.pythonhosted.org/packages/c3/b4/04eb9d39ec26a1b939689102da23d505ea16cdae3dbb18ffc53d1f831044/scipy-1.16.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:18aca1646a29ee9a0625a1be5637fa798d4d81fdf426481f06d69af828f16958", size = 35698906, upload-time = "2025-07-27T16:27:14.943Z" },
- { url = "https://files.pythonhosted.org/packages/04/d6/bb5468da53321baeb001f6e4e0d9049eadd175a4a497709939128556e3ec/scipy-1.16.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d85495cef541729a70cdddbbf3e6b903421bc1af3e8e3a9a72a06751f33b7c39", size = 38129275, upload-time = "2025-07-27T16:27:23.873Z" },
- { url = "https://files.pythonhosted.org/packages/c4/94/994369978509f227cba7dfb9e623254d0d5559506fe994aef4bea3ed469c/scipy-1.16.1-cp311-cp311-win_amd64.whl", hash = "sha256:226652fca853008119c03a8ce71ffe1b3f6d2844cc1686e8f9806edafae68596", size = 38644572, upload-time = "2025-07-27T16:27:32.637Z" },
- { url = "https://files.pythonhosted.org/packages/f8/d9/ec4864f5896232133f51382b54a08de91a9d1af7a76dfa372894026dfee2/scipy-1.16.1-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:81b433bbeaf35728dad619afc002db9b189e45eebe2cd676effe1fb93fef2b9c", size = 36575194, upload-time = "2025-07-27T16:27:41.321Z" },
- { url = "https://files.pythonhosted.org/packages/5c/6d/40e81ecfb688e9d25d34a847dca361982a6addf8e31f0957b1a54fbfa994/scipy-1.16.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:886cc81fdb4c6903a3bb0464047c25a6d1016fef77bb97949817d0c0d79f9e04", size = 28594590, upload-time = "2025-07-27T16:27:49.204Z" },
- { url = "https://files.pythonhosted.org/packages/0e/37/9f65178edfcc629377ce9a64fc09baebea18c80a9e57ae09a52edf84880b/scipy-1.16.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:15240c3aac087a522b4eaedb09f0ad061753c5eebf1ea430859e5bf8640d5919", size = 20866458, upload-time = "2025-07-27T16:27:54.98Z" },
- { url = "https://files.pythonhosted.org/packages/2c/7b/749a66766871ea4cb1d1ea10f27004db63023074c22abed51f22f09770e0/scipy-1.16.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:65f81a25805f3659b48126b5053d9e823d3215e4a63730b5e1671852a1705921", size = 23539318, upload-time = "2025-07-27T16:28:01.604Z" },
- { url = "https://files.pythonhosted.org/packages/c4/db/8d4afec60eb833a666434d4541a3151eedbf2494ea6d4d468cbe877f00cd/scipy-1.16.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:6c62eea7f607f122069b9bad3f99489ddca1a5173bef8a0c75555d7488b6f725", size = 33292899, upload-time = "2025-07-27T16:28:09.147Z" },
- { url = "https://files.pythonhosted.org/packages/51/1e/79023ca3bbb13a015d7d2757ecca3b81293c663694c35d6541b4dca53e98/scipy-1.16.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f965bbf3235b01c776115ab18f092a95aa74c271a52577bcb0563e85738fd618", size = 35162637, upload-time = "2025-07-27T16:28:17.535Z" },
- { url = "https://files.pythonhosted.org/packages/b6/49/0648665f9c29fdaca4c679182eb972935b3b4f5ace41d323c32352f29816/scipy-1.16.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f006e323874ffd0b0b816d8c6a8e7f9a73d55ab3b8c3f72b752b226d0e3ac83d", size = 35490507, upload-time = "2025-07-27T16:28:25.705Z" },
- { url = "https://files.pythonhosted.org/packages/62/8f/66cbb9d6bbb18d8c658f774904f42a92078707a7c71e5347e8bf2f52bb89/scipy-1.16.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e8fd15fc5085ab4cca74cb91fe0a4263b1f32e4420761ddae531ad60934c2119", size = 37923998, upload-time = "2025-07-27T16:28:34.339Z" },
- { url = "https://files.pythonhosted.org/packages/14/c3/61f273ae550fbf1667675701112e380881905e28448c080b23b5a181df7c/scipy-1.16.1-cp312-cp312-win_amd64.whl", hash = "sha256:f7b8013c6c066609577d910d1a2a077021727af07b6fab0ee22c2f901f22352a", size = 38508060, upload-time = "2025-07-27T16:28:43.242Z" },
- { url = "https://files.pythonhosted.org/packages/93/0b/b5c99382b839854a71ca9482c684e3472badc62620287cbbdab499b75ce6/scipy-1.16.1-cp313-cp313-macosx_10_14_x86_64.whl", hash = "sha256:5451606823a5e73dfa621a89948096c6528e2896e40b39248295d3a0138d594f", size = 36533717, upload-time = "2025-07-27T16:28:51.706Z" },
- { url = "https://files.pythonhosted.org/packages/eb/e5/69ab2771062c91e23e07c12e7d5033a6b9b80b0903ee709c3c36b3eb520c/scipy-1.16.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:89728678c5ca5abd610aee148c199ac1afb16e19844401ca97d43dc548a354eb", size = 28570009, upload-time = "2025-07-27T16:28:57.017Z" },
- { url = "https://files.pythonhosted.org/packages/f4/69/bd75dbfdd3cf524f4d753484d723594aed62cfaac510123e91a6686d520b/scipy-1.16.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:e756d688cb03fd07de0fffad475649b03cb89bee696c98ce508b17c11a03f95c", size = 20841942, upload-time = "2025-07-27T16:29:01.152Z" },
- { url = "https://files.pythonhosted.org/packages/ea/74/add181c87663f178ba7d6144b370243a87af8476664d5435e57d599e6874/scipy-1.16.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:5aa2687b9935da3ed89c5dbed5234576589dd28d0bf7cd237501ccfbdf1ad608", size = 23498507, upload-time = "2025-07-27T16:29:05.202Z" },
- { url = "https://files.pythonhosted.org/packages/1d/74/ece2e582a0d9550cee33e2e416cc96737dce423a994d12bbe59716f47ff1/scipy-1.16.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0851f6a1e537fe9399f35986897e395a1aa61c574b178c0d456be5b1a0f5ca1f", size = 33286040, upload-time = "2025-07-27T16:29:10.201Z" },
- { url = "https://files.pythonhosted.org/packages/e4/82/08e4076df538fb56caa1d489588d880ec7c52d8273a606bb54d660528f7c/scipy-1.16.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fedc2cbd1baed37474b1924c331b97bdff611d762c196fac1a9b71e67b813b1b", size = 35176096, upload-time = "2025-07-27T16:29:17.091Z" },
- { url = "https://files.pythonhosted.org/packages/fa/79/cd710aab8c921375711a8321c6be696e705a120e3011a643efbbcdeeabcc/scipy-1.16.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2ef500e72f9623a6735769e4b93e9dcb158d40752cdbb077f305487e3e2d1f45", size = 35490328, upload-time = "2025-07-27T16:29:22.928Z" },
- { url = "https://files.pythonhosted.org/packages/71/73/e9cc3d35ee4526d784520d4494a3e1ca969b071fb5ae5910c036a375ceec/scipy-1.16.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:978d8311674b05a8f7ff2ea6c6bce5d8b45a0cb09d4c5793e0318f448613ea65", size = 37939921, upload-time = "2025-07-27T16:29:29.108Z" },
- { url = "https://files.pythonhosted.org/packages/21/12/c0efd2941f01940119b5305c375ae5c0fcb7ec193f806bd8f158b73a1782/scipy-1.16.1-cp313-cp313-win_amd64.whl", hash = "sha256:81929ed0fa7a5713fcdd8b2e6f73697d3b4c4816d090dd34ff937c20fa90e8ab", size = 38479462, upload-time = "2025-07-27T16:30:24.078Z" },
- { url = "https://files.pythonhosted.org/packages/7a/19/c3d08b675260046a991040e1ea5d65f91f40c7df1045fffff412dcfc6765/scipy-1.16.1-cp313-cp313t-macosx_10_14_x86_64.whl", hash = "sha256:bcc12db731858abda693cecdb3bdc9e6d4bd200213f49d224fe22df82687bdd6", size = 36938832, upload-time = "2025-07-27T16:29:35.057Z" },
- { url = "https://files.pythonhosted.org/packages/81/f2/ce53db652c033a414a5b34598dba6b95f3d38153a2417c5a3883da429029/scipy-1.16.1-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:744d977daa4becb9fc59135e75c069f8d301a87d64f88f1e602a9ecf51e77b27", size = 29093084, upload-time = "2025-07-27T16:29:40.201Z" },
- { url = "https://files.pythonhosted.org/packages/a9/ae/7a10ff04a7dc15f9057d05b33737ade244e4bd195caa3f7cc04d77b9e214/scipy-1.16.1-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:dc54f76ac18073bcecffb98d93f03ed6b81a92ef91b5d3b135dcc81d55a724c7", size = 21365098, upload-time = "2025-07-27T16:29:44.295Z" },
- { url = "https://files.pythonhosted.org/packages/36/ac/029ff710959932ad3c2a98721b20b405f05f752f07344622fd61a47c5197/scipy-1.16.1-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:367d567ee9fc1e9e2047d31f39d9d6a7a04e0710c86e701e053f237d14a9b4f6", size = 23896858, upload-time = "2025-07-27T16:29:48.784Z" },
- { url = "https://files.pythonhosted.org/packages/71/13/d1ef77b6bd7898720e1f0b6b3743cb945f6c3cafa7718eaac8841035ab60/scipy-1.16.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4cf5785e44e19dcd32a0e4807555e1e9a9b8d475c6afff3d21c3c543a6aa84f4", size = 33438311, upload-time = "2025-07-27T16:29:54.164Z" },
- { url = "https://files.pythonhosted.org/packages/2d/e0/e64a6821ffbb00b4c5b05169f1c1fddb4800e9307efe3db3788995a82a2c/scipy-1.16.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3d0b80fb26d3e13a794c71d4b837e2a589d839fd574a6bbb4ee1288c213ad4a3", size = 35279542, upload-time = "2025-07-27T16:30:00.249Z" },
- { url = "https://files.pythonhosted.org/packages/57/59/0dc3c8b43e118f1e4ee2b798dcc96ac21bb20014e5f1f7a8e85cc0653bdb/scipy-1.16.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:8503517c44c18d1030d666cb70aaac1cc8913608816e06742498833b128488b7", size = 35667665, upload-time = "2025-07-27T16:30:05.916Z" },
- { url = "https://files.pythonhosted.org/packages/45/5f/844ee26e34e2f3f9f8febb9343748e72daeaec64fe0c70e9bf1ff84ec955/scipy-1.16.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:30cc4bb81c41831ecfd6dc450baf48ffd80ef5aed0f5cf3ea775740e80f16ecc", size = 38045210, upload-time = "2025-07-27T16:30:11.655Z" },
- { url = "https://files.pythonhosted.org/packages/8d/d7/210f2b45290f444f1de64bc7353aa598ece9f0e90c384b4a156f9b1a5063/scipy-1.16.1-cp313-cp313t-win_amd64.whl", hash = "sha256:c24fa02f7ed23ae514460a22c57eca8f530dbfa50b1cfdbf4f37c05b5309cc39", size = 38593661, upload-time = "2025-07-27T16:30:17.825Z" },
- { url = "https://files.pythonhosted.org/packages/81/ea/84d481a5237ed223bd3d32d6e82d7a6a96e34756492666c260cef16011d1/scipy-1.16.1-cp314-cp314-macosx_10_14_x86_64.whl", hash = "sha256:796a5a9ad36fa3a782375db8f4241ab02a091308eb079746bc0f874c9b998318", size = 36525921, upload-time = "2025-07-27T16:30:30.081Z" },
- { url = "https://files.pythonhosted.org/packages/4e/9f/d9edbdeff9f3a664807ae3aea383e10afaa247e8e6255e6d2aa4515e8863/scipy-1.16.1-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:3ea0733a2ff73fd6fdc5fecca54ee9b459f4d74f00b99aced7d9a3adb43fb1cc", size = 28564152, upload-time = "2025-07-27T16:30:35.336Z" },
- { url = "https://files.pythonhosted.org/packages/3b/95/8125bcb1fe04bc267d103e76516243e8d5e11229e6b306bda1024a5423d1/scipy-1.16.1-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:85764fb15a2ad994e708258bb4ed8290d1305c62a4e1ef07c414356a24fcfbf8", size = 20836028, upload-time = "2025-07-27T16:30:39.421Z" },
- { url = "https://files.pythonhosted.org/packages/77/9c/bf92e215701fc70bbcd3d14d86337cf56a9b912a804b9c776a269524a9e9/scipy-1.16.1-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:ca66d980469cb623b1759bdd6e9fd97d4e33a9fad5b33771ced24d0cb24df67e", size = 23489666, upload-time = "2025-07-27T16:30:43.663Z" },
- { url = "https://files.pythonhosted.org/packages/5e/00/5e941d397d9adac41b02839011594620d54d99488d1be5be755c00cde9ee/scipy-1.16.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e7cc1ffcc230f568549fc56670bcf3df1884c30bd652c5da8138199c8c76dae0", size = 33358318, upload-time = "2025-07-27T16:30:48.982Z" },
- { url = "https://files.pythonhosted.org/packages/0e/87/8db3aa10dde6e3e8e7eb0133f24baa011377d543f5b19c71469cf2648026/scipy-1.16.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3ddfb1e8d0b540cb4ee9c53fc3dea3186f97711248fb94b4142a1b27178d8b4b", size = 35185724, upload-time = "2025-07-27T16:30:54.26Z" },
- { url = "https://files.pythonhosted.org/packages/89/b4/6ab9ae443216807622bcff02690262d8184078ea467efee2f8c93288a3b1/scipy-1.16.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:4dc0e7be79e95d8ba3435d193e0d8ce372f47f774cffd882f88ea4e1e1ddc731", size = 35554335, upload-time = "2025-07-27T16:30:59.765Z" },
- { url = "https://files.pythonhosted.org/packages/9c/9a/d0e9dc03c5269a1afb60661118296a32ed5d2c24298af61b676c11e05e56/scipy-1.16.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f23634f9e5adb51b2a77766dac217063e764337fbc816aa8ad9aaebcd4397fd3", size = 37960310, upload-time = "2025-07-27T16:31:06.151Z" },
- { url = "https://files.pythonhosted.org/packages/5e/00/c8f3130a50521a7977874817ca89e0599b1b4ee8e938bad8ae798a0e1f0d/scipy-1.16.1-cp314-cp314-win_amd64.whl", hash = "sha256:57d75524cb1c5a374958a2eae3d84e1929bb971204cc9d52213fb8589183fc19", size = 39319239, upload-time = "2025-07-27T16:31:59.942Z" },
- { url = "https://files.pythonhosted.org/packages/f2/f2/1ca3eda54c3a7e4c92f6acef7db7b3a057deb135540d23aa6343ef8ad333/scipy-1.16.1-cp314-cp314t-macosx_10_14_x86_64.whl", hash = "sha256:d8da7c3dd67bcd93f15618938f43ed0995982eb38973023d46d4646c4283ad65", size = 36939460, upload-time = "2025-07-27T16:31:11.865Z" },
- { url = "https://files.pythonhosted.org/packages/80/30/98c2840b293a132400c0940bb9e140171dcb8189588619048f42b2ce7b4f/scipy-1.16.1-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:cc1d2f2fd48ba1e0620554fe5bc44d3e8f5d4185c8c109c7fbdf5af2792cfad2", size = 29093322, upload-time = "2025-07-27T16:31:17.045Z" },
- { url = "https://files.pythonhosted.org/packages/c1/e6/1e6e006e850622cf2a039b62d1a6ddc4497d4851e58b68008526f04a9a00/scipy-1.16.1-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:21a611ced9275cb861bacadbada0b8c0623bc00b05b09eb97f23b370fc2ae56d", size = 21365329, upload-time = "2025-07-27T16:31:21.188Z" },
- { url = "https://files.pythonhosted.org/packages/8e/02/72a5aa5b820589dda9a25e329ca752842bfbbaf635e36bc7065a9b42216e/scipy-1.16.1-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:8dfbb25dffc4c3dd9371d8ab456ca81beeaf6f9e1c2119f179392f0dc1ab7695", size = 23897544, upload-time = "2025-07-27T16:31:25.408Z" },
- { url = "https://files.pythonhosted.org/packages/2b/dc/7122d806a6f9eb8a33532982234bed91f90272e990f414f2830cfe656e0b/scipy-1.16.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f0ebb7204f063fad87fc0a0e4ff4a2ff40b2a226e4ba1b7e34bf4b79bf97cd86", size = 33442112, upload-time = "2025-07-27T16:31:30.62Z" },
- { url = "https://files.pythonhosted.org/packages/24/39/e383af23564daa1021a5b3afbe0d8d6a68ec639b943661841f44ac92de85/scipy-1.16.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f1b9e5962656f2734c2b285a8745358ecb4e4efbadd00208c80a389227ec61ff", size = 35286594, upload-time = "2025-07-27T16:31:36.112Z" },
- { url = "https://files.pythonhosted.org/packages/95/47/1a0b0aff40c3056d955f38b0df5d178350c3d74734ec54f9c68d23910be5/scipy-1.16.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5e1a106f8c023d57a2a903e771228bf5c5b27b5d692088f457acacd3b54511e4", size = 35665080, upload-time = "2025-07-27T16:31:42.025Z" },
- { url = "https://files.pythonhosted.org/packages/64/df/ce88803e9ed6e27fe9b9abefa157cf2c80e4fa527cf17ee14be41f790ad4/scipy-1.16.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:709559a1db68a9abc3b2c8672c4badf1614f3b440b3ab326d86a5c0491eafae3", size = 38050306, upload-time = "2025-07-27T16:31:48.109Z" },
- { url = "https://files.pythonhosted.org/packages/6e/6c/a76329897a7cae4937d403e623aa6aaea616a0bb5b36588f0b9d1c9a3739/scipy-1.16.1-cp314-cp314t-win_amd64.whl", hash = "sha256:c0c804d60492a0aad7f5b2bb1862f4548b990049e27e828391ff2bf6f7199998", size = 39427705, upload-time = "2025-07-27T16:31:53.96Z" },
-]
-
-[[package]]
-name = "sentence-transformers"
-version = "5.1.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "huggingface-hub" },
- { name = "pillow" },
- { name = "scikit-learn", version = "1.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
- { name = "scikit-learn", version = "1.7.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
- { name = "scipy", version = "1.13.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
- { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" },
- { name = "scipy", version = "1.16.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
- { name = "torch" },
- { name = "tqdm" },
- { name = "transformers" },
- { name = "typing-extensions" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/46/b8/1b99379b730bc403d8e9ddc2db56f8ac9ce743734b44a1dbeebb900490d4/sentence_transformers-5.1.0.tar.gz", hash = "sha256:70c7630697cc1c64ffca328d6e8688430ebd134b3c2df03dc07cb3a016b04739", size = 370745, upload-time = "2025-08-06T13:48:55.226Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/6d/70/2b5b76e98191ec3b8b0d1dde52d00ddcc3806799149a9ce987b0d2d31015/sentence_transformers-5.1.0-py3-none-any.whl", hash = "sha256:fc803929f6a3ce82e2b2c06e0efed7a36de535c633d5ce55efac0b710ea5643e", size = 483377, upload-time = "2025-08-06T13:48:53.627Z" },
-]
-
-[[package]]
-name = "sentry-sdk"
-version = "2.35.1"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "certifi" },
- { name = "urllib3", version = "1.26.20", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
- { name = "urllib3", version = "2.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/72/75/6223b9ffa0bf5a79ece08055469be73c18034e46ed082742a0899cc58351/sentry_sdk-2.35.1.tar.gz", hash = "sha256:241b41e059632fe1f7c54ae6e1b93af9456aebdfc297be9cf7ecfd6da5167e8e", size = 343145, upload-time = "2025-08-26T08:23:32.429Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/62/1f/5feb6c42cc30126e9574eabc28139f8c626b483a47c537f648d133628df0/sentry_sdk-2.35.1-py2.py3-none-any.whl", hash = "sha256:13b6d6cfdae65d61fe1396a061cf9113b20f0ec1bcb257f3826b88f01bb55720", size = 363887, upload-time = "2025-08-26T08:23:30.335Z" },
-]
-
-[[package]]
-name = "setuptools"
-version = "80.9.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/18/5d/3bf57dcd21979b887f014ea83c24ae194cfcd12b9e0fda66b957c69d1fca/setuptools-80.9.0.tar.gz", hash = "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c", size = 1319958, upload-time = "2025-05-27T00:56:51.443Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/a3/dc/17031897dae0efacfea57dfd3a82fdd2a2aeb58e0ff71b77b87e44edc772/setuptools-80.9.0-py3-none-any.whl", hash = "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922", size = 1201486, upload-time = "2025-05-27T00:56:49.664Z" },
-]
-
-[[package]]
-name = "simplejson"
-version = "3.20.1"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/af/92/51b417685abd96b31308b61b9acce7ec50d8e1de8fbc39a7fd4962c60689/simplejson-3.20.1.tar.gz", hash = "sha256:e64139b4ec4f1f24c142ff7dcafe55a22b811a74d86d66560c8815687143037d", size = 85591, upload-time = "2025-02-15T05:18:53.15Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/25/c4/627214fb418cd4a17fb0230ff0b6c3bb4a85cbb48dd69c85dcc3b85df828/simplejson-3.20.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e580aa65d5f6c3bf41b9b4afe74be5d5ddba9576701c107c772d936ea2b5043a", size = 93790, upload-time = "2025-02-15T05:15:32.954Z" },
- { url = "https://files.pythonhosted.org/packages/15/ca/56a6a2a33cbcf330c4d71af3f827c47e4e0ba791e78f2642f3d1ab02ff31/simplejson-3.20.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4a586ce4f78cec11f22fe55c5bee0f067e803aab9bad3441afe2181693b5ebb5", size = 75707, upload-time = "2025-02-15T05:15:34.954Z" },
- { url = "https://files.pythonhosted.org/packages/a9/c8/3d92b67e03a3b6207d97202669f9454ed700b35ade9bd4428265a078fb6c/simplejson-3.20.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:74a1608f9e6e8c27a4008d70a54270868306d80ed48c9df7872f9f4b8ac87808", size = 75700, upload-time = "2025-02-15T05:15:37.144Z" },
- { url = "https://files.pythonhosted.org/packages/74/30/20001219d6fdca4aaa3974c96dfb6955a766b4e2cc950505a5b51fd050b0/simplejson-3.20.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:03db8cb64154189a92a7786209f24e391644f3a3fa335658be2df2af1960b8d8", size = 138672, upload-time = "2025-02-15T05:15:38.547Z" },
- { url = "https://files.pythonhosted.org/packages/21/47/50157810876c2a7ebbd6e6346ec25eda841fe061fecaa02538a7742a3d2a/simplejson-3.20.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eea7e2b7d858f6fdfbf0fe3cb846d6bd8a45446865bc09960e51f3d473c2271b", size = 146616, upload-time = "2025-02-15T05:15:39.871Z" },
- { url = "https://files.pythonhosted.org/packages/95/60/8c97cdc93096437b0aca2745aca63c880fe2315fd7f6a6ce6edbb344a2ae/simplejson-3.20.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e66712b17d8425bb7ff8968d4c7c7fd5a2dd7bd63728b28356223c000dd2f91f", size = 134344, upload-time = "2025-02-15T05:15:42.091Z" },
- { url = "https://files.pythonhosted.org/packages/bb/9e/da184f0e9bb3a5d7ffcde713bd41b4fe46cca56b6f24d9bd155fac56805a/simplejson-3.20.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a2cc4f6486f9f515b62f5831ff1888886619b84fc837de68f26d919ba7bbdcbc", size = 138017, upload-time = "2025-02-15T05:15:43.542Z" },
- { url = "https://files.pythonhosted.org/packages/31/db/00d1a8d9b036db98f678c8a3c69ed17d2894d1768d7a00576e787ad3e546/simplejson-3.20.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a3c2df555ee4016148fa192e2b9cd9e60bc1d40769366134882685e90aee2a1e", size = 140118, upload-time = "2025-02-15T05:15:45.7Z" },
- { url = "https://files.pythonhosted.org/packages/52/21/57fc47eab8c1c73390b933a5ba9271f08e3e1ec83162c580357f28f5b97c/simplejson-3.20.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:78520f04b7548a5e476b5396c0847e066f1e0a4c0c5e920da1ad65e95f410b11", size = 140314, upload-time = "2025-02-15T05:16:07.949Z" },
- { url = "https://files.pythonhosted.org/packages/ad/cc/7cfd78d1e0fa5e57350b98cfe77353b6dfa13dce21afa4060e1019223852/simplejson-3.20.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f4bd49ecde87b0fe9f55cc971449a32832bca9910821f7072bbfae1155eaa007", size = 148544, upload-time = "2025-02-15T05:16:09.455Z" },
- { url = "https://files.pythonhosted.org/packages/63/26/1c894a1c2bd95dc8be0cf5a2fa73b0d173105b6ca18c90cb981ff10443d0/simplejson-3.20.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7eaae2b88eb5da53caaffdfa50e2e12022553949b88c0df4f9a9663609373f72", size = 141172, upload-time = "2025-02-15T05:16:10.966Z" },
- { url = "https://files.pythonhosted.org/packages/93/27/0717dccc10cd9988dbf1314def52ab32678a95a95328bb37cafacf499400/simplejson-3.20.1-cp310-cp310-win32.whl", hash = "sha256:e836fb88902799eac8debc2b642300748f4860a197fa3d9ea502112b6bb8e142", size = 74181, upload-time = "2025-02-15T05:16:12.361Z" },
- { url = "https://files.pythonhosted.org/packages/5f/af/593f896573f306519332d4287b1ab8b7b888c239bbd5159f7054d7055c2d/simplejson-3.20.1-cp310-cp310-win_amd64.whl", hash = "sha256:b122a19b552b212fc3b5b96fc5ce92333d4a9ac0a800803e1f17ebb16dac4be5", size = 75738, upload-time = "2025-02-15T05:16:14.438Z" },
- { url = "https://files.pythonhosted.org/packages/76/59/74bc90d1c051bc2432c96b34bd4e8036875ab58b4fcbe4d6a5a76985f853/simplejson-3.20.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:325b8c107253d3217e89d7b50c71015b5b31e2433e6c5bf38967b2f80630a8ca", size = 92132, upload-time = "2025-02-15T05:16:15.743Z" },
- { url = "https://files.pythonhosted.org/packages/71/c7/1970916e0c51794fff89f76da2f632aaf0b259b87753c88a8c409623d3e1/simplejson-3.20.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:88a7baa8211089b9e58d78fbc1b0b322103f3f3d459ff16f03a36cece0d0fcf0", size = 74956, upload-time = "2025-02-15T05:16:17.062Z" },
- { url = "https://files.pythonhosted.org/packages/c8/0d/98cc5909180463f1d75fac7180de62d4cdb4e82c4fef276b9e591979372c/simplejson-3.20.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:299b1007b8101d50d95bc0db1bf5c38dc372e85b504cf77f596462083ee77e3f", size = 74772, upload-time = "2025-02-15T05:16:19.204Z" },
- { url = "https://files.pythonhosted.org/packages/e1/94/a30a5211a90d67725a3e8fcc1c788189f2ae2ed2b96b63ed15d0b7f5d6bb/simplejson-3.20.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:03ec618ed65caab48e81e3ed29586236a8e57daef792f1f3bb59504a7e98cd10", size = 143575, upload-time = "2025-02-15T05:16:21.337Z" },
- { url = "https://files.pythonhosted.org/packages/ee/08/cdb6821f1058eb5db46d252de69ff7e6c53f05f1bae6368fe20d5b51d37e/simplejson-3.20.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cd2cdead1d3197f0ff43373cf4730213420523ba48697743e135e26f3d179f38", size = 153241, upload-time = "2025-02-15T05:16:22.859Z" },
- { url = "https://files.pythonhosted.org/packages/4c/2d/ca3caeea0bdc5efc5503d5f57a2dfb56804898fb196dfada121323ee0ccb/simplejson-3.20.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3466d2839fdc83e1af42e07b90bc8ff361c4e8796cd66722a40ba14e458faddd", size = 141500, upload-time = "2025-02-15T05:16:25.068Z" },
- { url = "https://files.pythonhosted.org/packages/e1/33/d3e0779d5c58245e7370c98eb969275af6b7a4a5aec3b97cbf85f09ad328/simplejson-3.20.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d492ed8e92f3a9f9be829205f44b1d0a89af6582f0cf43e0d129fa477b93fe0c", size = 144757, upload-time = "2025-02-15T05:16:28.301Z" },
- { url = "https://files.pythonhosted.org/packages/54/53/2d93128bb55861b2fa36c5944f38da51a0bc6d83e513afc6f7838440dd15/simplejson-3.20.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:f924b485537b640dc69434565463fd6fc0c68c65a8c6e01a823dd26c9983cf79", size = 144409, upload-time = "2025-02-15T05:16:29.687Z" },
- { url = "https://files.pythonhosted.org/packages/99/4c/dac310a98f897ad3435b4bdc836d92e78f09e38c5dbf28211ed21dc59fa2/simplejson-3.20.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:9e8eacf6a3491bf76ea91a8d46726368a6be0eb94993f60b8583550baae9439e", size = 146082, upload-time = "2025-02-15T05:16:31.064Z" },
- { url = "https://files.pythonhosted.org/packages/ee/22/d7ba958cfed39827335b82656b1c46f89678faecda9a7677b47e87b48ee6/simplejson-3.20.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:d34d04bf90b4cea7c22d8b19091633908f14a096caa301b24c2f3d85b5068fb8", size = 154339, upload-time = "2025-02-15T05:16:32.719Z" },
- { url = "https://files.pythonhosted.org/packages/b8/c8/b072b741129406a7086a0799c6f5d13096231bf35fdd87a0cffa789687fc/simplejson-3.20.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:69dd28d4ce38390ea4aaf212902712c0fd1093dc4c1ff67e09687c3c3e15a749", size = 147915, upload-time = "2025-02-15T05:16:34.291Z" },
- { url = "https://files.pythonhosted.org/packages/6c/46/8347e61e9cf3db5342a42f7fd30a81b4f5cf85977f916852d7674a540907/simplejson-3.20.1-cp311-cp311-win32.whl", hash = "sha256:dfe7a9da5fd2a3499436cd350f31539e0a6ded5da6b5b3d422df016444d65e43", size = 73972, upload-time = "2025-02-15T05:16:35.712Z" },
- { url = "https://files.pythonhosted.org/packages/01/85/b52f24859237b4e9d523d5655796d911ba3d46e242eb1959c45b6af5aedd/simplejson-3.20.1-cp311-cp311-win_amd64.whl", hash = "sha256:896a6c04d7861d507d800da7642479c3547060bf97419d9ef73d98ced8258766", size = 75595, upload-time = "2025-02-15T05:16:36.957Z" },
- { url = "https://files.pythonhosted.org/packages/8d/eb/34c16a1ac9ba265d024dc977ad84e1659d931c0a700967c3e59a98ed7514/simplejson-3.20.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f31c4a3a7ab18467ee73a27f3e59158255d1520f3aad74315edde7a940f1be23", size = 93100, upload-time = "2025-02-15T05:16:38.801Z" },
- { url = "https://files.pythonhosted.org/packages/41/fc/2c2c007d135894971e6814e7c0806936e5bade28f8db4dd7e2a58b50debd/simplejson-3.20.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:884e6183d16b725e113b83a6fc0230152ab6627d4d36cb05c89c2c5bccfa7bc6", size = 75464, upload-time = "2025-02-15T05:16:40.905Z" },
- { url = "https://files.pythonhosted.org/packages/0f/05/2b5ecb33b776c34bb5cace5de5d7669f9b60e3ca13c113037b2ca86edfbd/simplejson-3.20.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:03d7a426e416fe0d3337115f04164cd9427eb4256e843a6b8751cacf70abc832", size = 75112, upload-time = "2025-02-15T05:16:42.246Z" },
- { url = "https://files.pythonhosted.org/packages/fe/36/1f3609a2792f06cd4b71030485f78e91eb09cfd57bebf3116bf2980a8bac/simplejson-3.20.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:000602141d0bddfcff60ea6a6e97d5e10c9db6b17fd2d6c66199fa481b6214bb", size = 150182, upload-time = "2025-02-15T05:16:43.557Z" },
- { url = "https://files.pythonhosted.org/packages/2f/b0/053fbda38b8b602a77a4f7829def1b4f316cd8deb5440a6d3ee90790d2a4/simplejson-3.20.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:af8377a8af78226e82e3a4349efdde59ffa421ae88be67e18cef915e4023a595", size = 158363, upload-time = "2025-02-15T05:16:45.748Z" },
- { url = "https://files.pythonhosted.org/packages/d1/4b/2eb84ae867539a80822e92f9be4a7200dffba609275faf99b24141839110/simplejson-3.20.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:15c7de4c88ab2fbcb8781a3b982ef883696736134e20b1210bca43fb42ff1acf", size = 148415, upload-time = "2025-02-15T05:16:47.861Z" },
- { url = "https://files.pythonhosted.org/packages/e0/bd/400b0bd372a5666addf2540c7358bfc3841b9ce5cdbc5cc4ad2f61627ad8/simplejson-3.20.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:455a882ff3f97d810709f7b620007d4e0aca8da71d06fc5c18ba11daf1c4df49", size = 152213, upload-time = "2025-02-15T05:16:49.25Z" },
- { url = "https://files.pythonhosted.org/packages/50/12/143f447bf6a827ee9472693768dc1a5eb96154f8feb140a88ce6973a3cfa/simplejson-3.20.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:fc0f523ce923e7f38eb67804bc80e0a028c76d7868500aa3f59225574b5d0453", size = 150048, upload-time = "2025-02-15T05:16:51.5Z" },
- { url = "https://files.pythonhosted.org/packages/5e/ea/dd9b3e8e8ed710a66f24a22c16a907c9b539b6f5f45fd8586bd5c231444e/simplejson-3.20.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:76461ec929282dde4a08061071a47281ad939d0202dc4e63cdd135844e162fbc", size = 151668, upload-time = "2025-02-15T05:16:53Z" },
- { url = "https://files.pythonhosted.org/packages/99/af/ee52a8045426a0c5b89d755a5a70cc821815ef3c333b56fbcad33c4435c0/simplejson-3.20.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:ab19c2da8c043607bde4d4ef3a6b633e668a7d2e3d56f40a476a74c5ea71949f", size = 158840, upload-time = "2025-02-15T05:16:54.851Z" },
- { url = "https://files.pythonhosted.org/packages/68/db/ab32869acea6b5de7d75fa0dac07a112ded795d41eaa7e66c7813b17be95/simplejson-3.20.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b2578bedaedf6294415197b267d4ef678fea336dd78ee2a6d2f4b028e9d07be3", size = 154212, upload-time = "2025-02-15T05:16:56.318Z" },
- { url = "https://files.pythonhosted.org/packages/fa/7a/e3132d454977d75a3bf9a6d541d730f76462ebf42a96fea2621498166f41/simplejson-3.20.1-cp312-cp312-win32.whl", hash = "sha256:339f407373325a36b7fd744b688ba5bae0666b5d340ec6d98aebc3014bf3d8ea", size = 74101, upload-time = "2025-02-15T05:16:57.746Z" },
- { url = "https://files.pythonhosted.org/packages/bc/5d/4e243e937fa3560107c69f6f7c2eed8589163f5ed14324e864871daa2dd9/simplejson-3.20.1-cp312-cp312-win_amd64.whl", hash = "sha256:627d4486a1ea7edf1f66bb044ace1ce6b4c1698acd1b05353c97ba4864ea2e17", size = 75736, upload-time = "2025-02-15T05:16:59.017Z" },
- { url = "https://files.pythonhosted.org/packages/c4/03/0f453a27877cb5a5fff16a975925f4119102cc8552f52536b9a98ef0431e/simplejson-3.20.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:71e849e7ceb2178344998cbe5ade101f1b329460243c79c27fbfc51c0447a7c3", size = 93109, upload-time = "2025-02-15T05:17:00.377Z" },
- { url = "https://files.pythonhosted.org/packages/74/1f/a729f4026850cabeaff23e134646c3f455e86925d2533463420635ae54de/simplejson-3.20.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b63fdbab29dc3868d6f009a59797cefaba315fd43cd32ddd998ee1da28e50e29", size = 75475, upload-time = "2025-02-15T05:17:02.544Z" },
- { url = "https://files.pythonhosted.org/packages/e2/14/50a2713fee8ff1f8d655b1a14f4a0f1c0c7246768a1b3b3d12964a4ed5aa/simplejson-3.20.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1190f9a3ce644fd50ec277ac4a98c0517f532cfebdcc4bd975c0979a9f05e1fb", size = 75112, upload-time = "2025-02-15T05:17:03.875Z" },
- { url = "https://files.pythonhosted.org/packages/45/86/ea9835abb646755140e2d482edc9bc1e91997ed19a59fd77ae4c6a0facea/simplejson-3.20.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1336ba7bcb722ad487cd265701ff0583c0bb6de638364ca947bb84ecc0015d1", size = 150245, upload-time = "2025-02-15T05:17:06.899Z" },
- { url = "https://files.pythonhosted.org/packages/12/b4/53084809faede45da829fe571c65fbda8479d2a5b9c633f46b74124d56f5/simplejson-3.20.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e975aac6a5acd8b510eba58d5591e10a03e3d16c1cf8a8624ca177491f7230f0", size = 158465, upload-time = "2025-02-15T05:17:08.707Z" },
- { url = "https://files.pythonhosted.org/packages/a9/7d/d56579468d1660b3841e1f21c14490d103e33cf911886b22652d6e9683ec/simplejson-3.20.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6a6dd11ee282937ad749da6f3b8d87952ad585b26e5edfa10da3ae2536c73078", size = 148514, upload-time = "2025-02-15T05:17:11.323Z" },
- { url = "https://files.pythonhosted.org/packages/19/e3/874b1cca3d3897b486d3afdccc475eb3a09815bf1015b01cf7fcb52a55f0/simplejson-3.20.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ab980fcc446ab87ea0879edad41a5c28f2d86020014eb035cf5161e8de4474c6", size = 152262, upload-time = "2025-02-15T05:17:13.543Z" },
- { url = "https://files.pythonhosted.org/packages/32/84/f0fdb3625292d945c2bd13a814584603aebdb38cfbe5fe9be6b46fe598c4/simplejson-3.20.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f5aee2a4cb6b146bd17333ac623610f069f34e8f31d2f4f0c1a2186e50c594f0", size = 150164, upload-time = "2025-02-15T05:17:15.021Z" },
- { url = "https://files.pythonhosted.org/packages/95/51/6d625247224f01eaaeabace9aec75ac5603a42f8ebcce02c486fbda8b428/simplejson-3.20.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:652d8eecbb9a3b6461b21ec7cf11fd0acbab144e45e600c817ecf18e4580b99e", size = 151795, upload-time = "2025-02-15T05:17:16.542Z" },
- { url = "https://files.pythonhosted.org/packages/7f/d9/bb921df6b35be8412f519e58e86d1060fddf3ad401b783e4862e0a74c4c1/simplejson-3.20.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:8c09948f1a486a89251ee3a67c9f8c969b379f6ffff1a6064b41fea3bce0a112", size = 159027, upload-time = "2025-02-15T05:17:18.083Z" },
- { url = "https://files.pythonhosted.org/packages/03/c5/5950605e4ad023a6621cf4c931b29fd3d2a9c1f36be937230bfc83d7271d/simplejson-3.20.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:cbbd7b215ad4fc6f058b5dd4c26ee5c59f72e031dfda3ac183d7968a99e4ca3a", size = 154380, upload-time = "2025-02-15T05:17:20.334Z" },
- { url = "https://files.pythonhosted.org/packages/66/ad/b74149557c5ec1e4e4d55758bda426f5d2ec0123cd01a53ae63b8de51fa3/simplejson-3.20.1-cp313-cp313-win32.whl", hash = "sha256:ae81e482476eaa088ef9d0120ae5345de924f23962c0c1e20abbdff597631f87", size = 74102, upload-time = "2025-02-15T05:17:22.475Z" },
- { url = "https://files.pythonhosted.org/packages/db/a9/25282fdd24493e1022f30b7f5cdf804255c007218b2bfaa655bd7ad34b2d/simplejson-3.20.1-cp313-cp313-win_amd64.whl", hash = "sha256:1b9fd15853b90aec3b1739f4471efbf1ac05066a2c7041bf8db821bb73cd2ddc", size = 75736, upload-time = "2025-02-15T05:17:24.122Z" },
- { url = "https://files.pythonhosted.org/packages/4c/ba/d32fe890a5edaf4a8518adf043bccf7866b600123f512a6de0988cf36810/simplejson-3.20.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a8011f1dd1d676befcd4d675ebdbfdbbefd3bf350052b956ba8c699fca7d8cef", size = 93773, upload-time = "2025-02-15T05:18:28.231Z" },
- { url = "https://files.pythonhosted.org/packages/48/c7/361e7f6695b56001a04e0a5cc623cd6c82ea2f45e872e61213e405cc8a24/simplejson-3.20.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e91703a4c5fec53e36875ae426ad785f4120bd1d93b65bed4752eeccd1789e0c", size = 75697, upload-time = "2025-02-15T05:18:30.006Z" },
- { url = "https://files.pythonhosted.org/packages/3c/2f/d0ff0b772d4ef092876eb85c99bc591c446b0502715551dad7dfc7f7c2c0/simplejson-3.20.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e39eaa57c7757daa25bcd21f976c46be443b73dd6c3da47fe5ce7b7048ccefe2", size = 75692, upload-time = "2025-02-15T05:18:31.424Z" },
- { url = "https://files.pythonhosted.org/packages/26/94/cab4db9530b6ca9d62f16a260e8311b04130ccd670dab75e958fcb44590e/simplejson-3.20.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ceab2ce2acdc7fbaa433a93006758db6ba9a659e80c4faa13b80b9d2318e9b17", size = 138106, upload-time = "2025-02-15T05:18:32.907Z" },
- { url = "https://files.pythonhosted.org/packages/40/22/11c0f746bdb44c297cea8a37d8f7ccb75ea6681132aadfb9f820d9a52647/simplejson-3.20.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6d4f320c33277a5b715db5bf5b10dae10c19076bd6d66c2843e04bd12d1f1ea5", size = 146242, upload-time = "2025-02-15T05:18:35.223Z" },
- { url = "https://files.pythonhosted.org/packages/78/e9/b7c4c26f29b41cc41ba5f0224c47adbfa7f28427418edfd58ab122f3b584/simplejson-3.20.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2b6436c48e64378fa844d8c9e58a5ed0352bbcfd4028369a9b46679b7ab79d2d", size = 133866, upload-time = "2025-02-15T05:18:36.998Z" },
- { url = "https://files.pythonhosted.org/packages/09/68/1e81ed83f38906c8859f2b973afb19302357d6003e724a6105cee0f61ec7/simplejson-3.20.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e18345c8dda5d699be8166b61f9d80aaee4545b709f1363f60813dc032dac53", size = 137444, upload-time = "2025-02-15T05:18:38.763Z" },
- { url = "https://files.pythonhosted.org/packages/9a/6b/8d1e076c543277c1d603230eec24f4dd75ebce46d351c0679526d202981f/simplejson-3.20.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:90b573693d1526bed576f6817e2a492eaaef68f088b57d7a9e83d122bbb49e51", size = 139617, upload-time = "2025-02-15T05:18:40.36Z" },
- { url = "https://files.pythonhosted.org/packages/d1/46/7b74803de10d4157c5cd2e89028897fa733374667bc5520a44b23b6c887a/simplejson-3.20.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:272cc767826e924a6bd369ea3dbf18e166ded29059c7a4d64d21a9a22424b5b5", size = 139725, upload-time = "2025-02-15T05:18:42.012Z" },
- { url = "https://files.pythonhosted.org/packages/4b/8f/9991582665a7b6d95415e439bb4fbaa4faf0f77231666675a0fd1de54107/simplejson-3.20.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:51b41f284d603c4380732d7d619f8b34bd04bc4aa0ed0ed5f4ffd0539b14da44", size = 148010, upload-time = "2025-02-15T05:18:43.749Z" },
- { url = "https://files.pythonhosted.org/packages/54/ee/3c6e91989cdf65ec75e75662d9f15cfe167a792b893806169ea5b1da6fd2/simplejson-3.20.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:6e6697a3067d281f01de0fe96fc7cba4ea870d96d7deb7bfcf85186d74456503", size = 140624, upload-time = "2025-02-15T05:18:45.498Z" },
- { url = "https://files.pythonhosted.org/packages/9d/bd/05e13ebb7ead81c8b555f4ccc741ea7dfa0ef5c2a0c183d6a7bc50a02bca/simplejson-3.20.1-cp39-cp39-win32.whl", hash = "sha256:6dd3a1d5aca87bf947f3339b0f8e8e329f1badf548bdbff37fac63c17936da8e", size = 74148, upload-time = "2025-02-15T05:18:47.27Z" },
- { url = "https://files.pythonhosted.org/packages/88/c9/d8bf87aaebec5a4c3ccfd5228689578e2fe77027d6114a259255d54969bf/simplejson-3.20.1-cp39-cp39-win_amd64.whl", hash = "sha256:463f1fca8fbf23d088e5850fdd0dd4d5faea8900a9f9680270bd98fd649814ca", size = 75732, upload-time = "2025-02-15T05:18:49.598Z" },
- { url = "https://files.pythonhosted.org/packages/4b/30/00f02a0a921556dd5a6db1ef2926a1bc7a8bbbfb1c49cfed68a275b8ab2b/simplejson-3.20.1-py3-none-any.whl", hash = "sha256:8a6c1bbac39fa4a79f83cbf1df6ccd8ff7069582a9fd8db1e52cea073bc2c697", size = 57121, upload-time = "2025-02-15T05:18:51.243Z" },
-]
-
-[[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, upload-time = "2024-12-04T17:35:28.174Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" },
-]
-
-[[package]]
-name = "smmap"
-version = "5.0.2"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/44/cd/a040c4b3119bbe532e5b0732286f805445375489fceaec1f48306068ee3b/smmap-5.0.2.tar.gz", hash = "sha256:26ea65a03958fa0c8a1c7e8c7a58fdc77221b8910f6be2131affade476898ad5", size = 22329, upload-time = "2025-01-02T07:14:40.909Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/04/be/d09147ad1ec7934636ad912901c5fd7667e1c858e19d355237db0d0cd5e4/smmap-5.0.2-py3-none-any.whl", hash = "sha256:b30115f0def7d7531d22a0fb6502488d879e75b260a9db4d0819cfb25403af5e", size = 24303, upload-time = "2025-01-02T07:14:38.724Z" },
-]
-
-[[package]]
-name = "sniffio"
-version = "1.3.1"
-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, upload-time = "2024-02-25T23:20:04.057Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" },
-]
-
-[[package]]
-name = "snowflake-connector-python"
-version = "3.17.2"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "asn1crypto" },
- { name = "boto3" },
- { name = "botocore" },
- { name = "certifi" },
- { name = "cffi" },
- { name = "charset-normalizer" },
- { name = "cryptography" },
- { name = "filelock" },
- { name = "idna" },
- { name = "packaging" },
- { name = "platformdirs" },
- { name = "pyjwt" },
- { name = "pyopenssl" },
- { name = "pytz" },
- { name = "requests" },
- { name = "sortedcontainers" },
- { name = "tomlkit" },
- { name = "typing-extensions" },
- { name = "urllib3", version = "1.26.20", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/48/92/68a0e5cba0991c8d375a9bf8a2b6a6cd1d1149e4a32075a9b6e723cc6ca2/snowflake_connector_python-3.17.2.tar.gz", hash = "sha256:0708de0f64e3c6789fdf0d75f22c55eb0215b60b125a5d7d18e48a1d5b2e7def", size = 794385, upload-time = "2025-08-20T14:41:22.965Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/31/70/325040e309c308a220e86cafb021156264b4778f78d4af5897874df80db1/snowflake_connector_python-3.17.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:58c273b610ba25d6386134238c27290ea9ed468443da4061d80050288dc7b01d", size = 1007927, upload-time = "2025-08-20T14:41:24.906Z" },
- { url = "https://files.pythonhosted.org/packages/a2/ba/2b35750aa1a3238735b7a5d525190ec11c3444accb10ced873688d6b5244/snowflake_connector_python-3.17.2-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:ed2c183058245a63125ac1ebb20062e41c0cc5b0ab6cde5df1250e1c332d3da1", size = 1020560, upload-time = "2025-08-20T14:41:26.574Z" },
- { url = "https://files.pythonhosted.org/packages/91/e3/f61cb425536ec22f92c7956ef6d4d836f510fd5020ca1f35c6b0612b1aae/snowflake_connector_python-3.17.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89d5001de2b708ba3941cf1a2a33e391ba3c11b4f5176bf89ee0c76590eb0900", size = 2633845, upload-time = "2025-08-20T14:41:04.568Z" },
- { url = "https://files.pythonhosted.org/packages/9b/d4/4251c85998fa19cac5345a7b2e7c644507acd1e88217f780663e6d7d064f/snowflake_connector_python-3.17.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc3d216fa04f3f9126207d71aa150f219367fc44759caabec4d2d96687242135", size = 2661519, upload-time = "2025-08-20T14:41:07.999Z" },
- { url = "https://files.pythonhosted.org/packages/b2/ca/69972cda6b58049c7eb2b0c3df30fff1ec6b32fa4b99fafff5aaadedda05/snowflake_connector_python-3.17.2-cp310-cp310-win_amd64.whl", hash = "sha256:b6b5956b59f3c379c72a008a9d413e878188979c003e2aced0e1eebf27ca4cc0", size = 1155233, upload-time = "2025-08-20T14:41:42.269Z" },
- { url = "https://files.pythonhosted.org/packages/fa/42/810ec5d744002563873a505ea524be2ce487c48c6a675fc0ad2338b6cf03/snowflake_connector_python-3.17.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d8a97d7dc4abf194ca540564f75b4b4a79004a553767001e1d70fae49bb7bf4f", size = 1008087, upload-time = "2025-08-20T14:41:27.905Z" },
- { url = "https://files.pythonhosted.org/packages/2b/e8/72d2bbdc3aa05cc35d76081da365516f7173cf04b312afc1c6ad01ad2fc4/snowflake_connector_python-3.17.2-cp311-cp311-macosx_11_0_x86_64.whl", hash = "sha256:d06d9c4ade7fb8f9d1e237cab9c5d38408785d3d2ddfe94001bbef9bb076203f", size = 1020548, upload-time = "2025-08-20T14:41:29.228Z" },
- { url = "https://files.pythonhosted.org/packages/a8/52/d616ad8dbc729f7acb5bfc422f730e6b3d16af01575c6940c96f960c7db4/snowflake_connector_python-3.17.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6e737d8b66f0bdbb698f8e1a394f191eadcd16b9145abc08fdf8959a77d6a70", size = 2647033, upload-time = "2025-08-20T14:41:09.612Z" },
- { url = "https://files.pythonhosted.org/packages/28/63/81529e7ae2125962823223e082c2e35c5ec489b189c0ea68e8d34b0bf351/snowflake_connector_python-3.17.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:53ad13df88b2dd568e8f9c2944386c8446482152141dff83f27e680ceffa1b10", size = 2672278, upload-time = "2025-08-20T14:41:11.49Z" },
- { url = "https://files.pythonhosted.org/packages/7d/2d/69fbfac3142c871407de3deceb74ccc4ab3a90fb7e80b8de0ea736e57cbe/snowflake_connector_python-3.17.2-cp311-cp311-win_amd64.whl", hash = "sha256:a601dfe86ab4b7900b68fa1cfadcc674b23f6f6b4a1c57c3202e57761de6b183", size = 1155288, upload-time = "2025-08-20T14:41:43.645Z" },
- { url = "https://files.pythonhosted.org/packages/2a/f6/af023d21530bbe6e3c8ec89226480007f6ec9a6fabd19fba2d0c9d518f08/snowflake_connector_python-3.17.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:63a57cb67d14c7da6b91b8db0db3a92dfa5cf5082c388006d2f9f480c2df0234", size = 1007126, upload-time = "2025-08-20T14:41:30.637Z" },
- { url = "https://files.pythonhosted.org/packages/8d/f5/489b41a8c0b9c270b203c991e90f3db7d2ffb08a1810f7722773f52b6f7c/snowflake_connector_python-3.17.2-cp312-cp312-macosx_11_0_x86_64.whl", hash = "sha256:c6f59c47e43bf889fd5a2ead8ffeacd447cd792d711f010c91b23b4804c67f6b", size = 1018904, upload-time = "2025-08-20T14:41:31.952Z" },
- { url = "https://files.pythonhosted.org/packages/8c/c9/fa7a45e16f48f2d5beaccc383ffa8ecd9a6071d0ee3a4228901fc1667c29/snowflake_connector_python-3.17.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4934a4f552876592696ab8def7e2d33163f99bb059bac4b90b5cfce165aa6499", size = 2667918, upload-time = "2025-08-20T14:41:13.454Z" },
- { url = "https://files.pythonhosted.org/packages/32/6c/4a2f6564b614745d26735854806f33ce9029b380d2ae6d72b771bacf1e60/snowflake_connector_python-3.17.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70f7cf3dcfcceea99d4b9d4fb8ef67788790832cbeceb222fbdf5595da52103b", size = 2696625, upload-time = "2025-08-20T14:41:14.843Z" },
- { url = "https://files.pythonhosted.org/packages/7f/8e/94286f36c3877ca8c1e50760b49530dec3033db6792206dffbb681d61df4/snowflake_connector_python-3.17.2-cp312-cp312-win_amd64.whl", hash = "sha256:9e05c9d55b234c8a903d96397b9eb4accbddf7d2fcfac2e27239f0f49d16f475", size = 1154225, upload-time = "2025-08-20T14:41:45.231Z" },
- { url = "https://files.pythonhosted.org/packages/54/d4/6f8810e686b6cc1b6f9e39bda3b6cb1472f5c3ef5379dd3e544b887f513d/snowflake_connector_python-3.17.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e21a68c5fb04a16f48fd5146ab7a72d17b2d3d09c9b537f64fffa99316abd670", size = 1008338, upload-time = "2025-08-20T14:41:33.296Z" },
- { url = "https://files.pythonhosted.org/packages/70/9a/d5647e2f13e753511bbd0c4c1c17fe9814a2e07262894640e2f3f1ca201d/snowflake_connector_python-3.17.2-cp313-cp313-macosx_11_0_x86_64.whl", hash = "sha256:1691f5f7ff508b1fefc491f0fb85524165681bb29242f508731515057a1b4f9d", size = 1019604, upload-time = "2025-08-20T14:41:37.557Z" },
- { url = "https://files.pythonhosted.org/packages/f3/e1/ed67c43e081009f80c469714b2551e774c41f01dc674c6af1b8076ecb950/snowflake_connector_python-3.17.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a668e5e9ae04ec0ab0f7cdd450ef3757283f37856655ae7e971ee645a25b4b3", size = 2670328, upload-time = "2025-08-20T14:41:16.206Z" },
- { url = "https://files.pythonhosted.org/packages/bf/84/7968b5d6a674f97e08e9ffe5a660194eb1e2d0457602c5622776059b7ae6/snowflake_connector_python-3.17.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed6a05c55238d076ad186e27889a72fe888f02e9c8c341127f828439cbf1e42f", size = 2698215, upload-time = "2025-08-20T14:41:18.105Z" },
- { url = "https://files.pythonhosted.org/packages/d5/fa/f85298096284893a328d4005997e98dd66a8b2921ca6aeec91f92195bb84/snowflake_connector_python-3.17.2-cp313-cp313-win_amd64.whl", hash = "sha256:360f21c576847636c3560a2607c5388f9e57e607a3558f5f5a188a71c82108e1", size = 1154275, upload-time = "2025-08-20T14:41:47.069Z" },
- { url = "https://files.pythonhosted.org/packages/d9/cd/f84f6c8bfac7a5f1d43c936183ae64c2a19f1e0bae213a4df08738abe670/snowflake_connector_python-3.17.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c526f18487fc1e7d4f6b294198079cceefa47a77084c9d2a87a2e617fceb999d", size = 1008145, upload-time = "2025-08-20T14:41:39.041Z" },
- { url = "https://files.pythonhosted.org/packages/56/cc/3c54b4efe1a5779ebbad3ffc03ee6ba7e87867c2bb9772ab60d96acc1701/snowflake_connector_python-3.17.2-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:54dfeba63d8ccef4182951ab2dc605cc7cb2a811e68e98f1146281f4721e3af0", size = 1020818, upload-time = "2025-08-20T14:41:40.356Z" },
- { url = "https://files.pythonhosted.org/packages/ab/9a/f0b639e528fb00b3451498d8d24f2e334d000831d3bb00481acc75e1706a/snowflake_connector_python-3.17.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3620f33f6a453775fb527d32308ffdf167cc426a81fc1dfdadd4cd657dc6ac9", size = 2629245, upload-time = "2025-08-20T14:41:19.804Z" },
- { url = "https://files.pythonhosted.org/packages/ca/b3/3085aa134691913cc2680af75aa715801f4bb54d3e66d1df6ffbd096c73e/snowflake_connector_python-3.17.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:07195dcde0a8265bc26f404e124ff514b47c16964e1a84625ab53d3ea52359db", size = 2657234, upload-time = "2025-08-20T14:41:21.663Z" },
- { url = "https://files.pythonhosted.org/packages/95/ed/85586ac385fe1fb83c5899b34bb360e1ef9f08fa0cf2694329b9807e24bb/snowflake_connector_python-3.17.2-cp39-cp39-win_amd64.whl", hash = "sha256:3be73a65f5fdb8f074eef129c7828134aac9b429afab18f142ecc0f7260668b8", size = 1156318, upload-time = "2025-08-20T14:41:48.439Z" },
-]
-
-[[package]]
-name = "sortedcontainers"
-version = "2.4.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/e8/c4/ba2f8066cceb6f23394729afe52f3bf7adec04bf9ed2c820b39e19299111/sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88", size = 30594, upload-time = "2021-05-16T22:03:42.897Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/32/46/9cb0e58b2deb7f82b84065f37f3bffeb12413f947f9388e4cac22c4621ce/sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0", size = 29575, upload-time = "2021-05-16T22:03:41.177Z" },
-]
-
-[[package]]
-name = "sqlalchemy"
-version = "2.0.43"
-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/d7/bc/d59b5d97d27229b0e009bd9098cd81af71c2fa5549c580a0a67b9bed0496/sqlalchemy-2.0.43.tar.gz", hash = "sha256:788bfcef6787a7764169cfe9859fe425bf44559619e1d9f56f5bddf2ebf6f417", size = 9762949, upload-time = "2025-08-11T14:24:58.438Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/8f/4e/985f7da36f09592c5ade99321c72c15101d23c0bb7eecfd1daaca5714422/sqlalchemy-2.0.43-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:70322986c0c699dca241418fcf18e637a4369e0ec50540a2b907b184c8bca069", size = 2133162, upload-time = "2025-08-11T15:52:17.854Z" },
- { url = "https://files.pythonhosted.org/packages/37/34/798af8db3cae069461e3bc0898a1610dc469386a97048471d364dc8aae1c/sqlalchemy-2.0.43-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:87accdbba88f33efa7b592dc2e8b2a9c2cdbca73db2f9d5c510790428c09c154", size = 2123082, upload-time = "2025-08-11T15:52:19.181Z" },
- { url = "https://files.pythonhosted.org/packages/fb/0f/79cf4d9dad42f61ec5af1e022c92f66c2d110b93bb1dc9b033892971abfa/sqlalchemy-2.0.43-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c00e7845d2f692ebfc7d5e4ec1a3fd87698e4337d09e58d6749a16aedfdf8612", size = 3208871, upload-time = "2025-08-11T15:50:30.656Z" },
- { url = "https://files.pythonhosted.org/packages/56/b3/59befa58fb0e1a9802c87df02344548e6d007e77e87e6084e2131c29e033/sqlalchemy-2.0.43-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:022e436a1cb39b13756cf93b48ecce7aa95382b9cfacceb80a7d263129dfd019", size = 3209583, upload-time = "2025-08-11T15:57:47.697Z" },
- { url = "https://files.pythonhosted.org/packages/29/d2/124b50c0eb8146e8f0fe16d01026c1a073844f0b454436d8544fe9b33bd7/sqlalchemy-2.0.43-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c5e73ba0d76eefc82ec0219d2301cb33bfe5205ed7a2602523111e2e56ccbd20", size = 3148177, upload-time = "2025-08-11T15:50:32.078Z" },
- { url = "https://files.pythonhosted.org/packages/83/f5/e369cd46aa84278107624617034a5825fedfc5c958b2836310ced4d2eadf/sqlalchemy-2.0.43-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:9c2e02f06c68092b875d5cbe4824238ab93a7fa35d9c38052c033f7ca45daa18", size = 3172276, upload-time = "2025-08-11T15:57:49.477Z" },
- { url = "https://files.pythonhosted.org/packages/de/2b/4602bf4c3477fa4c837c9774e6dd22e0389fc52310c4c4dfb7e7ba05e90d/sqlalchemy-2.0.43-cp310-cp310-win32.whl", hash = "sha256:e7a903b5b45b0d9fa03ac6a331e1c1d6b7e0ab41c63b6217b3d10357b83c8b00", size = 2101491, upload-time = "2025-08-11T15:54:59.191Z" },
- { url = "https://files.pythonhosted.org/packages/38/2d/bfc6b6143adef553a08295490ddc52607ee435b9c751c714620c1b3dd44d/sqlalchemy-2.0.43-cp310-cp310-win_amd64.whl", hash = "sha256:4bf0edb24c128b7be0c61cd17eef432e4bef507013292415f3fb7023f02b7d4b", size = 2125148, upload-time = "2025-08-11T15:55:00.593Z" },
- { url = "https://files.pythonhosted.org/packages/9d/77/fa7189fe44114658002566c6fe443d3ed0ec1fa782feb72af6ef7fbe98e7/sqlalchemy-2.0.43-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:52d9b73b8fb3e9da34c2b31e6d99d60f5f99fd8c1225c9dad24aeb74a91e1d29", size = 2136472, upload-time = "2025-08-11T15:52:21.789Z" },
- { url = "https://files.pythonhosted.org/packages/99/ea/92ac27f2fbc2e6c1766bb807084ca455265707e041ba027c09c17d697867/sqlalchemy-2.0.43-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f42f23e152e4545157fa367b2435a1ace7571cab016ca26038867eb7df2c3631", size = 2126535, upload-time = "2025-08-11T15:52:23.109Z" },
- { url = "https://files.pythonhosted.org/packages/94/12/536ede80163e295dc57fff69724caf68f91bb40578b6ac6583a293534849/sqlalchemy-2.0.43-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4fb1a8c5438e0c5ea51afe9c6564f951525795cf432bed0c028c1cb081276685", size = 3297521, upload-time = "2025-08-11T15:50:33.536Z" },
- { url = "https://files.pythonhosted.org/packages/03/b5/cacf432e6f1fc9d156eca0560ac61d4355d2181e751ba8c0cd9cb232c8c1/sqlalchemy-2.0.43-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db691fa174e8f7036afefe3061bc40ac2b770718be2862bfb03aabae09051aca", size = 3297343, upload-time = "2025-08-11T15:57:51.186Z" },
- { url = "https://files.pythonhosted.org/packages/ca/ba/d4c9b526f18457667de4c024ffbc3a0920c34237b9e9dd298e44c7c00ee5/sqlalchemy-2.0.43-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fe2b3b4927d0bc03d02ad883f402d5de201dbc8894ac87d2e981e7d87430e60d", size = 3232113, upload-time = "2025-08-11T15:50:34.949Z" },
- { url = "https://files.pythonhosted.org/packages/aa/79/c0121b12b1b114e2c8a10ea297a8a6d5367bc59081b2be896815154b1163/sqlalchemy-2.0.43-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4d3d9b904ad4a6b175a2de0738248822f5ac410f52c2fd389ada0b5262d6a1e3", size = 3258240, upload-time = "2025-08-11T15:57:52.983Z" },
- { url = "https://files.pythonhosted.org/packages/79/99/a2f9be96fb382f3ba027ad42f00dbe30fdb6ba28cda5f11412eee346bec5/sqlalchemy-2.0.43-cp311-cp311-win32.whl", hash = "sha256:5cda6b51faff2639296e276591808c1726c4a77929cfaa0f514f30a5f6156921", size = 2101248, upload-time = "2025-08-11T15:55:01.855Z" },
- { url = "https://files.pythonhosted.org/packages/ee/13/744a32ebe3b4a7a9c7ea4e57babae7aa22070d47acf330d8e5a1359607f1/sqlalchemy-2.0.43-cp311-cp311-win_amd64.whl", hash = "sha256:c5d1730b25d9a07727d20ad74bc1039bbbb0a6ca24e6769861c1aa5bf2c4c4a8", size = 2126109, upload-time = "2025-08-11T15:55:04.092Z" },
- { url = "https://files.pythonhosted.org/packages/61/db/20c78f1081446095450bdc6ee6cc10045fce67a8e003a5876b6eaafc5cc4/sqlalchemy-2.0.43-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:20d81fc2736509d7a2bd33292e489b056cbae543661bb7de7ce9f1c0cd6e7f24", size = 2134891, upload-time = "2025-08-11T15:51:13.019Z" },
- { url = "https://files.pythonhosted.org/packages/45/0a/3d89034ae62b200b4396f0f95319f7d86e9945ee64d2343dcad857150fa2/sqlalchemy-2.0.43-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:25b9fc27650ff5a2c9d490c13c14906b918b0de1f8fcbb4c992712d8caf40e83", size = 2123061, upload-time = "2025-08-11T15:51:14.319Z" },
- { url = "https://files.pythonhosted.org/packages/cb/10/2711f7ff1805919221ad5bee205971254845c069ee2e7036847103ca1e4c/sqlalchemy-2.0.43-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6772e3ca8a43a65a37c88e2f3e2adfd511b0b1da37ef11ed78dea16aeae85bd9", size = 3320384, upload-time = "2025-08-11T15:52:35.088Z" },
- { url = "https://files.pythonhosted.org/packages/6e/0e/3d155e264d2ed2778484006ef04647bc63f55b3e2d12e6a4f787747b5900/sqlalchemy-2.0.43-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a113da919c25f7f641ffbd07fbc9077abd4b3b75097c888ab818f962707eb48", size = 3329648, upload-time = "2025-08-11T15:56:34.153Z" },
- { url = "https://files.pythonhosted.org/packages/5b/81/635100fb19725c931622c673900da5efb1595c96ff5b441e07e3dd61f2be/sqlalchemy-2.0.43-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4286a1139f14b7d70141c67a8ae1582fc2b69105f1b09d9573494eb4bb4b2687", size = 3258030, upload-time = "2025-08-11T15:52:36.933Z" },
- { url = "https://files.pythonhosted.org/packages/0c/ed/a99302716d62b4965fded12520c1cbb189f99b17a6d8cf77611d21442e47/sqlalchemy-2.0.43-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:529064085be2f4d8a6e5fab12d36ad44f1909a18848fcfbdb59cc6d4bbe48efe", size = 3294469, upload-time = "2025-08-11T15:56:35.553Z" },
- { url = "https://files.pythonhosted.org/packages/5d/a2/3a11b06715149bf3310b55a98b5c1e84a42cfb949a7b800bc75cb4e33abc/sqlalchemy-2.0.43-cp312-cp312-win32.whl", hash = "sha256:b535d35dea8bbb8195e7e2b40059e2253acb2b7579b73c1b432a35363694641d", size = 2098906, upload-time = "2025-08-11T15:55:00.645Z" },
- { url = "https://files.pythonhosted.org/packages/bc/09/405c915a974814b90aa591280623adc6ad6b322f61fd5cff80aeaef216c9/sqlalchemy-2.0.43-cp312-cp312-win_amd64.whl", hash = "sha256:1c6d85327ca688dbae7e2b06d7d84cfe4f3fffa5b5f9e21bb6ce9d0e1a0e0e0a", size = 2126260, upload-time = "2025-08-11T15:55:02.965Z" },
- { url = "https://files.pythonhosted.org/packages/41/1c/a7260bd47a6fae7e03768bf66451437b36451143f36b285522b865987ced/sqlalchemy-2.0.43-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e7c08f57f75a2bb62d7ee80a89686a5e5669f199235c6d1dac75cd59374091c3", size = 2130598, upload-time = "2025-08-11T15:51:15.903Z" },
- { url = "https://files.pythonhosted.org/packages/8e/84/8a337454e82388283830b3586ad7847aa9c76fdd4f1df09cdd1f94591873/sqlalchemy-2.0.43-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:14111d22c29efad445cd5021a70a8b42f7d9152d8ba7f73304c4d82460946aaa", size = 2118415, upload-time = "2025-08-11T15:51:17.256Z" },
- { url = "https://files.pythonhosted.org/packages/cf/ff/22ab2328148492c4d71899d62a0e65370ea66c877aea017a244a35733685/sqlalchemy-2.0.43-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21b27b56eb2f82653168cefe6cb8e970cdaf4f3a6cb2c5e3c3c1cf3158968ff9", size = 3248707, upload-time = "2025-08-11T15:52:38.444Z" },
- { url = "https://files.pythonhosted.org/packages/dc/29/11ae2c2b981de60187f7cbc84277d9d21f101093d1b2e945c63774477aba/sqlalchemy-2.0.43-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c5a9da957c56e43d72126a3f5845603da00e0293720b03bde0aacffcf2dc04f", size = 3253602, upload-time = "2025-08-11T15:56:37.348Z" },
- { url = "https://files.pythonhosted.org/packages/b8/61/987b6c23b12c56d2be451bc70900f67dd7d989d52b1ee64f239cf19aec69/sqlalchemy-2.0.43-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5d79f9fdc9584ec83d1b3c75e9f4595c49017f5594fee1a2217117647225d738", size = 3183248, upload-time = "2025-08-11T15:52:39.865Z" },
- { url = "https://files.pythonhosted.org/packages/86/85/29d216002d4593c2ce1c0ec2cec46dda77bfbcd221e24caa6e85eff53d89/sqlalchemy-2.0.43-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9df7126fd9db49e3a5a3999442cc67e9ee8971f3cb9644250107d7296cb2a164", size = 3219363, upload-time = "2025-08-11T15:56:39.11Z" },
- { url = "https://files.pythonhosted.org/packages/b6/e4/bd78b01919c524f190b4905d47e7630bf4130b9f48fd971ae1c6225b6f6a/sqlalchemy-2.0.43-cp313-cp313-win32.whl", hash = "sha256:7f1ac7828857fcedb0361b48b9ac4821469f7694089d15550bbcf9ab22564a1d", size = 2096718, upload-time = "2025-08-11T15:55:05.349Z" },
- { url = "https://files.pythonhosted.org/packages/ac/a5/ca2f07a2a201f9497de1928f787926613db6307992fe5cda97624eb07c2f/sqlalchemy-2.0.43-cp313-cp313-win_amd64.whl", hash = "sha256:971ba928fcde01869361f504fcff3b7143b47d30de188b11c6357c0505824197", size = 2123200, upload-time = "2025-08-11T15:55:07.932Z" },
- { url = "https://files.pythonhosted.org/packages/92/95/ddb5acf74a71e0fa4f9410c7d8555f169204ae054a49693b3cd31d0bf504/sqlalchemy-2.0.43-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ceb5c832cc30663aeaf5e39657712f4c4241ad1f638d487ef7216258f6d41fe7", size = 2136445, upload-time = "2025-08-12T17:29:06.145Z" },
- { url = "https://files.pythonhosted.org/packages/ea/d4/7d7ea7dfbc1ddb0aa54dd63a686cd43842192b8e1bfb5315bb052925f704/sqlalchemy-2.0.43-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:11f43c39b4b2ec755573952bbcc58d976779d482f6f832d7f33a8d869ae891bf", size = 2126411, upload-time = "2025-08-12T17:29:08.138Z" },
- { url = "https://files.pythonhosted.org/packages/07/bd/123ba09bec14112de10e49d8835e6561feb24fd34131099d98d28d34f106/sqlalchemy-2.0.43-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:413391b2239db55be14fa4223034d7e13325a1812c8396ecd4f2c08696d5ccad", size = 3221776, upload-time = "2025-08-11T16:00:30.938Z" },
- { url = "https://files.pythonhosted.org/packages/ae/35/553e45d5b91b15980c13e1dbcd7591f49047589843fff903c086d7985afb/sqlalchemy-2.0.43-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c379e37b08c6c527181a397212346be39319fb64323741d23e46abd97a400d34", size = 3221665, upload-time = "2025-08-12T17:29:11.307Z" },
- { url = "https://files.pythonhosted.org/packages/07/4d/ff03e516087251da99bd879b5fdb2c697ff20295c836318dda988e12ec19/sqlalchemy-2.0.43-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:03d73ab2a37d9e40dec4984d1813d7878e01dbdc742448d44a7341b7a9f408c7", size = 3160067, upload-time = "2025-08-11T16:00:33.148Z" },
- { url = "https://files.pythonhosted.org/packages/ae/88/cbc7caa186ecdc5dea013e9ccc00d78b93a6638dc39656a42369a9536458/sqlalchemy-2.0.43-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:8cee08f15d9e238ede42e9bbc1d6e7158d0ca4f176e4eab21f88ac819ae3bd7b", size = 3184462, upload-time = "2025-08-12T17:29:14.919Z" },
- { url = "https://files.pythonhosted.org/packages/ab/69/f8bbd43080b6fa75cb44ff3a1cc99aaae538dd0ade1a58206912b2565d72/sqlalchemy-2.0.43-cp39-cp39-win32.whl", hash = "sha256:b3edaec7e8b6dc5cd94523c6df4f294014df67097c8217a89929c99975811414", size = 2104031, upload-time = "2025-08-11T15:48:56.453Z" },
- { url = "https://files.pythonhosted.org/packages/36/39/2ec1b0e7a4f44d833d924e7bfca8054c72e37eb73f4d02795d16d8b0230a/sqlalchemy-2.0.43-cp39-cp39-win_amd64.whl", hash = "sha256:227119ce0a89e762ecd882dc661e0aa677a690c914e358f0dd8932a2e8b2765b", size = 2128007, upload-time = "2025-08-11T15:48:57.872Z" },
- { url = "https://files.pythonhosted.org/packages/b8/d9/13bdde6521f322861fab67473cec4b1cc8999f3871953531cf61945fad92/sqlalchemy-2.0.43-py3-none-any.whl", hash = "sha256:1681c21dd2ccee222c2fe0bef671d1aef7c504087c9c4e800371cfcc8ac966fc", size = 1924759, upload-time = "2025-08-11T15:39:53.024Z" },
-]
-
-[[package]]
-name = "stack-data"
-version = "0.6.3"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "asttokens" },
- { name = "executing" },
- { name = "pure-eval" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/28/e3/55dcc2cfbc3ca9c29519eb6884dd1415ecb53b0e934862d3559ddcb7e20b/stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9", size = 44707, upload-time = "2023-09-30T13:58:05.479Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695", size = 24521, upload-time = "2023-09-30T13:58:03.53Z" },
+ { url = "https://files.pythonhosted.org/packages/ab/14/510deed325e262afeb8b360043c5d7c960da7d3ecd6d6f9496c9c56dc7f4/propcache-0.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:22d9962a358aedbb7a2e36187ff273adeaab9743373a272976d2e348d08c7770", size = 73178, upload-time = "2025-06-09T22:53:40.126Z" },
+ { url = "https://files.pythonhosted.org/packages/cd/4e/ad52a7925ff01c1325653a730c7ec3175a23f948f08626a534133427dcff/propcache-0.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0d0fda578d1dc3f77b6b5a5dce3b9ad69a8250a891760a548df850a5e8da87f3", size = 43133, upload-time = "2025-06-09T22:53:41.965Z" },
+ { url = "https://files.pythonhosted.org/packages/63/7c/e9399ba5da7780871db4eac178e9c2e204c23dd3e7d32df202092a1ed400/propcache-0.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3def3da3ac3ce41562d85db655d18ebac740cb3fa4367f11a52b3da9d03a5cc3", size = 43039, upload-time = "2025-06-09T22:53:43.268Z" },
+ { url = "https://files.pythonhosted.org/packages/22/e1/58da211eb8fdc6fc854002387d38f415a6ca5f5c67c1315b204a5d3e9d7a/propcache-0.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9bec58347a5a6cebf239daba9bda37dffec5b8d2ce004d9fe4edef3d2815137e", size = 201903, upload-time = "2025-06-09T22:53:44.872Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/0a/550ea0f52aac455cb90111c8bab995208443e46d925e51e2f6ebdf869525/propcache-0.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:55ffda449a507e9fbd4aca1a7d9aa6753b07d6166140e5a18d2ac9bc49eac220", size = 213362, upload-time = "2025-06-09T22:53:46.707Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/af/9893b7d878deda9bb69fcf54600b247fba7317761b7db11fede6e0f28bd0/propcache-0.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:64a67fb39229a8a8491dd42f864e5e263155e729c2e7ff723d6e25f596b1e8cb", size = 210525, upload-time = "2025-06-09T22:53:48.547Z" },
+ { url = "https://files.pythonhosted.org/packages/7c/bb/38fd08b278ca85cde36d848091ad2b45954bc5f15cce494bb300b9285831/propcache-0.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9da1cf97b92b51253d5b68cf5a2b9e0dafca095e36b7f2da335e27dc6172a614", size = 198283, upload-time = "2025-06-09T22:53:50.067Z" },
+ { url = "https://files.pythonhosted.org/packages/78/8c/9fe55bd01d362bafb413dfe508c48753111a1e269737fa143ba85693592c/propcache-0.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5f559e127134b07425134b4065be45b166183fdcb433cb6c24c8e4149056ad50", size = 191872, upload-time = "2025-06-09T22:53:51.438Z" },
+ { url = "https://files.pythonhosted.org/packages/54/14/4701c33852937a22584e08abb531d654c8bcf7948a8f87ad0a4822394147/propcache-0.3.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:aff2e4e06435d61f11a428360a932138d0ec288b0a31dd9bd78d200bd4a2b339", size = 199452, upload-time = "2025-06-09T22:53:53.229Z" },
+ { url = "https://files.pythonhosted.org/packages/16/44/447f2253d859602095356007657ee535e0093215ea0b3d1d6a41d16e5201/propcache-0.3.2-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:4927842833830942a5d0a56e6f4839bc484785b8e1ce8d287359794818633ba0", size = 191567, upload-time = "2025-06-09T22:53:54.541Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/b3/e4756258749bb2d3b46defcff606a2f47410bab82be5824a67e84015b267/propcache-0.3.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:6107ddd08b02654a30fb8ad7a132021759d750a82578b94cd55ee2772b6ebea2", size = 193015, upload-time = "2025-06-09T22:53:56.44Z" },
+ { url = "https://files.pythonhosted.org/packages/1e/df/e6d3c7574233164b6330b9fd697beeac402afd367280e6dc377bb99b43d9/propcache-0.3.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:70bd8b9cd6b519e12859c99f3fc9a93f375ebd22a50296c3a295028bea73b9e7", size = 204660, upload-time = "2025-06-09T22:53:57.839Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/53/e4d31dd5170b4a0e2e6b730f2385a96410633b4833dc25fe5dffd1f73294/propcache-0.3.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2183111651d710d3097338dd1893fcf09c9f54e27ff1a8795495a16a469cc90b", size = 206105, upload-time = "2025-06-09T22:53:59.638Z" },
+ { url = "https://files.pythonhosted.org/packages/7f/fe/74d54cf9fbe2a20ff786e5f7afcfde446588f0cf15fb2daacfbc267b866c/propcache-0.3.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:fb075ad271405dcad8e2a7ffc9a750a3bf70e533bd86e89f0603e607b93aa64c", size = 196980, upload-time = "2025-06-09T22:54:01.071Z" },
+ { url = "https://files.pythonhosted.org/packages/22/ec/c469c9d59dada8a7679625e0440b544fe72e99311a4679c279562051f6fc/propcache-0.3.2-cp310-cp310-win32.whl", hash = "sha256:404d70768080d3d3bdb41d0771037da19d8340d50b08e104ca0e7f9ce55fce70", size = 37679, upload-time = "2025-06-09T22:54:03.003Z" },
+ { url = "https://files.pythonhosted.org/packages/38/35/07a471371ac89d418f8d0b699c75ea6dca2041fbda360823de21f6a9ce0a/propcache-0.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:7435d766f978b4ede777002e6b3b6641dd229cd1da8d3d3106a45770365f9ad9", size = 41459, upload-time = "2025-06-09T22:54:04.134Z" },
+ { url = "https://files.pythonhosted.org/packages/80/8d/e8b436717ab9c2cfc23b116d2c297305aa4cd8339172a456d61ebf5669b8/propcache-0.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0b8d2f607bd8f80ddc04088bc2a037fdd17884a6fcadc47a96e334d72f3717be", size = 74207, upload-time = "2025-06-09T22:54:05.399Z" },
+ { url = "https://files.pythonhosted.org/packages/d6/29/1e34000e9766d112171764b9fa3226fa0153ab565d0c242c70e9945318a7/propcache-0.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:06766d8f34733416e2e34f46fea488ad5d60726bb9481d3cddf89a6fa2d9603f", size = 43648, upload-time = "2025-06-09T22:54:08.023Z" },
+ { url = "https://files.pythonhosted.org/packages/46/92/1ad5af0df781e76988897da39b5f086c2bf0f028b7f9bd1f409bb05b6874/propcache-0.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a2dc1f4a1df4fecf4e6f68013575ff4af84ef6f478fe5344317a65d38a8e6dc9", size = 43496, upload-time = "2025-06-09T22:54:09.228Z" },
+ { url = "https://files.pythonhosted.org/packages/b3/ce/e96392460f9fb68461fabab3e095cb00c8ddf901205be4eae5ce246e5b7e/propcache-0.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be29c4f4810c5789cf10ddf6af80b041c724e629fa51e308a7a0fb19ed1ef7bf", size = 217288, upload-time = "2025-06-09T22:54:10.466Z" },
+ { url = "https://files.pythonhosted.org/packages/c5/2a/866726ea345299f7ceefc861a5e782b045545ae6940851930a6adaf1fca6/propcache-0.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59d61f6970ecbd8ff2e9360304d5c8876a6abd4530cb752c06586849ac8a9dc9", size = 227456, upload-time = "2025-06-09T22:54:11.828Z" },
+ { url = "https://files.pythonhosted.org/packages/de/03/07d992ccb6d930398689187e1b3c718339a1c06b8b145a8d9650e4726166/propcache-0.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:62180e0b8dbb6b004baec00a7983e4cc52f5ada9cd11f48c3528d8cfa7b96a66", size = 225429, upload-time = "2025-06-09T22:54:13.823Z" },
+ { url = "https://files.pythonhosted.org/packages/5d/e6/116ba39448753b1330f48ab8ba927dcd6cf0baea8a0ccbc512dfb49ba670/propcache-0.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c144ca294a204c470f18cf4c9d78887810d04a3e2fbb30eea903575a779159df", size = 213472, upload-time = "2025-06-09T22:54:15.232Z" },
+ { url = "https://files.pythonhosted.org/packages/a6/85/f01f5d97e54e428885a5497ccf7f54404cbb4f906688a1690cd51bf597dc/propcache-0.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c5c2a784234c28854878d68978265617aa6dc0780e53d44b4d67f3651a17a9a2", size = 204480, upload-time = "2025-06-09T22:54:17.104Z" },
+ { url = "https://files.pythonhosted.org/packages/e3/79/7bf5ab9033b8b8194cc3f7cf1aaa0e9c3256320726f64a3e1f113a812dce/propcache-0.3.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5745bc7acdafa978ca1642891b82c19238eadc78ba2aaa293c6863b304e552d7", size = 214530, upload-time = "2025-06-09T22:54:18.512Z" },
+ { url = "https://files.pythonhosted.org/packages/31/0b/bd3e0c00509b609317df4a18e6b05a450ef2d9a963e1d8bc9c9415d86f30/propcache-0.3.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:c0075bf773d66fa8c9d41f66cc132ecc75e5bb9dd7cce3cfd14adc5ca184cb95", size = 205230, upload-time = "2025-06-09T22:54:19.947Z" },
+ { url = "https://files.pythonhosted.org/packages/7a/23/fae0ff9b54b0de4e819bbe559508da132d5683c32d84d0dc2ccce3563ed4/propcache-0.3.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5f57aa0847730daceff0497f417c9de353c575d8da3579162cc74ac294c5369e", size = 206754, upload-time = "2025-06-09T22:54:21.716Z" },
+ { url = "https://files.pythonhosted.org/packages/b7/7f/ad6a3c22630aaa5f618b4dc3c3598974a72abb4c18e45a50b3cdd091eb2f/propcache-0.3.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:eef914c014bf72d18efb55619447e0aecd5fb7c2e3fa7441e2e5d6099bddff7e", size = 218430, upload-time = "2025-06-09T22:54:23.17Z" },
+ { url = "https://files.pythonhosted.org/packages/5b/2c/ba4f1c0e8a4b4c75910742f0d333759d441f65a1c7f34683b4a74c0ee015/propcache-0.3.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:2a4092e8549031e82facf3decdbc0883755d5bbcc62d3aea9d9e185549936dcf", size = 223884, upload-time = "2025-06-09T22:54:25.539Z" },
+ { url = "https://files.pythonhosted.org/packages/88/e4/ebe30fc399e98572019eee82ad0caf512401661985cbd3da5e3140ffa1b0/propcache-0.3.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:85871b050f174bc0bfb437efbdb68aaf860611953ed12418e4361bc9c392749e", size = 211480, upload-time = "2025-06-09T22:54:26.892Z" },
+ { url = "https://files.pythonhosted.org/packages/96/0a/7d5260b914e01d1d0906f7f38af101f8d8ed0dc47426219eeaf05e8ea7c2/propcache-0.3.2-cp311-cp311-win32.whl", hash = "sha256:36c8d9b673ec57900c3554264e630d45980fd302458e4ac801802a7fd2ef7897", size = 37757, upload-time = "2025-06-09T22:54:28.241Z" },
+ { url = "https://files.pythonhosted.org/packages/e1/2d/89fe4489a884bc0da0c3278c552bd4ffe06a1ace559db5ef02ef24ab446b/propcache-0.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:e53af8cb6a781b02d2ea079b5b853ba9430fcbe18a8e3ce647d5982a3ff69f39", size = 41500, upload-time = "2025-06-09T22:54:29.4Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/42/9ca01b0a6f48e81615dca4765a8f1dd2c057e0540f6116a27dc5ee01dfb6/propcache-0.3.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8de106b6c84506b31c27168582cd3cb3000a6412c16df14a8628e5871ff83c10", size = 73674, upload-time = "2025-06-09T22:54:30.551Z" },
+ { url = "https://files.pythonhosted.org/packages/af/6e/21293133beb550f9c901bbece755d582bfaf2176bee4774000bd4dd41884/propcache-0.3.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:28710b0d3975117239c76600ea351934ac7b5ff56e60953474342608dbbb6154", size = 43570, upload-time = "2025-06-09T22:54:32.296Z" },
+ { url = "https://files.pythonhosted.org/packages/0c/c8/0393a0a3a2b8760eb3bde3c147f62b20044f0ddac81e9d6ed7318ec0d852/propcache-0.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce26862344bdf836650ed2487c3d724b00fbfec4233a1013f597b78c1cb73615", size = 43094, upload-time = "2025-06-09T22:54:33.929Z" },
+ { url = "https://files.pythonhosted.org/packages/37/2c/489afe311a690399d04a3e03b069225670c1d489eb7b044a566511c1c498/propcache-0.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bca54bd347a253af2cf4544bbec232ab982f4868de0dd684246b67a51bc6b1db", size = 226958, upload-time = "2025-06-09T22:54:35.186Z" },
+ { url = "https://files.pythonhosted.org/packages/9d/ca/63b520d2f3d418c968bf596839ae26cf7f87bead026b6192d4da6a08c467/propcache-0.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:55780d5e9a2ddc59711d727226bb1ba83a22dd32f64ee15594b9392b1f544eb1", size = 234894, upload-time = "2025-06-09T22:54:36.708Z" },
+ { url = "https://files.pythonhosted.org/packages/11/60/1d0ed6fff455a028d678df30cc28dcee7af77fa2b0e6962ce1df95c9a2a9/propcache-0.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:035e631be25d6975ed87ab23153db6a73426a48db688070d925aa27e996fe93c", size = 233672, upload-time = "2025-06-09T22:54:38.062Z" },
+ { url = "https://files.pythonhosted.org/packages/37/7c/54fd5301ef38505ab235d98827207176a5c9b2aa61939b10a460ca53e123/propcache-0.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee6f22b6eaa39297c751d0e80c0d3a454f112f5c6481214fcf4c092074cecd67", size = 224395, upload-time = "2025-06-09T22:54:39.634Z" },
+ { url = "https://files.pythonhosted.org/packages/ee/1a/89a40e0846f5de05fdc6779883bf46ba980e6df4d2ff8fb02643de126592/propcache-0.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7ca3aee1aa955438c4dba34fc20a9f390e4c79967257d830f137bd5a8a32ed3b", size = 212510, upload-time = "2025-06-09T22:54:41.565Z" },
+ { url = "https://files.pythonhosted.org/packages/5e/33/ca98368586c9566a6b8d5ef66e30484f8da84c0aac3f2d9aec6d31a11bd5/propcache-0.3.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7a4f30862869fa2b68380d677cc1c5fcf1e0f2b9ea0cf665812895c75d0ca3b8", size = 222949, upload-time = "2025-06-09T22:54:43.038Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/11/ace870d0aafe443b33b2f0b7efdb872b7c3abd505bfb4890716ad7865e9d/propcache-0.3.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:b77ec3c257d7816d9f3700013639db7491a434644c906a2578a11daf13176251", size = 217258, upload-time = "2025-06-09T22:54:44.376Z" },
+ { url = "https://files.pythonhosted.org/packages/5b/d2/86fd6f7adffcfc74b42c10a6b7db721d1d9ca1055c45d39a1a8f2a740a21/propcache-0.3.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:cab90ac9d3f14b2d5050928483d3d3b8fb6b4018893fc75710e6aa361ecb2474", size = 213036, upload-time = "2025-06-09T22:54:46.243Z" },
+ { url = "https://files.pythonhosted.org/packages/07/94/2d7d1e328f45ff34a0a284cf5a2847013701e24c2a53117e7c280a4316b3/propcache-0.3.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:0b504d29f3c47cf6b9e936c1852246c83d450e8e063d50562115a6be6d3a2535", size = 227684, upload-time = "2025-06-09T22:54:47.63Z" },
+ { url = "https://files.pythonhosted.org/packages/b7/05/37ae63a0087677e90b1d14710e532ff104d44bc1efa3b3970fff99b891dc/propcache-0.3.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:ce2ac2675a6aa41ddb2a0c9cbff53780a617ac3d43e620f8fd77ba1c84dcfc06", size = 234562, upload-time = "2025-06-09T22:54:48.982Z" },
+ { url = "https://files.pythonhosted.org/packages/a4/7c/3f539fcae630408d0bd8bf3208b9a647ccad10976eda62402a80adf8fc34/propcache-0.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:62b4239611205294cc433845b914131b2a1f03500ff3c1ed093ed216b82621e1", size = 222142, upload-time = "2025-06-09T22:54:50.424Z" },
+ { url = "https://files.pythonhosted.org/packages/7c/d2/34b9eac8c35f79f8a962546b3e97e9d4b990c420ee66ac8255d5d9611648/propcache-0.3.2-cp312-cp312-win32.whl", hash = "sha256:df4a81b9b53449ebc90cc4deefb052c1dd934ba85012aa912c7ea7b7e38b60c1", size = 37711, upload-time = "2025-06-09T22:54:52.072Z" },
+ { url = "https://files.pythonhosted.org/packages/19/61/d582be5d226cf79071681d1b46b848d6cb03d7b70af7063e33a2787eaa03/propcache-0.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:7046e79b989d7fe457bb755844019e10f693752d169076138abf17f31380800c", size = 41479, upload-time = "2025-06-09T22:54:53.234Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/d1/8c747fafa558c603c4ca19d8e20b288aa0c7cda74e9402f50f31eb65267e/propcache-0.3.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ca592ed634a73ca002967458187109265e980422116c0a107cf93d81f95af945", size = 71286, upload-time = "2025-06-09T22:54:54.369Z" },
+ { url = "https://files.pythonhosted.org/packages/61/99/d606cb7986b60d89c36de8a85d58764323b3a5ff07770a99d8e993b3fa73/propcache-0.3.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9ecb0aad4020e275652ba3975740f241bd12a61f1a784df044cf7477a02bc252", size = 42425, upload-time = "2025-06-09T22:54:55.642Z" },
+ { url = "https://files.pythonhosted.org/packages/8c/96/ef98f91bbb42b79e9bb82bdd348b255eb9d65f14dbbe3b1594644c4073f7/propcache-0.3.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7f08f1cc28bd2eade7a8a3d2954ccc673bb02062e3e7da09bc75d843386b342f", size = 41846, upload-time = "2025-06-09T22:54:57.246Z" },
+ { url = "https://files.pythonhosted.org/packages/5b/ad/3f0f9a705fb630d175146cd7b1d2bf5555c9beaed54e94132b21aac098a6/propcache-0.3.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1a342c834734edb4be5ecb1e9fb48cb64b1e2320fccbd8c54bf8da8f2a84c33", size = 208871, upload-time = "2025-06-09T22:54:58.975Z" },
+ { url = "https://files.pythonhosted.org/packages/3a/38/2085cda93d2c8b6ec3e92af2c89489a36a5886b712a34ab25de9fbca7992/propcache-0.3.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8a544caaae1ac73f1fecfae70ded3e93728831affebd017d53449e3ac052ac1e", size = 215720, upload-time = "2025-06-09T22:55:00.471Z" },
+ { url = "https://files.pythonhosted.org/packages/61/c1/d72ea2dc83ac7f2c8e182786ab0fc2c7bd123a1ff9b7975bee671866fe5f/propcache-0.3.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:310d11aa44635298397db47a3ebce7db99a4cc4b9bbdfcf6c98a60c8d5261cf1", size = 215203, upload-time = "2025-06-09T22:55:01.834Z" },
+ { url = "https://files.pythonhosted.org/packages/af/81/b324c44ae60c56ef12007105f1460d5c304b0626ab0cc6b07c8f2a9aa0b8/propcache-0.3.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c1396592321ac83157ac03a2023aa6cc4a3cc3cfdecb71090054c09e5a7cce3", size = 206365, upload-time = "2025-06-09T22:55:03.199Z" },
+ { url = "https://files.pythonhosted.org/packages/09/73/88549128bb89e66d2aff242488f62869014ae092db63ccea53c1cc75a81d/propcache-0.3.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8cabf5b5902272565e78197edb682017d21cf3b550ba0460ee473753f28d23c1", size = 196016, upload-time = "2025-06-09T22:55:04.518Z" },
+ { url = "https://files.pythonhosted.org/packages/b9/3f/3bdd14e737d145114a5eb83cb172903afba7242f67c5877f9909a20d948d/propcache-0.3.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0a2f2235ac46a7aa25bdeb03a9e7060f6ecbd213b1f9101c43b3090ffb971ef6", size = 205596, upload-time = "2025-06-09T22:55:05.942Z" },
+ { url = "https://files.pythonhosted.org/packages/0f/ca/2f4aa819c357d3107c3763d7ef42c03980f9ed5c48c82e01e25945d437c1/propcache-0.3.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:92b69e12e34869a6970fd2f3da91669899994b47c98f5d430b781c26f1d9f387", size = 200977, upload-time = "2025-06-09T22:55:07.792Z" },
+ { url = "https://files.pythonhosted.org/packages/cd/4a/e65276c7477533c59085251ae88505caf6831c0e85ff8b2e31ebcbb949b1/propcache-0.3.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:54e02207c79968ebbdffc169591009f4474dde3b4679e16634d34c9363ff56b4", size = 197220, upload-time = "2025-06-09T22:55:09.173Z" },
+ { url = "https://files.pythonhosted.org/packages/7c/54/fc7152e517cf5578278b242396ce4d4b36795423988ef39bb8cd5bf274c8/propcache-0.3.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4adfb44cb588001f68c5466579d3f1157ca07f7504fc91ec87862e2b8e556b88", size = 210642, upload-time = "2025-06-09T22:55:10.62Z" },
+ { url = "https://files.pythonhosted.org/packages/b9/80/abeb4a896d2767bf5f1ea7b92eb7be6a5330645bd7fb844049c0e4045d9d/propcache-0.3.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:fd3e6019dc1261cd0291ee8919dd91fbab7b169bb76aeef6c716833a3f65d206", size = 212789, upload-time = "2025-06-09T22:55:12.029Z" },
+ { url = "https://files.pythonhosted.org/packages/b3/db/ea12a49aa7b2b6d68a5da8293dcf50068d48d088100ac016ad92a6a780e6/propcache-0.3.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4c181cad81158d71c41a2bce88edce078458e2dd5ffee7eddd6b05da85079f43", size = 205880, upload-time = "2025-06-09T22:55:13.45Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/e5/9076a0bbbfb65d1198007059c65639dfd56266cf8e477a9707e4b1999ff4/propcache-0.3.2-cp313-cp313-win32.whl", hash = "sha256:8a08154613f2249519e549de2330cf8e2071c2887309a7b07fb56098f5170a02", size = 37220, upload-time = "2025-06-09T22:55:15.284Z" },
+ { url = "https://files.pythonhosted.org/packages/d3/f5/b369e026b09a26cd77aa88d8fffd69141d2ae00a2abaaf5380d2603f4b7f/propcache-0.3.2-cp313-cp313-win_amd64.whl", hash = "sha256:e41671f1594fc4ab0a6dec1351864713cb3a279910ae8b58f884a88a0a632c05", size = 40678, upload-time = "2025-06-09T22:55:16.445Z" },
+ { url = "https://files.pythonhosted.org/packages/a4/3a/6ece377b55544941a08d03581c7bc400a3c8cd3c2865900a68d5de79e21f/propcache-0.3.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:9a3cf035bbaf035f109987d9d55dc90e4b0e36e04bbbb95af3055ef17194057b", size = 76560, upload-time = "2025-06-09T22:55:17.598Z" },
+ { url = "https://files.pythonhosted.org/packages/0c/da/64a2bb16418740fa634b0e9c3d29edff1db07f56d3546ca2d86ddf0305e1/propcache-0.3.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:156c03d07dc1323d8dacaa221fbe028c5c70d16709cdd63502778e6c3ccca1b0", size = 44676, upload-time = "2025-06-09T22:55:18.922Z" },
+ { url = "https://files.pythonhosted.org/packages/36/7b/f025e06ea51cb72c52fb87e9b395cced02786610b60a3ed51da8af017170/propcache-0.3.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:74413c0ba02ba86f55cf60d18daab219f7e531620c15f1e23d95563f505efe7e", size = 44701, upload-time = "2025-06-09T22:55:20.106Z" },
+ { url = "https://files.pythonhosted.org/packages/a4/00/faa1b1b7c3b74fc277f8642f32a4c72ba1d7b2de36d7cdfb676db7f4303e/propcache-0.3.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f066b437bb3fa39c58ff97ab2ca351db465157d68ed0440abecb21715eb24b28", size = 276934, upload-time = "2025-06-09T22:55:21.5Z" },
+ { url = "https://files.pythonhosted.org/packages/74/ab/935beb6f1756e0476a4d5938ff44bf0d13a055fed880caf93859b4f1baf4/propcache-0.3.2-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f1304b085c83067914721e7e9d9917d41ad87696bf70f0bc7dee450e9c71ad0a", size = 278316, upload-time = "2025-06-09T22:55:22.918Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/9d/994a5c1ce4389610838d1caec74bdf0e98b306c70314d46dbe4fcf21a3e2/propcache-0.3.2-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ab50cef01b372763a13333b4e54021bdcb291fc9a8e2ccb9c2df98be51bcde6c", size = 282619, upload-time = "2025-06-09T22:55:24.651Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/00/a10afce3d1ed0287cef2e09506d3be9822513f2c1e96457ee369adb9a6cd/propcache-0.3.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fad3b2a085ec259ad2c2842666b2a0a49dea8463579c606426128925af1ed725", size = 265896, upload-time = "2025-06-09T22:55:26.049Z" },
+ { url = "https://files.pythonhosted.org/packages/2e/a8/2aa6716ffa566ca57c749edb909ad27884680887d68517e4be41b02299f3/propcache-0.3.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:261fa020c1c14deafd54c76b014956e2f86991af198c51139faf41c4d5e83892", size = 252111, upload-time = "2025-06-09T22:55:27.381Z" },
+ { url = "https://files.pythonhosted.org/packages/36/4f/345ca9183b85ac29c8694b0941f7484bf419c7f0fea2d1e386b4f7893eed/propcache-0.3.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:46d7f8aa79c927e5f987ee3a80205c987717d3659f035c85cf0c3680526bdb44", size = 268334, upload-time = "2025-06-09T22:55:28.747Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/ca/fcd54f78b59e3f97b3b9715501e3147f5340167733d27db423aa321e7148/propcache-0.3.2-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:6d8f3f0eebf73e3c0ff0e7853f68be638b4043c65a70517bb575eff54edd8dbe", size = 255026, upload-time = "2025-06-09T22:55:30.184Z" },
+ { url = "https://files.pythonhosted.org/packages/8b/95/8e6a6bbbd78ac89c30c225210a5c687790e532ba4088afb8c0445b77ef37/propcache-0.3.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:03c89c1b14a5452cf15403e291c0ccd7751d5b9736ecb2c5bab977ad6c5bcd81", size = 250724, upload-time = "2025-06-09T22:55:31.646Z" },
+ { url = "https://files.pythonhosted.org/packages/ee/b0/0dd03616142baba28e8b2d14ce5df6631b4673850a3d4f9c0f9dd714a404/propcache-0.3.2-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:0cc17efde71e12bbaad086d679ce575268d70bc123a5a71ea7ad76f70ba30bba", size = 268868, upload-time = "2025-06-09T22:55:33.209Z" },
+ { url = "https://files.pythonhosted.org/packages/c5/98/2c12407a7e4fbacd94ddd32f3b1e3d5231e77c30ef7162b12a60e2dd5ce3/propcache-0.3.2-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:acdf05d00696bc0447e278bb53cb04ca72354e562cf88ea6f9107df8e7fd9770", size = 271322, upload-time = "2025-06-09T22:55:35.065Z" },
+ { url = "https://files.pythonhosted.org/packages/35/91/9cb56efbb428b006bb85db28591e40b7736847b8331d43fe335acf95f6c8/propcache-0.3.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4445542398bd0b5d32df908031cb1b30d43ac848e20470a878b770ec2dcc6330", size = 265778, upload-time = "2025-06-09T22:55:36.45Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/4c/b0fe775a2bdd01e176b14b574be679d84fc83958335790f7c9a686c1f468/propcache-0.3.2-cp313-cp313t-win32.whl", hash = "sha256:f86e5d7cd03afb3a1db8e9f9f6eff15794e79e791350ac48a8c924e6f439f394", size = 41175, upload-time = "2025-06-09T22:55:38.436Z" },
+ { url = "https://files.pythonhosted.org/packages/a4/ff/47f08595e3d9b5e149c150f88d9714574f1a7cbd89fe2817158a952674bf/propcache-0.3.2-cp313-cp313t-win_amd64.whl", hash = "sha256:9704bedf6e7cbe3c65eca4379a9b53ee6a83749f047808cbb5044d40d7d72198", size = 44857, upload-time = "2025-06-09T22:55:39.687Z" },
+ { url = "https://files.pythonhosted.org/packages/cc/35/cc0aaecf278bb4575b8555f2b137de5ab821595ddae9da9d3cd1da4072c7/propcache-0.3.2-py3-none-any.whl", hash = "sha256:98f1ec44fb675f5052cccc8e609c46ed23a35a1cfd18545ad4e29002d858a43f", size = 12663, upload-time = "2025-06-09T22:56:04.484Z" },
]
[[package]]
-name = "streamlit"
-version = "1.41.1"
+name = "psycopg"
+version = "3.2.9"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "altair" },
- { name = "blinker" },
- { name = "cachetools" },
- { name = "click", version = "8.1.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
- { name = "click", version = "8.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
- { name = "gitpython" },
- { name = "numpy" },
- { name = "packaging" },
- { name = "pandas" },
- { name = "pillow" },
- { name = "protobuf" },
- { name = "pyarrow" },
- { name = "pydeck" },
- { name = "requests" },
- { name = "rich" },
- { name = "tenacity" },
- { name = "toml" },
- { name = "tornado" },
- { name = "typing-extensions" },
- { name = "watchdog", marker = "sys_platform != 'darwin'" },
+ { name = "typing-extensions", marker = "python_full_version < '3.13'" },
+ { name = "tzdata", marker = "sys_platform == 'win32'" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/78/33/14b5ac0369ecf0af675911e5e84b934e6fcc2cec850857d2390eb373b0a6/streamlit-1.41.1.tar.gz", hash = "sha256:6626d32b098ba1458b71eebdd634c62af2dd876380e59c4b6a1e828a39d62d69", size = 8712473, upload-time = "2024-12-13T21:22:15.849Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/27/4a/93a6ab570a8d1a4ad171a1f4256e205ce48d828781312c0bbaff36380ecb/psycopg-3.2.9.tar.gz", hash = "sha256:2fbb46fcd17bc81f993f28c47f1ebea38d66ae97cc2dbc3cad73b37cefbff700", size = 158122, upload-time = "2025-05-13T16:11:15.533Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/c2/87/b2e162869500062a94dde7589c167367b5538dab6eacce2e7c0f00d5c9c5/streamlit-1.41.1-py2.py3-none-any.whl", hash = "sha256:0def00822480071d642e6df36cd63c089f991da3a69fd9eb4ab8f65ce27de4e0", size = 9100386, upload-time = "2024-12-13T21:22:11.1Z" },
+ { url = "https://files.pythonhosted.org/packages/44/b0/a73c195a56eb6b92e937a5ca58521a5c3346fb233345adc80fd3e2f542e2/psycopg-3.2.9-py3-none-any.whl", hash = "sha256:01a8dadccdaac2123c916208c96e06631641c0566b22005493f09663c7a8d3b6", size = 202705, upload-time = "2025-05-13T16:06:26.584Z" },
]
-[[package]]
-name = "sympy"
-version = "1.14.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "mpmath" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/83/d3/803453b36afefb7c2bb238361cd4ae6125a569b4db67cd9e79846ba2d68c/sympy-1.14.0.tar.gz", hash = "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517", size = 7793921, upload-time = "2025-04-27T18:05:01.611Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353, upload-time = "2025-04-27T18:04:59.103Z" },
+[package.optional-dependencies]
+binary = [
+ { name = "psycopg-binary", marker = "implementation_name != 'pypy'" },
]
[[package]]
-name = "tabulate"
-version = "0.9.0"
+name = "psycopg-binary"
+version = "3.2.9"
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, upload-time = "2022-10-06T17:21:48.54Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/40/44/4a5f08c96eb108af5cb50b41f76142f0afa346dfa99d5296fe7202a11854/tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f", size = 35252, upload-time = "2022-10-06T17:21:44.262Z" },
+ { url = "https://files.pythonhosted.org/packages/b6/ce/d677bc51f9b180986e5515268603519cee682eb6b5e765ae46cdb8526579/psycopg_binary-3.2.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:528239bbf55728ba0eacbd20632342867590273a9bacedac7538ebff890f1093", size = 4033081, upload-time = "2025-05-13T16:06:29.666Z" },
+ { url = "https://files.pythonhosted.org/packages/de/f4/b56263eb20dc36d71d7188622872098400536928edf86895736e28546b3c/psycopg_binary-3.2.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e4978c01ca4c208c9d6376bd585e2c0771986b76ff7ea518f6d2b51faece75e8", size = 4082141, upload-time = "2025-05-13T16:06:33.81Z" },
+ { url = "https://files.pythonhosted.org/packages/68/47/5316c3b0a2b1ff5f1d440a27638250569994534874a2ce88bf24f5c51c0f/psycopg_binary-3.2.9-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ed2bab85b505d13e66a914d0f8cdfa9475c16d3491cf81394e0748b77729af2", size = 4678993, upload-time = "2025-05-13T16:06:36.309Z" },
+ { url = "https://files.pythonhosted.org/packages/53/24/b2c667b59f07fd7d7805c0c2074351bf2b98a336c5030d961db316512ffb/psycopg_binary-3.2.9-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:799fa1179ab8a58d1557a95df28b492874c8f4135101b55133ec9c55fc9ae9d7", size = 4500117, upload-time = "2025-05-13T16:06:38.847Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/91/a08f8878b0fe0b34b083c149df950bce168bc1b18b2fe849fa42bf4378d4/psycopg_binary-3.2.9-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bb37ac3955d19e4996c3534abfa4f23181333974963826db9e0f00731274b695", size = 4766985, upload-time = "2025-05-13T16:06:42.502Z" },
+ { url = "https://files.pythonhosted.org/packages/10/be/3a45d5b7d8f4c4332fd42465f2170b5aef4d28a7c79e79ac7e5e1dac74d7/psycopg_binary-3.2.9-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:001e986656f7e06c273dd4104e27f4b4e0614092e544d950c7c938d822b1a894", size = 4461990, upload-time = "2025-05-13T16:06:45.971Z" },
+ { url = "https://files.pythonhosted.org/packages/03/ce/20682b9a4fc270d8dc644a0b16c1978732146c6ff0abbc48fbab2f4a70aa/psycopg_binary-3.2.9-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:fa5c80d8b4cbf23f338db88a7251cef8bb4b68e0f91cf8b6ddfa93884fdbb0c1", size = 3777947, upload-time = "2025-05-13T16:06:49.134Z" },
+ { url = "https://files.pythonhosted.org/packages/07/5c/f6d486e00bcd8709908ccdd436b2a190d390dfd61e318de4060bc6ee2a1e/psycopg_binary-3.2.9-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:39a127e0cf9b55bd4734a8008adf3e01d1fd1cb36339c6a9e2b2cbb6007c50ee", size = 3337502, upload-time = "2025-05-13T16:06:51.378Z" },
+ { url = "https://files.pythonhosted.org/packages/0b/a1/086508e929c0123a7f532840bb0a0c8a1ebd7e06aef3ee7fa44a3589bcdf/psycopg_binary-3.2.9-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fb7599e436b586e265bea956751453ad32eb98be6a6e694252f4691c31b16edb", size = 3440809, upload-time = "2025-05-13T16:06:54.552Z" },
+ { url = "https://files.pythonhosted.org/packages/40/f2/3a347a0f894355a6b173fca2202eca279b6197727b24e4896cf83f4263ee/psycopg_binary-3.2.9-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5d2c9fe14fe42b3575a0b4e09b081713e83b762c8dc38a3771dd3265f8f110e7", size = 3497231, upload-time = "2025-05-13T16:06:58.858Z" },
+ { url = "https://files.pythonhosted.org/packages/18/31/0845a385eb6f4521b398793293b5f746a101e80d5c43792990442d26bc2e/psycopg_binary-3.2.9-cp310-cp310-win_amd64.whl", hash = "sha256:7e4660fad2807612bb200de7262c88773c3483e85d981324b3c647176e41fdc8", size = 2936845, upload-time = "2025-05-13T16:07:02.712Z" },
+ { url = "https://files.pythonhosted.org/packages/b6/84/259ea58aca48e03c3c793b4ccfe39ed63db7b8081ef784d039330d9eed96/psycopg_binary-3.2.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2504e9fd94eabe545d20cddcc2ff0da86ee55d76329e1ab92ecfcc6c0a8156c4", size = 4040785, upload-time = "2025-05-13T16:07:07.569Z" },
+ { url = "https://files.pythonhosted.org/packages/25/22/ce58ffda2b7e36e45042b4d67f1bbd4dd2ccf4cfd2649696685c61046475/psycopg_binary-3.2.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:093a0c079dd6228a7f3c3d82b906b41964eaa062a9a8c19f45ab4984bf4e872b", size = 4087601, upload-time = "2025-05-13T16:07:11.75Z" },
+ { url = "https://files.pythonhosted.org/packages/c6/4f/b043e85268650c245025e80039b79663d8986f857bc3d3a72b1de67f3550/psycopg_binary-3.2.9-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:387c87b51d72442708e7a853e7e7642717e704d59571da2f3b29e748be58c78a", size = 4676524, upload-time = "2025-05-13T16:07:17.038Z" },
+ { url = "https://files.pythonhosted.org/packages/da/29/7afbfbd3740ea52fda488db190ef2ef2a9ff7379b85501a2142fb9f7dd56/psycopg_binary-3.2.9-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d9ac10a2ebe93a102a326415b330fff7512f01a9401406896e78a81d75d6eddc", size = 4495671, upload-time = "2025-05-13T16:07:21.709Z" },
+ { url = "https://files.pythonhosted.org/packages/ea/eb/df69112d18a938cbb74efa1573082248437fa663ba66baf2cdba8a95a2d0/psycopg_binary-3.2.9-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:72fdbda5b4c2a6a72320857ef503a6589f56d46821592d4377c8c8604810342b", size = 4768132, upload-time = "2025-05-13T16:07:25.818Z" },
+ { url = "https://files.pythonhosted.org/packages/76/fe/4803b20220c04f508f50afee9169268553f46d6eed99640a08c8c1e76409/psycopg_binary-3.2.9-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f34e88940833d46108f949fdc1fcfb74d6b5ae076550cd67ab59ef47555dba95", size = 4458394, upload-time = "2025-05-13T16:07:29.148Z" },
+ { url = "https://files.pythonhosted.org/packages/0f/0f/5ecc64607ef6f62b04e610b7837b1a802ca6f7cb7211339f5d166d55f1dd/psycopg_binary-3.2.9-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a3e0f89fe35cb03ff1646ab663dabf496477bab2a072315192dbaa6928862891", size = 3776879, upload-time = "2025-05-13T16:07:32.503Z" },
+ { url = "https://files.pythonhosted.org/packages/c8/d8/1c3d6e99b7db67946d0eac2cd15d10a79aa7b1e3222ce4aa8e7df72027f5/psycopg_binary-3.2.9-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:6afb3e62f2a3456f2180a4eef6b03177788df7ce938036ff7f09b696d418d186", size = 3333329, upload-time = "2025-05-13T16:07:35.555Z" },
+ { url = "https://files.pythonhosted.org/packages/d7/02/a4e82099816559f558ccaf2b6945097973624dc58d5d1c91eb1e54e5a8e9/psycopg_binary-3.2.9-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:cc19ed5c7afca3f6b298bfc35a6baa27adb2019670d15c32d0bb8f780f7d560d", size = 3435683, upload-time = "2025-05-13T16:07:37.863Z" },
+ { url = "https://files.pythonhosted.org/packages/91/e4/f27055290d58e8818bed8a297162a096ef7f8ecdf01d98772d4b02af46c4/psycopg_binary-3.2.9-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bc75f63653ce4ec764c8f8c8b0ad9423e23021e1c34a84eb5f4ecac8538a4a4a", size = 3497124, upload-time = "2025-05-13T16:07:40.567Z" },
+ { url = "https://files.pythonhosted.org/packages/67/3d/17ed07579625529534605eeaeba34f0536754a5667dbf20ea2624fc80614/psycopg_binary-3.2.9-cp311-cp311-win_amd64.whl", hash = "sha256:3db3ba3c470801e94836ad78bf11fd5fab22e71b0c77343a1ee95d693879937a", size = 2939520, upload-time = "2025-05-13T16:07:45.467Z" },
+ { url = "https://files.pythonhosted.org/packages/29/6f/ec9957e37a606cd7564412e03f41f1b3c3637a5be018d0849914cb06e674/psycopg_binary-3.2.9-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:be7d650a434921a6b1ebe3fff324dbc2364393eb29d7672e638ce3e21076974e", size = 4022205, upload-time = "2025-05-13T16:07:48.195Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/ba/497b8bea72b20a862ac95a94386967b745a472d9ddc88bc3f32d5d5f0d43/psycopg_binary-3.2.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6a76b4722a529390683c0304501f238b365a46b1e5fb6b7249dbc0ad6fea51a0", size = 4083795, upload-time = "2025-05-13T16:07:50.917Z" },
+ { url = "https://files.pythonhosted.org/packages/42/07/af9503e8e8bdad3911fd88e10e6a29240f9feaa99f57d6fac4a18b16f5a0/psycopg_binary-3.2.9-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:96a551e4683f1c307cfc3d9a05fec62c00a7264f320c9962a67a543e3ce0d8ff", size = 4655043, upload-time = "2025-05-13T16:07:54.857Z" },
+ { url = "https://files.pythonhosted.org/packages/28/ed/aff8c9850df1648cc6a5cc7a381f11ee78d98a6b807edd4a5ae276ad60ad/psycopg_binary-3.2.9-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:61d0a6ceed8f08c75a395bc28cb648a81cf8dee75ba4650093ad1a24a51c8724", size = 4477972, upload-time = "2025-05-13T16:07:57.925Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/bd/8e9d1b77ec1a632818fe2f457c3a65af83c68710c4c162d6866947d08cc5/psycopg_binary-3.2.9-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad280bbd409bf598683dda82232f5215cfc5f2b1bf0854e409b4d0c44a113b1d", size = 4737516, upload-time = "2025-05-13T16:08:01.616Z" },
+ { url = "https://files.pythonhosted.org/packages/46/ec/222238f774cd5a0881f3f3b18fb86daceae89cc410f91ef6a9fb4556f236/psycopg_binary-3.2.9-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76eddaf7fef1d0994e3d536ad48aa75034663d3a07f6f7e3e601105ae73aeff6", size = 4436160, upload-time = "2025-05-13T16:08:04.278Z" },
+ { url = "https://files.pythonhosted.org/packages/37/78/af5af2a1b296eeca54ea7592cd19284739a844974c9747e516707e7b3b39/psycopg_binary-3.2.9-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:52e239cd66c4158e412318fbe028cd94b0ef21b0707f56dcb4bdc250ee58fd40", size = 3753518, upload-time = "2025-05-13T16:08:07.567Z" },
+ { url = "https://files.pythonhosted.org/packages/ec/ac/8a3ed39ea069402e9e6e6a2f79d81a71879708b31cc3454283314994b1ae/psycopg_binary-3.2.9-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:08bf9d5eabba160dd4f6ad247cf12f229cc19d2458511cab2eb9647f42fa6795", size = 3313598, upload-time = "2025-05-13T16:08:09.999Z" },
+ { url = "https://files.pythonhosted.org/packages/da/43/26549af068347c808fbfe5f07d2fa8cef747cfff7c695136172991d2378b/psycopg_binary-3.2.9-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1b2cf018168cad87580e67bdde38ff5e51511112f1ce6ce9a8336871f465c19a", size = 3407289, upload-time = "2025-05-13T16:08:12.66Z" },
+ { url = "https://files.pythonhosted.org/packages/67/55/ea8d227c77df8e8aec880ded398316735add8fda5eb4ff5cc96fac11e964/psycopg_binary-3.2.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:14f64d1ac6942ff089fc7e926440f7a5ced062e2ed0949d7d2d680dc5c00e2d4", size = 3472493, upload-time = "2025-05-13T16:08:15.672Z" },
+ { url = "https://files.pythonhosted.org/packages/3c/02/6ff2a5bc53c3cd653d281666728e29121149179c73fddefb1e437024c192/psycopg_binary-3.2.9-cp312-cp312-win_amd64.whl", hash = "sha256:7a838852e5afb6b4126f93eb409516a8c02a49b788f4df8b6469a40c2157fa21", size = 2927400, upload-time = "2025-05-13T16:08:18.652Z" },
+ { url = "https://files.pythonhosted.org/packages/28/0b/f61ff4e9f23396aca674ed4d5c9a5b7323738021d5d72d36d8b865b3deaf/psycopg_binary-3.2.9-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:98bbe35b5ad24a782c7bf267596638d78aa0e87abc7837bdac5b2a2ab954179e", size = 4017127, upload-time = "2025-05-13T16:08:21.391Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/00/7e181fb1179fbfc24493738b61efd0453d4b70a0c4b12728e2b82db355fd/psycopg_binary-3.2.9-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:72691a1615ebb42da8b636c5ca9f2b71f266be9e172f66209a361c175b7842c5", size = 4080322, upload-time = "2025-05-13T16:08:24.049Z" },
+ { url = "https://files.pythonhosted.org/packages/58/fd/94fc267c1d1392c4211e54ccb943be96ea4032e761573cf1047951887494/psycopg_binary-3.2.9-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25ab464bfba8c401f5536d5aa95f0ca1dd8257b5202eede04019b4415f491351", size = 4655097, upload-time = "2025-05-13T16:08:27.376Z" },
+ { url = "https://files.pythonhosted.org/packages/41/17/31b3acf43de0b2ba83eac5878ff0dea5a608ca2a5c5dd48067999503a9de/psycopg_binary-3.2.9-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e8aeefebe752f46e3c4b769e53f1d4ad71208fe1150975ef7662c22cca80fab", size = 4482114, upload-time = "2025-05-13T16:08:30.781Z" },
+ { url = "https://files.pythonhosted.org/packages/85/78/b4d75e5fd5a85e17f2beb977abbba3389d11a4536b116205846b0e1cf744/psycopg_binary-3.2.9-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b7e4e4dd177a8665c9ce86bc9caae2ab3aa9360b7ce7ec01827ea1baea9ff748", size = 4737693, upload-time = "2025-05-13T16:08:34.625Z" },
+ { url = "https://files.pythonhosted.org/packages/3b/95/7325a8550e3388b00b5e54f4ced5e7346b531eb4573bf054c3dbbfdc14fe/psycopg_binary-3.2.9-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7fc2915949e5c1ea27a851f7a472a7da7d0a40d679f0a31e42f1022f3c562e87", size = 4437423, upload-time = "2025-05-13T16:08:37.444Z" },
+ { url = "https://files.pythonhosted.org/packages/1a/db/cef77d08e59910d483df4ee6da8af51c03bb597f500f1fe818f0f3b925d3/psycopg_binary-3.2.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a1fa38a4687b14f517f049477178093c39c2a10fdcced21116f47c017516498f", size = 3758667, upload-time = "2025-05-13T16:08:40.116Z" },
+ { url = "https://files.pythonhosted.org/packages/95/3e/252fcbffb47189aa84d723b54682e1bb6d05c8875fa50ce1ada914ae6e28/psycopg_binary-3.2.9-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:5be8292d07a3ab828dc95b5ee6b69ca0a5b2e579a577b39671f4f5b47116dfd2", size = 3320576, upload-time = "2025-05-13T16:08:43.243Z" },
+ { url = "https://files.pythonhosted.org/packages/1c/cd/9b5583936515d085a1bec32b45289ceb53b80d9ce1cea0fef4c782dc41a7/psycopg_binary-3.2.9-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:778588ca9897b6c6bab39b0d3034efff4c5438f5e3bd52fda3914175498202f9", size = 3411439, upload-time = "2025-05-13T16:08:47.321Z" },
+ { url = "https://files.pythonhosted.org/packages/45/6b/6f1164ea1634c87956cdb6db759e0b8c5827f989ee3cdff0f5c70e8331f2/psycopg_binary-3.2.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f0d5b3af045a187aedbd7ed5fc513bd933a97aaff78e61c3745b330792c4345b", size = 3477477, upload-time = "2025-05-13T16:08:51.166Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/1d/bf54cfec79377929da600c16114f0da77a5f1670f45e0c3af9fcd36879bc/psycopg_binary-3.2.9-cp313-cp313-win_amd64.whl", hash = "sha256:2290bc146a1b6a9730350f695e8b670e1d1feb8446597bed0bbe7c3c30e0abcb", size = 2928009, upload-time = "2025-05-13T16:08:53.67Z" },
]
[[package]]
-name = "tenacity"
-version = "9.1.2"
+name = "pycparser"
+version = "2.22"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/0a/d4/2b0cd0fe285e14b36db076e78c93766ff1d529d70408bd1d2a5a84f1d929/tenacity-9.1.2.tar.gz", hash = "sha256:1169d376c297e7de388d18b4481760d478b0e99a777cad3a9c86e556f4b697cb", size = 48036, upload-time = "2025-04-02T08:25:09.966Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/1d/b2/31537cf4b1ca988837256c910a668b553fceb8f069bedc4b1c826024b52c/pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6", size = 172736, upload-time = "2024-03-30T13:22:22.564Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/e5/30/643397144bfbfec6f6ef821f36f33e57d35946c44a2352d3c9f0ae847619/tenacity-9.1.2-py3-none-any.whl", hash = "sha256:f77bf36710d8b73a50b2dd155c97b870017ad21afe6ab300326b0371b3b05138", size = 28248, upload-time = "2025-04-02T08:25:07.678Z" },
+ { url = "https://files.pythonhosted.org/packages/13/a3/a812df4e2dd5696d1f351d58b8fe16a405b234ad2886a0dab9183fb78109/pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc", size = 117552, upload-time = "2024-03-30T13:22:20.476Z" },
]
[[package]]
-name = "threadpoolctl"
-version = "3.6.0"
+name = "pygments"
+version = "2.19.2"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/b7/4d/08c89e34946fce2aec4fbb45c9016efd5f4d7f24af8e5d93296e935631d8/threadpoolctl-3.6.0.tar.gz", hash = "sha256:8ab8b4aa3491d812b623328249fab5302a68d2d71745c8a4c719a2fcaba9f44e", size = 21274, upload-time = "2025-03-13T13:49:23.031Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/32/d5/f9a850d79b0851d1d4ef6456097579a9005b31fea68726a4ae5f2d82ddd9/threadpoolctl-3.6.0-py3-none-any.whl", hash = "sha256:43a0b8fd5a2928500110039e43a5eed8480b918967083ea48dc3ab9f13c4a7fb", size = 18638, upload-time = "2025-03-13T13:49:21.846Z" },
-]
-
-[[package]]
-name = "thrift"
-version = "0.20.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "six" },
+ { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/3c/2d/8946864f716ac82dcc88d290ed613cba7a80ec75df4f553ec3ff275f486e/thrift-0.20.0.tar.gz", hash = "sha256:4dd662eadf6b8aebe8a41729527bd69adf6ceaa2a8681cbef64d1273b3e8feba", size = 62295, upload-time = "2024-03-22T22:53:08.228Z" }
[[package]]
-name = "tiktoken"
-version = "0.11.0"
+name = "pytest"
+version = "8.4.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "regex" },
- { name = "requests" },
+ { name = "colorama", marker = "sys_platform == 'win32'" },
+ { name = "exceptiongroup", marker = "python_full_version < '3.11'" },
+ { name = "iniconfig" },
+ { name = "packaging" },
+ { name = "pluggy" },
+ { name = "pygments" },
+ { name = "tomli", marker = "python_full_version < '3.11'" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/a7/86/ad0155a37c4f310935d5ac0b1ccf9bdb635dcb906e0a9a26b616dd55825a/tiktoken-0.11.0.tar.gz", hash = "sha256:3c518641aee1c52247c2b97e74d8d07d780092af79d5911a6ab5e79359d9b06a", size = 37648, upload-time = "2025-08-08T23:58:08.495Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/08/ba/45911d754e8eba3d5a841a5ce61a65a685ff1798421ac054f85aa8747dfb/pytest-8.4.1.tar.gz", hash = "sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c", size = 1517714, upload-time = "2025-06-18T05:48:06.109Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/8b/4d/c6a2e7dca2b4f2e9e0bfd62b3fe4f114322e2c028cfba905a72bc76ce479/tiktoken-0.11.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:8a9b517d6331d7103f8bef29ef93b3cca95fa766e293147fe7bacddf310d5917", size = 1059937, upload-time = "2025-08-08T23:57:28.57Z" },
- { url = "https://files.pythonhosted.org/packages/41/54/3739d35b9f94cb8dc7b0db2edca7192d5571606aa2369a664fa27e811804/tiktoken-0.11.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b4ddb1849e6bf0afa6cc1c5d809fb980ca240a5fffe585a04e119519758788c0", size = 999230, upload-time = "2025-08-08T23:57:30.241Z" },
- { url = "https://files.pythonhosted.org/packages/dd/f4/ec8d43338d28d53513004ebf4cd83732a135d11011433c58bf045890cc10/tiktoken-0.11.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:10331d08b5ecf7a780b4fe4d0281328b23ab22cdb4ff65e68d56caeda9940ecc", size = 1130076, upload-time = "2025-08-08T23:57:31.706Z" },
- { url = "https://files.pythonhosted.org/packages/94/80/fb0ada0a882cb453caf519a4bf0d117c2a3ee2e852c88775abff5413c176/tiktoken-0.11.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b062c82300341dc87e0258c69f79bed725f87e753c21887aea90d272816be882", size = 1183942, upload-time = "2025-08-08T23:57:33.142Z" },
- { url = "https://files.pythonhosted.org/packages/2f/e9/6c104355b463601719582823f3ea658bc3aa7c73d1b3b7553ebdc48468ce/tiktoken-0.11.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:195d84bec46169af3b1349a1495c151d37a0ff4cba73fd08282736be7f92cc6c", size = 1244705, upload-time = "2025-08-08T23:57:34.594Z" },
- { url = "https://files.pythonhosted.org/packages/94/75/eaa6068f47e8b3f0aab9e05177cce2cf5aa2cc0ca93981792e620d4d4117/tiktoken-0.11.0-cp310-cp310-win_amd64.whl", hash = "sha256:fe91581b0ecdd8783ce8cb6e3178f2260a3912e8724d2f2d49552b98714641a1", size = 884152, upload-time = "2025-08-08T23:57:36.18Z" },
- { url = "https://files.pythonhosted.org/packages/8a/91/912b459799a025d2842566fe1e902f7f50d54a1ce8a0f236ab36b5bd5846/tiktoken-0.11.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4ae374c46afadad0f501046db3da1b36cd4dfbfa52af23c998773682446097cf", size = 1059743, upload-time = "2025-08-08T23:57:37.516Z" },
- { url = "https://files.pythonhosted.org/packages/8c/e9/6faa6870489ce64f5f75dcf91512bf35af5864583aee8fcb0dcb593121f5/tiktoken-0.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:25a512ff25dc6c85b58f5dd4f3d8c674dc05f96b02d66cdacf628d26a4e4866b", size = 999334, upload-time = "2025-08-08T23:57:38.595Z" },
- { url = "https://files.pythonhosted.org/packages/a1/3e/a05d1547cf7db9dc75d1461cfa7b556a3b48e0516ec29dfc81d984a145f6/tiktoken-0.11.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2130127471e293d385179c1f3f9cd445070c0772be73cdafb7cec9a3684c0458", size = 1129402, upload-time = "2025-08-08T23:57:39.627Z" },
- { url = "https://files.pythonhosted.org/packages/34/9a/db7a86b829e05a01fd4daa492086f708e0a8b53952e1dbc9d380d2b03677/tiktoken-0.11.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21e43022bf2c33f733ea9b54f6a3f6b4354b909f5a73388fb1b9347ca54a069c", size = 1184046, upload-time = "2025-08-08T23:57:40.689Z" },
- { url = "https://files.pythonhosted.org/packages/9d/bb/52edc8e078cf062ed749248f1454e9e5cfd09979baadb830b3940e522015/tiktoken-0.11.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:adb4e308eb64380dc70fa30493e21c93475eaa11669dea313b6bbf8210bfd013", size = 1244691, upload-time = "2025-08-08T23:57:42.251Z" },
- { url = "https://files.pythonhosted.org/packages/60/d9/884b6cd7ae2570ecdcaffa02b528522b18fef1cbbfdbcaa73799807d0d3b/tiktoken-0.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:ece6b76bfeeb61a125c44bbefdfccc279b5288e6007fbedc0d32bfec602df2f2", size = 884392, upload-time = "2025-08-08T23:57:43.628Z" },
- { url = "https://files.pythonhosted.org/packages/e7/9e/eceddeffc169fc75fe0fd4f38471309f11cb1906f9b8aa39be4f5817df65/tiktoken-0.11.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fd9e6b23e860973cf9526544e220b223c60badf5b62e80a33509d6d40e6c8f5d", size = 1055199, upload-time = "2025-08-08T23:57:45.076Z" },
- { url = "https://files.pythonhosted.org/packages/4f/cf/5f02bfefffdc6b54e5094d2897bc80efd43050e5b09b576fd85936ee54bf/tiktoken-0.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6a76d53cee2da71ee2731c9caa747398762bda19d7f92665e882fef229cb0b5b", size = 996655, upload-time = "2025-08-08T23:57:46.304Z" },
- { url = "https://files.pythonhosted.org/packages/65/8e/c769b45ef379bc360c9978c4f6914c79fd432400a6733a8afc7ed7b0726a/tiktoken-0.11.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ef72aab3ea240646e642413cb363b73869fed4e604dcfd69eec63dc54d603e8", size = 1128867, upload-time = "2025-08-08T23:57:47.438Z" },
- { url = "https://files.pythonhosted.org/packages/d5/2d/4d77f6feb9292bfdd23d5813e442b3bba883f42d0ac78ef5fdc56873f756/tiktoken-0.11.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f929255c705efec7a28bf515e29dc74220b2f07544a8c81b8d69e8efc4578bd", size = 1183308, upload-time = "2025-08-08T23:57:48.566Z" },
- { url = "https://files.pythonhosted.org/packages/7a/65/7ff0a65d3bb0fc5a1fb6cc71b03e0f6e71a68c5eea230d1ff1ba3fd6df49/tiktoken-0.11.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:61f1d15822e4404953d499fd1dcc62817a12ae9fb1e4898033ec8fe3915fdf8e", size = 1244301, upload-time = "2025-08-08T23:57:49.642Z" },
- { url = "https://files.pythonhosted.org/packages/f5/6e/5b71578799b72e5bdcef206a214c3ce860d999d579a3b56e74a6c8989ee2/tiktoken-0.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:45927a71ab6643dfd3ef57d515a5db3d199137adf551f66453be098502838b0f", size = 884282, upload-time = "2025-08-08T23:57:50.759Z" },
- { url = "https://files.pythonhosted.org/packages/cc/cd/a9034bcee638716d9310443818d73c6387a6a96db93cbcb0819b77f5b206/tiktoken-0.11.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a5f3f25ffb152ee7fec78e90a5e5ea5b03b4ea240beed03305615847f7a6ace2", size = 1055339, upload-time = "2025-08-08T23:57:51.802Z" },
- { url = "https://files.pythonhosted.org/packages/f1/91/9922b345f611b4e92581f234e64e9661e1c524875c8eadd513c4b2088472/tiktoken-0.11.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7dc6e9ad16a2a75b4c4be7208055a1f707c9510541d94d9cc31f7fbdc8db41d8", size = 997080, upload-time = "2025-08-08T23:57:53.442Z" },
- { url = "https://files.pythonhosted.org/packages/d0/9d/49cd047c71336bc4b4af460ac213ec1c457da67712bde59b892e84f1859f/tiktoken-0.11.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5a0517634d67a8a48fd4a4ad73930c3022629a85a217d256a6e9b8b47439d1e4", size = 1128501, upload-time = "2025-08-08T23:57:54.808Z" },
- { url = "https://files.pythonhosted.org/packages/52/d5/a0dcdb40dd2ea357e83cb36258967f0ae96f5dd40c722d6e382ceee6bba9/tiktoken-0.11.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7fb4effe60574675118b73c6fbfd3b5868e5d7a1f570d6cc0d18724b09ecf318", size = 1182743, upload-time = "2025-08-08T23:57:56.307Z" },
- { url = "https://files.pythonhosted.org/packages/3b/17/a0fc51aefb66b7b5261ca1314afa83df0106b033f783f9a7bcbe8e741494/tiktoken-0.11.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:94f984c9831fd32688aef4348803b0905d4ae9c432303087bae370dc1381a2b8", size = 1244057, upload-time = "2025-08-08T23:57:57.628Z" },
- { url = "https://files.pythonhosted.org/packages/50/79/bcf350609f3a10f09fe4fc207f132085e497fdd3612f3925ab24d86a0ca0/tiktoken-0.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:2177ffda31dec4023356a441793fed82f7af5291120751dee4d696414f54db0c", size = 883901, upload-time = "2025-08-08T23:57:59.359Z" },
- { url = "https://files.pythonhosted.org/packages/aa/b6/81c5799ab77a9580c6d840cf77d4717e929193a42190fd623a080c647aa6/tiktoken-0.11.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:13220f12c9e82e399377e768640ddfe28bea962739cc3a869cad98f42c419a89", size = 1061648, upload-time = "2025-08-08T23:58:00.753Z" },
- { url = "https://files.pythonhosted.org/packages/50/89/faa668066b2a4640534ef5797c09ecd0a48b43367502129b217339dfaa97/tiktoken-0.11.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7f2db627f5c74477c0404b4089fd8a28ae22fa982a6f7d9c7d4c305c375218f3", size = 1000950, upload-time = "2025-08-08T23:58:01.855Z" },
- { url = "https://files.pythonhosted.org/packages/aa/7f/5f950528b54cb3025af4bc3522c23dbfb691afe8ffb292aa1e8dc2e6bddf/tiktoken-0.11.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2302772f035dceb2bcf8e55a735e4604a0b51a6dd50f38218ff664d46ec43807", size = 1130777, upload-time = "2025-08-08T23:58:03.256Z" },
- { url = "https://files.pythonhosted.org/packages/27/a4/e82ddf0773835ba24536ac8c0dce561e697698ec020a93212a1e041d39b4/tiktoken-0.11.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20b977989afe44c94bcc50db1f76971bb26dca44218bd203ba95925ef56f8e7a", size = 1185692, upload-time = "2025-08-08T23:58:04.476Z" },
- { url = "https://files.pythonhosted.org/packages/1b/c2/06361e41d176e62797ae65fa678111cdd30553321cf4d83e7b84107ea95f/tiktoken-0.11.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:669a1aa1ad6ebf1b3c26b45deb346f345da7680f845b5ea700bba45c20dea24c", size = 1246518, upload-time = "2025-08-08T23:58:06.126Z" },
- { url = "https://files.pythonhosted.org/packages/bb/ad/ca37e15c46741ebb3904d562d03194e845539a08f7751a6df0f391757312/tiktoken-0.11.0-cp39-cp39-win_amd64.whl", hash = "sha256:e363f33c720a055586f730c00e330df4c7ea0024bf1c83a8a9a9dbc054c4f304", size = 884702, upload-time = "2025-08-08T23:58:07.534Z" },
+ { url = "https://files.pythonhosted.org/packages/29/16/c8a903f4c4dffe7a12843191437d7cd8e32751d5de349d45d3fe69544e87/pytest-8.4.1-py3-none-any.whl", hash = "sha256:539c70ba6fcead8e78eebbf1115e8b589e7565830d7d006a8723f19ac8a0afb7", size = 365474, upload-time = "2025-06-18T05:48:03.955Z" },
]
[[package]]
-name = "tokenizers"
-version = "0.21.4"
+name = "pytest-asyncio"
+version = "1.3.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "huggingface-hub" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/c2/2f/402986d0823f8d7ca139d969af2917fefaa9b947d1fb32f6168c509f2492/tokenizers-0.21.4.tar.gz", hash = "sha256:fa23f85fbc9a02ec5c6978da172cdcbac23498c3ca9f3645c5c68740ac007880", size = 351253, upload-time = "2025-07-28T15:48:54.325Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/98/c6/fdb6f72bf6454f52eb4a2510be7fb0f614e541a2554d6210e370d85efff4/tokenizers-0.21.4-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:2ccc10a7c3bcefe0f242867dc914fc1226ee44321eb618cfe3019b5df3400133", size = 2863987, upload-time = "2025-07-28T15:48:44.877Z" },
- { url = "https://files.pythonhosted.org/packages/8d/a6/28975479e35ddc751dc1ddc97b9b69bf7fcf074db31548aab37f8116674c/tokenizers-0.21.4-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:5e2f601a8e0cd5be5cc7506b20a79112370b9b3e9cb5f13f68ab11acd6ca7d60", size = 2732457, upload-time = "2025-07-28T15:48:43.265Z" },
- { url = "https://files.pythonhosted.org/packages/aa/8f/24f39d7b5c726b7b0be95dca04f344df278a3fe3a4deb15a975d194cbb32/tokenizers-0.21.4-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:39b376f5a1aee67b4d29032ee85511bbd1b99007ec735f7f35c8a2eb104eade5", size = 3012624, upload-time = "2025-07-28T13:22:43.895Z" },
- { url = "https://files.pythonhosted.org/packages/58/47/26358925717687a58cb74d7a508de96649544fad5778f0cd9827398dc499/tokenizers-0.21.4-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2107ad649e2cda4488d41dfd031469e9da3fcbfd6183e74e4958fa729ffbf9c6", size = 2939681, upload-time = "2025-07-28T13:22:47.499Z" },
- { url = "https://files.pythonhosted.org/packages/99/6f/cc300fea5db2ab5ddc2c8aea5757a27b89c84469899710c3aeddc1d39801/tokenizers-0.21.4-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c73012da95afafdf235ba80047699df4384fdc481527448a078ffd00e45a7d9", size = 3247445, upload-time = "2025-07-28T15:48:39.711Z" },
- { url = "https://files.pythonhosted.org/packages/be/bf/98cb4b9c3c4afd8be89cfa6423704337dc20b73eb4180397a6e0d456c334/tokenizers-0.21.4-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f23186c40395fc390d27f519679a58023f368a0aad234af145e0f39ad1212732", size = 3428014, upload-time = "2025-07-28T13:22:49.569Z" },
- { url = "https://files.pythonhosted.org/packages/75/c7/96c1cc780e6ca7f01a57c13235dd05b7bc1c0f3588512ebe9d1331b5f5ae/tokenizers-0.21.4-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cc88bb34e23a54cc42713d6d98af5f1bf79c07653d24fe984d2d695ba2c922a2", size = 3193197, upload-time = "2025-07-28T13:22:51.471Z" },
- { url = "https://files.pythonhosted.org/packages/f2/90/273b6c7ec78af547694eddeea9e05de771278bd20476525ab930cecaf7d8/tokenizers-0.21.4-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51b7eabb104f46c1c50b486520555715457ae833d5aee9ff6ae853d1130506ff", size = 3115426, upload-time = "2025-07-28T15:48:41.439Z" },
- { url = "https://files.pythonhosted.org/packages/91/43/c640d5a07e95f1cf9d2c92501f20a25f179ac53a4f71e1489a3dcfcc67ee/tokenizers-0.21.4-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:714b05b2e1af1288bd1bc56ce496c4cebb64a20d158ee802887757791191e6e2", size = 9089127, upload-time = "2025-07-28T15:48:46.472Z" },
- { url = "https://files.pythonhosted.org/packages/44/a1/dd23edd6271d4dca788e5200a807b49ec3e6987815cd9d0a07ad9c96c7c2/tokenizers-0.21.4-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:1340ff877ceedfa937544b7d79f5b7becf33a4cfb58f89b3b49927004ef66f78", size = 9055243, upload-time = "2025-07-28T15:48:48.539Z" },
- { url = "https://files.pythonhosted.org/packages/21/2b/b410d6e9021c4b7ddb57248304dc817c4d4970b73b6ee343674914701197/tokenizers-0.21.4-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:3c1f4317576e465ac9ef0d165b247825a2a4078bcd01cba6b54b867bdf9fdd8b", size = 9298237, upload-time = "2025-07-28T15:48:50.443Z" },
- { url = "https://files.pythonhosted.org/packages/b7/0a/42348c995c67e2e6e5c89ffb9cfd68507cbaeb84ff39c49ee6e0a6dd0fd2/tokenizers-0.21.4-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:c212aa4e45ec0bb5274b16b6f31dd3f1c41944025c2358faaa5782c754e84c24", size = 9461980, upload-time = "2025-07-28T15:48:52.325Z" },
- { url = "https://files.pythonhosted.org/packages/3d/d3/dacccd834404cd71b5c334882f3ba40331ad2120e69ded32cf5fda9a7436/tokenizers-0.21.4-cp39-abi3-win32.whl", hash = "sha256:6c42a930bc5f4c47f4ea775c91de47d27910881902b0f20e4990ebe045a415d0", size = 2329871, upload-time = "2025-07-28T15:48:56.841Z" },
- { url = "https://files.pythonhosted.org/packages/41/f2/fd673d979185f5dcbac4be7d09461cbb99751554ffb6718d0013af8604cb/tokenizers-0.21.4-cp39-abi3-win_amd64.whl", hash = "sha256:475d807a5c3eb72c59ad9b5fcdb254f6e17f53dfcbb9903233b0dfa9c943b597", size = 2507568, upload-time = "2025-07-28T15:48:55.456Z" },
+ { name = "backports-asyncio-runner", marker = "python_full_version < '3.11'" },
+ { name = "pytest" },
+ { name = "typing-extensions", marker = "python_full_version < '3.13'" },
]
-
-[[package]]
-name = "toml"
-version = "0.10.2"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f", size = 22253, upload-time = "2020-11-01T01:40:22.204Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/90/2c/8af215c0f776415f3590cac4f9086ccefd6fd463befeae41cd4d3f193e5a/pytest_asyncio-1.3.0.tar.gz", hash = "sha256:d7f52f36d231b80ee124cd216ffb19369aa168fc10095013c6b014a34d3ee9e5", size = 50087, upload-time = "2025-11-10T16:07:47.256Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", size = 16588, upload-time = "2020-11-01T01:40:20.672Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/35/f8b19922b6a25bc0880171a2f1a003eaeb93657475193ab516fd87cac9da/pytest_asyncio-1.3.0-py3-none-any.whl", hash = "sha256:611e26147c7f77640e6d0a92a38ed17c3e9848063698d5c93d5aa7aa11cebff5", size = 15075, upload-time = "2025-11-10T16:07:45.537Z" },
]
[[package]]
@@ -6056,168 +848,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", size = 14257, upload-time = "2024-11-27T22:38:35.385Z" },
]
-[[package]]
-name = "tomlkit"
-version = "0.13.3"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/cc/18/0bbf3884e9eaa38819ebe46a7bd25dcd56b67434402b66a58c4b8e552575/tomlkit-0.13.3.tar.gz", hash = "sha256:430cf247ee57df2b94ee3fbe588e71d362a941ebb545dec29b53961d61add2a1", size = 185207, upload-time = "2025-06-05T07:13:44.947Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/bd/75/8539d011f6be8e29f339c42e633aae3cb73bffa95dd0f9adec09b9c58e85/tomlkit-0.13.3-py3-none-any.whl", hash = "sha256:c89c649d79ee40629a9fda55f8ace8c6a1b42deb912b2a8fd8d942ddadb606b0", size = 38901, upload-time = "2025-06-05T07:13:43.546Z" },
-]
-
-[[package]]
-name = "torch"
-version = "2.8.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "filelock" },
- { name = "fsspec" },
- { name = "jinja2" },
- { name = "networkx", version = "3.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
- { name = "networkx", version = "3.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" },
- { name = "networkx", version = "3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
- { name = "nvidia-cublas-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
- { name = "nvidia-cuda-cupti-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
- { name = "nvidia-cuda-nvrtc-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
- { name = "nvidia-cuda-runtime-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
- { name = "nvidia-cudnn-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
- { name = "nvidia-cufft-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
- { name = "nvidia-cufile-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
- { name = "nvidia-curand-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
- { name = "nvidia-cusolver-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
- { name = "nvidia-cusparse-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
- { name = "nvidia-cusparselt-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
- { name = "nvidia-nccl-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
- { name = "nvidia-nvjitlink-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
- { name = "nvidia-nvtx-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
- { name = "setuptools", marker = "python_full_version >= '3.12'" },
- { name = "sympy" },
- { name = "triton", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
- { name = "typing-extensions" },
-]
-wheels = [
- { url = "https://files.pythonhosted.org/packages/63/28/110f7274254f1b8476c561dada127173f994afa2b1ffc044efb773c15650/torch-2.8.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:0be92c08b44009d4131d1ff7a8060d10bafdb7ddcb7359ef8d8c5169007ea905", size = 102052793, upload-time = "2025-08-06T14:53:15.852Z" },
- { url = "https://files.pythonhosted.org/packages/70/1c/58da560016f81c339ae14ab16c98153d51c941544ae568da3cb5b1ceb572/torch-2.8.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:89aa9ee820bb39d4d72b794345cccef106b574508dd17dbec457949678c76011", size = 888025420, upload-time = "2025-08-06T14:54:18.014Z" },
- { url = "https://files.pythonhosted.org/packages/70/87/f69752d0dd4ba8218c390f0438130c166fa264a33b7025adb5014b92192c/torch-2.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:e8e5bf982e87e2b59d932769938b698858c64cc53753894be25629bdf5cf2f46", size = 241363614, upload-time = "2025-08-06T14:53:31.496Z" },
- { url = "https://files.pythonhosted.org/packages/ef/d6/e6d4c57e61c2b2175d3aafbfb779926a2cfd7c32eeda7c543925dceec923/torch-2.8.0-cp310-none-macosx_11_0_arm64.whl", hash = "sha256:a3f16a58a9a800f589b26d47ee15aca3acf065546137fc2af039876135f4c760", size = 73611154, upload-time = "2025-08-06T14:53:10.919Z" },
- { url = "https://files.pythonhosted.org/packages/8f/c4/3e7a3887eba14e815e614db70b3b529112d1513d9dae6f4d43e373360b7f/torch-2.8.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:220a06fd7af8b653c35d359dfe1aaf32f65aa85befa342629f716acb134b9710", size = 102073391, upload-time = "2025-08-06T14:53:20.937Z" },
- { url = "https://files.pythonhosted.org/packages/5a/63/4fdc45a0304536e75a5e1b1bbfb1b56dd0e2743c48ee83ca729f7ce44162/torch-2.8.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:c12fa219f51a933d5f80eeb3a7a5d0cbe9168c0a14bbb4055f1979431660879b", size = 888063640, upload-time = "2025-08-06T14:55:05.325Z" },
- { url = "https://files.pythonhosted.org/packages/84/57/2f64161769610cf6b1c5ed782bd8a780e18a3c9d48931319f2887fa9d0b1/torch-2.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:8c7ef765e27551b2fbfc0f41bcf270e1292d9bf79f8e0724848b1682be6e80aa", size = 241366752, upload-time = "2025-08-06T14:53:38.692Z" },
- { url = "https://files.pythonhosted.org/packages/a4/5e/05a5c46085d9b97e928f3f037081d3d2b87fb4b4195030fc099aaec5effc/torch-2.8.0-cp311-none-macosx_11_0_arm64.whl", hash = "sha256:5ae0524688fb6707c57a530c2325e13bb0090b745ba7b4a2cd6a3ce262572916", size = 73621174, upload-time = "2025-08-06T14:53:25.44Z" },
- { url = "https://files.pythonhosted.org/packages/49/0c/2fd4df0d83a495bb5e54dca4474c4ec5f9c62db185421563deeb5dabf609/torch-2.8.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:e2fab4153768d433f8ed9279c8133a114a034a61e77a3a104dcdf54388838705", size = 101906089, upload-time = "2025-08-06T14:53:52.631Z" },
- { url = "https://files.pythonhosted.org/packages/99/a8/6acf48d48838fb8fe480597d98a0668c2beb02ee4755cc136de92a0a956f/torch-2.8.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:b2aca0939fb7e4d842561febbd4ffda67a8e958ff725c1c27e244e85e982173c", size = 887913624, upload-time = "2025-08-06T14:56:44.33Z" },
- { url = "https://files.pythonhosted.org/packages/af/8a/5c87f08e3abd825c7dfecef5a0f1d9aa5df5dd0e3fd1fa2f490a8e512402/torch-2.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:2f4ac52f0130275d7517b03a33d2493bab3693c83dcfadf4f81688ea82147d2e", size = 241326087, upload-time = "2025-08-06T14:53:46.503Z" },
- { url = "https://files.pythonhosted.org/packages/be/66/5c9a321b325aaecb92d4d1855421e3a055abd77903b7dab6575ca07796db/torch-2.8.0-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:619c2869db3ada2c0105487ba21b5008defcc472d23f8b80ed91ac4a380283b0", size = 73630478, upload-time = "2025-08-06T14:53:57.144Z" },
- { url = "https://files.pythonhosted.org/packages/10/4e/469ced5a0603245d6a19a556e9053300033f9c5baccf43a3d25ba73e189e/torch-2.8.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:2b2f96814e0345f5a5aed9bf9734efa913678ed19caf6dc2cddb7930672d6128", size = 101936856, upload-time = "2025-08-06T14:54:01.526Z" },
- { url = "https://files.pythonhosted.org/packages/16/82/3948e54c01b2109238357c6f86242e6ecbf0c63a1af46906772902f82057/torch-2.8.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:65616ca8ec6f43245e1f5f296603e33923f4c30f93d65e103d9e50c25b35150b", size = 887922844, upload-time = "2025-08-06T14:55:50.78Z" },
- { url = "https://files.pythonhosted.org/packages/e3/54/941ea0a860f2717d86a811adf0c2cd01b3983bdd460d0803053c4e0b8649/torch-2.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:659df54119ae03e83a800addc125856effda88b016dfc54d9f65215c3975be16", size = 241330968, upload-time = "2025-08-06T14:54:45.293Z" },
- { url = "https://files.pythonhosted.org/packages/de/69/8b7b13bba430f5e21d77708b616f767683629fc4f8037564a177d20f90ed/torch-2.8.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:1a62a1ec4b0498930e2543535cf70b1bef8c777713de7ceb84cd79115f553767", size = 73915128, upload-time = "2025-08-06T14:54:34.769Z" },
- { url = "https://files.pythonhosted.org/packages/15/0e/8a800e093b7f7430dbaefa80075aee9158ec22e4c4fc3c1a66e4fb96cb4f/torch-2.8.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:83c13411a26fac3d101fe8035a6b0476ae606deb8688e904e796a3534c197def", size = 102020139, upload-time = "2025-08-06T14:54:39.047Z" },
- { url = "https://files.pythonhosted.org/packages/4a/15/5e488ca0bc6162c86a33b58642bc577c84ded17c7b72d97e49b5833e2d73/torch-2.8.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:8f0a9d617a66509ded240add3754e462430a6c1fc5589f86c17b433dd808f97a", size = 887990692, upload-time = "2025-08-06T14:56:18.286Z" },
- { url = "https://files.pythonhosted.org/packages/b4/a8/6a04e4b54472fc5dba7ca2341ab219e529f3c07b6941059fbf18dccac31f/torch-2.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:a7242b86f42be98ac674b88a4988643b9bc6145437ec8f048fea23f72feb5eca", size = 241603453, upload-time = "2025-08-06T14:55:22.945Z" },
- { url = "https://files.pythonhosted.org/packages/04/6e/650bb7f28f771af0cb791b02348db8b7f5f64f40f6829ee82aa6ce99aabe/torch-2.8.0-cp313-none-macosx_11_0_arm64.whl", hash = "sha256:7b677e17f5a3e69fdef7eb3b9da72622f8d322692930297e4ccb52fefc6c8211", size = 73632395, upload-time = "2025-08-06T14:55:28.645Z" },
- { url = "https://files.pythonhosted.org/packages/5b/b0/a321f27270049baa12f5c3fb0d6ceea005634787e3af9a8d75dce8306b0a/torch-2.8.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:da6afa31c13b669d4ba49d8a2169f0db2c3ec6bec4af898aa714f401d4c38904", size = 102059214, upload-time = "2025-08-06T14:55:33.433Z" },
- { url = "https://files.pythonhosted.org/packages/fd/dd/1630cb51b10d3d2e97db95e5a84c32def81fc26b005bce6fc880b0e6db81/torch-2.8.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:06fcee8000e5c62a9f3e52a688b9c5abb7c6228d0e56e3452983416025c41381", size = 888024302, upload-time = "2025-08-06T14:57:28.23Z" },
- { url = "https://files.pythonhosted.org/packages/b9/dc/1f1f621afe15e3c496e1e8f94f8903f75f87e7d642d5a985e92210cc208d/torch-2.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:5128fe752a355d9308e56af1ad28b15266fe2da5948660fad44de9e3a9e36e8c", size = 241249338, upload-time = "2025-08-06T14:57:05.669Z" },
- { url = "https://files.pythonhosted.org/packages/ae/95/ae26263aceb3d57b821179f827d0e321373ed49423e603dd5906ab14a730/torch-2.8.0-cp39-none-macosx_11_0_arm64.whl", hash = "sha256:e9f071f5b52a9f6970dc8a919694b27a91ae9dc08898b2b988abbef5eddfd1ae", size = 73610795, upload-time = "2025-08-06T14:57:11.513Z" },
-]
-
-[[package]]
-name = "tornado"
-version = "6.5.2"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/09/ce/1eb500eae19f4648281bb2186927bb062d2438c2e5093d1360391afd2f90/tornado-6.5.2.tar.gz", hash = "sha256:ab53c8f9a0fa351e2c0741284e06c7a45da86afb544133201c5cc8578eb076a0", size = 510821, upload-time = "2025-08-08T18:27:00.78Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/f6/48/6a7529df2c9cc12efd2e8f5dd219516184d703b34c06786809670df5b3bd/tornado-6.5.2-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:2436822940d37cde62771cff8774f4f00b3c8024fe482e16ca8387b8a2724db6", size = 442563, upload-time = "2025-08-08T18:26:42.945Z" },
- { url = "https://files.pythonhosted.org/packages/f2/b5/9b575a0ed3e50b00c40b08cbce82eb618229091d09f6d14bce80fc01cb0b/tornado-6.5.2-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:583a52c7aa94ee046854ba81d9ebb6c81ec0fd30386d96f7640c96dad45a03ef", size = 440729, upload-time = "2025-08-08T18:26:44.473Z" },
- { url = "https://files.pythonhosted.org/packages/1b/4e/619174f52b120efcf23633c817fd3fed867c30bff785e2cd5a53a70e483c/tornado-6.5.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b0fe179f28d597deab2842b86ed4060deec7388f1fd9c1b4a41adf8af058907e", size = 444295, upload-time = "2025-08-08T18:26:46.021Z" },
- { url = "https://files.pythonhosted.org/packages/95/fa/87b41709552bbd393c85dd18e4e3499dcd8983f66e7972926db8d96aa065/tornado-6.5.2-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b186e85d1e3536d69583d2298423744740986018e393d0321df7340e71898882", size = 443644, upload-time = "2025-08-08T18:26:47.625Z" },
- { url = "https://files.pythonhosted.org/packages/f9/41/fb15f06e33d7430ca89420283a8762a4e6b8025b800ea51796ab5e6d9559/tornado-6.5.2-cp39-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e792706668c87709709c18b353da1f7662317b563ff69f00bab83595940c7108", size = 443878, upload-time = "2025-08-08T18:26:50.599Z" },
- { url = "https://files.pythonhosted.org/packages/11/92/fe6d57da897776ad2e01e279170ea8ae726755b045fe5ac73b75357a5a3f/tornado-6.5.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:06ceb1300fd70cb20e43b1ad8aaee0266e69e7ced38fa910ad2e03285009ce7c", size = 444549, upload-time = "2025-08-08T18:26:51.864Z" },
- { url = "https://files.pythonhosted.org/packages/9b/02/c8f4f6c9204526daf3d760f4aa555a7a33ad0e60843eac025ccfd6ff4a93/tornado-6.5.2-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:74db443e0f5251be86cbf37929f84d8c20c27a355dd452a5cfa2aada0d001ec4", size = 443973, upload-time = "2025-08-08T18:26:53.625Z" },
- { url = "https://files.pythonhosted.org/packages/ae/2d/f5f5707b655ce2317190183868cd0f6822a1121b4baeae509ceb9590d0bd/tornado-6.5.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b5e735ab2889d7ed33b32a459cac490eda71a1ba6857b0118de476ab6c366c04", size = 443954, upload-time = "2025-08-08T18:26:55.072Z" },
- { url = "https://files.pythonhosted.org/packages/e8/59/593bd0f40f7355806bf6573b47b8c22f8e1374c9b6fd03114bd6b7a3dcfd/tornado-6.5.2-cp39-abi3-win32.whl", hash = "sha256:c6f29e94d9b37a95013bb669616352ddb82e3bfe8326fccee50583caebc8a5f0", size = 445023, upload-time = "2025-08-08T18:26:56.677Z" },
- { url = "https://files.pythonhosted.org/packages/c7/2a/f609b420c2f564a748a2d80ebfb2ee02a73ca80223af712fca591386cafb/tornado-6.5.2-cp39-abi3-win_amd64.whl", hash = "sha256:e56a5af51cc30dd2cae649429af65ca2f6571da29504a07995175df14c18f35f", size = 445427, upload-time = "2025-08-08T18:26:57.91Z" },
- { url = "https://files.pythonhosted.org/packages/5e/4f/e1f65e8f8c76d73658b33d33b81eed4322fb5085350e4328d5c956f0c8f9/tornado-6.5.2-cp39-abi3-win_arm64.whl", hash = "sha256:d6c33dc3672e3a1f3618eb63b7ef4683a7688e7b9e6e8f0d9aa5726360a004af", size = 444456, upload-time = "2025-08-08T18:26:59.207Z" },
-]
-
-[[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, upload-time = "2024-11-24T20:12:22.481Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540, upload-time = "2024-11-24T20:12:19.698Z" },
-]
-
-[[package]]
-name = "traitlets"
-version = "5.14.3"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/eb/79/72064e6a701c2183016abbbfedaba506d81e30e232a68c9f0d6f6fcd1574/traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7", size = 161621, upload-time = "2024-04-19T11:11:49.746Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f", size = 85359, upload-time = "2024-04-19T11:11:46.763Z" },
-]
-
-[[package]]
-name = "transformers"
-version = "4.51.2"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "filelock" },
- { name = "huggingface-hub" },
- { name = "numpy" },
- { name = "packaging" },
- { name = "pyyaml" },
- { name = "regex" },
- { name = "requests" },
- { name = "safetensors" },
- { name = "tokenizers" },
- { name = "tqdm" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/e4/cf/f5b8a3e9e9f9f8290202278d1e1156ee40ef7bca4055f55893e79026d77a/transformers-4.51.2.tar.gz", hash = "sha256:ed221c31581e97127cff5de775b05f05d19698b439d7d638ff445502a7f37331", size = 8925420, upload-time = "2025-04-10T16:00:14.049Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/15/af/a3eb4449c8fdde24413555a66e9c100b669f4428fc829bad4ceb73472f4f/transformers-4.51.2-py3-none-any.whl", hash = "sha256:5cb8259098b75ff4b5dd04533a318f7c4750d5307d9617e6d0593526432c404d", size = 10366692, upload-time = "2025-04-10T16:00:10.287Z" },
-]
-
-[[package]]
-name = "trino"
-version = "0.336.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "lz4" },
- { name = "orjson", marker = "platform_python_implementation != 'PyPy'" },
- { name = "python-dateutil" },
- { name = "pytz" },
- { name = "requests" },
- { name = "tzlocal" },
- { name = "zstandard" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/63/bc/1559f7eaf8501e5205be18f18acb13c2e91604c5eae5a9299963b24e62df/trino-0.336.0.tar.gz", hash = "sha256:389150841446949119c3c2c13c1a51bb4be1a27818e40ae40dd3701f36c02550", size = 55505, upload-time = "2025-08-14T11:09:16.315Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/4a/5b/ce0c2740cb73d11e0fe47aff292599db0edfa8c86bbb274cc72b31a5b76c/trino-0.336.0-py3-none-any.whl", hash = "sha256:e82339e9fffe5c6c51de3bfdf28f083e3ae5945a4502739ab2094a0d08d68070", size = 57983, upload-time = "2025-08-14T11:09:15.359Z" },
-]
-
-[[package]]
-name = "triton"
-version = "3.4.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "importlib-metadata", marker = "python_full_version < '3.10'" },
- { name = "setuptools" },
-]
-wheels = [
- { url = "https://files.pythonhosted.org/packages/62/ee/0ee5f64a87eeda19bbad9bc54ae5ca5b98186ed00055281fd40fb4beb10e/triton-3.4.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7ff2785de9bc02f500e085420273bb5cc9c9bb767584a4aa28d6e360cec70128", size = 155430069, upload-time = "2025-07-30T19:58:21.715Z" },
- { url = "https://files.pythonhosted.org/packages/7d/39/43325b3b651d50187e591eefa22e236b2981afcebaefd4f2fc0ea99df191/triton-3.4.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7b70f5e6a41e52e48cfc087436c8a28c17ff98db369447bcaff3b887a3ab4467", size = 155531138, upload-time = "2025-07-30T19:58:29.908Z" },
- { url = "https://files.pythonhosted.org/packages/d0/66/b1eb52839f563623d185f0927eb3530ee4d5ffe9d377cdaf5346b306689e/triton-3.4.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:31c1d84a5c0ec2c0f8e8a072d7fd150cab84a9c239eaddc6706c081bfae4eb04", size = 155560068, upload-time = "2025-07-30T19:58:37.081Z" },
- { url = "https://files.pythonhosted.org/packages/30/7b/0a685684ed5322d2af0bddefed7906674f67974aa88b0fae6e82e3b766f6/triton-3.4.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00be2964616f4c619193cb0d1b29a99bd4b001d7dc333816073f92cf2a8ccdeb", size = 155569223, upload-time = "2025-07-30T19:58:44.017Z" },
- { url = "https://files.pythonhosted.org/packages/20/63/8cb444ad5cdb25d999b7d647abac25af0ee37d292afc009940c05b82dda0/triton-3.4.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7936b18a3499ed62059414d7df563e6c163c5e16c3773678a3ee3d417865035d", size = 155659780, upload-time = "2025-07-30T19:58:51.171Z" },
- { url = "https://files.pythonhosted.org/packages/12/34/1251beb5a3cb93f3950ebe68732752014646003ef6eb11eb5f1a37ca78cd/triton-3.4.0-cp39-cp39-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:98e5c1442eaeabae2e2452ae765801bd53cd4ce873cab0d1bdd59a32ab2d9397", size = 155430799, upload-time = "2025-07-30T19:58:57.664Z" },
-]
-
[[package]]
name = "typing-extensions"
version = "4.15.0"
@@ -6227,31 +857,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" },
]
-[[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, upload-time = "2023-05-24T20:25:47.612Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/65/f3/107a22063bf27bdccf2024833d3445f4eea42b2e598abfbd46f6a63b6cb0/typing_inspect-0.9.0-py3-none-any.whl", hash = "sha256:9ee6fc59062311ef8547596ab6b955e1b8aa46242d854bfc78f4f6b0eff35f9f", size = 8827, upload-time = "2023-05-24T20:25:45.287Z" },
-]
-
-[[package]]
-name = "typing-inspection"
-version = "0.4.1"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "typing-extensions" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/f8/b1/0c11f5058406b3af7609f121aaa6b609744687f1d158b3c3a5bf4cc94238/typing_inspection-0.4.1.tar.gz", hash = "sha256:6ae134cc0203c33377d43188d4064e9b357dba58cff3185f22924610e70a9d28", size = 75726, upload-time = "2025-05-21T18:55:23.885Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/17/69/cd203477f944c353c31bade965f880aa1061fd6bf05ded0726ca845b6ff7/typing_inspection-0.4.1-py3-none-any.whl", hash = "sha256:389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51", size = 14552, upload-time = "2025-05-21T18:55:22.152Z" },
-]
-
[[package]]
name = "tzdata"
version = "2025.2"
@@ -6261,176 +866,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/5c/23/c7abc0ca0a1526a0774eca151daeb8de62ec457e77262b66b359c3c7679e/tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8", size = 347839, upload-time = "2025-03-23T13:54:41.845Z" },
]
-[[package]]
-name = "tzlocal"
-version = "5.3.1"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "tzdata", marker = "sys_platform == 'win32'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/8b/2e/c14812d3d4d9cd1773c6be938f89e5735a1f11a9f184ac3639b93cef35d5/tzlocal-5.3.1.tar.gz", hash = "sha256:cceffc7edecefea1f595541dbd6e990cb1ea3d19bf01b2809f362a03dd7921fd", size = 30761, upload-time = "2025-03-05T21:17:41.549Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/c2/14/e2a54fabd4f08cd7af1c07030603c3356b74da07f7cc056e600436edfa17/tzlocal-5.3.1-py3-none-any.whl", hash = "sha256:eb1a66c3ef5847adf7a834f1be0800581b683b5608e74f86ecbcef8ab91bb85d", size = 18026, upload-time = "2025-03-05T21:17:39.857Z" },
-]
-
-[[package]]
-name = "urllib3"
-version = "1.26.20"
-source = { registry = "https://pypi.org/simple" }
-resolution-markers = [
- "python_full_version < '3.10'",
-]
-sdist = { url = "https://files.pythonhosted.org/packages/e4/e8/6ff5e6bc22095cfc59b6ea711b687e2b7ed4bdb373f7eeec370a97d7392f/urllib3-1.26.20.tar.gz", hash = "sha256:40c2dc0c681e47eb8f90e7e27bf6ff7df2e677421fd46756da1161c39ca70d32", size = 307380, upload-time = "2024-08-29T15:43:11.37Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/33/cf/8435d5a7159e2a9c83a95896ed596f68cf798005fe107cc655b5c5c14704/urllib3-1.26.20-py2.py3-none-any.whl", hash = "sha256:0ed14ccfbf1c30a9072c7ca157e4319b70d65f623e91e7b32fadb2853431016e", size = 144225, upload-time = "2024-08-29T15:43:08.921Z" },
-]
-
-[[package]]
-name = "urllib3"
-version = "2.1.0"
-source = { registry = "https://pypi.org/simple" }
-resolution-markers = [
- "python_full_version >= '3.13'",
- "python_full_version >= '3.12.4' and python_full_version < '3.13'",
- "python_full_version >= '3.12' and python_full_version < '3.12.4'",
- "python_full_version == '3.11.*'",
- "python_full_version == '3.10.*'",
-]
-sdist = { url = "https://files.pythonhosted.org/packages/36/dd/a6b232f449e1bc71802a5b7950dc3675d32c6dbc2a1bd6d71f065551adb6/urllib3-2.1.0.tar.gz", hash = "sha256:df7aa8afb0148fa78488e7899b2c59b5f4ffcfa82e6c54ccb9dd37c1d7b52d54", size = 263900, upload-time = "2023-11-13T12:29:45.049Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/96/94/c31f58c7a7f470d5665935262ebd7455c7e4c7782eb525658d3dbf4b9403/urllib3-2.1.0-py3-none-any.whl", hash = "sha256:55901e917a5896a349ff771be919f8bd99aff50b79fe58fec595eb37bbc56bb3", size = 104579, upload-time = "2023-11-13T12:29:42.719Z" },
-]
-
-[[package]]
-name = "verlib2"
-version = "0.2.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/7a/e3/a27d3b956f9ba6dcdf1404474c466d29bf219cc92f803cbbb3a7f698abe1/verlib2-0.2.0.tar.gz", hash = "sha256:0ab94087c094e27163948dfa817c98d62b6bdd01226cce5188f8cc3cd68345b9", size = 10714, upload-time = "2023-10-24T19:10:13.503Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/48/d3/ec173825185edcb54b950237561c5cf2310e8fb9f288974652449e0a079e/verlib2-0.2.0-py3-none-any.whl", hash = "sha256:c0fb8dbae76e228e55bb6178207cda931d44c843d8485c873b7676835edf2e8f", size = 8927, upload-time = "2023-10-24T19:10:11.051Z" },
-]
-
-[[package]]
-name = "virtualenv"
-version = "20.34.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "distlib" },
- { name = "filelock" },
- { name = "platformdirs" },
- { name = "typing-extensions", marker = "python_full_version < '3.11'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/1c/14/37fcdba2808a6c615681cd216fecae00413c9dab44fb2e57805ecf3eaee3/virtualenv-20.34.0.tar.gz", hash = "sha256:44815b2c9dee7ed86e387b842a84f20b93f7f417f95886ca1996a72a4138eb1a", size = 6003808, upload-time = "2025-08-13T14:24:07.464Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/76/06/04c8e804f813cf972e3262f3f8584c232de64f0cde9f703b46cf53a45090/virtualenv-20.34.0-py3-none-any.whl", hash = "sha256:341f5afa7eee943e4984a9207c025feedd768baff6753cd660c857ceb3e36026", size = 5983279, upload-time = "2025-08-13T14:24:05.111Z" },
-]
-
-[[package]]
-name = "watchdog"
-version = "6.0.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/db/7d/7f3d619e951c88ed75c6037b246ddcf2d322812ee8ea189be89511721d54/watchdog-6.0.0.tar.gz", hash = "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282", size = 131220, upload-time = "2024-11-01T14:07:13.037Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/a9/c7/ca4bf3e518cb57a686b2feb4f55a1892fd9a3dd13f470fca14e00f80ea36/watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13", size = 79079, upload-time = "2024-11-01T14:06:59.472Z" },
- { url = "https://files.pythonhosted.org/packages/5c/51/d46dc9332f9a647593c947b4b88e2381c8dfc0942d15b8edc0310fa4abb1/watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379", size = 79078, upload-time = "2024-11-01T14:07:01.431Z" },
- { url = "https://files.pythonhosted.org/packages/d4/57/04edbf5e169cd318d5f07b4766fee38e825d64b6913ca157ca32d1a42267/watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e", size = 79076, upload-time = "2024-11-01T14:07:02.568Z" },
- { url = "https://files.pythonhosted.org/packages/ab/cc/da8422b300e13cb187d2203f20b9253e91058aaf7db65b74142013478e66/watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f", size = 79077, upload-time = "2024-11-01T14:07:03.893Z" },
- { url = "https://files.pythonhosted.org/packages/2c/3b/b8964e04ae1a025c44ba8e4291f86e97fac443bca31de8bd98d3263d2fcf/watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26", size = 79078, upload-time = "2024-11-01T14:07:05.189Z" },
- { url = "https://files.pythonhosted.org/packages/62/ae/a696eb424bedff7407801c257d4b1afda455fe40821a2be430e173660e81/watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c", size = 79077, upload-time = "2024-11-01T14:07:06.376Z" },
- { url = "https://files.pythonhosted.org/packages/b5/e8/dbf020b4d98251a9860752a094d09a65e1b436ad181faf929983f697048f/watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2", size = 79078, upload-time = "2024-11-01T14:07:07.547Z" },
- { url = "https://files.pythonhosted.org/packages/07/f6/d0e5b343768e8bcb4cda79f0f2f55051bf26177ecd5651f84c07567461cf/watchdog-6.0.0-py3-none-win32.whl", hash = "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a", size = 79065, upload-time = "2024-11-01T14:07:09.525Z" },
- { url = "https://files.pythonhosted.org/packages/db/d9/c495884c6e548fce18a8f40568ff120bc3a4b7b99813081c8ac0c936fa64/watchdog-6.0.0-py3-none-win_amd64.whl", hash = "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680", size = 79070, upload-time = "2024-11-01T14:07:10.686Z" },
- { url = "https://files.pythonhosted.org/packages/33/e8/e40370e6d74ddba47f002a32919d91310d6074130fe4e17dabcafc15cbf1/watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f", size = 79067, upload-time = "2024-11-01T14:07:11.845Z" },
-]
-
-[[package]]
-name = "wcwidth"
-version = "0.2.13"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/6c/63/53559446a878410fc5a5974feb13d31d78d752eb18aeba59c7fef1af7598/wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5", size = 101301, upload-time = "2024-01-06T02:10:57.829Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/fd/84/fd2ba7aafacbad3c4201d395674fc6348826569da3c0937e75505ead3528/wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859", size = 34166, upload-time = "2024-01-06T02:10:55.763Z" },
-]
-
-[[package]]
-name = "wrapt"
-version = "1.17.3"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/95/8f/aeb76c5b46e273670962298c23e7ddde79916cb74db802131d49a85e4b7d/wrapt-1.17.3.tar.gz", hash = "sha256:f66eb08feaa410fe4eebd17f2a2c8e2e46d3476e9f8c783daa8e09e0faa666d0", size = 55547, upload-time = "2025-08-12T05:53:21.714Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/3f/23/bb82321b86411eb51e5a5db3fb8f8032fd30bd7c2d74bfe936136b2fa1d6/wrapt-1.17.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:88bbae4d40d5a46142e70d58bf664a89b6b4befaea7b2ecc14e03cedb8e06c04", size = 53482, upload-time = "2025-08-12T05:51:44.467Z" },
- { url = "https://files.pythonhosted.org/packages/45/69/f3c47642b79485a30a59c63f6d739ed779fb4cc8323205d047d741d55220/wrapt-1.17.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6b13af258d6a9ad602d57d889f83b9d5543acd471eee12eb51f5b01f8eb1bc2", size = 38676, upload-time = "2025-08-12T05:51:32.636Z" },
- { url = "https://files.pythonhosted.org/packages/d1/71/e7e7f5670c1eafd9e990438e69d8fb46fa91a50785332e06b560c869454f/wrapt-1.17.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd341868a4b6714a5962c1af0bd44f7c404ef78720c7de4892901e540417111c", size = 38957, upload-time = "2025-08-12T05:51:54.655Z" },
- { url = "https://files.pythonhosted.org/packages/de/17/9f8f86755c191d6779d7ddead1a53c7a8aa18bccb7cea8e7e72dfa6a8a09/wrapt-1.17.3-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f9b2601381be482f70e5d1051a5965c25fb3625455a2bf520b5a077b22afb775", size = 81975, upload-time = "2025-08-12T05:52:30.109Z" },
- { url = "https://files.pythonhosted.org/packages/f2/15/dd576273491f9f43dd09fce517f6c2ce6eb4fe21681726068db0d0467096/wrapt-1.17.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:343e44b2a8e60e06a7e0d29c1671a0d9951f59174f3709962b5143f60a2a98bd", size = 83149, upload-time = "2025-08-12T05:52:09.316Z" },
- { url = "https://files.pythonhosted.org/packages/0c/c4/5eb4ce0d4814521fee7aa806264bf7a114e748ad05110441cd5b8a5c744b/wrapt-1.17.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:33486899acd2d7d3066156b03465b949da3fd41a5da6e394ec49d271baefcf05", size = 82209, upload-time = "2025-08-12T05:52:10.331Z" },
- { url = "https://files.pythonhosted.org/packages/31/4b/819e9e0eb5c8dc86f60dfc42aa4e2c0d6c3db8732bce93cc752e604bb5f5/wrapt-1.17.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e6f40a8aa5a92f150bdb3e1c44b7e98fb7113955b2e5394122fa5532fec4b418", size = 81551, upload-time = "2025-08-12T05:52:31.137Z" },
- { url = "https://files.pythonhosted.org/packages/f8/83/ed6baf89ba3a56694700139698cf703aac9f0f9eb03dab92f57551bd5385/wrapt-1.17.3-cp310-cp310-win32.whl", hash = "sha256:a36692b8491d30a8c75f1dfee65bef119d6f39ea84ee04d9f9311f83c5ad9390", size = 36464, upload-time = "2025-08-12T05:53:01.204Z" },
- { url = "https://files.pythonhosted.org/packages/2f/90/ee61d36862340ad7e9d15a02529df6b948676b9a5829fd5e16640156627d/wrapt-1.17.3-cp310-cp310-win_amd64.whl", hash = "sha256:afd964fd43b10c12213574db492cb8f73b2f0826c8df07a68288f8f19af2ebe6", size = 38748, upload-time = "2025-08-12T05:53:00.209Z" },
- { url = "https://files.pythonhosted.org/packages/bd/c3/cefe0bd330d389c9983ced15d326f45373f4073c9f4a8c2f99b50bfea329/wrapt-1.17.3-cp310-cp310-win_arm64.whl", hash = "sha256:af338aa93554be859173c39c85243970dc6a289fa907402289eeae7543e1ae18", size = 36810, upload-time = "2025-08-12T05:52:51.906Z" },
- { url = "https://files.pythonhosted.org/packages/52/db/00e2a219213856074a213503fdac0511203dceefff26e1daa15250cc01a0/wrapt-1.17.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:273a736c4645e63ac582c60a56b0acb529ef07f78e08dc6bfadf6a46b19c0da7", size = 53482, upload-time = "2025-08-12T05:51:45.79Z" },
- { url = "https://files.pythonhosted.org/packages/5e/30/ca3c4a5eba478408572096fe9ce36e6e915994dd26a4e9e98b4f729c06d9/wrapt-1.17.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5531d911795e3f935a9c23eb1c8c03c211661a5060aab167065896bbf62a5f85", size = 38674, upload-time = "2025-08-12T05:51:34.629Z" },
- { url = "https://files.pythonhosted.org/packages/31/25/3e8cc2c46b5329c5957cec959cb76a10718e1a513309c31399a4dad07eb3/wrapt-1.17.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0610b46293c59a3adbae3dee552b648b984176f8562ee0dba099a56cfbe4df1f", size = 38959, upload-time = "2025-08-12T05:51:56.074Z" },
- { url = "https://files.pythonhosted.org/packages/5d/8f/a32a99fc03e4b37e31b57cb9cefc65050ea08147a8ce12f288616b05ef54/wrapt-1.17.3-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b32888aad8b6e68f83a8fdccbf3165f5469702a7544472bdf41f582970ed3311", size = 82376, upload-time = "2025-08-12T05:52:32.134Z" },
- { url = "https://files.pythonhosted.org/packages/31/57/4930cb8d9d70d59c27ee1332a318c20291749b4fba31f113c2f8ac49a72e/wrapt-1.17.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8cccf4f81371f257440c88faed6b74f1053eef90807b77e31ca057b2db74edb1", size = 83604, upload-time = "2025-08-12T05:52:11.663Z" },
- { url = "https://files.pythonhosted.org/packages/a8/f3/1afd48de81d63dd66e01b263a6fbb86e1b5053b419b9b33d13e1f6d0f7d0/wrapt-1.17.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8a210b158a34164de8bb68b0e7780041a903d7b00c87e906fb69928bf7890d5", size = 82782, upload-time = "2025-08-12T05:52:12.626Z" },
- { url = "https://files.pythonhosted.org/packages/1e/d7/4ad5327612173b144998232f98a85bb24b60c352afb73bc48e3e0d2bdc4e/wrapt-1.17.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:79573c24a46ce11aab457b472efd8d125e5a51da2d1d24387666cd85f54c05b2", size = 82076, upload-time = "2025-08-12T05:52:33.168Z" },
- { url = "https://files.pythonhosted.org/packages/bb/59/e0adfc831674a65694f18ea6dc821f9fcb9ec82c2ce7e3d73a88ba2e8718/wrapt-1.17.3-cp311-cp311-win32.whl", hash = "sha256:c31eebe420a9a5d2887b13000b043ff6ca27c452a9a22fa71f35f118e8d4bf89", size = 36457, upload-time = "2025-08-12T05:53:03.936Z" },
- { url = "https://files.pythonhosted.org/packages/83/88/16b7231ba49861b6f75fc309b11012ede4d6b0a9c90969d9e0db8d991aeb/wrapt-1.17.3-cp311-cp311-win_amd64.whl", hash = "sha256:0b1831115c97f0663cb77aa27d381237e73ad4f721391a9bfb2fe8bc25fa6e77", size = 38745, upload-time = "2025-08-12T05:53:02.885Z" },
- { url = "https://files.pythonhosted.org/packages/9a/1e/c4d4f3398ec073012c51d1c8d87f715f56765444e1a4b11e5180577b7e6e/wrapt-1.17.3-cp311-cp311-win_arm64.whl", hash = "sha256:5a7b3c1ee8265eb4c8f1b7d29943f195c00673f5ab60c192eba2d4a7eae5f46a", size = 36806, upload-time = "2025-08-12T05:52:53.368Z" },
- { url = "https://files.pythonhosted.org/packages/9f/41/cad1aba93e752f1f9268c77270da3c469883d56e2798e7df6240dcb2287b/wrapt-1.17.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ab232e7fdb44cdfbf55fc3afa31bcdb0d8980b9b95c38b6405df2acb672af0e0", size = 53998, upload-time = "2025-08-12T05:51:47.138Z" },
- { url = "https://files.pythonhosted.org/packages/60/f8/096a7cc13097a1869fe44efe68dace40d2a16ecb853141394047f0780b96/wrapt-1.17.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9baa544e6acc91130e926e8c802a17f3b16fbea0fd441b5a60f5cf2cc5c3deba", size = 39020, upload-time = "2025-08-12T05:51:35.906Z" },
- { url = "https://files.pythonhosted.org/packages/33/df/bdf864b8997aab4febb96a9ae5c124f700a5abd9b5e13d2a3214ec4be705/wrapt-1.17.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6b538e31eca1a7ea4605e44f81a48aa24c4632a277431a6ed3f328835901f4fd", size = 39098, upload-time = "2025-08-12T05:51:57.474Z" },
- { url = "https://files.pythonhosted.org/packages/9f/81/5d931d78d0eb732b95dc3ddaeeb71c8bb572fb01356e9133916cd729ecdd/wrapt-1.17.3-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:042ec3bb8f319c147b1301f2393bc19dba6e176b7da446853406d041c36c7828", size = 88036, upload-time = "2025-08-12T05:52:34.784Z" },
- { url = "https://files.pythonhosted.org/packages/ca/38/2e1785df03b3d72d34fc6252d91d9d12dc27a5c89caef3335a1bbb8908ca/wrapt-1.17.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3af60380ba0b7b5aeb329bc4e402acd25bd877e98b3727b0135cb5c2efdaefe9", size = 88156, upload-time = "2025-08-12T05:52:13.599Z" },
- { url = "https://files.pythonhosted.org/packages/b3/8b/48cdb60fe0603e34e05cffda0b2a4adab81fd43718e11111a4b0100fd7c1/wrapt-1.17.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0b02e424deef65c9f7326d8c19220a2c9040c51dc165cddb732f16198c168396", size = 87102, upload-time = "2025-08-12T05:52:14.56Z" },
- { url = "https://files.pythonhosted.org/packages/3c/51/d81abca783b58f40a154f1b2c56db1d2d9e0d04fa2d4224e357529f57a57/wrapt-1.17.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:74afa28374a3c3a11b3b5e5fca0ae03bef8450d6aa3ab3a1e2c30e3a75d023dc", size = 87732, upload-time = "2025-08-12T05:52:36.165Z" },
- { url = "https://files.pythonhosted.org/packages/9e/b1/43b286ca1392a006d5336412d41663eeef1ad57485f3e52c767376ba7e5a/wrapt-1.17.3-cp312-cp312-win32.whl", hash = "sha256:4da9f45279fff3543c371d5ababc57a0384f70be244de7759c85a7f989cb4ebe", size = 36705, upload-time = "2025-08-12T05:53:07.123Z" },
- { url = "https://files.pythonhosted.org/packages/28/de/49493f962bd3c586ab4b88066e967aa2e0703d6ef2c43aa28cb83bf7b507/wrapt-1.17.3-cp312-cp312-win_amd64.whl", hash = "sha256:e71d5c6ebac14875668a1e90baf2ea0ef5b7ac7918355850c0908ae82bcb297c", size = 38877, upload-time = "2025-08-12T05:53:05.436Z" },
- { url = "https://files.pythonhosted.org/packages/f1/48/0f7102fe9cb1e8a5a77f80d4f0956d62d97034bbe88d33e94699f99d181d/wrapt-1.17.3-cp312-cp312-win_arm64.whl", hash = "sha256:604d076c55e2fdd4c1c03d06dc1a31b95130010517b5019db15365ec4a405fc6", size = 36885, upload-time = "2025-08-12T05:52:54.367Z" },
- { url = "https://files.pythonhosted.org/packages/fc/f6/759ece88472157acb55fc195e5b116e06730f1b651b5b314c66291729193/wrapt-1.17.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a47681378a0439215912ef542c45a783484d4dd82bac412b71e59cf9c0e1cea0", size = 54003, upload-time = "2025-08-12T05:51:48.627Z" },
- { url = "https://files.pythonhosted.org/packages/4f/a9/49940b9dc6d47027dc850c116d79b4155f15c08547d04db0f07121499347/wrapt-1.17.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:54a30837587c6ee3cd1a4d1c2ec5d24e77984d44e2f34547e2323ddb4e22eb77", size = 39025, upload-time = "2025-08-12T05:51:37.156Z" },
- { url = "https://files.pythonhosted.org/packages/45/35/6a08de0f2c96dcdd7fe464d7420ddb9a7655a6561150e5fc4da9356aeaab/wrapt-1.17.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:16ecf15d6af39246fe33e507105d67e4b81d8f8d2c6598ff7e3ca1b8a37213f7", size = 39108, upload-time = "2025-08-12T05:51:58.425Z" },
- { url = "https://files.pythonhosted.org/packages/0c/37/6faf15cfa41bf1f3dba80cd3f5ccc6622dfccb660ab26ed79f0178c7497f/wrapt-1.17.3-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6fd1ad24dc235e4ab88cda009e19bf347aabb975e44fd5c2fb22a3f6e4141277", size = 88072, upload-time = "2025-08-12T05:52:37.53Z" },
- { url = "https://files.pythonhosted.org/packages/78/f2/efe19ada4a38e4e15b6dff39c3e3f3f73f5decf901f66e6f72fe79623a06/wrapt-1.17.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ed61b7c2d49cee3c027372df5809a59d60cf1b6c2f81ee980a091f3afed6a2d", size = 88214, upload-time = "2025-08-12T05:52:15.886Z" },
- { url = "https://files.pythonhosted.org/packages/40/90/ca86701e9de1622b16e09689fc24b76f69b06bb0150990f6f4e8b0eeb576/wrapt-1.17.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:423ed5420ad5f5529db9ce89eac09c8a2f97da18eb1c870237e84c5a5c2d60aa", size = 87105, upload-time = "2025-08-12T05:52:17.914Z" },
- { url = "https://files.pythonhosted.org/packages/fd/e0/d10bd257c9a3e15cbf5523025252cc14d77468e8ed644aafb2d6f54cb95d/wrapt-1.17.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e01375f275f010fcbf7f643b4279896d04e571889b8a5b3f848423d91bf07050", size = 87766, upload-time = "2025-08-12T05:52:39.243Z" },
- { url = "https://files.pythonhosted.org/packages/e8/cf/7d848740203c7b4b27eb55dbfede11aca974a51c3d894f6cc4b865f42f58/wrapt-1.17.3-cp313-cp313-win32.whl", hash = "sha256:53e5e39ff71b3fc484df8a522c933ea2b7cdd0d5d15ae82e5b23fde87d44cbd8", size = 36711, upload-time = "2025-08-12T05:53:10.074Z" },
- { url = "https://files.pythonhosted.org/packages/57/54/35a84d0a4d23ea675994104e667ceff49227ce473ba6a59ba2c84f250b74/wrapt-1.17.3-cp313-cp313-win_amd64.whl", hash = "sha256:1f0b2f40cf341ee8cc1a97d51ff50dddb9fcc73241b9143ec74b30fc4f44f6cb", size = 38885, upload-time = "2025-08-12T05:53:08.695Z" },
- { url = "https://files.pythonhosted.org/packages/01/77/66e54407c59d7b02a3c4e0af3783168fff8e5d61def52cda8728439d86bc/wrapt-1.17.3-cp313-cp313-win_arm64.whl", hash = "sha256:7425ac3c54430f5fc5e7b6f41d41e704db073309acfc09305816bc6a0b26bb16", size = 36896, upload-time = "2025-08-12T05:52:55.34Z" },
- { url = "https://files.pythonhosted.org/packages/02/a2/cd864b2a14f20d14f4c496fab97802001560f9f41554eef6df201cd7f76c/wrapt-1.17.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cf30f6e3c077c8e6a9a7809c94551203c8843e74ba0c960f4a98cd80d4665d39", size = 54132, upload-time = "2025-08-12T05:51:49.864Z" },
- { url = "https://files.pythonhosted.org/packages/d5/46/d011725b0c89e853dc44cceb738a307cde5d240d023d6d40a82d1b4e1182/wrapt-1.17.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:e228514a06843cae89621384cfe3a80418f3c04aadf8a3b14e46a7be704e4235", size = 39091, upload-time = "2025-08-12T05:51:38.935Z" },
- { url = "https://files.pythonhosted.org/packages/2e/9e/3ad852d77c35aae7ddebdbc3b6d35ec8013af7d7dddad0ad911f3d891dae/wrapt-1.17.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:5ea5eb3c0c071862997d6f3e02af1d055f381b1d25b286b9d6644b79db77657c", size = 39172, upload-time = "2025-08-12T05:51:59.365Z" },
- { url = "https://files.pythonhosted.org/packages/c3/f7/c983d2762bcce2326c317c26a6a1e7016f7eb039c27cdf5c4e30f4160f31/wrapt-1.17.3-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:281262213373b6d5e4bb4353bc36d1ba4084e6d6b5d242863721ef2bf2c2930b", size = 87163, upload-time = "2025-08-12T05:52:40.965Z" },
- { url = "https://files.pythonhosted.org/packages/e4/0f/f673f75d489c7f22d17fe0193e84b41540d962f75fce579cf6873167c29b/wrapt-1.17.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dc4a8d2b25efb6681ecacad42fca8859f88092d8732b170de6a5dddd80a1c8fa", size = 87963, upload-time = "2025-08-12T05:52:20.326Z" },
- { url = "https://files.pythonhosted.org/packages/df/61/515ad6caca68995da2fac7a6af97faab8f78ebe3bf4f761e1b77efbc47b5/wrapt-1.17.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:373342dd05b1d07d752cecbec0c41817231f29f3a89aa8b8843f7b95992ed0c7", size = 86945, upload-time = "2025-08-12T05:52:21.581Z" },
- { url = "https://files.pythonhosted.org/packages/d3/bd/4e70162ce398462a467bc09e768bee112f1412e563620adc353de9055d33/wrapt-1.17.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d40770d7c0fd5cbed9d84b2c3f2e156431a12c9a37dc6284060fb4bec0b7ffd4", size = 86857, upload-time = "2025-08-12T05:52:43.043Z" },
- { url = "https://files.pythonhosted.org/packages/2b/b8/da8560695e9284810b8d3df8a19396a6e40e7518059584a1a394a2b35e0a/wrapt-1.17.3-cp314-cp314-win32.whl", hash = "sha256:fbd3c8319de8e1dc79d346929cd71d523622da527cca14e0c1d257e31c2b8b10", size = 37178, upload-time = "2025-08-12T05:53:12.605Z" },
- { url = "https://files.pythonhosted.org/packages/db/c8/b71eeb192c440d67a5a0449aaee2310a1a1e8eca41676046f99ed2487e9f/wrapt-1.17.3-cp314-cp314-win_amd64.whl", hash = "sha256:e1a4120ae5705f673727d3253de3ed0e016f7cd78dc463db1b31e2463e1f3cf6", size = 39310, upload-time = "2025-08-12T05:53:11.106Z" },
- { url = "https://files.pythonhosted.org/packages/45/20/2cda20fd4865fa40f86f6c46ed37a2a8356a7a2fde0773269311f2af56c7/wrapt-1.17.3-cp314-cp314-win_arm64.whl", hash = "sha256:507553480670cab08a800b9463bdb881b2edeed77dc677b0a5915e6106e91a58", size = 37266, upload-time = "2025-08-12T05:52:56.531Z" },
- { url = "https://files.pythonhosted.org/packages/77/ed/dd5cf21aec36c80443c6f900449260b80e2a65cf963668eaef3b9accce36/wrapt-1.17.3-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:ed7c635ae45cfbc1a7371f708727bf74690daedc49b4dba310590ca0bd28aa8a", size = 56544, upload-time = "2025-08-12T05:51:51.109Z" },
- { url = "https://files.pythonhosted.org/packages/8d/96/450c651cc753877ad100c7949ab4d2e2ecc4d97157e00fa8f45df682456a/wrapt-1.17.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:249f88ed15503f6492a71f01442abddd73856a0032ae860de6d75ca62eed8067", size = 40283, upload-time = "2025-08-12T05:51:39.912Z" },
- { url = "https://files.pythonhosted.org/packages/d1/86/2fcad95994d9b572db57632acb6f900695a648c3e063f2cd344b3f5c5a37/wrapt-1.17.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5a03a38adec8066d5a37bea22f2ba6bbf39fcdefbe2d91419ab864c3fb515454", size = 40366, upload-time = "2025-08-12T05:52:00.693Z" },
- { url = "https://files.pythonhosted.org/packages/64/0e/f4472f2fdde2d4617975144311f8800ef73677a159be7fe61fa50997d6c0/wrapt-1.17.3-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:5d4478d72eb61c36e5b446e375bbc49ed002430d17cdec3cecb36993398e1a9e", size = 108571, upload-time = "2025-08-12T05:52:44.521Z" },
- { url = "https://files.pythonhosted.org/packages/cc/01/9b85a99996b0a97c8a17484684f206cbb6ba73c1ce6890ac668bcf3838fb/wrapt-1.17.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:223db574bb38637e8230eb14b185565023ab624474df94d2af18f1cdb625216f", size = 113094, upload-time = "2025-08-12T05:52:22.618Z" },
- { url = "https://files.pythonhosted.org/packages/25/02/78926c1efddcc7b3aa0bc3d6b33a822f7d898059f7cd9ace8c8318e559ef/wrapt-1.17.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e405adefb53a435f01efa7ccdec012c016b5a1d3f35459990afc39b6be4d5056", size = 110659, upload-time = "2025-08-12T05:52:24.057Z" },
- { url = "https://files.pythonhosted.org/packages/dc/ee/c414501ad518ac3e6fe184753632fe5e5ecacdcf0effc23f31c1e4f7bfcf/wrapt-1.17.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:88547535b787a6c9ce4086917b6e1d291aa8ed914fdd3a838b3539dc95c12804", size = 106946, upload-time = "2025-08-12T05:52:45.976Z" },
- { url = "https://files.pythonhosted.org/packages/be/44/a1bd64b723d13bb151d6cc91b986146a1952385e0392a78567e12149c7b4/wrapt-1.17.3-cp314-cp314t-win32.whl", hash = "sha256:41b1d2bc74c2cac6f9074df52b2efbef2b30bdfe5f40cb78f8ca22963bc62977", size = 38717, upload-time = "2025-08-12T05:53:15.214Z" },
- { url = "https://files.pythonhosted.org/packages/79/d9/7cfd5a312760ac4dd8bf0184a6ee9e43c33e47f3dadc303032ce012b8fa3/wrapt-1.17.3-cp314-cp314t-win_amd64.whl", hash = "sha256:73d496de46cd2cdbdbcce4ae4bcdb4afb6a11234a1df9c085249d55166b95116", size = 41334, upload-time = "2025-08-12T05:53:14.178Z" },
- { url = "https://files.pythonhosted.org/packages/46/78/10ad9781128ed2f99dbc474f43283b13fea8ba58723e98844367531c18e9/wrapt-1.17.3-cp314-cp314t-win_arm64.whl", hash = "sha256:f38e60678850c42461d4202739f9bf1e3a737c7ad283638251e79cc49effb6b6", size = 38471, upload-time = "2025-08-12T05:52:57.784Z" },
- { url = "https://files.pythonhosted.org/packages/41/be/be9b3b0a461ee3e30278706f3f3759b9b69afeedef7fe686036286c04ac6/wrapt-1.17.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:30ce38e66630599e1193798285706903110d4f057aab3168a34b7fdc85569afc", size = 53485, upload-time = "2025-08-12T05:51:53.11Z" },
- { url = "https://files.pythonhosted.org/packages/b3/a8/8f61d6b8f526efc8c10e12bf80b4206099fea78ade70427846a37bc9cbea/wrapt-1.17.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:65d1d00fbfb3ea5f20add88bbc0f815150dbbde3b026e6c24759466c8b5a9ef9", size = 38675, upload-time = "2025-08-12T05:51:42.885Z" },
- { url = "https://files.pythonhosted.org/packages/48/f1/23950c29a25637b74b322f9e425a17cc01a478f6afb35138ecb697f9558d/wrapt-1.17.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a7c06742645f914f26c7f1fa47b8bc4c91d222f76ee20116c43d5ef0912bba2d", size = 38956, upload-time = "2025-08-12T05:52:03.149Z" },
- { url = "https://files.pythonhosted.org/packages/43/46/dd0791943613885f62619f18ee6107e6133237a6b6ed8a9ecfac339d0b4f/wrapt-1.17.3-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7e18f01b0c3e4a07fe6dfdb00e29049ba17eadbc5e7609a2a3a4af83ab7d710a", size = 81745, upload-time = "2025-08-12T05:52:49.62Z" },
- { url = "https://files.pythonhosted.org/packages/dd/ec/bb2d19bd1a614cc4f438abac13ae26c57186197920432d2a915183b15a8b/wrapt-1.17.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f5f51a6466667a5a356e6381d362d259125b57f059103dd9fdc8c0cf1d14139", size = 82833, upload-time = "2025-08-12T05:52:27.738Z" },
- { url = "https://files.pythonhosted.org/packages/8d/eb/66579aea6ad36f07617fedca8e282e49c7c9bab64c63b446cfe4f7f47a49/wrapt-1.17.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:59923aa12d0157f6b82d686c3fd8e1166fa8cdfb3e17b42ce3b6147ff81528df", size = 81889, upload-time = "2025-08-12T05:52:29.023Z" },
- { url = "https://files.pythonhosted.org/packages/04/9c/a56b5ac0e2473bdc3fb11b22dd69ff423154d63861cf77911cdde5e38fd2/wrapt-1.17.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:46acc57b331e0b3bcb3e1ca3b421d65637915cfcd65eb783cb2f78a511193f9b", size = 81344, upload-time = "2025-08-12T05:52:50.869Z" },
- { url = "https://files.pythonhosted.org/packages/93/4c/9bd735c42641d81cb58d7bfb142c58f95c833962d15113026705add41a07/wrapt-1.17.3-cp39-cp39-win32.whl", hash = "sha256:3e62d15d3cfa26e3d0788094de7b64efa75f3a53875cdbccdf78547aed547a81", size = 36462, upload-time = "2025-08-12T05:53:19.623Z" },
- { url = "https://files.pythonhosted.org/packages/f0/ea/0b72f29cb5ebc16eb55c57dc0c98e5de76fc97f435fd407f7d409459c0a6/wrapt-1.17.3-cp39-cp39-win_amd64.whl", hash = "sha256:1f23fa283f51c890eda8e34e4937079114c74b4c81d2b2f1f1d94948f5cc3d7f", size = 38740, upload-time = "2025-08-12T05:53:18.271Z" },
- { url = "https://files.pythonhosted.org/packages/c3/8b/9eae65fb92321e38dbfec7719b87d840a4b92fde83fd1bbf238c5488d055/wrapt-1.17.3-cp39-cp39-win_arm64.whl", hash = "sha256:24c2ed34dc222ed754247a2702b1e1e89fdbaa4016f324b4b8f1a802d4ffe87f", size = 36806, upload-time = "2025-08-12T05:52:58.765Z" },
- { url = "https://files.pythonhosted.org/packages/1f/f6/a933bd70f98e9cf3e08167fc5cd7aaaca49147e48411c0bd5ae701bb2194/wrapt-1.17.3-py3-none-any.whl", hash = "sha256:7171ae35d2c33d326ac19dd8facb1e82e5fd04ef8c6c0e394d7af55a55051c22", size = 23591, upload-time = "2025-08-12T05:53:20.674Z" },
-]
-
[[package]]
name = "yarl"
version = "1.20.1"
@@ -6527,137 +962,5 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/9e/ed/c5fb04869b99b717985e244fd93029c7a8e8febdfcffa06093e32d7d44e7/yarl-1.20.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:88cab98aa4e13e1ade8c141daeedd300a4603b7132819c484841bb7af3edce9e", size = 341709, upload-time = "2025-06-10T00:45:23.221Z" },
{ url = "https://files.pythonhosted.org/packages/24/fd/725b8e73ac2a50e78a4534ac43c6addf5c1c2d65380dd48a9169cc6739a9/yarl-1.20.1-cp313-cp313t-win32.whl", hash = "sha256:b121ff6a7cbd4abc28985b6028235491941b9fe8fe226e6fdc539c977ea1739d", size = 86591, upload-time = "2025-06-10T00:45:25.793Z" },
{ url = "https://files.pythonhosted.org/packages/94/c3/b2e9f38bc3e11191981d57ea08cab2166e74ea770024a646617c9cddd9f6/yarl-1.20.1-cp313-cp313t-win_amd64.whl", hash = "sha256:541d050a355bbbc27e55d906bc91cb6fe42f96c01413dd0f4ed5a5240513874f", size = 93003, upload-time = "2025-06-10T00:45:27.752Z" },
- { url = "https://files.pythonhosted.org/packages/01/75/0d37402d208d025afa6b5b8eb80e466d267d3fd1927db8e317d29a94a4cb/yarl-1.20.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e42ba79e2efb6845ebab49c7bf20306c4edf74a0b20fc6b2ccdd1a219d12fad3", size = 134259, upload-time = "2025-06-10T00:45:29.882Z" },
- { url = "https://files.pythonhosted.org/packages/73/84/1fb6c85ae0cf9901046f07d0ac9eb162f7ce6d95db541130aa542ed377e6/yarl-1.20.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:41493b9b7c312ac448b7f0a42a089dffe1d6e6e981a2d76205801a023ed26a2b", size = 91269, upload-time = "2025-06-10T00:45:32.917Z" },
- { url = "https://files.pythonhosted.org/packages/f3/9c/eae746b24c4ea29a5accba9a06c197a70fa38a49c7df244e0d3951108861/yarl-1.20.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f5a5928ff5eb13408c62a968ac90d43f8322fd56d87008b8f9dabf3c0f6ee983", size = 89995, upload-time = "2025-06-10T00:45:35.066Z" },
- { url = "https://files.pythonhosted.org/packages/fb/30/693e71003ec4bc1daf2e4cf7c478c417d0985e0a8e8f00b2230d517876fc/yarl-1.20.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:30c41ad5d717b3961b2dd785593b67d386b73feca30522048d37298fee981805", size = 325253, upload-time = "2025-06-10T00:45:37.052Z" },
- { url = "https://files.pythonhosted.org/packages/0f/a2/5264dbebf90763139aeb0b0b3154763239398400f754ae19a0518b654117/yarl-1.20.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:59febc3969b0781682b469d4aca1a5cab7505a4f7b85acf6db01fa500fa3f6ba", size = 320897, upload-time = "2025-06-10T00:45:39.962Z" },
- { url = "https://files.pythonhosted.org/packages/e7/17/77c7a89b3c05856489777e922f41db79ab4faf58621886df40d812c7facd/yarl-1.20.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d2b6fb3622b7e5bf7a6e5b679a69326b4279e805ed1699d749739a61d242449e", size = 340696, upload-time = "2025-06-10T00:45:41.915Z" },
- { url = "https://files.pythonhosted.org/packages/6d/55/28409330b8ef5f2f681f5b478150496ec9cf3309b149dab7ec8ab5cfa3f0/yarl-1.20.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:749d73611db8d26a6281086f859ea7ec08f9c4c56cec864e52028c8b328db723", size = 335064, upload-time = "2025-06-10T00:45:43.893Z" },
- { url = "https://files.pythonhosted.org/packages/85/58/cb0257cbd4002828ff735f44d3c5b6966c4fd1fc8cc1cd3cd8a143fbc513/yarl-1.20.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9427925776096e664c39e131447aa20ec738bdd77c049c48ea5200db2237e000", size = 327256, upload-time = "2025-06-10T00:45:46.393Z" },
- { url = "https://files.pythonhosted.org/packages/53/f6/c77960370cfa46f6fb3d6a5a79a49d3abfdb9ef92556badc2dcd2748bc2a/yarl-1.20.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff70f32aa316393eaf8222d518ce9118148eddb8a53073c2403863b41033eed5", size = 316389, upload-time = "2025-06-10T00:45:48.358Z" },
- { url = "https://files.pythonhosted.org/packages/64/ab/be0b10b8e029553c10905b6b00c64ecad3ebc8ace44b02293a62579343f6/yarl-1.20.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:c7ddf7a09f38667aea38801da8b8d6bfe81df767d9dfc8c88eb45827b195cd1c", size = 340481, upload-time = "2025-06-10T00:45:50.663Z" },
- { url = "https://files.pythonhosted.org/packages/c5/c3/3f327bd3905a4916029bf5feb7f86dcf864c7704f099715f62155fb386b2/yarl-1.20.1-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:57edc88517d7fc62b174fcfb2e939fbc486a68315d648d7e74d07fac42cec240", size = 336941, upload-time = "2025-06-10T00:45:52.554Z" },
- { url = "https://files.pythonhosted.org/packages/d1/42/040bdd5d3b3bb02b4a6ace4ed4075e02f85df964d6e6cb321795d2a6496a/yarl-1.20.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:dab096ce479d5894d62c26ff4f699ec9072269d514b4edd630a393223f45a0ee", size = 339936, upload-time = "2025-06-10T00:45:54.919Z" },
- { url = "https://files.pythonhosted.org/packages/0d/1c/911867b8e8c7463b84dfdc275e0d99b04b66ad5132b503f184fe76be8ea4/yarl-1.20.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:14a85f3bd2d7bb255be7183e5d7d6e70add151a98edf56a770d6140f5d5f4010", size = 360163, upload-time = "2025-06-10T00:45:56.87Z" },
- { url = "https://files.pythonhosted.org/packages/e2/31/8c389f6c6ca0379b57b2da87f1f126c834777b4931c5ee8427dd65d0ff6b/yarl-1.20.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:2c89b5c792685dd9cd3fa9761c1b9f46fc240c2a3265483acc1565769996a3f8", size = 359108, upload-time = "2025-06-10T00:45:58.869Z" },
- { url = "https://files.pythonhosted.org/packages/7f/09/ae4a649fb3964324c70a3e2b61f45e566d9ffc0affd2b974cbf628957673/yarl-1.20.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:69e9b141de5511021942a6866990aea6d111c9042235de90e08f94cf972ca03d", size = 351875, upload-time = "2025-06-10T00:46:01.45Z" },
- { url = "https://files.pythonhosted.org/packages/8d/43/bbb4ed4c34d5bb62b48bf957f68cd43f736f79059d4f85225ab1ef80f4b9/yarl-1.20.1-cp39-cp39-win32.whl", hash = "sha256:b5f307337819cdfdbb40193cad84978a029f847b0a357fbe49f712063cfc4f06", size = 82293, upload-time = "2025-06-10T00:46:03.763Z" },
- { url = "https://files.pythonhosted.org/packages/d7/cd/ce185848a7dba68ea69e932674b5c1a42a1852123584bccc5443120f857c/yarl-1.20.1-cp39-cp39-win_amd64.whl", hash = "sha256:eae7bfe2069f9c1c5b05fc7fe5d612e5bbc089a39309904ee8b829e322dcad00", size = 87385, upload-time = "2025-06-10T00:46:05.655Z" },
{ url = "https://files.pythonhosted.org/packages/b4/2d/2345fce04cfd4bee161bf1e7d9cdc702e3e16109021035dbb24db654a622/yarl-1.20.1-py3-none-any.whl", hash = "sha256:83b8eb083fe4683c6115795d9fc1cfaf2cbbefb19b3a1cb68f6527460f483a77", size = 46542, upload-time = "2025-06-10T00:46:07.521Z" },
]
-
-[[package]]
-name = "zipp"
-version = "3.23.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166", size = 25547, upload-time = "2025-06-08T17:06:39.4Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276, upload-time = "2025-06-08T17:06:38.034Z" },
-]
-
-[[package]]
-name = "zstandard"
-version = "0.25.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/fd/aa/3e0508d5a5dd96529cdc5a97011299056e14c6505b678fd58938792794b1/zstandard-0.25.0.tar.gz", hash = "sha256:7713e1179d162cf5c7906da876ec2ccb9c3a9dcbdffef0cc7f70c3667a205f0b", size = 711513, upload-time = "2025-09-14T22:15:54.002Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/56/7a/28efd1d371f1acd037ac64ed1c5e2b41514a6cc937dd6ab6a13ab9f0702f/zstandard-0.25.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e59fdc271772f6686e01e1b3b74537259800f57e24280be3f29c8a0deb1904dd", size = 795256, upload-time = "2025-09-14T22:15:56.415Z" },
- { url = "https://files.pythonhosted.org/packages/96/34/ef34ef77f1ee38fc8e4f9775217a613b452916e633c4f1d98f31db52c4a5/zstandard-0.25.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4d441506e9b372386a5271c64125f72d5df6d2a8e8a2a45a0ae09b03cb781ef7", size = 640565, upload-time = "2025-09-14T22:15:58.177Z" },
- { url = "https://files.pythonhosted.org/packages/9d/1b/4fdb2c12eb58f31f28c4d28e8dc36611dd7205df8452e63f52fb6261d13e/zstandard-0.25.0-cp310-cp310-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:ab85470ab54c2cb96e176f40342d9ed41e58ca5733be6a893b730e7af9c40550", size = 5345306, upload-time = "2025-09-14T22:16:00.165Z" },
- { url = "https://files.pythonhosted.org/packages/73/28/a44bdece01bca027b079f0e00be3b6bd89a4df180071da59a3dd7381665b/zstandard-0.25.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e05ab82ea7753354bb054b92e2f288afb750e6b439ff6ca78af52939ebbc476d", size = 5055561, upload-time = "2025-09-14T22:16:02.22Z" },
- { url = "https://files.pythonhosted.org/packages/e9/74/68341185a4f32b274e0fc3410d5ad0750497e1acc20bd0f5b5f64ce17785/zstandard-0.25.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:78228d8a6a1c177a96b94f7e2e8d012c55f9c760761980da16ae7546a15a8e9b", size = 5402214, upload-time = "2025-09-14T22:16:04.109Z" },
- { url = "https://files.pythonhosted.org/packages/8b/67/f92e64e748fd6aaffe01e2b75a083c0c4fd27abe1c8747fee4555fcee7dd/zstandard-0.25.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:2b6bd67528ee8b5c5f10255735abc21aa106931f0dbaf297c7be0c886353c3d0", size = 5449703, upload-time = "2025-09-14T22:16:06.312Z" },
- { url = "https://files.pythonhosted.org/packages/fd/e5/6d36f92a197c3c17729a2125e29c169f460538a7d939a27eaaa6dcfcba8e/zstandard-0.25.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4b6d83057e713ff235a12e73916b6d356e3084fd3d14ced499d84240f3eecee0", size = 5556583, upload-time = "2025-09-14T22:16:08.457Z" },
- { url = "https://files.pythonhosted.org/packages/d7/83/41939e60d8d7ebfe2b747be022d0806953799140a702b90ffe214d557638/zstandard-0.25.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9174f4ed06f790a6869b41cba05b43eeb9a35f8993c4422ab853b705e8112bbd", size = 5045332, upload-time = "2025-09-14T22:16:10.444Z" },
- { url = "https://files.pythonhosted.org/packages/b3/87/d3ee185e3d1aa0133399893697ae91f221fda79deb61adbe998a7235c43f/zstandard-0.25.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:25f8f3cd45087d089aef5ba3848cd9efe3ad41163d3400862fb42f81a3a46701", size = 5572283, upload-time = "2025-09-14T22:16:12.128Z" },
- { url = "https://files.pythonhosted.org/packages/0a/1d/58635ae6104df96671076ac7d4ae7816838ce7debd94aecf83e30b7121b0/zstandard-0.25.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3756b3e9da9b83da1796f8809dd57cb024f838b9eeafde28f3cb472012797ac1", size = 4959754, upload-time = "2025-09-14T22:16:14.225Z" },
- { url = "https://files.pythonhosted.org/packages/75/d6/57e9cb0a9983e9a229dd8fd2e6e96593ef2aa82a3907188436f22b111ccd/zstandard-0.25.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:81dad8d145d8fd981b2962b686b2241d3a1ea07733e76a2f15435dfb7fb60150", size = 5266477, upload-time = "2025-09-14T22:16:16.343Z" },
- { url = "https://files.pythonhosted.org/packages/d1/a9/ee891e5edf33a6ebce0a028726f0bbd8567effe20fe3d5808c42323e8542/zstandard-0.25.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:a5a419712cf88862a45a23def0ae063686db3d324cec7edbe40509d1a79a0aab", size = 5440914, upload-time = "2025-09-14T22:16:18.453Z" },
- { url = "https://files.pythonhosted.org/packages/58/08/a8522c28c08031a9521f27abc6f78dbdee7312a7463dd2cfc658b813323b/zstandard-0.25.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:e7360eae90809efd19b886e59a09dad07da4ca9ba096752e61a2e03c8aca188e", size = 5819847, upload-time = "2025-09-14T22:16:20.559Z" },
- { url = "https://files.pythonhosted.org/packages/6f/11/4c91411805c3f7b6f31c60e78ce347ca48f6f16d552fc659af6ec3b73202/zstandard-0.25.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:75ffc32a569fb049499e63ce68c743155477610532da1eb38e7f24bf7cd29e74", size = 5363131, upload-time = "2025-09-14T22:16:22.206Z" },
- { url = "https://files.pythonhosted.org/packages/ef/d6/8c4bd38a3b24c4c7676a7a3d8de85d6ee7a983602a734b9f9cdefb04a5d6/zstandard-0.25.0-cp310-cp310-win32.whl", hash = "sha256:106281ae350e494f4ac8a80470e66d1fe27e497052c8d9c3b95dc4cf1ade81aa", size = 436469, upload-time = "2025-09-14T22:16:25.002Z" },
- { url = "https://files.pythonhosted.org/packages/93/90/96d50ad417a8ace5f841b3228e93d1bb13e6ad356737f42e2dde30d8bd68/zstandard-0.25.0-cp310-cp310-win_amd64.whl", hash = "sha256:ea9d54cc3d8064260114a0bbf3479fc4a98b21dffc89b3459edd506b69262f6e", size = 506100, upload-time = "2025-09-14T22:16:23.569Z" },
- { url = "https://files.pythonhosted.org/packages/2a/83/c3ca27c363d104980f1c9cee1101cc8ba724ac8c28a033ede6aab89585b1/zstandard-0.25.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:933b65d7680ea337180733cf9e87293cc5500cc0eb3fc8769f4d3c88d724ec5c", size = 795254, upload-time = "2025-09-14T22:16:26.137Z" },
- { url = "https://files.pythonhosted.org/packages/ac/4d/e66465c5411a7cf4866aeadc7d108081d8ceba9bc7abe6b14aa21c671ec3/zstandard-0.25.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a3f79487c687b1fc69f19e487cd949bf3aae653d181dfb5fde3bf6d18894706f", size = 640559, upload-time = "2025-09-14T22:16:27.973Z" },
- { url = "https://files.pythonhosted.org/packages/12/56/354fe655905f290d3b147b33fe946b0f27e791e4b50a5f004c802cb3eb7b/zstandard-0.25.0-cp311-cp311-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:0bbc9a0c65ce0eea3c34a691e3c4b6889f5f3909ba4822ab385fab9057099431", size = 5348020, upload-time = "2025-09-14T22:16:29.523Z" },
- { url = "https://files.pythonhosted.org/packages/3b/13/2b7ed68bd85e69a2069bcc72141d378f22cae5a0f3b353a2c8f50ef30c1b/zstandard-0.25.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:01582723b3ccd6939ab7b3a78622c573799d5d8737b534b86d0e06ac18dbde4a", size = 5058126, upload-time = "2025-09-14T22:16:31.811Z" },
- { url = "https://files.pythonhosted.org/packages/c9/dd/fdaf0674f4b10d92cb120ccff58bbb6626bf8368f00ebfd2a41ba4a0dc99/zstandard-0.25.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5f1ad7bf88535edcf30038f6919abe087f606f62c00a87d7e33e7fc57cb69fcc", size = 5405390, upload-time = "2025-09-14T22:16:33.486Z" },
- { url = "https://files.pythonhosted.org/packages/0f/67/354d1555575bc2490435f90d67ca4dd65238ff2f119f30f72d5cde09c2ad/zstandard-0.25.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:06acb75eebeedb77b69048031282737717a63e71e4ae3f77cc0c3b9508320df6", size = 5452914, upload-time = "2025-09-14T22:16:35.277Z" },
- { url = "https://files.pythonhosted.org/packages/bb/1f/e9cfd801a3f9190bf3e759c422bbfd2247db9d7f3d54a56ecde70137791a/zstandard-0.25.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9300d02ea7c6506f00e627e287e0492a5eb0371ec1670ae852fefffa6164b072", size = 5559635, upload-time = "2025-09-14T22:16:37.141Z" },
- { url = "https://files.pythonhosted.org/packages/21/88/5ba550f797ca953a52d708c8e4f380959e7e3280af029e38fbf47b55916e/zstandard-0.25.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bfd06b1c5584b657a2892a6014c2f4c20e0db0208c159148fa78c65f7e0b0277", size = 5048277, upload-time = "2025-09-14T22:16:38.807Z" },
- { url = "https://files.pythonhosted.org/packages/46/c0/ca3e533b4fa03112facbe7fbe7779cb1ebec215688e5df576fe5429172e0/zstandard-0.25.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f373da2c1757bb7f1acaf09369cdc1d51d84131e50d5fa9863982fd626466313", size = 5574377, upload-time = "2025-09-14T22:16:40.523Z" },
- { url = "https://files.pythonhosted.org/packages/12/9b/3fb626390113f272abd0799fd677ea33d5fc3ec185e62e6be534493c4b60/zstandard-0.25.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6c0e5a65158a7946e7a7affa6418878ef97ab66636f13353b8502d7ea03c8097", size = 4961493, upload-time = "2025-09-14T22:16:43.3Z" },
- { url = "https://files.pythonhosted.org/packages/cb/d3/23094a6b6a4b1343b27ae68249daa17ae0651fcfec9ed4de09d14b940285/zstandard-0.25.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:c8e167d5adf59476fa3e37bee730890e389410c354771a62e3c076c86f9f7778", size = 5269018, upload-time = "2025-09-14T22:16:45.292Z" },
- { url = "https://files.pythonhosted.org/packages/8c/a7/bb5a0c1c0f3f4b5e9d5b55198e39de91e04ba7c205cc46fcb0f95f0383c1/zstandard-0.25.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:98750a309eb2f020da61e727de7d7ba3c57c97cf6213f6f6277bb7fb42a8e065", size = 5443672, upload-time = "2025-09-14T22:16:47.076Z" },
- { url = "https://files.pythonhosted.org/packages/27/22/503347aa08d073993f25109c36c8d9f029c7d5949198050962cb568dfa5e/zstandard-0.25.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:22a086cff1b6ceca18a8dd6096ec631e430e93a8e70a9ca5efa7561a00f826fa", size = 5822753, upload-time = "2025-09-14T22:16:49.316Z" },
- { url = "https://files.pythonhosted.org/packages/e2/be/94267dc6ee64f0f8ba2b2ae7c7a2df934a816baaa7291db9e1aa77394c3c/zstandard-0.25.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:72d35d7aa0bba323965da807a462b0966c91608ef3a48ba761678cb20ce5d8b7", size = 5366047, upload-time = "2025-09-14T22:16:51.328Z" },
- { url = "https://files.pythonhosted.org/packages/7b/a3/732893eab0a3a7aecff8b99052fecf9f605cf0fb5fb6d0290e36beee47a4/zstandard-0.25.0-cp311-cp311-win32.whl", hash = "sha256:f5aeea11ded7320a84dcdd62a3d95b5186834224a9e55b92ccae35d21a8b63d4", size = 436484, upload-time = "2025-09-14T22:16:55.005Z" },
- { url = "https://files.pythonhosted.org/packages/43/a3/c6155f5c1cce691cb80dfd38627046e50af3ee9ddc5d0b45b9b063bfb8c9/zstandard-0.25.0-cp311-cp311-win_amd64.whl", hash = "sha256:daab68faadb847063d0c56f361a289c4f268706b598afbf9ad113cbe5c38b6b2", size = 506183, upload-time = "2025-09-14T22:16:52.753Z" },
- { url = "https://files.pythonhosted.org/packages/8c/3e/8945ab86a0820cc0e0cdbf38086a92868a9172020fdab8a03ac19662b0e5/zstandard-0.25.0-cp311-cp311-win_arm64.whl", hash = "sha256:22a06c5df3751bb7dc67406f5374734ccee8ed37fc5981bf1ad7041831fa1137", size = 462533, upload-time = "2025-09-14T22:16:53.878Z" },
- { url = "https://files.pythonhosted.org/packages/82/fc/f26eb6ef91ae723a03e16eddb198abcfce2bc5a42e224d44cc8b6765e57e/zstandard-0.25.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7b3c3a3ab9daa3eed242d6ecceead93aebbb8f5f84318d82cee643e019c4b73b", size = 795738, upload-time = "2025-09-14T22:16:56.237Z" },
- { url = "https://files.pythonhosted.org/packages/aa/1c/d920d64b22f8dd028a8b90e2d756e431a5d86194caa78e3819c7bf53b4b3/zstandard-0.25.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:913cbd31a400febff93b564a23e17c3ed2d56c064006f54efec210d586171c00", size = 640436, upload-time = "2025-09-14T22:16:57.774Z" },
- { url = "https://files.pythonhosted.org/packages/53/6c/288c3f0bd9fcfe9ca41e2c2fbfd17b2097f6af57b62a81161941f09afa76/zstandard-0.25.0-cp312-cp312-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:011d388c76b11a0c165374ce660ce2c8efa8e5d87f34996aa80f9c0816698b64", size = 5343019, upload-time = "2025-09-14T22:16:59.302Z" },
- { url = "https://files.pythonhosted.org/packages/1e/15/efef5a2f204a64bdb5571e6161d49f7ef0fffdbca953a615efbec045f60f/zstandard-0.25.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:6dffecc361d079bb48d7caef5d673c88c8988d3d33fb74ab95b7ee6da42652ea", size = 5063012, upload-time = "2025-09-14T22:17:01.156Z" },
- { url = "https://files.pythonhosted.org/packages/b7/37/a6ce629ffdb43959e92e87ebdaeebb5ac81c944b6a75c9c47e300f85abdf/zstandard-0.25.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:7149623bba7fdf7e7f24312953bcf73cae103db8cae49f8154dd1eadc8a29ecb", size = 5394148, upload-time = "2025-09-14T22:17:03.091Z" },
- { url = "https://files.pythonhosted.org/packages/e3/79/2bf870b3abeb5c070fe2d670a5a8d1057a8270f125ef7676d29ea900f496/zstandard-0.25.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:6a573a35693e03cf1d67799fd01b50ff578515a8aeadd4595d2a7fa9f3ec002a", size = 5451652, upload-time = "2025-09-14T22:17:04.979Z" },
- { url = "https://files.pythonhosted.org/packages/53/60/7be26e610767316c028a2cbedb9a3beabdbe33e2182c373f71a1c0b88f36/zstandard-0.25.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5a56ba0db2d244117ed744dfa8f6f5b366e14148e00de44723413b2f3938a902", size = 5546993, upload-time = "2025-09-14T22:17:06.781Z" },
- { url = "https://files.pythonhosted.org/packages/85/c7/3483ad9ff0662623f3648479b0380d2de5510abf00990468c286c6b04017/zstandard-0.25.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:10ef2a79ab8e2974e2075fb984e5b9806c64134810fac21576f0668e7ea19f8f", size = 5046806, upload-time = "2025-09-14T22:17:08.415Z" },
- { url = "https://files.pythonhosted.org/packages/08/b3/206883dd25b8d1591a1caa44b54c2aad84badccf2f1de9e2d60a446f9a25/zstandard-0.25.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:aaf21ba8fb76d102b696781bddaa0954b782536446083ae3fdaa6f16b25a1c4b", size = 5576659, upload-time = "2025-09-14T22:17:10.164Z" },
- { url = "https://files.pythonhosted.org/packages/9d/31/76c0779101453e6c117b0ff22565865c54f48f8bd807df2b00c2c404b8e0/zstandard-0.25.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1869da9571d5e94a85a5e8d57e4e8807b175c9e4a6294e3b66fa4efb074d90f6", size = 4953933, upload-time = "2025-09-14T22:17:11.857Z" },
- { url = "https://files.pythonhosted.org/packages/18/e1/97680c664a1bf9a247a280a053d98e251424af51f1b196c6d52f117c9720/zstandard-0.25.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:809c5bcb2c67cd0ed81e9229d227d4ca28f82d0f778fc5fea624a9def3963f91", size = 5268008, upload-time = "2025-09-14T22:17:13.627Z" },
- { url = "https://files.pythonhosted.org/packages/1e/73/316e4010de585ac798e154e88fd81bb16afc5c5cb1a72eeb16dd37e8024a/zstandard-0.25.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f27662e4f7dbf9f9c12391cb37b4c4c3cb90ffbd3b1fb9284dadbbb8935fa708", size = 5433517, upload-time = "2025-09-14T22:17:16.103Z" },
- { url = "https://files.pythonhosted.org/packages/5b/60/dd0f8cfa8129c5a0ce3ea6b7f70be5b33d2618013a161e1ff26c2b39787c/zstandard-0.25.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:99c0c846e6e61718715a3c9437ccc625de26593fea60189567f0118dc9db7512", size = 5814292, upload-time = "2025-09-14T22:17:17.827Z" },
- { url = "https://files.pythonhosted.org/packages/fc/5f/75aafd4b9d11b5407b641b8e41a57864097663699f23e9ad4dbb91dc6bfe/zstandard-0.25.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:474d2596a2dbc241a556e965fb76002c1ce655445e4e3bf38e5477d413165ffa", size = 5360237, upload-time = "2025-09-14T22:17:19.954Z" },
- { url = "https://files.pythonhosted.org/packages/ff/8d/0309daffea4fcac7981021dbf21cdb2e3427a9e76bafbcdbdf5392ff99a4/zstandard-0.25.0-cp312-cp312-win32.whl", hash = "sha256:23ebc8f17a03133b4426bcc04aabd68f8236eb78c3760f12783385171b0fd8bd", size = 436922, upload-time = "2025-09-14T22:17:24.398Z" },
- { url = "https://files.pythonhosted.org/packages/79/3b/fa54d9015f945330510cb5d0b0501e8253c127cca7ebe8ba46a965df18c5/zstandard-0.25.0-cp312-cp312-win_amd64.whl", hash = "sha256:ffef5a74088f1e09947aecf91011136665152e0b4b359c42be3373897fb39b01", size = 506276, upload-time = "2025-09-14T22:17:21.429Z" },
- { url = "https://files.pythonhosted.org/packages/ea/6b/8b51697e5319b1f9ac71087b0af9a40d8a6288ff8025c36486e0c12abcc4/zstandard-0.25.0-cp312-cp312-win_arm64.whl", hash = "sha256:181eb40e0b6a29b3cd2849f825e0fa34397f649170673d385f3598ae17cca2e9", size = 462679, upload-time = "2025-09-14T22:17:23.147Z" },
- { url = "https://files.pythonhosted.org/packages/35/0b/8df9c4ad06af91d39e94fa96cc010a24ac4ef1378d3efab9223cc8593d40/zstandard-0.25.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ec996f12524f88e151c339688c3897194821d7f03081ab35d31d1e12ec975e94", size = 795735, upload-time = "2025-09-14T22:17:26.042Z" },
- { url = "https://files.pythonhosted.org/packages/3f/06/9ae96a3e5dcfd119377ba33d4c42a7d89da1efabd5cb3e366b156c45ff4d/zstandard-0.25.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a1a4ae2dec3993a32247995bdfe367fc3266da832d82f8438c8570f989753de1", size = 640440, upload-time = "2025-09-14T22:17:27.366Z" },
- { url = "https://files.pythonhosted.org/packages/d9/14/933d27204c2bd404229c69f445862454dcc101cd69ef8c6068f15aaec12c/zstandard-0.25.0-cp313-cp313-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:e96594a5537722fdfb79951672a2a63aec5ebfb823e7560586f7484819f2a08f", size = 5343070, upload-time = "2025-09-14T22:17:28.896Z" },
- { url = "https://files.pythonhosted.org/packages/6d/db/ddb11011826ed7db9d0e485d13df79b58586bfdec56e5c84a928a9a78c1c/zstandard-0.25.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:bfc4e20784722098822e3eee42b8e576b379ed72cca4a7cb856ae733e62192ea", size = 5063001, upload-time = "2025-09-14T22:17:31.044Z" },
- { url = "https://files.pythonhosted.org/packages/db/00/87466ea3f99599d02a5238498b87bf84a6348290c19571051839ca943777/zstandard-0.25.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:457ed498fc58cdc12fc48f7950e02740d4f7ae9493dd4ab2168a47c93c31298e", size = 5394120, upload-time = "2025-09-14T22:17:32.711Z" },
- { url = "https://files.pythonhosted.org/packages/2b/95/fc5531d9c618a679a20ff6c29e2b3ef1d1f4ad66c5e161ae6ff847d102a9/zstandard-0.25.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:fd7a5004eb1980d3cefe26b2685bcb0b17989901a70a1040d1ac86f1d898c551", size = 5451230, upload-time = "2025-09-14T22:17:34.41Z" },
- { url = "https://files.pythonhosted.org/packages/63/4b/e3678b4e776db00f9f7b2fe58e547e8928ef32727d7a1ff01dea010f3f13/zstandard-0.25.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8e735494da3db08694d26480f1493ad2cf86e99bdd53e8e9771b2752a5c0246a", size = 5547173, upload-time = "2025-09-14T22:17:36.084Z" },
- { url = "https://files.pythonhosted.org/packages/4e/d5/ba05ed95c6b8ec30bd468dfeab20589f2cf709b5c940483e31d991f2ca58/zstandard-0.25.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3a39c94ad7866160a4a46d772e43311a743c316942037671beb264e395bdd611", size = 5046736, upload-time = "2025-09-14T22:17:37.891Z" },
- { url = "https://files.pythonhosted.org/packages/50/d5/870aa06b3a76c73eced65c044b92286a3c4e00554005ff51962deef28e28/zstandard-0.25.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:172de1f06947577d3a3005416977cce6168f2261284c02080e7ad0185faeced3", size = 5576368, upload-time = "2025-09-14T22:17:40.206Z" },
- { url = "https://files.pythonhosted.org/packages/5d/35/398dc2ffc89d304d59bc12f0fdd931b4ce455bddf7038a0a67733a25f550/zstandard-0.25.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3c83b0188c852a47cd13ef3bf9209fb0a77fa5374958b8c53aaa699398c6bd7b", size = 4954022, upload-time = "2025-09-14T22:17:41.879Z" },
- { url = "https://files.pythonhosted.org/packages/9a/5c/36ba1e5507d56d2213202ec2b05e8541734af5f2ce378c5d1ceaf4d88dc4/zstandard-0.25.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:1673b7199bbe763365b81a4f3252b8e80f44c9e323fc42940dc8843bfeaf9851", size = 5267889, upload-time = "2025-09-14T22:17:43.577Z" },
- { url = "https://files.pythonhosted.org/packages/70/e8/2ec6b6fb7358b2ec0113ae202647ca7c0e9d15b61c005ae5225ad0995df5/zstandard-0.25.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:0be7622c37c183406f3dbf0cba104118eb16a4ea7359eeb5752f0794882fc250", size = 5433952, upload-time = "2025-09-14T22:17:45.271Z" },
- { url = "https://files.pythonhosted.org/packages/7b/01/b5f4d4dbc59ef193e870495c6f1275f5b2928e01ff5a81fecb22a06e22fb/zstandard-0.25.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:5f5e4c2a23ca271c218ac025bd7d635597048b366d6f31f420aaeb715239fc98", size = 5814054, upload-time = "2025-09-14T22:17:47.08Z" },
- { url = "https://files.pythonhosted.org/packages/b2/e5/fbd822d5c6f427cf158316d012c5a12f233473c2f9c5fe5ab1ae5d21f3d8/zstandard-0.25.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4f187a0bb61b35119d1926aee039524d1f93aaf38a9916b8c4b78ac8514a0aaf", size = 5360113, upload-time = "2025-09-14T22:17:48.893Z" },
- { url = "https://files.pythonhosted.org/packages/8e/e0/69a553d2047f9a2c7347caa225bb3a63b6d7704ad74610cb7823baa08ed7/zstandard-0.25.0-cp313-cp313-win32.whl", hash = "sha256:7030defa83eef3e51ff26f0b7bfb229f0204b66fe18e04359ce3474ac33cbc09", size = 436936, upload-time = "2025-09-14T22:17:52.658Z" },
- { url = "https://files.pythonhosted.org/packages/d9/82/b9c06c870f3bd8767c201f1edbdf9e8dc34be5b0fbc5682c4f80fe948475/zstandard-0.25.0-cp313-cp313-win_amd64.whl", hash = "sha256:1f830a0dac88719af0ae43b8b2d6aef487d437036468ef3c2ea59c51f9d55fd5", size = 506232, upload-time = "2025-09-14T22:17:50.402Z" },
- { url = "https://files.pythonhosted.org/packages/d4/57/60c3c01243bb81d381c9916e2a6d9e149ab8627c0c7d7abb2d73384b3c0c/zstandard-0.25.0-cp313-cp313-win_arm64.whl", hash = "sha256:85304a43f4d513f5464ceb938aa02c1e78c2943b29f44a750b48b25ac999a049", size = 462671, upload-time = "2025-09-14T22:17:51.533Z" },
- { url = "https://files.pythonhosted.org/packages/3d/5c/f8923b595b55fe49e30612987ad8bf053aef555c14f05bb659dd5dbe3e8a/zstandard-0.25.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:e29f0cf06974c899b2c188ef7f783607dbef36da4c242eb6c82dcd8b512855e3", size = 795887, upload-time = "2025-09-14T22:17:54.198Z" },
- { url = "https://files.pythonhosted.org/packages/8d/09/d0a2a14fc3439c5f874042dca72a79c70a532090b7ba0003be73fee37ae2/zstandard-0.25.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:05df5136bc5a011f33cd25bc9f506e7426c0c9b3f9954f056831ce68f3b6689f", size = 640658, upload-time = "2025-09-14T22:17:55.423Z" },
- { url = "https://files.pythonhosted.org/packages/5d/7c/8b6b71b1ddd517f68ffb55e10834388d4f793c49c6b83effaaa05785b0b4/zstandard-0.25.0-cp314-cp314-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:f604efd28f239cc21b3adb53eb061e2a205dc164be408e553b41ba2ffe0ca15c", size = 5379849, upload-time = "2025-09-14T22:17:57.372Z" },
- { url = "https://files.pythonhosted.org/packages/a4/86/a48e56320d0a17189ab7a42645387334fba2200e904ee47fc5a26c1fd8ca/zstandard-0.25.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:223415140608d0f0da010499eaa8ccdb9af210a543fac54bce15babbcfc78439", size = 5058095, upload-time = "2025-09-14T22:17:59.498Z" },
- { url = "https://files.pythonhosted.org/packages/f8/ad/eb659984ee2c0a779f9d06dbfe45e2dc39d99ff40a319895df2d3d9a48e5/zstandard-0.25.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2e54296a283f3ab5a26fc9b8b5d4978ea0532f37b231644f367aa588930aa043", size = 5551751, upload-time = "2025-09-14T22:18:01.618Z" },
- { url = "https://files.pythonhosted.org/packages/61/b3/b637faea43677eb7bd42ab204dfb7053bd5c4582bfe6b1baefa80ac0c47b/zstandard-0.25.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ca54090275939dc8ec5dea2d2afb400e0f83444b2fc24e07df7fdef677110859", size = 6364818, upload-time = "2025-09-14T22:18:03.769Z" },
- { url = "https://files.pythonhosted.org/packages/31/dc/cc50210e11e465c975462439a492516a73300ab8caa8f5e0902544fd748b/zstandard-0.25.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e09bb6252b6476d8d56100e8147b803befa9a12cea144bbe629dd508800d1ad0", size = 5560402, upload-time = "2025-09-14T22:18:05.954Z" },
- { url = "https://files.pythonhosted.org/packages/c9/ae/56523ae9c142f0c08efd5e868a6da613ae76614eca1305259c3bf6a0ed43/zstandard-0.25.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a9ec8c642d1ec73287ae3e726792dd86c96f5681eb8df274a757bf62b750eae7", size = 4955108, upload-time = "2025-09-14T22:18:07.68Z" },
- { url = "https://files.pythonhosted.org/packages/98/cf/c899f2d6df0840d5e384cf4c4121458c72802e8bda19691f3b16619f51e9/zstandard-0.25.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:a4089a10e598eae6393756b036e0f419e8c1d60f44a831520f9af41c14216cf2", size = 5269248, upload-time = "2025-09-14T22:18:09.753Z" },
- { url = "https://files.pythonhosted.org/packages/1b/c0/59e912a531d91e1c192d3085fc0f6fb2852753c301a812d856d857ea03c6/zstandard-0.25.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:f67e8f1a324a900e75b5e28ffb152bcac9fbed1cc7b43f99cd90f395c4375344", size = 5430330, upload-time = "2025-09-14T22:18:11.966Z" },
- { url = "https://files.pythonhosted.org/packages/a0/1d/7e31db1240de2df22a58e2ea9a93fc6e38cc29353e660c0272b6735d6669/zstandard-0.25.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:9654dbc012d8b06fc3d19cc825af3f7bf8ae242226df5f83936cb39f5fdc846c", size = 5811123, upload-time = "2025-09-14T22:18:13.907Z" },
- { url = "https://files.pythonhosted.org/packages/f6/49/fac46df5ad353d50535e118d6983069df68ca5908d4d65b8c466150a4ff1/zstandard-0.25.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:4203ce3b31aec23012d3a4cf4a2ed64d12fea5269c49aed5e4c3611b938e4088", size = 5359591, upload-time = "2025-09-14T22:18:16.465Z" },
- { url = "https://files.pythonhosted.org/packages/c2/38/f249a2050ad1eea0bb364046153942e34abba95dd5520af199aed86fbb49/zstandard-0.25.0-cp314-cp314-win32.whl", hash = "sha256:da469dc041701583e34de852d8634703550348d5822e66a0c827d39b05365b12", size = 444513, upload-time = "2025-09-14T22:18:20.61Z" },
- { url = "https://files.pythonhosted.org/packages/3a/43/241f9615bcf8ba8903b3f0432da069e857fc4fd1783bd26183db53c4804b/zstandard-0.25.0-cp314-cp314-win_amd64.whl", hash = "sha256:c19bcdd826e95671065f8692b5a4aa95c52dc7a02a4c5a0cac46deb879a017a2", size = 516118, upload-time = "2025-09-14T22:18:17.849Z" },
- { url = "https://files.pythonhosted.org/packages/f0/ef/da163ce2450ed4febf6467d77ccb4cd52c4c30ab45624bad26ca0a27260c/zstandard-0.25.0-cp314-cp314-win_arm64.whl", hash = "sha256:d7541afd73985c630bafcd6338d2518ae96060075f9463d7dc14cfb33514383d", size = 476940, upload-time = "2025-09-14T22:18:19.088Z" },
- { url = "https://files.pythonhosted.org/packages/14/0d/d0a405dad6ab6f9f759c26d866cca66cb209bff6f8db656074d662a953dd/zstandard-0.25.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b9af1fe743828123e12b41dd8091eca1074d0c1569cc42e6e1eee98027f2bbd0", size = 795263, upload-time = "2025-09-14T22:18:21.683Z" },
- { url = "https://files.pythonhosted.org/packages/ca/aa/ceb8d79cbad6dabd4cb1178ca853f6a4374d791c5e0241a0988173e2a341/zstandard-0.25.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4b14abacf83dfb5c25eb4e4a79520de9e7e205f72c9ee7702f91233ae57d33a2", size = 640560, upload-time = "2025-09-14T22:18:22.867Z" },
- { url = "https://files.pythonhosted.org/packages/88/cd/2cf6d476131b509cc122d25d3416a2d0aa17687ddbada7599149f9da620e/zstandard-0.25.0-cp39-cp39-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:a51ff14f8017338e2f2e5dab738ce1ec3b5a851f23b18c1ae1359b1eecbee6df", size = 5344244, upload-time = "2025-09-14T22:18:24.724Z" },
- { url = "https://files.pythonhosted.org/packages/5c/71/e14820b61a1c137966b7667b400b72fa4a45c836257e443f3d77607db268/zstandard-0.25.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3b870ce5a02d4b22286cf4944c628e0f0881b11b3f14667c1d62185a99e04f53", size = 5054550, upload-time = "2025-09-14T22:18:26.445Z" },
- { url = "https://files.pythonhosted.org/packages/f9/ce/26dc5a6fa956be41d0e984909224ed196ee6f91d607f0b3fd84577741a77/zstandard-0.25.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:05353cef599a7b0b98baca9b068dd36810c3ef0f42bf282583f438caf6ddcee3", size = 5401150, upload-time = "2025-09-14T22:18:28.745Z" },
- { url = "https://files.pythonhosted.org/packages/f2/1b/402cab5edcfe867465daf869d5ac2a94930931c0989633bc01d6a7d8bd68/zstandard-0.25.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:19796b39075201d51d5f5f790bf849221e58b48a39a5fc74837675d8bafc7362", size = 5448595, upload-time = "2025-09-14T22:18:30.475Z" },
- { url = "https://files.pythonhosted.org/packages/86/b2/fc50c58271a1ead0e5a0a0e6311f4b221f35954dce438ce62751b3af9b68/zstandard-0.25.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:53e08b2445a6bc241261fea89d065536f00a581f02535f8122eba42db9375530", size = 5555290, upload-time = "2025-09-14T22:18:32.336Z" },
- { url = "https://files.pythonhosted.org/packages/d2/20/5f72d6ba970690df90fdd37195c5caa992e70cb6f203f74cc2bcc0b8cf30/zstandard-0.25.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:1f3689581a72eaba9131b1d9bdbfe520ccd169999219b41000ede2fca5c1bfdb", size = 5043898, upload-time = "2025-09-14T22:18:34.215Z" },
- { url = "https://files.pythonhosted.org/packages/e4/f1/131a0382b8b8d11e84690574645f528f5c5b9343e06cefd77f5fd730cd2b/zstandard-0.25.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d8c56bb4e6c795fc77d74d8e8b80846e1fb8292fc0b5060cd8131d522974b751", size = 5571173, upload-time = "2025-09-14T22:18:36.117Z" },
- { url = "https://files.pythonhosted.org/packages/53/f6/2a37931023f737fd849c5c28def57442bbafadb626da60cf9ed58461fe24/zstandard-0.25.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:53f94448fe5b10ee75d246497168e5825135d54325458c4bfffbaafabcc0a577", size = 4958261, upload-time = "2025-09-14T22:18:38.098Z" },
- { url = "https://files.pythonhosted.org/packages/b5/52/ca76ed6dbfd8845a5563d3af4e972da3b9da8a9308ca6b56b0b929d93e23/zstandard-0.25.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:c2ba942c94e0691467ab901fc51b6f2085ff48f2eea77b1a48240f011e8247c7", size = 5265680, upload-time = "2025-09-14T22:18:39.834Z" },
- { url = "https://files.pythonhosted.org/packages/7a/59/edd117dedb97a768578b49fb2f1156defb839d1aa5b06200a62be943667f/zstandard-0.25.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:07b527a69c1e1c8b5ab1ab14e2afe0675614a09182213f21a0717b62027b5936", size = 5439747, upload-time = "2025-09-14T22:18:41.647Z" },
- { url = "https://files.pythonhosted.org/packages/75/71/c2e9234643dcfbd6c5e975e9a2b0050e1b2afffda6c3a959e1b87997bc80/zstandard-0.25.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:51526324f1b23229001eb3735bc8c94f9c578b1bd9e867a0a646a3b17109f388", size = 5818805, upload-time = "2025-09-14T22:18:43.602Z" },
- { url = "https://files.pythonhosted.org/packages/f5/93/8ebc19f0a31c44ea0e7348f9b0d4b326ed413b6575a3c6ff4ed50222abb6/zstandard-0.25.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:89c4b48479a43f820b749df49cd7ba2dbc2b1b78560ecb5ab52985574fd40b27", size = 5362280, upload-time = "2025-09-14T22:18:45.625Z" },
- { url = "https://files.pythonhosted.org/packages/b8/e9/29cc59d4a9d51b3fd8b477d858d0bd7ab627f700908bf1517f46ddd470ae/zstandard-0.25.0-cp39-cp39-win32.whl", hash = "sha256:1cd5da4d8e8ee0e88be976c294db744773459d51bb32f707a0f166e5ad5c8649", size = 436460, upload-time = "2025-09-14T22:18:49.077Z" },
- { url = "https://files.pythonhosted.org/packages/41/b5/bc7a92c116e2ef32dc8061c209d71e97ff6df37487d7d39adb51a343ee89/zstandard-0.25.0-cp39-cp39-win_amd64.whl", hash = "sha256:37daddd452c0ffb65da00620afb8e17abd4adaae6ce6310702841760c2c26860", size = 506097, upload-time = "2025-09-14T22:18:47.342Z" },
-]
diff --git a/version.py b/version.py
deleted file mode 100644
index 87ec59a..0000000
--- a/version.py
+++ /dev/null
@@ -1,21 +0,0 @@
-"""
-Lang2SQL 패키지의 버전 정보를 정의하는 모듈입니다.
-
-이 모듈은 패키지의 버전을 추적하고 관리하는 데 사용됩니다.
-
-패키지의 버전은 다음과 같은 형식을 따라야 합니다:
-
-MAJOR.MINOR.PATCH
-
-여기서:
-- MAJOR는 큰 변경이 있을 때 증가합니다.
-- MINOR는 새로운 기능이 추가되거나 중요한 변경이 있을 때 증가합니다.
-- PATCH는 버그 수정이 있을 때 증가합니다.
-
-예를 들어, 버전 0.1.0에서 0.1.1로 업그레이드하면:
-- MAJOR는 변경되지 않습니다.
-- MINOR는 변경되지 않습니다.
-- PATCH는 1로 증가합니다.
-"""
-
-__version__ = "0.3.0"