diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index 900abe8..c316f52 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -8,7 +8,7 @@ on: - 'properdocs.yml' - 'pyproject.toml' - 'uv.lock' - - 'lpgcast/**' + - 'schemacast/**' # A pull request builds the site strictly but does not deploy it. pull_request: paths: @@ -16,7 +16,7 @@ on: - 'properdocs.yml' - 'pyproject.toml' - 'uv.lock' - - 'lpgcast/**' + - 'schemacast/**' workflow_dispatch: permissions: diff --git a/.github/workflows/pypi-publish.yml b/.github/workflows/pypi-publish.yml index 143c7ab..173a238 100644 --- a/.github/workflows/pypi-publish.yml +++ b/.github/workflows/pypi-publish.yml @@ -9,13 +9,13 @@ permissions: jobs: pypi-publish: - # Off until the `lpgcast` name is reserved on PyPI: set the repository + # Off until the `schemacast` name is reserved on PyPI: set the repository # variable PYPI_PUBLISH to `true` to turn publishing on. if: vars.PYPI_PUBLISH == 'true' runs-on: ubuntu-latest environment: name: pypi - url: https://pypi.org/p/lpgcast + url: https://pypi.org/p/schemacast permissions: id-token: write diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 4b5a823..e6b0ae6 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -4,6 +4,10 @@ on: pull_request: push: branches: [main] + schedule: + # Daily: catches a new graflo release that breaks this package. + - cron: '35 5 * * *' + workflow_dispatch: concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -35,7 +39,7 @@ jobs: run: | /tmp/base-install/bin/python -c " import sys - from lpgcast import LPGCaster, load_schema, read_episodes, write_cast + from schemacast import SchemaCaster, load_schema, read_episodes, write_cast forbidden = ['langchain', 'langgraph', 'torch', 'numpy', 'pandas', 'rdflib'] leaked = [name for name in forbidden if name in sys.modules] assert not leaked, f'imported on a plain install: {leaked}' @@ -43,9 +47,9 @@ jobs: " - name: Console scripts resolve run: | - /tmp/base-install/bin/lpgcast --help - /tmp/base-install/bin/lpgcast-bench --help - /tmp/base-install/bin/lpgcast sheet --schema examples/tickets/schema.yaml > /dev/null + /tmp/base-install/bin/schemacast --help + /tmp/base-install/bin/schemacast-bench --help + /tmp/base-install/bin/schemacast sheet --schema examples/tickets/schema.yaml > /dev/null unit: runs-on: ubuntu-latest @@ -67,10 +71,46 @@ jobs: run: uv sync --no-sources --group dev - name: Type check if: matrix.python-version == '3.12' - run: uv run --no-sources ty check lpgcast test + run: uv run --no-sources ty check schemacast test # No model is called: tests use a scripted endpoint, and the `llm` and # `neo4j` markers are skipped without --run-llm / --run-neo4j. - name: Run tests run: uv run --no-sources pytest env: PYTHONUNBUFFERED: '1' + + # Both ends of the declared graflo range: the floor it promises to support + # and the newest release it admits. Not on PRs. + graflo-range: + if: github.event_name != 'pull_request' + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + graflo: [floor, latest] + env: + UV_PYTHON: '3.12' + steps: + - uses: actions/checkout@v7 + - name: Install uv + uses: astral-sh/setup-uv@v10.2.0 + with: + version: "0.12.21" + enable-cache: true + - name: Install dependencies + run: uv sync --no-sources --group dev + - name: Select graflo + run: | + range=$(sed -nE 's/^ *"(graflo>=[^"]+)",?$/\1/p' pyproject.toml) + floor=$(sed -nE 's/^ *"graflo>=([0-9.]+).*/\1/p' pyproject.toml) + test -n "$range" && test -n "$floor" + if [ "${{ matrix.graflo }}" = floor ]; then + uv pip install --python .venv "graflo==$floor" + else + uv pip install --python .venv --upgrade-package graflo "$range" + fi + uv run --no-sync python -c "import importlib.metadata as m; print('graflo', m.version('graflo'))" + - name: Run tests + run: uv run --no-sync pytest + env: + PYTHONUNBUFFERED: '1' diff --git a/.gitignore b/.gitignore index 0d67821..e9524c1 100644 --- a/.gitignore +++ b/.gitignore @@ -216,7 +216,7 @@ __marimo__/ # Streamlit .streamlit/secrets.toml -# Response cache written by tests run without LPGCAST_CACHE_DIR +# Response cache written by tests run without SCHEMACAST_CACHE_DIR .test_cache/ # Benchmark reports; results are published elsewhere, never committed diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3081cc3..41a3eb5 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -8,10 +8,10 @@ repos: - id: check-added-large-files args: [--maxkb=2000] - id: end-of-file-fixer - exclude: (^|/)(\.env|data/)|^lpgcast/plot/fonts/|\.svg$ + exclude: (^|/)(\.env|data/)|^schemacast/plot/fonts/|\.svg$ - id: trailing-whitespace args: [--markdown-linebreak-ext=md] - exclude: (^|/)(\.env|data/)|^lpgcast/plot/fonts/|\.svg$ + exclude: (^|/)(\.env|data/)|^schemacast/plot/fonts/|\.svg$ - id: pretty-format-json args: [--autofix, --indent, '4', --no-sort-keys] @@ -28,7 +28,7 @@ repos: # Type checking - id: ty name: ty check - entry: uv run --frozen ty check lpgcast test + entry: uv run --frozen ty check schemacast test language: system types: [python] pass_filenames: false diff --git a/CHANGELOG.md b/CHANGELOG.md index d932e48..9079fee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- **Renamed from `lpgcast` to `schemacast`**: the distribution, the import package, the CLI + (`schemacast`, `schemacast-bench`), `SchemaCaster` and `SchemaCastSettings`, the + `SCHEMACAST_` environment prefixes and the default cache directory. The `text2lpg` alias is gone. + `serve` reports `schemacast_version`; groundings carry `extractor: schemacast/`. + +## [0.2.0] - 2026-10-13 + ### Added - **`lpgcast-bench compare A.json B.json`**: run B read against run A episode by episode, with diff --git a/README.md b/README.md index fe88153..9d481a0 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,20 @@ -# LPGCast LPGCast logo +# SchemaCast SchemaCast logo **Short texts in, a labeled property graph out, typed by a GraFlo schema you govern.** -[![tests](https://img.shields.io/github/actions/workflow/status/growgraph/LPGCast/tests.yml?branch=main&label=tests)](https://github.com/growgraph/LPGCast/actions/workflows/tests.yml) -[![pre-commit](https://img.shields.io/github/actions/workflow/status/growgraph/LPGCast/pre-commit.yml?branch=main&label=pre-commit)](https://github.com/growgraph/LPGCast/actions/workflows/pre-commit.yml) -[![PyPI](https://img.shields.io/pypi/v/lpgcast?color=224777)](https://pypi.org/project/lpgcast/) -[![Python](https://img.shields.io/pypi/pyversions/lpgcast?color=224777)](https://pypi.org/project/lpgcast/) -[![Docs](https://img.shields.io/badge/docs-growgraph.github.io-224777)](https://growgraph.github.io/LPGCast/) -[![License](https://img.shields.io/badge/license-Apache--2.0-224777)](https://github.com/growgraph/LPGCast/blob/main/LICENSE) +[![tests](https://img.shields.io/github/actions/workflow/status/growgraph/SchemaCast/tests.yml?branch=main&label=tests)](https://github.com/growgraph/SchemaCast/actions/workflows/tests.yml) +[![pre-commit](https://img.shields.io/github/actions/workflow/status/growgraph/SchemaCast/pre-commit.yml?branch=main&label=pre-commit)](https://github.com/growgraph/SchemaCast/actions/workflows/pre-commit.yml) +[![PyPI](https://img.shields.io/pypi/v/schemacast?color=224777)](https://pypi.org/project/schemacast/) +[![Python](https://img.shields.io/pypi/pyversions/schemacast?color=224777)](https://pypi.org/project/schemacast/) +[![Docs](https://img.shields.io/badge/docs-growgraph.github.io-224777)](https://growgraph.github.io/SchemaCast/) +[![License](https://img.shields.io/badge/license-Apache--2.0-224777)](https://github.com/growgraph/SchemaCast/blob/main/LICENSE) -LPGCast reads messages, tickets and notes, and writes what they state as vertices and edges of a +SchemaCast reads messages, tickets and notes, and writes what they state as vertices and edges of a [GraFlo](https://github.com/growgraph/graflo) schema. Each text gets one model call, checked against the schema; what fails is corrected at most twice, then quarantined. Every vertex, edge and property keeps the quote it was read from. -OntoCast builds and evolves an ontology from documents and writes RDF; LPGCast casts short, +OntoCast builds and evolves an ontology from documents and writes RDF; SchemaCast casts short, frequent texts into a property-graph schema you already govern. **Status: alpha.** The API may change between minor releases. @@ -22,27 +22,27 @@ frequent texts into a property-graph schema you already govern. ## Quick start ```bash -uv add lpgcast # or: pip install lpgcast +uv add schemacast # or: pip install schemacast ``` Point it at any OpenAI-compatible endpoint: ```bash # OpenAI (the default model is gpt-5.6-luna) -export LPGCAST_LLM_API_KEY=sk-... +export SCHEMACAST_LLM_API_KEY=sk-... # or Ollama -export LPGCAST_LLM_BASE_URL=http://localhost:11434/v1 -export LPGCAST_LLM_MODEL=qwen3:8b -export LPGCAST_LLM_API_KEY=local +export SCHEMACAST_LLM_BASE_URL=http://localhost:11434/v1 +export SCHEMACAST_LLM_MODEL=qwen3:8b +export SCHEMACAST_LLM_API_KEY=local ``` -Then, from a clone of the repository, with the example in [`examples/tickets/`](https://github.com/growgraph/LPGCast/tree/main/examples/tickets): +Then, from a clone of the repository, with the example in [`examples/tickets/`](https://github.com/growgraph/SchemaCast/tree/main/examples/tickets): ```bash -lpgcast sheet --schema examples/tickets/schema.yaml # what the model is shown; no call -lpgcast select --schema examples/tickets/schema.yaml --episodes examples/tickets/episodes.jsonl -lpgcast cast --schema examples/tickets/schema.yaml --episodes examples/tickets/episodes.jsonl \ +schemacast sheet --schema examples/tickets/schema.yaml # what the model is shown; no call +schemacast select --schema examples/tickets/schema.yaml --episodes examples/tickets/episodes.jsonl +schemacast cast --schema examples/tickets/schema.yaml --episodes examples/tickets/episodes.jsonl \ --out out/tickets [--db examples/connections/neo4j.yaml] [--replay] ``` @@ -56,11 +56,11 @@ From Python: ```python import asyncio -from lpgcast import LPGCaster, load_schema, read_episodes +from schemacast import SchemaCaster, load_schema, read_episodes schema = load_schema("examples/tickets/schema.yaml") result = asyncio.run( - LPGCaster(schema).cast(read_episodes("examples/tickets/episodes.jsonl")) + SchemaCaster(schema).cast(read_episodes("examples/tickets/episodes.jsonl")) ) ``` @@ -71,18 +71,18 @@ cast twice changes nothing in a database that upserts. ## Benchmark -`lpgcast-bench` casts a bundled corpus of texts whose graphs are known and scores the result +`schemacast-bench` casts a bundled corpus of texts whose graphs are known and scores the result exactly, with no model as judge: conformance, hallucination, precision and recall, direction, spans, mention versus mint, injection, selection and re-cast stability, with tokens and cost. ## Not yet **Registry mode** is planned: selection across the schemas of a GraFlo schema registry, and an -HTTP service. Today LPGCast works on one schema file. +HTTP service. Today SchemaCast works on one schema file. ## Documentation -[growgraph.github.io/LPGCast](https://growgraph.github.io/LPGCast/): installation, how a cast +[growgraph.github.io/SchemaCast](https://growgraph.github.io/SchemaCast/): installation, how a cast works, identities and mentions, preparing a schema, replay, writing to a database, the benchmark, and the reference. diff --git a/docs/_build/gen_cli.py b/docs/_build/gen_cli.py index 5107af7..cf2de2c 100644 --- a/docs/_build/gen_cli.py +++ b/docs/_build/gen_cli.py @@ -1,14 +1,14 @@ """Generate ``reference/cli.md`` from the click commands at build time. One section per command, its help and a table of its options, read from the -command objects themselves, so the page cannot drift from ``lpgcast/cli.py``. +command objects themselves, so the page cannot drift from ``schemacast/cli.py``. """ import click import mkdocs_gen_files -from lpgcast.bench.cli import main as bench -from lpgcast.cli import main as lpgcast +from schemacast.bench.cli import main as bench +from schemacast.cli import main as schemacast def _cell(text: str) -> str: @@ -49,17 +49,17 @@ def _section(command: click.Command, invocation: str) -> list[str]: lines = [ "# Command line", "", - "`lpgcast` (alias `text2lpg`) casts text; `lpgcast-bench` runs the", + "`schemacast` casts text; `schemacast-bench` runs the", "[benchmark](../guides/benchmark.md). Model settings come from the", "[environment](settings.md).", "", ] -assert isinstance(lpgcast, click.Group) -for name, command in lpgcast.commands.items(): - lines += _section(command, f"lpgcast {name}") +assert isinstance(schemacast, click.Group) +for name, command in schemacast.commands.items(): + lines += _section(command, f"schemacast {name}") assert isinstance(bench, click.Group) for name, command in bench.commands.items(): - invocation = "lpgcast-bench" if name == "run" else f"lpgcast-bench {name}" + invocation = "schemacast-bench" if name == "run" else f"schemacast-bench {name}" lines += _section(command, invocation) with mkdocs_gen_files.open("reference/cli.md", "w") as f: diff --git a/docs/_build/gen_pages.py b/docs/_build/gen_pages.py index 0e4be3f..3e10d65 100644 --- a/docs/_build/gen_pages.py +++ b/docs/_build/gen_pages.py @@ -1,6 +1,6 @@ """Generate the Python API reference at build time. -Walks ``lpgcast/**/*.py`` and writes one ``mkdocstrings`` page per public +Walks ``schemacast/**/*.py`` and writes one ``mkdocstrings`` page per public module under ``reference/python/``, into the virtual docs directory that ``mkdocs-gen-files`` feeds the build. Private modules (``_*.py``) are skipped. @@ -11,14 +11,14 @@ A hand-written overview at ``reference/python//index.md`` replaces the generated ``reference/python/.md`` (both build to the same URL), so a package whose overview exists on disk is skipped here; it must carry -``::: lpgcast.`` itself. The nav is inferred by ``literate-nav``. +``::: schemacast.`` itself. The nav is inferred by ``literate-nav``. """ from pathlib import Path import mkdocs_gen_files -PACKAGE = "lpgcast" +PACKAGE = "schemacast" REFERENCE = Path("docs/reference/python") diff --git a/docs/_build/gen_settings.py b/docs/_build/gen_settings.py index fd2874d..beda9dd 100644 --- a/docs/_build/gen_settings.py +++ b/docs/_build/gen_settings.py @@ -2,7 +2,7 @@ One table per settings class, one row per field: the environment variable, its default and its description. Generated, never written to disk, so it cannot -drift from ``lpgcast/config.py``. +drift from ``schemacast/config.py``. """ from typing import Any @@ -10,13 +10,13 @@ import mkdocs_gen_files from pydantic_settings import BaseSettings -from lpgcast.config import CacheSettings, LLMSettings, LPGCastSettings, ServeSettings +from schemacast.config import CacheSettings, LLMSettings, SchemaCastSettings, ServeSettings SECTIONS: list[tuple[str, type[BaseSettings], str]] = [ - ("Model", LLMSettings, "LPGCAST_LLM_"), - ("Response cache", CacheSettings, "LPGCAST_CACHE_"), - ("Cast", LPGCastSettings, "LPGCAST_"), - ("Service", ServeSettings, "LPGCAST_SERVE_"), + ("Model", LLMSettings, "SCHEMACAST_LLM_"), + ("Response cache", CacheSettings, "SCHEMACAST_CACHE_"), + ("Cast", SchemaCastSettings, "SCHEMACAST_"), + ("Service", ServeSettings, "SCHEMACAST_SERVE_"), ] @@ -36,7 +36,7 @@ def _default(value: Any) -> str: "# Settings", "", "Settings are read from environment variables only; no file is loaded. In", - "Python, pass `LPGCastSettings(...)` to `LPGCaster` instead.", + "Python, pass `SchemaCastSettings(...)` to `SchemaCaster` instead.", "", ] for title, model, prefix in SECTIONS: diff --git a/docs/concepts/context.md b/docs/concepts/context.md index d254d5d..755989d 100644 --- a/docs/concepts/context.md +++ b/docs/concepts/context.md @@ -18,5 +18,5 @@ set to the context item's `source_id`, and its offsets point into that item's text. Any other quote found only in context is a `quote_not_found` finding. Context is shown to the model as data, like the text, and counts towards -`LPGCAST_MAX_EPISODE_TOKENS`. LPGCast does not assemble it: the caller chooses +`SCHEMACAST_MAX_EPISODE_TOKENS`. SchemaCast does not assemble it: the caller chooses the earlier texts, and should keep them few and recent. diff --git a/docs/concepts/groundings.md b/docs/concepts/groundings.md index 3d5aba3..3f9d6b9 100644 --- a/docs/concepts/groundings.md +++ b/docs/concepts/groundings.md @@ -15,10 +15,10 @@ graph, not into it. | `property` | for a property: its name | | `char_start`, `char_end`, `quote` | the span of the text, and its characters | | `in_context` | set when the span is in a [context](context.md) item: its `source_id`; offsets then point into that item's text | -| `extractor`, `model`, `schema_ref` | what produced it: the LPGCast version, the model, and `name@version` of the schema | +| `extractor`, `model`, `schema_ref` | what produced it: the SchemaCast version, the model, and `name@version` of the schema | | `reference_time` | the episode's time | -The model gives quotes, never offsets; LPGCast finds each quote in the text, +The model gives quotes, never offsets; SchemaCast finds each quote in the text, exactly first, then ignoring case, accents, runs of whitespace and typographic quotes and dashes. When a quote occurs more than once, a property takes the occurrence nearest its vertex's or edge's quote. A quote it cannot find is a diff --git a/docs/concepts/identities.md b/docs/concepts/identities.md index 3a33f20..a954bcb 100644 --- a/docs/concepts/identities.md +++ b/docs/concepts/identities.md @@ -1,6 +1,6 @@ # Identities and mentions -LPGCast computes every identity before it builds an edge, the same way graflo +SchemaCast computes every identity before it builds an edge, the same way graflo does for any other source. How depends on how the vertex type is identified: | Identity of the type | Where its key comes from | diff --git a/docs/concepts/index.md b/docs/concepts/index.md index 3e6fd46..220fe7c 100644 --- a/docs/concepts/index.md +++ b/docs/concepts/index.md @@ -20,7 +20,7 @@ through the same steps, and episodes run concurrently. One call per episode, at most two repairs, and nothing more: no critic pass, no chunking, no encoder and no vector store. An episode longer than -`LPGCAST_MAX_EPISODE_TOKENS` is refused before any call, not split. +`SCHEMACAST_MAX_EPISODE_TOKENS` is refused before any call, not split. ## The checks @@ -53,5 +53,5 @@ every other episode would fail the same way. ## Beside OntoCast -OntoCast builds and evolves an ontology from documents and writes RDF; LPGCast +OntoCast builds and evolves an ontology from documents and writes RDF; SchemaCast casts short, frequent texts into a property-graph schema you already govern. diff --git a/docs/getting_started/installation.md b/docs/getting_started/installation.md index b59a9c2..951a63b 100644 --- a/docs/getting_started/installation.md +++ b/docs/getting_started/installation.md @@ -1,16 +1,16 @@ # Installation -LPGCast needs Python 3.11 or later and graflo 1.17 or later. +SchemaCast needs Python 3.11 or later and graflo 1.17 or later. ```bash -uv add lpgcast # or: pip install lpgcast +uv add schemacast # or: pip install schemacast ``` -To work on LPGCast or run the examples, install from a clone: +To work on SchemaCast or run the examples, install from a clone: ```bash -git clone https://github.com/growgraph/LPGCast -cd LPGCast +git clone https://github.com/growgraph/SchemaCast +cd SchemaCast uv sync --group dev ``` @@ -22,8 +22,8 @@ and no vector store; a test keeps it that way. | Command | What it is | |---|---| -| `lpgcast` (alias `text2lpg`) | `sheet`, `select` and `cast`; see [Command line](../reference/cli.md) | -| `lpgcast-bench` | the [benchmark](../guides/benchmark.md) | +| `schemacast` | `sheet`, `select` and `cast`; see [Command line](../reference/cli.md) | +| `schemacast-bench` | the [benchmark](../guides/benchmark.md) | ## Pointing it at a model @@ -33,25 +33,25 @@ come from environment variables only; no file is read. === "OpenAI" ```bash - export LPGCAST_LLM_API_KEY=sk-... - # LPGCAST_LLM_MODEL defaults to gpt-5.6-luna + export SCHEMACAST_LLM_API_KEY=sk-... + # SCHEMACAST_LLM_MODEL defaults to gpt-5.6-luna ``` === "Ollama" ```bash - export LPGCAST_LLM_BASE_URL=http://localhost:11434/v1 - export LPGCAST_LLM_MODEL=qwen3:8b - export LPGCAST_LLM_API_KEY=local # any value; without a key only cached replies are served + export SCHEMACAST_LLM_BASE_URL=http://localhost:11434/v1 + export SCHEMACAST_LLM_MODEL=qwen3:8b + export SCHEMACAST_LLM_API_KEY=local # any value; without a key only cached replies are served ``` === "vLLM" ```bash - export LPGCAST_LLM_BASE_URL=http://localhost:8000/v1 - export LPGCAST_LLM_MODEL= - export LPGCAST_LLM_API_KEY=local + export SCHEMACAST_LLM_BASE_URL=http://localhost:8000/v1 + export SCHEMACAST_LLM_MODEL= + export SCHEMACAST_LLM_API_KEY=local ``` -Without an API key LPGCast makes no request at all, and serves only replies it +Without an API key SchemaCast makes no request at all, and serves only replies it has cached. Every setting is listed in [Settings](../reference/settings.md). diff --git a/docs/getting_started/quickstart.md b/docs/getting_started/quickstart.md index 69a878c..66abda6 100644 --- a/docs/getting_started/quickstart.md +++ b/docs/getting_started/quickstart.md @@ -7,7 +7,7 @@ products, tags and comments. ## 1. Look at what the model will see ```bash -lpgcast sheet --schema examples/tickets/schema.yaml +schemacast sheet --schema examples/tickets/schema.yaml ``` This prints the **type sheet**: every vertex type with its description, other @@ -16,7 +16,7 @@ names and properties, how it is identified, and every edge as No model is called. ```bash -lpgcast select --schema examples/tickets/schema.yaml --episodes examples/tickets/episodes.jsonl +schemacast select --schema examples/tickets/schema.yaml --episodes examples/tickets/episodes.jsonl ``` This shows the slice of the schema each text would be cast against. A small @@ -39,7 +39,7 @@ shown to the model. With a model configured (see [Installation](installation.md)): ```bash -lpgcast cast --schema examples/tickets/schema.yaml \ +schemacast cast --schema examples/tickets/schema.yaml \ --episodes examples/tickets/episodes.jsonl --out out/tickets ``` @@ -62,7 +62,7 @@ pass the schema. ## 4. Write to a database ```bash -lpgcast cast ... --db examples/connections/neo4j.yaml +schemacast cast ... --db examples/connections/neo4j.yaml ``` A connection file is a graflo connection config; see @@ -71,7 +71,7 @@ A connection file is a graflo connection config; see ## 5. Run it again for free ```bash -lpgcast cast ... --out out/tickets-again --replay +schemacast cast ... --out out/tickets-again --replay ``` Every reply comes from the cache: no request is made, and everything but @@ -82,11 +82,11 @@ Every reply comes from the cache: no request is made, and everything but ```python import asyncio -from lpgcast import LPGCaster, load_schema, read_episodes, save_cast +from schemacast import SchemaCaster, load_schema, read_episodes, save_cast schema = load_schema("examples/tickets/schema.yaml") result = asyncio.run( - LPGCaster(schema).cast(read_episodes("examples/tickets/episodes.jsonl")) + SchemaCaster(schema).cast(read_episodes("examples/tickets/episodes.jsonl")) ) print(result.manifest.status) # {'ok': ..., 'partial': ..., 'failed': ...} diff --git a/docs/guides/benchmark.md b/docs/guides/benchmark.md index 3cea22f..f9015f7 100644 --- a/docs/guides/benchmark.md +++ b/docs/guides/benchmark.md @@ -1,25 +1,25 @@ # The benchmark -`lpgcast-bench` casts a corpus of short texts and scores the result against the graph each text +`schemacast-bench` casts a corpus of short texts and scores the result against the graph each text states. No model judges: matching is exact. ```bash -lpgcast-bench # the oracle row only; free -lpgcast-bench --model gpt-5.6-luna # plus a model row (LPGCAST_LLM_* settings) -lpgcast-bench --split test --model a --model b -lpgcast-bench --model a --replay # from the cache; makes no request -lpgcast-bench compare a.json b.json # read run b against run a +schemacast-bench # the oracle row only; free +schemacast-bench --model gpt-5.6-luna # plus a model row (SCHEMACAST_LLM_* settings) +schemacast-bench --split test --model a --model b +schemacast-bench --model a --replay # from the cache; makes no request +schemacast-bench compare a.json b.json # read run b against run a ``` Each run writes `.json` (counts per episode, rates per case and row) and `.md` -under `--out` (default `bench-results/`, ignored by git). Set `LPGCAST_BENCH_PRICES` to a JSON +under `--out` (default `bench-results/`, ignored by git). Set `SCHEMACAST_BENCH_PRICES` to a JSON file `{"": {"input_per_1m": …, "output_per_1m": …}, "_source": "…"}` for a cost column; no prices ship with the package. ## Corpus -`lpgcast/bench/cases///` holds `case.yaml` (description, schema, and optionally `recast: true` -or pinned `settings`), `episodes.jsonl` and `expected.yaml`. Schemas live in `lpgcast/bench/schemas/`; a case +`schemacast/bench/cases///` holds `case.yaml` (description, schema, and optionally `recast: true` +or pinned `settings`), `episodes.jsonl` and `expected.yaml`. Schemas live in `schemacast/bench/schemas/`; a case naming several merges their vertices and edges. Each family isolates one failure mode in four domains: @@ -51,7 +51,7 @@ comparable. - **oracle** replies with the expected graph through the whole pipeline. It is the ceiling the pipeline allows: what selection leaves out of the slice costs it too. -- **a model** row per `--model`, against `LPGCAST_LLM_BASE_URL`. +- **a model** row per `--model`, against `SCHEMACAST_LLM_BASE_URL`. ## Metrics @@ -81,7 +81,7 @@ Values compare after coercion by their field; strings ignore case and runs of wh ## Comparing two runs -`lpgcast-bench compare A.json B.json` reads run B against run A. Episodes pair on case and source +`schemacast-bench compare A.json B.json` reads run B against run A. Episodes pair on case and source id, and only paired episodes count; the rest are listed. For each rate it gives A, B and B − A over the pooled counts, with a 95% paired bootstrap interval: episodes are resampled together with their pair, so an interval that spans zero says the corpus cannot tell the two runs apart. @@ -96,5 +96,5 @@ when a report holds more than one model; `--json` also writes the comparison as In a clone: write the three files under a new instance directory, add it to `SPLITS.yaml`, run `uv run pytest test/test_bench.py`, and set `CORPUS_DIGEST` in -`lpgcast/bench/corpus.py` to the value the failing digest test prints, with a +`schemacast/bench/corpus.py` to the value the failing digest test prints, with a new `CORPUS_VERSION`. diff --git a/docs/guides/database.md b/docs/guides/database.md index e940370..c5906a4 100644 --- a/docs/guides/database.md +++ b/docs/guides/database.md @@ -1,6 +1,6 @@ # Writing to a database -`lpgcast cast --db conn.yaml` writes the graph to any database graflo +`schemacast cast --db conn.yaml` writes the graph to any database graflo supports, after saving it under `--out`. From Python, call `await write_cast(result, load_connection("conn.yaml"))`. diff --git a/docs/guides/replay.md b/docs/guides/replay.md index aaca1e6..8f30695 100644 --- a/docs/guides/replay.md +++ b/docs/guides/replay.md @@ -8,10 +8,10 @@ request, so it is answered from the cache. ## Replaying a cast ```bash -lpgcast cast ... --replay +schemacast cast ... --replay ``` -`--replay` (or `LPGCAST_CACHE_REQUIRE_HIT=true`) serves every reply from the +`--replay` (or `SCHEMACAST_CACHE_REQUIRE_HIT=true`) serves every reply from the cache, and a miss stops the run with an error instead of calling the model. A replay that finishes therefore made no request. Its graph, groundings, quarantine, pending edges and episode results are byte-identical to the first @@ -25,14 +25,14 @@ replay misses too; cast it again without `--replay` to fill the cache. | Variable | Effect | |---|---| -| `LPGCAST_CACHE_DIR` | Where replies are kept. Default `$XDG_CACHE_HOME/lpgcast` or `~/.cache/lpgcast`. | -| `LPGCAST_CACHE_ENABLED` | `false` neither reads nor writes the cache. | -| `LPGCAST_CACHE_READ_ONLY` | Serve cached replies, write no new ones. | -| `LPGCAST_CACHE_REQUIRE_HIT` | A miss raises; what `--replay` sets. | +| `SCHEMACAST_CACHE_DIR` | Where replies are kept. Default `$XDG_CACHE_HOME/schemacast` or `~/.cache/schemacast`. | +| `SCHEMACAST_CACHE_ENABLED` | `false` neither reads nor writes the cache. | +| `SCHEMACAST_CACHE_READ_ONLY` | Serve cached replies, write no new ones. | +| `SCHEMACAST_CACHE_REQUIRE_HIT` | A miss raises; what `--replay` sets. | ## Without a key -With no `LPGCAST_LLM_API_KEY`, LPGCast never calls a provider: a cached reply +With no `SCHEMACAST_LLM_API_KEY`, SchemaCast never calls a provider: a cached reply is served, and a miss is a configuration error. Sharing a cache directory is therefore enough to let someone reproduce a run without credentials. diff --git a/docs/guides/schema.md b/docs/guides/schema.md index 412555b..2d21dcd 100644 --- a/docs/guides/schema.md +++ b/docs/guides/schema.md @@ -1,8 +1,8 @@ # Preparing a schema -LPGCast casts into a graflo `Schema`, or the schema block of a `GraphManifest`, +SchemaCast casts into a graflo `Schema`, or the schema block of a `GraphManifest`, in YAML or JSON. Anything graflo accepts works; a few things decide how well a -model reads it. Check the result with `lpgcast sheet --schema schema.yaml`: +model reads it. Check the result with `schemacast sheet --schema schema.yaml`: that is everything the model sees. ## Describe types in the reader's words @@ -47,10 +47,10 @@ unit, so the model knows what to expect. The model writes numbers plain ## Large schemas -A schema over the slice budget (`LPGCAST_SLICE_MAX_ELEMENTS`, -`LPGCAST_SLICE_MAX_TOKENS`) is cut down per text: the types the text matches +A schema over the slice budget (`SCHEMACAST_SLICE_MAX_ELEMENTS`, +`SCHEMACAST_SLICE_MAX_TOKENS`) is cut down per text: the types the text matches are seeds, and graflo's `subschema()` closes them into a valid slice, never -dropping an identity field. `lpgcast select --episodes episodes.jsonl` shows +dropping an identity field. `schemacast select --episodes episodes.jsonl` shows each slice and its seeds without calling a model. A text that matches nothing in a large schema is quarantined as `selection_no_match` rather than cast against the whole schema. diff --git a/docs/guides/serve.md b/docs/guides/serve.md index de58784..636df01 100644 --- a/docs/guides/serve.md +++ b/docs/guides/serve.md @@ -1,15 +1,15 @@ # Running as a service -`lpgcast serve` casts over HTTP. It suits a platform that holds the schema and +`schemacast serve` casts over HTTP. It suits a platform that holds the schema and writes the graph itself: the service receives the schema with the episodes, casts them, and returns the result. It never writes to a database and never calls back, so it holds no database credentials. ```bash -pip install 'lpgcast[server]' -export LPGCAST_SERVE_API_KEY=… # required; see below -export LPGCAST_LLM_MODEL=… LPGCAST_LLM_API_KEY=… -lpgcast serve --port 8997 +pip install 'schemacast[server]' +export SCHEMACAST_SERVE_API_KEY=… # required; see below +export SCHEMACAST_LLM_MODEL=… SCHEMACAST_LLM_API_KEY=… +schemacast serve --port 8997 ``` ## `POST /cast` @@ -28,7 +28,7 @@ lpgcast serve --port 8997 [setting](../reference/settings.md). A request cannot choose the model or its endpoint. -The reply holds what [`lpgcast cast --out`](../reference/output.md) writes: +The reply holds what [`schemacast cast --out`](../reference/output.md) writes: `graph` (graflo's `GraFloOutput`), `groundings`, `pending`, `quarantine`, `episodes` and `manifest`. To write the graph, pass `graph` and `pending` to graflo's `write_container`, the function `write_cast` uses. @@ -37,7 +37,7 @@ graflo's `write_container`, the function `write_cast` uses. |---|---| | 200 | cast; an episode that failed is in `episodes` and `quarantine`, not an error | | 401 | missing or wrong bearer token | -| 413 | more episodes than `LPGCAST_SERVE_MAX_EPISODES` | +| 413 | more episodes than `SCHEMACAST_SERVE_MAX_EPISODES` | | 422 | the body, or its `schema`, is not valid | | 503 | no key configured, or the model is misconfigured (no key, unknown model) | @@ -47,9 +47,9 @@ batches small enough for the caller's timeout. ## Authentication Every route but `GET /health` needs `Authorization: Bearer -$LPGCAST_SERVE_API_KEY`. Without a configured key the service refuses to cast +$SCHEMACAST_SERVE_API_KEY`. Without a configured key the service refuses to cast (503) rather than serve anyone who can reach it; set -`LPGCAST_SERVE_ALLOW_ANONYMOUS=true` only on a development machine. +`SCHEMACAST_SERVE_ALLOW_ANONYMOUS=true` only on a development machine. `GET /health` reports the versions, whether a key is configured and the batch cap. diff --git a/docs/index.md b/docs/index.md index ede3a8c..823d9e8 100644 --- a/docs/index.md +++ b/docs/index.md @@ -7,9 +7,9 @@ hide:
-# LPGCast +# SchemaCast -LPGCast reads short texts — messages, tickets, notes — and writes what they +SchemaCast reads short texts — messages, tickets, notes — and writes what they state as a labeled property graph, typed by a GraFlo schema you govern. { .lc-lead } @@ -20,7 +20,7 @@ and property keeps the quote it was read from. It is for teams that already have a schema and want text to land in it. ```bash -uv add lpgcast +uv add schemacast ``` [Quick start](getting_started/quickstart.md){ .md-button .md-button--primary } @@ -75,7 +75,7 @@ uv add lpgcast
!!! note "Early development" - LPGCast is on PyPI (`pip install lpgcast`) and its API may still change; see + SchemaCast is on PyPI (`pip install schemacast`) and its API may still change; see [Installation](getting_started/installation.md). ## What it does diff --git a/docs/reference/index.md b/docs/reference/index.md index 537a89f..9ae1d9d 100644 --- a/docs/reference/index.md +++ b/docs/reference/index.md @@ -3,6 +3,6 @@ | Reference | What it lists | |---|---| | [Settings](settings.md) | Every environment variable, with its default | -| [Command line](cli.md) | `lpgcast` and `lpgcast-bench`, every option | +| [Command line](cli.md) | `schemacast` and `schemacast-bench`, every option | | [Output files](output.md) | What a cast writes under `--out`, field by field | | [Python API](python/index.md) | Every public module, class and function, from the docstrings | diff --git a/docs/reference/output.md b/docs/reference/output.md index 449bc3b..c78f253 100644 --- a/docs/reference/output.md +++ b/docs/reference/output.md @@ -1,6 +1,6 @@ # Output files -`lpgcast cast --out DIR` (or `save_cast(result, DIR)`) writes: +`schemacast cast --out DIR` (or `save_cast(result, DIR)`) writes: | File | Holds | Same bytes on replay | |---|---|---| @@ -36,7 +36,7 @@ An edge with an endpoint known only by a secondary identity: its `edge_id`, its ## `manifest.json` -The output format version (2: groundings may point into context), the LPGCast +The output format version (2: groundings may point into context), the SchemaCast and graflo versions, the schema as `name@version`, every model setting that reaches the provider, the token ceiling and slice budget, the counts by status, and `usage`: calls, cache hits, failures, re-asks, and diff --git a/docs/reference/python/index.md b/docs/reference/python/index.md index 56f98ff..49e3ace 100644 --- a/docs/reference/python/index.md +++ b/docs/reference/python/index.md @@ -1,18 +1,18 @@ # Python API Generated from the docstrings at build time, one page per module. The names most -programs need are importable from `lpgcast` itself: +programs need are importable from `schemacast` itself: | Name | What it is | |---|---| -| `LPGCaster`, `CastResult`, `EpisodeResult` | the pipeline over many episodes, and what it returns ([`cast`](cast.md)) | +| `SchemaCaster`, `CastResult`, `EpisodeResult` | the pipeline over many episodes, and what it returns ([`cast`](cast.md)) | | `Episode`, `read_episodes` | the input ([`episode`](episode.md)) | | `load_schema` | a graflo schema or manifest from YAML or JSON ([`schema_io`](schema_io.md)) | | `save_cast` | a cast on disk ([`output`](output.md)) | | `write_cast`, `load_connection`, `WriteReport` (graflo's `ContainerWriteReport`) | writing to a database through graflo's `write_container` ([`write`](write.md)) | | `Grounding` | where an assertion was read ([`ground`](ground.md)) | | `JsonlQuarantineSink`, `MemoryQuarantineSink` | where left-out items go ([`sink`](sink.md)) | -| `LPGCastSettings`, `LLMSettings`, `CacheSettings` | settings ([`config`](config.md)) | +| `SchemaCastSettings`, `LLMSettings`, `CacheSettings` | settings ([`config`](config.md)) | The steps of a cast are modules of their own: [`select`](select.md), [`prompt`](prompt.md), [`extract`](extract.md), [`validate`](validate.md), diff --git a/examples/connections/neo4j.yaml b/examples/connections/neo4j.yaml index ab9534e..958ea17 100644 --- a/examples/connections/neo4j.yaml +++ b/examples/connections/neo4j.yaml @@ -1,6 +1,6 @@ # Neo4j over Bolt. Keep the password out of this file: set NEO4J_PASSWORD, # which graflo reads for every field the file leaves out. -# `lpgcast cast --recreate-schema` and the `neo4j` test marker wipe the +# `schemacast cast --recreate-schema` and the `neo4j` test marker wipe the # database: point them at a disposable one. db_type: neo4j uri: bolt://localhost:7688 diff --git a/examples/tickets/README.md b/examples/tickets/README.md index 218aeb3..b588af3 100644 --- a/examples/tickets/README.md +++ b/examples/tickets/README.md @@ -19,15 +19,15 @@ What the episodes exercise: Look first; neither command calls a model: ```bash -uv run lpgcast sheet --schema examples/tickets/schema.yaml -uv run lpgcast select --schema examples/tickets/schema.yaml --episodes examples/tickets/episodes.jsonl +uv run schemacast sheet --schema examples/tickets/schema.yaml +uv run schemacast select --schema examples/tickets/schema.yaml --episodes examples/tickets/episodes.jsonl ``` -Cast, with `LPGCAST_LLM_API_KEY` set (and `LPGCAST_LLM_BASE_URL` / `LPGCAST_LLM_MODEL` for a +Cast, with `SCHEMACAST_LLM_API_KEY` set (and `SCHEMACAST_LLM_BASE_URL` / `SCHEMACAST_LLM_MODEL` for a local server): ```bash -uv run lpgcast cast --schema examples/tickets/schema.yaml \ +uv run schemacast cast --schema examples/tickets/schema.yaml \ --episodes examples/tickets/episodes.jsonl --out out/tickets ``` diff --git a/lpgcast/__init__.py b/lpgcast/__init__.py deleted file mode 100644 index 613e2a4..0000000 --- a/lpgcast/__init__.py +++ /dev/null @@ -1,77 +0,0 @@ -"""LPGCast: text to a labeled property graph, against a governed GraFlo schema. - -The names below load on first use, so ``import lpgcast`` stays light. -""" - -from __future__ import annotations - -import importlib -from importlib.metadata import PackageNotFoundError, version -from typing import TYPE_CHECKING, Any - -try: - __version__ = version("lpgcast") -except PackageNotFoundError: # running from a source tree without install - __version__ = "0.0.0" - -if TYPE_CHECKING: - from lpgcast.cast import CastResult, EpisodeResult, LPGCaster - from lpgcast.config import CacheSettings, LLMSettings, LPGCastSettings - from lpgcast.episode import Episode, read_episodes - from lpgcast.ground import Grounding - from lpgcast.output import save_cast - from lpgcast.schema_io import load_schema - from lpgcast.sink import JsonlQuarantineSink, MemoryQuarantineSink - from lpgcast.write import WriteReport, load_connection, write_cast - -_EXPORTS: dict[str, str] = { - "CacheSettings": "lpgcast.config", - "CastResult": "lpgcast.cast", - "Episode": "lpgcast.episode", - "EpisodeResult": "lpgcast.cast", - "Grounding": "lpgcast.ground", - "JsonlQuarantineSink": "lpgcast.sink", - "LLMSettings": "lpgcast.config", - "LPGCastSettings": "lpgcast.config", - "LPGCaster": "lpgcast.cast", - "MemoryQuarantineSink": "lpgcast.sink", - "WriteReport": "lpgcast.write", - "load_connection": "lpgcast.write", - "load_schema": "lpgcast.schema_io", - "read_episodes": "lpgcast.episode", - "save_cast": "lpgcast.output", - "write_cast": "lpgcast.write", -} - -__all__ = [ - "CacheSettings", - "CastResult", - "Episode", - "EpisodeResult", - "Grounding", - "JsonlQuarantineSink", - "LLMSettings", - "LPGCastSettings", - "LPGCaster", - "MemoryQuarantineSink", - "WriteReport", - "__version__", - "load_connection", - "load_schema", - "read_episodes", - "save_cast", - "write_cast", -] - - -def __getattr__(name: str) -> Any: - module = _EXPORTS.get(name) - if module is None: - raise AttributeError(f"module 'lpgcast' has no attribute {name!r}") - value = getattr(importlib.import_module(module), name) - globals()[name] = value - return value - - -def __dir__() -> list[str]: - return sorted(__all__) diff --git a/properdocs.yml b/properdocs.yml index ebe877b..708fb99 100644 --- a/properdocs.yml +++ b/properdocs.yml @@ -1,8 +1,8 @@ -site_name: LPGCast +site_name: SchemaCast site_description: Text to a labeled property graph, against a governed GraFlo schema -site_url: https://growgraph.github.io/LPGCast -repo_url: https://github.com/growgraph/LPGCast -repo_name: growgraph/LPGCast +site_url: https://growgraph.github.io/SchemaCast +repo_url: https://github.com/growgraph/SchemaCast +repo_name: growgraph/SchemaCast copyright: Copyright © 2026 GrowGraph edit_uri: edit/main/docs/ theme: @@ -46,7 +46,7 @@ extra_css: exclude_docs: | _build/ plugins: -# Underscores are separators, so a setting such as `LPGCAST_CACHE_REQUIRE_HIT` +# Underscores are separators, so a setting such as `SCHEMACAST_CACHE_REQUIRE_HIT` # is indexed as its words as well as whole. - search: separator: '[\s\-,:!=\[\]()"`/]+|\.(?!\d)|&[lg]t;|_' diff --git a/pyproject.toml b/pyproject.toml index 2793758..a498d2c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -47,33 +47,34 @@ dependencies = [ description = "Text to labeled property graph against a governed GraFlo schema" keywords = ["graflo", "information-extraction", "knowledge-graph", "llm", "property-graph"] license = "Apache-2.0" -name = "lpgcast" +name = "schemacast" readme = "README.md" requires-python = ">=3.11" -version = "0.2.0" +version = "0.3.0" [project.optional-dependencies] # Reserved: the dense retrieval lane. embed = [] -# `lpgcast serve`. +# `schemacast serve`. server = ["fastapi>=0.134.0,<1", "uvicorn[standard]>=0.30.0,<1"] [project.scripts] -lpgcast = "lpgcast.cli:main" -lpgcast-bench = "lpgcast.bench.cli:main" -text2lpg = "lpgcast.cli:main" +schemacast = "schemacast.cli:main" +schemacast-bench = "schemacast.bench.cli:main" [project.urls] -Documentation = "https://growgraph.github.io/LPGCast/" -Issues = "https://github.com/growgraph/LPGCast/issues" -Repository = "https://github.com/growgraph/LPGCast" +Documentation = "https://growgraph.github.io/SchemaCast/" +Issues = "https://github.com/growgraph/SchemaCast/issues" +Repository = "https://github.com/growgraph/SchemaCast" [tool.hatch.build.targets.wheel] -packages = ["lpgcast"] +packages = ["schemacast"] [tool.pytest.ini_options] asyncio_default_fixture_loop_scope = "function" asyncio_mode = "auto" +# A graflo deprecation fails the suite, so it is fixed before graflo removes the name. +filterwarnings = ["error:.*is deprecated since graflo:DeprecationWarning"] markers = [ "llm: exercises a real language model (skipped unless --run-llm)", "neo4j: wipes the Neo4j named by NEO4J_URI (skipped unless --run-neo4j)" diff --git a/schemacast/__init__.py b/schemacast/__init__.py new file mode 100644 index 0000000..2c1bb59 --- /dev/null +++ b/schemacast/__init__.py @@ -0,0 +1,77 @@ +"""SchemaCast: text to a labeled property graph, against a governed GraFlo schema. + +The names below load on first use, so ``import schemacast`` stays light. +""" + +from __future__ import annotations + +import importlib +from importlib.metadata import PackageNotFoundError, version +from typing import TYPE_CHECKING, Any + +try: + __version__ = version("schemacast") +except PackageNotFoundError: # running from a source tree without install + __version__ = "0.0.0" + +if TYPE_CHECKING: + from schemacast.cast import CastResult, EpisodeResult, SchemaCaster + from schemacast.config import CacheSettings, LLMSettings, SchemaCastSettings + from schemacast.episode import Episode, read_episodes + from schemacast.ground import Grounding + from schemacast.output import save_cast + from schemacast.schema_io import load_schema + from schemacast.sink import JsonlQuarantineSink, MemoryQuarantineSink + from schemacast.write import WriteReport, load_connection, write_cast + +_EXPORTS: dict[str, str] = { + "CacheSettings": "schemacast.config", + "CastResult": "schemacast.cast", + "Episode": "schemacast.episode", + "EpisodeResult": "schemacast.cast", + "Grounding": "schemacast.ground", + "JsonlQuarantineSink": "schemacast.sink", + "LLMSettings": "schemacast.config", + "SchemaCastSettings": "schemacast.config", + "SchemaCaster": "schemacast.cast", + "MemoryQuarantineSink": "schemacast.sink", + "WriteReport": "schemacast.write", + "load_connection": "schemacast.write", + "load_schema": "schemacast.schema_io", + "read_episodes": "schemacast.episode", + "save_cast": "schemacast.output", + "write_cast": "schemacast.write", +} + +__all__ = [ + "CacheSettings", + "CastResult", + "Episode", + "EpisodeResult", + "Grounding", + "JsonlQuarantineSink", + "LLMSettings", + "MemoryQuarantineSink", + "SchemaCastSettings", + "SchemaCaster", + "WriteReport", + "__version__", + "load_connection", + "load_schema", + "read_episodes", + "save_cast", + "write_cast", +] + + +def __getattr__(name: str) -> Any: + module = _EXPORTS.get(name) + if module is None: + raise AttributeError(f"module 'schemacast' has no attribute {name!r}") + value = getattr(importlib.import_module(module), name) + globals()[name] = value + return value + + +def __dir__() -> list[str]: + return sorted(__all__) diff --git a/lpgcast/bench/README.md b/schemacast/bench/README.md similarity index 76% rename from lpgcast/bench/README.md rename to schemacast/bench/README.md index b988abf..f10c7f8 100644 --- a/lpgcast/bench/README.md +++ b/schemacast/bench/README.md @@ -1,7 +1,7 @@ # Benchmark corpus -The cases and schemas `lpgcast-bench` scores against. How the benchmark works, what it -measures and how to run it: the [benchmark guide](https://growgraph.github.io/LPGCast/guides/benchmark/) +The cases and schemas `schemacast-bench` scores against. How the benchmark works, what it +measures and how to run it: the [benchmark guide](https://growgraph.github.io/SchemaCast/guides/benchmark/) (`docs/guides/benchmark.md` in this repository). ## Adding a case diff --git a/lpgcast/bench/__init__.py b/schemacast/bench/__init__.py similarity index 54% rename from lpgcast/bench/__init__.py rename to schemacast/bench/__init__.py index 01377c9..7e26638 100644 --- a/lpgcast/bench/__init__.py +++ b/schemacast/bench/__init__.py @@ -1,2 +1,2 @@ """The benchmark: a corpus of texts with the graphs they state, a scorer that -needs no model as judge, and the ``lpgcast-bench`` command.""" +needs no model as judge, and the ``schemacast-bench`` command.""" diff --git a/lpgcast/bench/cases/SPLITS.yaml b/schemacast/bench/cases/SPLITS.yaml similarity index 100% rename from lpgcast/bench/cases/SPLITS.yaml rename to schemacast/bench/cases/SPLITS.yaml diff --git a/lpgcast/bench/cases/context/helpdesk/case.yaml b/schemacast/bench/cases/context/helpdesk/case.yaml similarity index 100% rename from lpgcast/bench/cases/context/helpdesk/case.yaml rename to schemacast/bench/cases/context/helpdesk/case.yaml diff --git a/lpgcast/bench/cases/context/helpdesk/episodes.jsonl b/schemacast/bench/cases/context/helpdesk/episodes.jsonl similarity index 100% rename from lpgcast/bench/cases/context/helpdesk/episodes.jsonl rename to schemacast/bench/cases/context/helpdesk/episodes.jsonl diff --git a/lpgcast/bench/cases/context/helpdesk/expected.yaml b/schemacast/bench/cases/context/helpdesk/expected.yaml similarity index 100% rename from lpgcast/bench/cases/context/helpdesk/expected.yaml rename to schemacast/bench/cases/context/helpdesk/expected.yaml diff --git a/lpgcast/bench/cases/context/lab/case.yaml b/schemacast/bench/cases/context/lab/case.yaml similarity index 100% rename from lpgcast/bench/cases/context/lab/case.yaml rename to schemacast/bench/cases/context/lab/case.yaml diff --git a/lpgcast/bench/cases/context/lab/episodes.jsonl b/schemacast/bench/cases/context/lab/episodes.jsonl similarity index 100% rename from lpgcast/bench/cases/context/lab/episodes.jsonl rename to schemacast/bench/cases/context/lab/episodes.jsonl diff --git a/lpgcast/bench/cases/context/lab/expected.yaml b/schemacast/bench/cases/context/lab/expected.yaml similarity index 100% rename from lpgcast/bench/cases/context/lab/expected.yaml rename to schemacast/bench/cases/context/lab/expected.yaml diff --git a/lpgcast/bench/cases/context/logistics/case.yaml b/schemacast/bench/cases/context/logistics/case.yaml similarity index 100% rename from lpgcast/bench/cases/context/logistics/case.yaml rename to schemacast/bench/cases/context/logistics/case.yaml diff --git a/lpgcast/bench/cases/context/logistics/episodes.jsonl b/schemacast/bench/cases/context/logistics/episodes.jsonl similarity index 100% rename from lpgcast/bench/cases/context/logistics/episodes.jsonl rename to schemacast/bench/cases/context/logistics/episodes.jsonl diff --git a/lpgcast/bench/cases/context/logistics/expected.yaml b/schemacast/bench/cases/context/logistics/expected.yaml similarity index 100% rename from lpgcast/bench/cases/context/logistics/expected.yaml rename to schemacast/bench/cases/context/logistics/expected.yaml diff --git a/lpgcast/bench/cases/context/plant/case.yaml b/schemacast/bench/cases/context/plant/case.yaml similarity index 100% rename from lpgcast/bench/cases/context/plant/case.yaml rename to schemacast/bench/cases/context/plant/case.yaml diff --git a/lpgcast/bench/cases/context/plant/episodes.jsonl b/schemacast/bench/cases/context/plant/episodes.jsonl similarity index 100% rename from lpgcast/bench/cases/context/plant/episodes.jsonl rename to schemacast/bench/cases/context/plant/episodes.jsonl diff --git a/lpgcast/bench/cases/context/plant/expected.yaml b/schemacast/bench/cases/context/plant/expected.yaml similarity index 100% rename from lpgcast/bench/cases/context/plant/expected.yaml rename to schemacast/bench/cases/context/plant/expected.yaml diff --git a/lpgcast/bench/cases/direction/helpdesk/case.yaml b/schemacast/bench/cases/direction/helpdesk/case.yaml similarity index 100% rename from lpgcast/bench/cases/direction/helpdesk/case.yaml rename to schemacast/bench/cases/direction/helpdesk/case.yaml diff --git a/lpgcast/bench/cases/direction/helpdesk/episodes.jsonl b/schemacast/bench/cases/direction/helpdesk/episodes.jsonl similarity index 100% rename from lpgcast/bench/cases/direction/helpdesk/episodes.jsonl rename to schemacast/bench/cases/direction/helpdesk/episodes.jsonl diff --git a/lpgcast/bench/cases/direction/helpdesk/expected.yaml b/schemacast/bench/cases/direction/helpdesk/expected.yaml similarity index 100% rename from lpgcast/bench/cases/direction/helpdesk/expected.yaml rename to schemacast/bench/cases/direction/helpdesk/expected.yaml diff --git a/lpgcast/bench/cases/direction/lab/case.yaml b/schemacast/bench/cases/direction/lab/case.yaml similarity index 100% rename from lpgcast/bench/cases/direction/lab/case.yaml rename to schemacast/bench/cases/direction/lab/case.yaml diff --git a/lpgcast/bench/cases/direction/lab/episodes.jsonl b/schemacast/bench/cases/direction/lab/episodes.jsonl similarity index 100% rename from lpgcast/bench/cases/direction/lab/episodes.jsonl rename to schemacast/bench/cases/direction/lab/episodes.jsonl diff --git a/lpgcast/bench/cases/direction/lab/expected.yaml b/schemacast/bench/cases/direction/lab/expected.yaml similarity index 100% rename from lpgcast/bench/cases/direction/lab/expected.yaml rename to schemacast/bench/cases/direction/lab/expected.yaml diff --git a/lpgcast/bench/cases/direction/logistics/case.yaml b/schemacast/bench/cases/direction/logistics/case.yaml similarity index 100% rename from lpgcast/bench/cases/direction/logistics/case.yaml rename to schemacast/bench/cases/direction/logistics/case.yaml diff --git a/lpgcast/bench/cases/direction/logistics/episodes.jsonl b/schemacast/bench/cases/direction/logistics/episodes.jsonl similarity index 100% rename from lpgcast/bench/cases/direction/logistics/episodes.jsonl rename to schemacast/bench/cases/direction/logistics/episodes.jsonl diff --git a/lpgcast/bench/cases/direction/logistics/expected.yaml b/schemacast/bench/cases/direction/logistics/expected.yaml similarity index 100% rename from lpgcast/bench/cases/direction/logistics/expected.yaml rename to schemacast/bench/cases/direction/logistics/expected.yaml diff --git a/lpgcast/bench/cases/direction/plant/case.yaml b/schemacast/bench/cases/direction/plant/case.yaml similarity index 100% rename from lpgcast/bench/cases/direction/plant/case.yaml rename to schemacast/bench/cases/direction/plant/case.yaml diff --git a/lpgcast/bench/cases/direction/plant/episodes.jsonl b/schemacast/bench/cases/direction/plant/episodes.jsonl similarity index 100% rename from lpgcast/bench/cases/direction/plant/episodes.jsonl rename to schemacast/bench/cases/direction/plant/episodes.jsonl diff --git a/lpgcast/bench/cases/direction/plant/expected.yaml b/schemacast/bench/cases/direction/plant/expected.yaml similarity index 100% rename from lpgcast/bench/cases/direction/plant/expected.yaml rename to schemacast/bench/cases/direction/plant/expected.yaml diff --git a/lpgcast/bench/cases/distractors/helpdesk/case.yaml b/schemacast/bench/cases/distractors/helpdesk/case.yaml similarity index 100% rename from lpgcast/bench/cases/distractors/helpdesk/case.yaml rename to schemacast/bench/cases/distractors/helpdesk/case.yaml diff --git a/lpgcast/bench/cases/distractors/helpdesk/episodes.jsonl b/schemacast/bench/cases/distractors/helpdesk/episodes.jsonl similarity index 100% rename from lpgcast/bench/cases/distractors/helpdesk/episodes.jsonl rename to schemacast/bench/cases/distractors/helpdesk/episodes.jsonl diff --git a/lpgcast/bench/cases/distractors/helpdesk/expected.yaml b/schemacast/bench/cases/distractors/helpdesk/expected.yaml similarity index 100% rename from lpgcast/bench/cases/distractors/helpdesk/expected.yaml rename to schemacast/bench/cases/distractors/helpdesk/expected.yaml diff --git a/lpgcast/bench/cases/distractors/lab/case.yaml b/schemacast/bench/cases/distractors/lab/case.yaml similarity index 100% rename from lpgcast/bench/cases/distractors/lab/case.yaml rename to schemacast/bench/cases/distractors/lab/case.yaml diff --git a/lpgcast/bench/cases/distractors/lab/episodes.jsonl b/schemacast/bench/cases/distractors/lab/episodes.jsonl similarity index 100% rename from lpgcast/bench/cases/distractors/lab/episodes.jsonl rename to schemacast/bench/cases/distractors/lab/episodes.jsonl diff --git a/lpgcast/bench/cases/distractors/lab/expected.yaml b/schemacast/bench/cases/distractors/lab/expected.yaml similarity index 100% rename from lpgcast/bench/cases/distractors/lab/expected.yaml rename to schemacast/bench/cases/distractors/lab/expected.yaml diff --git a/lpgcast/bench/cases/distractors/logistics/case.yaml b/schemacast/bench/cases/distractors/logistics/case.yaml similarity index 100% rename from lpgcast/bench/cases/distractors/logistics/case.yaml rename to schemacast/bench/cases/distractors/logistics/case.yaml diff --git a/lpgcast/bench/cases/distractors/logistics/episodes.jsonl b/schemacast/bench/cases/distractors/logistics/episodes.jsonl similarity index 100% rename from lpgcast/bench/cases/distractors/logistics/episodes.jsonl rename to schemacast/bench/cases/distractors/logistics/episodes.jsonl diff --git a/lpgcast/bench/cases/distractors/logistics/expected.yaml b/schemacast/bench/cases/distractors/logistics/expected.yaml similarity index 100% rename from lpgcast/bench/cases/distractors/logistics/expected.yaml rename to schemacast/bench/cases/distractors/logistics/expected.yaml diff --git a/lpgcast/bench/cases/distractors/plant/case.yaml b/schemacast/bench/cases/distractors/plant/case.yaml similarity index 100% rename from lpgcast/bench/cases/distractors/plant/case.yaml rename to schemacast/bench/cases/distractors/plant/case.yaml diff --git a/lpgcast/bench/cases/distractors/plant/episodes.jsonl b/schemacast/bench/cases/distractors/plant/episodes.jsonl similarity index 100% rename from lpgcast/bench/cases/distractors/plant/episodes.jsonl rename to schemacast/bench/cases/distractors/plant/episodes.jsonl diff --git a/lpgcast/bench/cases/distractors/plant/expected.yaml b/schemacast/bench/cases/distractors/plant/expected.yaml similarity index 100% rename from lpgcast/bench/cases/distractors/plant/expected.yaml rename to schemacast/bench/cases/distractors/plant/expected.yaml diff --git a/lpgcast/bench/cases/identity/helpdesk/case.yaml b/schemacast/bench/cases/identity/helpdesk/case.yaml similarity index 100% rename from lpgcast/bench/cases/identity/helpdesk/case.yaml rename to schemacast/bench/cases/identity/helpdesk/case.yaml diff --git a/lpgcast/bench/cases/identity/helpdesk/episodes.jsonl b/schemacast/bench/cases/identity/helpdesk/episodes.jsonl similarity index 100% rename from lpgcast/bench/cases/identity/helpdesk/episodes.jsonl rename to schemacast/bench/cases/identity/helpdesk/episodes.jsonl diff --git a/lpgcast/bench/cases/identity/helpdesk/expected.yaml b/schemacast/bench/cases/identity/helpdesk/expected.yaml similarity index 100% rename from lpgcast/bench/cases/identity/helpdesk/expected.yaml rename to schemacast/bench/cases/identity/helpdesk/expected.yaml diff --git a/lpgcast/bench/cases/identity/lab/case.yaml b/schemacast/bench/cases/identity/lab/case.yaml similarity index 100% rename from lpgcast/bench/cases/identity/lab/case.yaml rename to schemacast/bench/cases/identity/lab/case.yaml diff --git a/lpgcast/bench/cases/identity/lab/episodes.jsonl b/schemacast/bench/cases/identity/lab/episodes.jsonl similarity index 100% rename from lpgcast/bench/cases/identity/lab/episodes.jsonl rename to schemacast/bench/cases/identity/lab/episodes.jsonl diff --git a/lpgcast/bench/cases/identity/lab/expected.yaml b/schemacast/bench/cases/identity/lab/expected.yaml similarity index 100% rename from lpgcast/bench/cases/identity/lab/expected.yaml rename to schemacast/bench/cases/identity/lab/expected.yaml diff --git a/lpgcast/bench/cases/identity/logistics/case.yaml b/schemacast/bench/cases/identity/logistics/case.yaml similarity index 100% rename from lpgcast/bench/cases/identity/logistics/case.yaml rename to schemacast/bench/cases/identity/logistics/case.yaml diff --git a/lpgcast/bench/cases/identity/logistics/episodes.jsonl b/schemacast/bench/cases/identity/logistics/episodes.jsonl similarity index 100% rename from lpgcast/bench/cases/identity/logistics/episodes.jsonl rename to schemacast/bench/cases/identity/logistics/episodes.jsonl diff --git a/lpgcast/bench/cases/identity/logistics/expected.yaml b/schemacast/bench/cases/identity/logistics/expected.yaml similarity index 100% rename from lpgcast/bench/cases/identity/logistics/expected.yaml rename to schemacast/bench/cases/identity/logistics/expected.yaml diff --git a/lpgcast/bench/cases/identity/plant/case.yaml b/schemacast/bench/cases/identity/plant/case.yaml similarity index 100% rename from lpgcast/bench/cases/identity/plant/case.yaml rename to schemacast/bench/cases/identity/plant/case.yaml diff --git a/lpgcast/bench/cases/identity/plant/episodes.jsonl b/schemacast/bench/cases/identity/plant/episodes.jsonl similarity index 100% rename from lpgcast/bench/cases/identity/plant/episodes.jsonl rename to schemacast/bench/cases/identity/plant/episodes.jsonl diff --git a/lpgcast/bench/cases/identity/plant/expected.yaml b/schemacast/bench/cases/identity/plant/expected.yaml similarity index 100% rename from lpgcast/bench/cases/identity/plant/expected.yaml rename to schemacast/bench/cases/identity/plant/expected.yaml diff --git a/lpgcast/bench/cases/injection/helpdesk/case.yaml b/schemacast/bench/cases/injection/helpdesk/case.yaml similarity index 100% rename from lpgcast/bench/cases/injection/helpdesk/case.yaml rename to schemacast/bench/cases/injection/helpdesk/case.yaml diff --git a/lpgcast/bench/cases/injection/helpdesk/episodes.jsonl b/schemacast/bench/cases/injection/helpdesk/episodes.jsonl similarity index 100% rename from lpgcast/bench/cases/injection/helpdesk/episodes.jsonl rename to schemacast/bench/cases/injection/helpdesk/episodes.jsonl diff --git a/lpgcast/bench/cases/injection/helpdesk/expected.yaml b/schemacast/bench/cases/injection/helpdesk/expected.yaml similarity index 100% rename from lpgcast/bench/cases/injection/helpdesk/expected.yaml rename to schemacast/bench/cases/injection/helpdesk/expected.yaml diff --git a/lpgcast/bench/cases/injection/lab/case.yaml b/schemacast/bench/cases/injection/lab/case.yaml similarity index 100% rename from lpgcast/bench/cases/injection/lab/case.yaml rename to schemacast/bench/cases/injection/lab/case.yaml diff --git a/lpgcast/bench/cases/injection/lab/episodes.jsonl b/schemacast/bench/cases/injection/lab/episodes.jsonl similarity index 100% rename from lpgcast/bench/cases/injection/lab/episodes.jsonl rename to schemacast/bench/cases/injection/lab/episodes.jsonl diff --git a/lpgcast/bench/cases/injection/lab/expected.yaml b/schemacast/bench/cases/injection/lab/expected.yaml similarity index 100% rename from lpgcast/bench/cases/injection/lab/expected.yaml rename to schemacast/bench/cases/injection/lab/expected.yaml diff --git a/lpgcast/bench/cases/injection/logistics/case.yaml b/schemacast/bench/cases/injection/logistics/case.yaml similarity index 100% rename from lpgcast/bench/cases/injection/logistics/case.yaml rename to schemacast/bench/cases/injection/logistics/case.yaml diff --git a/lpgcast/bench/cases/injection/logistics/episodes.jsonl b/schemacast/bench/cases/injection/logistics/episodes.jsonl similarity index 100% rename from lpgcast/bench/cases/injection/logistics/episodes.jsonl rename to schemacast/bench/cases/injection/logistics/episodes.jsonl diff --git a/lpgcast/bench/cases/injection/logistics/expected.yaml b/schemacast/bench/cases/injection/logistics/expected.yaml similarity index 100% rename from lpgcast/bench/cases/injection/logistics/expected.yaml rename to schemacast/bench/cases/injection/logistics/expected.yaml diff --git a/lpgcast/bench/cases/injection/plant/case.yaml b/schemacast/bench/cases/injection/plant/case.yaml similarity index 100% rename from lpgcast/bench/cases/injection/plant/case.yaml rename to schemacast/bench/cases/injection/plant/case.yaml diff --git a/lpgcast/bench/cases/injection/plant/episodes.jsonl b/schemacast/bench/cases/injection/plant/episodes.jsonl similarity index 100% rename from lpgcast/bench/cases/injection/plant/episodes.jsonl rename to schemacast/bench/cases/injection/plant/episodes.jsonl diff --git a/lpgcast/bench/cases/injection/plant/expected.yaml b/schemacast/bench/cases/injection/plant/expected.yaml similarity index 100% rename from lpgcast/bench/cases/injection/plant/expected.yaml rename to schemacast/bench/cases/injection/plant/expected.yaml diff --git a/lpgcast/bench/cases/mention/helpdesk/case.yaml b/schemacast/bench/cases/mention/helpdesk/case.yaml similarity index 100% rename from lpgcast/bench/cases/mention/helpdesk/case.yaml rename to schemacast/bench/cases/mention/helpdesk/case.yaml diff --git a/lpgcast/bench/cases/mention/helpdesk/episodes.jsonl b/schemacast/bench/cases/mention/helpdesk/episodes.jsonl similarity index 100% rename from lpgcast/bench/cases/mention/helpdesk/episodes.jsonl rename to schemacast/bench/cases/mention/helpdesk/episodes.jsonl diff --git a/lpgcast/bench/cases/mention/helpdesk/expected.yaml b/schemacast/bench/cases/mention/helpdesk/expected.yaml similarity index 100% rename from lpgcast/bench/cases/mention/helpdesk/expected.yaml rename to schemacast/bench/cases/mention/helpdesk/expected.yaml diff --git a/lpgcast/bench/cases/mention/lab/case.yaml b/schemacast/bench/cases/mention/lab/case.yaml similarity index 100% rename from lpgcast/bench/cases/mention/lab/case.yaml rename to schemacast/bench/cases/mention/lab/case.yaml diff --git a/lpgcast/bench/cases/mention/lab/episodes.jsonl b/schemacast/bench/cases/mention/lab/episodes.jsonl similarity index 100% rename from lpgcast/bench/cases/mention/lab/episodes.jsonl rename to schemacast/bench/cases/mention/lab/episodes.jsonl diff --git a/lpgcast/bench/cases/mention/lab/expected.yaml b/schemacast/bench/cases/mention/lab/expected.yaml similarity index 100% rename from lpgcast/bench/cases/mention/lab/expected.yaml rename to schemacast/bench/cases/mention/lab/expected.yaml diff --git a/lpgcast/bench/cases/mention/logistics/case.yaml b/schemacast/bench/cases/mention/logistics/case.yaml similarity index 100% rename from lpgcast/bench/cases/mention/logistics/case.yaml rename to schemacast/bench/cases/mention/logistics/case.yaml diff --git a/lpgcast/bench/cases/mention/logistics/episodes.jsonl b/schemacast/bench/cases/mention/logistics/episodes.jsonl similarity index 100% rename from lpgcast/bench/cases/mention/logistics/episodes.jsonl rename to schemacast/bench/cases/mention/logistics/episodes.jsonl diff --git a/lpgcast/bench/cases/mention/logistics/expected.yaml b/schemacast/bench/cases/mention/logistics/expected.yaml similarity index 100% rename from lpgcast/bench/cases/mention/logistics/expected.yaml rename to schemacast/bench/cases/mention/logistics/expected.yaml diff --git a/lpgcast/bench/cases/mention/plant/case.yaml b/schemacast/bench/cases/mention/plant/case.yaml similarity index 100% rename from lpgcast/bench/cases/mention/plant/case.yaml rename to schemacast/bench/cases/mention/plant/case.yaml diff --git a/lpgcast/bench/cases/mention/plant/episodes.jsonl b/schemacast/bench/cases/mention/plant/episodes.jsonl similarity index 100% rename from lpgcast/bench/cases/mention/plant/episodes.jsonl rename to schemacast/bench/cases/mention/plant/episodes.jsonl diff --git a/lpgcast/bench/cases/mention/plant/expected.yaml b/schemacast/bench/cases/mention/plant/expected.yaml similarity index 100% rename from lpgcast/bench/cases/mention/plant/expected.yaml rename to schemacast/bench/cases/mention/plant/expected.yaml diff --git a/lpgcast/bench/cases/recast/helpdesk/case.yaml b/schemacast/bench/cases/recast/helpdesk/case.yaml similarity index 100% rename from lpgcast/bench/cases/recast/helpdesk/case.yaml rename to schemacast/bench/cases/recast/helpdesk/case.yaml diff --git a/lpgcast/bench/cases/recast/helpdesk/episodes.jsonl b/schemacast/bench/cases/recast/helpdesk/episodes.jsonl similarity index 100% rename from lpgcast/bench/cases/recast/helpdesk/episodes.jsonl rename to schemacast/bench/cases/recast/helpdesk/episodes.jsonl diff --git a/lpgcast/bench/cases/recast/helpdesk/expected.yaml b/schemacast/bench/cases/recast/helpdesk/expected.yaml similarity index 100% rename from lpgcast/bench/cases/recast/helpdesk/expected.yaml rename to schemacast/bench/cases/recast/helpdesk/expected.yaml diff --git a/lpgcast/bench/cases/recast/lab/case.yaml b/schemacast/bench/cases/recast/lab/case.yaml similarity index 100% rename from lpgcast/bench/cases/recast/lab/case.yaml rename to schemacast/bench/cases/recast/lab/case.yaml diff --git a/lpgcast/bench/cases/recast/lab/episodes.jsonl b/schemacast/bench/cases/recast/lab/episodes.jsonl similarity index 100% rename from lpgcast/bench/cases/recast/lab/episodes.jsonl rename to schemacast/bench/cases/recast/lab/episodes.jsonl diff --git a/lpgcast/bench/cases/recast/lab/expected.yaml b/schemacast/bench/cases/recast/lab/expected.yaml similarity index 100% rename from lpgcast/bench/cases/recast/lab/expected.yaml rename to schemacast/bench/cases/recast/lab/expected.yaml diff --git a/lpgcast/bench/cases/recast/logistics/case.yaml b/schemacast/bench/cases/recast/logistics/case.yaml similarity index 100% rename from lpgcast/bench/cases/recast/logistics/case.yaml rename to schemacast/bench/cases/recast/logistics/case.yaml diff --git a/lpgcast/bench/cases/recast/logistics/episodes.jsonl b/schemacast/bench/cases/recast/logistics/episodes.jsonl similarity index 100% rename from lpgcast/bench/cases/recast/logistics/episodes.jsonl rename to schemacast/bench/cases/recast/logistics/episodes.jsonl diff --git a/lpgcast/bench/cases/recast/logistics/expected.yaml b/schemacast/bench/cases/recast/logistics/expected.yaml similarity index 100% rename from lpgcast/bench/cases/recast/logistics/expected.yaml rename to schemacast/bench/cases/recast/logistics/expected.yaml diff --git a/lpgcast/bench/cases/recast/plant/case.yaml b/schemacast/bench/cases/recast/plant/case.yaml similarity index 100% rename from lpgcast/bench/cases/recast/plant/case.yaml rename to schemacast/bench/cases/recast/plant/case.yaml diff --git a/lpgcast/bench/cases/recast/plant/episodes.jsonl b/schemacast/bench/cases/recast/plant/episodes.jsonl similarity index 100% rename from lpgcast/bench/cases/recast/plant/episodes.jsonl rename to schemacast/bench/cases/recast/plant/episodes.jsonl diff --git a/lpgcast/bench/cases/recast/plant/expected.yaml b/schemacast/bench/cases/recast/plant/expected.yaml similarity index 100% rename from lpgcast/bench/cases/recast/plant/expected.yaml rename to schemacast/bench/cases/recast/plant/expected.yaml diff --git a/lpgcast/bench/cases/selection/helpdesk/case.yaml b/schemacast/bench/cases/selection/helpdesk/case.yaml similarity index 100% rename from lpgcast/bench/cases/selection/helpdesk/case.yaml rename to schemacast/bench/cases/selection/helpdesk/case.yaml diff --git a/lpgcast/bench/cases/selection/helpdesk/episodes.jsonl b/schemacast/bench/cases/selection/helpdesk/episodes.jsonl similarity index 100% rename from lpgcast/bench/cases/selection/helpdesk/episodes.jsonl rename to schemacast/bench/cases/selection/helpdesk/episodes.jsonl diff --git a/lpgcast/bench/cases/selection/helpdesk/expected.yaml b/schemacast/bench/cases/selection/helpdesk/expected.yaml similarity index 100% rename from lpgcast/bench/cases/selection/helpdesk/expected.yaml rename to schemacast/bench/cases/selection/helpdesk/expected.yaml diff --git a/lpgcast/bench/cases/selection/lab/case.yaml b/schemacast/bench/cases/selection/lab/case.yaml similarity index 100% rename from lpgcast/bench/cases/selection/lab/case.yaml rename to schemacast/bench/cases/selection/lab/case.yaml diff --git a/lpgcast/bench/cases/selection/lab/episodes.jsonl b/schemacast/bench/cases/selection/lab/episodes.jsonl similarity index 100% rename from lpgcast/bench/cases/selection/lab/episodes.jsonl rename to schemacast/bench/cases/selection/lab/episodes.jsonl diff --git a/lpgcast/bench/cases/selection/lab/expected.yaml b/schemacast/bench/cases/selection/lab/expected.yaml similarity index 100% rename from lpgcast/bench/cases/selection/lab/expected.yaml rename to schemacast/bench/cases/selection/lab/expected.yaml diff --git a/lpgcast/bench/cases/selection/logistics/case.yaml b/schemacast/bench/cases/selection/logistics/case.yaml similarity index 100% rename from lpgcast/bench/cases/selection/logistics/case.yaml rename to schemacast/bench/cases/selection/logistics/case.yaml diff --git a/lpgcast/bench/cases/selection/logistics/episodes.jsonl b/schemacast/bench/cases/selection/logistics/episodes.jsonl similarity index 100% rename from lpgcast/bench/cases/selection/logistics/episodes.jsonl rename to schemacast/bench/cases/selection/logistics/episodes.jsonl diff --git a/lpgcast/bench/cases/selection/logistics/expected.yaml b/schemacast/bench/cases/selection/logistics/expected.yaml similarity index 100% rename from lpgcast/bench/cases/selection/logistics/expected.yaml rename to schemacast/bench/cases/selection/logistics/expected.yaml diff --git a/lpgcast/bench/cases/selection/plant/case.yaml b/schemacast/bench/cases/selection/plant/case.yaml similarity index 100% rename from lpgcast/bench/cases/selection/plant/case.yaml rename to schemacast/bench/cases/selection/plant/case.yaml diff --git a/lpgcast/bench/cases/selection/plant/episodes.jsonl b/schemacast/bench/cases/selection/plant/episodes.jsonl similarity index 100% rename from lpgcast/bench/cases/selection/plant/episodes.jsonl rename to schemacast/bench/cases/selection/plant/episodes.jsonl diff --git a/lpgcast/bench/cases/selection/plant/expected.yaml b/schemacast/bench/cases/selection/plant/expected.yaml similarity index 100% rename from lpgcast/bench/cases/selection/plant/expected.yaml rename to schemacast/bench/cases/selection/plant/expected.yaml diff --git a/lpgcast/bench/cases/units/helpdesk/case.yaml b/schemacast/bench/cases/units/helpdesk/case.yaml similarity index 100% rename from lpgcast/bench/cases/units/helpdesk/case.yaml rename to schemacast/bench/cases/units/helpdesk/case.yaml diff --git a/lpgcast/bench/cases/units/helpdesk/episodes.jsonl b/schemacast/bench/cases/units/helpdesk/episodes.jsonl similarity index 100% rename from lpgcast/bench/cases/units/helpdesk/episodes.jsonl rename to schemacast/bench/cases/units/helpdesk/episodes.jsonl diff --git a/lpgcast/bench/cases/units/helpdesk/expected.yaml b/schemacast/bench/cases/units/helpdesk/expected.yaml similarity index 100% rename from lpgcast/bench/cases/units/helpdesk/expected.yaml rename to schemacast/bench/cases/units/helpdesk/expected.yaml diff --git a/lpgcast/bench/cases/units/lab/case.yaml b/schemacast/bench/cases/units/lab/case.yaml similarity index 100% rename from lpgcast/bench/cases/units/lab/case.yaml rename to schemacast/bench/cases/units/lab/case.yaml diff --git a/lpgcast/bench/cases/units/lab/episodes.jsonl b/schemacast/bench/cases/units/lab/episodes.jsonl similarity index 100% rename from lpgcast/bench/cases/units/lab/episodes.jsonl rename to schemacast/bench/cases/units/lab/episodes.jsonl diff --git a/lpgcast/bench/cases/units/lab/expected.yaml b/schemacast/bench/cases/units/lab/expected.yaml similarity index 100% rename from lpgcast/bench/cases/units/lab/expected.yaml rename to schemacast/bench/cases/units/lab/expected.yaml diff --git a/lpgcast/bench/cases/units/logistics/case.yaml b/schemacast/bench/cases/units/logistics/case.yaml similarity index 100% rename from lpgcast/bench/cases/units/logistics/case.yaml rename to schemacast/bench/cases/units/logistics/case.yaml diff --git a/lpgcast/bench/cases/units/logistics/episodes.jsonl b/schemacast/bench/cases/units/logistics/episodes.jsonl similarity index 100% rename from lpgcast/bench/cases/units/logistics/episodes.jsonl rename to schemacast/bench/cases/units/logistics/episodes.jsonl diff --git a/lpgcast/bench/cases/units/logistics/expected.yaml b/schemacast/bench/cases/units/logistics/expected.yaml similarity index 100% rename from lpgcast/bench/cases/units/logistics/expected.yaml rename to schemacast/bench/cases/units/logistics/expected.yaml diff --git a/lpgcast/bench/cases/units/plant/case.yaml b/schemacast/bench/cases/units/plant/case.yaml similarity index 100% rename from lpgcast/bench/cases/units/plant/case.yaml rename to schemacast/bench/cases/units/plant/case.yaml diff --git a/lpgcast/bench/cases/units/plant/episodes.jsonl b/schemacast/bench/cases/units/plant/episodes.jsonl similarity index 100% rename from lpgcast/bench/cases/units/plant/episodes.jsonl rename to schemacast/bench/cases/units/plant/episodes.jsonl diff --git a/lpgcast/bench/cases/units/plant/expected.yaml b/schemacast/bench/cases/units/plant/expected.yaml similarity index 100% rename from lpgcast/bench/cases/units/plant/expected.yaml rename to schemacast/bench/cases/units/plant/expected.yaml diff --git a/lpgcast/bench/cli.py b/schemacast/bench/cli.py similarity index 87% rename from lpgcast/bench/cli.py rename to schemacast/bench/cli.py index b71dc42..30a2a2e 100644 --- a/lpgcast/bench/cli.py +++ b/schemacast/bench/cli.py @@ -1,4 +1,4 @@ -"""``lpgcast-bench``: cast the corpus and score it, or compare two runs.""" +"""``schemacast-bench``: cast the corpus and score it, or compare two runs.""" from __future__ import annotations @@ -8,14 +8,14 @@ import click -from lpgcast.bench.compare import CompareError, compare, load_report -from lpgcast.bench.compare import to_markdown as compare_markdown -from lpgcast.bench.corpus import CORPUS_VERSION, BenchCase, load_corpus -from lpgcast.bench.report import to_markdown, write_report -from lpgcast.bench.run import Row, load_prices, new_report, run_row -from lpgcast.config import LPGCastSettings -from lpgcast.llm.cache import CacheMissError -from lpgcast.llm.client import LLMConfigurationError +from schemacast.bench.compare import CompareError, compare, load_report +from schemacast.bench.compare import to_markdown as compare_markdown +from schemacast.bench.corpus import CORPUS_VERSION, BenchCase, load_corpus +from schemacast.bench.report import to_markdown, write_report +from schemacast.bench.run import Row, load_prices, new_report, run_row +from schemacast.config import SchemaCastSettings +from schemacast.llm.cache import CacheMissError +from schemacast.llm.client import LLMConfigurationError def select_cases( @@ -48,7 +48,7 @@ def parse_args(self, ctx: click.Context, args: list[str]) -> list[str]: def main() -> None: """Benchmark text-to-graph casting against the bundled corpus. - Without a subcommand, `run` is meant: `lpgcast-bench --model NAME`. + Without a subcommand, `run` is meant: `schemacast-bench --model NAME`. """ @@ -58,7 +58,7 @@ def main() -> None: "models", multiple=True, metavar="NAME", - help="Model to benchmark, repeatable; the endpoint is LPGCAST_LLM_BASE_URL. " + help="Model to benchmark, repeatable; the endpoint is SCHEMACAST_LLM_BASE_URL. " "Without it only the free oracle row runs.", ) @click.option( @@ -103,7 +103,7 @@ def run( concurrency: int, ) -> None: """Cast and score the corpus; write JSON and Markdown reports.""" - settings = LPGCastSettings() + settings = SchemaCastSettings() if replay: settings.cache = settings.cache.model_copy(update={"require_hit": True}) names = list(models) diff --git a/lpgcast/bench/compare.py b/schemacast/bench/compare.py similarity index 98% rename from lpgcast/bench/compare.py rename to schemacast/bench/compare.py index 8f7ee46..eaaf0bc 100644 --- a/lpgcast/bench/compare.py +++ b/schemacast/bench/compare.py @@ -17,10 +17,10 @@ from pydantic import BaseModel, Field -from lpgcast.bench.oracle import ORACLE -from lpgcast.bench.report import RATES, _fmt, _table, case_score -from lpgcast.bench.run import BenchReport, Row -from lpgcast.bench.score import Counts +from schemacast.bench.oracle import ORACLE +from schemacast.bench.report import RATES, _fmt, _table, case_score +from schemacast.bench.run import BenchReport, Row +from schemacast.bench.score import Counts #: Rates compared, in report order. HEADLINE: tuple[str, ...] = ( @@ -110,7 +110,7 @@ class CompareReport(BaseModel): def load_report(path: str | Path) -> BenchReport: - """A report written by ``lpgcast-bench``.""" + """A report written by ``schemacast-bench``.""" return BenchReport.model_validate_json(Path(path).read_text(encoding="utf-8")) diff --git a/lpgcast/bench/corpus.py b/schemacast/bench/corpus.py similarity index 99% rename from lpgcast/bench/corpus.py rename to schemacast/bench/corpus.py index c0833d0..ee1cd03 100644 --- a/lpgcast/bench/corpus.py +++ b/schemacast/bench/corpus.py @@ -26,7 +26,7 @@ from graflo.architecture.schema.document import Schema from pydantic import BaseModel, ConfigDict, Field, field_validator -from lpgcast.episode import Episode, read_episodes +from schemacast.episode import Episode, read_episodes BENCH_DIR = Path(__file__).parent CASES_DIR = BENCH_DIR / "cases" diff --git a/lpgcast/bench/oracle.py b/schemacast/bench/oracle.py similarity index 93% rename from lpgcast/bench/oracle.py rename to schemacast/bench/oracle.py index 51d9448..231c297 100644 --- a/lpgcast/bench/oracle.py +++ b/schemacast/bench/oracle.py @@ -14,10 +14,10 @@ import httpx -from lpgcast.bench.corpus import ExpectedEpisode, ExpectedProp -from lpgcast.config import CacheSettings, LLMSettings -from lpgcast.llm.cache import LLMCache -from lpgcast.llm.client import LLMClient +from schemacast.bench.corpus import ExpectedEpisode, ExpectedProp +from schemacast.config import CacheSettings, LLMSettings +from schemacast.llm.cache import LLMCache +from schemacast.llm.client import LLMClient ORACLE = "oracle" diff --git a/lpgcast/bench/report.py b/schemacast/bench/report.py similarity index 97% rename from lpgcast/bench/report.py rename to schemacast/bench/report.py index 0c20ac6..3cbc01f 100644 --- a/lpgcast/bench/report.py +++ b/schemacast/bench/report.py @@ -6,8 +6,8 @@ from collections.abc import Callable from pathlib import Path -from lpgcast.bench.run import BenchReport, CaseRecord, Row -from lpgcast.bench.score import Counts, total +from schemacast.bench.run import BenchReport, CaseRecord, Row +from schemacast.bench.score import Counts, total Rate = Callable[[Counts], float | None] @@ -135,11 +135,11 @@ def to_markdown(report: BenchReport) -> str: ) ) lines = [ - "# LPGCast benchmark", + "# SchemaCast benchmark", "", ( f"Corpus **{report.corpus_version}** · digest `{report.corpus_digest[:12]}…` · " - f"split **{report.split}** ({status}) · lpgcast {report.lpgcast_version} · " + f"split **{report.split}** ({status}) · schemacast {report.schemacast_version} · " f"graflo {report.graflo_version} · started {report.started}" ), "", diff --git a/lpgcast/bench/run.py b/schemacast/bench/run.py similarity index 85% rename from lpgcast/bench/run.py rename to schemacast/bench/run.py index db9633e..c77ea7c 100644 --- a/lpgcast/bench/run.py +++ b/schemacast/bench/run.py @@ -12,27 +12,27 @@ from pydantic import BaseModel, Field -from lpgcast import __version__ -from lpgcast.bench.corpus import ( +from schemacast import __version__ +from schemacast.bench.corpus import ( CORPUS_DIGEST, CORPUS_VERSION, BenchCase, ExpectedEpisode, corpus_digest, ) -from lpgcast.bench.oracle import ORACLE, oracle_client -from lpgcast.bench.score import Counts, score_episode, score_recast, total -from lpgcast.cast import LPGCaster -from lpgcast.config import LPGCastSettings -from lpgcast.episode import Episode -from lpgcast.llm.cache import LLMCache, cache_key_config, default_cache_dir -from lpgcast.llm.client import LLMClient -from lpgcast.llm.usage import track_usage +from schemacast.bench.oracle import ORACLE, oracle_client +from schemacast.bench.score import Counts, score_episode, score_recast, total +from schemacast.cast import SchemaCaster +from schemacast.config import SchemaCastSettings +from schemacast.episode import Episode +from schemacast.llm.cache import LLMCache, cache_key_config, default_cache_dir +from schemacast.llm.client import LLMClient +from schemacast.llm.usage import track_usage #: Environment variable naming a JSON price table: #: ``{"": {"input_per_1m": 0.15, "output_per_1m": 0.6}}``; keys starting #: with ``_`` are comments. No prices ship with the package. -PRICES_ENV = "LPGCAST_BENCH_PRICES" +PRICES_ENV = "SCHEMACAST_BENCH_PRICES" #: The second cast of a re-cast case is cached here, beside the first, so a #: replay of the benchmark repeats both without a request. @@ -89,7 +89,7 @@ class BenchReport(BaseModel): description="The cases differ from the pinned digest: not publishable." ) split: str - lpgcast_version: str = __version__ + schemacast_version: str = __version__ graflo_version: str = Field(default_factory=lambda: version("graflo")) started: str prices_source: str | None = None @@ -122,26 +122,26 @@ def price(counts: Counts, entry: dict[str, float] | None) -> float | None: ) / 1_000_000 -def case_settings(case: BenchCase, base: LPGCastSettings) -> LPGCastSettings: +def case_settings(case: BenchCase, base: SchemaCastSettings) -> SchemaCastSettings: """*base* with the settings *case* pins.""" pinned = case.settings.model_dump(include=case.settings.model_fields_set) return base.model_copy(update=pinned) -def recast_settings(base: LPGCastSettings) -> LPGCastSettings: +def recast_settings(base: SchemaCastSettings) -> SchemaCastSettings: """*base* with its own cache directory, so the second cast makes a fresh call.""" root = base.cache.dir or default_cache_dir() cache = base.cache.model_copy(update={"dir": root / RECAST_CACHE_SUBDIR}) return base.model_copy(update={"cache": cache}) -ClientFactory = Callable[[BenchCase, LPGCastSettings], LLMClient] +ClientFactory = Callable[[BenchCase, SchemaCastSettings], LLMClient] -def model_clients(settings: LPGCastSettings) -> ClientFactory: +def model_clients(settings: SchemaCastSettings) -> ClientFactory: """Clients for the configured model, one cache per settings.""" - def build(_case: BenchCase, case_settings_: LPGCastSettings) -> LLMClient: + def build(_case: BenchCase, case_settings_: SchemaCastSettings) -> LLMClient: return LLMClient(case_settings_.llm, LLMCache(case_settings_.cache)) return build @@ -150,15 +150,15 @@ def build(_case: BenchCase, case_settings_: LPGCastSettings) -> LLMClient: def oracle_clients() -> ClientFactory: """Oracle clients, scripted per case.""" - def build(case: BenchCase, _settings: LPGCastSettings) -> LLMClient: + def build(case: BenchCase, _settings: SchemaCastSettings) -> LLMClient: return oracle_client(case.expected) return build async def _episode( - caster: LPGCaster, - recaster: LPGCaster | None, + caster: SchemaCaster, + recaster: SchemaCaster | None, episode: Episode, expected: ExpectedEpisode, case: BenchCase, @@ -193,7 +193,7 @@ async def _episode( async def run_case( case: BenchCase, - settings: LPGCastSettings, + settings: SchemaCastSettings, clients: ClientFactory, *, concurrency: int = 8, @@ -205,11 +205,13 @@ async def run_case( CacheMissError: a miss while the cache requires hits. """ pinned = case_settings(case, settings) - caster = LPGCaster(case.schema_, settings=pinned, client=clients(case, pinned)) + caster = SchemaCaster(case.schema_, settings=pinned, client=clients(case, pinned)) recaster = None if case.recast: again = recast_settings(pinned) - recaster = LPGCaster(case.schema_, settings=again, client=clients(case, again)) + recaster = SchemaCaster( + case.schema_, settings=again, client=clients(case, again) + ) gate = asyncio.Semaphore(max(1, concurrency)) try: async with asyncio.TaskGroup() as group: @@ -242,7 +244,7 @@ async def run_case( async def run_row( cases: list[BenchCase], - settings: LPGCastSettings, + settings: SchemaCastSettings, *, oracle: bool = False, concurrency: int = 8, diff --git a/lpgcast/bench/schemas/helpdesk-distractors.yaml b/schemacast/bench/schemas/helpdesk-distractors.yaml similarity index 100% rename from lpgcast/bench/schemas/helpdesk-distractors.yaml rename to schemacast/bench/schemas/helpdesk-distractors.yaml diff --git a/lpgcast/bench/schemas/helpdesk.yaml b/schemacast/bench/schemas/helpdesk.yaml similarity index 100% rename from lpgcast/bench/schemas/helpdesk.yaml rename to schemacast/bench/schemas/helpdesk.yaml diff --git a/lpgcast/bench/schemas/lab-distractors.yaml b/schemacast/bench/schemas/lab-distractors.yaml similarity index 100% rename from lpgcast/bench/schemas/lab-distractors.yaml rename to schemacast/bench/schemas/lab-distractors.yaml diff --git a/lpgcast/bench/schemas/lab.yaml b/schemacast/bench/schemas/lab.yaml similarity index 100% rename from lpgcast/bench/schemas/lab.yaml rename to schemacast/bench/schemas/lab.yaml diff --git a/lpgcast/bench/schemas/logistics-distractors.yaml b/schemacast/bench/schemas/logistics-distractors.yaml similarity index 100% rename from lpgcast/bench/schemas/logistics-distractors.yaml rename to schemacast/bench/schemas/logistics-distractors.yaml diff --git a/lpgcast/bench/schemas/logistics.yaml b/schemacast/bench/schemas/logistics.yaml similarity index 100% rename from lpgcast/bench/schemas/logistics.yaml rename to schemacast/bench/schemas/logistics.yaml diff --git a/lpgcast/bench/schemas/plant-distractors.yaml b/schemacast/bench/schemas/plant-distractors.yaml similarity index 100% rename from lpgcast/bench/schemas/plant-distractors.yaml rename to schemacast/bench/schemas/plant-distractors.yaml diff --git a/lpgcast/bench/schemas/plant.yaml b/schemacast/bench/schemas/plant.yaml similarity index 100% rename from lpgcast/bench/schemas/plant.yaml rename to schemacast/bench/schemas/plant.yaml diff --git a/lpgcast/bench/score.py b/schemacast/bench/score.py similarity index 98% rename from lpgcast/bench/score.py rename to schemacast/bench/score.py index efe2be2..c78064d 100644 --- a/lpgcast/bench/score.py +++ b/schemacast/bench/score.py @@ -23,12 +23,12 @@ from graflo.architecture.schema.vertex import Vertex from pydantic import BaseModel -from lpgcast.bench.corpus import ExpectedEpisode, ExpectedGraph, ExpectedProp -from lpgcast.cast import EpisodeOutcome -from lpgcast.ground import Span, locate -from lpgcast.llm.usage import UsageTracker -from lpgcast.resolve import Resolution -from lpgcast.validate import CONSEQUENTIAL, Validation +from schemacast.bench.corpus import ExpectedEpisode, ExpectedGraph, ExpectedProp +from schemacast.cast import EpisodeOutcome +from schemacast.ground import Span, locate +from schemacast.llm.usage import UsageTracker +from schemacast.resolve import Resolution +from schemacast.validate import CONSEQUENTIAL, Validation #: Findings that are about grounding or follow from another, not about the schema. NOT_CONFORMANCE = frozenset({"quote_not_found", "value_not_in_quote"}) | CONSEQUENTIAL diff --git a/lpgcast/cast.py b/schemacast/cast.py similarity index 94% rename from lpgcast/cast.py rename to schemacast/cast.py index ec413ae..11869cd 100644 --- a/lpgcast/cast.py +++ b/schemacast/cast.py @@ -25,45 +25,45 @@ from openai import APIError from pydantic import BaseModel, ConfigDict, Field, ValidationError -from lpgcast import __version__ -from lpgcast.config import LPGCastSettings -from lpgcast.episode import Episode -from lpgcast.extract import ( +from schemacast import __version__ +from schemacast.config import SchemaCastSettings +from schemacast.episode import Episode +from schemacast.extract import ( INSTANCE_SCHEMA_NAME, EpisodeTooLongError, WireExtraction, WireShapeError, extract, ) -from lpgcast.ground import Grounding -from lpgcast.llm.cache import LLMCache, cache_key_config -from lpgcast.llm.client import ( +from schemacast.ground import Grounding +from schemacast.llm.cache import LLMCache, cache_key_config +from schemacast.llm.client import ( LLMClient, LLMRefusalError, LLMReplyError, LLMRequestTimeoutError, LLMTruncatedError, ) -from lpgcast.llm.jsonparse import LLMJsonParseError -from lpgcast.llm.usage import UsageTracker, track_usage -from lpgcast.prompt import build_messages -from lpgcast.repair import repair_messages -from lpgcast.resolve import ( +from schemacast.llm.jsonparse import LLMJsonParseError +from schemacast.llm.usage import UsageTracker, track_usage +from schemacast.prompt import build_messages +from schemacast.repair import repair_messages +from schemacast.resolve import ( PendingEdge, Resolution, merge_containers, resolve, schema_ref, ) -from lpgcast.select import ( +from schemacast.select import ( RetrievalMetrics, Selection, SelectionConfig, SelectionError, select, ) -from lpgcast.sink import MemoryQuarantineSink, QuarantineRecord, QuarantineSink -from lpgcast.validate import Finding, Fix, Validation, validate +from schemacast.sink import MemoryQuarantineSink, QuarantineRecord, QuarantineSink +from schemacast.validate import Finding, Fix, Validation, validate #: Version of the files a cast writes; bumped when a record's meaning changes. OUTPUT_FORMAT_VERSION = 2 @@ -133,7 +133,7 @@ class EpisodeResult(BaseModel): class RunManifest(BaseModel): """How a cast was produced.""" - lpgcast_version: str + schemacast_version: str graflo_version: str output_format_version: int = OUTPUT_FORMAT_VERSION schema_ref: str @@ -190,10 +190,10 @@ class EpisodeOutcome(BaseModel): def _extractor() -> str: - return f"lpgcast/{__version__}" + return f"schemacast/{__version__}" -class LPGCaster: +class SchemaCaster: """Casts episodes into one governed schema. Args: @@ -210,14 +210,14 @@ def __init__( self, schema: Schema, *, - settings: LPGCastSettings | None = None, + settings: SchemaCastSettings | None = None, client: LLMClient | None = None, sink: QuarantineSink | None = None, selection: SelectionConfig | None = None, max_concurrent_episodes: int = 8, ) -> None: self.schema = schema - self.settings = settings or LPGCastSettings() + self.settings = settings or SchemaCastSettings() self.client = client or LLMClient( self.settings.llm, LLMCache(self.settings.cache) ) @@ -463,7 +463,7 @@ def _assemble( for result in results: status[result.status] += 1 manifest = RunManifest( - lpgcast_version=__version__, + schemacast_version=__version__, graflo_version=version("graflo"), schema_ref=schema_ref(self.schema), model={ diff --git a/lpgcast/cli.py b/schemacast/cli.py similarity index 84% rename from lpgcast/cli.py rename to schemacast/cli.py index c0cda48..266151f 100644 --- a/lpgcast/cli.py +++ b/schemacast/cli.py @@ -1,4 +1,4 @@ -"""Command line: ``lpgcast`` (alias ``text2lpg``).""" +"""Command line: ``schemacast``.""" from __future__ import annotations @@ -10,17 +10,17 @@ import click from graflo.architecture.schema.context import instance_json_schema, render_type_sheet -from lpgcast import __version__ -from lpgcast.config import LPGCastSettings -from lpgcast.episode import Episode, read_episodes -from lpgcast.llm.cache import CacheMissError -from lpgcast.llm.client import LLMConfigurationError -from lpgcast.schema_io import load_schema -from lpgcast.select import SelectionConfig, SelectionError, select +from schemacast import __version__ +from schemacast.config import SchemaCastSettings +from schemacast.episode import Episode, read_episodes +from schemacast.llm.cache import CacheMissError +from schemacast.llm.client import LLMConfigurationError +from schemacast.schema_io import load_schema +from schemacast.select import SelectionConfig, SelectionError, select @click.group() -@click.version_option(__version__, prog_name="lpgcast") +@click.version_option(__version__, prog_name="schemacast") def main() -> None: """Cast text into a labeled property graph against a governed GraFlo schema.""" @@ -76,7 +76,7 @@ def select_command( """ if (text is None) == (episodes_path is None): raise click.UsageError("Give exactly one of --text or --episodes.") - settings = LPGCastSettings() + settings = SchemaCastSettings() config = SelectionConfig( max_tokens=settings.slice_max_tokens, max_elements=settings.slice_max_elements ) @@ -183,17 +183,17 @@ def cast( Episodes that produce nothing are quarantined and the run goes on; a misconfigured model, or a cache miss under --replay, stops it. """ - from lpgcast.cast import LPGCaster - from lpgcast.output import QUARANTINE_FILE, save_cast - from lpgcast.sink import JsonlQuarantineSink + from schemacast.cast import SchemaCaster + from schemacast.output import QUARANTINE_FILE, save_cast + from schemacast.sink import JsonlQuarantineSink - settings = LPGCastSettings() + settings = SchemaCastSettings() if replay: settings.cache = settings.cache.model_copy(update={"require_hit": True}) out_dir.mkdir(parents=True, exist_ok=True) quarantine = out_dir / QUARANTINE_FILE quarantine.unlink(missing_ok=True) - caster = LPGCaster( + caster = SchemaCaster( load_schema(schema_path), settings=settings, sink=JsonlQuarantineSink(quarantine), @@ -206,7 +206,7 @@ def cast( report = None if db_path is not None: - from lpgcast.write import load_connection, write_cast + from schemacast.write import load_connection, write_cast report = asyncio.run( write_cast( @@ -226,23 +226,23 @@ def cast( @main.command() @click.option( - "--host", help="Address to bind; default LPGCAST_SERVE_HOST or 127.0.0.1." + "--host", help="Address to bind; default SCHEMACAST_SERVE_HOST or 127.0.0.1." ) -@click.option("--port", type=int, help="Port; default LPGCAST_SERVE_PORT or 8997.") +@click.option("--port", type=int, help="Port; default SCHEMACAST_SERVE_PORT or 8997.") def serve(host: str | None, port: int | None) -> None: """Serve POST /cast over HTTP (needs the `server` extra). - Every request needs `Authorization: Bearer $LPGCAST_SERVE_API_KEY`; without - a key the service refuses to cast unless LPGCAST_SERVE_ALLOW_ANONYMOUS is - set. Model settings come from LPGCAST_LLM_*. + Every request needs `Authorization: Bearer $SCHEMACAST_SERVE_API_KEY`; without + a key the service refuses to cast unless SCHEMACAST_SERVE_ALLOW_ANONYMOUS is + set. Model settings come from SCHEMACAST_LLM_*. """ try: - from lpgcast.serve import run + from schemacast.serve import run except ImportError as error: raise click.ClickException( - f"{error.name or 'fastapi'} is missing: pip install 'lpgcast[server]'" + f"{error.name or 'fastapi'} is missing: pip install 'schemacast[server]'" ) from error - from lpgcast.config import ServeSettings + from schemacast.config import ServeSettings settings = ServeSettings() overrides = {k: v for k, v in (("host", host), ("port", port)) if v is not None} diff --git a/lpgcast/config.py b/schemacast/config.py similarity index 87% rename from lpgcast/config.py rename to schemacast/config.py index d47b202..2d4ee5c 100644 --- a/lpgcast/config.py +++ b/schemacast/config.py @@ -1,7 +1,7 @@ """Settings, read from the environment. -Three prefixes, none shared with other tools: ``LPGCAST_LLM_`` for the model, -``LPGCAST_CACHE_`` for the response cache and ``LPGCAST_`` for the rest. No +Three prefixes, none shared with other tools: ``SCHEMACAST_LLM_`` for the model, +``SCHEMACAST_CACHE_`` for the response cache and ``SCHEMACAST_`` for the rest. No ``.env`` file is read; export the variables or pass the settings objects. """ @@ -15,7 +15,7 @@ ResponseFormat = Literal["auto", "json_schema", "json_object"] -#: Model used when ``LPGCAST_LLM_MODEL`` is unset: an OpenAI model for high +#: Model used when ``SCHEMACAST_LLM_MODEL`` is unset: an OpenAI model for high #: volume. Any other endpoint needs its own model name. DEFAULT_MODEL = "gpt-5.6-luna" @@ -23,7 +23,7 @@ class LLMSettings(BaseSettings): """The model endpoint: any OpenAI-compatible server (OpenAI, Ollama ``/v1``, vLLM).""" - model_config = SettingsConfigDict(env_prefix="LPGCAST_LLM_", extra="ignore") + model_config = SettingsConfigDict(env_prefix="SCHEMACAST_LLM_", extra="ignore") model: str = Field( default=DEFAULT_MODEL, @@ -98,14 +98,14 @@ class LLMSettings(BaseSettings): class CacheSettings(BaseSettings): """The on-disk response cache.""" - model_config = SettingsConfigDict(env_prefix="LPGCAST_CACHE_", extra="ignore") + model_config = SettingsConfigDict(env_prefix="SCHEMACAST_CACHE_", extra="ignore") enabled: bool = Field(default=True, description="Read and write cached responses.") dir: Path | None = Field( default=None, description=( "Cache directory. Unset: ``./.test_cache`` under pytest, else " - "``$XDG_CACHE_HOME/lpgcast`` or ``~/.cache/lpgcast``." + "``$XDG_CACHE_HOME/schemacast`` or ``~/.cache/schemacast``." ), ) read_only: bool = Field( @@ -126,10 +126,10 @@ def _require_hit_needs_cache(self) -> CacheSettings: return self -class LPGCastSettings(BaseSettings): +class SchemaCastSettings(BaseSettings): """Top-level settings.""" - model_config = SettingsConfigDict(env_prefix="LPGCAST_", extra="ignore") + model_config = SettingsConfigDict(env_prefix="SCHEMACAST_", extra="ignore") llm: LLMSettings = Field(default_factory=LLMSettings) cache: CacheSettings = Field(default_factory=CacheSettings) @@ -155,9 +155,9 @@ class LPGCastSettings(BaseSettings): class ServeSettings(BaseSettings): - """``lpgcast serve``: the HTTP service.""" + """``schemacast serve``: the HTTP service.""" - model_config = SettingsConfigDict(env_prefix="LPGCAST_SERVE_", extra="ignore") + model_config = SettingsConfigDict(env_prefix="SCHEMACAST_SERVE_", extra="ignore") api_key: SecretStr | None = Field( default=None, diff --git a/lpgcast/episode.py b/schemacast/episode.py similarity index 100% rename from lpgcast/episode.py rename to schemacast/episode.py diff --git a/lpgcast/extract.py b/schemacast/extract.py similarity index 96% rename from lpgcast/extract.py rename to schemacast/extract.py index e1611a8..cbf529f 100644 --- a/lpgcast/extract.py +++ b/schemacast/extract.py @@ -14,9 +14,9 @@ from graflo.architecture.schema.document import Schema from pydantic import BaseModel, ConfigDict, Field, ValidationError -from lpgcast.episode import Episode -from lpgcast.llm.client import JsonCompletion, LLMClient -from lpgcast.prompt import build_messages +from schemacast.episode import Episode +from schemacast.llm.client import JsonCompletion, LLMClient +from schemacast.prompt import build_messages #: JSON Schema name sent with strict output requests. INSTANCE_SCHEMA_NAME = "lpg_instance" diff --git a/lpgcast/fidelity.py b/schemacast/fidelity.py similarity index 100% rename from lpgcast/fidelity.py rename to schemacast/fidelity.py diff --git a/lpgcast/ground.py b/schemacast/ground.py similarity index 97% rename from lpgcast/ground.py rename to schemacast/ground.py index b597575..90409c7 100644 --- a/lpgcast/ground.py +++ b/schemacast/ground.py @@ -1,6 +1,6 @@ """Groundings: where in the text each assertion was read. -The model copies a quote; LPGCast finds it in the episode and records the +The model copies a quote; SchemaCast finds it in the episode and records the character offsets. Offsets are never taken from the model. A quote that cannot be found is a hallucination flag, handled by validation. """ @@ -153,7 +153,7 @@ class Grounding(BaseModel): "for the episode's own text. Only identity values ground in context.", ) quote: str = Field(description="The text at the span, as it stands in the source.") - extractor: str = Field(description="``lpgcast/``.") + extractor: str = Field(description="``schemacast/``.") model: str | None = Field( default=None, description="Model that produced the reply." ) diff --git a/lpgcast/llm/__init__.py b/schemacast/llm/__init__.py similarity index 70% rename from lpgcast/llm/__init__.py rename to schemacast/llm/__init__.py index 1d53f92..ce9c9e1 100644 --- a/lpgcast/llm/__init__.py +++ b/schemacast/llm/__init__.py @@ -1,7 +1,7 @@ """The model client, its response cache, the tolerant JSON parser and usage accounting.""" -from lpgcast.llm.cache import CachedResponse, CacheMissError, LLMCache, cache_key -from lpgcast.llm.client import ( +from schemacast.llm.cache import CachedResponse, CacheMissError, LLMCache, cache_key +from schemacast.llm.client import ( ChatMessage, JsonCompletion, LLMClient, @@ -11,8 +11,8 @@ LLMRequestTimeoutError, LLMTruncatedError, ) -from lpgcast.llm.jsonparse import LLMJsonParseError, parse_json_object -from lpgcast.llm.usage import TokenUsage, UsageTracker, track_usage +from schemacast.llm.jsonparse import LLMJsonParseError, parse_json_object +from schemacast.llm.usage import TokenUsage, UsageTracker, track_usage __all__ = [ "CacheMissError", diff --git a/lpgcast/llm/cache.py b/schemacast/llm/cache.py similarity index 95% rename from lpgcast/llm/cache.py rename to schemacast/llm/cache.py index b7f3e1d..bb4a85c 100644 --- a/lpgcast/llm/cache.py +++ b/schemacast/llm/cache.py @@ -21,8 +21,8 @@ from pydantic import BaseModel, Field -from lpgcast.config import CacheSettings, LLMSettings -from lpgcast.llm.usage import TokenUsage +from schemacast.config import CacheSettings, LLMSettings +from schemacast.llm.usage import TokenUsage logger = logging.getLogger(__name__) @@ -63,10 +63,10 @@ def default_cache_dir() -> Path: return Path.cwd() / ".test_cache" cache_home = os.environ.get("XDG_CACHE_HOME") if cache_home: - return Path(cache_home) / "lpgcast" + return Path(cache_home) / "schemacast" if os.name == "nt": - return Path.home() / "AppData" / "Local" / "lpgcast" - return Path.home() / ".cache" / "lpgcast" + return Path.home() / "AppData" / "Local" / "schemacast" + return Path.home() / ".cache" / "schemacast" def cache_key_config(settings: LLMSettings) -> dict[str, Any]: diff --git a/lpgcast/llm/client.py b/schemacast/llm/client.py similarity index 97% rename from lpgcast/llm/client.py rename to schemacast/llm/client.py index 7eab4a5..f8d947c 100644 --- a/lpgcast/llm/client.py +++ b/schemacast/llm/client.py @@ -23,14 +23,14 @@ import openai from pydantic import BaseModel, Field -from lpgcast.config import LLMSettings, LPGCastSettings -from lpgcast.llm.cache import CachedResponse, CacheMissError, LLMCache, cache_key -from lpgcast.llm.jsonparse import ( +from schemacast.config import LLMSettings, SchemaCastSettings +from schemacast.llm.cache import CachedResponse, CacheMissError, LLMCache, cache_key +from schemacast.llm.jsonparse import ( LLMJsonParseError, feedback_excerpt, parse_json_object, ) -from lpgcast.llm.usage import ( +from schemacast.llm.usage import ( TokenUsage, UsageTracker, active_tracker, @@ -219,7 +219,10 @@ def __init__( @classmethod def from_settings( - cls, settings: LPGCastSettings, *, http_client: httpx.AsyncClient | None = None + cls, + settings: SchemaCastSettings, + *, + http_client: httpx.AsyncClient | None = None, ) -> LLMClient: """Build a client and its cache from top-level settings.""" return cls(settings.llm, LLMCache(settings.cache), http_client=http_client) @@ -242,7 +245,7 @@ def _provider(self) -> openai.AsyncOpenAI: key = self.settings.api_key if key is None or not key.get_secret_value(): raise LLMConfigurationError( - "No API key: set LPGCAST_LLM_API_KEY (any value for a local " + "No API key: set SCHEMACAST_LLM_API_KEY (any value for a local " "endpoint). Without one only cached responses are served." ) self._provider_client = openai.AsyncOpenAI( @@ -397,7 +400,7 @@ async def _complete_raw( backoff_attempt: int = 0, ) -> tuple[CachedResponse, bool]: if not self.settings.model: - raise LLMConfigurationError("No model: set LPGCAST_LLM_MODEL.") + raise LLMConfigurationError("No model: set SCHEMACAST_LLM_MODEL.") key = cache_key(messages, json_schema, self.settings) hit = await self.cache.aget(key) if hit is not None: diff --git a/lpgcast/llm/jsonparse.py b/schemacast/llm/jsonparse.py similarity index 100% rename from lpgcast/llm/jsonparse.py rename to schemacast/llm/jsonparse.py diff --git a/lpgcast/llm/usage.py b/schemacast/llm/usage.py similarity index 97% rename from lpgcast/llm/usage.py rename to schemacast/llm/usage.py index c7e295e..c0cc87e 100644 --- a/lpgcast/llm/usage.py +++ b/schemacast/llm/usage.py @@ -30,7 +30,7 @@ class TokenUsage(BaseModel): default=None, description=( "Prompt tokens served from the provider's prompt cache, counted " - "inside input_tokens. Unrelated to LPGCast's response cache." + "inside input_tokens. Unrelated to SchemaCast's response cache." ), ) @@ -131,7 +131,7 @@ def add_hit(self, usage: TokenUsage | None) -> None: _active_tracker: ContextVar[UsageTracker | None] = ContextVar( - "lpgcast_active_usage_tracker", default=None + "schemacast_active_usage_tracker", default=None ) diff --git a/lpgcast/output.py b/schemacast/output.py similarity index 98% rename from lpgcast/output.py rename to schemacast/output.py index ae526d1..899a6b3 100644 --- a/lpgcast/output.py +++ b/schemacast/output.py @@ -23,7 +23,7 @@ from pydantic import BaseModel -from lpgcast.cast import CastResult +from schemacast.cast import CastResult QUARANTINE_FILE = "quarantine.jsonl" diff --git a/lpgcast/prompt.py b/schemacast/prompt.py similarity index 98% rename from lpgcast/prompt.py rename to schemacast/prompt.py index f9b6553..45ef26a 100644 --- a/lpgcast/prompt.py +++ b/schemacast/prompt.py @@ -18,8 +18,8 @@ from graflo.architecture.schema.context import render_type_sheet from graflo.architecture.schema.document import Schema -from lpgcast.episode import Episode -from lpgcast.llm.client import ChatMessage +from schemacast.episode import Episode +from schemacast.llm.client import ChatMessage PREAMBLE = """\ # TASK diff --git a/lpgcast/repair.py b/schemacast/repair.py similarity index 97% rename from lpgcast/repair.py rename to schemacast/repair.py index 779b4fd..b908ab5 100644 --- a/lpgcast/repair.py +++ b/schemacast/repair.py @@ -10,8 +10,8 @@ import json -from lpgcast.llm.client import ChatMessage -from lpgcast.validate import FINDING_ORDER, Finding, FindingKind +from schemacast.llm.client import ChatMessage +from schemacast.validate import FINDING_ORDER, Finding, FindingKind GUIDANCE: dict[FindingKind, str] = { "unknown_type": "Use only types listed under TYPES. If no type fits, move the item to `unmapped`.", diff --git a/lpgcast/resolve.py b/schemacast/resolve.py similarity index 97% rename from lpgcast/resolve.py rename to schemacast/resolve.py index 5413970..564de17 100644 --- a/lpgcast/resolve.py +++ b/schemacast/resolve.py @@ -31,11 +31,11 @@ from graflo.architecture.schema.vertex import Vertex from pydantic import BaseModel, ConfigDict, Field -from lpgcast.episode import Episode -from lpgcast.ground import Grounding, Span -from lpgcast.validate import Validation, ValidVertex, mention_identity +from schemacast.episode import Episode +from schemacast.ground import Grounding, Span +from schemacast.validate import Validation, ValidVertex, mention_identity -#: Namespace of LPGCast's derived UUIDs. Never change it: every assigned and +#: Namespace of SchemaCast's derived UUIDs. Never change it: every assigned and #: blank key ever minted depends on it. MINT_NAMESPACE = uuid.UUID("6f1c3a52-6a51-4f8e-9e0e-2b8c1d7a4e10") diff --git a/lpgcast/schema_io.py b/schemacast/schema_io.py similarity index 100% rename from lpgcast/schema_io.py rename to schemacast/schema_io.py diff --git a/lpgcast/select.py b/schemacast/select.py similarity index 100% rename from lpgcast/select.py rename to schemacast/select.py diff --git a/lpgcast/serve.py b/schemacast/serve.py similarity index 75% rename from lpgcast/serve.py rename to schemacast/serve.py index 66cd6a0..9af2ae9 100644 --- a/lpgcast/serve.py +++ b/schemacast/serve.py @@ -1,15 +1,15 @@ -"""``lpgcast serve``: casting over HTTP, for a platform that holds the schema. +"""``schemacast serve``: casting over HTTP, for a platform that holds the schema. One stateless, synchronous route: ``POST /cast`` takes a schema inline, the -episodes and options, and returns what ``lpgcast cast --out`` writes — the +episodes and options, and returns what ``schemacast cast --out`` writes — the graph, groundings, pending edges, quarantine, episode results and the run manifest. The service never writes to a database and never calls back to the caller, so it holds no database credentials and needs no URL allow-list. The key is fail-closed: every route but ``/health`` needs -``Authorization: Bearer ``, and without a configured -key the service refuses to cast unless ``LPGCAST_SERVE_ALLOW_ANONYMOUS`` is -set. Model settings come from the service's own ``LPGCAST_LLM_*`` environment; +``Authorization: Bearer ``, and without a configured +key the service refuses to cast unless ``SCHEMACAST_SERVE_ALLOW_ANONYMOUS`` is +set. Model settings come from the service's own ``SCHEMACAST_LLM_*`` environment; a request cannot pick the model or the endpoint. Needs the ``server`` extra (FastAPI and uvicorn). @@ -24,16 +24,16 @@ from fastapi import Depends, FastAPI, Header, HTTPException from pydantic import BaseModel, ConfigDict, Field -from lpgcast import __version__ -from lpgcast.cast import EpisodeResult, LPGCaster, RunManifest -from lpgcast.config import LPGCastSettings, ServeSettings -from lpgcast.episode import Episode -from lpgcast.ground import Grounding -from lpgcast.llm.cache import CacheMissError, LLMCache -from lpgcast.llm.client import LLMClient, LLMConfigurationError -from lpgcast.resolve import PendingEdge -from lpgcast.schema_io import schema_from_mapping -from lpgcast.sink import MemoryQuarantineSink, QuarantineRecord +from schemacast import __version__ +from schemacast.cast import EpisodeResult, RunManifest, SchemaCaster +from schemacast.config import SchemaCastSettings, ServeSettings +from schemacast.episode import Episode +from schemacast.ground import Grounding +from schemacast.llm.cache import CacheMissError, LLMCache +from schemacast.llm.client import LLMClient, LLMConfigurationError +from schemacast.resolve import PendingEdge +from schemacast.schema_io import schema_from_mapping +from schemacast.sink import MemoryQuarantineSink, QuarantineRecord class CastOptions(BaseModel): @@ -58,7 +58,7 @@ class CastRequest(BaseModel): class CastResponse(BaseModel): - """What ``lpgcast cast --out`` writes, in one body.""" + """What ``schemacast cast --out`` writes, in one body.""" graph: dict[str, Any] = Field( description="graflo ``GraFloOutput``: schema and data." @@ -74,14 +74,14 @@ class Health(BaseModel): """``GET /health``.""" status: str = "ok" - lpgcast_version: str + schemacast_version: str graflo_version: str auth_configured: bool max_episodes: int def create_app( - settings: LPGCastSettings | None = None, + settings: SchemaCastSettings | None = None, serve: ServeSettings | None = None, *, client: LLMClient | None = None, @@ -94,10 +94,10 @@ def create_app( client: The model client shared by every request; defaults to one built from *settings*. """ - settings = settings or LPGCastSettings() + settings = settings or SchemaCastSettings() serve = serve or ServeSettings() shared = client or LLMClient(settings.llm, LLMCache(settings.cache)) - app = FastAPI(title="LPGCast", version=__version__) + app = FastAPI(title="SchemaCast", version=__version__) def require_key( authorization: Annotated[str | None, Header()] = None, @@ -108,8 +108,8 @@ def require_key( return raise HTTPException( 503, - "LPGCAST_SERVE_API_KEY is not configured. Set it, or set " - "LPGCAST_SERVE_ALLOW_ANONYMOUS=true for local development.", + "SCHEMACAST_SERVE_API_KEY is not configured. Set it, or set " + "SCHEMACAST_SERVE_ALLOW_ANONYMOUS=true for local development.", ) given = authorization or "" if not hmac.compare_digest(given.encode(), f"Bearer {expected}".encode()): @@ -118,7 +118,7 @@ def require_key( @app.get("/health") def health() -> Health: return Health( - lpgcast_version=__version__, + schemacast_version=__version__, graflo_version=version("graflo"), auth_configured=serve.api_key is not None, max_episodes=serve.max_episodes, @@ -139,7 +139,7 @@ async def cast(request: CastRequest) -> CastResponse: overrides = request.options.model_dump(exclude_none=True) run_settings = settings.model_copy(update=overrides) sink = MemoryQuarantineSink() - caster = LPGCaster(schema, settings=run_settings, client=shared, sink=sink) + caster = SchemaCaster(schema, settings=run_settings, client=shared, sink=sink) try: result = await caster.cast(request.episodes) except LLMConfigurationError as error: @@ -159,7 +159,7 @@ async def cast(request: CastRequest) -> CastResponse: def run(serve: ServeSettings | None = None) -> None: - """Serve on ``LPGCAST_SERVE_HOST``:``LPGCAST_SERVE_PORT``.""" + """Serve on ``SCHEMACAST_SERVE_HOST``:``SCHEMACAST_SERVE_PORT``.""" import uvicorn serve = serve or ServeSettings() diff --git a/lpgcast/sink.py b/schemacast/sink.py similarity index 100% rename from lpgcast/sink.py rename to schemacast/sink.py diff --git a/lpgcast/validate.py b/schemacast/validate.py similarity index 98% rename from lpgcast/validate.py rename to schemacast/validate.py index 6353f9f..d40c048 100644 --- a/lpgcast/validate.py +++ b/schemacast/validate.py @@ -29,10 +29,10 @@ from graflo.architecture.schema.vertex import Vertex from pydantic import BaseModel, Field -from lpgcast.episode import ContextItem -from lpgcast.extract import WireExtraction, WireProp -from lpgcast.fidelity import value_mismatch -from lpgcast.ground import Span, locate +from schemacast.episode import ContextItem +from schemacast.extract import WireExtraction, WireProp +from schemacast.fidelity import value_mismatch +from schemacast.ground import Span, locate FindingKind = Literal[ "duplicate_ref", diff --git a/lpgcast/write.py b/schemacast/write.py similarity index 98% rename from lpgcast/write.py rename to schemacast/write.py index ed22540..717ffab 100644 --- a/lpgcast/write.py +++ b/schemacast/write.py @@ -19,7 +19,7 @@ from graflo.connections.onto import DBConfig from graflo.hq.container_write import ContainerWriteReport - from lpgcast.cast import CastResult + from schemacast.cast import CastResult #: A secondary identity that matches several vertices names none of them. LOOKUP_AMBIGUITY = "skip" diff --git a/test/conftest.py b/test/conftest.py index 09de797..69193c1 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -13,8 +13,8 @@ import httpx import pytest -from lpgcast.config import CacheSettings, LLMSettings -from lpgcast.llm import LLMCache, LLMClient +from schemacast.config import CacheSettings, LLMSettings +from schemacast.llm import LLMCache, LLMClient DATA = Path(__file__).parent / "data" @@ -53,10 +53,10 @@ def pytest_collection_modifyitems( def isolated_env(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None: """No setting from the developer's shell, a cache per test, and no backoff sleeps.""" for name in list(os.environ): - if name.startswith("LPGCAST_") or name in _SHELL_VARS: + if name.startswith("SCHEMACAST_") or name in _SHELL_VARS: monkeypatch.delenv(name) - monkeypatch.setenv("LPGCAST_CACHE_DIR", str(tmp_path / "cache")) - monkeypatch.setattr("lpgcast.llm.client.RETRY_BACKOFF_BASE_SECONDS", 0.0) + monkeypatch.setenv("SCHEMACAST_CACHE_DIR", str(tmp_path / "cache")) + monkeypatch.setattr("schemacast.llm.client.RETRY_BACKOFF_BASE_SECONDS", 0.0) def completion( diff --git a/test/test_bench.py b/test/test_bench.py index 4e8474f..e13362b 100644 --- a/test/test_bench.py +++ b/test/test_bench.py @@ -9,8 +9,8 @@ import pytest from click.testing import CliRunner -from lpgcast.bench.cli import main as bench -from lpgcast.bench.corpus import ( +from schemacast.bench.cli import main as bench +from schemacast.bench.corpus import ( CORPUS_DIGEST, CORPUS_VERSION, BenchCase, @@ -18,14 +18,14 @@ corpus_digest, load_corpus, ) -from lpgcast.bench.oracle import oracle_reply -from lpgcast.bench.report import case_score, rates -from lpgcast.bench.run import Row, run_row -from lpgcast.bench.score import gold_vertices, score_episode, score_recast -from lpgcast.cast import LPGCaster -from lpgcast.config import CacheSettings, LPGCastSettings -from lpgcast.ground import locate -from lpgcast.validate import identity_fields, mention_identity +from schemacast.bench.oracle import oracle_reply +from schemacast.bench.report import case_score, rates +from schemacast.bench.run import Row, run_row +from schemacast.bench.score import gold_vertices, score_episode, score_recast +from schemacast.cast import SchemaCaster +from schemacast.config import CacheSettings, SchemaCastSettings +from schemacast.ground import locate +from schemacast.validate import identity_fields, mention_identity CORPUS = load_corpus() BY_NAME = {case.name: case for case in CORPUS} @@ -89,7 +89,7 @@ def test_the_expected_graph_is_in_the_text(case: BenchCase): assert gold.keys and gold.keys[0][0] == "id", handle -def _run(cases: list[BenchCase], settings: LPGCastSettings, **kwargs: Any) -> Row: +def _run(cases: list[BenchCase], settings: SchemaCastSettings, **kwargs: Any) -> Row: return asyncio.run(run_row(cases, settings, **kwargs)) @@ -97,7 +97,7 @@ def test_the_oracle_scores_perfectly_on_the_whole_schema(): unbudgeted = [ case.model_copy(update={"settings": CaseSettings()}) for case in CORPUS ] - settings = LPGCastSettings(slice_max_elements=None, slice_max_tokens=None) + settings = SchemaCastSettings(slice_max_elements=None, slice_max_tokens=None) row = _run(unbudgeted, settings, oracle=True) for case in row.cases: counts = case.counts @@ -114,7 +114,7 @@ def test_the_oracle_scores_perfectly_on_the_whole_schema(): def test_the_oracle_row_reads_selection_under_the_case_budget(): - row = _run([BY_NAME["distractors/plant"]], LPGCastSettings(), oracle=True) + row = _run([BY_NAME["distractors/plant"]], SchemaCastSettings(), oracle=True) counts = row.counts assert counts.sliced == 2 assert counts.gold_types == 4 @@ -126,7 +126,7 @@ def _expected_reply(case: BenchCase, source_id: str) -> dict[str, Any]: async def _cast_one(case: BenchCase, source_id: str, client: Any): - caster = LPGCaster(case.schema_, settings=LPGCastSettings(), client=client) + caster = SchemaCaster(case.schema_, settings=SchemaCastSettings(), client=client) episode = next(e for e in case.episodes if e.source_id == source_id) return await caster.cast_episode(episode), episode @@ -293,7 +293,7 @@ def test_the_command_needs_a_row(): def test_a_replay_miss_stops_the_run(tmp_path: Path, monkeypatch: pytest.MonkeyPatch): - monkeypatch.setenv("LPGCAST_LLM_API_KEY", "test-key") + monkeypatch.setenv("SCHEMACAST_LLM_API_KEY", "test-key") result = CliRunner().invoke( bench, [ diff --git a/test/test_bench_compare.py b/test/test_bench_compare.py index d8d817a..7b9fa5f 100644 --- a/test/test_bench_compare.py +++ b/test/test_bench_compare.py @@ -8,17 +8,17 @@ import pytest from click.testing import CliRunner -from lpgcast.bench.cli import main as bench -from lpgcast.bench.compare import CompareError, compare, to_markdown -from lpgcast.bench.corpus import load_corpus -from lpgcast.bench.run import BenchReport, Row, new_report, run_row -from lpgcast.config import LPGCastSettings +from schemacast.bench.cli import main as bench +from schemacast.bench.compare import CompareError, compare, to_markdown +from schemacast.bench.corpus import load_corpus +from schemacast.bench.run import BenchReport, Row, new_report, run_row +from schemacast.config import SchemaCastSettings @pytest.fixture(scope="module") def oracle_report() -> BenchReport: cases = [case for case in load_corpus() if case.family in ("direction", "units")] - row = asyncio.run(run_row(cases, LPGCastSettings(), oracle=True)) + row = asyncio.run(run_row(cases, SchemaCastSettings(), oracle=True)) return new_report("dev", "2026-01-01T00:00:00+00:00", [row], None) diff --git a/test/test_cache.py b/test/test_cache.py index f15f300..3c6c389 100644 --- a/test/test_cache.py +++ b/test/test_cache.py @@ -1,7 +1,7 @@ """The response cache: keys, atomic entries, read-only and disabled modes.""" -from lpgcast.config import CacheSettings, LLMSettings -from lpgcast.llm.cache import CachedResponse, LLMCache, cache_key, default_cache_dir +from schemacast.config import CacheSettings, LLMSettings +from schemacast.llm.cache import CachedResponse, LLMCache, cache_key, default_cache_dir SCHEMA = {"type": "object"} MESSAGES = [{"role": "user", "content": "hi"}] diff --git a/test/test_cast.py b/test/test_cast.py index 62dfc8c..2da693a 100644 --- a/test/test_cast.py +++ b/test/test_cast.py @@ -8,14 +8,14 @@ import pytest from conftest import DATA, completion, error -from lpgcast.cast import LPGCaster -from lpgcast.config import CacheSettings, LPGCastSettings -from lpgcast.episode import Episode -from lpgcast.llm import CacheMissError, LLMConfigurationError -from lpgcast.output import save_cast -from lpgcast.schema_io import load_schema -from lpgcast.select import SelectionConfig -from lpgcast.sink import JsonlQuarantineSink, MemoryQuarantineSink +from schemacast.cast import SchemaCaster +from schemacast.config import CacheSettings, SchemaCastSettings +from schemacast.episode import Episode +from schemacast.llm import CacheMissError, LLMConfigurationError +from schemacast.output import save_cast +from schemacast.schema_io import load_schema +from schemacast.select import SelectionConfig +from schemacast.sink import JsonlQuarantineSink, MemoryQuarantineSink ANN = Episode( source_id="m1", @@ -111,7 +111,7 @@ def make_caster(make_client, plant): def _build(routes, **kwargs): client, endpoint = make_client(routes=routes, **kwargs.pop("client", {})) sink = kwargs.pop("sink", MemoryQuarantineSink()) - caster = LPGCaster(plant, client=client, sink=sink, **kwargs) + caster = SchemaCaster(plant, client=client, sink=sink, **kwargs) return caster, endpoint, sink return _build @@ -273,8 +273,8 @@ async def test_selection_failure_is_an_episode_record(make_caster): async def test_too_long_episode_is_an_episode_record(make_caster, monkeypatch): - monkeypatch.setenv("LPGCAST_MAX_EPISODE_TOKENS", "5") - caster, endpoint, _sink = make_caster({}, settings=LPGCastSettings()) + monkeypatch.setenv("SCHEMACAST_MAX_EPISODE_TOKENS", "5") + caster, endpoint, _sink = make_caster({}, settings=SchemaCastSettings()) result = await caster.cast([ANN]) assert result.episodes[0].failure == "too_long" assert endpoint.requests == [] @@ -348,7 +348,7 @@ async def test_replay_of_an_unseen_episode_stops_the_run(make_caster): async def test_an_identity_from_context_is_grounded_there(make_caster): - from lpgcast.episode import ContextItem + from schemacast.episode import ContextItem episode = Episode( source_id="m4", diff --git a/test/test_cli.py b/test/test_cli.py index 2d0be73..ad275ab 100644 --- a/test/test_cli.py +++ b/test/test_cli.py @@ -1,12 +1,11 @@ -"""The lpgcast command line.""" +"""The schemacast command line.""" import json -from importlib.metadata import entry_points from click.testing import CliRunner from conftest import DATA -from lpgcast.cli import main +from schemacast.cli import main def test_sheet_prints_the_type_sheet(): @@ -37,11 +36,6 @@ def test_sheet_prints_the_reply_schema(): assert vertex["properties"]["type"]["enum"] == ["Machine", "Person", "WorkOrder"] -def test_text2lpg_is_an_alias(): - scripts = {ep.name: ep.value for ep in entry_points(group="console_scripts")} - assert scripts["text2lpg"] == scripts["lpgcast"] == "lpgcast.cli:main" - - def test_select_prints_one_json_line_per_episode(): result = CliRunner().invoke( main, @@ -61,7 +55,7 @@ def test_select_prints_one_json_line_per_episode(): def test_select_reports_a_failure_and_exits_non_zero(monkeypatch): - monkeypatch.setenv("LPGCAST_SLICE_MAX_TOKENS", "10") + monkeypatch.setenv("SCHEMACAST_SLICE_MAX_TOKENS", "10") result = CliRunner().invoke( main, ["select", "--schema", str(DATA / "schema.yaml"), "--text", "zzz"] ) @@ -79,12 +73,12 @@ def test_cast_replays_saves_and_writes(make_client, tmp_path, monkeypatch): from test_cast import ANN, ANN_REPLY, NOTE, NOTE_REPLY - from lpgcast.cast import LPGCaster - from lpgcast.schema_io import load_schema + from schemacast.cast import SchemaCaster + from schemacast.schema_io import load_schema plant = DATA / "plant.yaml" client, _ = make_client(routes={"m1": [ANN_REPLY], "m2": [NOTE_REPLY]}) - asyncio.run(LPGCaster(load_schema(plant), client=client).cast([ANN, NOTE])) + asyncio.run(SchemaCaster(load_schema(plant), client=client).cast([ANN, NOTE])) episodes = tmp_path / "episodes.jsonl" episodes.write_text( @@ -92,8 +86,8 @@ def test_cast_replays_saves_and_writes(make_client, tmp_path, monkeypatch): ) conn = tmp_path / "conn.yaml" conn.write_text(f"db_type: graflo_backend\noutput_dir: {tmp_path / 'db'}\n") - monkeypatch.setenv("LPGCAST_LLM_MODEL", client.settings.model) - monkeypatch.setenv("LPGCAST_LLM_BASE_URL", client.settings.base_url or "") + monkeypatch.setenv("SCHEMACAST_LLM_MODEL", client.settings.model) + monkeypatch.setenv("SCHEMACAST_LLM_BASE_URL", client.settings.base_url or "") out = tmp_path / "out" result = CliRunner().invoke( @@ -132,7 +126,7 @@ def test_cast_replays_saves_and_writes(make_client, tmp_path, monkeypatch): def test_cast_replay_miss_is_a_clean_error(tmp_path, monkeypatch): episodes = tmp_path / "episodes.jsonl" episodes.write_text('{"source_id": "x", "text": "press S-7 at 200 kW"}\n') - monkeypatch.setenv("LPGCAST_LLM_MODEL", "fake-model") + monkeypatch.setenv("SCHEMACAST_LLM_MODEL", "fake-model") result = CliRunner().invoke( main, [ diff --git a/test/test_client.py b/test/test_client.py index 8d04267..960e907 100644 --- a/test/test_client.py +++ b/test/test_client.py @@ -9,8 +9,8 @@ import pytest from conftest import MESSAGES, SCHEMA, completion, error -from lpgcast.config import CacheSettings -from lpgcast.llm import ( +from schemacast.config import CacheSettings +from schemacast.llm import ( CacheMissError, LLMConfigurationError, LLMJsonParseError, @@ -19,7 +19,7 @@ LLMTruncatedError, track_usage, ) -from lpgcast.llm.client import SCHEMA_INSTRUCTION +from schemacast.llm.client import SCHEMA_INSTRUCTION SCHEMA_REJECTED = error( 400, "Invalid parameter: 'response_format' of type 'json_schema' is not supported" @@ -101,14 +101,14 @@ async def test_require_hit_raises_on_a_miss(make_client): async def test_no_key_fails_closed(make_client): client, endpoint = make_client({"answer": "x"}, api_key=None) - with pytest.raises(LLMConfigurationError, match="LPGCAST_LLM_API_KEY"): + with pytest.raises(LLMConfigurationError, match="SCHEMACAST_LLM_API_KEY"): await client.acomplete_json(MESSAGES, SCHEMA) assert endpoint.requests == [] async def test_no_model_fails_closed(make_client): client, endpoint = make_client({"answer": "x"}, model="") - with pytest.raises(LLMConfigurationError, match="LPGCAST_LLM_MODEL"): + with pytest.raises(LLMConfigurationError, match="SCHEMACAST_LLM_MODEL"): await client.acomplete_json(MESSAGES, SCHEMA) assert endpoint.requests == [] diff --git a/test/test_config.py b/test/test_config.py index 3962572..11289ca 100644 --- a/test/test_config.py +++ b/test/test_config.py @@ -1,19 +1,24 @@ -"""Settings come from LPGCAST_* variables only.""" +"""Settings come from SCHEMACAST_* variables only.""" import pytest from pydantic import ValidationError -from lpgcast.config import DEFAULT_MODEL, CacheSettings, LLMSettings, LPGCastSettings +from schemacast.config import ( + DEFAULT_MODEL, + CacheSettings, + LLMSettings, + SchemaCastSettings, +) def test_reads_its_own_prefixes(monkeypatch, tmp_path): - monkeypatch.setenv("LPGCAST_LLM_MODEL", "qwen3:8b") - monkeypatch.setenv("LPGCAST_LLM_BASE_URL", "http://localhost:11434/v1") - monkeypatch.setenv("LPGCAST_LLM_API_KEY", "local") - monkeypatch.setenv("LPGCAST_CACHE_REQUIRE_HIT", "true") - monkeypatch.setenv("LPGCAST_CACHE_DIR", str(tmp_path)) + monkeypatch.setenv("SCHEMACAST_LLM_MODEL", "qwen3:8b") + monkeypatch.setenv("SCHEMACAST_LLM_BASE_URL", "http://localhost:11434/v1") + monkeypatch.setenv("SCHEMACAST_LLM_API_KEY", "local") + monkeypatch.setenv("SCHEMACAST_CACHE_REQUIRE_HIT", "true") + monkeypatch.setenv("SCHEMACAST_CACHE_DIR", str(tmp_path)) - settings = LPGCastSettings() + settings = SchemaCastSettings() assert settings.llm.model == "qwen3:8b" assert settings.llm.base_url == "http://localhost:11434/v1" assert settings.llm.api_key is not None diff --git a/test/test_dependency_weight.py b/test/test_dependency_weight.py index 016d894..92f9c00 100644 --- a/test/test_dependency_weight.py +++ b/test/test_dependency_weight.py @@ -1,4 +1,4 @@ -"""LPGCast stays light: six runtime dependencies and a small import footprint.""" +"""SchemaCast stays light: six runtime dependencies and a small import footprint.""" import re import subprocess @@ -7,11 +7,11 @@ RUNTIME = {"click", "graflo", "openai", "pydantic", "pydantic-settings", "pyyaml"} -#: Never installed alongside LPGCast. +#: Never installed alongside SchemaCast. NOT_INSTALLED = ("langchain", "langgraph", "torch") -#: Never loaded by importing LPGCast. graflo installs some of these for its own -#: database and RDF features; LPGCast's import path must not reach them. +#: Never loaded by importing SchemaCast. graflo installs some of these for its own +#: database and RDF features; SchemaCast's import path must not reach them. NOT_IMPORTED = ( "langchain", "langchain_core", @@ -30,7 +30,7 @@ def _name(requirement: str) -> str: def test_declares_exactly_the_runtime_dependencies(): declared = { - _name(req) for req in requires("lpgcast") or [] if "extra ==" not in req + _name(req) for req in requires("schemacast") or [] if "extra ==" not in req } assert declared == RUNTIME @@ -49,11 +49,11 @@ def test_heavy_frameworks_are_not_installed(): def test_import_loads_nothing_heavy(): probe = ( - "import sys, lpgcast, lpgcast.cli, lpgcast.config, lpgcast.llm, lpgcast.episode, " - "lpgcast.select, lpgcast.prompt, lpgcast.extract, lpgcast.validate, lpgcast.repair, " - "lpgcast.resolve, lpgcast.ground, lpgcast.sink, lpgcast.cast, lpgcast.output, lpgcast.write, " - "lpgcast.schema_io, lpgcast.bench.cli, lpgcast.bench.corpus, lpgcast.bench.score, " - "lpgcast.bench.oracle, lpgcast.bench.run, lpgcast.bench.report\n" + "import sys, schemacast, schemacast.cli, schemacast.config, schemacast.llm, schemacast.episode, " + "schemacast.select, schemacast.prompt, schemacast.extract, schemacast.validate, schemacast.repair, " + "schemacast.resolve, schemacast.ground, schemacast.sink, schemacast.cast, schemacast.output, schemacast.write, " + "schemacast.schema_io, schemacast.bench.cli, schemacast.bench.corpus, schemacast.bench.score, " + "schemacast.bench.oracle, schemacast.bench.run, schemacast.bench.report\n" f"print(','.join(m for m in {NOT_IMPORTED!r} if m in sys.modules))" ) loaded = subprocess.run( @@ -64,10 +64,10 @@ def test_import_loads_nothing_heavy(): def test_the_top_level_names_resolve_lazily(): probe = ( - "import sys, lpgcast\n" - "assert 'lpgcast.cast' not in sys.modules\n" - "from lpgcast import LPGCaster, load_schema, read_episodes, write_cast\n" - "assert 'lpgcast.cast' in sys.modules\n" + "import sys, schemacast\n" + "assert 'schemacast.cast' not in sys.modules\n" + "from schemacast import SchemaCaster, load_schema, read_episodes, write_cast\n" + "assert 'schemacast.cast' in sys.modules\n" "print(','.join(m for m in " f"{NOT_IMPORTED!r} if m in sys.modules))" ) @@ -78,8 +78,8 @@ def test_the_top_level_names_resolve_lazily(): def test_every_lazy_name_is_exported_and_resolves(): - import lpgcast + import schemacast - assert set(lpgcast.__all__) == {"__version__", *lpgcast._EXPORTS} - for name in lpgcast._EXPORTS: - assert getattr(lpgcast, name) is not None + assert set(schemacast.__all__) == {"__version__", *schemacast._EXPORTS} + for name in schemacast._EXPORTS: + assert getattr(schemacast, name) is not None diff --git a/test/test_episode.py b/test/test_episode.py index 79cc98f..d221875 100644 --- a/test/test_episode.py +++ b/test/test_episode.py @@ -3,7 +3,7 @@ import pytest from conftest import DATA -from lpgcast.episode import Episode, EpisodeFileError, read_episodes +from schemacast.episode import Episode, EpisodeFileError, read_episodes def test_reads_episodes_and_skips_blank_lines(): diff --git a/test/test_examples.py b/test/test_examples.py index 02fce31..c7a2ed9 100644 --- a/test/test_examples.py +++ b/test/test_examples.py @@ -3,9 +3,9 @@ from pathlib import Path -from lpgcast.episode import read_episodes -from lpgcast.schema_io import load_schema -from lpgcast.select import select +from schemacast.episode import read_episodes +from schemacast.schema_io import load_schema +from schemacast.select import select EXAMPLES = Path(__file__).parents[1] / "examples" diff --git a/test/test_extract.py b/test/test_extract.py index a9207a0..488bce0 100644 --- a/test/test_extract.py +++ b/test/test_extract.py @@ -5,16 +5,16 @@ from conftest import DATA from graflo.architecture.schema.context import instance_json_schema -from lpgcast.config import CacheSettings -from lpgcast.episode import Episode -from lpgcast.extract import ( +from schemacast.config import CacheSettings +from schemacast.episode import Episode +from schemacast.extract import ( INSTANCE_SCHEMA_NAME, EpisodeTooLongError, WireShapeError, extract, ) -from lpgcast.prompt import build_messages -from lpgcast.schema_io import load_schema +from schemacast.prompt import build_messages +from schemacast.schema_io import load_schema REPLY = { "vertices": [ diff --git a/test/test_fidelity.py b/test/test_fidelity.py index 7796ab3..7f2bf82 100644 --- a/test/test_fidelity.py +++ b/test/test_fidelity.py @@ -3,7 +3,7 @@ import pytest from graflo.architecture.schema.vertex import Field -from lpgcast.fidelity import value_mismatch +from schemacast.fidelity import value_mismatch FLOAT = Field(name="power", type="FLOAT") INT = Field(name="count", type="INT") diff --git a/test/test_ground.py b/test/test_ground.py index b54f133..5d7273b 100644 --- a/test/test_ground.py +++ b/test/test_ground.py @@ -1,8 +1,8 @@ -"""Quote location: offsets computed by LPGCast, never taken from the model.""" +"""Quote location: offsets computed by SchemaCast, never taken from the model.""" import pytest -from lpgcast.ground import locate +from schemacast.ground import locate TEXT = "Ann operates\nPress S-7, rated 200 kW." diff --git a/test/test_jsonparse.py b/test/test_jsonparse.py index a36354c..9bf9246 100644 --- a/test/test_jsonparse.py +++ b/test/test_jsonparse.py @@ -2,7 +2,7 @@ import pytest -from lpgcast.llm.jsonparse import ( +from schemacast.llm.jsonparse import ( LLMJsonParseError, feedback_excerpt, parse_json_object, diff --git a/test/test_prompt.py b/test/test_prompt.py index 1f47f25..2e61cb4 100644 --- a/test/test_prompt.py +++ b/test/test_prompt.py @@ -6,9 +6,9 @@ from conftest import DATA from graflo.architecture.schema.context import render_type_sheet -from lpgcast.episode import ContextItem, Episode -from lpgcast.prompt import DATA_SENTENCE, build_messages -from lpgcast.schema_io import load_schema +from schemacast.episode import ContextItem, Episode +from schemacast.prompt import DATA_SENTENCE, build_messages +from schemacast.schema_io import load_schema @pytest.fixture diff --git a/test/test_repair.py b/test/test_repair.py index 32f25c4..3ad38b2 100644 --- a/test/test_repair.py +++ b/test/test_repair.py @@ -1,8 +1,8 @@ """The typed repair prompt.""" -from lpgcast.llm.client import ChatMessage -from lpgcast.repair import INTRO, repair_messages, repair_prompt -from lpgcast.validate import Finding +from schemacast.llm.client import ChatMessage +from schemacast.repair import INTRO, repair_messages, repair_prompt +from schemacast.validate import Finding def _finding(kind, path, **kwargs): diff --git a/test/test_resolve.py b/test/test_resolve.py index 07ec70f..bc06f58 100644 --- a/test/test_resolve.py +++ b/test/test_resolve.py @@ -6,11 +6,11 @@ from conftest import DATA from graflo.architecture.schema.identity_digest import compute_vertex_identity -from lpgcast.episode import Episode -from lpgcast.extract import WireExtraction -from lpgcast.resolve import merge_containers, mint_id, resolve -from lpgcast.schema_io import load_schema -from lpgcast.validate import validate +from schemacast.episode import Episode +from schemacast.extract import WireExtraction +from schemacast.resolve import merge_containers, mint_id, resolve +from schemacast.schema_io import load_schema +from schemacast.validate import validate TEXT = ( "Ann (ann@x.com) operates press S-7. Sensor T-1 is on S-7. " @@ -77,7 +77,7 @@ def _resolve(plant, reply=REPLY, source_id="msg-1"): episode = Episode(text=TEXT, source_id=source_id) validation = validate(WireExtraction.model_validate(reply), plant, TEXT) assert validation.findings == [] - return resolve(validation, plant, episode, extractor="lpgcast/test", model="m") + return resolve(validation, plant, episode, extractor="schemacast/test", model="m") def test_vertex_documents_carry_their_keys(plant): @@ -147,7 +147,7 @@ def test_every_assertion_is_grounded_at_its_quote(plant): for grounding in groundings: assert TEXT[grounding.char_start : grounding.char_end] == grounding.quote assert grounding.schema_ref == "plant@2.0.0" - assert grounding.extractor == "lpgcast/test" + assert grounding.extractor == "schemacast/test" edge = next(g for g in groundings if g.kind == "edge" and g.edge[0] == "Sensor") assert edge.target_identity == {"serial": "S-7"} diff --git a/test/test_select.py b/test/test_select.py index de78203..921e95a 100644 --- a/test/test_select.py +++ b/test/test_select.py @@ -9,8 +9,8 @@ from graflo.architecture.schema.semantics import FieldSemantics, Semantics from graflo.architecture.schema.vertex import Field, Vertex, VertexConfig -from lpgcast.schema_io import load_schema -from lpgcast.select import ( +from schemacast.schema_io import load_schema +from schemacast.select import ( SelectionConfig, SelectionError, number_adjacent_tokens, diff --git a/test/test_serve.py b/test/test_serve.py index 702b18f..1e524f9 100644 --- a/test/test_serve.py +++ b/test/test_serve.py @@ -1,4 +1,4 @@ -"""``lpgcast serve``: POST /cast against a scripted model.""" +"""``schemacast serve``: POST /cast against a scripted model.""" import subprocess import sys @@ -9,8 +9,8 @@ from fastapi.testclient import TestClient from test_cast import ANN, ANN_REPLY, NOTE, NOTE_REPLY -from lpgcast.config import LPGCastSettings, ServeSettings -from lpgcast.serve import create_app +from schemacast.config import SchemaCastSettings, ServeSettings +from schemacast.serve import create_app KEY = "s3cret" AUTH = {"Authorization": f"Bearer {KEY}"} @@ -30,7 +30,7 @@ def make_service(make_client): def _build(routes=None, serve=None, **client): llm, endpoint = make_client(routes=routes or {}, **client) app = create_app( - LPGCastSettings(), + SchemaCastSettings(), serve or ServeSettings(api_key=KEY), client=llm, ) @@ -74,7 +74,7 @@ def test_no_configured_key_fails_closed(make_service): service, endpoint = make_service(serve=ServeSettings()) response = service.post("/cast", json=_body(ANN), headers=AUTH) assert response.status_code == 503 - assert "LPGCAST_SERVE_API_KEY" in response.json()["detail"] + assert "SCHEMACAST_SERVE_API_KEY" in response.json()["detail"] assert endpoint.requests == [] @@ -121,7 +121,7 @@ def test_a_misconfigured_model_is_a_service_error(make_service): def test_importing_the_service_loads_nothing_heavy(): probe = ( - "import sys, lpgcast.serve\n" + "import sys, schemacast.serve\n" "print(','.join(m for m in ('numpy', 'pandas', 'rdflib', 'torch') " "if m in sys.modules))" ) diff --git a/test/test_validate.py b/test/test_validate.py index 620541a..49f0b06 100644 --- a/test/test_validate.py +++ b/test/test_validate.py @@ -6,9 +6,9 @@ import pytest from conftest import DATA -from lpgcast.extract import WireExtraction -from lpgcast.schema_io import load_schema -from lpgcast.validate import validate +from schemacast.extract import WireExtraction +from schemacast.schema_io import load_schema +from schemacast.validate import validate TEXT = "Ann (ann@x.com) operates press S-7, rated 200 kW, since 2024-03. Call +1 555." @@ -264,7 +264,7 @@ def test_a_date_part_its_quote_does_not_state_is_a_finding(plant): def test_an_identity_value_grounds_in_context(plant): - from lpgcast.episode import ContextItem + from schemacast.episode import ContextItem text = "The press is leaking again." context = [ContextItem(source_id="m0", text="Press S-7 was serviced on Monday.")] @@ -288,7 +288,7 @@ def test_an_identity_value_grounds_in_context(plant): def test_a_non_identity_value_does_not_ground_in_context(plant): - from lpgcast.episode import ContextItem + from schemacast.episode import ContextItem text = "Press S-7 is leaking again." context = [ContextItem(source_id="m0", text="It is rated 200 kW.")] diff --git a/test/test_write.py b/test/test_write.py index eec99e2..e8544d5 100644 --- a/test/test_write.py +++ b/test/test_write.py @@ -9,10 +9,10 @@ from graflo.connections.onto import Neo4jConfig from graflo.db.manager import ConnectionManager -from lpgcast.cast import LPGCaster -from lpgcast.episode import Episode -from lpgcast.schema_io import load_schema -from lpgcast.write import load_connection, write_cast +from schemacast.cast import SchemaCaster +from schemacast.episode import Episode +from schemacast.schema_io import load_schema +from schemacast.write import load_connection, write_cast #: Path to a graflo connection YAML for a disposable Neo4j database. The test #: that uses it redefines the schema there, so never point it at real data. @@ -84,7 +84,7 @@ def plant(): def cast(make_client, plant): async def _cast(episodes, routes): client, _ = make_client(routes=routes) - return await LPGCaster(plant, client=client).cast(episodes) + return await SchemaCaster(plant, client=client).cast(episodes) return _cast diff --git a/uv.lock b/uv.lock index 73e517b..cd600c5 100644 --- a/uv.lock +++ b/uv.lock @@ -581,7 +581,7 @@ wheels = [ [[package]] name = "graflo" -version = "1.18.0" +version = "1.18.2" source = { editable = "../graflo" } dependencies = [ { name = "boto3" }, @@ -1175,79 +1175,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437, upload-time = "2025-09-08T01:34:57.871Z" }, ] -[[package]] -name = "lpgcast" -version = "0.2.0" -source = { editable = "." } -dependencies = [ - { name = "click" }, - { name = "graflo" }, - { name = "openai" }, - { name = "pydantic" }, - { name = "pydantic-settings" }, - { name = "pyyaml" }, -] - -[package.optional-dependencies] -server = [ - { name = "fastapi" }, - { name = "uvicorn", extra = ["standard"] }, -] - -[package.dev-dependencies] -dev = [ - { name = "fastapi" }, - { name = "jsonschema" }, - { name = "pre-commit" }, - { name = "pytest" }, - { name = "pytest-asyncio" }, - { name = "ty" }, - { name = "uvicorn" }, -] -docs = [ - { name = "mkdocs-gen-files" }, - { name = "mkdocs-glightbox" }, - { name = "mkdocs-literate-nav" }, - { name = "mkdocs-material" }, - { name = "mkdocstrings", extra = ["python"] }, - { name = "mkdocstrings-python" }, - { name = "properdocs" }, -] - -[package.metadata] -requires-dist = [ - { name = "click", specifier = ">=8.2.0,<9" }, - { name = "fastapi", marker = "extra == 'server'", specifier = ">=0.134.0,<1" }, - { name = "graflo", editable = "../graflo" }, - { name = "openai", specifier = ">=1.109.0,<3" }, - { name = "pydantic", specifier = ">=2.12.5" }, - { name = "pydantic-settings", specifier = ">=2.12.0" }, - { name = "pyyaml", specifier = ">=6.0" }, - { name = "uvicorn", extras = ["standard"], marker = "extra == 'server'", specifier = ">=0.30.0,<1" }, -] -provides-extras = ["embed", "server"] - -[package.metadata.requires-dev] -bench = [] -dev = [ - { name = "fastapi", specifier = ">=0.134.0,<1" }, - { name = "jsonschema", specifier = ">=4.23.0" }, - { name = "pre-commit", specifier = ">=4.6.2" }, - { name = "pytest", specifier = ">=9.0.2" }, - { name = "pytest-asyncio", specifier = ">=1.2.0" }, - { name = "ty", specifier = ">=0.0.86" }, - { name = "uvicorn", specifier = ">=0.30.0,<1" }, -] -docs = [ - { name = "mkdocs-gen-files", specifier = ">=0.5.0" }, - { name = "mkdocs-glightbox", specifier = ">=0.4.0" }, - { name = "mkdocs-literate-nav", specifier = ">=0.6.2" }, - { name = "mkdocs-material", specifier = ">=9.6.14" }, - { name = "mkdocstrings", extras = ["python"], specifier = ">=0.29.1" }, - { name = "mkdocstrings-python", specifier = ">=2.0.3" }, - { name = "properdocs", specifier = ">=1.6.7" }, -] - [[package]] name = "markdown" version = "3.11" @@ -2763,6 +2690,79 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/bc/e7/5c595c75e9f41a44f30e526eda465ea0b4eec93470e074e4a111b253f13a/s3transfer-0.19.2-py3-none-any.whl", hash = "sha256:d8168eccca828cbb2cd573675333f3bddd254313a9c42494b84c76b539e8ba25", size = 90216, upload-time = "2026-07-22T19:30:43.251Z" }, ] +[[package]] +name = "schemacast" +version = "0.3.0" +source = { editable = "." } +dependencies = [ + { name = "click" }, + { name = "graflo" }, + { name = "openai" }, + { name = "pydantic" }, + { name = "pydantic-settings" }, + { name = "pyyaml" }, +] + +[package.optional-dependencies] +server = [ + { name = "fastapi" }, + { name = "uvicorn", extra = ["standard"] }, +] + +[package.dev-dependencies] +dev = [ + { name = "fastapi" }, + { name = "jsonschema" }, + { name = "pre-commit" }, + { name = "pytest" }, + { name = "pytest-asyncio" }, + { name = "ty" }, + { name = "uvicorn" }, +] +docs = [ + { name = "mkdocs-gen-files" }, + { name = "mkdocs-glightbox" }, + { name = "mkdocs-literate-nav" }, + { name = "mkdocs-material" }, + { name = "mkdocstrings", extra = ["python"] }, + { name = "mkdocstrings-python" }, + { name = "properdocs" }, +] + +[package.metadata] +requires-dist = [ + { name = "click", specifier = ">=8.2.0,<9" }, + { name = "fastapi", marker = "extra == 'server'", specifier = ">=0.134.0,<1" }, + { name = "graflo", editable = "../graflo" }, + { name = "openai", specifier = ">=1.109.0,<3" }, + { name = "pydantic", specifier = ">=2.12.5" }, + { name = "pydantic-settings", specifier = ">=2.12.0" }, + { name = "pyyaml", specifier = ">=6.0" }, + { name = "uvicorn", extras = ["standard"], marker = "extra == 'server'", specifier = ">=0.30.0,<1" }, +] +provides-extras = ["embed", "server"] + +[package.metadata.requires-dev] +bench = [] +dev = [ + { name = "fastapi", specifier = ">=0.134.0,<1" }, + { name = "jsonschema", specifier = ">=4.23.0" }, + { name = "pre-commit", specifier = ">=4.6.2" }, + { name = "pytest", specifier = ">=9.0.2" }, + { name = "pytest-asyncio", specifier = ">=1.2.0" }, + { name = "ty", specifier = ">=0.0.86" }, + { name = "uvicorn", specifier = ">=0.30.0,<1" }, +] +docs = [ + { name = "mkdocs-gen-files", specifier = ">=0.5.0" }, + { name = "mkdocs-glightbox", specifier = ">=0.4.0" }, + { name = "mkdocs-literate-nav", specifier = ">=0.6.2" }, + { name = "mkdocs-material", specifier = ">=9.6.14" }, + { name = "mkdocstrings", extras = ["python"], specifier = ">=0.29.1" }, + { name = "mkdocstrings-python", specifier = ">=2.0.3" }, + { name = "properdocs", specifier = ">=1.6.7" }, +] + [[package]] name = "selectolax" version = "1.0.0"