From 3083763a4556bdd1125da95788c17ae1276cdb67 Mon Sep 17 00:00:00 2001 From: Jax Liu Date: Mon, 30 Mar 2026 22:35:43 +0800 Subject: [PATCH 01/16] feat(wren): add LanceDB-backed memory module for schema and query memory Introduce wren.memory with hybrid retrieval strategy: small schemas are returned as full plain text for LLM context, large schemas use embedding search (paraphrase-multilingual-MiniLM-L12-v2, local). - schema_indexer: extract models/columns/relationships/views into indexable records, plus describe_schema() for plain-text output - store: LanceDB CRUD with get_context() hybrid auto-selection based on a 30K-char threshold (~8K tokens) - embeddings: local sentence-transformers via LanceDB registry - WrenMemory public API for programmatic use - 37 unit + integration tests - Optional dependency: pip install wren-engine[memory] Co-Authored-By: Claude Opus 4.6 (1M context) --- wren/pyproject.toml | 3 +- wren/src/wren/memory/__init__.py | 108 ++++++++ wren/src/wren/memory/embeddings.py | 26 ++ wren/src/wren/memory/schema_indexer.py | 268 +++++++++++++++++++ wren/src/wren/memory/store.py | 256 ++++++++++++++++++ wren/tests/unit/test_memory.py | 347 +++++++++++++++++++++++++ 6 files changed, 1007 insertions(+), 1 deletion(-) create mode 100644 wren/src/wren/memory/__init__.py create mode 100644 wren/src/wren/memory/embeddings.py create mode 100644 wren/src/wren/memory/schema_indexer.py create mode 100644 wren/src/wren/memory/store.py create mode 100644 wren/tests/unit/test_memory.py diff --git a/wren/pyproject.toml b/wren/pyproject.toml index 8a5315ca7..6d70b2952 100644 --- a/wren/pyproject.toml +++ b/wren/pyproject.toml @@ -50,8 +50,9 @@ redshift = ["redshift_connector"] spark = ["pyspark>=3.5"] athena = ["ibis-framework[athena]"] oracle = ["ibis-framework[oracle]", "oracledb"] +memory = ["lancedb>=0.6", "sentence-transformers>=2.2"] all = [ - "wren[postgres,mysql,bigquery,snowflake,clickhouse,trino,mssql,databricks,redshift,athena,oracle]", + "wren[postgres,mysql,bigquery,snowflake,clickhouse,trino,mssql,databricks,redshift,athena,oracle,memory]", ] dev = [ "pytest>=8", diff --git a/wren/src/wren/memory/__init__.py b/wren/src/wren/memory/__init__.py new file mode 100644 index 000000000..f4e0728a7 --- /dev/null +++ b/wren/src/wren/memory/__init__.py @@ -0,0 +1,108 @@ +"""Wren Memory — LanceDB-backed schema and query memory. + +Public API for programmatic use:: + + from wren.memory import WrenMemory + + mem = WrenMemory() + mem.index_manifest(manifest_dict) + results = mem.search_schema("customer orders") +""" + +from __future__ import annotations + +from pathlib import Path + + +class WrenMemory: + """High-level memory API for Wren Engine. + + Parameters + ---------- + path: + LanceDB storage directory. Defaults to ``~/.wren/memory/``. + """ + + def __init__(self, path: str | Path | None = None): + from wren.memory.store import MemoryStore # noqa: PLC0415 + + self._store = MemoryStore(path=path) + + def index_manifest(self, manifest: dict, *, replace: bool = True) -> int: + """Index MDL schema into LanceDB. Returns record count.""" + return self._store.index_schema(manifest, replace=replace) + + def search_schema( + self, + query: str, + *, + limit: int = 5, + item_type: str | None = None, + model_name: str | None = None, + ) -> list[dict]: + """Semantic search over indexed schema items.""" + return self._store.search_schema( + query, limit=limit, item_type=item_type, model_name=model_name + ) + + def store_query( + self, + nl_query: str, + sql_query: str, + *, + datasource: str | None = None, + tags: str | None = None, + ) -> None: + """Store a NL→SQL pair for future few-shot retrieval.""" + self._store.store_query(nl_query, sql_query, datasource=datasource, tags=tags) + + def recall_queries( + self, + query: str, + *, + limit: int = 3, + datasource: str | None = None, + ) -> list[dict]: + """Search past NL→SQL pairs by semantic similarity.""" + return self._store.recall_queries(query, limit=limit, datasource=datasource) + + @staticmethod + def describe_schema(manifest: dict) -> str: + """Return the full schema as structured plain text.""" + from wren.memory.schema_indexer import describe_schema # noqa: PLC0415 + + return describe_schema(manifest) + + def get_context( + self, + manifest: dict, + query: str, + *, + limit: int = 5, + threshold: int | None = None, + ) -> dict: + """Return schema context using the best strategy for the schema size. + + Small schemas (below *threshold* chars) are returned as full plain + text. Large schemas use embedding search to return only relevant + fragments. See :data:`~wren.memory.schema_indexer.SCHEMA_DESCRIBE_THRESHOLD`. + """ + kwargs = {"limit": limit} + if threshold is not None: + kwargs["threshold"] = threshold + return self._store.get_context(manifest, query, **kwargs) + + def schema_is_current(self, manifest: dict) -> bool: + """Check if the indexed schema matches the given manifest.""" + return self._store.schema_is_current(manifest) + + def status(self) -> dict: + """Return index statistics (path, table row counts).""" + return self._store.status() + + def reset(self) -> None: + """Drop all memory tables.""" + self._store.reset() + + +__all__ = ["WrenMemory"] diff --git a/wren/src/wren/memory/embeddings.py b/wren/src/wren/memory/embeddings.py new file mode 100644 index 000000000..660831481 --- /dev/null +++ b/wren/src/wren/memory/embeddings.py @@ -0,0 +1,26 @@ +"""Embedding function abstraction for Wren Memory. + +Uses LanceDB's embedding registry with sentence-transformers (local, no API key). +""" + +from __future__ import annotations + +_DEFAULT_MODEL = "paraphrase-multilingual-MiniLM-L12-v2" +_DEFAULT_DIM = 384 + + +def get_embedding_function(model_name: str = _DEFAULT_MODEL): + """Return a LanceDB sentence-transformers embedding function. + + The returned object implements ``compute_source_embeddings(texts)`` + and ``compute_query_embeddings(query)`` used by :class:`MemoryStore`. + """ + import lancedb.embeddings # noqa: PLC0415 + + registry = lancedb.embeddings.get_registry() + return registry.get("sentence-transformers").create(name=model_name) + + +def default_dimension() -> int: + """Return the vector dimension for the default model.""" + return _DEFAULT_DIM diff --git a/wren/src/wren/memory/schema_indexer.py b/wren/src/wren/memory/schema_indexer.py new file mode 100644 index 000000000..d22b5ab57 --- /dev/null +++ b/wren/src/wren/memory/schema_indexer.py @@ -0,0 +1,268 @@ +"""Extract indexable records from an MDL manifest dict. + +Pure functions — no LanceDB or embedding dependency. +""" + +from __future__ import annotations + +import hashlib +import json +from datetime import datetime, timezone + + +def manifest_hash(manifest: dict) -> str: + """Deterministic SHA-256 hash (16 hex chars) of a manifest dict.""" + raw = json.dumps(manifest, sort_keys=True, separators=(",", ":")) + return hashlib.sha256(raw.encode()).hexdigest()[:16] + + +# ~30K chars ≈ ~8K tokens. Below this threshold the full plain-text +# description fits comfortably in a single LLM context window and +# outperforms embedding search because the LLM sees the complete +# schema structure (model→column relationships, join paths, etc.) +# rather than isolated fragments. The threshold is measured in +# characters (not tokens) because character length is free to compute +# after generating the text, while accurate token counting requires a +# tokeniser dependency. The 4:1 chars-to-tokens ratio holds for +# English; CJK text is ~1.5:1, so a CJK-heavy schema will switch to +# embedding search sooner — which is the conservative (safe) direction. +SCHEMA_DESCRIBE_THRESHOLD = 30_000 + + +def describe_schema(manifest: dict) -> str: + """Generate a structured plain-text description of the full MDL schema. + + Designed to be pasted directly into an LLM prompt when the schema is + small enough (see :data:`SCHEMA_DESCRIBE_THRESHOLD`). + """ + lines: list[str] = [] + + catalog = manifest.get("catalog", "") + schema = manifest.get("schema", "") + if catalog or schema: + lines.append(f"Catalog: {catalog}, Schema: {schema}") + lines.append("") + + for model in manifest.get("models", []): + _describe_model(model, lines) + + for rel in manifest.get("relationships", []): + _describe_relationship(rel, lines) + + for view in manifest.get("views", []): + _describe_view(view, lines) + + return "\n".join(lines) + + +def _describe_model(model: dict, lines: list[str]) -> None: + name = model["name"] + desc = _prop_description(model) + header = f"### Model: {name}" + if desc: + header += f" — {desc}" + lines.append(header) + + ref = model.get("tableReference") + if ref: + if isinstance(ref, dict): + ref = ".".join(filter(None, [ref.get("schema"), ref.get("table")])) + lines.append(f" Table: {ref}") + + pk = model.get("primaryKey") + if pk: + lines.append(f" Primary key: {pk}") + + cols = model.get("columns", []) + if cols: + lines.append(" Columns:") + for col in cols: + _describe_column(col, lines) + lines.append("") + + +def _describe_column(col: dict, lines: list[str]) -> None: + name = col["name"] + dtype = col.get("type", "?") + parts = [f" - {name} ({dtype})"] + + desc = _prop_description(col) + if desc: + parts.append(f" — {desc}") + + is_calc = col.get("isCalculated", False) + expr = col.get("expression") + if is_calc and expr: + parts.append(f" [calculated: {expr}]") + + rel = col.get("relationship") + if rel: + parts.append(f" [relationship: {rel}]") + + if col.get("notNull"): + parts.append(" NOT NULL") + + lines.append("".join(parts)) + + +def _describe_relationship(rel: dict, lines: list[str]) -> None: + name = rel["name"] + models = rel.get("models", []) + left = models[0] if len(models) > 0 else "?" + right = models[1] if len(models) > 1 else "?" + join_type = rel.get("joinType", "") + condition = rel.get("condition", "") + lines.append(f"### Relationship: {name}") + lines.append(f" {left} → {right} ({join_type})") + if condition: + lines.append(f" Condition: {condition}") + lines.append("") + + +def _describe_view(view: dict, lines: list[str]) -> None: + name = view["name"] + stmt = view.get("statement", "") + lines.append(f"### View: {name}") + if stmt: + lines.append(f" SQL: {stmt}") + lines.append("") + + +def extract_schema_items(manifest: dict) -> list[dict]: + """Walk an MDL manifest and produce one record per indexable element. + + Each record contains a ``text`` field (synthesised description for + embedding) plus structured metadata columns that match the + ``schema_items`` LanceDB table schema. + """ + now = datetime.now(timezone.utc) + mdl_h = manifest_hash(manifest) + items: list[dict] = [] + + for model in manifest.get("models", []): + items.append(_model_record(model, mdl_h, now)) + for col in model.get("columns", []): + items.append(_column_record(col, model["name"], mdl_h, now)) + + for rel in manifest.get("relationships", []): + items.append(_relationship_record(rel, mdl_h, now)) + + for view in manifest.get("views", []): + items.append(_view_record(view, mdl_h, now)) + + return items + + +# ── Record builders ─────────────────────────────────────────────────────── + + +def _model_record(model: dict, mdl_h: str, now: datetime) -> dict: + name = model["name"] + cols = model.get("columns", []) + col_summaries = ", ".join(f"{c['name']} ({c.get('type', '?')})" for c in cols[:20]) + pk = model.get("primaryKey") or "" + + description = _prop_description(model) + parts = [f"Model '{name}'"] + if description: + parts.append(f": {description}") + parts.append(f". Columns: {col_summaries}") + if pk: + parts.append(f". Primary key: {pk}") + text = "".join(parts) + "." + + return { + "text": text, + "item_type": "model", + "model_name": name, + "item_name": name, + "data_type": None, + "expression": None, + "is_calculated": False, + "mdl_hash": mdl_h, + "indexed_at": now, + } + + +def _column_record(col: dict, model_name: str, mdl_h: str, now: datetime) -> dict: + name = col["name"] + dtype = col.get("type", "") + expr = col.get("expression") or None + is_calc = col.get("isCalculated", False) + rel = col.get("relationship") or None + + description = _prop_description(col) + parts = [f"Column '{name}' ({dtype}) in model '{model_name}'"] + if description: + parts.append(f": {description}") + if is_calc and expr: + parts.append(f". Calculated: {expr}") + if rel: + parts.append(f". Relationship: {rel}") + text = "".join(parts) + "." + + return { + "text": text, + "item_type": "column", + "model_name": model_name, + "item_name": name, + "data_type": dtype or None, + "expression": expr, + "is_calculated": is_calc, + "mdl_hash": mdl_h, + "indexed_at": now, + } + + +def _relationship_record(rel: dict, mdl_h: str, now: datetime) -> dict: + name = rel["name"] + models = rel.get("models", []) + join_type = rel.get("joinType", "") + condition = rel.get("condition", "") + + left = models[0] if len(models) > 0 else "?" + right = models[1] if len(models) > 1 else "?" + text = ( + f"Relationship '{name}': {left} → {right} ({join_type}). " + f"Condition: {condition}." + ) + + return { + "text": text, + "item_type": "relationship", + "model_name": left, + "item_name": name, + "data_type": None, + "expression": condition or None, + "is_calculated": False, + "mdl_hash": mdl_h, + "indexed_at": now, + } + + +def _view_record(view: dict, mdl_h: str, now: datetime) -> dict: + name = view["name"] + stmt = view.get("statement", "") + truncated = stmt[:200] + ("…" if len(stmt) > 200 else "") + + text = f"View '{name}'. SQL: {truncated}" + + return { + "text": text, + "item_type": "view", + "model_name": "", + "item_name": name, + "data_type": None, + "expression": stmt or None, + "is_calculated": False, + "mdl_hash": mdl_h, + "indexed_at": now, + } + + +def _prop_description(obj: dict) -> str: + """Extract description from the ``properties`` dict, if present.""" + props = obj.get("properties") or {} + if isinstance(props, dict): + return props.get("description", "") + return "" diff --git a/wren/src/wren/memory/store.py b/wren/src/wren/memory/store.py new file mode 100644 index 000000000..ca53cfe4a --- /dev/null +++ b/wren/src/wren/memory/store.py @@ -0,0 +1,256 @@ +"""LanceDB-backed memory store for schema items and query history.""" + +from __future__ import annotations + +from datetime import datetime, timezone +from pathlib import Path + +import pyarrow as pa + +from wren.memory.embeddings import _DEFAULT_DIM, _DEFAULT_MODEL, get_embedding_function +from wren.memory.schema_indexer import ( + SCHEMA_DESCRIBE_THRESHOLD, + describe_schema, + extract_schema_items, + manifest_hash, +) + +_WREN_MEMORY_DIR = Path.home() / ".wren" / "memory" + +_SCHEMA_TABLE = "schema_items" +_QUERY_TABLE = "query_history" + + +def _schema_items_arrow_schema(dim: int = _DEFAULT_DIM) -> pa.Schema: + return pa.schema( + [ + pa.field("text", pa.utf8()), + pa.field("vector", pa.list_(pa.float32(), dim)), + pa.field("item_type", pa.utf8()), + pa.field("model_name", pa.utf8()), + pa.field("item_name", pa.utf8()), + pa.field("data_type", pa.utf8()), + pa.field("expression", pa.utf8()), + pa.field("is_calculated", pa.bool_()), + pa.field("mdl_hash", pa.utf8()), + pa.field("indexed_at", pa.timestamp("us", tz="UTC")), + ] + ) + + +def _query_history_arrow_schema(dim: int = _DEFAULT_DIM) -> pa.Schema: + return pa.schema( + [ + pa.field("text", pa.utf8()), + pa.field("vector", pa.list_(pa.float32(), dim)), + pa.field("nl_query", pa.utf8()), + pa.field("sql_query", pa.utf8()), + pa.field("datasource", pa.utf8()), + pa.field("created_at", pa.timestamp("us", tz="UTC")), + pa.field("tags", pa.utf8()), + ] + ) + + +def _table_names(db) -> list[str]: + """Get table names, compatible with lancedb >=0.30 (ListTablesResponse).""" + result = db.list_tables() + if isinstance(result, list): + return result + return result.tables + + +class MemoryStore: + """Manage LanceDB tables for schema and query memory. + + Parameters + ---------- + path: + Directory for LanceDB storage. Defaults to ``~/.wren/memory/``. + model_name: + Sentence-transformers model name. ``None`` → default multilingual model. + """ + + def __init__( + self, + path: str | Path | None = None, + model_name: str | None = None, + ): + import lancedb # noqa: PLC0415 + + resolved = Path(path).expanduser() if path else _WREN_MEMORY_DIR + resolved.mkdir(parents=True, exist_ok=True) + self._path = resolved + self._db = lancedb.connect(str(resolved)) + self._embed_fn = get_embedding_function(model_name or _DEFAULT_MODEL) + + # ── Schema indexing ─────────────────────────────────────────────────── + + def index_schema(self, manifest: dict, *, replace: bool = True) -> int: + """Extract schema items from *manifest*, embed, and store. + + Returns the number of records indexed. + """ + items = extract_schema_items(manifest) + if not items: + return 0 + + texts = [item["text"] for item in items] + vectors = self._embed_fn.compute_source_embeddings(texts) + + for item, vec in zip(items, vectors): + item["vector"] = vec + + if replace and _SCHEMA_TABLE in _table_names(self._db): + self._db.drop_table(_SCHEMA_TABLE) + + self._db.create_table( + _SCHEMA_TABLE, + items, + schema=_schema_items_arrow_schema(), + mode="overwrite", + ) + return len(items) + + def search_schema( + self, + query: str, + *, + limit: int = 5, + item_type: str | None = None, + model_name: str | None = None, + ) -> list[dict]: + """Semantic search over indexed schema items.""" + if _SCHEMA_TABLE not in _table_names(self._db): + return [] + + table = self._db.open_table(_SCHEMA_TABLE) + q = table.search( + self._embed_fn.compute_query_embeddings(query)[0], + ) + + where_parts: list[str] = [] + if item_type: + where_parts.append(f"item_type = '{item_type}'") + if model_name: + where_parts.append(f"model_name = '{model_name}'") + if where_parts: + q = q.where(" AND ".join(where_parts)) + + results = q.limit(limit).to_list() + for r in results: + r.pop("vector", None) + return results + + def schema_is_current(self, manifest: dict) -> bool: + """Check whether the indexed schema matches *manifest*.""" + if _SCHEMA_TABLE not in _table_names(self._db): + return False + table = self._db.open_table(_SCHEMA_TABLE) + current_hash = manifest_hash(manifest) + df = table.to_pandas() + return bool((df["mdl_hash"] == current_hash).any()) + + # ── Plain-text / hybrid ──────────────────────────────────────────────── + + @staticmethod + def describe_schema(manifest: dict) -> str: + """Return the full schema as structured plain text (no embedding).""" + return describe_schema(manifest) + + def get_context( + self, + manifest: dict, + query: str, + *, + limit: int = 5, + threshold: int = SCHEMA_DESCRIBE_THRESHOLD, + ) -> dict: + """Return schema context using the best strategy for the schema size. + + If the plain-text description of *manifest* is shorter than + *threshold* characters, return the full text (``strategy="full"``). + Otherwise, fall back to embedding search (``strategy="search"``). + + Returns a dict with keys ``strategy``, ``schema``, and (for search) + ``results``. + """ + text = describe_schema(manifest) + if len(text) <= threshold: + return {"strategy": "full", "schema": text} + + results = self.search_schema(query, limit=limit) + return {"strategy": "search", "results": results} + + # ── Query history ───────────────────────────────────────────────────── + + def store_query( + self, + nl_query: str, + sql_query: str, + *, + datasource: str | None = None, + tags: str | None = None, + ) -> None: + """Store a NL→SQL pair with embedding of the NL query.""" + now = datetime.now(timezone.utc) + vectors = self._embed_fn.compute_source_embeddings([nl_query]) + + record = { + "text": nl_query, + "vector": vectors[0], + "nl_query": nl_query, + "sql_query": sql_query, + "datasource": datasource or "", + "created_at": now, + "tags": tags or "", + } + + if _QUERY_TABLE in _table_names(self._db): + table = self._db.open_table(_QUERY_TABLE) + table.add([record]) + else: + self._db.create_table( + _QUERY_TABLE, + [record], + schema=_query_history_arrow_schema(), + ) + + def recall_queries( + self, + query: str, + *, + limit: int = 3, + datasource: str | None = None, + ) -> list[dict]: + """Search past NL→SQL pairs by semantic similarity.""" + if _QUERY_TABLE not in _table_names(self._db): + return [] + + table = self._db.open_table(_QUERY_TABLE) + q = table.search( + self._embed_fn.compute_query_embeddings(query)[0], + ) + + if datasource: + q = q.where(f"datasource = '{datasource}'") + + results = q.limit(limit).to_list() + for r in results: + r.pop("vector", None) + return results + + # ── Housekeeping ────────────────────────────────────────────────────── + + def status(self) -> dict: + """Return index statistics.""" + info: dict = {"path": str(self._path), "tables": {}} + for name in _table_names(self._db): + table = self._db.open_table(name) + info["tables"][name] = table.count_rows() + return info + + def reset(self) -> None: + """Drop all tables.""" + for name in list(_table_names(self._db)): + self._db.drop_table(name) diff --git a/wren/tests/unit/test_memory.py b/wren/tests/unit/test_memory.py new file mode 100644 index 000000000..b50471f9b --- /dev/null +++ b/wren/tests/unit/test_memory.py @@ -0,0 +1,347 @@ +"""Unit tests for the wren.memory module.""" + +from __future__ import annotations + +import pytest + +from wren.memory.schema_indexer import ( + describe_schema, + extract_schema_items, + manifest_hash, +) + +# ── Fixtures ────────────────────────────────────────────────────────────── + +_MANIFEST = { + "catalog": "test", + "schema": "public", + "models": [ + { + "name": "orders", + "tableReference": "test.public.orders", + "primaryKey": "o_orderkey", + "columns": [ + {"name": "o_orderkey", "type": "varchar", "isCalculated": False}, + {"name": "o_custkey", "type": "varchar", "isCalculated": False}, + { + "name": "o_totalprice", + "type": "double", + "isCalculated": True, + "expression": "sum(l_extendedprice)", + }, + { + "name": "customer", + "type": "varchar", + "isCalculated": False, + "relationship": "orders_customer", + }, + ], + }, + { + "name": "customer", + "tableReference": "test.public.customer", + "primaryKey": "c_custkey", + "columns": [ + {"name": "c_custkey", "type": "varchar", "isCalculated": False}, + {"name": "c_name", "type": "varchar", "isCalculated": False}, + ], + "properties": {"description": "Customer master data"}, + }, + ], + "relationships": [ + { + "name": "orders_customer", + "models": ["orders", "customer"], + "joinType": "many_to_one", + "condition": "orders.o_custkey = customer.c_custkey", + } + ], + "views": [ + { + "name": "top_customers", + "statement": "SELECT c_name, sum(o_totalprice) FROM orders JOIN customer LIMIT 10", + } + ], +} + + +# ── manifest_hash tests ─────────────────────────────────────────────────── + + +class TestManifestHash: + def test_deterministic(self): + h1 = manifest_hash(_MANIFEST) + h2 = manifest_hash(_MANIFEST) + assert h1 == h2 + assert len(h1) == 16 + + def test_changes_on_modification(self): + modified = {**_MANIFEST, "catalog": "other"} + assert manifest_hash(_MANIFEST) != manifest_hash(modified) + + +# ── extract_schema_items tests ──────────────────────────────────────────── + + +class TestExtractSchemaItems: + def test_total_count(self): + items = extract_schema_items(_MANIFEST) + # 2 models + 6 columns + 1 relationship + 1 view = 10 + assert len(items) == 10 + + def test_item_types(self): + items = extract_schema_items(_MANIFEST) + types = {item["item_type"] for item in items} + assert types == {"model", "column", "relationship", "view"} + + def test_model_record(self): + items = extract_schema_items(_MANIFEST) + models = [i for i in items if i["item_type"] == "model"] + assert len(models) == 2 + orders = next(m for m in models if m["item_name"] == "orders") + assert "o_orderkey" in orders["text"] + assert orders["model_name"] == "orders" + + def test_model_with_description(self): + items = extract_schema_items(_MANIFEST) + models = [i for i in items if i["item_type"] == "model"] + customer = next(m for m in models if m["item_name"] == "customer") + assert "Customer master data" in customer["text"] + + def test_column_calculated(self): + items = extract_schema_items(_MANIFEST) + cols = [i for i in items if i["item_type"] == "column"] + calc = next(c for c in cols if c["item_name"] == "o_totalprice") + assert calc["is_calculated"] is True + assert "sum(l_extendedprice)" in calc["text"] + assert calc["expression"] == "sum(l_extendedprice)" + + def test_column_relationship(self): + items = extract_schema_items(_MANIFEST) + cols = [i for i in items if i["item_type"] == "column"] + rel_col = next(c for c in cols if c["item_name"] == "customer") + assert "orders_customer" in rel_col["text"] + + def test_relationship_record(self): + items = extract_schema_items(_MANIFEST) + rels = [i for i in items if i["item_type"] == "relationship"] + assert len(rels) == 1 + r = rels[0] + assert r["item_name"] == "orders_customer" + assert "many_to_one" in r["text"] + + def test_view_record(self): + items = extract_schema_items(_MANIFEST) + views = [i for i in items if i["item_type"] == "view"] + assert len(views) == 1 + assert "top_customers" in views[0]["text"] + + def test_all_items_have_required_keys(self): + items = extract_schema_items(_MANIFEST) + required = { + "text", + "item_type", + "model_name", + "item_name", + "mdl_hash", + "indexed_at", + } + for item in items: + assert required.issubset(item.keys()), f"Missing keys in {item}" + + def test_empty_manifest(self): + items = extract_schema_items({}) + assert items == [] + + def test_manifest_without_optional_sections(self): + minimal = {"models": [{"name": "t1", "columns": []}]} + items = extract_schema_items(minimal) + assert len(items) == 1 + assert items[0]["item_type"] == "model" + + +# ── describe_schema tests ───────────────────────────────────────────────── + + +class TestDescribeSchema: + def test_contains_model_names(self): + text = describe_schema(_MANIFEST) + assert "### Model: orders" in text + assert "### Model: customer" in text + + def test_contains_columns(self): + text = describe_schema(_MANIFEST) + assert "o_orderkey (varchar)" in text + assert "o_totalprice (double)" in text + + def test_contains_calculated_expression(self): + text = describe_schema(_MANIFEST) + assert "[calculated: sum(l_extendedprice)]" in text + + def test_contains_relationship_column(self): + text = describe_schema(_MANIFEST) + assert "[relationship: orders_customer]" in text + + def test_contains_relationship_section(self): + text = describe_schema(_MANIFEST) + assert "### Relationship: orders_customer" in text + assert "many_to_one" in text + + def test_contains_view(self): + text = describe_schema(_MANIFEST) + assert "### View: top_customers" in text + + def test_contains_description(self): + text = describe_schema(_MANIFEST) + assert "Customer master data" in text + + def test_contains_primary_key(self): + text = describe_schema(_MANIFEST) + assert "Primary key: o_orderkey" in text + + def test_empty_manifest(self): + text = describe_schema({}) + assert text == "" + + def test_return_type_is_string(self): + text = describe_schema(_MANIFEST) + assert isinstance(text, str) + assert len(text) > 0 + + +# ── MemoryStore integration tests ───────────────────────────────────────── +# These require lancedb + sentence-transformers (wren[memory] extra). + + +@pytest.fixture +def memory_store(tmp_path): + """Create a MemoryStore backed by a temp directory.""" + try: + from wren.memory.store import MemoryStore # noqa: PLC0415 + except ImportError: + pytest.skip("wren[memory] extras not installed") + return MemoryStore(path=tmp_path) + + +@pytest.mark.unit +class TestMemoryStore: + def test_index_and_search(self, memory_store): + count = memory_store.index_schema(_MANIFEST) + assert count == 10 + + results = memory_store.search_schema("customer order price", limit=3) + assert len(results) > 0 + assert "text" in results[0] + + def test_search_with_type_filter(self, memory_store): + memory_store.index_schema(_MANIFEST) + results = memory_store.search_schema("order", item_type="model", limit=5) + assert all(r["item_type"] == "model" for r in results) + + def test_search_with_model_filter(self, memory_store): + memory_store.index_schema(_MANIFEST) + results = memory_store.search_schema("price", model_name="orders", limit=10) + assert all(r["model_name"] == "orders" for r in results) + + def test_search_empty_store(self, memory_store): + results = memory_store.search_schema("anything") + assert results == [] + + def test_schema_is_current(self, memory_store): + memory_store.index_schema(_MANIFEST) + assert memory_store.schema_is_current(_MANIFEST) is True + + modified = {**_MANIFEST, "catalog": "changed"} + assert memory_store.schema_is_current(modified) is False + + def test_store_and_recall_query(self, memory_store): + memory_store.store_query( + nl_query="show top customers by revenue", + sql_query="SELECT c_name, sum(o_totalprice) FROM orders GROUP BY 1 ORDER BY 2 DESC", + datasource="postgres", + ) + results = memory_store.recall_queries("best customers", limit=1) + assert len(results) == 1 + assert "top customers" in results[0]["nl_query"] + assert "SELECT" in results[0]["sql_query"] + + def test_recall_empty_store(self, memory_store): + results = memory_store.recall_queries("anything") + assert results == [] + + def test_status(self, memory_store): + info = memory_store.status() + assert "path" in info + assert "tables" in info + + memory_store.index_schema(_MANIFEST) + info = memory_store.status() + assert info["tables"]["schema_items"] == 10 + + def test_reset(self, memory_store): + memory_store.index_schema(_MANIFEST) + memory_store.reset() + info = memory_store.status() + assert info["tables"] == {} + + def test_get_context_full_for_small_schema(self, memory_store): + memory_store.index_schema(_MANIFEST) + result = memory_store.get_context(_MANIFEST, "customer orders") + assert result["strategy"] == "full" + assert "### Model: orders" in result["schema"] + + def test_get_context_search_for_large_schema(self, memory_store): + memory_store.index_schema(_MANIFEST) + result = memory_store.get_context(_MANIFEST, "customer orders", threshold=10) + assert result["strategy"] == "search" + assert "results" in result + assert len(result["results"]) > 0 + + def test_describe_schema_static(self, memory_store): + text = memory_store.describe_schema(_MANIFEST) + assert "### Model: orders" in text + + def test_index_replace(self, memory_store): + memory_store.index_schema(_MANIFEST) + count = memory_store.index_schema(_MANIFEST, replace=True) + assert count == 10 + info = memory_store.status() + assert info["tables"]["schema_items"] == 10 + + +# ── WrenMemory public API tests ─────────────────────────────────────────── + + +@pytest.fixture +def wren_memory(tmp_path): + try: + from wren.memory import WrenMemory # noqa: PLC0415 + except ImportError: + pytest.skip("wren[memory] extras not installed") + return WrenMemory(path=tmp_path) + + +@pytest.mark.unit +class TestWrenMemory: + def test_full_lifecycle(self, wren_memory): + count = wren_memory.index_manifest(_MANIFEST) + assert count == 10 + + results = wren_memory.search_schema("customer") + assert len(results) > 0 + + wren_memory.store_query( + nl_query="find expensive orders", + sql_query="SELECT * FROM orders WHERE o_totalprice > 1000", + ) + recalled = wren_memory.recall_queries("costly orders") + assert len(recalled) == 1 + + assert wren_memory.schema_is_current(_MANIFEST) + + status = wren_memory.status() + assert status["tables"]["schema_items"] == 10 + assert status["tables"]["query_history"] == 1 + + wren_memory.reset() + assert wren_memory.status()["tables"] == {} From 91c55026c4450d7d36b8020d637155932989451d Mon Sep 17 00:00:00 2001 From: Jax Liu Date: Mon, 30 Mar 2026 22:36:04 +0800 Subject: [PATCH 02/16] feat(wren): add CLI commands for memory module Register wren memory sub-app with commands: index, describe, context, search, store, recall, status, reset. Gracefully no-ops when wren[memory] extra is not installed. Co-Authored-By: Claude Opus 4.6 (1M context) --- wren/src/wren/cli.py | 8 ++ wren/src/wren/memory/cli.py | 246 ++++++++++++++++++++++++++++++++++++ 2 files changed, 254 insertions(+) create mode 100644 wren/src/wren/memory/cli.py diff --git a/wren/src/wren/cli.py b/wren/src/wren/cli.py index 7fc069275..bfabcdea6 100644 --- a/wren/src/wren/cli.py +++ b/wren/src/wren/cli.py @@ -359,5 +359,13 @@ def _print_result(table, output: str) -> None: typer.echo(str(table)) +try: + from wren.memory.cli import memory_app # noqa: PLC0415 + + app.add_typer(memory_app) +except ImportError: + pass # wren[memory] not installed + + if __name__ == "__main__": app() diff --git a/wren/src/wren/memory/cli.py b/wren/src/wren/memory/cli.py new file mode 100644 index 000000000..a888d2a36 --- /dev/null +++ b/wren/src/wren/memory/cli.py @@ -0,0 +1,246 @@ +"""Typer sub-app for ``wren memory`` commands.""" + +from __future__ import annotations + +import json +from pathlib import Path +from typing import Annotated, Optional + +import typer + +memory_app = typer.Typer( + name="memory", + help="Schema and query memory backed by LanceDB.", +) + +_WREN_HOME = Path.home() / ".wren" +_DEFAULT_MDL = _WREN_HOME / "mdl.json" +_DEFAULT_MEMORY = _WREN_HOME / "memory" + +# ── Shared option types ─────────────────────────────────────────────────── + +PathOpt = Annotated[ + Optional[str], + typer.Option( + "--path", + "-p", + help=f"LanceDB storage directory. Defaults to {_DEFAULT_MEMORY}.", + ), +] +MdlOpt = Annotated[ + Optional[str], + typer.Option( + "--mdl", + "-m", + help=f"Path to MDL JSON file. Defaults to {_DEFAULT_MDL}.", + ), +] +OutputOpt = Annotated[ + str, typer.Option("--output", "-o", help="Output format: json|table") +] + + +# ── Helpers ─────────────────────────────────────────────────────────────── + + +def _load_manifest(mdl: str | None) -> dict: + """Load and return the MDL manifest as a dict.""" + mdl_path = Path(mdl).expanduser() if mdl else _DEFAULT_MDL + if not mdl_path.exists(): + typer.echo( + f"Error: MDL file not found: {mdl_path}", + err=True, + ) + raise typer.Exit(1) + try: + return json.loads(mdl_path.read_text()) + except json.JSONDecodeError as e: + typer.echo(f"Error: invalid JSON in {mdl_path}: {e}", err=True) + raise typer.Exit(1) + + +def _get_store(path: str | None): + """Lazy-import and construct a MemoryStore.""" + try: + from wren.memory.store import MemoryStore # noqa: PLC0415 + except ImportError: + typer.echo( + "Error: wren[memory] extras not installed. " + "Run: pip install 'wren-engine[memory]'", + err=True, + ) + raise typer.Exit(1) + return MemoryStore(path=path) + + +def _print_results(results: list[dict], output: str) -> None: + """Format and print search results.""" + if not results: + typer.echo("No results found.") + return + + output = output.lower() + if output == "json": + for r in results: + # Convert datetime objects to ISO strings for JSON serialization + for k, v in r.items(): + if hasattr(v, "isoformat"): + r[k] = v.isoformat() + typer.echo(json.dumps(results, indent=2, ensure_ascii=False)) + else: + try: + import pandas as pd # noqa: PLC0415 + + df = pd.DataFrame(results) + # Drop noisy columns for table display + for col in ("vector", "_rowid"): + if col in df.columns: + df = df.drop(columns=[col]) + typer.echo(df.to_string(index=False)) + except Exception: + for r in results: + typer.echo(str(r)) + + +# ── Commands ────────────────────────────────────────────────────────────── + + +@memory_app.command() +def index( + mdl: MdlOpt = None, + path: PathOpt = None, +) -> None: + """Index MDL schema into LanceDB for semantic search.""" + manifest = _load_manifest(mdl) + store = _get_store(path) + count = store.index_schema(manifest) + typer.echo(f"Indexed {count} schema items.") + + +@memory_app.command() +def describe( + mdl: MdlOpt = None, +) -> None: + """Print the full schema as structured plain text (no embedding needed).""" + from wren.memory.schema_indexer import describe_schema # noqa: PLC0415 + + manifest = _load_manifest(mdl) + text = describe_schema(manifest) + typer.echo(text) + + +@memory_app.command() +def context( + query: Annotated[str, typer.Option("--query", "-q", help="Search query")], + mdl: MdlOpt = None, + limit: Annotated[int, typer.Option("--limit", "-l")] = 5, + threshold: Annotated[ + Optional[int], + typer.Option( + "--threshold", help="Character threshold for full vs search strategy" + ), + ] = None, + path: PathOpt = None, + output: OutputOpt = "table", +) -> None: + """Get schema context using the best strategy for the schema size. + + Small schemas are returned as full plain text. Large schemas use + embedding search to return only relevant fragments. + """ + manifest = _load_manifest(mdl) + store = _get_store(path) + kwargs: dict = {"limit": limit} + if threshold is not None: + kwargs["threshold"] = threshold + result = store.get_context(manifest, query, **kwargs) + strategy = result["strategy"] + typer.echo(f"Strategy: {strategy}") + if strategy == "full": + typer.echo(result["schema"]) + else: + _print_results(result["results"], output) + + +@memory_app.command() +def search( + query: Annotated[str, typer.Option("--query", "-q", help="Search query")], + limit: Annotated[int, typer.Option("--limit", "-l")] = 5, + item_type: Annotated[ + Optional[str], + typer.Option("--type", "-t", help="Filter: model|column|relationship|view"), + ] = None, + model_name: Annotated[ + Optional[str], + typer.Option("--model", help="Filter by model name"), + ] = None, + path: PathOpt = None, + output: OutputOpt = "table", +) -> None: + """Semantic search over indexed schema items.""" + store = _get_store(path) + results = store.search_schema( + query, limit=limit, item_type=item_type, model_name=model_name + ) + _print_results(results, output) + + +@memory_app.command() +def store( + nl: Annotated[str, typer.Option("--nl", help="Natural language query")], + sql: Annotated[str, typer.Option("--sql", help="Corresponding SQL query")], + datasource: Annotated[Optional[str], typer.Option("--datasource", "-d")] = None, + tags: Annotated[Optional[str], typer.Option("--tags")] = None, + path: PathOpt = None, +) -> None: + """Store a NL→SQL pair for future few-shot retrieval.""" + mem_store = _get_store(path) + mem_store.store_query(nl, sql, datasource=datasource, tags=tags) + typer.echo("Query stored.") + + +@memory_app.command() +def recall( + query: Annotated[str, typer.Option("--query", "-q", help="Search query")], + limit: Annotated[int, typer.Option("--limit", "-l")] = 3, + datasource: Annotated[Optional[str], typer.Option("--datasource", "-d")] = None, + path: PathOpt = None, + output: OutputOpt = "table", +) -> None: + """Search past NL→SQL pairs by semantic similarity.""" + mem_store = _get_store(path) + results = mem_store.recall_queries(query, limit=limit, datasource=datasource) + _print_results(results, output) + + +@memory_app.command() +def status( + path: PathOpt = None, +) -> None: + """Show memory index statistics.""" + mem_store = _get_store(path) + info = mem_store.status() + typer.echo(f"Path: {info['path']}") + tables = info.get("tables", {}) + if not tables: + typer.echo("No tables indexed yet.") + return + for name, count in tables.items(): + typer.echo(f" {name}: {count} rows") + + +@memory_app.command() +def reset( + path: PathOpt = None, + force: Annotated[ + bool, typer.Option("--force", "-f", help="Skip confirmation") + ] = False, +) -> None: + """Drop all memory tables and start fresh.""" + if not force: + confirm = typer.confirm("This will delete all indexed memory. Continue?") + if not confirm: + raise typer.Abort() + mem_store = _get_store(path) + mem_store.reset() + typer.echo("Memory reset.") From ac1585f8871631b8b4b80dd784579aaac7850359 Mon Sep 17 00:00:00 2001 From: Jax Liu Date: Mon, 30 Mar 2026 22:36:18 +0800 Subject: [PATCH 03/16] docs(wren): document memory CLI commands and hybrid strategy Add wren memory section to cli.md with hybrid strategy explanation (30K-char threshold, char-based measurement rationale, CJK handling). Document all 8 subcommands with flags and examples. Update README with memory extra installation and quick-start step. Co-Authored-By: Claude Opus 4.6 (1M context) --- wren/README.md | 12 ++++- wren/docs/cli.md | 131 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 142 insertions(+), 1 deletion(-) diff --git a/wren/README.md b/wren/README.md index ae5b9aa37..65d28673f 100644 --- a/wren/README.md +++ b/wren/README.md @@ -10,7 +10,8 @@ Translate natural SQL queries through an MDL (Modeling Definition Language) sema pip install wren-engine[mysql] # MySQL pip install wren-engine[postgres] # PostgreSQL pip install wren-engine[duckdb] # DuckDB (local files) -pip install wren-engine[all] # All connectors +pip install wren-engine[memory] # Schema & query memory (LanceDB) +pip install wren-engine[all] # All connectors + memory ``` ## Quick start @@ -58,6 +59,15 @@ wren --sql 'SELECT order_id FROM "orders" LIMIT 10' For the full CLI reference and per-datasource `connection_info.json` formats, see [`docs/cli.md`](docs/cli.md) and [`docs/connections.md`](docs/connections.md). +**4. Index schema for semantic search** (optional, requires `wren-engine[memory]`): + +```bash +wren memory index # index MDL schema +wren memory search -q "customer order price" # find relevant models/columns +wren memory store --nl "top customers" --sql "SELECT ..." # store NL→SQL pair +wren memory recall -q "best customers" # retrieve similar past queries +``` + --- ## Python SDK diff --git a/wren/docs/cli.md b/wren/docs/cli.md index 2bb353e61..3febf2be1 100644 --- a/wren/docs/cli.md +++ b/wren/docs/cli.md @@ -62,3 +62,134 @@ Or pass connection info inline: wren --sql 'SELECT COUNT(*) FROM "orders"' \ --connection-info '{"datasource":"mysql","host":"localhost","port":3306,"database":"mydb","user":"root","password":"secret"}' ``` + +--- + +## `wren memory` — Schema & Query Memory + +LanceDB-backed semantic memory for MDL schema search and NL→SQL retrieval. Requires the `memory` extra: + +```bash +pip install 'wren-engine[memory]' +``` + +All `memory` subcommands accept `--path DIR` to override the default storage location (`~/.wren/memory/`). + +### Hybrid strategy: full text vs. embedding search + +When providing schema context to an LLM, there is a trade-off: + +- **Small schemas** — the full plain-text description fits easily in the LLM context window and gives better results because the LLM sees the complete structure (model→column relationships, join paths, primary keys) rather than isolated fragments from a vector search. +- **Large schemas** — the full text exceeds what is practical to send in a single prompt, so embedding search is needed to retrieve only the relevant fragments. + +`wren memory context` automatically picks the right strategy based on the **character length** of the generated plain-text description: + +| Schema size | Threshold | Strategy | +|---|---|---| +| Below 30,000 chars (~8K tokens) | Default | Returns full plain text | +| Above 30,000 chars | Default | Returns embedding search results | + +The threshold is measured in characters (not tokens) because character length is free to compute, while accurate token counting requires a tokeniser. The 4:1 chars-to-tokens ratio holds for English; CJK text compresses less (~1.5:1), so a CJK-heavy schema switches to embedding search sooner — which is the conservative direction. + +The default threshold (30,000 chars) can be overridden with `--threshold`. + +### `wren memory index` + +Parse the MDL manifest and index all schema items (models, columns, relationships, views) into LanceDB with local embeddings. + +```bash +wren memory index # uses ~/.wren/mdl.json +wren memory index --mdl /path/to/mdl.json # explicit MDL file +``` + +### `wren memory describe` + +Print the full schema as structured plain text. No embedding or LanceDB required — this is a pure transformation of the MDL manifest into a human/LLM-readable format. + +```bash +wren memory describe # uses ~/.wren/mdl.json +wren memory describe --mdl /path/to/mdl.json +``` + +### `wren memory context` + +Get schema context using the best strategy for the schema size. Automatically chooses between full plain text and embedding search based on the threshold. + +```bash +wren memory context -q "customer order price" +wren memory context -q "revenue" --threshold 50000 +wren memory context -q "日期" --output json +``` + +| Flag | Description | +|------|-------------| +| `-q, --query` | Search query (required) | +| `--mdl` | Path to MDL JSON file | +| `-l, --limit` | Max results for search strategy (default: 5) | +| `--threshold` | Character threshold for full vs search (default: 30,000) | +| `-o, --output` | Output format: `table` (default), `json` | + +### `wren memory search` + +Semantic search over the indexed schema. Returns the most relevant models, columns, etc. for a natural language query. + +```bash +wren memory search -q "customer order price" +wren memory search -q "revenue" --type column --limit 3 +wren memory search -q "日期欄位" --model orders --output json +``` + +| Flag | Description | +|------|-------------| +| `-q, --query` | Search query (required) | +| `-l, --limit` | Max results (default: 5) | +| `-t, --type` | Filter by item type: `model`, `column`, `relationship`, `view` | +| `--model` | Filter by model name | +| `-o, --output` | Output format: `table` (default), `json` | + +### `wren memory store` + +Store a natural-language-to-SQL pair for future few-shot retrieval. + +```bash +wren memory store \ + --nl "show top customers by revenue" \ + --sql "SELECT c_name, sum(o_totalprice) FROM orders JOIN customer GROUP BY 1 ORDER BY 2 DESC" \ + --datasource postgres +``` + +### `wren memory recall` + +Search stored NL→SQL pairs by semantic similarity to a query. + +```bash +wren memory recall -q "best customers" +wren memory recall -q "月度營收" --datasource mysql --limit 5 --output json +``` + +| Flag | Description | +|------|-------------| +| `-q, --query` | Search query (required) | +| `-l, --limit` | Max results (default: 3) | +| `-d, --datasource` | Filter by data source | +| `-o, --output` | Output format: `table` (default), `json` | + +### `wren memory status` + +Show index statistics: storage path, table names, and row counts. + +```bash +wren memory status +# Path: /Users/you/.wren/memory +# schema_items: 47 rows +# query_history: 12 rows +``` + +### `wren memory reset` + +Drop all memory tables and start fresh. + +```bash +wren memory reset # prompts for confirmation +wren memory reset --force # skip confirmation +``` From f35ff7d1c09faefff7f8fdf91ef32a47ee1ee36e Mon Sep 17 00:00:00 2001 From: Jax Liu Date: Mon, 30 Mar 2026 23:10:34 +0800 Subject: [PATCH 04/16] feat(skills): add wren-memory skill for agent memory guidance MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Teach agents when to index schema, search for context, store confirmed NL-to-SQL pairs, and recall past queries. Key principle: the agent stores only after user confirmation — query success alone does not imply correctness. Also registers wren-memory in index.json, versions.json, and adds it to wren-usage delegation table and dependencies. Co-Authored-By: Claude Opus 4.6 (1M context) --- skills/index.json | 16 ++++ skills/versions.json | 3 +- skills/wren-memory/SKILL.md | 172 ++++++++++++++++++++++++++++++++++++ skills/wren-usage/SKILL.md | 4 +- 4 files changed, 193 insertions(+), 2 deletions(-) create mode 100644 skills/wren-memory/SKILL.md diff --git a/skills/index.json b/skills/index.json index 4f1ca26c0..41b57ee04 100644 --- a/skills/index.json +++ b/skills/index.json @@ -112,6 +112,21 @@ ], "repository": "https://github.com/Canner/wren-engine/tree/main/skills/wren-http-api" }, + { + "name": "wren-memory", + "version": "1.0", + "description": "Schema and query memory for Wren Engine — index MDL schema for semantic search, store and recall NL-to-SQL pairs for few-shot retrieval.", + "tags": [ + "wren", + "memory", + "lancedb", + "embedding", + "semantic-search", + "few-shot", + "text-to-sql" + ], + "repository": "https://github.com/Canner/wren-engine/tree/main/skills/wren-memory" + }, { "name": "wren-usage", "version": "1.2", @@ -132,6 +147,7 @@ "wren-generate-mdl", "wren-project", "wren-sql", + "wren-memory", "wren-mcp-setup", "wren-http-api" ], diff --git a/skills/versions.json b/skills/versions.json index 67b174e04..f986be01f 100644 --- a/skills/versions.json +++ b/skills/versions.json @@ -6,5 +6,6 @@ "wren-mcp-setup": "1.4", "wren-quickstart": "1.3", "wren-http-api": "1.0", - "wren-usage": "1.2" + "wren-usage": "1.2", + "wren-memory": "1.0" } diff --git a/skills/wren-memory/SKILL.md b/skills/wren-memory/SKILL.md new file mode 100644 index 000000000..22d70ea21 --- /dev/null +++ b/skills/wren-memory/SKILL.md @@ -0,0 +1,172 @@ +--- +name: wren-memory +description: "Schema and query memory for Wren Engine — index MDL schema for semantic search, store and recall NL-to-SQL pairs for few-shot retrieval. Guides the agent on when to index, search, store, and recall using the wren memory CLI. Use when: the agent needs schema context before writing SQL, wants to find relevant models/columns by natural language, or should store a confirmed-correct query for future reference." +license: Apache-2.0 +metadata: + author: wren-engine + version: "1.0" +--- + +# Wren Memory + +## Version check + +Silently fetch `https://raw.githubusercontent.com/Canner/wren-engine/main/skills/versions.json`. +Compare the `wren-memory` key with this skill's version (from the frontmatter above). +If the remote version is newer, notify the user before proceeding: + +> A newer version of the **wren-memory** skill is available. +> Update with: +> ``` +> npx skills add Canner/wren-engine --skill wren-memory --agent claude-code +> ``` + +Then continue with the workflow below regardless of update status. + +--- + +## What is Wren Memory? + +Wren Memory is a LanceDB-backed local memory layer for the `wren` CLI. It provides two capabilities: + +1. **Schema Memory** — index MDL models, columns, relationships, and views for semantic search +2. **Query Memory** — store confirmed NL-to-SQL pairs for few-shot retrieval + +Requires the `memory` extra: `pip install 'wren-engine[memory]'` + +--- + +## When to use each command + +### Schema context: `context` > `describe` > `search` + +Before writing SQL, you need schema context. Choose the right command: + +| Command | When to use | +|---------|-------------| +| `wren memory context -q "..."` | **Default choice.** Auto-selects full text or embedding search based on schema size. | +| `wren memory describe` | When you want the full schema and know it is small. | +| `wren memory search -q "..."` | When you need fine-grained filtering (by item type or model name). | + +**Workflow — before writing SQL:** + +``` +1. User asks a question in natural language +2. Run: wren memory context -q "" --mdl ~/.wren/mdl.json +3. Use the returned schema context to write SQL +4. If context strategy is "search" and results are insufficient, + try a broader query or different keywords +``` + +### Indexing: `index` + +Index the MDL schema so that `search` and `context` (search strategy) work. + +**When to index:** +- After deploying a new or updated MDL (`wren memory index`) +- When `wren memory status` shows `schema_items: 0 rows` +- When the MDL has changed and the schema memory is stale + +**When NOT to index:** +- Before every query — indexing is expensive, do it once per MDL change +- When only using `describe` or `context` with full strategy — those read the MDL directly + +```bash +wren memory index --mdl ~/.wren/mdl.json +``` + +### Storing queries: `store` + +Store a NL-to-SQL pair so it can be recalled later as a few-shot example. + +**When to store — ALL of these must be true:** +1. The SQL query executed successfully +2. The user confirmed the result is correct, OR the user continued working with the result (asked a follow-up question, exported the data, etc.) +3. There is a clear natural language question that the SQL answers + +**When NOT to store:** +- The query failed or returned an error +- The user said the result is wrong or asked to fix it +- The query is exploratory / throwaway (e.g. `SELECT * FROM orders LIMIT 5`) +- There is no natural language question — just raw SQL from the user +- The user explicitly asked not to store it + +```bash +wren memory store \ + --nl "top 5 customers by revenue last quarter" \ + --sql "SELECT c_name, SUM(o_totalprice) AS revenue FROM orders JOIN customer ON orders.o_custkey = customer.c_custkey WHERE o_orderdate >= DATE '2025-10-01' GROUP BY 1 ORDER BY 2 DESC LIMIT 5" \ + --datasource postgres +``` + +**Important:** The `--nl` value should be the user's original question, not a paraphrase. This ensures the embedding matches future similar questions. + +### Recalling queries: `recall` + +Search past NL-to-SQL pairs for few-shot examples before writing new SQL. + +**When to recall:** +- Before writing SQL for a new question, especially if it is complex +- When the user asks something similar to a past question +- When you want few-shot examples to improve SQL quality + +**Workflow — SQL generation with memory:** + +``` +1. User asks: "show me monthly revenue trend for the top 3 product categories" +2. Run: wren memory recall -q "monthly revenue trend by product category" --limit 3 +3. Run: wren memory context -q "revenue product category monthly" --mdl ~/.wren/mdl.json +4. Use recalled queries as few-shot examples + schema context to write SQL +5. Execute the SQL +6. If user confirms result → store the new pair +``` + +### Housekeeping: `status` and `reset` + +```bash +wren memory status # check what is indexed +wren memory reset --force # clear everything and start fresh +``` + +--- + +## Complete workflow example + +Here is the full lifecycle for a typical agent session: + +``` +Session start: + 1. Check: wren memory status + → If schema_items is 0 or MDL has changed: wren memory index + +User asks a question: + 2. Recall: wren memory recall -q "" --limit 3 + → Use any results as few-shot examples + 3. Context: wren memory context -q "" + → Get relevant schema information + 4. Write SQL using recalled examples + schema context + 5. Execute: wren query --sql "..." + +After successful execution: + 6. Show results to user + 7. Wait for user's response: + - User confirms / continues → wren memory store --nl "..." --sql "..." + - User says "wrong" / asks to fix → do NOT store, fix the SQL instead + - User says nothing and moves on → do NOT store (silence ≠ confirmation) +``` + +--- + +## CLI quick reference + +| Command | Purpose | +|---------|---------| +| `wren memory index [--mdl PATH]` | Index MDL schema into LanceDB | +| `wren memory describe [--mdl PATH]` | Print full schema as plain text | +| `wren memory context -q "..." [--mdl PATH]` | Get schema context (auto full/search) | +| `wren memory search -q "..." [--type T] [--model M]` | Semantic search over schema | +| `wren memory store --nl "..." --sql "..."` | Store a confirmed NL-to-SQL pair | +| `wren memory recall -q "..." [--limit N]` | Recall similar past queries | +| `wren memory status` | Show index statistics | +| `wren memory reset [--force]` | Drop all memory tables | + +All commands accept `--path DIR` to override the default storage location (`~/.wren/memory/`). diff --git a/skills/wren-usage/SKILL.md b/skills/wren-usage/SKILL.md index 4b25573a8..7e8d0b926 100644 --- a/skills/wren-usage/SKILL.md +++ b/skills/wren-usage/SKILL.md @@ -41,7 +41,7 @@ npx skills add Canner/wren-engine --skill '*' --agent claude-code clawhub install wren-usage ``` -This installs `wren-usage` and its dependent skills (`wren-connection-info`, `wren-generate-mdl`, `wren-project`, `wren-sql`, `wren-mcp-setup`, `wren-http-api`) into `~/.claude/skills/`. +This installs `wren-usage` and its dependent skills (`wren-connection-info`, `wren-generate-mdl`, `wren-project`, `wren-sql`, `wren-memory`, `wren-mcp-setup`, `wren-http-api`) into `~/.claude/skills/`. After installation, the user must **start a new session** for the new skills to be loaded. @@ -56,6 +56,8 @@ Identify the user's intent and delegate to the appropriate skill: | Task | Skill | |------|-------| | Write or debug a SQL query | `@wren-sql` | +| Get schema context / find relevant models before writing SQL | `@wren-memory` | +| Store a confirmed NL-to-SQL pair / recall past queries | `@wren-memory` | | Connect to a new database / change credentials | `@wren-connection-info` | | Generate MDL from an existing database | `@wren-generate-mdl` | | Save MDL to YAML files (version control) | `@wren-project` | From 21ec21661ee4b203d0a06f0c7c982dd9653eeca5 Mon Sep 17 00:00:00 2001 From: Jax Liu Date: Mon, 30 Mar 2026 23:35:09 +0800 Subject: [PATCH 05/16] feat(skills): add cli-skills/ for standalone wren CLI agent workflows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Separate from the Docker-based MCP skills in skills/, these are for agents using the wren CLI directly. - wren-usage: decision-driven workflow guide covering 4 main flows: answering data questions, error recovery, data source onboarding, and MDL change handling. Teaches agents when/why to run each command, not just how. - wren-memory: copied from skills/wren-memory — memory-specific decisions (when to store, recall, index). Co-Authored-By: Claude Opus 4.6 (1M context) --- cli-skills/wren-memory/SKILL.md | 172 ++++++++++++++++++++++ cli-skills/wren-usage/SKILL.md | 245 ++++++++++++++++++++++++++++++++ 2 files changed, 417 insertions(+) create mode 100644 cli-skills/wren-memory/SKILL.md create mode 100644 cli-skills/wren-usage/SKILL.md diff --git a/cli-skills/wren-memory/SKILL.md b/cli-skills/wren-memory/SKILL.md new file mode 100644 index 000000000..22d70ea21 --- /dev/null +++ b/cli-skills/wren-memory/SKILL.md @@ -0,0 +1,172 @@ +--- +name: wren-memory +description: "Schema and query memory for Wren Engine — index MDL schema for semantic search, store and recall NL-to-SQL pairs for few-shot retrieval. Guides the agent on when to index, search, store, and recall using the wren memory CLI. Use when: the agent needs schema context before writing SQL, wants to find relevant models/columns by natural language, or should store a confirmed-correct query for future reference." +license: Apache-2.0 +metadata: + author: wren-engine + version: "1.0" +--- + +# Wren Memory + +## Version check + +Silently fetch `https://raw.githubusercontent.com/Canner/wren-engine/main/skills/versions.json`. +Compare the `wren-memory` key with this skill's version (from the frontmatter above). +If the remote version is newer, notify the user before proceeding: + +> A newer version of the **wren-memory** skill is available. +> Update with: +> ``` +> npx skills add Canner/wren-engine --skill wren-memory --agent claude-code +> ``` + +Then continue with the workflow below regardless of update status. + +--- + +## What is Wren Memory? + +Wren Memory is a LanceDB-backed local memory layer for the `wren` CLI. It provides two capabilities: + +1. **Schema Memory** — index MDL models, columns, relationships, and views for semantic search +2. **Query Memory** — store confirmed NL-to-SQL pairs for few-shot retrieval + +Requires the `memory` extra: `pip install 'wren-engine[memory]'` + +--- + +## When to use each command + +### Schema context: `context` > `describe` > `search` + +Before writing SQL, you need schema context. Choose the right command: + +| Command | When to use | +|---------|-------------| +| `wren memory context -q "..."` | **Default choice.** Auto-selects full text or embedding search based on schema size. | +| `wren memory describe` | When you want the full schema and know it is small. | +| `wren memory search -q "..."` | When you need fine-grained filtering (by item type or model name). | + +**Workflow — before writing SQL:** + +``` +1. User asks a question in natural language +2. Run: wren memory context -q "" --mdl ~/.wren/mdl.json +3. Use the returned schema context to write SQL +4. If context strategy is "search" and results are insufficient, + try a broader query or different keywords +``` + +### Indexing: `index` + +Index the MDL schema so that `search` and `context` (search strategy) work. + +**When to index:** +- After deploying a new or updated MDL (`wren memory index`) +- When `wren memory status` shows `schema_items: 0 rows` +- When the MDL has changed and the schema memory is stale + +**When NOT to index:** +- Before every query — indexing is expensive, do it once per MDL change +- When only using `describe` or `context` with full strategy — those read the MDL directly + +```bash +wren memory index --mdl ~/.wren/mdl.json +``` + +### Storing queries: `store` + +Store a NL-to-SQL pair so it can be recalled later as a few-shot example. + +**When to store — ALL of these must be true:** +1. The SQL query executed successfully +2. The user confirmed the result is correct, OR the user continued working with the result (asked a follow-up question, exported the data, etc.) +3. There is a clear natural language question that the SQL answers + +**When NOT to store:** +- The query failed or returned an error +- The user said the result is wrong or asked to fix it +- The query is exploratory / throwaway (e.g. `SELECT * FROM orders LIMIT 5`) +- There is no natural language question — just raw SQL from the user +- The user explicitly asked not to store it + +```bash +wren memory store \ + --nl "top 5 customers by revenue last quarter" \ + --sql "SELECT c_name, SUM(o_totalprice) AS revenue FROM orders JOIN customer ON orders.o_custkey = customer.c_custkey WHERE o_orderdate >= DATE '2025-10-01' GROUP BY 1 ORDER BY 2 DESC LIMIT 5" \ + --datasource postgres +``` + +**Important:** The `--nl` value should be the user's original question, not a paraphrase. This ensures the embedding matches future similar questions. + +### Recalling queries: `recall` + +Search past NL-to-SQL pairs for few-shot examples before writing new SQL. + +**When to recall:** +- Before writing SQL for a new question, especially if it is complex +- When the user asks something similar to a past question +- When you want few-shot examples to improve SQL quality + +**Workflow — SQL generation with memory:** + +``` +1. User asks: "show me monthly revenue trend for the top 3 product categories" +2. Run: wren memory recall -q "monthly revenue trend by product category" --limit 3 +3. Run: wren memory context -q "revenue product category monthly" --mdl ~/.wren/mdl.json +4. Use recalled queries as few-shot examples + schema context to write SQL +5. Execute the SQL +6. If user confirms result → store the new pair +``` + +### Housekeeping: `status` and `reset` + +```bash +wren memory status # check what is indexed +wren memory reset --force # clear everything and start fresh +``` + +--- + +## Complete workflow example + +Here is the full lifecycle for a typical agent session: + +``` +Session start: + 1. Check: wren memory status + → If schema_items is 0 or MDL has changed: wren memory index + +User asks a question: + 2. Recall: wren memory recall -q "" --limit 3 + → Use any results as few-shot examples + 3. Context: wren memory context -q "" + → Get relevant schema information + 4. Write SQL using recalled examples + schema context + 5. Execute: wren query --sql "..." + +After successful execution: + 6. Show results to user + 7. Wait for user's response: + - User confirms / continues → wren memory store --nl "..." --sql "..." + - User says "wrong" / asks to fix → do NOT store, fix the SQL instead + - User says nothing and moves on → do NOT store (silence ≠ confirmation) +``` + +--- + +## CLI quick reference + +| Command | Purpose | +|---------|---------| +| `wren memory index [--mdl PATH]` | Index MDL schema into LanceDB | +| `wren memory describe [--mdl PATH]` | Print full schema as plain text | +| `wren memory context -q "..." [--mdl PATH]` | Get schema context (auto full/search) | +| `wren memory search -q "..." [--type T] [--model M]` | Semantic search over schema | +| `wren memory store --nl "..." --sql "..."` | Store a confirmed NL-to-SQL pair | +| `wren memory recall -q "..." [--limit N]` | Recall similar past queries | +| `wren memory status` | Show index statistics | +| `wren memory reset [--force]` | Drop all memory tables | + +All commands accept `--path DIR` to override the default storage location (`~/.wren/memory/`). diff --git a/cli-skills/wren-usage/SKILL.md b/cli-skills/wren-usage/SKILL.md new file mode 100644 index 000000000..36f30202a --- /dev/null +++ b/cli-skills/wren-usage/SKILL.md @@ -0,0 +1,245 @@ +--- +name: wren-usage +description: "Wren Engine CLI workflow guide for AI agents. Teaches agents how to answer data questions end-to-end: gather schema context, recall past queries, write SQL through the MDL semantic layer, execute, and learn from results. Covers error recovery, MDL change handling, and data source onboarding. Use when: agent needs to query data, connect a new data source, or handle MDL changes via the wren CLI." +license: Apache-2.0 +metadata: + author: wren-engine + version: "1.0" +--- + +# Wren Engine CLI — Agent Workflow Guide + +This skill teaches you how to use the `wren` CLI to answer data questions. It is decision-driven: it tells you **when** and **why** to run each command, not just **how**. + +For memory-specific decisions (when to store, when to recall), see `@wren-memory`. + +--- + +## Core concept: the MDL semantic layer + +The `wren` CLI does not query database tables directly. All SQL goes through an **MDL (Model Definition Language)** layer that defines models, columns, relationships, and views. You query model names, not table names. + +Two files drive everything: +- `~/.wren/mdl.json` — the semantic model (what tables/columns exist and how they relate) +- `~/.wren/connection_info.json` — how to connect to the database + +If both exist, every `wren` command auto-discovers them. No flags needed. + +--- + +## Workflow 1: Answering a data question + +This is the most common workflow. A user asks a natural language question and expects data back. + +``` +User: "What were the top 5 customers by revenue last quarter?" +``` + +### Step 1 — Gather context + +Before writing SQL, you need to know what models and columns are available. + +**Decision: which context command?** + +| Situation | Command | Why | +|-----------|---------|-----| +| First question in session | `wren memory context -q ""` | Auto-selects full text or search based on schema size | +| You already have schema context from a recent call | Skip | Don't re-fetch what you already know | +| Need a specific model's columns | `wren memory search -q "..." --model orders` | Targeted search | +| Memory extras not installed | `wren dry-plan --sql "SELECT 1"` and read the error for model hints | Fallback — no embedding needed | + +### Step 2 — Check for similar past queries + +```bash +wren memory recall -q "top customers by revenue" --limit 3 +``` + +If results come back, use them as **few-shot examples** — adapt the SQL pattern to the current question rather than writing from scratch. This significantly improves SQL quality for complex queries. + +If no results, proceed without examples. + +### Step 3 — Write and execute SQL + +Write SQL targeting MDL model names (not raw database tables). + +```bash +wren --sql 'SELECT c_name, SUM(o_totalprice) AS revenue +FROM orders JOIN customer ON orders.o_custkey = customer.c_custkey +WHERE o_orderdate >= DATE '\''2025-10-01'\'' +GROUP BY 1 ORDER BY 2 DESC LIMIT 5' +``` + +**Key SQL rules:** +- Use model names from the MDL, not database table names +- Use `CAST(x AS type)` for conversions, not `::type` +- Avoid correlated subqueries — use JOINs or CTEs +- Wren SQL is dialect-neutral (ANSI-ish) — the engine translates to the target dialect + +### Step 4 — Handle the result + +**Decision: what to do after execution?** + +| Outcome | Action | +|---------|--------| +| Success + user confirms result is correct | `wren memory store --nl "..." --sql "..."` | +| Success + user continues with follow-up question | `wren memory store --nl "..." --sql "..."` then handle follow-up | +| Success + user says nothing | Do NOT store — silence is not confirmation | +| Success + user says result is wrong | Do NOT store — fix the SQL | +| Query error | Go to **Error recovery** below | + +--- + +## Workflow 2: Error recovery + +When a query fails, diagnose before retrying. + +### "table not found" or "model not found" + +``` +1. Check: is the model name correct? + → wren memory search -q "" --type model + → Compare with what the user asked for + +2. Is the MDL deployed? + → ls ~/.wren/mdl.json + → If missing: ask user for MDL file or generate one + +3. Is the column name correct? + → wren memory search -q "" --model +``` + +### "connection refused" or "could not connect" + +``` +1. Check connection file exists: + → cat ~/.wren/connection_info.json + +2. Verify datasource field matches the connector: + → Must be one of: postgres, mysql, bigquery, snowflake, clickhouse, + trino, mssql, databricks, redshift, spark, athena, oracle, duckdb + +3. Test with a minimal query: + → wren --sql "SELECT 1" +``` + +### SQL syntax / planning error + +``` +1. Use dry-plan to isolate planning vs execution: + → wren dry-plan --sql "" + → If dry-plan fails: the SQL has MDL-level issues (bad model/column names, unsupported syntax) + → If dry-plan succeeds but execution fails: the SQL is valid but the DB rejects the translated query + +2. Common fixes: + - Replace :: casts with CAST() + - Replace correlated subqueries with JOINs + - Check column types — don't compare varchar to integer without CAST +``` + +--- + +## Workflow 3: Connecting a new data source + +When the user wants to query a new database for the first time. + +### Step 1 — Create connection_info.json + +Ask the user for: datasource type, host, port, database, credentials. + +Write to `~/.wren/connection_info.json`: + +```json +{ + "datasource": "postgres", + "host": "localhost", + "port": 5432, + "database": "mydb", + "user": "postgres", + "password": "..." +} +``` + +See `wren/docs/connections.md` for all connector formats (BigQuery needs base64-encoded credentials, Snowflake needs account identifier, etc.). + +### Step 2 — Test connectivity + +```bash +wren --sql "SELECT 1" +``` + +If this fails, fix connection_info before proceeding. + +### Step 3 — Get or create MDL + +**Decision: does the user already have an MDL?** + +| Situation | Action | +|-----------|--------| +| User has an existing mdl.json | Copy to `~/.wren/mdl.json` | +| User has a YAML project | Build it: the project tooling produces `target/mdl.json` | +| Starting from scratch | The user needs to create an MDL — this defines which tables and columns are queryable. Help them write one based on their database schema. | + +### Step 4 — Index schema and verify + +```bash +wren memory index +wren --sql "SELECT * FROM LIMIT 5" +``` + +--- + +## Workflow 4: MDL changes + +When the MDL is updated (new model, new column, changed relationship), downstream state becomes stale. + +### What needs to happen after MDL changes + +``` +1. Deploy: copy updated mdl.json to ~/.wren/mdl.json +2. Re-index: wren memory index + → Schema memory becomes stale if you skip this +3. Verify: wren --sql "SELECT * FROM LIMIT 1" +``` + +### How to detect stale schema memory + +If `wren memory context` returns results that reference models/columns that no longer exist, the index is stale. Re-index. + +--- + +## Command decision tree + +When unsure which command to use: + +``` +Want to get data back? + → wren --sql "..." (or wren query --sql "...") + +Want to see the translated SQL without executing? + → wren dry-plan --sql "..." + +Want to check if SQL is valid against the live DB? + → wren dry-run --sql "..." + +Want schema context before writing SQL? + → wren memory context -q "..." + +Want to find a specific model or column? + → wren memory search -q "..." [--type model|column] + +Want to store a confirmed query? + → wren memory store --nl "..." --sql "..." + +Want few-shot examples for a new question? + → wren memory recall -q "..." +``` + +--- + +## Things to avoid + +- **Do not guess model or column names.** Always check schema context first. +- **Do not store queries the user hasn't confirmed.** Query success != correctness. +- **Do not re-index before every query.** Index once per MDL change. +- **Do not use database-specific syntax in wren SQL.** Write ANSI SQL and let the engine translate. +- **Do not pass connection passwords in --connection-info on the command line** if the shell history is shared. Use --connection-file instead. From bd4c108b19c8c34d0564c4b37998f2ec4caf7c9a Mon Sep 17 00:00:00 2001 From: Jax Liu Date: Mon, 30 Mar 2026 23:42:08 +0800 Subject: [PATCH 06/16] refactor(skills): consolidate cli-skills into single wren-usage skill MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Merge wren-memory into wren-usage following the agent skills spec: - SKILL.md (138 lines): decision-driven workflows for answering questions, error recovery, onboarding, and MDL changes - references/memory.md: when to index, store, recall, and the hybrid context strategy Removes the separate wren-memory skill — memory decisions are now a reference under wren-usage, not a standalone skill. Co-Authored-By: Claude Opus 4.6 (1M context) --- cli-skills/wren-memory/SKILL.md | 172 --------------- cli-skills/wren-usage/SKILL.md | 235 ++++++--------------- cli-skills/wren-usage/references/memory.md | 112 ++++++++++ 3 files changed, 176 insertions(+), 343 deletions(-) delete mode 100644 cli-skills/wren-memory/SKILL.md create mode 100644 cli-skills/wren-usage/references/memory.md diff --git a/cli-skills/wren-memory/SKILL.md b/cli-skills/wren-memory/SKILL.md deleted file mode 100644 index 22d70ea21..000000000 --- a/cli-skills/wren-memory/SKILL.md +++ /dev/null @@ -1,172 +0,0 @@ ---- -name: wren-memory -description: "Schema and query memory for Wren Engine — index MDL schema for semantic search, store and recall NL-to-SQL pairs for few-shot retrieval. Guides the agent on when to index, search, store, and recall using the wren memory CLI. Use when: the agent needs schema context before writing SQL, wants to find relevant models/columns by natural language, or should store a confirmed-correct query for future reference." -license: Apache-2.0 -metadata: - author: wren-engine - version: "1.0" ---- - -# Wren Memory - -## Version check - -Silently fetch `https://raw.githubusercontent.com/Canner/wren-engine/main/skills/versions.json`. -Compare the `wren-memory` key with this skill's version (from the frontmatter above). -If the remote version is newer, notify the user before proceeding: - -> A newer version of the **wren-memory** skill is available. -> Update with: -> ``` -> npx skills add Canner/wren-engine --skill wren-memory --agent claude-code -> ``` - -Then continue with the workflow below regardless of update status. - ---- - -## What is Wren Memory? - -Wren Memory is a LanceDB-backed local memory layer for the `wren` CLI. It provides two capabilities: - -1. **Schema Memory** — index MDL models, columns, relationships, and views for semantic search -2. **Query Memory** — store confirmed NL-to-SQL pairs for few-shot retrieval - -Requires the `memory` extra: `pip install 'wren-engine[memory]'` - ---- - -## When to use each command - -### Schema context: `context` > `describe` > `search` - -Before writing SQL, you need schema context. Choose the right command: - -| Command | When to use | -|---------|-------------| -| `wren memory context -q "..."` | **Default choice.** Auto-selects full text or embedding search based on schema size. | -| `wren memory describe` | When you want the full schema and know it is small. | -| `wren memory search -q "..."` | When you need fine-grained filtering (by item type or model name). | - -**Workflow — before writing SQL:** - -``` -1. User asks a question in natural language -2. Run: wren memory context -q "" --mdl ~/.wren/mdl.json -3. Use the returned schema context to write SQL -4. If context strategy is "search" and results are insufficient, - try a broader query or different keywords -``` - -### Indexing: `index` - -Index the MDL schema so that `search` and `context` (search strategy) work. - -**When to index:** -- After deploying a new or updated MDL (`wren memory index`) -- When `wren memory status` shows `schema_items: 0 rows` -- When the MDL has changed and the schema memory is stale - -**When NOT to index:** -- Before every query — indexing is expensive, do it once per MDL change -- When only using `describe` or `context` with full strategy — those read the MDL directly - -```bash -wren memory index --mdl ~/.wren/mdl.json -``` - -### Storing queries: `store` - -Store a NL-to-SQL pair so it can be recalled later as a few-shot example. - -**When to store — ALL of these must be true:** -1. The SQL query executed successfully -2. The user confirmed the result is correct, OR the user continued working with the result (asked a follow-up question, exported the data, etc.) -3. There is a clear natural language question that the SQL answers - -**When NOT to store:** -- The query failed or returned an error -- The user said the result is wrong or asked to fix it -- The query is exploratory / throwaway (e.g. `SELECT * FROM orders LIMIT 5`) -- There is no natural language question — just raw SQL from the user -- The user explicitly asked not to store it - -```bash -wren memory store \ - --nl "top 5 customers by revenue last quarter" \ - --sql "SELECT c_name, SUM(o_totalprice) AS revenue FROM orders JOIN customer ON orders.o_custkey = customer.c_custkey WHERE o_orderdate >= DATE '2025-10-01' GROUP BY 1 ORDER BY 2 DESC LIMIT 5" \ - --datasource postgres -``` - -**Important:** The `--nl` value should be the user's original question, not a paraphrase. This ensures the embedding matches future similar questions. - -### Recalling queries: `recall` - -Search past NL-to-SQL pairs for few-shot examples before writing new SQL. - -**When to recall:** -- Before writing SQL for a new question, especially if it is complex -- When the user asks something similar to a past question -- When you want few-shot examples to improve SQL quality - -**Workflow — SQL generation with memory:** - -``` -1. User asks: "show me monthly revenue trend for the top 3 product categories" -2. Run: wren memory recall -q "monthly revenue trend by product category" --limit 3 -3. Run: wren memory context -q "revenue product category monthly" --mdl ~/.wren/mdl.json -4. Use recalled queries as few-shot examples + schema context to write SQL -5. Execute the SQL -6. If user confirms result → store the new pair -``` - -### Housekeeping: `status` and `reset` - -```bash -wren memory status # check what is indexed -wren memory reset --force # clear everything and start fresh -``` - ---- - -## Complete workflow example - -Here is the full lifecycle for a typical agent session: - -``` -Session start: - 1. Check: wren memory status - → If schema_items is 0 or MDL has changed: wren memory index - -User asks a question: - 2. Recall: wren memory recall -q "" --limit 3 - → Use any results as few-shot examples - 3. Context: wren memory context -q "" - → Get relevant schema information - 4. Write SQL using recalled examples + schema context - 5. Execute: wren query --sql "..." - -After successful execution: - 6. Show results to user - 7. Wait for user's response: - - User confirms / continues → wren memory store --nl "..." --sql "..." - - User says "wrong" / asks to fix → do NOT store, fix the SQL instead - - User says nothing and moves on → do NOT store (silence ≠ confirmation) -``` - ---- - -## CLI quick reference - -| Command | Purpose | -|---------|---------| -| `wren memory index [--mdl PATH]` | Index MDL schema into LanceDB | -| `wren memory describe [--mdl PATH]` | Print full schema as plain text | -| `wren memory context -q "..." [--mdl PATH]` | Get schema context (auto full/search) | -| `wren memory search -q "..." [--type T] [--model M]` | Semantic search over schema | -| `wren memory store --nl "..." --sql "..."` | Store a confirmed NL-to-SQL pair | -| `wren memory recall -q "..." [--limit N]` | Recall similar past queries | -| `wren memory status` | Show index statistics | -| `wren memory reset [--force]` | Drop all memory tables | - -All commands accept `--path DIR` to override the default storage location (`~/.wren/memory/`). diff --git a/cli-skills/wren-usage/SKILL.md b/cli-skills/wren-usage/SKILL.md index 36f30202a..c2dc4be3f 100644 --- a/cli-skills/wren-usage/SKILL.md +++ b/cli-skills/wren-usage/SKILL.md @@ -1,6 +1,6 @@ --- name: wren-usage -description: "Wren Engine CLI workflow guide for AI agents. Teaches agents how to answer data questions end-to-end: gather schema context, recall past queries, write SQL through the MDL semantic layer, execute, and learn from results. Covers error recovery, MDL change handling, and data source onboarding. Use when: agent needs to query data, connect a new data source, or handle MDL changes via the wren CLI." +description: "Wren Engine CLI workflow guide for AI agents. Answer data questions end-to-end using the wren CLI: gather schema context, recall past queries, write SQL through the MDL semantic layer, execute, and learn from confirmed results. Use when: agent needs to query data, connect a data source, handle errors, or manage MDL changes via the wren CLI." license: Apache-2.0 metadata: author: wren-engine @@ -9,237 +9,130 @@ metadata: # Wren Engine CLI — Agent Workflow Guide -This skill teaches you how to use the `wren` CLI to answer data questions. It is decision-driven: it tells you **when** and **why** to run each command, not just **how**. +The `wren` CLI queries databases through an MDL (Model Definition Language) semantic layer. You write SQL against model names, not raw tables. The engine translates to the target dialect. -For memory-specific decisions (when to store, when to recall), see `@wren-memory`. +Two files drive everything (auto-discovered from `~/.wren/`): +- `mdl.json` — the semantic model +- `connection_info.json` — database credentials ---- - -## Core concept: the MDL semantic layer - -The `wren` CLI does not query database tables directly. All SQL goes through an **MDL (Model Definition Language)** layer that defines models, columns, relationships, and views. You query model names, not table names. - -Two files drive everything: -- `~/.wren/mdl.json` — the semantic model (what tables/columns exist and how they relate) -- `~/.wren/connection_info.json` — how to connect to the database - -If both exist, every `wren` command auto-discovers them. No flags needed. +For memory-specific decisions, see [references/memory.md](references/memory.md). --- ## Workflow 1: Answering a data question -This is the most common workflow. A user asks a natural language question and expects data back. - -``` -User: "What were the top 5 customers by revenue last quarter?" -``` - ### Step 1 — Gather context -Before writing SQL, you need to know what models and columns are available. - -**Decision: which context command?** - -| Situation | Command | Why | -|-----------|---------|-----| -| First question in session | `wren memory context -q ""` | Auto-selects full text or search based on schema size | -| You already have schema context from a recent call | Skip | Don't re-fetch what you already know | -| Need a specific model's columns | `wren memory search -q "..." --model orders` | Targeted search | -| Memory extras not installed | `wren dry-plan --sql "SELECT 1"` and read the error for model hints | Fallback — no embedding needed | +| Situation | Command | +|-----------|---------| +| Default | `wren memory context -q ""` | +| Need specific model's columns | `wren memory search -q "..." --model ` | +| Memory not installed | Read `~/.wren/mdl.json` directly | -### Step 2 — Check for similar past queries +### Step 2 — Recall past queries ```bash -wren memory recall -q "top customers by revenue" --limit 3 +wren memory recall -q "" --limit 3 ``` -If results come back, use them as **few-shot examples** — adapt the SQL pattern to the current question rather than writing from scratch. This significantly improves SQL quality for complex queries. - -If no results, proceed without examples. +Use results as few-shot examples. Skip if empty. ### Step 3 — Write and execute SQL -Write SQL targeting MDL model names (not raw database tables). - ```bash -wren --sql 'SELECT c_name, SUM(o_totalprice) AS revenue -FROM orders JOIN customer ON orders.o_custkey = customer.c_custkey -WHERE o_orderdate >= DATE '\''2025-10-01'\'' +wren --sql 'SELECT c_name, SUM(o_totalprice) FROM orders +JOIN customer ON orders.o_custkey = customer.c_custkey GROUP BY 1 ORDER BY 2 DESC LIMIT 5' ``` -**Key SQL rules:** -- Use model names from the MDL, not database table names -- Use `CAST(x AS type)` for conversions, not `::type` +**SQL rules:** +- Target MDL model names, not database tables +- Use `CAST(x AS type)`, not `::type` - Avoid correlated subqueries — use JOINs or CTEs -- Wren SQL is dialect-neutral (ANSI-ish) — the engine translates to the target dialect +- Write dialect-neutral SQL — the engine translates ### Step 4 — Handle the result -**Decision: what to do after execution?** - | Outcome | Action | |---------|--------| -| Success + user confirms result is correct | `wren memory store --nl "..." --sql "..."` | -| Success + user continues with follow-up question | `wren memory store --nl "..." --sql "..."` then handle follow-up | -| Success + user says nothing | Do NOT store — silence is not confirmation | -| Success + user says result is wrong | Do NOT store — fix the SQL | -| Query error | Go to **Error recovery** below | +| User confirms correct | `wren memory store --nl "..." --sql "..."` | +| User continues with follow-up | Store, then handle follow-up | +| User says nothing | Do NOT store | +| User says wrong | Do NOT store — fix the SQL | +| Query error | See Error recovery below | --- ## Workflow 2: Error recovery -When a query fails, diagnose before retrying. +### "table not found" -### "table not found" or "model not found" +1. Verify model name: `wren memory search -q "" --type model` +2. Check MDL exists: `ls ~/.wren/mdl.json` +3. Verify column: `wren memory search -q "" --model ` -``` -1. Check: is the model name correct? - → wren memory search -q "" --type model - → Compare with what the user asked for - -2. Is the MDL deployed? - → ls ~/.wren/mdl.json - → If missing: ask user for MDL file or generate one - -3. Is the column name correct? - → wren memory search -q "" --model -``` - -### "connection refused" or "could not connect" - -``` -1. Check connection file exists: - → cat ~/.wren/connection_info.json +### Connection error -2. Verify datasource field matches the connector: - → Must be one of: postgres, mysql, bigquery, snowflake, clickhouse, - trino, mssql, databricks, redshift, spark, athena, oracle, duckdb - -3. Test with a minimal query: - → wren --sql "SELECT 1" -``` +1. Check: `cat ~/.wren/connection_info.json` +2. Test: `wren --sql "SELECT 1"` +3. Valid datasource values: `postgres`, `mysql`, `bigquery`, `snowflake`, `clickhouse`, `trino`, `mssql`, `databricks`, `redshift`, `spark`, `athena`, `oracle`, `duckdb` ### SQL syntax / planning error -``` -1. Use dry-plan to isolate planning vs execution: - → wren dry-plan --sql "" - → If dry-plan fails: the SQL has MDL-level issues (bad model/column names, unsupported syntax) - → If dry-plan succeeds but execution fails: the SQL is valid but the DB rejects the translated query - -2. Common fixes: - - Replace :: casts with CAST() - - Replace correlated subqueries with JOINs - - Check column types — don't compare varchar to integer without CAST -``` +1. Isolate the layer: + - `wren dry-plan --sql "..."` — if this fails, it is an MDL-level issue + - If dry-plan succeeds but execution fails, the DB rejects the translated SQL +2. Common fixes: replace `::` with `CAST()`, replace correlated subqueries with JOINs --- ## Workflow 3: Connecting a new data source -When the user wants to query a new database for the first time. - -### Step 1 — Create connection_info.json - -Ask the user for: datasource type, host, port, database, credentials. +1. Create `~/.wren/connection_info.json` — see [wren/docs/connections.md](../../wren/docs/connections.md) for per-connector formats +2. Test: `wren --sql "SELECT 1"` +3. Place or create `~/.wren/mdl.json` +4. Index: `wren memory index` +5. Verify: `wren --sql "SELECT * FROM LIMIT 5"` -Write to `~/.wren/connection_info.json`: - -```json -{ - "datasource": "postgres", - "host": "localhost", - "port": 5432, - "database": "mydb", - "user": "postgres", - "password": "..." -} -``` +--- -See `wren/docs/connections.md` for all connector formats (BigQuery needs base64-encoded credentials, Snowflake needs account identifier, etc.). +## Workflow 4: After MDL changes -### Step 2 — Test connectivity +When the MDL is updated, downstream state goes stale: ```bash -wren --sql "SELECT 1" -``` - -If this fails, fix connection_info before proceeding. - -### Step 3 — Get or create MDL - -**Decision: does the user already have an MDL?** +# 1. Deploy updated MDL +cp updated-mdl.json ~/.wren/mdl.json -| Situation | Action | -|-----------|--------| -| User has an existing mdl.json | Copy to `~/.wren/mdl.json` | -| User has a YAML project | Build it: the project tooling produces `target/mdl.json` | -| Starting from scratch | The user needs to create an MDL — this defines which tables and columns are queryable. Help them write one based on their database schema. | - -### Step 4 — Index schema and verify - -```bash +# 2. Re-index schema memory wren memory index -wren --sql "SELECT * FROM LIMIT 5" -``` - ---- -## Workflow 4: MDL changes - -When the MDL is updated (new model, new column, changed relationship), downstream state becomes stale. - -### What needs to happen after MDL changes - -``` -1. Deploy: copy updated mdl.json to ~/.wren/mdl.json -2. Re-index: wren memory index - → Schema memory becomes stale if you skip this -3. Verify: wren --sql "SELECT * FROM LIMIT 1" +# 3. Verify +wren --sql "SELECT * FROM LIMIT 1" ``` -### How to detect stale schema memory - -If `wren memory context` returns results that reference models/columns that no longer exist, the index is stale. Re-index. - --- ## Command decision tree -When unsure which command to use: - ``` -Want to get data back? - → wren --sql "..." (or wren query --sql "...") - -Want to see the translated SQL without executing? - → wren dry-plan --sql "..." - -Want to check if SQL is valid against the live DB? - → wren dry-run --sql "..." - -Want schema context before writing SQL? - → wren memory context -q "..." - -Want to find a specific model or column? - → wren memory search -q "..." [--type model|column] - -Want to store a confirmed query? - → wren memory store --nl "..." --sql "..." - -Want few-shot examples for a new question? - → wren memory recall -q "..." +Get data back → wren --sql "..." +See translated SQL only → wren dry-plan --sql "..." +Validate against DB → wren dry-run --sql "..." +Schema context → wren memory context -q "..." +Find model/column → wren memory search -q "..." [--type T] +Store confirmed query → wren memory store --nl "..." --sql "..." +Few-shot examples → wren memory recall -q "..." +Index stats → wren memory status +Re-index after MDL change → wren memory index ``` --- ## Things to avoid -- **Do not guess model or column names.** Always check schema context first. -- **Do not store queries the user hasn't confirmed.** Query success != correctness. -- **Do not re-index before every query.** Index once per MDL change. -- **Do not use database-specific syntax in wren SQL.** Write ANSI SQL and let the engine translate. -- **Do not pass connection passwords in --connection-info on the command line** if the shell history is shared. Use --connection-file instead. +- Do not guess model or column names — check context first +- Do not store queries the user has not confirmed — success != correctness +- Do not re-index before every query — once per MDL change +- Do not use database-specific syntax — write ANSI SQL +- Do not pass passwords via `--connection-info` if shell history is shared — use `--connection-file` diff --git a/cli-skills/wren-usage/references/memory.md b/cli-skills/wren-usage/references/memory.md new file mode 100644 index 000000000..339c46873 --- /dev/null +++ b/cli-skills/wren-usage/references/memory.md @@ -0,0 +1,112 @@ +# Wren Memory — When to index, search, store, and recall + +This reference covers the decision logic for each memory command. The main workflow is in the parent SKILL.md. + +--- + +## Schema context: `context` > `describe` > `search` + +| Command | When to use | +|---------|-------------| +| `wren memory context -q "..."` | Default. Auto-selects full text (small schema) or embedding search (large schema) based on a 30K-char threshold. | +| `wren memory describe` | When you want the full schema text and know it is small. | +| `wren memory search -q "..." --type T --model M` | When you need fine-grained filtering by item type or model name. | + +The hybrid strategy works like this: +- Below 30K characters (~8K tokens): returns the entire schema as structured plain text — the LLM sees complete model-to-column relationships, join paths, and primary keys +- Above 30K characters: returns embedding search results — only the most relevant fragments + +CJK-heavy schemas switch to search sooner (~1.5 chars per token vs 4 for English), which is the safe direction. + +Override with `--threshold`: +```bash +wren memory context -q "revenue" --threshold 50000 # raise for larger context windows +``` + +--- + +## Indexing: `wren memory index` + +**When to index:** +- After deploying a new or updated MDL +- When `wren memory status` shows `schema_items: 0 rows` +- When `wren memory context` returns stale results (references deleted models) + +**When NOT to index:** +- Before every query — indexing is expensive, do it once per MDL change +- When only using `describe` or `context` with full strategy — those read the MDL directly + +```bash +wren memory index --mdl ~/.wren/mdl.json +``` + +--- + +## Storing queries: `wren memory store` + +**Store when ALL of these are true:** +1. The SQL query executed successfully +2. The user confirmed the result is correct, OR continued working with it (follow-up question, exported data, etc.) +3. There is a clear natural language question that the SQL answers + +**Do NOT store when:** +- The query failed or returned an error +- The user said the result is wrong or asked to fix it +- The query is exploratory / throwaway (`SELECT * FROM orders LIMIT 5`) +- There is no natural language question — just raw SQL +- The user explicitly asked not to store it + +```bash +wren memory store \ + --nl "top 5 customers by revenue last quarter" \ + --sql "SELECT c_name, SUM(o_totalprice) AS revenue ..." \ + --datasource postgres +``` + +The `--nl` value should be the user's original question, not a paraphrase. + +--- + +## Recalling queries: `wren memory recall` + +**When to recall:** +- Before writing SQL for a new question, especially complex ones +- When the user asks something similar to a past question + +```bash +wren memory recall -q "monthly revenue by category" --limit 3 +``` + +Use results as few-shot examples: adapt the SQL pattern to the current question. + +--- + +## Full lifecycle example + +``` +Session start: + 1. wren memory status → if schema_items is 0: wren memory index + +User asks a question: + 2. wren memory recall -q "" --limit 3 + 3. wren memory context -q "" + 4. Write SQL using recalled examples + schema context + 5. wren --sql "..." + +After execution: + 6. Show results to user + 7. User confirms → wren memory store --nl "..." --sql "..." + User says wrong → fix SQL, do NOT store + User silent → do NOT store +``` + +--- + +## Housekeeping + +```bash +wren memory status # path, table names, row counts +wren memory reset --force # drop everything, start fresh +``` + +All memory commands accept `--path DIR` to override `~/.wren/memory/`. From 51a035f98b2d48f04ffcd3631af8fe770f83e108 Mon Sep 17 00:00:00 2001 From: Jax Liu Date: Tue, 31 Mar 2026 01:21:01 +0800 Subject: [PATCH 07/16] refactor(wren): merge memory search into context command Consolidate search_schema into get_context to give agents a single command for schema retrieval. context auto-selects full text vs embedding search, and now accepts --type and --model filters for the search strategy. Removes the standalone search CLI command. Co-Authored-By: Claude Opus 4.6 (1M context) --- cli-skills/wren-usage/SKILL.md | 8 +-- cli-skills/wren-usage/references/memory.md | 6 +- wren/docs/cli.md | 28 ++------ wren/src/wren/memory/__init__.py | 59 ++++++++--------- wren/src/wren/memory/cli.py | 41 +++++------- wren/src/wren/memory/store.py | 76 ++++++++++++---------- wren/tests/unit/test_memory.py | 60 +++++++++-------- 7 files changed, 127 insertions(+), 151 deletions(-) diff --git a/cli-skills/wren-usage/SKILL.md b/cli-skills/wren-usage/SKILL.md index c2dc4be3f..0ad02cfc1 100644 --- a/cli-skills/wren-usage/SKILL.md +++ b/cli-skills/wren-usage/SKILL.md @@ -26,7 +26,7 @@ For memory-specific decisions, see [references/memory.md](references/memory.md). | Situation | Command | |-----------|---------| | Default | `wren memory context -q ""` | -| Need specific model's columns | `wren memory search -q "..." --model ` | +| Need specific model's columns | `wren memory context -q "..." --model --threshold 0` | | Memory not installed | Read `~/.wren/mdl.json` directly | ### Step 2 — Recall past queries @@ -67,9 +67,9 @@ GROUP BY 1 ORDER BY 2 DESC LIMIT 5' ### "table not found" -1. Verify model name: `wren memory search -q "" --type model` +1. Verify model name: `wren memory context -q "" --type model --threshold 0` 2. Check MDL exists: `ls ~/.wren/mdl.json` -3. Verify column: `wren memory search -q "" --model ` +3. Verify column: `wren memory context -q "" --model --threshold 0` ### Connection error @@ -120,7 +120,7 @@ Get data back → wren --sql "..." See translated SQL only → wren dry-plan --sql "..." Validate against DB → wren dry-run --sql "..." Schema context → wren memory context -q "..." -Find model/column → wren memory search -q "..." [--type T] +Filter by type/model → wren memory context -q "..." --type T --model M --threshold 0 Store confirmed query → wren memory store --nl "..." --sql "..." Few-shot examples → wren memory recall -q "..." Index stats → wren memory status diff --git a/cli-skills/wren-usage/references/memory.md b/cli-skills/wren-usage/references/memory.md index 339c46873..a2770164c 100644 --- a/cli-skills/wren-usage/references/memory.md +++ b/cli-skills/wren-usage/references/memory.md @@ -1,16 +1,16 @@ -# Wren Memory — When to index, search, store, and recall +# Wren Memory — When to index, context, store, and recall This reference covers the decision logic for each memory command. The main workflow is in the parent SKILL.md. --- -## Schema context: `context` > `describe` > `search` +## Schema context: `context` and `describe` | Command | When to use | |---------|-------------| | `wren memory context -q "..."` | Default. Auto-selects full text (small schema) or embedding search (large schema) based on a 30K-char threshold. | +| `wren memory context -q "..." --type T --model M` | When you need filtering (forces search strategy on large schemas). | | `wren memory describe` | When you want the full schema text and know it is small. | -| `wren memory search -q "..." --type T --model M` | When you need fine-grained filtering by item type or model name. | The hybrid strategy works like this: - Below 30K characters (~8K tokens): returns the entire schema as structured plain text — the LLM sees complete model-to-column relationships, join paths, and primary keys diff --git a/wren/docs/cli.md b/wren/docs/cli.md index 3febf2be1..3a5bf0615 100644 --- a/wren/docs/cli.md +++ b/wren/docs/cli.md @@ -113,12 +113,14 @@ wren memory describe --mdl /path/to/mdl.json ### `wren memory context` -Get schema context using the best strategy for the schema size. Automatically chooses between full plain text and embedding search based on the threshold. +Get schema context for an LLM. Automatically chooses the best strategy based on schema size: full plain text for small schemas, embedding search for large schemas. + +When using the search strategy, optional `--type` and `--model` filters narrow the results. ```bash wren memory context -q "customer order price" -wren memory context -q "revenue" --threshold 50000 -wren memory context -q "日期" --output json +wren memory context -q "revenue" --type column --model orders +wren memory context -q "日期" --threshold 50000 --output json ``` | Flag | Description | @@ -126,27 +128,11 @@ wren memory context -q "日期" --output json | `-q, --query` | Search query (required) | | `--mdl` | Path to MDL JSON file | | `-l, --limit` | Max results for search strategy (default: 5) | +| `-t, --type` | Filter: `model`, `column`, `relationship`, `view` (search strategy only) | +| `--model` | Filter by model name (search strategy only) | | `--threshold` | Character threshold for full vs search (default: 30,000) | | `-o, --output` | Output format: `table` (default), `json` | -### `wren memory search` - -Semantic search over the indexed schema. Returns the most relevant models, columns, etc. for a natural language query. - -```bash -wren memory search -q "customer order price" -wren memory search -q "revenue" --type column --limit 3 -wren memory search -q "日期欄位" --model orders --output json -``` - -| Flag | Description | -|------|-------------| -| `-q, --query` | Search query (required) | -| `-l, --limit` | Max results (default: 5) | -| `-t, --type` | Filter by item type: `model`, `column`, `relationship`, `view` | -| `--model` | Filter by model name | -| `-o, --output` | Output format: `table` (default), `json` | - ### `wren memory store` Store a natural-language-to-SQL pair for future few-shot retrieval. diff --git a/wren/src/wren/memory/__init__.py b/wren/src/wren/memory/__init__.py index f4e0728a7..567f0fda8 100644 --- a/wren/src/wren/memory/__init__.py +++ b/wren/src/wren/memory/__init__.py @@ -6,7 +6,7 @@ mem = WrenMemory() mem.index_manifest(manifest_dict) - results = mem.search_schema("customer orders") + ctx = mem.get_context(manifest_dict, "customer orders") """ from __future__ import annotations @@ -32,18 +32,37 @@ def index_manifest(self, manifest: dict, *, replace: bool = True) -> int: """Index MDL schema into LanceDB. Returns record count.""" return self._store.index_schema(manifest, replace=replace) - def search_schema( + @staticmethod + def describe_schema(manifest: dict) -> str: + """Return the full schema as structured plain text.""" + from wren.memory.schema_indexer import describe_schema # noqa: PLC0415 + + return describe_schema(manifest) + + def get_context( self, + manifest: dict, query: str, *, limit: int = 5, item_type: str | None = None, model_name: str | None = None, - ) -> list[dict]: - """Semantic search over indexed schema items.""" - return self._store.search_schema( - query, limit=limit, item_type=item_type, model_name=model_name - ) + threshold: int | None = None, + ) -> dict: + """Return schema context using the best strategy for the schema size. + + Small schemas (below *threshold* chars) are returned as full plain + text. Large schemas use embedding search with optional filters. + See :data:`~wren.memory.schema_indexer.SCHEMA_DESCRIBE_THRESHOLD`. + """ + kwargs: dict = { + "limit": limit, + "item_type": item_type, + "model_name": model_name, + } + if threshold is not None: + kwargs["threshold"] = threshold + return self._store.get_context(manifest, query, **kwargs) def store_query( self, @@ -66,32 +85,6 @@ def recall_queries( """Search past NL→SQL pairs by semantic similarity.""" return self._store.recall_queries(query, limit=limit, datasource=datasource) - @staticmethod - def describe_schema(manifest: dict) -> str: - """Return the full schema as structured plain text.""" - from wren.memory.schema_indexer import describe_schema # noqa: PLC0415 - - return describe_schema(manifest) - - def get_context( - self, - manifest: dict, - query: str, - *, - limit: int = 5, - threshold: int | None = None, - ) -> dict: - """Return schema context using the best strategy for the schema size. - - Small schemas (below *threshold* chars) are returned as full plain - text. Large schemas use embedding search to return only relevant - fragments. See :data:`~wren.memory.schema_indexer.SCHEMA_DESCRIBE_THRESHOLD`. - """ - kwargs = {"limit": limit} - if threshold is not None: - kwargs["threshold"] = threshold - return self._store.get_context(manifest, query, **kwargs) - def schema_is_current(self, manifest: dict) -> bool: """Check if the indexed schema matches the given manifest.""" return self._store.schema_is_current(manifest) diff --git a/wren/src/wren/memory/cli.py b/wren/src/wren/memory/cli.py index a888d2a36..595a51188 100644 --- a/wren/src/wren/memory/cli.py +++ b/wren/src/wren/memory/cli.py @@ -134,6 +134,18 @@ def context( query: Annotated[str, typer.Option("--query", "-q", help="Search query")], mdl: MdlOpt = None, limit: Annotated[int, typer.Option("--limit", "-l")] = 5, + item_type: Annotated[ + Optional[str], + typer.Option( + "--type", + "-t", + help="Filter: model|column|relationship|view (search strategy only)", + ), + ] = None, + model_name: Annotated[ + Optional[str], + typer.Option("--model", help="Filter by model name (search strategy only)"), + ] = None, threshold: Annotated[ Optional[int], typer.Option( @@ -143,14 +155,14 @@ def context( path: PathOpt = None, output: OutputOpt = "table", ) -> None: - """Get schema context using the best strategy for the schema size. + """Get schema context for an LLM. Small schemas are returned as full plain text. Large schemas use - embedding search to return only relevant fragments. + embedding search with optional --type and --model filters. """ manifest = _load_manifest(mdl) store = _get_store(path) - kwargs: dict = {"limit": limit} + kwargs: dict = {"limit": limit, "item_type": item_type, "model_name": model_name} if threshold is not None: kwargs["threshold"] = threshold result = store.get_context(manifest, query, **kwargs) @@ -162,29 +174,6 @@ def context( _print_results(result["results"], output) -@memory_app.command() -def search( - query: Annotated[str, typer.Option("--query", "-q", help="Search query")], - limit: Annotated[int, typer.Option("--limit", "-l")] = 5, - item_type: Annotated[ - Optional[str], - typer.Option("--type", "-t", help="Filter: model|column|relationship|view"), - ] = None, - model_name: Annotated[ - Optional[str], - typer.Option("--model", help="Filter by model name"), - ] = None, - path: PathOpt = None, - output: OutputOpt = "table", -) -> None: - """Semantic search over indexed schema items.""" - store = _get_store(path) - results = store.search_schema( - query, limit=limit, item_type=item_type, model_name=model_name - ) - _print_results(results, output) - - @memory_app.command() def store( nl: Annotated[str, typer.Option("--nl", help="Natural language query")], diff --git a/wren/src/wren/memory/store.py b/wren/src/wren/memory/store.py index ca53cfe4a..2bae00ee7 100644 --- a/wren/src/wren/memory/store.py +++ b/wren/src/wren/memory/store.py @@ -112,36 +112,6 @@ def index_schema(self, manifest: dict, *, replace: bool = True) -> int: ) return len(items) - def search_schema( - self, - query: str, - *, - limit: int = 5, - item_type: str | None = None, - model_name: str | None = None, - ) -> list[dict]: - """Semantic search over indexed schema items.""" - if _SCHEMA_TABLE not in _table_names(self._db): - return [] - - table = self._db.open_table(_SCHEMA_TABLE) - q = table.search( - self._embed_fn.compute_query_embeddings(query)[0], - ) - - where_parts: list[str] = [] - if item_type: - where_parts.append(f"item_type = '{item_type}'") - if model_name: - where_parts.append(f"model_name = '{model_name}'") - if where_parts: - q = q.where(" AND ".join(where_parts)) - - results = q.limit(limit).to_list() - for r in results: - r.pop("vector", None) - return results - def schema_is_current(self, manifest: dict) -> bool: """Check whether the indexed schema matches *manifest*.""" if _SCHEMA_TABLE not in _table_names(self._db): @@ -164,24 +134,58 @@ def get_context( query: str, *, limit: int = 5, + item_type: str | None = None, + model_name: str | None = None, threshold: int = SCHEMA_DESCRIBE_THRESHOLD, ) -> dict: """Return schema context using the best strategy for the schema size. - If the plain-text description of *manifest* is shorter than - *threshold* characters, return the full text (``strategy="full"``). - Otherwise, fall back to embedding search (``strategy="search"``). + For small schemas (plain-text description below *threshold* chars), + returns the full text (``strategy="full"``). For large schemas, + uses embedding search with optional filters (``strategy="search"``). - Returns a dict with keys ``strategy``, ``schema``, and (for search) - ``results``. + Returns a dict with keys ``strategy``, ``schema`` (full) or + ``results`` (search). """ text = describe_schema(manifest) if len(text) <= threshold: return {"strategy": "full", "schema": text} - results = self.search_schema(query, limit=limit) + results = self._search_schema( + query, limit=limit, item_type=item_type, model_name=model_name + ) return {"strategy": "search", "results": results} + def _search_schema( + self, + query: str, + *, + limit: int = 5, + item_type: str | None = None, + model_name: str | None = None, + ) -> list[dict]: + """Embedding search over indexed schema items (internal).""" + if _SCHEMA_TABLE not in _table_names(self._db): + return [] + + table = self._db.open_table(_SCHEMA_TABLE) + q = table.search( + self._embed_fn.compute_query_embeddings(query)[0], + ) + + where_parts: list[str] = [] + if item_type: + where_parts.append(f"item_type = '{item_type}'") + if model_name: + where_parts.append(f"model_name = '{model_name}'") + if where_parts: + q = q.where(" AND ".join(where_parts)) + + results = q.limit(limit).to_list() + for r in results: + r.pop("vector", None) + return results + # ── Query history ───────────────────────────────────────────────────── def store_query( diff --git a/wren/tests/unit/test_memory.py b/wren/tests/unit/test_memory.py index b50471f9b..b3ac0a064 100644 --- a/wren/tests/unit/test_memory.py +++ b/wren/tests/unit/test_memory.py @@ -225,27 +225,43 @@ def memory_store(tmp_path): @pytest.mark.unit class TestMemoryStore: - def test_index_and_search(self, memory_store): + def test_index_and_context(self, memory_store): count = memory_store.index_schema(_MANIFEST) assert count == 10 - results = memory_store.search_schema("customer order price", limit=3) - assert len(results) > 0 - assert "text" in results[0] + # Small schema → full strategy + result = memory_store.get_context(_MANIFEST, "customer order price") + assert result["strategy"] == "full" + assert "### Model: orders" in result["schema"] - def test_search_with_type_filter(self, memory_store): + def test_context_search_strategy(self, memory_store): memory_store.index_schema(_MANIFEST) - results = memory_store.search_schema("order", item_type="model", limit=5) - assert all(r["item_type"] == "model" for r in results) + result = memory_store.get_context(_MANIFEST, "customer orders", threshold=10) + assert result["strategy"] == "search" + assert "results" in result + assert len(result["results"]) > 0 + assert "text" in result["results"][0] - def test_search_with_model_filter(self, memory_store): + def test_context_search_with_type_filter(self, memory_store): memory_store.index_schema(_MANIFEST) - results = memory_store.search_schema("price", model_name="orders", limit=10) - assert all(r["model_name"] == "orders" for r in results) + result = memory_store.get_context( + _MANIFEST, "order", item_type="model", threshold=10 + ) + assert result["strategy"] == "search" + assert all(r["item_type"] == "model" for r in result["results"]) - def test_search_empty_store(self, memory_store): - results = memory_store.search_schema("anything") - assert results == [] + def test_context_search_with_model_filter(self, memory_store): + memory_store.index_schema(_MANIFEST) + result = memory_store.get_context( + _MANIFEST, "price", model_name="orders", threshold=10 + ) + assert result["strategy"] == "search" + assert all(r["model_name"] == "orders" for r in result["results"]) + + def test_context_empty_store(self, memory_store): + result = memory_store.get_context(_MANIFEST, "anything", threshold=10) + assert result["strategy"] == "search" + assert result["results"] == [] def test_schema_is_current(self, memory_store): memory_store.index_schema(_MANIFEST) @@ -284,19 +300,6 @@ def test_reset(self, memory_store): info = memory_store.status() assert info["tables"] == {} - def test_get_context_full_for_small_schema(self, memory_store): - memory_store.index_schema(_MANIFEST) - result = memory_store.get_context(_MANIFEST, "customer orders") - assert result["strategy"] == "full" - assert "### Model: orders" in result["schema"] - - def test_get_context_search_for_large_schema(self, memory_store): - memory_store.index_schema(_MANIFEST) - result = memory_store.get_context(_MANIFEST, "customer orders", threshold=10) - assert result["strategy"] == "search" - assert "results" in result - assert len(result["results"]) > 0 - def test_describe_schema_static(self, memory_store): text = memory_store.describe_schema(_MANIFEST) assert "### Model: orders" in text @@ -327,8 +330,9 @@ def test_full_lifecycle(self, wren_memory): count = wren_memory.index_manifest(_MANIFEST) assert count == 10 - results = wren_memory.search_schema("customer") - assert len(results) > 0 + ctx = wren_memory.get_context(_MANIFEST, "customer") + assert ctx["strategy"] == "full" + assert "### Model: customer" in ctx["schema"] wren_memory.store_query( nl_query="find expensive orders", From 8c1d8673e09bd17174775d6abfc71518b894b7a8 Mon Sep 17 00:00:00 2001 From: Jax Liu Date: Tue, 31 Mar 2026 01:38:14 +0800 Subject: [PATCH 08/16] refactor(wren): rename memory context command to fetch Rename the CLI command from `wren memory context` to `wren memory fetch` for consistency with other verb-based commands (index, store, recall, reset). Python API remains get_context() unchanged. Co-Authored-By: Claude Opus 4.6 (1M context) --- cli-skills/wren-usage/SKILL.md | 12 ++++++------ cli-skills/wren-usage/references/memory.md | 10 +++++----- wren/README.md | 2 +- wren/docs/cli.md | 10 +++++----- wren/src/wren/memory/cli.py | 2 +- 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/cli-skills/wren-usage/SKILL.md b/cli-skills/wren-usage/SKILL.md index 0ad02cfc1..ab2b463c3 100644 --- a/cli-skills/wren-usage/SKILL.md +++ b/cli-skills/wren-usage/SKILL.md @@ -25,8 +25,8 @@ For memory-specific decisions, see [references/memory.md](references/memory.md). | Situation | Command | |-----------|---------| -| Default | `wren memory context -q ""` | -| Need specific model's columns | `wren memory context -q "..." --model --threshold 0` | +| Default | `wren memory fetch -q ""` | +| Need specific model's columns | `wren memory fetch -q "..." --model --threshold 0` | | Memory not installed | Read `~/.wren/mdl.json` directly | ### Step 2 — Recall past queries @@ -67,9 +67,9 @@ GROUP BY 1 ORDER BY 2 DESC LIMIT 5' ### "table not found" -1. Verify model name: `wren memory context -q "" --type model --threshold 0` +1. Verify model name: `wren memory fetch -q "" --type model --threshold 0` 2. Check MDL exists: `ls ~/.wren/mdl.json` -3. Verify column: `wren memory context -q "" --model --threshold 0` +3. Verify column: `wren memory fetch -q "" --model --threshold 0` ### Connection error @@ -119,8 +119,8 @@ wren --sql "SELECT * FROM LIMIT 1" Get data back → wren --sql "..." See translated SQL only → wren dry-plan --sql "..." Validate against DB → wren dry-run --sql "..." -Schema context → wren memory context -q "..." -Filter by type/model → wren memory context -q "..." --type T --model M --threshold 0 +Schema context → wren memory fetch -q "..." +Filter by type/model → wren memory fetch -q "..." --type T --model M --threshold 0 Store confirmed query → wren memory store --nl "..." --sql "..." Few-shot examples → wren memory recall -q "..." Index stats → wren memory status diff --git a/cli-skills/wren-usage/references/memory.md b/cli-skills/wren-usage/references/memory.md index a2770164c..4c3054e25 100644 --- a/cli-skills/wren-usage/references/memory.md +++ b/cli-skills/wren-usage/references/memory.md @@ -8,8 +8,8 @@ This reference covers the decision logic for each memory command. The main workf | Command | When to use | |---------|-------------| -| `wren memory context -q "..."` | Default. Auto-selects full text (small schema) or embedding search (large schema) based on a 30K-char threshold. | -| `wren memory context -q "..." --type T --model M` | When you need filtering (forces search strategy on large schemas). | +| `wren memory fetch -q "..."` | Default. Auto-selects full text (small schema) or embedding search (large schema) based on a 30K-char threshold. | +| `wren memory fetch -q "..." --type T --model M` | When you need filtering (forces search strategy on large schemas). | | `wren memory describe` | When you want the full schema text and know it is small. | The hybrid strategy works like this: @@ -20,7 +20,7 @@ CJK-heavy schemas switch to search sooner (~1.5 chars per token vs 4 for English Override with `--threshold`: ```bash -wren memory context -q "revenue" --threshold 50000 # raise for larger context windows +wren memory fetch -q "revenue" --threshold 50000 # raise for larger context windows ``` --- @@ -30,7 +30,7 @@ wren memory context -q "revenue" --threshold 50000 # raise for larger context **When to index:** - After deploying a new or updated MDL - When `wren memory status` shows `schema_items: 0 rows` -- When `wren memory context` returns stale results (references deleted models) +- When `wren memory fetch` returns stale results (references deleted models) **When NOT to index:** - Before every query — indexing is expensive, do it once per MDL change @@ -89,7 +89,7 @@ Session start: User asks a question: 2. wren memory recall -q "" --limit 3 - 3. wren memory context -q "" + 3. wren memory fetch -q "" 4. Write SQL using recalled examples + schema context 5. wren --sql "..." diff --git a/wren/README.md b/wren/README.md index 65d28673f..506d4dc20 100644 --- a/wren/README.md +++ b/wren/README.md @@ -63,7 +63,7 @@ For the full CLI reference and per-datasource `connection_info.json` formats, se ```bash wren memory index # index MDL schema -wren memory search -q "customer order price" # find relevant models/columns +wren memory fetch -q "customer order price" # fetch relevant schema context wren memory store --nl "top customers" --sql "SELECT ..." # store NL→SQL pair wren memory recall -q "best customers" # retrieve similar past queries ``` diff --git a/wren/docs/cli.md b/wren/docs/cli.md index 3a5bf0615..96396dc1d 100644 --- a/wren/docs/cli.md +++ b/wren/docs/cli.md @@ -82,7 +82,7 @@ When providing schema context to an LLM, there is a trade-off: - **Small schemas** — the full plain-text description fits easily in the LLM context window and gives better results because the LLM sees the complete structure (model→column relationships, join paths, primary keys) rather than isolated fragments from a vector search. - **Large schemas** — the full text exceeds what is practical to send in a single prompt, so embedding search is needed to retrieve only the relevant fragments. -`wren memory context` automatically picks the right strategy based on the **character length** of the generated plain-text description: +`wren memory fetch` automatically picks the right strategy based on the **character length** of the generated plain-text description: | Schema size | Threshold | Strategy | |---|---|---| @@ -111,16 +111,16 @@ wren memory describe # uses ~/.wren/mdl.json wren memory describe --mdl /path/to/mdl.json ``` -### `wren memory context` +### `wren memory fetch` Get schema context for an LLM. Automatically chooses the best strategy based on schema size: full plain text for small schemas, embedding search for large schemas. When using the search strategy, optional `--type` and `--model` filters narrow the results. ```bash -wren memory context -q "customer order price" -wren memory context -q "revenue" --type column --model orders -wren memory context -q "日期" --threshold 50000 --output json +wren memory fetch -q "customer order price" +wren memory fetch -q "revenue" --type column --model orders +wren memory fetch -q "日期" --threshold 50000 --output json ``` | Flag | Description | diff --git a/wren/src/wren/memory/cli.py b/wren/src/wren/memory/cli.py index 595a51188..3f15794da 100644 --- a/wren/src/wren/memory/cli.py +++ b/wren/src/wren/memory/cli.py @@ -130,7 +130,7 @@ def describe( @memory_app.command() -def context( +def fetch( query: Annotated[str, typer.Option("--query", "-q", help="Search query")], mdl: MdlOpt = None, limit: Annotated[int, typer.Option("--limit", "-l")] = 5, From d5f80e7f54f750760cdf88ded6005bc75c8b3763 Mon Sep 17 00:00:00 2001 From: Jax Liu Date: Tue, 31 Mar 2026 09:30:43 +0800 Subject: [PATCH 09/16] chore(skills): remove wren-memory from MCP skills Memory skills are CLI-specific and live in cli-skills/ now. Remove from skills/, index.json, versions.json, and revert wren-usage dependency/delegation references. Co-Authored-By: Claude Opus 4.6 (1M context) --- skills/index.json | 16 ---- skills/versions.json | 3 +- skills/wren-memory/SKILL.md | 172 ------------------------------------ skills/wren-usage/SKILL.md | 4 +- 4 files changed, 2 insertions(+), 193 deletions(-) delete mode 100644 skills/wren-memory/SKILL.md diff --git a/skills/index.json b/skills/index.json index 41b57ee04..4f1ca26c0 100644 --- a/skills/index.json +++ b/skills/index.json @@ -112,21 +112,6 @@ ], "repository": "https://github.com/Canner/wren-engine/tree/main/skills/wren-http-api" }, - { - "name": "wren-memory", - "version": "1.0", - "description": "Schema and query memory for Wren Engine — index MDL schema for semantic search, store and recall NL-to-SQL pairs for few-shot retrieval.", - "tags": [ - "wren", - "memory", - "lancedb", - "embedding", - "semantic-search", - "few-shot", - "text-to-sql" - ], - "repository": "https://github.com/Canner/wren-engine/tree/main/skills/wren-memory" - }, { "name": "wren-usage", "version": "1.2", @@ -147,7 +132,6 @@ "wren-generate-mdl", "wren-project", "wren-sql", - "wren-memory", "wren-mcp-setup", "wren-http-api" ], diff --git a/skills/versions.json b/skills/versions.json index f986be01f..67b174e04 100644 --- a/skills/versions.json +++ b/skills/versions.json @@ -6,6 +6,5 @@ "wren-mcp-setup": "1.4", "wren-quickstart": "1.3", "wren-http-api": "1.0", - "wren-usage": "1.2", - "wren-memory": "1.0" + "wren-usage": "1.2" } diff --git a/skills/wren-memory/SKILL.md b/skills/wren-memory/SKILL.md deleted file mode 100644 index 22d70ea21..000000000 --- a/skills/wren-memory/SKILL.md +++ /dev/null @@ -1,172 +0,0 @@ ---- -name: wren-memory -description: "Schema and query memory for Wren Engine — index MDL schema for semantic search, store and recall NL-to-SQL pairs for few-shot retrieval. Guides the agent on when to index, search, store, and recall using the wren memory CLI. Use when: the agent needs schema context before writing SQL, wants to find relevant models/columns by natural language, or should store a confirmed-correct query for future reference." -license: Apache-2.0 -metadata: - author: wren-engine - version: "1.0" ---- - -# Wren Memory - -## Version check - -Silently fetch `https://raw.githubusercontent.com/Canner/wren-engine/main/skills/versions.json`. -Compare the `wren-memory` key with this skill's version (from the frontmatter above). -If the remote version is newer, notify the user before proceeding: - -> A newer version of the **wren-memory** skill is available. -> Update with: -> ``` -> npx skills add Canner/wren-engine --skill wren-memory --agent claude-code -> ``` - -Then continue with the workflow below regardless of update status. - ---- - -## What is Wren Memory? - -Wren Memory is a LanceDB-backed local memory layer for the `wren` CLI. It provides two capabilities: - -1. **Schema Memory** — index MDL models, columns, relationships, and views for semantic search -2. **Query Memory** — store confirmed NL-to-SQL pairs for few-shot retrieval - -Requires the `memory` extra: `pip install 'wren-engine[memory]'` - ---- - -## When to use each command - -### Schema context: `context` > `describe` > `search` - -Before writing SQL, you need schema context. Choose the right command: - -| Command | When to use | -|---------|-------------| -| `wren memory context -q "..."` | **Default choice.** Auto-selects full text or embedding search based on schema size. | -| `wren memory describe` | When you want the full schema and know it is small. | -| `wren memory search -q "..."` | When you need fine-grained filtering (by item type or model name). | - -**Workflow — before writing SQL:** - -``` -1. User asks a question in natural language -2. Run: wren memory context -q "" --mdl ~/.wren/mdl.json -3. Use the returned schema context to write SQL -4. If context strategy is "search" and results are insufficient, - try a broader query or different keywords -``` - -### Indexing: `index` - -Index the MDL schema so that `search` and `context` (search strategy) work. - -**When to index:** -- After deploying a new or updated MDL (`wren memory index`) -- When `wren memory status` shows `schema_items: 0 rows` -- When the MDL has changed and the schema memory is stale - -**When NOT to index:** -- Before every query — indexing is expensive, do it once per MDL change -- When only using `describe` or `context` with full strategy — those read the MDL directly - -```bash -wren memory index --mdl ~/.wren/mdl.json -``` - -### Storing queries: `store` - -Store a NL-to-SQL pair so it can be recalled later as a few-shot example. - -**When to store — ALL of these must be true:** -1. The SQL query executed successfully -2. The user confirmed the result is correct, OR the user continued working with the result (asked a follow-up question, exported the data, etc.) -3. There is a clear natural language question that the SQL answers - -**When NOT to store:** -- The query failed or returned an error -- The user said the result is wrong or asked to fix it -- The query is exploratory / throwaway (e.g. `SELECT * FROM orders LIMIT 5`) -- There is no natural language question — just raw SQL from the user -- The user explicitly asked not to store it - -```bash -wren memory store \ - --nl "top 5 customers by revenue last quarter" \ - --sql "SELECT c_name, SUM(o_totalprice) AS revenue FROM orders JOIN customer ON orders.o_custkey = customer.c_custkey WHERE o_orderdate >= DATE '2025-10-01' GROUP BY 1 ORDER BY 2 DESC LIMIT 5" \ - --datasource postgres -``` - -**Important:** The `--nl` value should be the user's original question, not a paraphrase. This ensures the embedding matches future similar questions. - -### Recalling queries: `recall` - -Search past NL-to-SQL pairs for few-shot examples before writing new SQL. - -**When to recall:** -- Before writing SQL for a new question, especially if it is complex -- When the user asks something similar to a past question -- When you want few-shot examples to improve SQL quality - -**Workflow — SQL generation with memory:** - -``` -1. User asks: "show me monthly revenue trend for the top 3 product categories" -2. Run: wren memory recall -q "monthly revenue trend by product category" --limit 3 -3. Run: wren memory context -q "revenue product category monthly" --mdl ~/.wren/mdl.json -4. Use recalled queries as few-shot examples + schema context to write SQL -5. Execute the SQL -6. If user confirms result → store the new pair -``` - -### Housekeeping: `status` and `reset` - -```bash -wren memory status # check what is indexed -wren memory reset --force # clear everything and start fresh -``` - ---- - -## Complete workflow example - -Here is the full lifecycle for a typical agent session: - -``` -Session start: - 1. Check: wren memory status - → If schema_items is 0 or MDL has changed: wren memory index - -User asks a question: - 2. Recall: wren memory recall -q "" --limit 3 - → Use any results as few-shot examples - 3. Context: wren memory context -q "" - → Get relevant schema information - 4. Write SQL using recalled examples + schema context - 5. Execute: wren query --sql "..." - -After successful execution: - 6. Show results to user - 7. Wait for user's response: - - User confirms / continues → wren memory store --nl "..." --sql "..." - - User says "wrong" / asks to fix → do NOT store, fix the SQL instead - - User says nothing and moves on → do NOT store (silence ≠ confirmation) -``` - ---- - -## CLI quick reference - -| Command | Purpose | -|---------|---------| -| `wren memory index [--mdl PATH]` | Index MDL schema into LanceDB | -| `wren memory describe [--mdl PATH]` | Print full schema as plain text | -| `wren memory context -q "..." [--mdl PATH]` | Get schema context (auto full/search) | -| `wren memory search -q "..." [--type T] [--model M]` | Semantic search over schema | -| `wren memory store --nl "..." --sql "..."` | Store a confirmed NL-to-SQL pair | -| `wren memory recall -q "..." [--limit N]` | Recall similar past queries | -| `wren memory status` | Show index statistics | -| `wren memory reset [--force]` | Drop all memory tables | - -All commands accept `--path DIR` to override the default storage location (`~/.wren/memory/`). diff --git a/skills/wren-usage/SKILL.md b/skills/wren-usage/SKILL.md index 7e8d0b926..4b25573a8 100644 --- a/skills/wren-usage/SKILL.md +++ b/skills/wren-usage/SKILL.md @@ -41,7 +41,7 @@ npx skills add Canner/wren-engine --skill '*' --agent claude-code clawhub install wren-usage ``` -This installs `wren-usage` and its dependent skills (`wren-connection-info`, `wren-generate-mdl`, `wren-project`, `wren-sql`, `wren-memory`, `wren-mcp-setup`, `wren-http-api`) into `~/.claude/skills/`. +This installs `wren-usage` and its dependent skills (`wren-connection-info`, `wren-generate-mdl`, `wren-project`, `wren-sql`, `wren-mcp-setup`, `wren-http-api`) into `~/.claude/skills/`. After installation, the user must **start a new session** for the new skills to be loaded. @@ -56,8 +56,6 @@ Identify the user's intent and delegate to the appropriate skill: | Task | Skill | |------|-------| | Write or debug a SQL query | `@wren-sql` | -| Get schema context / find relevant models before writing SQL | `@wren-memory` | -| Store a confirmed NL-to-SQL pair / recall past queries | `@wren-memory` | | Connect to a new database / change credentials | `@wren-connection-info` | | Generate MDL from an existing database | `@wren-generate-mdl` | | Save MDL to YAML files (version control) | `@wren-project` | From 34bcaa7dc4f261ad07d044bce568c399a4fed59a Mon Sep 17 00:00:00 2001 From: Jax Liu Date: Tue, 31 Mar 2026 09:50:29 +0800 Subject: [PATCH 10/16] fix(wren): remove tableReference from schema describe output Users query model names (SELECT * FROM model_name), not physical tables. Table references are an internal MDL detail that adds noise to the LLM context without helping SQL generation. Co-Authored-By: Claude Opus 4.6 (1M context) --- wren/src/wren/memory/schema_indexer.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/wren/src/wren/memory/schema_indexer.py b/wren/src/wren/memory/schema_indexer.py index d22b5ab57..28703de62 100644 --- a/wren/src/wren/memory/schema_indexer.py +++ b/wren/src/wren/memory/schema_indexer.py @@ -63,12 +63,6 @@ def _describe_model(model: dict, lines: list[str]) -> None: header += f" — {desc}" lines.append(header) - ref = model.get("tableReference") - if ref: - if isinstance(ref, dict): - ref = ".".join(filter(None, [ref.get("schema"), ref.get("table")])) - lines.append(f" Table: {ref}") - pk = model.get("primaryKey") if pk: lines.append(f" Primary key: {pk}") From 71e54289dbbd4a081d5790f07dbd6763ce23ea65 Mon Sep 17 00:00:00 2001 From: Jax Liu Date: Tue, 31 Mar 2026 09:53:39 +0800 Subject: [PATCH 11/16] update lock --- wren/uv.lock | 1029 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 991 insertions(+), 38 deletions(-) diff --git a/wren/uv.lock b/wren/uv.lock index a29e3033c..d1c8408b7 100644 --- a/wren/uv.lock +++ b/wren/uv.lock @@ -34,6 +34,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643 }, ] +[[package]] +name = "anyio" +version = "4.13.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/19/14/2c5dd9f512b66549ae92767a9c7b330ae88e1932ca57876909410251fe13/anyio-4.13.0.tar.gz", hash = "sha256:334b70e641fd2221c1505b3890c69882fe4a2df910cba14d97019b90b24439dc", size = 231622 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/da/42/e921fccf5015463e32a3cf6ee7f980a6ed0f395ceeaa45060b61d86486c2/anyio-4.13.0-py3-none-any.whl", hash = "sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708", size = 114353 }, +] + [[package]] name = "asn1crypto" version = "1.5.1" @@ -407,6 +420,77 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/bc/58/6b3d24e6b9bc474a2dcdee65dfd1f008867015408a271562e4b690561a4d/cryptography-46.0.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:8456928655f856c6e1533ff59d5be76578a7157224dbd9ce6872f25055ab9ab7", size = 3407605 }, ] +[[package]] +name = "cuda-bindings" +version = "13.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cuda-pathfinder", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/e0/a9/3a8241c6e19483ac1f1dcf5c10238205dcb8a6e9d0d4d4709240dff28ff4/cuda_bindings-13.2.0-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:721104c603f059780d287969be3d194a18d0cc3b713ed9049065a1107706759d", size = 5730273 }, + { url = "https://files.pythonhosted.org/packages/e9/94/2748597f47bb1600cd466b20cab4159f1530a3a33fe7f70fee199b3abb9e/cuda_bindings-13.2.0-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1eba9504ac70667dd48313395fe05157518fd6371b532790e96fbb31bbb5a5e1", size = 6313924 }, + { url = "https://files.pythonhosted.org/packages/52/c8/b2589d68acf7e3d63e2be330b84bc25712e97ed799affbca7edd7eae25d6/cuda_bindings-13.2.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e865447abfb83d6a98ad5130ed3c70b1fc295ae3eeee39fd07b4ddb0671b6788", size = 5722404 }, + { url = "https://files.pythonhosted.org/packages/1f/92/f899f7bbb5617bb65ec52a6eac1e9a1447a86b916c4194f8a5001b8cde0c/cuda_bindings-13.2.0-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:46d8776a55d6d5da9dd6e9858fba2efcda2abe6743871dee47dd06eb8cb6d955", size = 6320619 }, + { url = "https://files.pythonhosted.org/packages/df/93/eef988860a3ca985f82c4f3174fc0cdd94e07331ba9a92e8e064c260337f/cuda_bindings-13.2.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6629ca2df6f795b784752409bcaedbd22a7a651b74b56a165ebc0c9dcbd504d0", size = 5614610 }, + { url = "https://files.pythonhosted.org/packages/18/23/6db3aba46864aee357ab2415135b3fe3da7e9f1fa0221fa2a86a5968099c/cuda_bindings-13.2.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7dca0da053d3b4cc4869eff49c61c03f3c5dbaa0bcd712317a358d5b8f3f385d", size = 6149914 }, + { url = "https://files.pythonhosted.org/packages/c0/87/87a014f045b77c6de5c8527b0757fe644417b184e5367db977236a141602/cuda_bindings-13.2.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a6464b30f46692d6c7f65d4a0e0450d81dd29de3afc1bb515653973d01c2cd6e", size = 5685673 }, + { url = "https://files.pythonhosted.org/packages/ee/5e/c0fe77a73aaefd3fff25ffaccaac69c5a63eafdf8b9a4c476626ef0ac703/cuda_bindings-13.2.0-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f4af9f3e1be603fa12d5ad6cfca7844c9d230befa9792b5abdf7dd79979c3626", size = 6191386 }, + { url = "https://files.pythonhosted.org/packages/5f/58/ed2c3b39c8dd5f96aa7a4abef0d47a73932c7a988e30f5fa428f00ed0da1/cuda_bindings-13.2.0-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:df850a1ff8ce1b3385257b08e47b70e959932f5f432d0a4e46a355962b4e4771", size = 5507469 }, + { url = "https://files.pythonhosted.org/packages/1f/01/0c941b112ceeb21439b05895eace78ca1aa2eaaf695c8521a068fd9b4c00/cuda_bindings-13.2.0-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e8a16384c6494e5485f39314b0b4afb04bee48d49edb16d5d8593fd35bbd231b", size = 6059693 }, +] + +[[package]] +name = "cuda-pathfinder" +version = "1.5.0" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/93/66/0c02bd330e7d976f83fa68583d6198d76f23581bcbb5c0e98a6148f326e5/cuda_pathfinder-1.5.0-py3-none-any.whl", hash = "sha256:498f90a9e9de36044a7924742aecce11c50c49f735f1bc53e05aa46de9ea4110", size = 49739 }, +] + +[[package]] +name = "cuda-toolkit" +version = "13.0.2" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/57/b2/453099f5f3b698d7d0eab38916aac44c7f76229f451709e2eb9db6615dcd/cuda_toolkit-13.0.2-py2.py3-none-any.whl", hash = "sha256:b198824cf2f54003f50d64ada3a0f184b42ca0846c1c94192fa269ecd97a66eb", size = 2364 }, +] + +[package.optional-dependencies] +cublas = [ + { name = "nvidia-cublas", marker = "sys_platform == 'linux'" }, +] +cudart = [ + { name = "nvidia-cuda-runtime", marker = "sys_platform == 'linux'" }, +] +cufft = [ + { name = "nvidia-cufft", marker = "sys_platform == 'linux'" }, +] +cufile = [ + { name = "nvidia-cufile", marker = "sys_platform == 'linux'" }, +] +cupti = [ + { name = "nvidia-cuda-cupti", marker = "sys_platform == 'linux'" }, +] +curand = [ + { name = "nvidia-curand", marker = "sys_platform == 'linux'" }, +] +cusolver = [ + { name = "nvidia-cusolver", marker = "sys_platform == 'linux'" }, +] +cusparse = [ + { name = "nvidia-cusparse", marker = "sys_platform == 'linux'" }, +] +nvjitlink = [ + { name = "nvidia-nvjitlink", marker = "sys_platform == 'linux'" }, +] +nvrtc = [ + { name = "nvidia-cuda-nvrtc", marker = "sys_platform == 'linux'" }, +] +nvtx = [ + { name = "nvidia-nvtx", marker = "sys_platform == 'linux'" }, +] + [[package]] name = "databricks-sdk" version = "0.99.0" @@ -457,6 +541,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/01/80/171c7c5b78e60ab25d6f11e3d38675fe7ef843ddc79a7fd26916d3a6ca05/db_dtypes-1.5.0-py3-none-any.whl", hash = "sha256:abdbb2e4eb965800ed6f98af0c5c1cafff9063ace09114be2d26a7f046be2c8a", size = 18267 }, ] +[[package]] +name = "deprecation" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "packaging" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5a/d3/8ae2869247df154b64c1884d7346d412fed0c49df84db635aab2d1c40e62/deprecation-2.1.0.tar.gz", hash = "sha256:72b3bde64e5d778694b0cf68178aed03d15e15477116add3fb773e581f9518ff", size = 173788 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/02/c3/253a89ee03fc9b9682f1541728eb66db7db22148cd94f89ab22528cd1e1b/deprecation-2.1.0-py2.py3-none-any.whl", hash = "sha256:a10811591210e1fb0e768a8c25517cabeabcba6f0bf96564f8ff45189f90b14a", size = 11178 }, +] + [[package]] name = "docker" version = "7.1.0" @@ -805,6 +901,95 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/83/8a/1241ec22c41028bddd4a052ae9369267b4475265ad0ce7140974548dc3fa/grpcio_status-1.78.0-py3-none-any.whl", hash = "sha256:b492b693d4bf27b47a6c32590701724f1d3b9444b36491878fb71f6208857f34", size = 14523 }, ] +[[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 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515 }, +] + +[[package]] +name = "hf-xet" +version = "1.4.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/09/08/23c84a26716382c89151b5b447b4beb19e3345f3a93d3b73009a71a57ad3/hf_xet-1.4.2.tar.gz", hash = "sha256:b7457b6b482d9e0743bd116363239b1fa904a5e65deede350fbc0c4ea67c71ea", size = 672357 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/06/e8cf74c3c48e5485c7acc5a990d0d8516cdfb5fdf80f799174f1287cc1b5/hf_xet-1.4.2-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:ac8202ae1e664b2c15cdfc7298cbb25e80301ae596d602ef7870099a126fcad4", size = 3796125 }, + { url = "https://files.pythonhosted.org/packages/66/d4/b73ebab01cbf60777323b7de9ef05550790451eb5172a220d6b9845385ec/hf_xet-1.4.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:6d2f8ee39fa9fba9af929f8c0d0482f8ee6e209179ad14a909b6ad78ffcb7c81", size = 3555985 }, + { url = "https://files.pythonhosted.org/packages/ff/e7/ded6d1bd041c3f2bca9e913a0091adfe32371988e047dd3a68a2463c15a2/hf_xet-1.4.2-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4642a6cf249c09da8c1f87fe50b24b2a3450b235bf8adb55700b52f0ea6e2eb6", size = 4212085 }, + { url = "https://files.pythonhosted.org/packages/97/c1/a0a44d1f98934f7bdf17f7a915b934f9fca44bb826628c553589900f6df8/hf_xet-1.4.2-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:769431385e746c92dc05492dde6f687d304584b89c33d79def8367ace06cb555", size = 3988266 }, + { url = "https://files.pythonhosted.org/packages/7a/82/be713b439060e7d1f1d93543c8053d4ef2fe7e6922c5b31642eaa26f3c4b/hf_xet-1.4.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c9dd1c1bc4cc56168f81939b0e05b4c36dd2d28c13dc1364b17af89aa0082496", size = 4188513 }, + { url = "https://files.pythonhosted.org/packages/21/a6/cbd4188b22abd80ebd0edbb2b3e87f2633e958983519980815fb8314eae5/hf_xet-1.4.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:fca58a2ae4e6f6755cc971ac6fcdf777ea9284d7e540e350bb000813b9a3008d", size = 4428287 }, + { url = "https://files.pythonhosted.org/packages/b2/4e/84e45b25e2e3e903ed3db68d7eafa96dae9a1d1f6d0e7fc85120347a852f/hf_xet-1.4.2-cp313-cp313t-win_amd64.whl", hash = "sha256:163aab46854ccae0ab6a786f8edecbbfbaa38fcaa0184db6feceebf7000c93c0", size = 3665574 }, + { url = "https://files.pythonhosted.org/packages/ee/71/c5ac2b9a7ae39c14e91973035286e73911c31980fe44e7b1d03730c00adc/hf_xet-1.4.2-cp313-cp313t-win_arm64.whl", hash = "sha256:09b138422ecbe50fd0c84d4da5ff537d27d487d3607183cd10e3e53f05188e82", size = 3528760 }, + { url = "https://files.pythonhosted.org/packages/1e/0f/fcd2504015eab26358d8f0f232a1aed6b8d363a011adef83fe130bff88f7/hf_xet-1.4.2-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:949dcf88b484bb9d9276ca83f6599e4aa03d493c08fc168c124ad10b2e6f75d7", size = 3796493 }, + { url = "https://files.pythonhosted.org/packages/82/56/19c25105ff81731ca6d55a188b5de2aa99d7a2644c7aa9de1810d5d3b726/hf_xet-1.4.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:41659966020d59eb9559c57de2cde8128b706a26a64c60f0531fa2318f409418", size = 3555797 }, + { url = "https://files.pythonhosted.org/packages/bf/e3/8933c073186849b5e06762aa89847991d913d10a95d1603eb7f2c3834086/hf_xet-1.4.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5c588e21d80010119458dd5d02a69093f0d115d84e3467efe71ffb2c67c19146", size = 4212127 }, + { url = "https://files.pythonhosted.org/packages/eb/01/f89ebba4e369b4ed699dcb60d3152753870996f41c6d22d3d7cac01310e1/hf_xet-1.4.2-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:a296744d771a8621ad1d50c098d7ab975d599800dae6d48528ba3944e5001ba0", size = 3987788 }, + { url = "https://files.pythonhosted.org/packages/84/4d/8a53e5ffbc2cc33bbf755382ac1552c6d9af13f623ed125fe67cc3e6772f/hf_xet-1.4.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:f563f7efe49588b7d0629d18d36f46d1658fe7e08dce3fa3d6526e1c98315e2d", size = 4188315 }, + { url = "https://files.pythonhosted.org/packages/d1/b8/b7a1c1b5592254bd67050632ebbc1b42cc48588bf4757cb03c2ef87e704a/hf_xet-1.4.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5b2e0132c56d7ee1bf55bdb638c4b62e7106f6ac74f0b786fed499d5548c5570", size = 4428306 }, + { url = "https://files.pythonhosted.org/packages/a0/0c/40779e45b20e11c7c5821a94135e0207080d6b3d76e7b78ccb413c6f839b/hf_xet-1.4.2-cp314-cp314t-win_amd64.whl", hash = "sha256:2f45c712c2fa1215713db10df6ac84b49d0e1c393465440e9cb1de73ecf7bbf6", size = 3665826 }, + { url = "https://files.pythonhosted.org/packages/51/4c/e2688c8ad1760d7c30f7c429c79f35f825932581bc7c9ec811436d2f21a0/hf_xet-1.4.2-cp314-cp314t-win_arm64.whl", hash = "sha256:6d53df40616f7168abfccff100d232e9d460583b9d86fa4912c24845f192f2b8", size = 3529113 }, + { url = "https://files.pythonhosted.org/packages/b4/86/b40b83a2ff03ef05c4478d2672b1fc2b9683ff870e2b25f4f3af240f2e7b/hf_xet-1.4.2-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:71f02d6e4cdd07f344f6844845d78518cc7186bd2bc52d37c3b73dc26a3b0bc5", size = 3800339 }, + { url = "https://files.pythonhosted.org/packages/64/2e/af4475c32b4378b0e92a587adb1aa3ec53e3450fd3e5fe0372a874531c00/hf_xet-1.4.2-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:e9b38d876e94d4bdcf650778d6ebbaa791dd28de08db9736c43faff06ede1b5a", size = 3559664 }, + { url = "https://files.pythonhosted.org/packages/3c/4c/781267da3188db679e601de18112021a5cb16506fe86b246e22c5401a9c4/hf_xet-1.4.2-cp37-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:77e8c180b7ef12d8a96739a4e1e558847002afe9ea63b6f6358b2271a8bdda1c", size = 4217422 }, + { url = "https://files.pythonhosted.org/packages/68/47/d6cf4a39ecf6c7705f887a46f6ef5c8455b44ad9eb0d391aa7e8a2ff7fea/hf_xet-1.4.2-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:c3b3c6a882016b94b6c210957502ff7877802d0dbda8ad142c8595db8b944271", size = 3992847 }, + { url = "https://files.pythonhosted.org/packages/2d/ef/e80815061abff54697239803948abc665c6b1d237102c174f4f7a9a5ffc5/hf_xet-1.4.2-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9d9a634cc929cfbaf2e1a50c0e532ae8c78fa98618426769480c58501e8c8ac2", size = 4193843 }, + { url = "https://files.pythonhosted.org/packages/54/75/07f6aa680575d9646c4167db6407c41340cbe2357f5654c4e72a1b01ca14/hf_xet-1.4.2-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6b0932eb8b10317ea78b7da6bab172b17be03bbcd7809383d8d5abd6a2233e04", size = 4432751 }, + { url = "https://files.pythonhosted.org/packages/cd/71/193eabd7e7d4b903c4aa983a215509c6114915a5a237525ec562baddb868/hf_xet-1.4.2-cp37-abi3-win_amd64.whl", hash = "sha256:ad185719fb2e8ac26f88c8100562dbf9dbdcc3d9d2add00faa94b5f106aea53f", size = 3671149 }, + { url = "https://files.pythonhosted.org/packages/b4/7e/ccf239da366b37ba7f0b36095450efae4a64980bdc7ec2f51354205fdf39/hf_xet-1.4.2-cp37-abi3-win_arm64.whl", hash = "sha256:32c012286b581f783653e718c1862aea5b9eb140631685bb0c5e7012c8719a87", size = 3533426 }, +] + +[[package]] +name = "httpcore" +version = "1.0.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784 }, +] + +[[package]] +name = "httpx" +version = "0.28.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "certifi" }, + { name = "httpcore" }, + { name = "idna" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517 }, +] + +[[package]] +name = "huggingface-hub" +version = "1.8.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "filelock" }, + { name = "fsspec" }, + { name = "hf-xet", marker = "platform_machine == 'AMD64' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'arm64' or platform_machine == 'x86_64'" }, + { name = "httpx" }, + { name = "packaging" }, + { name = "pyyaml" }, + { name = "tqdm" }, + { name = "typer" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8e/2a/a847fd02261cd051da218baf99f90ee7c7040c109a01833db4f838f25256/huggingface_hub-1.8.0.tar.gz", hash = "sha256:c5627b2fd521e00caf8eff4ac965ba988ea75167fad7ee72e17f9b7183ec63f3", size = 735839 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a9/ae/8a3a16ea4d202cb641b51d2681bdd3d482c1c592d7570b3fa264730829ce/huggingface_hub-1.8.0-py3-none-any.whl", hash = "sha256:d3eb5047bd4e33c987429de6020d4810d38a5bef95b3b40df9b17346b7f353f2", size = 625208 }, +] + [[package]] name = "ibis-framework" version = "12.0.0" @@ -920,6 +1105,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484 }, ] +[[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 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899 }, +] + [[package]] name = "jmespath" version = "1.1.0" @@ -929,6 +1126,63 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/14/2f/967ba146e6d58cf6a652da73885f52fc68001525b4197effc174321d70b4/jmespath-1.1.0-py3-none-any.whl", hash = "sha256:a5663118de4908c91729bea0acadca56526eb2698e83de10cd116ae0f4e97c64", size = 20419 }, ] +[[package]] +name = "joblib" +version = "1.5.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/41/f2/d34e8b3a08a9cc79a50b2208a93dce981fe615b64d5a4d4abee421d898df/joblib-1.5.3.tar.gz", hash = "sha256:8561a3269e6801106863fd0d6d84bb737be9e7631e33aaed3fb9ce5953688da3", size = 331603 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7b/91/984aca2ec129e2757d1e4e3c81c3fcda9d0f85b74670a094cc443d9ee949/joblib-1.5.3-py3-none-any.whl", hash = "sha256:5fc3c5039fc5ca8c0276333a188bbd59d6b7ab37fe6632daa76bc7f9ec18e713", size = 309071 }, +] + +[[package]] +name = "lance-namespace" +version = "0.6.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "lance-namespace-urllib3-client" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/28/9f/7906ba4117df8d965510285eaf07264a77de2fd283b9d44ec7fc63a4a57a/lance_namespace-0.6.1.tar.gz", hash = "sha256:f0deea442bd3f1056a8e2fed056ae2778e3356517ec2e680db049058b824d131", size = 10666 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/91/aee1c0a04d17f2810173bd304bd444eb78332045df1b0c1b07cebd01f530/lance_namespace-0.6.1-py3-none-any.whl", hash = "sha256:9699c9e3f12236e5e08ea979cc4e036a8e3c67ed2f37ae6f25c5353ab908e1be", size = 12498 }, +] + +[[package]] +name = "lance-namespace-urllib3-client" +version = "0.6.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pydantic" }, + { name = "python-dateutil" }, + { name = "typing-extensions" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/63/a1/8706a2be25bd184acccc411e48f1a42a4cbf3b6556cba15b9fcf4c15cfcc/lance_namespace_urllib3_client-0.6.1.tar.gz", hash = "sha256:31fbd058ce1ea0bf49045cdeaa756360ece0bc61e9e10276f41af6d217debe87", size = 182567 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cd/c7/cb9580602dec25f0fdd6005c1c9ba1d4c8c0c3dc8d543107e5a9f248bba8/lance_namespace_urllib3_client-0.6.1-py3-none-any.whl", hash = "sha256:b9c103e1377ad46d2bd70eec894bfec0b1e2133dae0964d7e4de543c6e16293b", size = 317111 }, +] + +[[package]] +name = "lancedb" +version = "0.30.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "deprecation" }, + { name = "lance-namespace" }, + { name = "numpy" }, + { name = "overrides", marker = "python_full_version < '3.12'" }, + { name = "packaging" }, + { name = "pyarrow" }, + { name = "pydantic" }, + { name = "tqdm" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/0e/f7/2692ddedcf966497d1356ab673c50672fc6b3107bea73f57d196f8ad9fd8/lancedb-0.30.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:34cf0d49e755313867c04e0f3e7e4dde222e31110a60ca45ef0dc4c72d9a705d", size = 41960617 }, + { url = "https://files.pythonhosted.org/packages/73/35/c7d9b738338f41b7b0f92a45860426d4f2565d798721cb09d17c1f3beab9/lancedb-0.30.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f21d794a506ff2bba77ba7eba12a476f518c62c78803af765ebd35b83f9097a3", size = 43862793 }, + { url = "https://files.pythonhosted.org/packages/fd/fc/5a4547a140b820d19e282005a4b1fea025ac39f3f284231e8e226cc90e48/lancedb-0.30.1-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:2eaebfa5add91c0185be4baa0c1a714877e203340a116102b12ad5e60bf36209", size = 43883183 }, + { url = "https://files.pythonhosted.org/packages/c4/f0/04895c6dc7b9fb797fb9243aa9e9aede54d1eef307bdf9562bbc0b4be5d7/lancedb-0.30.1-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:7beb23b096b7798f65aac6203deab0e4b4c9d541050f17bc38312e6cf143fafd", size = 46942257 }, +] + [[package]] name = "loguru" version = "0.7.3" @@ -1061,6 +1315,80 @@ 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 }, ] +[[package]] +name = "markupsafe" +version = "3.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/08/db/fefacb2136439fc8dd20e797950e749aa1f4997ed584c62cfb8ef7c2be0e/markupsafe-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad", size = 11631 }, + { url = "https://files.pythonhosted.org/packages/e1/2e/5898933336b61975ce9dc04decbc0a7f2fee78c30353c5efba7f2d6ff27a/markupsafe-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a", size = 12058 }, + { url = "https://files.pythonhosted.org/packages/1d/09/adf2df3699d87d1d8184038df46a9c80d78c0148492323f4693df54e17bb/markupsafe-3.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50", size = 24287 }, + { url = "https://files.pythonhosted.org/packages/30/ac/0273f6fcb5f42e314c6d8cd99effae6a5354604d461b8d392b5ec9530a54/markupsafe-3.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf", size = 22940 }, + { url = "https://files.pythonhosted.org/packages/19/ae/31c1be199ef767124c042c6c3e904da327a2f7f0cd63a0337e1eca2967a8/markupsafe-3.0.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f", size = 21887 }, + { url = "https://files.pythonhosted.org/packages/b2/76/7edcab99d5349a4532a459e1fe64f0b0467a3365056ae550d3bcf3f79e1e/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a", size = 23692 }, + { url = "https://files.pythonhosted.org/packages/a4/28/6e74cdd26d7514849143d69f0bf2399f929c37dc2b31e6829fd2045b2765/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115", size = 21471 }, + { url = "https://files.pythonhosted.org/packages/62/7e/a145f36a5c2945673e590850a6f8014318d5577ed7e5920a4b3448e0865d/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a", size = 22923 }, + { url = "https://files.pythonhosted.org/packages/0f/62/d9c46a7f5c9adbeeeda52f5b8d802e1094e9717705a645efc71b0913a0a8/markupsafe-3.0.3-cp311-cp311-win32.whl", hash = "sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19", size = 14572 }, + { url = "https://files.pythonhosted.org/packages/83/8a/4414c03d3f891739326e1783338e48fb49781cc915b2e0ee052aa490d586/markupsafe-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01", size = 15077 }, + { url = "https://files.pythonhosted.org/packages/35/73/893072b42e6862f319b5207adc9ae06070f095b358655f077f69a35601f0/markupsafe-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c", size = 13876 }, + { url = "https://files.pythonhosted.org/packages/5a/72/147da192e38635ada20e0a2e1a51cf8823d2119ce8883f7053879c2199b5/markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e", size = 11615 }, + { url = "https://files.pythonhosted.org/packages/9a/81/7e4e08678a1f98521201c3079f77db69fb552acd56067661f8c2f534a718/markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce", size = 12020 }, + { url = "https://files.pythonhosted.org/packages/1e/2c/799f4742efc39633a1b54a92eec4082e4f815314869865d876824c257c1e/markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d", size = 24332 }, + { url = "https://files.pythonhosted.org/packages/3c/2e/8d0c2ab90a8c1d9a24f0399058ab8519a3279d1bd4289511d74e909f060e/markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d", size = 22947 }, + { url = "https://files.pythonhosted.org/packages/2c/54/887f3092a85238093a0b2154bd629c89444f395618842e8b0c41783898ea/markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a", size = 21962 }, + { url = "https://files.pythonhosted.org/packages/c9/2f/336b8c7b6f4a4d95e91119dc8521402461b74a485558d8f238a68312f11c/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b", size = 23760 }, + { url = "https://files.pythonhosted.org/packages/32/43/67935f2b7e4982ffb50a4d169b724d74b62a3964bc1a9a527f5ac4f1ee2b/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f", size = 21529 }, + { url = "https://files.pythonhosted.org/packages/89/e0/4486f11e51bbba8b0c041098859e869e304d1c261e59244baa3d295d47b7/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b", size = 23015 }, + { url = "https://files.pythonhosted.org/packages/2f/e1/78ee7a023dac597a5825441ebd17170785a9dab23de95d2c7508ade94e0e/markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d", size = 14540 }, + { url = "https://files.pythonhosted.org/packages/aa/5b/bec5aa9bbbb2c946ca2733ef9c4ca91c91b6a24580193e891b5f7dbe8e1e/markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c", size = 15105 }, + { url = "https://files.pythonhosted.org/packages/e5/f1/216fc1bbfd74011693a4fd837e7026152e89c4bcf3e77b6692fba9923123/markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f", size = 13906 }, + { url = "https://files.pythonhosted.org/packages/38/2f/907b9c7bbba283e68f20259574b13d005c121a0fa4c175f9bed27c4597ff/markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795", size = 11622 }, + { url = "https://files.pythonhosted.org/packages/9c/d9/5f7756922cdd676869eca1c4e3c0cd0df60ed30199ffd775e319089cb3ed/markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219", size = 12029 }, + { url = "https://files.pythonhosted.org/packages/00/07/575a68c754943058c78f30db02ee03a64b3c638586fba6a6dd56830b30a3/markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6", size = 24374 }, + { url = "https://files.pythonhosted.org/packages/a9/21/9b05698b46f218fc0e118e1f8168395c65c8a2c750ae2bab54fc4bd4e0e8/markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676", size = 22980 }, + { url = "https://files.pythonhosted.org/packages/7f/71/544260864f893f18b6827315b988c146b559391e6e7e8f7252839b1b846a/markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9", size = 21990 }, + { url = "https://files.pythonhosted.org/packages/c2/28/b50fc2f74d1ad761af2f5dcce7492648b983d00a65b8c0e0cb457c82ebbe/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1", size = 23784 }, + { url = "https://files.pythonhosted.org/packages/ed/76/104b2aa106a208da8b17a2fb72e033a5a9d7073c68f7e508b94916ed47a9/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc", size = 21588 }, + { url = "https://files.pythonhosted.org/packages/b5/99/16a5eb2d140087ebd97180d95249b00a03aa87e29cc224056274f2e45fd6/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12", size = 23041 }, + { url = "https://files.pythonhosted.org/packages/19/bc/e7140ed90c5d61d77cea142eed9f9c303f4c4806f60a1044c13e3f1471d0/markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed", size = 14543 }, + { url = "https://files.pythonhosted.org/packages/05/73/c4abe620b841b6b791f2edc248f556900667a5a1cf023a6646967ae98335/markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5", size = 15113 }, + { url = "https://files.pythonhosted.org/packages/f0/3a/fa34a0f7cfef23cf9500d68cb7c32dd64ffd58a12b09225fb03dd37d5b80/markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485", size = 13911 }, + { url = "https://files.pythonhosted.org/packages/e4/d7/e05cd7efe43a88a17a37b3ae96e79a19e846f3f456fe79c57ca61356ef01/markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73", size = 11658 }, + { url = "https://files.pythonhosted.org/packages/99/9e/e412117548182ce2148bdeacdda3bb494260c0b0184360fe0d56389b523b/markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37", size = 12066 }, + { url = "https://files.pythonhosted.org/packages/bc/e6/fa0ffcda717ef64a5108eaa7b4f5ed28d56122c9a6d70ab8b72f9f715c80/markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19", size = 25639 }, + { url = "https://files.pythonhosted.org/packages/96/ec/2102e881fe9d25fc16cb4b25d5f5cde50970967ffa5dddafdb771237062d/markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025", size = 23569 }, + { url = "https://files.pythonhosted.org/packages/4b/30/6f2fce1f1f205fc9323255b216ca8a235b15860c34b6798f810f05828e32/markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6", size = 23284 }, + { url = "https://files.pythonhosted.org/packages/58/47/4a0ccea4ab9f5dcb6f79c0236d954acb382202721e704223a8aafa38b5c8/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f", size = 24801 }, + { url = "https://files.pythonhosted.org/packages/6a/70/3780e9b72180b6fecb83a4814d84c3bf4b4ae4bf0b19c27196104149734c/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb", size = 22769 }, + { url = "https://files.pythonhosted.org/packages/98/c5/c03c7f4125180fc215220c035beac6b9cb684bc7a067c84fc69414d315f5/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009", size = 23642 }, + { url = "https://files.pythonhosted.org/packages/80/d6/2d1b89f6ca4bff1036499b1e29a1d02d282259f3681540e16563f27ebc23/markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354", size = 14612 }, + { url = "https://files.pythonhosted.org/packages/2b/98/e48a4bfba0a0ffcf9925fe2d69240bfaa19c6f7507b8cd09c70684a53c1e/markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218", size = 15200 }, + { url = "https://files.pythonhosted.org/packages/0e/72/e3cc540f351f316e9ed0f092757459afbc595824ca724cbc5a5d4263713f/markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", size = 13973 }, + { url = "https://files.pythonhosted.org/packages/33/8a/8e42d4838cd89b7dde187011e97fe6c3af66d8c044997d2183fbd6d31352/markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe", size = 11619 }, + { url = "https://files.pythonhosted.org/packages/b5/64/7660f8a4a8e53c924d0fa05dc3a55c9cee10bbd82b11c5afb27d44b096ce/markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026", size = 12029 }, + { url = "https://files.pythonhosted.org/packages/da/ef/e648bfd021127bef5fa12e1720ffed0c6cbb8310c8d9bea7266337ff06de/markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737", size = 24408 }, + { url = "https://files.pythonhosted.org/packages/41/3c/a36c2450754618e62008bf7435ccb0f88053e07592e6028a34776213d877/markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97", size = 23005 }, + { url = "https://files.pythonhosted.org/packages/bc/20/b7fdf89a8456b099837cd1dc21974632a02a999ec9bf7ca3e490aacd98e7/markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d", size = 22048 }, + { url = "https://files.pythonhosted.org/packages/9a/a7/591f592afdc734f47db08a75793a55d7fbcc6902a723ae4cfbab61010cc5/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda", size = 23821 }, + { url = "https://files.pythonhosted.org/packages/7d/33/45b24e4f44195b26521bc6f1a82197118f74df348556594bd2262bda1038/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf", size = 21606 }, + { url = "https://files.pythonhosted.org/packages/ff/0e/53dfaca23a69fbfbbf17a4b64072090e70717344c52eaaaa9c5ddff1e5f0/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe", size = 23043 }, + { url = "https://files.pythonhosted.org/packages/46/11/f333a06fc16236d5238bfe74daccbca41459dcd8d1fa952e8fbd5dccfb70/markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9", size = 14747 }, + { url = "https://files.pythonhosted.org/packages/28/52/182836104b33b444e400b14f797212f720cbc9ed6ba34c800639d154e821/markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", size = 15341 }, + { url = "https://files.pythonhosted.org/packages/6f/18/acf23e91bd94fd7b3031558b1f013adfa21a8e407a3fdb32745538730382/markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4", size = 14073 }, + { url = "https://files.pythonhosted.org/packages/3c/f0/57689aa4076e1b43b15fdfa646b04653969d50cf30c32a102762be2485da/markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab", size = 11661 }, + { url = "https://files.pythonhosted.org/packages/89/c3/2e67a7ca217c6912985ec766c6393b636fb0c2344443ff9d91404dc4c79f/markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175", size = 12069 }, + { url = "https://files.pythonhosted.org/packages/f0/00/be561dce4e6ca66b15276e184ce4b8aec61fe83662cce2f7d72bd3249d28/markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634", size = 25670 }, + { url = "https://files.pythonhosted.org/packages/50/09/c419f6f5a92e5fadde27efd190eca90f05e1261b10dbd8cbcb39cd8ea1dc/markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50", size = 23598 }, + { url = "https://files.pythonhosted.org/packages/22/44/a0681611106e0b2921b3033fc19bc53323e0b50bc70cffdd19f7d679bb66/markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e", size = 23261 }, + { url = "https://files.pythonhosted.org/packages/5f/57/1b0b3f100259dc9fffe780cfb60d4be71375510e435efec3d116b6436d43/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5", size = 24835 }, + { url = "https://files.pythonhosted.org/packages/26/6a/4bf6d0c97c4920f1597cc14dd720705eca0bf7c787aebc6bb4d1bead5388/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523", size = 22733 }, + { url = "https://files.pythonhosted.org/packages/14/c7/ca723101509b518797fedc2fdf79ba57f886b4aca8a7d31857ba3ee8281f/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc", size = 23672 }, + { url = "https://files.pythonhosted.org/packages/fb/df/5bd7a48c256faecd1d36edc13133e51397e41b73bb77e1a69deab746ebac/markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", size = 14819 }, + { url = "https://files.pythonhosted.org/packages/1a/8a/0402ba61a2f16038b48b39bccca271134be00c5c9f0f623208399333c448/markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", size = 15426 }, + { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146 }, +] + [[package]] name = "mdurl" version = "0.1.2" @@ -1070,6 +1398,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 }, ] +[[package]] +name = "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 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198 }, +] + [[package]] name = "mysqlclient" version = "2.2.8" @@ -1083,6 +1420,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/66/e5/037d55623be9f681236e04abe12e1290847c06bd48270c3f19ac33493cbf/mysqlclient-2.2.8-cp314-cp314t-win_amd64.whl", hash = "sha256:260cce0e81446c83bf0a389e0fae38d68547d9f8fc0833bc733014e10ce28a99", size = 213067 }, ] +[[package]] +name = "networkx" +version = "3.6.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6a/51/63fe664f3908c97be9d2e4f1158eb633317598cfa6e1fc14af5383f17512/networkx-3.6.1.tar.gz", hash = "sha256:26b7c357accc0c8cde558ad486283728b65b6a95d85ee1cd66bafab4c8168509", size = 2517025 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/c9/b2622292ea83fbb4ec318f5b9ab867d0a28ab43c5717bb85b0a5f6b3b0a4/networkx-3.6.1-py3-none-any.whl", hash = "sha256:d47fbf302e7d9cbbb9e2555a0d267983d2aa476bac30e90dfbe5669bd57f3762", size = 2068504 }, +] + [[package]] name = "numpy" version = "2.4.3" @@ -1162,6 +1508,155 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/57/a7/b35835e278c18b85206834b3aa3abe68e77a98769c59233d1f6300284781/numpy-2.4.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:4b42639cdde6d24e732ff823a3fa5b701d8acad89c4142bc1d0bd6dc85200ba5", size = 12504685 }, ] +[[package]] +name = "nvidia-cublas" +version = "13.1.0.3" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e1/a5/fce49e2ae977e0ccc084e5adafceb4f0ac0c8333cb6863501618a7277f67/nvidia_cublas-13.1.0.3-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:c86fc7f7ae36d7528288c5d88098edcb7b02c633d262e7ddbb86b0ad91be5df2", size = 542851226 }, + { url = "https://files.pythonhosted.org/packages/e7/44/423ac00af4dd95a5aeb27207e2c0d9b7118702149bf4704c3ddb55bb7429/nvidia_cublas-13.1.0.3-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:ee8722c1f0145ab246bccb9e452153b5e0515fd094c3678df50b2a0888b8b171", size = 423133236 }, +] + +[[package]] +name = "nvidia-cuda-cupti" +version = "13.0.85" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/2a/80353b103fc20ce05ef51e928daed4b6015db4aaa9162ed0997090fe2250/nvidia_cuda_cupti-13.0.85-py3-none-manylinux_2_25_aarch64.whl", hash = "sha256:796bd679890ee55fb14a94629b698b6db54bcfd833d391d5e94017dd9d7d3151", size = 10310827 }, + { url = "https://files.pythonhosted.org/packages/33/6d/737d164b4837a9bbd202f5ae3078975f0525a55730fe871d8ed4e3b952b0/nvidia_cuda_cupti-13.0.85-py3-none-manylinux_2_25_x86_64.whl", hash = "sha256:4eb01c08e859bf924d222250d2e8f8b8ff6d3db4721288cf35d14252a4d933c8", size = 10715597 }, +] + +[[package]] +name = "nvidia-cuda-nvrtc" +version = "13.0.88" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c3/68/483a78f5e8f31b08fb1bb671559968c0ca3a065ac7acabfc7cee55214fd6/nvidia_cuda_nvrtc-13.0.88-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:ad9b6d2ead2435f11cbb6868809d2adeeee302e9bb94bcf0539c7a40d80e8575", size = 90215200 }, + { url = "https://files.pythonhosted.org/packages/b7/dc/6bb80850e0b7edd6588d560758f17e0550893a1feaf436807d64d2da040f/nvidia_cuda_nvrtc-13.0.88-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d27f20a0ca67a4bb34268a5e951033496c5b74870b868bacd046b1b8e0c3267b", size = 43015449 }, +] + +[[package]] +name = "nvidia-cuda-runtime" +version = "13.0.96" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/87/4f/17d7b9b8e285199c58ce28e31b5c5bbaa4d8271af06a89b6405258245de2/nvidia_cuda_runtime-13.0.96-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ef9bcbe90493a2b9d810e43d249adb3d02e98dd30200d86607d8d02687c43f55", size = 2261060 }, + { url = "https://files.pythonhosted.org/packages/2e/24/d1558f3b68b1d26e706813b1d10aa1d785e4698c425af8db8edc3dced472/nvidia_cuda_runtime-13.0.96-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7f82250d7782aa23b6cfe765ecc7db554bd3c2870c43f3d1821f1d18aebf0548", size = 2243632 }, +] + +[[package]] +name = "nvidia-cudnn-cu13" +version = "9.19.0.56" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-cublas", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/f1/84/26025437c1e6b61a707442184fa0c03d083b661adf3a3eecfd6d21677740/nvidia_cudnn_cu13-9.19.0.56-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:6ed29ffaee1176c612daf442e4dd6cfeb6a0caa43ddcbeb59da94953030b1be4", size = 433781201 }, + { url = "https://files.pythonhosted.org/packages/a3/22/0b4b932655d17a6da1b92fa92ab12844b053bb2ac2475e179ba6f043da1e/nvidia_cudnn_cu13-9.19.0.56-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:d20e1734305e9d68889a96e3f35094d733ff1f83932ebe462753973e53a572bf", size = 366066321 }, +] + +[[package]] +name = "nvidia-cufft" +version = "12.0.0.61" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-nvjitlink", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/8b/ae/f417a75c0259e85c1d2f83ca4e960289a5f814ed0cea74d18c353d3e989d/nvidia_cufft-12.0.0.61-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2708c852ef8cd89d1d2068bdbece0aa188813a0c934db3779b9b1faa8442e5f5", size = 214053554 }, + { url = "https://files.pythonhosted.org/packages/a8/2f/7b57e29836ea8714f81e9898409196f47d772d5ddedddf1592eadb8ab743/nvidia_cufft-12.0.0.61-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6c44f692dce8fd5ffd3e3df134b6cdb9c2f72d99cf40b62c32dde45eea9ddad3", size = 214085489 }, +] + +[[package]] +name = "nvidia-cufile" +version = "1.15.1.6" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3f/70/4f193de89a48b71714e74602ee14d04e4019ad36a5a9f20c425776e72cd6/nvidia_cufile-1.15.1.6-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:08a3ecefae5a01c7f5117351c64f17c7c62efa5fffdbe24fc7d298da19cd0b44", size = 1223672 }, + { url = "https://files.pythonhosted.org/packages/ab/73/cc4a14c9813a8a0d509417cf5f4bdaba76e924d58beb9864f5a7baceefbf/nvidia_cufile-1.15.1.6-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:bdc0deedc61f548bddf7733bdc216456c2fdb101d020e1ab4b88d232d5e2f6d1", size = 1136992 }, +] + +[[package]] +name = "nvidia-curand" +version = "10.4.0.35" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/72/7c2ae24fb6b63a32e6ae5d241cc65263ea18d08802aaae087d9f013335a2/nvidia_curand-10.4.0.35-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:133df5a7509c3e292aaa2b477afd0194f06ce4ea24d714d616ff36439cee349a", size = 61962106 }, + { url = "https://files.pythonhosted.org/packages/a5/9f/be0a41ca4a4917abf5cb9ae0daff1a6060cc5de950aec0396de9f3b52bc5/nvidia_curand-10.4.0.35-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:1aee33a5da6e1db083fe2b90082def8915f30f3248d5896bcec36a579d941bfc", size = 59544258 }, +] + +[[package]] +name = "nvidia-cusolver" +version = "12.0.4.66" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-cublas", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, + { name = "nvidia-cusparse", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, + { name = "nvidia-nvjitlink", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/c8/c3/b30c9e935fc01e3da443ec0116ed1b2a009bb867f5324d3f2d7e533e776b/nvidia_cusolver-12.0.4.66-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:02c2457eaa9e39de20f880f4bd8820e6a1cfb9f9a34f820eb12a155aa5bc92d2", size = 223467760 }, + { url = "https://files.pythonhosted.org/packages/5f/67/cba3777620cdacb99102da4042883709c41c709f4b6323c10781a9c3aa34/nvidia_cusolver-12.0.4.66-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:0a759da5dea5c0ea10fd307de75cdeb59e7ea4fcb8add0924859b944babf1112", size = 200941980 }, +] + +[[package]] +name = "nvidia-cusparse" +version = "12.6.3.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-nvjitlink", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/f8/94/5c26f33738ae35276672f12615a64bd008ed5be6d1ebcb23579285d960a9/nvidia_cusparse-12.6.3.3-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:80bcc4662f23f1054ee334a15c72b8940402975e0eab63178fc7e670aa59472c", size = 162155568 }, + { url = "https://files.pythonhosted.org/packages/fa/18/623c77619c31d62efd55302939756966f3ecc8d724a14dab2b75f1508850/nvidia_cusparse-12.6.3.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2b3c89c88d01ee0e477cb7f82ef60a11a4bcd57b6b87c33f789350b59759360b", size = 145942937 }, +] + +[[package]] +name = "nvidia-cusparselt-cu13" +version = "0.8.0" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/46/10/8dcd1175260706a2fc92a16a52e306b71d4c1ea0b0cc4a9484183399818a/nvidia_cusparselt_cu13-0.8.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:400c6ed1cf6780fc6efedd64ec9f1345871767e6a1a0a552a1ea0578117ea77c", size = 220791277 }, + { url = "https://files.pythonhosted.org/packages/fd/53/43b0d71f4e702fa9733f8b4571fdca50a8813f1e450b656c239beff12315/nvidia_cusparselt_cu13-0.8.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:25e30a8a7323935d4ad0340b95a0b69926eee755767e8e0b1cf8dd85b197d3fd", size = 169884119 }, +] + +[[package]] +name = "nvidia-nccl-cu13" +version = "2.28.9" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/39/55/1920646a2e43ffd4fc958536b276197ed740e9e0c54105b4bb3521591fc7/nvidia_nccl_cu13-2.28.9-py3-none-manylinux_2_18_aarch64.whl", hash = "sha256:01c873ba1626b54caa12272ed228dc5b2781545e0ae8ba3f432a8ef1c6d78643", size = 196561677 }, + { url = "https://files.pythonhosted.org/packages/b0/b4/878fefaad5b2bcc6fcf8d474a25e3e3774bc5133e4b58adff4d0bca238bc/nvidia_nccl_cu13-2.28.9-py3-none-manylinux_2_18_x86_64.whl", hash = "sha256:e4553a30f34195f3fa1da02a6da3d6337d28f2003943aa0a3d247bbc25fefc42", size = 196493177 }, +] + +[[package]] +name = "nvidia-nvjitlink" +version = "13.0.88" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/56/7a/123e033aaff487c77107195fa5a2b8686795ca537935a24efae476c41f05/nvidia_nvjitlink-13.0.88-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:13a74f429e23b921c1109976abefacc69835f2f433ebd323d3946e11d804e47b", size = 40713933 }, + { url = "https://files.pythonhosted.org/packages/ab/2c/93c5250e64df4f894f1cbb397c6fd71f79813f9fd79d7cd61de3f97b3c2d/nvidia_nvjitlink-13.0.88-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e931536ccc7d467a98ba1d8b89ff7fa7f1fa3b13f2b0069118cd7f47bff07d0c", size = 38768748 }, +] + +[[package]] +name = "nvidia-nvshmem-cu13" +version = "3.4.5" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dc/0f/05cc9c720236dcd2db9c1ab97fff629e96821be2e63103569da0c9b72f19/nvidia_nvshmem_cu13-3.4.5-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:6dc2a197f38e5d0376ad52cd1a2a3617d3cdc150fd5966f4aee9bcebb1d68fe9", size = 60215947 }, + { url = "https://files.pythonhosted.org/packages/3c/35/a9bf80a609e74e3b000fef598933235c908fcefcef9026042b8e6dfde2a9/nvidia_nvshmem_cu13-3.4.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:290f0a2ee94c9f3687a02502f3b9299a9f9fe826e6d0287ee18482e78d495b80", size = 60412546 }, +] + +[[package]] +name = "nvidia-nvtx" +version = "13.0.85" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c2/f3/d86c845465a2723ad7e1e5c36dcd75ddb82898b3f53be47ebd429fb2fa5d/nvidia_nvtx-13.0.85-py3-none-manylinux1_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:4936d1d6780fbe68db454f5e72a42ff64d1fd6397df9f363ae786930fd5c1cd4", size = 148047 }, + { url = "https://files.pythonhosted.org/packages/a8/64/3708a90d1ebe202ffdeb7185f878a3c84d15c2b2c31858da2ce0583e2def/nvidia_nvtx-13.0.85-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cb7780edb6b14107373c835bf8b72e7a178bac7367e23da7acb108f973f157a6", size = 148878 }, +] + [[package]] name = "oauthlib" version = "3.3.1" @@ -1307,6 +1802,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/6f/1c/f2a8d8a1b17514660a614ce5f7aac74b934e69f5abc2700cc7ced882a009/orjson-3.11.7-cp314-cp314-win_arm64.whl", hash = "sha256:4a2e9c5be347b937a2e0203866f12bba36082e89b402ddb9e927d5822e43088d", size = 126038 }, ] +[[package]] +name = "overrides" +version = "7.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/36/86/b585f53236dec60aba864e050778b25045f857e17f6e5ea0ae95fe80edd2/overrides-7.7.0.tar.gz", hash = "sha256:55158fa3d93b98cc75299b1e67078ad9003ca27945c76162c1c0766d6f91820a", size = 22812 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/ab/fc8290c6a4c722e5514d80f62b2dc4c4df1a68a41d1364e625c35990fcf3/overrides-7.7.0-py3-none-any.whl", hash = "sha256:c7ed9d062f78b8e4c1a7b70bd8796b35ead4d9f510227ef9c5dc7626c60d7e49", size = 17832 }, +] + [[package]] name = "packaging" version = "26.0" @@ -1713,14 +2217,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5c/96/5fb7d8c3c17bc8c62fdb031c47d77a1af698f1d7a406b0f79aaa1338f9ad/pydantic_core-2.41.5-cp314-cp314t-win32.whl", hash = "sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa", size = 1988906 }, { url = "https://files.pythonhosted.org/packages/22/ed/182129d83032702912c2e2d8bbe33c036f342cc735737064668585dac28f/pydantic_core-2.41.5-cp314-cp314t-win_amd64.whl", hash = "sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c", size = 1981607 }, { url = "https://files.pythonhosted.org/packages/9f/ed/068e41660b832bb0b1aa5b58011dea2a3fe0ba7861ff38c4d4904c1c1a99/pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008", size = 1974769 }, - { url = "https://files.pythonhosted.org/packages/11/72/90fda5ee3b97e51c494938a4a44c3a35a9c96c19bba12372fb9c634d6f57/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:b96d5f26b05d03cc60f11a7761a5ded1741da411e7fe0909e27a5e6a0cb7b034", size = 2115441 }, - { url = "https://files.pythonhosted.org/packages/1f/53/8942f884fa33f50794f119012dc6a1a02ac43a56407adaac20463df8e98f/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:634e8609e89ceecea15e2d61bc9ac3718caaaa71963717bf3c8f38bfde64242c", size = 1930291 }, - { url = "https://files.pythonhosted.org/packages/79/c8/ecb9ed9cd942bce09fc888ee960b52654fbdbede4ba6c2d6e0d3b1d8b49c/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:93e8740d7503eb008aa2df04d3b9735f845d43ae845e6dcd2be0b55a2da43cd2", size = 1948632 }, - { url = "https://files.pythonhosted.org/packages/2e/1b/687711069de7efa6af934e74f601e2a4307365e8fdc404703afc453eab26/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f15489ba13d61f670dcc96772e733aad1a6f9c429cc27574c6cdaed82d0146ad", size = 2138905 }, - { url = "https://files.pythonhosted.org/packages/09/32/59b0c7e63e277fa7911c2fc70ccfb45ce4b98991e7ef37110663437005af/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd", size = 2110495 }, - { url = "https://files.pythonhosted.org/packages/aa/81/05e400037eaf55ad400bcd318c05bb345b57e708887f07ddb2d20e3f0e98/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc", size = 1915388 }, - { url = "https://files.pythonhosted.org/packages/6e/0d/e3549b2399f71d56476b77dbf3cf8937cec5cd70536bdc0e374a421d0599/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56", size = 1942879 }, - { url = "https://files.pythonhosted.org/packages/f7/07/34573da085946b6a313d7c42f82f16e8920bfd730665de2d11c0c37a74b5/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b", size = 2139017 }, { url = "https://files.pythonhosted.org/packages/5f/9b/1b3f0e9f9305839d7e84912f9e8bfbd191ed1b1ef48083609f0dabde978c/pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b2379fa7ed44ddecb5bfe4e48577d752db9fc10be00a6b7446e9663ba143de26", size = 2101980 }, { url = "https://files.pythonhosted.org/packages/a4/ed/d71fefcb4263df0da6a85b5d8a7508360f2f2e9b3bf5814be9c8bccdccc1/pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:266fb4cbf5e3cbd0b53669a6d1b039c45e3ce651fd5442eff4d07c2cc8d66808", size = 1923865 }, { url = "https://files.pythonhosted.org/packages/ce/3a/626b38db460d675f873e4444b4bb030453bbe7b4ba55df821d026a0493c4/pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58133647260ea01e4d0500089a8c4f07bd7aa6ce109682b1426394988d8aaacc", size = 2134256 }, @@ -1917,6 +2413,61 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c0/d2/21af5c535501a7233e734b8af901574572da66fcc254cb35d0609c9080dd/pywin32-311-cp314-cp314-win_arm64.whl", hash = "sha256:a508e2d9025764a8270f93111a970e1d0fbfc33f4153b388bb649b7eec4f9b42", size = 8932540 }, ] +[[package]] +name = "pyyaml" +version = "6.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6d/16/a95b6757765b7b031c9374925bb718d55e0a9ba8a1b6a12d25962ea44347/pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e", size = 185826 }, + { url = "https://files.pythonhosted.org/packages/16/19/13de8e4377ed53079ee996e1ab0a9c33ec2faf808a4647b7b4c0d46dd239/pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824", size = 175577 }, + { url = "https://files.pythonhosted.org/packages/0c/62/d2eb46264d4b157dae1275b573017abec435397aa59cbcdab6fc978a8af4/pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c", size = 775556 }, + { url = "https://files.pythonhosted.org/packages/10/cb/16c3f2cf3266edd25aaa00d6c4350381c8b012ed6f5276675b9eba8d9ff4/pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00", size = 882114 }, + { url = "https://files.pythonhosted.org/packages/71/60/917329f640924b18ff085ab889a11c763e0b573da888e8404ff486657602/pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d", size = 806638 }, + { url = "https://files.pythonhosted.org/packages/dd/6f/529b0f316a9fd167281a6c3826b5583e6192dba792dd55e3203d3f8e655a/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a", size = 767463 }, + { url = "https://files.pythonhosted.org/packages/f2/6a/b627b4e0c1dd03718543519ffb2f1deea4a1e6d42fbab8021936a4d22589/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4", size = 794986 }, + { url = "https://files.pythonhosted.org/packages/45/91/47a6e1c42d9ee337c4839208f30d9f09caa9f720ec7582917b264defc875/pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b", size = 142543 }, + { url = "https://files.pythonhosted.org/packages/da/e3/ea007450a105ae919a72393cb06f122f288ef60bba2dc64b26e2646fa315/pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf", size = 158763 }, + { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063 }, + { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973 }, + { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116 }, + { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011 }, + { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870 }, + { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089 }, + { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181 }, + { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658 }, + { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003 }, + { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344 }, + { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669 }, + { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252 }, + { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081 }, + { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159 }, + { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626 }, + { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613 }, + { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115 }, + { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427 }, + { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090 }, + { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246 }, + { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814 }, + { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809 }, + { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454 }, + { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355 }, + { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175 }, + { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228 }, + { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194 }, + { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429 }, + { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912 }, + { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108 }, + { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641 }, + { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901 }, + { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132 }, + { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261 }, + { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272 }, + { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923 }, + { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062 }, + { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341 }, +] + [[package]] name = "redshift-connector" version = "2.1.12" @@ -1936,6 +2487,110 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/3e/c5/e214ea72fc22c441aeba9a780ab45385e920f81fa7e5ed78693cd7b2c484/redshift_connector-2.1.12-py3-none-any.whl", hash = "sha256:e68d46a98336e581cd5860dac0ad9c0f1e6796f41b8e5e545779acc2c7803584", size = 155538 }, ] +[[package]] +name = "regex" +version = "2026.3.32" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/81/93/5ab3e899c47fa7994e524447135a71cd121685a35c8fe35029005f8b236f/regex-2026.3.32.tar.gz", hash = "sha256:f1574566457161678297a116fa5d1556c5a4159d64c5ff7c760e7c564bf66f16", size = 415605 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/92/c1/c68163a6ce455996db71e249a65234b1c9f79a914ea2108c6c9af9e1812a/regex-2026.3.32-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0d7855f5e59fcf91d0c9f4a51dc5d8847813832a2230c3e8e35912ccf20baaa2", size = 489568 }, + { url = "https://files.pythonhosted.org/packages/96/9c/0bdd47733b832b5caa11e63df14dccdb311b41ab33c1221e249af4421f8f/regex-2026.3.32-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:18eb45f711e942c27dbed4109830bd070d8d618e008d0db39705f3f57070a4c6", size = 291287 }, + { url = "https://files.pythonhosted.org/packages/e1/ff/1977a595f15f8dc355f9cebd875dab67f3faeca1f36b905fe53305bbcaed/regex-2026.3.32-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ed3b8281c5d0944d939c82db4ec2300409dd69ee087f7a75a94f2e301e855fb4", size = 289325 }, + { url = "https://files.pythonhosted.org/packages/0a/68/dfa21aef5af4a144702befeb5ff20ea9f9fbe40a4dfd08d56148b5b48b0a/regex-2026.3.32-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ad5c53f2e8fcae9144009435ebe3d9832003508cf8935c04542a1b3b8deefa15", size = 790898 }, + { url = "https://files.pythonhosted.org/packages/36/26/9424e43e0e31ac3ce1ba0e7232ee91e113a04a579c53331bc0f16a4a5bf7/regex-2026.3.32-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:70c634e39c5cda0da05c93d6747fdc957599f7743543662b6dbabdd8d3ba8a96", size = 862462 }, + { url = "https://files.pythonhosted.org/packages/63/a8/06573154ac891c6b55b74a88e0fb7c10081c20916b82dd0abc8cef938e13/regex-2026.3.32-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:1e0f6648fd48f4c73d801c55ab976cd602e2da87de99c07bff005b131f269c6a", size = 906522 }, + { url = "https://files.pythonhosted.org/packages/e7/26/46673bb18448c51222c6272c850484a0092f364fae8d0315be9aa1e4baa7/regex-2026.3.32-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c5e0fdb5744caf1036dec5510f543164f2144cb64932251f6dfd42fa872b7f9c", size = 798289 }, + { url = "https://files.pythonhosted.org/packages/4d/cb/804f1bd5ff08687258e6a92b040aba9b770e626b8d3ba21fffdfa21db2db/regex-2026.3.32-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:dab4178a0bc1ef13178832b12db7bc7f562e8f028b2b5be186e370090dc50652", size = 774823 }, + { url = "https://files.pythonhosted.org/packages/e5/94/28a58258f8d822fb949c8ff87fc7e5f2a346922360ec084c193b3c95e51c/regex-2026.3.32-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:f95bd07f301135771559101c060f558e2cf896c7df00bec050ca7f93bf11585a", size = 781381 }, + { url = "https://files.pythonhosted.org/packages/c4/f3/71e69dbe0543586a3e3532cf36e8c9b38d6d93033161a9799c1e9090eb78/regex-2026.3.32-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:2dcca2bceb823c9cc610e57b86a265d7ffc30e9fe98548c609eba8bd3c0c2488", size = 855968 }, + { url = "https://files.pythonhosted.org/packages/6d/99/850feec404a02b62e048718ec1b4b98b5c3848cd9ca2316d0bdb65a53f6a/regex-2026.3.32-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:567b57eb987547a23306444e4f6f85d4314f83e65c71d320d898aa7550550443", size = 762785 }, + { url = "https://files.pythonhosted.org/packages/40/04/808ab0462a2d19b295a3b42134f5183692f798addfe6a8b6aa5f7c7a35b2/regex-2026.3.32-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:b6acb765e7c1f2fa08ac9057a33595e26104d7d67046becae184a8f100932dd9", size = 845797 }, + { url = "https://files.pythonhosted.org/packages/06/53/8afcf0fd4bd55440b48442c86cddfe61b0d21c92d96e384c0c47d769f4c3/regex-2026.3.32-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c1ed17104d1be7f807fdec35ec99777168dd793a09510d753f8710590ba54cdd", size = 785200 }, + { url = "https://files.pythonhosted.org/packages/99/4d/23d992ab4115456fec520d6c3aae39e0e33739b244ddb39aa4102a0f7ef0/regex-2026.3.32-cp311-cp311-win32.whl", hash = "sha256:c60f1de066eb5a0fd8ee5974de4194bb1c2e7692941458807162ffbc39887303", size = 266351 }, + { url = "https://files.pythonhosted.org/packages/62/74/27c3cdb3a3fbbf67f7231b872877416ec817ae84271573d2fd14bf8723d3/regex-2026.3.32-cp311-cp311-win_amd64.whl", hash = "sha256:8fe14e24124ef41220e5992a0f09432f890037df6f93fd3d6b7a0feff2db16b2", size = 278639 }, + { url = "https://files.pythonhosted.org/packages/0a/12/6a67bd509f38aec021d63096dbc884f39473e92adeb1e35d6fb6d89cbd59/regex-2026.3.32-cp311-cp311-win_arm64.whl", hash = "sha256:ded4fc0edf3de792850cb8b04bbf3c5bd725eeaf9df4c27aad510f6eed9c4e19", size = 270594 }, + { url = "https://files.pythonhosted.org/packages/38/94/69492c45b0e61b027109d8433a5c3d4f7a90709184c057c7cfc60acb1bfa/regex-2026.3.32-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ad8d372587e659940568afd009afeb72be939c769c552c9b28773d0337251391", size = 490572 }, + { url = "https://files.pythonhosted.org/packages/92/0a/7dcffeebe0fcac45a1f9caf80712002d3cbd66d7d69d719315ee142b280f/regex-2026.3.32-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3f5747501b69299c6b0b047853771e4ed390510bada68cb16da9c9c2078343f7", size = 292078 }, + { url = "https://files.pythonhosted.org/packages/e3/ec/988486058ef49eb931476419bae00f164c4ceb44787c45dc7a54b7de0ea4/regex-2026.3.32-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:db976be51375bca900e008941639448d148c655c9545071965d0571ecc04f5d0", size = 289786 }, + { url = "https://files.pythonhosted.org/packages/4a/cf/1955bb5567bc491bd63068e17f75ab0c9ff5e9d08466beec7e347f5e768d/regex-2026.3.32-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:66a5083c3ffe5a5a95f8281ea47a88072d4f24001d562d1d9d28d4cdc005fec5", size = 796431 }, + { url = "https://files.pythonhosted.org/packages/27/8a/67fcbca511b792107540181ee0690df6de877bfbcb41b7ecae7028025ca5/regex-2026.3.32-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e83ce8008b48762be296f1401f19afd9ea29f3d035d1974e0cecb74e9afbd1df", size = 865785 }, + { url = "https://files.pythonhosted.org/packages/c2/59/0677bc44f2c28305edcabc11933777b9ad34e9e8ded7ba573d24e4bc3ee7/regex-2026.3.32-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3aa21bad31db904e0b9055e12c8282df62d43169c4a9d2929407060066ebc74", size = 913593 }, + { url = "https://files.pythonhosted.org/packages/0a/fe/661043d1c263b0d9d10c6ff4e9c9745f3df9641c62b51f96a3473638e7ce/regex-2026.3.32-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f54840bea73541652f1170dc63402a5b776fc851ad36a842da9e5163c1f504a0", size = 801512 }, + { url = "https://files.pythonhosted.org/packages/ff/27/74c986061380e1811a46cf04cdf9c939db9f8c0e63953eddfe37ffd633ea/regex-2026.3.32-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:2ffbadc647325dd4e3118269bda93ded1eb5f5b0c3b7ba79a3da9fbd04f248e9", size = 776182 }, + { url = "https://files.pythonhosted.org/packages/b6/c8/d833397b70cd1bacfcdc0a611f0e2c1f5b91fee8eedd88affcee770cbbb6/regex-2026.3.32-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:66d3126afe7eac41759cd5f0b3b246598086e88e70527c0d68c9e615b81771c4", size = 785837 }, + { url = "https://files.pythonhosted.org/packages/e0/53/fa226b72989b5b93db6926fab5478115e085dfcf077e18d2cb386be0fd23/regex-2026.3.32-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f785f44a44702dea89b28bce5bc82552490694ce4e144e21a4f0545e364d2150", size = 860612 }, + { url = "https://files.pythonhosted.org/packages/04/28/bdd2fc0c055a1b15702bd4084829bbb6b06095f27990e5bee52b2898ea03/regex-2026.3.32-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:b7836aa13721dbdef658aebd11f60d00de633a95726521860fe1f6be75fa225a", size = 765285 }, + { url = "https://files.pythonhosted.org/packages/b4/da/21f5e2a35a191b27e5a47cccb3914c99e139b49b1342d3f36e64e8cc60f7/regex-2026.3.32-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5336b1506142eb0f23c96fb4a34b37c4fefd4fed2a7042069f3c8058efe17855", size = 851963 }, + { url = "https://files.pythonhosted.org/packages/18/f4/04ed04ebf335a44083695c22772be6a42efa31900415555563acf02cb4de/regex-2026.3.32-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b56993a7aeb4140c4770f4f7965c9e5af4f024457d06e23c01b0d47501cb18ed", size = 788332 }, + { url = "https://files.pythonhosted.org/packages/21/25/5355908f479d0dc13d044f88270cdcabc8723efc12e4c2b19e5a94ff1a96/regex-2026.3.32-cp312-cp312-win32.whl", hash = "sha256:d363660f9ef8c734495598d2f3e527fb41f745c73159dc0d743402f049fb6836", size = 266847 }, + { url = "https://files.pythonhosted.org/packages/00/e5/3be71c781a031db5df00735b613895ad5fdbf86c6e3bbea5fbbd7bfb5902/regex-2026.3.32-cp312-cp312-win_amd64.whl", hash = "sha256:c9f261ad3cd97257dc1d9355bfbaa7dd703e06574bffa0fa8fe1e31da915ee38", size = 278034 }, + { url = "https://files.pythonhosted.org/packages/31/5f/27f1e0b1eea4faa99c66daca34130af20c44fae0237bbc98b87999dbc4a8/regex-2026.3.32-cp312-cp312-win_arm64.whl", hash = "sha256:89e50667e7e8c0e7903e4d644a2764fffe9a3a5d6578f72ab7a7b4205bf204b7", size = 270673 }, + { url = "https://files.pythonhosted.org/packages/bd/ba/9c1819f302b42b5fbd4139ead6280e9ec37d19bbe33379df0039b2a57bb4/regex-2026.3.32-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:c6d9c6e783b348f719b6118bb3f187b2e138e3112576c9679eb458cc8b2e164b", size = 490394 }, + { url = "https://files.pythonhosted.org/packages/5b/0b/f62b0ce79eb83ca82fffea1736289d29bc24400355968301406789bcebd2/regex-2026.3.32-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0f21ae18dfd15752cdd98d03cbd7a3640be826bfd58482a93f730dbd24d7b9fb", size = 291993 }, + { url = "https://files.pythonhosted.org/packages/e7/d8/ba0f8f81f88cd20c0b27acc123561ac5495ea33f800f0b8ebed2038b23eb/regex-2026.3.32-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:844d88509c968dd44b30daeefac72b038b1bf31ac372d5106358ab01d393c48b", size = 289618 }, + { url = "https://files.pythonhosted.org/packages/fd/0d/b47a0e68bc511c195ff129c0311a4cd79b954b8676193a9d03a97c623a91/regex-2026.3.32-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8fc918cd003ba0d066bf0003deb05a259baaaab4dc9bd4f1207bbbe64224857a", size = 796427 }, + { url = "https://files.pythonhosted.org/packages/51/d7/32b05aa8fde7789ba316533c0f30e87b6b5d38d6d7f8765eadc5aab84671/regex-2026.3.32-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:bbc458a292aee57d572075f22c035fa32969cdb7987d454e3e34d45a40a0a8b4", size = 865850 }, + { url = "https://files.pythonhosted.org/packages/dc/67/828d8095501f237b83f630d4069eea8c0e5cb6a204e859cf0b67c223ce12/regex-2026.3.32-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:987cdfcfb97a249abc3601ad53c7de5c370529f1981e4c8c46793e4a1e1bfe8e", size = 913578 }, + { url = "https://files.pythonhosted.org/packages/0f/f8/acf1eb80f58852e85bd39a6ddfa78ce2243ddc8de8da7582e6ba657da593/regex-2026.3.32-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a5d88fa37ba5e8a80ca8d956b9ea03805cfa460223ac94b7d4854ee5e30f3173", size = 801536 }, + { url = "https://files.pythonhosted.org/packages/9f/05/986cdf8d12693451f5889aaf4ea4f65b2c49b1152ae814fa1fb75439e40b/regex-2026.3.32-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4d082be64e51671dd5ee1c208c92da2ddda0f2f20d8ef387e57634f7e97b6aae", size = 776226 }, + { url = "https://files.pythonhosted.org/packages/32/02/945a6a2348ca1c6608cb1747275c8affd2ccd957d4885c25218a86377912/regex-2026.3.32-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c1d7fa44aece1fa02b8927441614c96520253a5cad6a96994e3a81e060feed55", size = 785933 }, + { url = "https://files.pythonhosted.org/packages/53/12/c5bab6cc679ad79a45427a98c4e70809586ac963c5ad54a9217533c4763e/regex-2026.3.32-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d478a2ca902b6ef28ffc9521e5f0f728d036abe35c0b250ee8ae78cfe7c5e44e", size = 860671 }, + { url = "https://files.pythonhosted.org/packages/bf/68/8d85f98c2443469facabef62b82b851d369b13f92bec2ca7a3808deaa47b/regex-2026.3.32-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:2820d2231885e97aff0fcf230a19ebd5d2b5b8a1ba338c20deb34f16db1c7897", size = 765335 }, + { url = "https://files.pythonhosted.org/packages/89/a7/d8a9c270916107a501fca63b748547c6c77e570d19f16a29b557ce734f3d/regex-2026.3.32-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:fc8ced733d6cd9af5e412f256a32f7c61cd2d7371280a65c689939ac4572499f", size = 851913 }, + { url = "https://files.pythonhosted.org/packages/f4/8e/03d392b26679914ccf21f83d18ad4443232d2f8c3e2c30a962d4e3918d9c/regex-2026.3.32-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:847087abe98b3c1ebf1eb49d6ef320dbba75a83ee4f83c94704580f1df007dd4", size = 788447 }, + { url = "https://files.pythonhosted.org/packages/cf/df/692227d23535a50604333068b39eb262626db780ab1e1b19d83fc66853aa/regex-2026.3.32-cp313-cp313-win32.whl", hash = "sha256:d21a07edddb3e0ca12a8b8712abc8452481c3d3db19ae87fc94e9842d005964b", size = 266834 }, + { url = "https://files.pythonhosted.org/packages/b9/37/13e4e56adc16ba607cffa1fe880f233eb9ded8ab8a8580619683c9e4ce48/regex-2026.3.32-cp313-cp313-win_amd64.whl", hash = "sha256:3c054e39a9f85a3d76c62a1d50c626c5e9306964eaa675c53f61ff7ec1204bbb", size = 277972 }, + { url = "https://files.pythonhosted.org/packages/ab/1c/80a86dbb2b416fec003b1801462bdcebbf1d43202ed5acb176e99c1ba369/regex-2026.3.32-cp313-cp313-win_arm64.whl", hash = "sha256:b2e9c2ea2e93223579308263f359eab8837dc340530b860cb59b713651889f14", size = 270649 }, + { url = "https://files.pythonhosted.org/packages/58/08/e38372da599dc1c39c599907ec535016d110034bd3701ce36554f59767ef/regex-2026.3.32-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:5d86e3fb08c94f084a625c8dc2132a79a3a111c8bf6e2bc59351fa61753c2f6e", size = 494495 }, + { url = "https://files.pythonhosted.org/packages/5f/27/6e29ece8c9ce01001ece1137fa21c8707529c2305b22828f63623b0eb262/regex-2026.3.32-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:b6f366a5ef66a2df4d9e68035cfe9f0eb8473cdfb922c37fac1d169b468607b0", size = 293988 }, + { url = "https://files.pythonhosted.org/packages/e1/98/8752e18bb87a2fe728b73b0f83c082eb162a470766063f8028759fb26844/regex-2026.3.32-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b8fca73e16c49dd972ce3a88278dfa5b93bf91ddef332a46e9443abe21ca2f7c", size = 292634 }, + { url = "https://files.pythonhosted.org/packages/7f/7b/d7729fe294e23e9c7c3871cb69d49059fa7d65fd11e437a2cbea43f6615d/regex-2026.3.32-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b953d9d496d19786f4d46e6ba4b386c6e493e81e40f9c5392332458183b0599d", size = 810532 }, + { url = "https://files.pythonhosted.org/packages/fd/49/4dae7b000659f611b17b9c1541fba800b0569e4060debc4635ef1b23982c/regex-2026.3.32-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b565f25171e04d4fad950d1fa837133e3af6ea6f509d96166eed745eb0cf63bc", size = 871919 }, + { url = "https://files.pythonhosted.org/packages/83/85/aa8ad3977b9399861db3df62b33fe5fef6932ee23a1b9f4f357f58f2094b/regex-2026.3.32-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f28eac18a8733a124444643a66ac96fef2c0ad65f50034e0a043b90333dc677f", size = 916550 }, + { url = "https://files.pythonhosted.org/packages/c8/c0/6379d7f5b59ff0656ba49cf666d5013ecee55e83245275b310b0ffc79143/regex-2026.3.32-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7cdd508664430dd51b8888deb6c5b416d8de046b2e11837254378d31febe4a98", size = 814988 }, + { url = "https://files.pythonhosted.org/packages/2c/af/2dfddc64074bd9b70e27e170ee9db900542e2870210b489ad4471416ba86/regex-2026.3.32-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5c35d097f509cf7e40d20d5bee548d35d6049b36eb9965e8d43e4659923405b9", size = 786337 }, + { url = "https://files.pythonhosted.org/packages/eb/2f/4eb8abd705236402b4fe0e130971634deffb1855e2028bf02a2b7c0e841c/regex-2026.3.32-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:85c9b0c131427470a6423baa0a9330be6fd8c3630cc3ee6fdee03360724cbec5", size = 800029 }, + { url = "https://files.pythonhosted.org/packages/3e/2c/77d9ca2c9df483b51b4b1291c96d79c9ae301077841c4db39bc822f6b4c6/regex-2026.3.32-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:e50af656c15e2723eeb7279c0837e07accc594b95ec18b86821a4d44b51b24bf", size = 865843 }, + { url = "https://files.pythonhosted.org/packages/48/10/306f477a509f4eed699071b1f031d89edd5a2b5fa28c8ede5b2638eaba82/regex-2026.3.32-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:4bc32b4dbdb4f9f300cf9f38f8ea2ce9511a068ffaa45ac1373ee7a943f1d810", size = 772473 }, + { url = "https://files.pythonhosted.org/packages/f4/f6/54bd83ec46ac037de2beb049afc9dd5d2769c6ecaadf7856254ce610e62a/regex-2026.3.32-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e3e5d1802cba785210a4a800e63fcee7a228649a880f3bf7f2aadccb151a834b", size = 856805 }, + { url = "https://files.pythonhosted.org/packages/37/e8/ee0e7d14de1fc6582d5782f072db6c61465a38a4142f88e175dda494b536/regex-2026.3.32-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ef250a3f5e93182193f5c927c5e9575b2cb14b80d03e258bc0b89cc5de076b60", size = 801875 }, + { url = "https://files.pythonhosted.org/packages/8a/06/0fa9daca59d07b6aabd8e0468d3b86fd578576a157206fbcddbfc2298f7d/regex-2026.3.32-cp313-cp313t-win32.whl", hash = "sha256:9cf7036dfa2370ccc8651521fcbb40391974841119e9982fa312b552929e6c85", size = 269892 }, + { url = "https://files.pythonhosted.org/packages/13/47/77f16b5ad9f10ca574f03d84a354b359b0ac33f85054f2f2daafc9f7b807/regex-2026.3.32-cp313-cp313t-win_amd64.whl", hash = "sha256:c940e00e8d3d10932c929d4b8657c2ea47d2560f31874c3e174c0d3488e8b865", size = 281318 }, + { url = "https://files.pythonhosted.org/packages/c6/47/db4446faaea8d01c8315c9c89c7dc6abbb3305e8e712e9b23936095c4d58/regex-2026.3.32-cp313-cp313t-win_arm64.whl", hash = "sha256:ace48c5e157c1e58b7de633c5e257285ce85e567ac500c833349c363b3df69d4", size = 272366 }, + { url = "https://files.pythonhosted.org/packages/32/68/ff024bf6131b7446a791a636dbbb7fa732d586f33b276d84b3460ea49393/regex-2026.3.32-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:a416ee898ecbc5d8b283223b4cf4d560f93244f6f7615c1bd67359744b00c166", size = 490430 }, + { url = "https://files.pythonhosted.org/packages/61/72/039d9164817ee298f2a2d0246001afe662241dcbec0eedd1fe03e2a2555e/regex-2026.3.32-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:d76d62909bfb14521c3f7cfd5b94c0c75ec94b0a11f647d2f604998962ec7b6c", size = 291948 }, + { url = "https://files.pythonhosted.org/packages/06/9d/77f684d90ffe3e99b828d3cabb87a0f1601d2b9decd1333ff345809b1d02/regex-2026.3.32-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:631f7d95c83f42bccfe18946a38ad27ff6b6717fb4807e60cf24860b5eb277fc", size = 289786 }, + { url = "https://files.pythonhosted.org/packages/83/70/bd76069a0304e924682b2efd8683a01617a7e1da9b651af73039d8da76a4/regex-2026.3.32-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:12917c6c6813ffcdfb11680a04e4d63c5532b88cf089f844721c5f41f41a63ad", size = 796672 }, + { url = "https://files.pythonhosted.org/packages/80/31/c2d7d9a5671e111a2c16d57e0cb03e1ce35b28a115901590528aa928bb5b/regex-2026.3.32-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3e221b615f83b15887636fcb90ed21f1a19541366f8b7ba14ba1ad8304f4ded4", size = 866556 }, + { url = "https://files.pythonhosted.org/packages/d7/b9/9921a31931d0bc3416ac30205471e0e2ed60dcbd16fc922bbd69b427322b/regex-2026.3.32-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4f9ae4755fa90f1dc2d0d393d572ebc134c0fe30fcfc0ab7e67c1db15f192041", size = 912787 }, + { url = "https://files.pythonhosted.org/packages/41/ab/2c1bc8ab99f63cdabdbc7823af8f4cfcd6ddbb2babf01861826c3f1ad44d/regex-2026.3.32-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a094e9dcafedfb9d333db5cf880304946683f43a6582bb86688f123335122929", size = 800879 }, + { url = "https://files.pythonhosted.org/packages/49/e5/0be716eb2c0b2ae3a439e44432534e82b2f81848af64cb21c0473ad8ae46/regex-2026.3.32-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c1cecea3e477af105f32ef2119b8d895f297492e41d317e60d474bc4bffd62ff", size = 776332 }, + { url = "https://files.pythonhosted.org/packages/26/80/114a61bd25dec7d1070930eaef82aadf9b05961a37629e7cca7bc3fc2257/regex-2026.3.32-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f26262900edd16272b6360014495e8d68379c6c6e95983f9b7b322dc928a1194", size = 786384 }, + { url = "https://files.pythonhosted.org/packages/0c/78/be0a6531f8db426e8e60d6356aeef8e9cc3f541655a648c4968b63c87a88/regex-2026.3.32-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:1cb22fa9ee6a0acb22fc9aecce5f9995fe4d2426ed849357d499d62608fbd7f9", size = 861381 }, + { url = "https://files.pythonhosted.org/packages/45/b1/e5076fbe45b8fb39672584b1b606d512f5bd3a43155be68a95f6b88c1fc5/regex-2026.3.32-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:9b9118a78e031a2e4709cd2fcc3028432e89b718db70073a8da574c249b5b249", size = 765434 }, + { url = "https://files.pythonhosted.org/packages/a3/da/fd65d68b897f8b52b1390d20d776fa753582484724a9cb4f4c26de657ae5/regex-2026.3.32-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:b193ed199848aa96618cd5959c1582a0bf23cd698b0b900cb0ffe81b02c8659c", size = 851501 }, + { url = "https://files.pythonhosted.org/packages/e8/d6/1e9c991c32022a9312e9124cc974961b3a2501338de2cd1cce75a3612d7a/regex-2026.3.32-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:10fb2aaae1aaadf7d43c9f3c2450404253697bf8b9ce360bd5418d1d16292298", size = 788076 }, + { url = "https://files.pythonhosted.org/packages/f0/5b/b23c72f6d607cbb24ef42acf0c7c2ef4eee1377a9f7ba43b312f889edfbb/regex-2026.3.32-cp314-cp314-win32.whl", hash = "sha256:110ba4920721374d16c4c8ea7ce27b09546d43e16aea1d7f43681b5b8f80ba61", size = 272255 }, + { url = "https://files.pythonhosted.org/packages/2a/ec/32bbcc42366097a8cea2c481e02964be6c6fa5ccfb0fa9581686af0bec5f/regex-2026.3.32-cp314-cp314-win_amd64.whl", hash = "sha256:245667ad430745bae6a1e41081872d25819d86fbd9e0eec485ba00d9f78ad43d", size = 281160 }, + { url = "https://files.pythonhosted.org/packages/6c/e4/89038a028cb68e719fa03ab1ad603649fc199bcda12270d2ac7b471b8f5d/regex-2026.3.32-cp314-cp314-win_arm64.whl", hash = "sha256:1ca02ff0ef33e9d8276a1fcd6d90ff6ea055a32c9149c0050b5b67e26c6d2c51", size = 273688 }, + { url = "https://files.pythonhosted.org/packages/30/6e/87caccd608837a1fa4f8c7edc48e206103452b9bbc94fc724fa39340e807/regex-2026.3.32-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:51fb7e26f91f9091fd8ec6a946f99b15d3bc3667cb5ddc73dd6cb2222dd4a1cc", size = 494506 }, + { url = "https://files.pythonhosted.org/packages/16/53/a922e6b24694d70bdd68fc3fd076950e15b1b418cff9d2cc362b3968d86f/regex-2026.3.32-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:51a93452034d671b0e21b883d48ea66c5d6a05620ee16a9d3f229e828568f3f0", size = 293986 }, + { url = "https://files.pythonhosted.org/packages/60/e4/0cb32203c1aebad0577fcd5b9af1fe764869e617d5234bc6a0ad284299ea/regex-2026.3.32-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:03c2ebd15ff51e7b13bb3dc28dd5ac18cd39e59ebb40430b14ae1a19e833cff1", size = 292677 }, + { url = "https://files.pythonhosted.org/packages/f0/f8/5006b70291469d4174dd66ad162802e2f68419c0f2a7952d0c76c1288cfa/regex-2026.3.32-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5bf2f3c2c5bd8360d335c7dcd4a9006cf1dabae063ee2558ee1b07bbc8a20d88", size = 810661 }, + { url = "https://files.pythonhosted.org/packages/b2/9b/438763a20d22cd1f65f95c8f030dd25df2d80a941068a891d21a5f240456/regex-2026.3.32-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8a4a3189a99ecdd1c13f42513ab3fc7fa8311b38ba7596dd98537acb8cd9acc3", size = 872156 }, + { url = "https://files.pythonhosted.org/packages/6c/5b/1341287887ac982ed9f5f60125e440513ffe354aa7e3681940495af7c12a/regex-2026.3.32-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3c0bbfbd38506e1ea96a85da6782577f06239cb9fcf9696f1ea537c980c0680b", size = 916749 }, + { url = "https://files.pythonhosted.org/packages/42/e2/1d2b48b8e94debfffc6fefb84d2a86a178cc208652a1d6493d5f29821c70/regex-2026.3.32-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8aaf8ee8f34b677f90742ca089b9c83d64bdc410528767273c816a863ed57327", size = 814788 }, + { url = "https://files.pythonhosted.org/packages/a6/d9/7dacb34c43adaeb954518d851f3e5d3ce495ac00a9d6010e3b4b59917c4a/regex-2026.3.32-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3ea568832eca219c2be1721afa073c1c9eb8f98a9733fdedd0a9747639fc22a5", size = 786594 }, + { url = "https://files.pythonhosted.org/packages/ea/72/28295068c92dbd6d3ce4fd22554345cf504e957cc57dadeda4a64fa86a57/regex-2026.3.32-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8e4c8fa46aad1a11ae2f8fcd1c90b9d55e18925829ac0d98c5bb107f93351745", size = 800167 }, + { url = "https://files.pythonhosted.org/packages/ca/17/b10745adeca5b8d52da050e7c746137f5d01dabc6dbbe6e8d9d821dc65c1/regex-2026.3.32-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:0cec365d44835b043d7b3266487797639d07d621bec9dc0ea224b00775797cc1", size = 865906 }, + { url = "https://files.pythonhosted.org/packages/45/9d/1acbcce765044ac0c87f453f4876e0897f7a61c10315262f960184310798/regex-2026.3.32-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:09e26cad1544d856da85881ad292797289e4406338afe98163f3db9f7fac816c", size = 772642 }, + { url = "https://files.pythonhosted.org/packages/24/41/1ef8b4811355ad7b9d7579d3aeca00f18b7bc043ace26c8c609b9287346d/regex-2026.3.32-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:6062c4ef581a3e9e503dccf4e1b7f2d33fdc1c13ad510b287741ac73bc4c6b27", size = 856927 }, + { url = "https://files.pythonhosted.org/packages/97/b1/0dc1d361be80ec1b8b707ada041090181133a7a29d438e432260a4b26f9a/regex-2026.3.32-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:88ebc0783907468f17fca3d7821b30f9c21865a721144eb498cb0ff99a67bcac", size = 801910 }, + { url = "https://files.pythonhosted.org/packages/b5/db/1a23f767fa250844772a9464306d34e0fafe2c317303b88a1415096b6324/regex-2026.3.32-cp314-cp314t-win32.whl", hash = "sha256:e480d3dac06c89bc2e0fd87524cc38c546ac8b4a38177650745e64acbbcfdeba", size = 275714 }, + { url = "https://files.pythonhosted.org/packages/c2/2b/616d31b125ca76079d74d6b1d84ec0860ffdb41c379151135d06e35a8633/regex-2026.3.32-cp314-cp314t-win_amd64.whl", hash = "sha256:67015a8162d413af9e3309d9a24e385816666fbf09e48e3ec43342c8536f7df6", size = 285722 }, + { url = "https://files.pythonhosted.org/packages/7e/91/043d9a00d6123c5fa22a3dc96b10445ce434a8110e1d5e53efb01f243c8b/regex-2026.3.32-cp314-cp314t-win_arm64.whl", hash = "sha256:1a6ac1ed758902e664e0d95c1ee5991aa6fb355423f378ed184c6ec47a1ec0e9", size = 275700 }, +] + [[package]] name = "requests" version = "2.33.0" @@ -2027,6 +2682,149 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/fc/51/727abb13f44c1fcf6d145979e1535a35794db0f6e450a0cb46aa24732fe2/s3transfer-0.16.0-py3-none-any.whl", hash = "sha256:18e25d66fed509e3868dc1572b3f427ff947dd2c56f844a5bf09481ad3f3b2fe", size = 86830 }, ] +[[package]] +name = "safetensors" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/29/9c/6e74567782559a63bd040a236edca26fd71bc7ba88de2ef35d75df3bca5e/safetensors-0.7.0.tar.gz", hash = "sha256:07663963b67e8bd9f0b8ad15bb9163606cd27cc5a1b96235a50d8369803b96b0", size = 200878 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fa/47/aef6c06649039accf914afef490268e1067ed82be62bcfa5b7e886ad15e8/safetensors-0.7.0-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:c82f4d474cf725255d9e6acf17252991c3c8aac038d6ef363a4bf8be2f6db517", size = 467781 }, + { url = "https://files.pythonhosted.org/packages/e8/00/374c0c068e30cd31f1e1b46b4b5738168ec79e7689ca82ee93ddfea05109/safetensors-0.7.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:94fd4858284736bb67a897a41608b5b0c2496c9bdb3bf2af1fa3409127f20d57", size = 447058 }, + { url = "https://files.pythonhosted.org/packages/f1/06/578ffed52c2296f93d7fd2d844cabfa92be51a587c38c8afbb8ae449ca89/safetensors-0.7.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e07d91d0c92a31200f25351f4acb2bc6aff7f48094e13ebb1d0fb995b54b6542", size = 491748 }, + { url = "https://files.pythonhosted.org/packages/ae/33/1debbbb70e4791dde185edb9413d1fe01619255abb64b300157d7f15dddd/safetensors-0.7.0-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8469155f4cb518bafb4acf4865e8bb9d6804110d2d9bdcaa78564b9fd841e104", size = 503881 }, + { url = "https://files.pythonhosted.org/packages/8e/1c/40c2ca924d60792c3be509833df711b553c60effbd91da6f5284a83f7122/safetensors-0.7.0-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:54bef08bf00a2bff599982f6b08e8770e09cc012d7bba00783fc7ea38f1fb37d", size = 623463 }, + { url = "https://files.pythonhosted.org/packages/9b/3a/13784a9364bd43b0d61eef4bea2845039bc2030458b16594a1bd787ae26e/safetensors-0.7.0-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:42cb091236206bb2016d245c377ed383aa7f78691748f3bb6ee1bfa51ae2ce6a", size = 532855 }, + { url = "https://files.pythonhosted.org/packages/a0/60/429e9b1cb3fc651937727befe258ea24122d9663e4d5709a48c9cbfceecb/safetensors-0.7.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dac7252938f0696ddea46f5e855dd3138444e82236e3be475f54929f0c510d48", size = 507152 }, + { url = "https://files.pythonhosted.org/packages/3c/a8/4b45e4e059270d17af60359713ffd83f97900d45a6afa73aaa0d737d48b6/safetensors-0.7.0-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1d060c70284127fa805085d8f10fbd0962792aed71879d00864acda69dbab981", size = 541856 }, + { url = "https://files.pythonhosted.org/packages/06/87/d26d8407c44175d8ae164a95b5a62707fcc445f3c0c56108e37d98070a3d/safetensors-0.7.0-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:cdab83a366799fa730f90a4ebb563e494f28e9e92c4819e556152ad55e43591b", size = 674060 }, + { url = "https://files.pythonhosted.org/packages/11/f5/57644a2ff08dc6325816ba7217e5095f17269dada2554b658442c66aed51/safetensors-0.7.0-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:672132907fcad9f2aedcb705b2d7b3b93354a2aec1b2f706c4db852abe338f85", size = 771715 }, + { url = "https://files.pythonhosted.org/packages/86/31/17883e13a814bd278ae6e266b13282a01049b0c81341da7fd0e3e71a80a3/safetensors-0.7.0-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:5d72abdb8a4d56d4020713724ba81dac065fedb7f3667151c4a637f1d3fb26c0", size = 714377 }, + { url = "https://files.pythonhosted.org/packages/4a/d8/0c8a7dc9b41dcac53c4cbf9df2b9c83e0e0097203de8b37a712b345c0be5/safetensors-0.7.0-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b0f6d66c1c538d5a94a73aa9ddca8ccc4227e6c9ff555322ea40bdd142391dd4", size = 677368 }, + { url = "https://files.pythonhosted.org/packages/05/e5/cb4b713c8a93469e3c5be7c3f8d77d307e65fe89673e731f5c2bfd0a9237/safetensors-0.7.0-cp38-abi3-win32.whl", hash = "sha256:c74af94bf3ac15ac4d0f2a7c7b4663a15f8c2ab15ed0fc7531ca61d0835eccba", size = 326423 }, + { url = "https://files.pythonhosted.org/packages/5d/e6/ec8471c8072382cb91233ba7267fd931219753bb43814cbc71757bfd4dab/safetensors-0.7.0-cp38-abi3-win_amd64.whl", hash = "sha256:d1239932053f56f3456f32eb9625590cc7582e905021f94636202a864d470755", size = 341380 }, +] + +[[package]] +name = "scikit-learn" +version = "1.8.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "joblib" }, + { name = "numpy" }, + { name = "scipy" }, + { name = "threadpoolctl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0e/d4/40988bf3b8e34feec1d0e6a051446b1f66225f8529b9309becaeef62b6c4/scikit_learn-1.8.0.tar.gz", hash = "sha256:9bccbb3b40e3de10351f8f5068e105d0f4083b1a65fa07b6634fbc401a6287fd", size = 7335585 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c9/92/53ea2181da8ac6bf27170191028aee7251f8f841f8d3edbfdcaf2008fde9/scikit_learn-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:146b4d36f800c013d267b29168813f7a03a43ecd2895d04861f1240b564421da", size = 8595835 }, + { url = "https://files.pythonhosted.org/packages/01/18/d154dc1638803adf987910cdd07097d9c526663a55666a97c124d09fb96a/scikit_learn-1.8.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:f984ca4b14914e6b4094c5d52a32ea16b49832c03bd17a110f004db3c223e8e1", size = 8080381 }, + { url = "https://files.pythonhosted.org/packages/8a/44/226142fcb7b7101e64fdee5f49dbe6288d4c7af8abf593237b70fca080a4/scikit_learn-1.8.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5e30adb87f0cc81c7690a84f7932dd66be5bac57cfe16b91cb9151683a4a2d3b", size = 8799632 }, + { url = "https://files.pythonhosted.org/packages/36/4d/4a67f30778a45d542bbea5db2dbfa1e9e100bf9ba64aefe34215ba9f11f6/scikit_learn-1.8.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ada8121bcb4dac28d930febc791a69f7cb1673c8495e5eee274190b73a4559c1", size = 9103788 }, + { url = "https://files.pythonhosted.org/packages/89/3c/45c352094cfa60050bcbb967b1faf246b22e93cb459f2f907b600f2ceda5/scikit_learn-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:c57b1b610bd1f40ba43970e11ce62821c2e6569e4d74023db19c6b26f246cb3b", size = 8081706 }, + { url = "https://files.pythonhosted.org/packages/3d/46/5416595bb395757f754feb20c3d776553a386b661658fb21b7c814e89efe/scikit_learn-1.8.0-cp311-cp311-win_arm64.whl", hash = "sha256:2838551e011a64e3053ad7618dda9310175f7515f1742fa2d756f7c874c05961", size = 7688451 }, + { url = "https://files.pythonhosted.org/packages/90/74/e6a7cc4b820e95cc38cf36cd74d5aa2b42e8ffc2d21fe5a9a9c45c1c7630/scikit_learn-1.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5fb63362b5a7ddab88e52b6dbb47dac3fd7dafeee740dc6c8d8a446ddedade8e", size = 8548242 }, + { url = "https://files.pythonhosted.org/packages/49/d8/9be608c6024d021041c7f0b3928d4749a706f4e2c3832bbede4fb4f58c95/scikit_learn-1.8.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:5025ce924beccb28298246e589c691fe1b8c1c96507e6d27d12c5fadd85bfd76", size = 8079075 }, + { url = "https://files.pythonhosted.org/packages/dd/47/f187b4636ff80cc63f21cd40b7b2d177134acaa10f6bb73746130ee8c2e5/scikit_learn-1.8.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4496bb2cf7a43ce1a2d7524a79e40bc5da45cf598dbf9545b7e8316ccba47bb4", size = 8660492 }, + { url = "https://files.pythonhosted.org/packages/97/74/b7a304feb2b49df9fafa9382d4d09061a96ee9a9449a7cbea7988dda0828/scikit_learn-1.8.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a0bcfe4d0d14aec44921545fd2af2338c7471de9cb701f1da4c9d85906ab847a", size = 8931904 }, + { url = "https://files.pythonhosted.org/packages/9f/c4/0ab22726a04ede56f689476b760f98f8f46607caecff993017ac1b64aa5d/scikit_learn-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:35c007dedb2ffe38fe3ee7d201ebac4a2deccd2408e8621d53067733e3c74809", size = 8019359 }, + { url = "https://files.pythonhosted.org/packages/24/90/344a67811cfd561d7335c1b96ca21455e7e472d281c3c279c4d3f2300236/scikit_learn-1.8.0-cp312-cp312-win_arm64.whl", hash = "sha256:8c497fff237d7b4e07e9ef1a640887fa4fb765647f86fbe00f969ff6280ce2bb", size = 7641898 }, + { url = "https://files.pythonhosted.org/packages/03/aa/e22e0768512ce9255eba34775be2e85c2048da73da1193e841707f8f039c/scikit_learn-1.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0d6ae97234d5d7079dc0040990a6f7aeb97cb7fa7e8945f1999a429b23569e0a", size = 8513770 }, + { url = "https://files.pythonhosted.org/packages/58/37/31b83b2594105f61a381fc74ca19e8780ee923be2d496fcd8d2e1147bd99/scikit_learn-1.8.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:edec98c5e7c128328124a029bceb09eda2d526997780fef8d65e9a69eead963e", size = 8044458 }, + { url = "https://files.pythonhosted.org/packages/2d/5a/3f1caed8765f33eabb723596666da4ebbf43d11e96550fb18bdec42b467b/scikit_learn-1.8.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:74b66d8689d52ed04c271e1329f0c61635bcaf5b926db9b12d58914cdc01fe57", size = 8610341 }, + { url = "https://files.pythonhosted.org/packages/38/cf/06896db3f71c75902a8e9943b444a56e727418f6b4b4a90c98c934f51ed4/scikit_learn-1.8.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8fdf95767f989b0cfedb85f7ed8ca215d4be728031f56ff5a519ee1e3276dc2e", size = 8900022 }, + { url = "https://files.pythonhosted.org/packages/1c/f9/9b7563caf3ec8873e17a31401858efab6b39a882daf6c1bfa88879c0aa11/scikit_learn-1.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:2de443b9373b3b615aec1bb57f9baa6bb3a9bd093f1269ba95c17d870422b271", size = 7989409 }, + { url = "https://files.pythonhosted.org/packages/49/bd/1f4001503650e72c4f6009ac0c4413cb17d2d601cef6f71c0453da2732fc/scikit_learn-1.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:eddde82a035681427cbedded4e6eff5e57fa59216c2e3e90b10b19ab1d0a65c3", size = 7619760 }, + { url = "https://files.pythonhosted.org/packages/d2/7d/a630359fc9dcc95496588c8d8e3245cc8fd81980251079bc09c70d41d951/scikit_learn-1.8.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:7cc267b6108f0a1499a734167282c00c4ebf61328566b55ef262d48e9849c735", size = 8826045 }, + { url = "https://files.pythonhosted.org/packages/cc/56/a0c86f6930cfcd1c7054a2bc417e26960bb88d32444fe7f71d5c2cfae891/scikit_learn-1.8.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:fe1c011a640a9f0791146011dfd3c7d9669785f9fed2b2a5f9e207536cf5c2fd", size = 8420324 }, + { url = "https://files.pythonhosted.org/packages/46/1e/05962ea1cebc1cf3876667ecb14c283ef755bf409993c5946ade3b77e303/scikit_learn-1.8.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:72358cce49465d140cc4e7792015bb1f0296a9742d5622c67e31399b75468b9e", size = 8680651 }, + { url = "https://files.pythonhosted.org/packages/fe/56/a85473cd75f200c9759e3a5f0bcab2d116c92a8a02ee08ccd73b870f8bb4/scikit_learn-1.8.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:80832434a6cc114f5219211eec13dcbc16c2bac0e31ef64c6d346cde3cf054cb", size = 8925045 }, + { url = "https://files.pythonhosted.org/packages/cc/b7/64d8cfa896c64435ae57f4917a548d7ac7a44762ff9802f75a79b77cb633/scikit_learn-1.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ee787491dbfe082d9c3013f01f5991658b0f38aa8177e4cd4bf434c58f551702", size = 8507994 }, + { url = "https://files.pythonhosted.org/packages/5e/37/e192ea709551799379958b4c4771ec507347027bb7c942662c7fbeba31cb/scikit_learn-1.8.0-cp313-cp313t-win_arm64.whl", hash = "sha256:bf97c10a3f5a7543f9b88cbf488d33d175e9146115a451ae34568597ba33dcde", size = 7869518 }, + { url = "https://files.pythonhosted.org/packages/24/05/1af2c186174cc92dcab2233f327336058c077d38f6fe2aceb08e6ab4d509/scikit_learn-1.8.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:c22a2da7a198c28dd1a6e1136f19c830beab7fdca5b3e5c8bba8394f8a5c45b3", size = 8528667 }, + { url = "https://files.pythonhosted.org/packages/a8/25/01c0af38fe969473fb292bba9dc2b8f9b451f3112ff242c647fee3d0dfe7/scikit_learn-1.8.0-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:6b595b07a03069a2b1740dc08c2299993850ea81cce4fe19b2421e0c970de6b7", size = 8066524 }, + { url = "https://files.pythonhosted.org/packages/be/ce/a0623350aa0b68647333940ee46fe45086c6060ec604874e38e9ab7d8e6c/scikit_learn-1.8.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:29ffc74089f3d5e87dfca4c2c8450f88bdc61b0fc6ed5d267f3988f19a1309f6", size = 8657133 }, + { url = "https://files.pythonhosted.org/packages/b8/cb/861b41341d6f1245e6ca80b1c1a8c4dfce43255b03df034429089ca2a2c5/scikit_learn-1.8.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fb65db5d7531bccf3a4f6bec3462223bea71384e2cda41da0f10b7c292b9e7c4", size = 8923223 }, + { url = "https://files.pythonhosted.org/packages/76/18/a8def8f91b18cd1ba6e05dbe02540168cb24d47e8dcf69e8d00b7da42a08/scikit_learn-1.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:56079a99c20d230e873ea40753102102734c5953366972a71d5cb39a32bc40c6", size = 8096518 }, + { url = "https://files.pythonhosted.org/packages/d1/77/482076a678458307f0deb44e29891d6022617b2a64c840c725495bee343f/scikit_learn-1.8.0-cp314-cp314-win_arm64.whl", hash = "sha256:3bad7565bc9cf37ce19a7c0d107742b320c1285df7aab1a6e2d28780df167242", size = 7754546 }, + { url = "https://files.pythonhosted.org/packages/2d/d1/ef294ca754826daa043b2a104e59960abfab4cf653891037d19dd5b6f3cf/scikit_learn-1.8.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:4511be56637e46c25721e83d1a9cea9614e7badc7040c4d573d75fbe257d6fd7", size = 8848305 }, + { url = "https://files.pythonhosted.org/packages/5b/e2/b1f8b05138ee813b8e1a4149f2f0d289547e60851fd1bb268886915adbda/scikit_learn-1.8.0-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:a69525355a641bf8ef136a7fa447672fb54fe8d60cab5538d9eb7c6438543fb9", size = 8432257 }, + { url = "https://files.pythonhosted.org/packages/26/11/c32b2138a85dcb0c99f6afd13a70a951bfdff8a6ab42d8160522542fb647/scikit_learn-1.8.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c2656924ec73e5939c76ac4c8b026fc203b83d8900362eb2599d8aee80e4880f", size = 8678673 }, + { url = "https://files.pythonhosted.org/packages/c7/57/51f2384575bdec454f4fe4e7a919d696c9ebce914590abf3e52d47607ab8/scikit_learn-1.8.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:15fc3b5d19cc2be65404786857f2e13c70c83dd4782676dd6814e3b89dc8f5b9", size = 8922467 }, + { url = "https://files.pythonhosted.org/packages/35/4d/748c9e2872637a57981a04adc038dacaa16ba8ca887b23e34953f0b3f742/scikit_learn-1.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:00d6f1d66fbcf4eba6e356e1420d33cc06c70a45bb1363cd6f6a8e4ebbbdece2", size = 8774395 }, + { url = "https://files.pythonhosted.org/packages/60/22/d7b2ebe4704a5e50790ba089d5c2ae308ab6bb852719e6c3bd4f04c3a363/scikit_learn-1.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:f28dd15c6bb0b66ba09728cf09fd8736c304be29409bd8445a080c1280619e8c", size = 8002647 }, +] + +[[package]] +name = "scipy" +version = "1.17.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7a/97/5a3609c4f8d58b039179648e62dd220f89864f56f7357f5d4f45c29eb2cc/scipy-1.17.1.tar.gz", hash = "sha256:95d8e012d8cb8816c226aef832200b1d45109ed4464303e997c5b13122b297c0", size = 30573822 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/75/b4ce781849931fef6fd529afa6b63711d5a733065722d0c3e2724af9e40a/scipy-1.17.1-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:1f95b894f13729334fb990162e911c9e5dc1ab390c58aa6cbecb389c5b5e28ec", size = 31613675 }, + { url = "https://files.pythonhosted.org/packages/f7/58/bccc2861b305abdd1b8663d6130c0b3d7cc22e8d86663edbc8401bfd40d4/scipy-1.17.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:e18f12c6b0bc5a592ed23d3f7b891f68fd7f8241d69b7883769eb5d5dfb52696", size = 28162057 }, + { url = "https://files.pythonhosted.org/packages/6d/ee/18146b7757ed4976276b9c9819108adbc73c5aad636e5353e20746b73069/scipy-1.17.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:a3472cfbca0a54177d0faa68f697d8ba4c80bbdc19908c3465556d9f7efce9ee", size = 20334032 }, + { url = "https://files.pythonhosted.org/packages/ec/e6/cef1cf3557f0c54954198554a10016b6a03b2ec9e22a4e1df734936bd99c/scipy-1.17.1-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:766e0dc5a616d026a3a1cffa379af959671729083882f50307e18175797b3dfd", size = 22709533 }, + { url = "https://files.pythonhosted.org/packages/4d/60/8804678875fc59362b0fb759ab3ecce1f09c10a735680318ac30da8cd76b/scipy-1.17.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:744b2bf3640d907b79f3fd7874efe432d1cf171ee721243e350f55234b4cec4c", size = 33062057 }, + { url = "https://files.pythonhosted.org/packages/09/7d/af933f0f6e0767995b4e2d705a0665e454d1c19402aa7e895de3951ebb04/scipy-1.17.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43af8d1f3bea642559019edfe64e9b11192a8978efbd1539d7bc2aaa23d92de4", size = 35349300 }, + { url = "https://files.pythonhosted.org/packages/b4/3d/7ccbbdcbb54c8fdc20d3b6930137c782a163fa626f0aef920349873421ba/scipy-1.17.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cd96a1898c0a47be4520327e01f874acfd61fb48a9420f8aa9f6483412ffa444", size = 35127333 }, + { url = "https://files.pythonhosted.org/packages/e8/19/f926cb11c42b15ba08e3a71e376d816ac08614f769b4f47e06c3580c836a/scipy-1.17.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4eb6c25dd62ee8d5edf68a8e1c171dd71c292fdae95d8aeb3dd7d7de4c364082", size = 37741314 }, + { url = "https://files.pythonhosted.org/packages/95/da/0d1df507cf574b3f224ccc3d45244c9a1d732c81dcb26b1e8a766ae271a8/scipy-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:d30e57c72013c2a4fe441c2fcb8e77b14e152ad48b5464858e07e2ad9fbfceff", size = 36607512 }, + { url = "https://files.pythonhosted.org/packages/68/7f/bdd79ceaad24b671543ffe0ef61ed8e659440eb683b66f033454dcee90eb/scipy-1.17.1-cp311-cp311-win_arm64.whl", hash = "sha256:9ecb4efb1cd6e8c4afea0daa91a87fbddbce1b99d2895d151596716c0b2e859d", size = 24599248 }, + { url = "https://files.pythonhosted.org/packages/35/48/b992b488d6f299dbe3f11a20b24d3dda3d46f1a635ede1c46b5b17a7b163/scipy-1.17.1-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:35c3a56d2ef83efc372eaec584314bd0ef2e2f0d2adb21c55e6ad5b344c0dcb8", size = 31610954 }, + { url = "https://files.pythonhosted.org/packages/b2/02/cf107b01494c19dc100f1d0b7ac3cc08666e96ba2d64db7626066cee895e/scipy-1.17.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:fcb310ddb270a06114bb64bbe53c94926b943f5b7f0842194d585c65eb4edd76", size = 28172662 }, + { url = "https://files.pythonhosted.org/packages/cf/a9/599c28631bad314d219cf9ffd40e985b24d603fc8a2f4ccc5ae8419a535b/scipy-1.17.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:cc90d2e9c7e5c7f1a482c9875007c095c3194b1cfedca3c2f3291cdc2bc7c086", size = 20344366 }, + { url = "https://files.pythonhosted.org/packages/35/f5/906eda513271c8deb5af284e5ef0206d17a96239af79f9fa0aebfe0e36b4/scipy-1.17.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:c80be5ede8f3f8eded4eff73cc99a25c388ce98e555b17d31da05287015ffa5b", size = 22704017 }, + { url = "https://files.pythonhosted.org/packages/da/34/16f10e3042d2f1d6b66e0428308ab52224b6a23049cb2f5c1756f713815f/scipy-1.17.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e19ebea31758fac5893a2ac360fedd00116cbb7628e650842a6691ba7ca28a21", size = 32927842 }, + { url = "https://files.pythonhosted.org/packages/01/8e/1e35281b8ab6d5d72ebe9911edcdffa3f36b04ed9d51dec6dd140396e220/scipy-1.17.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:02ae3b274fde71c5e92ac4d54bc06c42d80e399fec704383dcd99b301df37458", size = 35235890 }, + { url = "https://files.pythonhosted.org/packages/c5/5c/9d7f4c88bea6e0d5a4f1bc0506a53a00e9fcb198de372bfe4d3652cef482/scipy-1.17.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8a604bae87c6195d8b1045eddece0514d041604b14f2727bbc2b3020172045eb", size = 35003557 }, + { url = "https://files.pythonhosted.org/packages/65/94/7698add8f276dbab7a9de9fb6b0e02fc13ee61d51c7c3f85ac28b65e1239/scipy-1.17.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f590cd684941912d10becc07325a3eeb77886fe981415660d9265c4c418d0bea", size = 37625856 }, + { url = "https://files.pythonhosted.org/packages/a2/84/dc08d77fbf3d87d3ee27f6a0c6dcce1de5829a64f2eae85a0ecc1f0daa73/scipy-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:41b71f4a3a4cab9d366cd9065b288efc4d4f3c0b37a91a8e0947fb5bd7f31d87", size = 36549682 }, + { url = "https://files.pythonhosted.org/packages/bc/98/fe9ae9ffb3b54b62559f52dedaebe204b408db8109a8c66fdd04869e6424/scipy-1.17.1-cp312-cp312-win_arm64.whl", hash = "sha256:f4115102802df98b2b0db3cce5cb9b92572633a1197c77b7553e5203f284a5b3", size = 24547340 }, + { url = "https://files.pythonhosted.org/packages/76/27/07ee1b57b65e92645f219b37148a7e7928b82e2b5dbeccecb4dff7c64f0b/scipy-1.17.1-cp313-cp313-macosx_10_14_x86_64.whl", hash = "sha256:5e3c5c011904115f88a39308379c17f91546f77c1667cea98739fe0fccea804c", size = 31590199 }, + { url = "https://files.pythonhosted.org/packages/ec/ae/db19f8ab842e9b724bf5dbb7db29302a91f1e55bc4d04b1025d6d605a2c5/scipy-1.17.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:6fac755ca3d2c3edcb22f479fceaa241704111414831ddd3bc6056e18516892f", size = 28154001 }, + { url = "https://files.pythonhosted.org/packages/5b/58/3ce96251560107b381cbd6e8413c483bbb1228a6b919fa8652b0d4090e7f/scipy-1.17.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:7ff200bf9d24f2e4d5dc6ee8c3ac64d739d3a89e2326ba68aaf6c4a2b838fd7d", size = 20325719 }, + { url = "https://files.pythonhosted.org/packages/b2/83/15087d945e0e4d48ce2377498abf5ad171ae013232ae31d06f336e64c999/scipy-1.17.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:4b400bdc6f79fa02a4d86640310dde87a21fba0c979efff5248908c6f15fad1b", size = 22683595 }, + { url = "https://files.pythonhosted.org/packages/b4/e0/e58fbde4a1a594c8be8114eb4aac1a55bcd6587047efc18a61eb1f5c0d30/scipy-1.17.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2b64ca7d4aee0102a97f3ba22124052b4bd2152522355073580bf4845e2550b6", size = 32896429 }, + { url = "https://files.pythonhosted.org/packages/f5/5f/f17563f28ff03c7b6799c50d01d5d856a1d55f2676f537ca8d28c7f627cd/scipy-1.17.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:581b2264fc0aa555f3f435a5944da7504ea3a065d7029ad60e7c3d1ae09c5464", size = 35203952 }, + { url = "https://files.pythonhosted.org/packages/8d/a5/9afd17de24f657fdfe4df9a3f1ea049b39aef7c06000c13db1530d81ccca/scipy-1.17.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:beeda3d4ae615106d7094f7e7cef6218392e4465cc95d25f900bebabfded0950", size = 34979063 }, + { url = "https://files.pythonhosted.org/packages/8b/13/88b1d2384b424bf7c924f2038c1c409f8d88bb2a8d49d097861dd64a57b2/scipy-1.17.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6609bc224e9568f65064cfa72edc0f24ee6655b47575954ec6339534b2798369", size = 37598449 }, + { url = "https://files.pythonhosted.org/packages/35/e5/d6d0e51fc888f692a35134336866341c08655d92614f492c6860dc45bb2c/scipy-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:37425bc9175607b0268f493d79a292c39f9d001a357bebb6b88fdfaff13f6448", size = 36510943 }, + { url = "https://files.pythonhosted.org/packages/2a/fd/3be73c564e2a01e690e19cc618811540ba5354c67c8680dce3281123fb79/scipy-1.17.1-cp313-cp313-win_arm64.whl", hash = "sha256:5cf36e801231b6a2059bf354720274b7558746f3b1a4efb43fcf557ccd484a87", size = 24545621 }, + { url = "https://files.pythonhosted.org/packages/6f/6b/17787db8b8114933a66f9dcc479a8272e4b4da75fe03b0c282f7b0ade8cd/scipy-1.17.1-cp313-cp313t-macosx_10_14_x86_64.whl", hash = "sha256:d59c30000a16d8edc7e64152e30220bfbd724c9bbb08368c054e24c651314f0a", size = 31936708 }, + { url = "https://files.pythonhosted.org/packages/38/2e/524405c2b6392765ab1e2b722a41d5da33dc5c7b7278184a8ad29b6cb206/scipy-1.17.1-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:010f4333c96c9bb1a4516269e33cb5917b08ef2166d5556ca2fd9f082a9e6ea0", size = 28570135 }, + { url = "https://files.pythonhosted.org/packages/fd/c3/5bd7199f4ea8556c0c8e39f04ccb014ac37d1468e6cfa6a95c6b3562b76e/scipy-1.17.1-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:2ceb2d3e01c5f1d83c4189737a42d9cb2fc38a6eeed225e7515eef71ad301dce", size = 20741977 }, + { url = "https://files.pythonhosted.org/packages/d9/b8/8ccd9b766ad14c78386599708eb745f6b44f08400a5fd0ade7cf89b6fc93/scipy-1.17.1-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:844e165636711ef41f80b4103ed234181646b98a53c8f05da12ca5ca289134f6", size = 23029601 }, + { url = "https://files.pythonhosted.org/packages/6d/a0/3cb6f4d2fb3e17428ad2880333cac878909ad1a89f678527b5328b93c1d4/scipy-1.17.1-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:158dd96d2207e21c966063e1635b1063cd7787b627b6f07305315dd73d9c679e", size = 33019667 }, + { url = "https://files.pythonhosted.org/packages/f3/c3/2d834a5ac7bf3a0c806ad1508efc02dda3c8c61472a56132d7894c312dea/scipy-1.17.1-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:74cbb80d93260fe2ffa334efa24cb8f2f0f622a9b9febf8b483c0b865bfb3475", size = 35264159 }, + { url = "https://files.pythonhosted.org/packages/4d/77/d3ed4becfdbd217c52062fafe35a72388d1bd82c2d0ba5ca19d6fcc93e11/scipy-1.17.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:dbc12c9f3d185f5c737d801da555fb74b3dcfa1a50b66a1a93e09190f41fab50", size = 35102771 }, + { url = "https://files.pythonhosted.org/packages/bd/12/d19da97efde68ca1ee5538bb261d5d2c062f0c055575128f11a2730e3ac1/scipy-1.17.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:94055a11dfebe37c656e70317e1996dc197e1a15bbcc351bcdd4610e128fe1ca", size = 37665910 }, + { url = "https://files.pythonhosted.org/packages/06/1c/1172a88d507a4baaf72c5a09bb6c018fe2ae0ab622e5830b703a46cc9e44/scipy-1.17.1-cp313-cp313t-win_amd64.whl", hash = "sha256:e30bdeaa5deed6bc27b4cc490823cd0347d7dae09119b8803ae576ea0ce52e4c", size = 36562980 }, + { url = "https://files.pythonhosted.org/packages/70/b0/eb757336e5a76dfa7911f63252e3b7d1de00935d7705cf772db5b45ec238/scipy-1.17.1-cp313-cp313t-win_arm64.whl", hash = "sha256:a720477885a9d2411f94a93d16f9d89bad0f28ca23c3f8daa521e2dcc3f44d49", size = 24856543 }, + { url = "https://files.pythonhosted.org/packages/cf/83/333afb452af6f0fd70414dc04f898647ee1423979ce02efa75c3b0f2c28e/scipy-1.17.1-cp314-cp314-macosx_10_14_x86_64.whl", hash = "sha256:a48a72c77a310327f6a3a920092fa2b8fd03d7deaa60f093038f22d98e096717", size = 31584510 }, + { url = "https://files.pythonhosted.org/packages/ed/a6/d05a85fd51daeb2e4ea71d102f15b34fedca8e931af02594193ae4fd25f7/scipy-1.17.1-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:45abad819184f07240d8a696117a7aacd39787af9e0b719d00285549ed19a1e9", size = 28170131 }, + { url = "https://files.pythonhosted.org/packages/db/7b/8624a203326675d7746a254083a187398090a179335b2e4a20e2ddc46e83/scipy-1.17.1-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:3fd1fcdab3ea951b610dc4cef356d416d5802991e7e32b5254828d342f7b7e0b", size = 20342032 }, + { url = "https://files.pythonhosted.org/packages/c9/35/2c342897c00775d688d8ff3987aced3426858fd89d5a0e26e020b660b301/scipy-1.17.1-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:7bdf2da170b67fdf10bca777614b1c7d96ae3ca5794fd9587dce41eb2966e866", size = 22678766 }, + { url = "https://files.pythonhosted.org/packages/ef/f2/7cdb8eb308a1a6ae1e19f945913c82c23c0c442a462a46480ce487fdc0ac/scipy-1.17.1-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:adb2642e060a6549c343603a3851ba76ef0b74cc8c079a9a58121c7ec9fe2350", size = 32957007 }, + { url = "https://files.pythonhosted.org/packages/0b/2e/7eea398450457ecb54e18e9d10110993fa65561c4f3add5e8eccd2b9cd41/scipy-1.17.1-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eee2cfda04c00a857206a4330f0c5e3e56535494e30ca445eb19ec624ae75118", size = 35221333 }, + { url = "https://files.pythonhosted.org/packages/d9/77/5b8509d03b77f093a0d52e606d3c4f79e8b06d1d38c441dacb1e26cacf46/scipy-1.17.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d2650c1fb97e184d12d8ba010493ee7b322864f7d3d00d3f9bb97d9c21de4068", size = 35042066 }, + { url = "https://files.pythonhosted.org/packages/f9/df/18f80fb99df40b4070328d5ae5c596f2f00fffb50167e31439e932f29e7d/scipy-1.17.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:08b900519463543aa604a06bec02461558a6e1cef8fdbb8098f77a48a83c8118", size = 37612763 }, + { url = "https://files.pythonhosted.org/packages/4b/39/f0e8ea762a764a9dc52aa7dabcfad51a354819de1f0d4652b6a1122424d6/scipy-1.17.1-cp314-cp314-win_amd64.whl", hash = "sha256:3877ac408e14da24a6196de0ddcace62092bfc12a83823e92e49e40747e52c19", size = 37290984 }, + { url = "https://files.pythonhosted.org/packages/7c/56/fe201e3b0f93d1a8bcf75d3379affd228a63d7e2d80ab45467a74b494947/scipy-1.17.1-cp314-cp314-win_arm64.whl", hash = "sha256:f8885db0bc2bffa59d5c1b72fad7a6a92d3e80e7257f967dd81abb553a90d293", size = 25192877 }, + { url = "https://files.pythonhosted.org/packages/96/ad/f8c414e121f82e02d76f310f16db9899c4fcde36710329502a6b2a3c0392/scipy-1.17.1-cp314-cp314t-macosx_10_14_x86_64.whl", hash = "sha256:1cc682cea2ae55524432f3cdff9e9a3be743d52a7443d0cba9017c23c87ae2f6", size = 31949750 }, + { url = "https://files.pythonhosted.org/packages/7c/b0/c741e8865d61b67c81e255f4f0a832846c064e426636cd7de84e74d209be/scipy-1.17.1-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:2040ad4d1795a0ae89bfc7e8429677f365d45aa9fd5e4587cf1ea737f927b4a1", size = 28585858 }, + { url = "https://files.pythonhosted.org/packages/ed/1b/3985219c6177866628fa7c2595bfd23f193ceebbe472c98a08824b9466ff/scipy-1.17.1-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:131f5aaea57602008f9822e2115029b55d4b5f7c070287699fe45c661d051e39", size = 20757723 }, + { url = "https://files.pythonhosted.org/packages/c0/19/2a04aa25050d656d6f7b9e7b685cc83d6957fb101665bfd9369ca6534563/scipy-1.17.1-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:9cdc1a2fcfd5c52cfb3045feb399f7b3ce822abdde3a193a6b9a60b3cb5854ca", size = 23043098 }, + { url = "https://files.pythonhosted.org/packages/86/f1/3383beb9b5d0dbddd030335bf8a8b32d4317185efe495374f134d8be6cce/scipy-1.17.1-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e3dcd57ab780c741fde8dc68619de988b966db759a3c3152e8e9142c26295ad", size = 33030397 }, + { url = "https://files.pythonhosted.org/packages/41/68/8f21e8a65a5a03f25a79165ec9d2b28c00e66dc80546cf5eb803aeeff35b/scipy-1.17.1-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a9956e4d4f4a301ebf6cde39850333a6b6110799d470dbbb1e25326ac447f52a", size = 35281163 }, + { url = "https://files.pythonhosted.org/packages/84/8d/c8a5e19479554007a5632ed7529e665c315ae7492b4f946b0deb39870e39/scipy-1.17.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:a4328d245944d09fd639771de275701ccadf5f781ba0ff092ad141e017eccda4", size = 35116291 }, + { url = "https://files.pythonhosted.org/packages/52/52/e57eceff0e342a1f50e274264ed47497b59e6a4e3118808ee58ddda7b74a/scipy-1.17.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a77cbd07b940d326d39a1d1b37817e2ee4d79cb30e7338f3d0cddffae70fcaa2", size = 37682317 }, + { url = "https://files.pythonhosted.org/packages/11/2f/b29eafe4a3fbc3d6de9662b36e028d5f039e72d345e05c250e121a230dd4/scipy-1.17.1-cp314-cp314t-win_amd64.whl", hash = "sha256:eb092099205ef62cd1782b006658db09e2fed75bffcae7cc0d44052d8aa0f484", size = 37345327 }, + { url = "https://files.pythonhosted.org/packages/07/39/338d9219c4e87f3e708f18857ecd24d22a0c3094752393319553096b98af/scipy-1.17.1-cp314-cp314t-win_arm64.whl", hash = "sha256:200e1050faffacc162be6a486a984a0497866ec54149a01270adc8a59b7c7d21", size = 25489165 }, +] + [[package]] name = "scramp" version = "1.4.8" @@ -2039,13 +2837,32 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/90/07/a962d2477331abfdb2c6a8251b65c673dbb07ad707d1882d61562b8b9147/scramp-1.4.8-py3-none-any.whl", hash = "sha256:87c2f15976845a2872fe5490a06097f0d01813cceb53774ea168c911f2ad025c", size = 13121 }, ] +[[package]] +name = "sentence-transformers" +version = "5.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "huggingface-hub" }, + { name = "numpy" }, + { name = "scikit-learn" }, + { name = "scipy" }, + { name = "torch" }, + { name = "tqdm" }, + { name = "transformers" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fe/26/448453925b6ce0c29d8b54327caa71ee4835511aef02070467402273079c/sentence_transformers-5.3.0.tar.gz", hash = "sha256:414a0a881f53a4df0e6cbace75f823bfcb6b94d674c42a384b498959b7c065e2", size = 403330 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e2/9c/2fa7224058cad8df68d84bafee21716f30892cecc7ad1ad73bde61d23754/sentence_transformers-5.3.0-py3-none-any.whl", hash = "sha256:dca6b98db790274a68185d27a65801b58b4caf653a4e556b5f62827509347c7d", size = 512390 }, +] + [[package]] name = "setuptools" -version = "82.0.1" +version = "81.0.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4f/db/cfac1baf10650ab4d1c111714410d2fbb77ac5a616db26775db562c8fab2/setuptools-82.0.1.tar.gz", hash = "sha256:7d872682c5d01cfde07da7bccc7b65469d3dca203318515ada1de5eda35efbf9", size = 1152316 } +sdist = { url = "https://files.pythonhosted.org/packages/0d/1c/73e719955c59b8e424d015ab450f51c0af856ae46ea2da83eba51cc88de1/setuptools-81.0.0.tar.gz", hash = "sha256:487b53915f52501f0a79ccfd0c02c165ffe06631443a886740b91af4b7a5845a", size = 1198299 } wheels = [ - { url = "https://files.pythonhosted.org/packages/9d/76/f789f7a86709c6b087c5a2f52f911838cad707cc613162401badc665acfe/setuptools-82.0.1-py3-none-any.whl", hash = "sha256:a59e362652f08dcd477c78bb6e7bd9d80a7995bc73ce773050228a348ce2e5bb", size = 1006223 }, + { url = "https://files.pythonhosted.org/packages/e1/e3/c164c88b2e5ce7b24d667b9bd83589cf4f3520d97cad01534cd3c4f55fdb/setuptools-81.0.0-py3-none-any.whl", hash = "sha256:fdd925d5c5d9f62e4b74b30d6dd7828ce236fd6ed998a08d81de62ce5a6310d6", size = 1062021 }, ] [[package]] @@ -2189,6 +3006,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ad/c9/f58c3a17beb650700f9d2eccd410726b6d96df8953663700764ca48636c7/sqlglot-29.0.1-py3-none-any.whl", hash = "sha256:06a473ea6c2b3632ac67bd38e687a6860265bf4156e66b54adeda15d07f00c65", size = 611448 }, ] +[[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 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353 }, +] + [[package]] name = "tenacity" version = "9.1.4" @@ -2220,6 +3049,15 @@ mysql = [ { name = "sqlalchemy" }, ] +[[package]] +name = "threadpoolctl" +version = "3.6.0" +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 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/32/d5/f9a850d79b0851d1d4ef6456097579a9005b31fea68726a4ae5f2d82ddd9/threadpoolctl-3.6.0-py3-none-any.whl", hash = "sha256:43a0b8fd5a2928500110039e43a5eed8480b918967083ea48dc3ab9f13c4a7fb", size = 18638 }, +] + [[package]] name = "thrift" version = "0.20.0" @@ -2229,6 +3067,32 @@ dependencies = [ ] sdist = { url = "https://files.pythonhosted.org/packages/3c/2d/8946864f716ac82dcc88d290ed613cba7a80ec75df4f553ec3ff275f486e/thrift-0.20.0.tar.gz", hash = "sha256:4dd662eadf6b8aebe8a41729527bd69adf6ceaa2a8681cbef64d1273b3e8feba", size = 62295 } +[[package]] +name = "tokenizers" +version = "0.22.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "huggingface-hub" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/73/6f/f80cfef4a312e1fb34baf7d85c72d4411afde10978d4657f8cdd811d3ccc/tokenizers-0.22.2.tar.gz", hash = "sha256:473b83b915e547aa366d1eee11806deaf419e17be16310ac0a14077f1e28f917", size = 372115 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/92/97/5dbfabf04c7e348e655e907ed27913e03db0923abb5dfdd120d7b25630e1/tokenizers-0.22.2-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:544dd704ae7238755d790de45ba8da072e9af3eea688f698b137915ae959281c", size = 3100275 }, + { url = "https://files.pythonhosted.org/packages/2e/47/174dca0502ef88b28f1c9e06b73ce33500eedfac7a7692108aec220464e7/tokenizers-0.22.2-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:1e418a55456beedca4621dbab65a318981467a2b188e982a23e117f115ce5001", size = 2981472 }, + { url = "https://files.pythonhosted.org/packages/d6/84/7990e799f1309a8b87af6b948f31edaa12a3ed22d11b352eaf4f4b2e5753/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2249487018adec45d6e3554c71d46eb39fa8ea67156c640f7513eb26f318cec7", size = 3290736 }, + { url = "https://files.pythonhosted.org/packages/78/59/09d0d9ba94dcd5f4f1368d4858d24546b4bdc0231c2354aa31d6199f0399/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:25b85325d0815e86e0bac263506dd114578953b7b53d7de09a6485e4a160a7dd", size = 3168835 }, + { url = "https://files.pythonhosted.org/packages/47/50/b3ebb4243e7160bda8d34b731e54dd8ab8b133e50775872e7a434e524c28/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bfb88f22a209ff7b40a576d5324bf8286b519d7358663db21d6246fb17eea2d5", size = 3521673 }, + { url = "https://files.pythonhosted.org/packages/e0/fa/89f4cb9e08df770b57adb96f8cbb7e22695a4cb6c2bd5f0c4f0ebcf33b66/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1c774b1276f71e1ef716e5486f21e76333464f47bece56bbd554485982a9e03e", size = 3724818 }, + { url = "https://files.pythonhosted.org/packages/64/04/ca2363f0bfbe3b3d36e95bf67e56a4c88c8e3362b658e616d1ac185d47f2/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:df6c4265b289083bf710dff49bc51ef252f9d5be33a45ee2bed151114a56207b", size = 3379195 }, + { url = "https://files.pythonhosted.org/packages/2e/76/932be4b50ef6ccedf9d3c6639b056a967a86258c6d9200643f01269211ca/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:369cc9fc8cc10cb24143873a0d95438bb8ee257bb80c71989e3ee290e8d72c67", size = 3274982 }, + { url = "https://files.pythonhosted.org/packages/1d/28/5f9f5a4cc211b69e89420980e483831bcc29dade307955cc9dc858a40f01/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:29c30b83d8dcd061078b05ae0cb94d3c710555fbb44861139f9f83dcca3dc3e4", size = 9478245 }, + { url = "https://files.pythonhosted.org/packages/6c/fb/66e2da4704d6aadebf8cb39f1d6d1957df667ab24cff2326b77cda0dcb85/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:37ae80a28c1d3265bb1f22464c856bd23c02a05bb211e56d0c5301a435be6c1a", size = 9560069 }, + { url = "https://files.pythonhosted.org/packages/16/04/fed398b05caa87ce9b1a1bb5166645e38196081b225059a6edaff6440fac/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:791135ee325f2336f498590eb2f11dc5c295232f288e75c99a36c5dbce63088a", size = 9899263 }, + { url = "https://files.pythonhosted.org/packages/05/a1/d62dfe7376beaaf1394917e0f8e93ee5f67fea8fcf4107501db35996586b/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:38337540fbbddff8e999d59970f3c6f35a82de10053206a7562f1ea02d046fa5", size = 10033429 }, + { url = "https://files.pythonhosted.org/packages/fd/18/a545c4ea42af3df6effd7d13d250ba77a0a86fb20393143bbb9a92e434d4/tokenizers-0.22.2-cp39-abi3-win32.whl", hash = "sha256:a6bf3f88c554a2b653af81f3204491c818ae2ac6fbc09e76ef4773351292bc92", size = 2502363 }, + { url = "https://files.pythonhosted.org/packages/65/71/0670843133a43d43070abeb1949abfdef12a86d490bea9cd9e18e37c5ff7/tokenizers-0.22.2-cp39-abi3-win_amd64.whl", hash = "sha256:c9ea31edff2968b44a88f97d784c2f16dc0729b8b143ed004699ebca91f05c48", size = 2747786 }, + { url = "https://files.pythonhosted.org/packages/72/f4/0de46cfa12cdcbcd464cc59fde36912af405696f687e53a091fb432f694c/tokenizers-0.22.2-cp39-abi3-win_arm64.whl", hash = "sha256:9ce725d22864a1e965217204946f830c37876eee3b2ba6fc6255e8e903d5fcbc", size = 2612133 }, +] + [[package]] name = "tomlkit" version = "0.14.0" @@ -2247,6 +3111,85 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/fb/12/5911ae3eeec47800503a238d971e51722ccea5feb8569b735184d5fcdbc0/toolz-1.1.0-py3-none-any.whl", hash = "sha256:15ccc861ac51c53696de0a5d6d4607f99c210739caf987b5d2054f3efed429d8", size = 58093 }, ] +[[package]] +name = "torch" +version = "2.11.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cuda-bindings", marker = "sys_platform == 'linux'" }, + { name = "cuda-toolkit", extra = ["cublas", "cudart", "cufft", "cufile", "cupti", "curand", "cusolver", "cusparse", "nvjitlink", "nvrtc", "nvtx"], marker = "sys_platform == 'linux'" }, + { name = "filelock" }, + { name = "fsspec" }, + { name = "jinja2" }, + { name = "networkx" }, + { name = "nvidia-cudnn-cu13", marker = "sys_platform == 'linux'" }, + { name = "nvidia-cusparselt-cu13", marker = "sys_platform == 'linux'" }, + { name = "nvidia-nccl-cu13", marker = "sys_platform == 'linux'" }, + { name = "nvidia-nvshmem-cu13", marker = "sys_platform == 'linux'" }, + { name = "setuptools" }, + { name = "sympy" }, + { name = "triton", marker = "sys_platform == 'linux'" }, + { name = "typing-extensions" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/ae/0d/98b410492609e34a155fa8b121b55c7dca229f39636851c3a9ec20edea21/torch-2.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7b6a60d48062809f58595509c524b88e6ddec3ebe25833d6462eeab81e5f2ce4", size = 80529712 }, + { url = "https://files.pythonhosted.org/packages/84/03/acea680005f098f79fd70c1d9d5ccc0cb4296ec2af539a0450108232fc0c/torch-2.11.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:d91aac77f24082809d2c5a93f52a5f085032740a1ebc9252a7b052ef5a4fddc6", size = 419718178 }, + { url = "https://files.pythonhosted.org/packages/8c/8b/d7be22fbec9ffee6cff31a39f8750d4b3a65d349a286cf4aec74c2375662/torch-2.11.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:7aa2f9bbc6d4595ba72138026b2074be1233186150e9292865e04b7a63b8c67a", size = 530604548 }, + { url = "https://files.pythonhosted.org/packages/d1/bd/9912d30b68845256aabbb4a40aeefeef3c3b20db5211ccda653544ada4b6/torch-2.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:73e24aaf8f36ab90d95cd1761208b2eb70841c2a9ca1a3f9061b39fc5331b708", size = 114519675 }, + { url = "https://files.pythonhosted.org/packages/6f/8b/69e3008d78e5cee2b30183340cc425081b78afc5eff3d080daab0adda9aa/torch-2.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4b5866312ee6e52ea625cd211dcb97d6a2cdc1131a5f15cc0d87eec948f6dd34", size = 80606338 }, + { url = "https://files.pythonhosted.org/packages/13/16/42e5915ebe4868caa6bac83a8ed59db57f12e9a61b7d749d584776ed53d5/torch-2.11.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:f99924682ef0aa6a4ab3b1b76f40dc6e273fca09f367d15a524266db100a723f", size = 419731115 }, + { url = "https://files.pythonhosted.org/packages/1a/c9/82638ef24d7877510f83baf821f5619a61b45568ce21c0a87a91576510aa/torch-2.11.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:0f68f4ac6d95d12e896c3b7a912b5871619542ec54d3649cf48cc1edd4dd2756", size = 530712279 }, + { url = "https://files.pythonhosted.org/packages/1c/ff/6756f1c7ee302f6d202120e0f4f05b432b839908f9071157302cedfc5232/torch-2.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:fbf39280699d1b869f55eac536deceaa1b60bd6788ba74f399cc67e60a5fab10", size = 114556047 }, + { url = "https://files.pythonhosted.org/packages/87/89/5ea6722763acee56b045435fb84258db7375c48165ec8be7880ab2b281c5/torch-2.11.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1e6debd97ccd3205bbb37eb806a9d8219e1139d15419982c09e23ef7d4369d18", size = 80606801 }, + { url = "https://files.pythonhosted.org/packages/32/d1/8ed2173589cbfe744ed54e5a73efc107c0085ba5777ee93a5f4c1ab90553/torch-2.11.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:63a68fa59de8f87acc7e85a5478bb2dddbb3392b7593ec3e78827c793c4b73fd", size = 419732382 }, + { url = "https://files.pythonhosted.org/packages/3d/e1/b73f7c575a4b8f87a5928f50a1e35416b5e27295d8be9397d5293e7e8d4c/torch-2.11.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:cc89b9b173d9adfab59fd227f0ab5e5516d9a52b658ae41d64e59d2e55a418db", size = 530711509 }, + { url = "https://files.pythonhosted.org/packages/66/82/3e3fcdd388fbe54e29fd3f991f36846ff4ac90b0d0181e9c8f7236565f82/torch-2.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:4dda3b3f52d121063a731ddb835f010dc137b920d7fec2778e52f60d8e4bf0cd", size = 114555842 }, + { url = "https://files.pythonhosted.org/packages/db/38/8ac78069621b8c2b4979c2f96dc8409ef5e9c4189f6aac629189a78677ca/torch-2.11.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:8b394322f49af4362d4f80e424bcaca7efcd049619af03a4cf4501520bdf0fb4", size = 80959574 }, + { url = "https://files.pythonhosted.org/packages/6d/6c/56bfb37073e7136e6dd86bfc6af7339946dd684e0ecf2155ac0eee687ae1/torch-2.11.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:2658f34ce7e2dabf4ec73b45e2ca68aedad7a5be87ea756ad656eaf32bf1e1ea", size = 419732324 }, + { url = "https://files.pythonhosted.org/packages/07/f4/1b666b6d61d3394cca306ea543ed03a64aad0a201b6cd159f1d41010aeb1/torch-2.11.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:98bb213c3084cfe176302949bdc360074b18a9da7ab59ef2edc9d9f742504778", size = 530596026 }, + { url = "https://files.pythonhosted.org/packages/48/6b/30d1459fa7e4b67e9e3fe1685ca1d8bb4ce7c62ef436c3a615963c6c866c/torch-2.11.0-cp313-cp313t-win_amd64.whl", hash = "sha256:a97b94bbf62992949b4730c6cd2cc9aee7b335921ee8dc207d930f2ed09ae2db", size = 114793702 }, + { url = "https://files.pythonhosted.org/packages/26/0d/8603382f61abd0db35841148ddc1ffd607bf3100b11c6e1dab6d2fc44e72/torch-2.11.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:01018087326984a33b64e04c8cb5c2795f9120e0d775ada1f6638840227b04d7", size = 80573442 }, + { url = "https://files.pythonhosted.org/packages/c7/86/7cd7c66cb9cec6be330fff36db5bd0eef386d80c031b581ec81be1d4b26c/torch-2.11.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:2bb3cc54bd0dea126b0060bb1ec9de0f9c7f7342d93d436646516b0330cd5be7", size = 419749385 }, + { url = "https://files.pythonhosted.org/packages/47/e8/b98ca2d39b2e0e4730c0ee52537e488e7008025bc77ca89552ff91021f7c/torch-2.11.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:4dc8b3809469b6c30b411bb8c4cad3828efd26236153d9beb6a3ec500f211a60", size = 530716756 }, + { url = "https://files.pythonhosted.org/packages/78/88/d4a4cda8362f8a30d1ed428564878c3cafb0d87971fbd3947d4c84552095/torch-2.11.0-cp314-cp314-win_amd64.whl", hash = "sha256:2b4e811728bd0cc58fb2b0948fe939a1ee2bf1422f6025be2fca4c7bd9d79718", size = 114552300 }, + { url = "https://files.pythonhosted.org/packages/bf/46/4419098ed6d801750f26567b478fc185c3432e11e2cad712bc6b4c2ab0d0/torch-2.11.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:8245477871c3700d4370352ffec94b103cfcb737229445cf9946cddb7b2ca7cd", size = 80959460 }, + { url = "https://files.pythonhosted.org/packages/fd/66/54a56a4a6ceaffb567231994a9745821d3af922a854ed33b0b3a278e0a99/torch-2.11.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:ab9a8482f475f9ba20e12db84b0e55e2f58784bdca43a854a6ccd3fd4b9f75e6", size = 419735835 }, + { url = "https://files.pythonhosted.org/packages/b1/e7/0b6665f533aa9e337662dc190425abc0af1fe3234088f4454c52393ded61/torch-2.11.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:563ed3d25542d7e7bbc5b235ccfacfeb97fb470c7fee257eae599adb8005c8a2", size = 530613405 }, + { url = "https://files.pythonhosted.org/packages/cf/bf/c8d12a2c86dbfd7f40fb2f56fbf5a505ccf2d9ce131eb559dfc7c51e1a04/torch-2.11.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b2a43985ff5ef6ddd923bbcf99943e5f58059805787c5c9a2622bf05ca2965b0", size = 114792991 }, +] + +[[package]] +name = "tqdm" +version = "4.67.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/09/a9/6ba95a270c6f1fbcd8dac228323f2777d886cb206987444e4bce66338dd4/tqdm-4.67.3.tar.gz", hash = "sha256:7d825f03f89244ef73f1d4ce193cb1774a8179fd96f31d7e1dcde62092b960bb", size = 169598 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/16/e1/3079a9ff9b8e11b846c6ac5c8b5bfb7ff225eee721825310c91b3b50304f/tqdm-4.67.3-py3-none-any.whl", hash = "sha256:ee1e4c0e59148062281c49d80b25b67771a127c85fc9676d3be5f243206826bf", size = 78374 }, +] + +[[package]] +name = "transformers" +version = "5.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "huggingface-hub" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "pyyaml" }, + { name = "regex" }, + { name = "safetensors" }, + { name = "tokenizers" }, + { name = "tqdm" }, + { name = "typer" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0b/4c/42a8e1c7bbe668d8e073941ec3205263afb1cd02683fa5a8a75e615fdfbe/transformers-5.4.0.tar.gz", hash = "sha256:cb34ca89dce345ae3224b290346b9c0fa9694b951d54f3ed16334a4b1bfe3d04", size = 8152836 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0b/a0/0a87883e564e364baab32adcacb4bec2e200b28a568423c8cf7fde316461/transformers-5.4.0-py3-none-any.whl", hash = "sha256:9fbe50602d2a4e6d0aa8a35a605433dfac72d595ee2192eae192590a6cc2df86", size = 10105556 }, +] + [[package]] name = "trino" version = "0.337.0" @@ -2265,6 +3208,25 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2d/99/c39eca8b3fca72d39fe507f9e7598174acb0a5845ecdd20630790e268cb2/trino-0.337.0-py3-none-any.whl", hash = "sha256:868f2b8137d4d1baa84c9bc341f2cdf29039462aa69d7c089a0b821b5a91f29c", size = 58558 }, ] +[[package]] +name = "triton" +version = "3.6.0" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0f/2c/96f92f3c60387e14cc45aed49487f3486f89ea27106c1b1376913c62abe4/triton-3.6.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:49df5ef37379c0c2b5c0012286f80174fcf0e073e5ade1ca9a86c36814553651", size = 176081190 }, + { url = "https://files.pythonhosted.org/packages/e0/12/b05ba554d2c623bffa59922b94b0775673de251f468a9609bc9e45de95e9/triton-3.6.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e8e323d608e3a9bfcc2d9efcc90ceefb764a82b99dea12a86d643c72539ad5d3", size = 188214640 }, + { url = "https://files.pythonhosted.org/packages/17/5d/08201db32823bdf77a0e2b9039540080b2e5c23a20706ddba942924ebcd6/triton-3.6.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:374f52c11a711fd062b4bfbb201fd9ac0a5febd28a96fb41b4a0f51dde3157f4", size = 176128243 }, + { url = "https://files.pythonhosted.org/packages/ab/a8/cdf8b3e4c98132f965f88c2313a4b493266832ad47fb52f23d14d4f86bb5/triton-3.6.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:74caf5e34b66d9f3a429af689c1c7128daba1d8208df60e81106b115c00d6fca", size = 188266850 }, + { url = "https://files.pythonhosted.org/packages/3c/12/34d71b350e89a204c2c7777a9bba0dcf2f19a5bfdd70b57c4dbc5ffd7154/triton-3.6.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:448e02fe6dc898e9e5aa89cf0ee5c371e99df5aa5e8ad976a80b93334f3494fd", size = 176133521 }, + { url = "https://files.pythonhosted.org/packages/f9/0b/37d991d8c130ce81a8728ae3c25b6e60935838e9be1b58791f5997b24a54/triton-3.6.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:10c7f76c6e72d2ef08df639e3d0d30729112f47a56b0c81672edc05ee5116ac9", size = 188289450 }, + { url = "https://files.pythonhosted.org/packages/ce/4e/41b0c8033b503fd3cfcd12392cdd256945026a91ff02452bef40ec34bee7/triton-3.6.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1722e172d34e32abc3eb7711d0025bb69d7959ebea84e3b7f7a341cd7ed694d6", size = 176276087 }, + { url = "https://files.pythonhosted.org/packages/35/f8/9c66bfc55361ec6d0e4040a0337fb5924ceb23de4648b8a81ae9d33b2b38/triton-3.6.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d002e07d7180fd65e622134fbd980c9a3d4211fb85224b56a0a0efbd422ab72f", size = 188400296 }, + { url = "https://files.pythonhosted.org/packages/49/55/5ecf0dcaa0f2fbbd4420f7ef227ee3cb172e91e5fede9d0ecaddc43363b4/triton-3.6.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef5523241e7d1abca00f1d240949eebdd7c673b005edbbce0aca95b8191f1d43", size = 176138577 }, + { url = "https://files.pythonhosted.org/packages/df/3d/9e7eee57b37c80cec63322c0231bb6da3cfe535a91d7a4d64896fcb89357/triton-3.6.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a17a5d5985f0ac494ed8a8e54568f092f7057ef60e1b0fa09d3fd1512064e803", size = 188273063 }, + { url = "https://files.pythonhosted.org/packages/48/db/56ee649cab5eaff4757541325aca81f52d02d4a7cd3506776cad2451e060/triton-3.6.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0b3a97e8ed304dfa9bd23bb41ca04cdf6b2e617d5e782a8653d616037a5d537d", size = 176274804 }, + { url = "https://files.pythonhosted.org/packages/f6/56/6113c23ff46c00aae423333eb58b3e60bdfe9179d542781955a5e1514cb3/triton-3.6.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:46bd1c1af4b6704e554cad2eeb3b0a6513a980d470ccfa63189737340c7746a7", size = 188397994 }, +] + [[package]] name = "typer" version = "0.24.1" @@ -2415,6 +3377,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/1a/c7/8528ac2dfa2c1e6708f647df7ae144ead13f0a31146f43c7264b4942bf12/wrapt-2.1.2-py3-none-any.whl", hash = "sha256:b8fd6fa2b2c4e7621808f8c62e8317f4aae56e59721ad933bac5239d913cf0e8", size = 43993 }, ] +[[package]] +name = "wren" +version = "0.3.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/00/ae/db2efb1907c66fcab6021fb707eb44cddb92ee31a172ad18f0ebb36f8809/wren-0.3.5.tar.gz", hash = "sha256:e0cff32ca3dabd1e7a38a2cce9f04a37cf20b7aaf13edd0e6de02204847d70be", size = 3014 } + [[package]] name = "wren-core-py" version = "0.1.0" @@ -2446,15 +3417,7 @@ dependencies = [ [package.optional-dependencies] all = [ - { name = "databricks-sdk" }, - { name = "databricks-sql-connector" }, - { name = "google-auth" }, - { name = "ibis-framework", extra = ["athena", "bigquery", "clickhouse", "mssql", "mysql", "oracle", "postgres", "snowflake", "trino"] }, - { name = "mysqlclient" }, - { name = "oracledb" }, - { name = "psycopg" }, - { name = "redshift-connector" }, - { name = "trino" }, + { name = "wren" }, ] athena = [ { name = "ibis-framework", extra = ["athena"] }, @@ -2476,6 +3439,10 @@ dev = [ { name = "ruff" }, { name = "testcontainers", extra = ["mysql"] }, ] +memory = [ + { name = "lancedb" }, + { name = "sentence-transformers" }, +] mssql = [ { name = "ibis-framework", extra = ["mssql"] }, ] @@ -2508,41 +3475,27 @@ trino = [ [package.metadata] requires-dist = [ { name = "boto3", specifier = ">=1.26" }, - { name = "databricks-sdk", marker = "extra == 'all'" }, { name = "databricks-sdk", marker = "extra == 'databricks'" }, - { name = "databricks-sql-connector", marker = "extra == 'all'" }, { name = "databricks-sql-connector", marker = "extra == 'databricks'" }, { name = "duckdb", specifier = ">=1.0" }, - { name = "google-auth", marker = "extra == 'all'" }, { name = "google-auth", marker = "extra == 'bigquery'" }, { name = "ibis-framework", specifier = ">=10" }, - { name = "ibis-framework", extras = ["athena"], marker = "extra == 'all'" }, { name = "ibis-framework", extras = ["athena"], marker = "extra == 'athena'" }, - { name = "ibis-framework", extras = ["bigquery"], marker = "extra == 'all'" }, { name = "ibis-framework", extras = ["bigquery"], marker = "extra == 'bigquery'" }, - { name = "ibis-framework", extras = ["clickhouse"], marker = "extra == 'all'" }, { name = "ibis-framework", extras = ["clickhouse"], marker = "extra == 'clickhouse'" }, - { name = "ibis-framework", extras = ["mssql"], marker = "extra == 'all'" }, { name = "ibis-framework", extras = ["mssql"], marker = "extra == 'mssql'" }, - { name = "ibis-framework", extras = ["mysql"], marker = "extra == 'all'" }, { name = "ibis-framework", extras = ["mysql"], marker = "extra == 'mysql'" }, - { name = "ibis-framework", extras = ["oracle"], marker = "extra == 'all'" }, { name = "ibis-framework", extras = ["oracle"], marker = "extra == 'oracle'" }, - { name = "ibis-framework", extras = ["postgres"], marker = "extra == 'all'" }, { name = "ibis-framework", extras = ["postgres"], marker = "extra == 'postgres'" }, - { name = "ibis-framework", extras = ["snowflake"], marker = "extra == 'all'" }, { name = "ibis-framework", extras = ["snowflake"], marker = "extra == 'snowflake'" }, - { name = "ibis-framework", extras = ["trino"], marker = "extra == 'all'" }, { name = "ibis-framework", extras = ["trino"], marker = "extra == 'trino'" }, + { name = "lancedb", marker = "extra == 'memory'", specifier = ">=0.6" }, { name = "loguru", specifier = ">=0.7" }, - { name = "mysqlclient", marker = "extra == 'all'", specifier = ">=2.2" }, { name = "mysqlclient", marker = "extra == 'mysql'", specifier = ">=2.2" }, { name = "opendal", specifier = ">=0.45" }, - { name = "oracledb", marker = "extra == 'all'" }, { name = "oracledb", marker = "extra == 'oracle'" }, { name = "orjson", marker = "extra == 'dev'", specifier = ">=3" }, { name = "pandas", specifier = ">=2" }, - { name = "psycopg", marker = "extra == 'all'", specifier = ">=3" }, { name = "psycopg", marker = "extra == 'postgres'", specifier = ">=3" }, { name = "pyarrow", specifier = ">=14" }, { name = "pyarrow-hotfix", specifier = ">=0.6" }, @@ -2551,18 +3504,18 @@ requires-dist = [ { name = "pyopenssl", specifier = ">=26.0.0" }, { name = "pyspark", marker = "extra == 'spark'", specifier = ">=3.5" }, { name = "pytest", marker = "extra == 'dev'", specifier = ">=8" }, - { name = "redshift-connector", marker = "extra == 'all'" }, { name = "redshift-connector", marker = "extra == 'redshift'" }, { name = "requests", specifier = ">=2.33.0" }, { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.4" }, + { name = "sentence-transformers", marker = "extra == 'memory'", specifier = ">=2.2" }, { name = "sqlglot", specifier = ">=27" }, { name = "testcontainers", extras = ["mysql", "postgres"], marker = "extra == 'dev'", specifier = ">=4" }, - { name = "trino", marker = "extra == 'all'", specifier = ">=0.321" }, { name = "trino", marker = "extra == 'trino'", specifier = ">=0.321" }, { name = "typer", specifier = ">=0.12" }, + { name = "wren", extras = ["athena", "bigquery", "clickhouse", "databricks", "memory", "mssql", "mysql", "oracle", "postgres", "redshift", "snowflake", "trino"], marker = "extra == 'all'" }, { name = "wren-core-py", specifier = ">=0.1" }, ] -provides-extras = ["all", "athena", "bigquery", "clickhouse", "databricks", "dev", "mssql", "mysql", "oracle", "postgres", "redshift", "snowflake", "spark", "trino"] +provides-extras = ["all", "athena", "bigquery", "clickhouse", "databricks", "dev", "memory", "mssql", "mysql", "oracle", "postgres", "redshift", "snowflake", "spark", "trino"] [[package]] name = "zstandard" From c8522a5c32bd3a9822c8c77ea84c2890a2af0f30 Mon Sep 17 00:00:00 2001 From: Jax Liu Date: Tue, 31 Mar 2026 10:09:38 +0800 Subject: [PATCH 12/16] fix(wren): catch ImportError at MemoryStore construction in tests The lancedb import happens inside MemoryStore.__init__, not at module import time. Move the try/except to wrap the full construction so CI without wren[memory] properly skips instead of erroring. Co-Authored-By: Claude Opus 4.6 (1M context) --- wren/tests/unit/test_memory.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/wren/tests/unit/test_memory.py b/wren/tests/unit/test_memory.py index b3ac0a064..2eb41557a 100644 --- a/wren/tests/unit/test_memory.py +++ b/wren/tests/unit/test_memory.py @@ -218,9 +218,10 @@ def memory_store(tmp_path): """Create a MemoryStore backed by a temp directory.""" try: from wren.memory.store import MemoryStore # noqa: PLC0415 + + return MemoryStore(path=tmp_path) except ImportError: pytest.skip("wren[memory] extras not installed") - return MemoryStore(path=tmp_path) @pytest.mark.unit @@ -319,9 +320,10 @@ def test_index_replace(self, memory_store): def wren_memory(tmp_path): try: from wren.memory import WrenMemory # noqa: PLC0415 + + return WrenMemory(path=tmp_path) except ImportError: pytest.skip("wren[memory] extras not installed") - return WrenMemory(path=tmp_path) @pytest.mark.unit From 273f42e8b9e46d8da50ee1237dd8aadcf4dbed18 Mon Sep 17 00:00:00 2001 From: Jax Liu Date: Tue, 31 Mar 2026 10:13:06 +0800 Subject: [PATCH 13/16] chore(wren): add just install-memory and test-memory scripts Convenience for local testing of the memory module with LanceDB and sentence-transformers. CI skips these tests since the extras are not installed there. Co-Authored-By: Claude Opus 4.6 (1M context) --- wren/justfile | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/wren/justfile b/wren/justfile index c3b8034d7..6026c6b98 100644 --- a/wren/justfile +++ b/wren/justfile @@ -59,6 +59,13 @@ test-mysql: test-connector marker: uv run pytest tests/connectors/ -v -m {{ marker }} +install-memory: + just build-core + uv sync --extra memory --extra dev --find-links {{ core-wheel-dir }} + +test-memory: + uv run pytest tests/unit/test_memory.py -v + lint: uv run ruff format --check src/ uv run ruff check src/ From ebd5699d92cf44cd97eec065aced217009353467 Mon Sep 17 00:00:00 2001 From: Jax Liu Date: Wed, 1 Apr 2026 11:35:20 +0800 Subject: [PATCH 14/16] fix(wren): address CodeRabbit review feedback on memory module MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix "context" → "fetch" naming in memory.md reference docs - Fix `all` extra: correct self-reference to wren-engine, add missing spark - Quote pip install extras in README for zsh compatibility - Narrow ImportError catch in cli.py to only missing optional deps - Avoid mutating input dicts during JSON serialization in memory/cli.py - Escape single quotes in LanceDB where-clause values in store.py - Add @pytest.mark.unit to TestManifestHash, TestExtractSchemaItems, TestDescribeSchema - Add regression test asserting tableReference is excluded from describe output Co-Authored-By: Claude Opus 4.6 (1M context) --- cli-skills/wren-usage/references/memory.md | 4 +- wren/README.md | 4 +- wren/pyproject.toml | 2 +- wren/src/wren/cli.py | 3 ++ wren/src/wren/memory/cli.py | 9 ++-- wren/src/wren/memory/store.py | 11 +++-- wren/tests/unit/test_memory.py | 9 ++++ wren/uv.lock | 48 ++++++++++++++++------ 8 files changed, 66 insertions(+), 24 deletions(-) diff --git a/cli-skills/wren-usage/references/memory.md b/cli-skills/wren-usage/references/memory.md index 4c3054e25..53d75654d 100644 --- a/cli-skills/wren-usage/references/memory.md +++ b/cli-skills/wren-usage/references/memory.md @@ -4,7 +4,7 @@ This reference covers the decision logic for each memory command. The main workf --- -## Schema context: `context` and `describe` +## Schema context: `fetch` and `describe` | Command | When to use | |---------|-------------| @@ -34,7 +34,7 @@ wren memory fetch -q "revenue" --threshold 50000 # raise for larger context wi **When NOT to index:** - Before every query — indexing is expensive, do it once per MDL change -- When only using `describe` or `context` with full strategy — those read the MDL directly +- When only using `describe` or `fetch` with full strategy — those read the MDL directly ```bash wren memory index --mdl ~/.wren/mdl.json diff --git a/wren/README.md b/wren/README.md index 506d4dc20..3fedddf25 100644 --- a/wren/README.md +++ b/wren/README.md @@ -10,8 +10,8 @@ Translate natural SQL queries through an MDL (Modeling Definition Language) sema pip install wren-engine[mysql] # MySQL pip install wren-engine[postgres] # PostgreSQL pip install wren-engine[duckdb] # DuckDB (local files) -pip install wren-engine[memory] # Schema & query memory (LanceDB) -pip install wren-engine[all] # All connectors + memory +pip install 'wren-engine[memory]' # Schema & query memory (LanceDB) +pip install 'wren-engine[all]' # All connectors + memory ``` ## Quick start diff --git a/wren/pyproject.toml b/wren/pyproject.toml index 6d70b2952..0c30922e9 100644 --- a/wren/pyproject.toml +++ b/wren/pyproject.toml @@ -52,7 +52,7 @@ athena = ["ibis-framework[athena]"] oracle = ["ibis-framework[oracle]", "oracledb"] memory = ["lancedb>=0.6", "sentence-transformers>=2.2"] all = [ - "wren[postgres,mysql,bigquery,snowflake,clickhouse,trino,mssql,databricks,redshift,athena,oracle,memory]", + "wren-engine[postgres,mysql,bigquery,snowflake,clickhouse,trino,mssql,databricks,redshift,athena,oracle,spark,memory]", ] dev = [ "pytest>=8", diff --git a/wren/src/wren/cli.py b/wren/src/wren/cli.py index bfabcdea6..a53cdeeeb 100644 --- a/wren/src/wren/cli.py +++ b/wren/src/wren/cli.py @@ -360,6 +360,9 @@ def _print_result(table, output: str) -> None: try: + import lancedb # noqa: PLC0415, F401 + import sentence_transformers # noqa: PLC0415, F401 + from wren.memory.cli import memory_app # noqa: PLC0415 app.add_typer(memory_app) diff --git a/wren/src/wren/memory/cli.py b/wren/src/wren/memory/cli.py index 3f15794da..2b7d0ef3e 100644 --- a/wren/src/wren/memory/cli.py +++ b/wren/src/wren/memory/cli.py @@ -81,12 +81,15 @@ def _print_results(results: list[dict], output: str) -> None: output = output.lower() if output == "json": + serializable = [] for r in results: + row = dict(r) # Convert datetime objects to ISO strings for JSON serialization - for k, v in r.items(): + for k, v in row.items(): if hasattr(v, "isoformat"): - r[k] = v.isoformat() - typer.echo(json.dumps(results, indent=2, ensure_ascii=False)) + row[k] = v.isoformat() + serializable.append(row) + typer.echo(json.dumps(serializable, indent=2, ensure_ascii=False)) else: try: import pandas as pd # noqa: PLC0415 diff --git a/wren/src/wren/memory/store.py b/wren/src/wren/memory/store.py index 2bae00ee7..8710481b8 100644 --- a/wren/src/wren/memory/store.py +++ b/wren/src/wren/memory/store.py @@ -21,6 +21,11 @@ _QUERY_TABLE = "query_history" +def _esc(value: str) -> str: + """Escape single quotes for LanceDB where-clause literals.""" + return value.replace("'", "''") + + def _schema_items_arrow_schema(dim: int = _DEFAULT_DIM) -> pa.Schema: return pa.schema( [ @@ -175,9 +180,9 @@ def _search_schema( where_parts: list[str] = [] if item_type: - where_parts.append(f"item_type = '{item_type}'") + where_parts.append(f"item_type = '{_esc(item_type)}'") if model_name: - where_parts.append(f"model_name = '{model_name}'") + where_parts.append(f"model_name = '{_esc(model_name)}'") if where_parts: q = q.where(" AND ".join(where_parts)) @@ -237,7 +242,7 @@ def recall_queries( ) if datasource: - q = q.where(f"datasource = '{datasource}'") + q = q.where(f"datasource = '{_esc(datasource)}'") results = q.limit(limit).to_list() for r in results: diff --git a/wren/tests/unit/test_memory.py b/wren/tests/unit/test_memory.py index 2eb41557a..23e311949 100644 --- a/wren/tests/unit/test_memory.py +++ b/wren/tests/unit/test_memory.py @@ -68,6 +68,7 @@ # ── manifest_hash tests ─────────────────────────────────────────────────── +@pytest.mark.unit class TestManifestHash: def test_deterministic(self): h1 = manifest_hash(_MANIFEST) @@ -83,6 +84,7 @@ def test_changes_on_modification(self): # ── extract_schema_items tests ──────────────────────────────────────────── +@pytest.mark.unit class TestExtractSchemaItems: def test_total_count(self): items = extract_schema_items(_MANIFEST) @@ -163,6 +165,7 @@ def test_manifest_without_optional_sections(self): # ── describe_schema tests ───────────────────────────────────────────────── +@pytest.mark.unit class TestDescribeSchema: def test_contains_model_names(self): text = describe_schema(_MANIFEST) @@ -199,6 +202,12 @@ def test_contains_primary_key(self): text = describe_schema(_MANIFEST) assert "Primary key: o_orderkey" in text + def test_excludes_table_reference(self): + text = describe_schema(_MANIFEST) + assert "tableReference" not in text + assert "test.public.orders" not in text + assert "test.public.customer" not in text + def test_empty_manifest(self): text = describe_schema({}) assert text == "" diff --git a/wren/uv.lock b/wren/uv.lock index d1c8408b7..1c8389a6d 100644 --- a/wren/uv.lock +++ b/wren/uv.lock @@ -3377,21 +3377,13 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/1a/c7/8528ac2dfa2c1e6708f647df7ae144ead13f0a31146f43c7264b4942bf12/wrapt-2.1.2-py3-none-any.whl", hash = "sha256:b8fd6fa2b2c4e7621808f8c62e8317f4aae56e59721ad933bac5239d913cf0e8", size = 43993 }, ] -[[package]] -name = "wren" -version = "0.3.5" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/00/ae/db2efb1907c66fcab6021fb707eb44cddb92ee31a172ad18f0ebb36f8809/wren-0.3.5.tar.gz", hash = "sha256:e0cff32ca3dabd1e7a38a2cce9f04a37cf20b7aaf13edd0e6de02204847d70be", size = 3014 } - [[package]] name = "wren-core-py" version = "0.1.0" -source = { registry = "../wren-core-py/target/wheels" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7e/89/64d5a0a5b4e6e4d9f36d0e8856e358b78f8df537d754457a143c596ade68/wren_core_py-0.1.0.tar.gz", hash = "sha256:e1f85e4a76cd753850ab9750121c85788eff85ef9497481ac8c660bf92363e80", size = 173500 } wheels = [ - { path = "wren_core_py-0.1.0-cp311-abi3-macosx_11_0_arm64.whl" }, + { url = "https://files.pythonhosted.org/packages/c8/37/02dae70f121c22cfb60dd0a0e16afd349d193e4bdca0e836e0b1952bb3c1/wren_core_py-0.1.0-cp311-abi3-macosx_11_0_arm64.whl", hash = "sha256:51624141000c008cead7b330883d39fd36d2a1ffc10e18aedcf1a938df2ced6f", size = 31035179 }, ] [[package]] @@ -3417,7 +3409,18 @@ dependencies = [ [package.optional-dependencies] all = [ - { name = "wren" }, + { name = "databricks-sdk" }, + { name = "databricks-sql-connector" }, + { name = "google-auth" }, + { name = "ibis-framework", extra = ["athena", "bigquery", "clickhouse", "mssql", "mysql", "oracle", "postgres", "snowflake", "trino"] }, + { name = "lancedb" }, + { name = "mysqlclient" }, + { name = "oracledb" }, + { name = "psycopg" }, + { name = "pyspark" }, + { name = "redshift-connector" }, + { name = "sentence-transformers" }, + { name = "trino" }, ] athena = [ { name = "ibis-framework", extra = ["athena"] }, @@ -3475,44 +3478,63 @@ trino = [ [package.metadata] requires-dist = [ { name = "boto3", specifier = ">=1.26" }, + { name = "databricks-sdk", marker = "extra == 'all'" }, { name = "databricks-sdk", marker = "extra == 'databricks'" }, + { name = "databricks-sql-connector", marker = "extra == 'all'" }, { name = "databricks-sql-connector", marker = "extra == 'databricks'" }, { name = "duckdb", specifier = ">=1.0" }, + { name = "google-auth", marker = "extra == 'all'" }, { name = "google-auth", marker = "extra == 'bigquery'" }, { name = "ibis-framework", specifier = ">=10" }, + { name = "ibis-framework", extras = ["athena"], marker = "extra == 'all'" }, { name = "ibis-framework", extras = ["athena"], marker = "extra == 'athena'" }, + { name = "ibis-framework", extras = ["bigquery"], marker = "extra == 'all'" }, { name = "ibis-framework", extras = ["bigquery"], marker = "extra == 'bigquery'" }, + { name = "ibis-framework", extras = ["clickhouse"], marker = "extra == 'all'" }, { name = "ibis-framework", extras = ["clickhouse"], marker = "extra == 'clickhouse'" }, + { name = "ibis-framework", extras = ["mssql"], marker = "extra == 'all'" }, { name = "ibis-framework", extras = ["mssql"], marker = "extra == 'mssql'" }, + { name = "ibis-framework", extras = ["mysql"], marker = "extra == 'all'" }, { name = "ibis-framework", extras = ["mysql"], marker = "extra == 'mysql'" }, + { name = "ibis-framework", extras = ["oracle"], marker = "extra == 'all'" }, { name = "ibis-framework", extras = ["oracle"], marker = "extra == 'oracle'" }, + { name = "ibis-framework", extras = ["postgres"], marker = "extra == 'all'" }, { name = "ibis-framework", extras = ["postgres"], marker = "extra == 'postgres'" }, + { name = "ibis-framework", extras = ["snowflake"], marker = "extra == 'all'" }, { name = "ibis-framework", extras = ["snowflake"], marker = "extra == 'snowflake'" }, + { name = "ibis-framework", extras = ["trino"], marker = "extra == 'all'" }, { name = "ibis-framework", extras = ["trino"], marker = "extra == 'trino'" }, + { name = "lancedb", marker = "extra == 'all'", specifier = ">=0.6" }, { name = "lancedb", marker = "extra == 'memory'", specifier = ">=0.6" }, { name = "loguru", specifier = ">=0.7" }, + { name = "mysqlclient", marker = "extra == 'all'", specifier = ">=2.2" }, { name = "mysqlclient", marker = "extra == 'mysql'", specifier = ">=2.2" }, { name = "opendal", specifier = ">=0.45" }, + { name = "oracledb", marker = "extra == 'all'" }, { name = "oracledb", marker = "extra == 'oracle'" }, { name = "orjson", marker = "extra == 'dev'", specifier = ">=3" }, { name = "pandas", specifier = ">=2" }, + { name = "psycopg", marker = "extra == 'all'", specifier = ">=3" }, { name = "psycopg", marker = "extra == 'postgres'", specifier = ">=3" }, { name = "pyarrow", specifier = ">=14" }, { name = "pyarrow-hotfix", specifier = ">=0.6" }, { name = "pyasn1", specifier = ">=0.6.3" }, { name = "pydantic", specifier = ">=2" }, { name = "pyopenssl", specifier = ">=26.0.0" }, + { name = "pyspark", marker = "extra == 'all'", specifier = ">=3.5" }, { name = "pyspark", marker = "extra == 'spark'", specifier = ">=3.5" }, { name = "pytest", marker = "extra == 'dev'", specifier = ">=8" }, + { name = "redshift-connector", marker = "extra == 'all'" }, { name = "redshift-connector", marker = "extra == 'redshift'" }, { name = "requests", specifier = ">=2.33.0" }, { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.4" }, + { name = "sentence-transformers", marker = "extra == 'all'", specifier = ">=2.2" }, { name = "sentence-transformers", marker = "extra == 'memory'", specifier = ">=2.2" }, { name = "sqlglot", specifier = ">=27" }, { name = "testcontainers", extras = ["mysql", "postgres"], marker = "extra == 'dev'", specifier = ">=4" }, + { name = "trino", marker = "extra == 'all'", specifier = ">=0.321" }, { name = "trino", marker = "extra == 'trino'", specifier = ">=0.321" }, { name = "typer", specifier = ">=0.12" }, - { name = "wren", extras = ["athena", "bigquery", "clickhouse", "databricks", "memory", "mssql", "mysql", "oracle", "postgres", "redshift", "snowflake", "trino"], marker = "extra == 'all'" }, { name = "wren-core-py", specifier = ">=0.1" }, ] provides-extras = ["all", "athena", "bigquery", "clickhouse", "databricks", "dev", "memory", "mssql", "mysql", "oracle", "postgres", "redshift", "snowflake", "spark", "trino"] From 5da354c5f9b8310ef6a240f2c02951ef2ca3d237 Mon Sep 17 00:00:00 2001 From: Jax Liu Date: Wed, 1 Apr 2026 11:59:30 +0800 Subject: [PATCH 15/16] fix(wren): address remaining CodeRabbit review feedback - Fix index_schema replace logic: replace=False now appends instead of overwriting; empty items with replace=True drops stale table - Fix embedding dimension mismatch: derive actual dim from model probe at init instead of hardcoding _DEFAULT_DIM in Arrow schemas - Use pytest.importorskip in test fixtures to only skip on genuinely missing extras; internal ImportErrors now surface as failures - Reject unsupported --output format values in memory CLI Co-Authored-By: Claude Opus 4.6 (1M context) --- wren/src/wren/memory/cli.py | 6 +++++ wren/src/wren/memory/store.py | 41 ++++++++++++++++++++++++++-------- wren/tests/unit/test_memory.py | 21 ++++++++--------- 3 files changed, 49 insertions(+), 19 deletions(-) diff --git a/wren/src/wren/memory/cli.py b/wren/src/wren/memory/cli.py index 2b7d0ef3e..97603da81 100644 --- a/wren/src/wren/memory/cli.py +++ b/wren/src/wren/memory/cli.py @@ -80,6 +80,12 @@ def _print_results(results: list[dict], output: str) -> None: return output = output.lower() + if output not in {"json", "table"}: + typer.echo( + f"Error: unsupported output format '{output}'. Use json or table.", + err=True, + ) + raise typer.Exit(1) if output == "json": serializable = [] for r in results: diff --git a/wren/src/wren/memory/store.py b/wren/src/wren/memory/store.py index 8710481b8..f1f362970 100644 --- a/wren/src/wren/memory/store.py +++ b/wren/src/wren/memory/store.py @@ -88,6 +88,15 @@ def __init__( self._path = resolved self._db = lancedb.connect(str(resolved)) self._embed_fn = get_embedding_function(model_name or _DEFAULT_MODEL) + # Derive actual vector dimension from the model so custom models work. + probe = self._embed_fn.compute_source_embeddings(["probe"]) + self._dim = len(probe[0]) + + def _schema_table_schema(self) -> pa.Schema: + return _schema_items_arrow_schema(dim=self._dim) + + def _query_table_schema(self) -> pa.Schema: + return _query_history_arrow_schema(dim=self._dim) # ── Schema indexing ─────────────────────────────────────────────────── @@ -97,7 +106,11 @@ def index_schema(self, manifest: dict, *, replace: bool = True) -> int: Returns the number of records indexed. """ items = extract_schema_items(manifest) + table_exists = _SCHEMA_TABLE in _table_names(self._db) + if not items: + if replace and table_exists: + self._db.drop_table(_SCHEMA_TABLE) return 0 texts = [item["text"] for item in items] @@ -106,15 +119,25 @@ def index_schema(self, manifest: dict, *, replace: bool = True) -> int: for item, vec in zip(items, vectors): item["vector"] = vec - if replace and _SCHEMA_TABLE in _table_names(self._db): - self._db.drop_table(_SCHEMA_TABLE) + if replace: + if table_exists: + self._db.drop_table(_SCHEMA_TABLE) + self._db.create_table( + _SCHEMA_TABLE, + items, + schema=self._schema_table_schema(), + ) + else: + if table_exists: + tbl = self._db.open_table(_SCHEMA_TABLE) + tbl.add(items) + else: + self._db.create_table( + _SCHEMA_TABLE, + items, + schema=self._schema_table_schema(), + ) - self._db.create_table( - _SCHEMA_TABLE, - items, - schema=_schema_items_arrow_schema(), - mode="overwrite", - ) return len(items) def schema_is_current(self, manifest: dict) -> bool: @@ -222,7 +245,7 @@ def store_query( self._db.create_table( _QUERY_TABLE, [record], - schema=_query_history_arrow_schema(), + schema=self._query_table_schema(), ) def recall_queries( diff --git a/wren/tests/unit/test_memory.py b/wren/tests/unit/test_memory.py index 23e311949..c4b288f76 100644 --- a/wren/tests/unit/test_memory.py +++ b/wren/tests/unit/test_memory.py @@ -225,12 +225,12 @@ def test_return_type_is_string(self): @pytest.fixture def memory_store(tmp_path): """Create a MemoryStore backed by a temp directory.""" - try: - from wren.memory.store import MemoryStore # noqa: PLC0415 + pytest.importorskip("lancedb", reason="wren[memory] extras not installed") + pytest.importorskip("sentence_transformers", reason="wren[memory] extras not installed") - return MemoryStore(path=tmp_path) - except ImportError: - pytest.skip("wren[memory] extras not installed") + from wren.memory.store import MemoryStore # noqa: PLC0415 + + return MemoryStore(path=tmp_path) @pytest.mark.unit @@ -327,12 +327,13 @@ def test_index_replace(self, memory_store): @pytest.fixture def wren_memory(tmp_path): - try: - from wren.memory import WrenMemory # noqa: PLC0415 + """Create a WrenMemory instance backed by a temp directory.""" + pytest.importorskip("lancedb", reason="wren[memory] extras not installed") + pytest.importorskip("sentence_transformers", reason="wren[memory] extras not installed") + + from wren.memory import WrenMemory # noqa: PLC0415 - return WrenMemory(path=tmp_path) - except ImportError: - pytest.skip("wren[memory] extras not installed") + return WrenMemory(path=tmp_path) @pytest.mark.unit From 886aa7e27d62dbc8e0f54a91657a7eb80a33dda8 Mon Sep 17 00:00:00 2001 From: Jax Liu Date: Wed, 1 Apr 2026 12:18:20 +0800 Subject: [PATCH 16/16] fix(wren): address third round of CodeRabbit review feedback - Fix fetch --output json: emit valid JSON for both full and search strategies instead of mixing plain text with JSON - Fix _get_store: move MemoryStore construction inside try block and narrow catch to ModuleNotFoundError with name guard - Scope reset() to only drop schema_items and query_history tables instead of all tables in the LanceDB directory - Scope _search_schema by mdl_hash so replace=False with multiple manifests returns only rows from the current manifest - Change schema_is_current to require ALL rows match current hash (was .any(), now .all()) to detect stale mixed-manifest state Co-Authored-By: Claude Opus 4.6 (1M context) --- wren/src/wren/memory/cli.py | 23 +++++++++++++++++++++-- wren/src/wren/memory/store.py | 28 ++++++++++++++++++++++------ 2 files changed, 43 insertions(+), 8 deletions(-) diff --git a/wren/src/wren/memory/cli.py b/wren/src/wren/memory/cli.py index 97603da81..c7e802a78 100644 --- a/wren/src/wren/memory/cli.py +++ b/wren/src/wren/memory/cli.py @@ -63,14 +63,21 @@ def _get_store(path: str | None): """Lazy-import and construct a MemoryStore.""" try: from wren.memory.store import MemoryStore # noqa: PLC0415 - except ImportError: + + return MemoryStore(path=path) + except ModuleNotFoundError as e: + if (e.name or "").split(".")[0] not in { + "lancedb", + "sentence_transformers", + "pyarrow", + }: + raise typer.echo( "Error: wren[memory] extras not installed. " "Run: pip install 'wren-engine[memory]'", err=True, ) raise typer.Exit(1) - return MemoryStore(path=path) def _print_results(results: list[dict], output: str) -> None: @@ -176,6 +183,18 @@ def fetch( kwargs["threshold"] = threshold result = store.get_context(manifest, query, **kwargs) strategy = result["strategy"] + if output.lower() == "json": + payload = dict(result) + if "results" in payload: + payload["results"] = [ + { + k: (v.isoformat() if hasattr(v, "isoformat") else v) + for k, v in row.items() + } + for row in payload["results"] + ] + typer.echo(json.dumps(payload, indent=2, ensure_ascii=False)) + return typer.echo(f"Strategy: {strategy}") if strategy == "full": typer.echo(result["schema"]) diff --git a/wren/src/wren/memory/store.py b/wren/src/wren/memory/store.py index f1f362970..a2fb6a192 100644 --- a/wren/src/wren/memory/store.py +++ b/wren/src/wren/memory/store.py @@ -141,13 +141,20 @@ def index_schema(self, manifest: dict, *, replace: bool = True) -> int: return len(items) def schema_is_current(self, manifest: dict) -> bool: - """Check whether the indexed schema matches *manifest*.""" + """Check whether the indexed schema matches *manifest*. + + Returns ``True`` only when every row in the schema table carries + the current manifest hash (i.e. no stale rows from a previous + manifest remain). + """ if _SCHEMA_TABLE not in _table_names(self._db): return False table = self._db.open_table(_SCHEMA_TABLE) + if table.count_rows() == 0: + return False current_hash = manifest_hash(manifest) df = table.to_pandas() - return bool((df["mdl_hash"] == current_hash).any()) + return bool((df["mdl_hash"] == current_hash).all()) # ── Plain-text / hybrid ──────────────────────────────────────────────── @@ -179,8 +186,13 @@ def get_context( if len(text) <= threshold: return {"strategy": "full", "schema": text} + mdl_hash_val = manifest_hash(manifest) results = self._search_schema( - query, limit=limit, item_type=item_type, model_name=model_name + query, + limit=limit, + item_type=item_type, + model_name=model_name, + mdl_hash=mdl_hash_val, ) return {"strategy": "search", "results": results} @@ -191,6 +203,7 @@ def _search_schema( limit: int = 5, item_type: str | None = None, model_name: str | None = None, + mdl_hash: str | None = None, ) -> list[dict]: """Embedding search over indexed schema items (internal).""" if _SCHEMA_TABLE not in _table_names(self._db): @@ -202,6 +215,8 @@ def _search_schema( ) where_parts: list[str] = [] + if mdl_hash: + where_parts.append(f"mdl_hash = '{_esc(mdl_hash)}'") if item_type: where_parts.append(f"item_type = '{_esc(item_type)}'") if model_name: @@ -283,6 +298,7 @@ def status(self) -> dict: return info def reset(self) -> None: - """Drop all tables.""" - for name in list(_table_names(self._db)): - self._db.drop_table(name) + """Drop Wren memory tables.""" + for name in (_SCHEMA_TABLE, _QUERY_TABLE): + if name in _table_names(self._db): + self._db.drop_table(name)