Skip to content

feat(ai): add ai extract, quote-verified field extraction to a draft manifest - #46

Merged
ChelseaKR merged 5 commits into
mainfrom
feat/ai-extract
Aug 22, 2026
Merged

feat(ai): add ai extract, quote-verified field extraction to a draft manifest#46
ChelseaKR merged 5 commits into
mainfrom
feat/ai-extract

Conversation

@ChelseaKR

Copy link
Copy Markdown
Owner

What

The first command of the opt-in ai group (ADR 0002, role 1: the model structures input; it does not decide).

  • ceqa_preflight/ai/text.py: bounded, process-isolated text extraction (same spawn + hard-timeout pattern as the inspector; page and character caps; image-only PDFs reported as having no text layer and never sent).
  • ceqa_preflight/ai/client.py: provider clients over the public anthropic SDK (Anthropic API; Amazon Bedrock via AnthropicBedrock), imported lazily, credentials from env only, a ScriptedClient for tests/replay. Default model claude-sonnet-5; --provider/--model and CEQA_PREFLIGHT_AI_PROVIDER/MODEL override.
  • ceqa_preflight/ai/extraction.py: the model is asked to copy out 13 manifest-relevant fields plus the document kind, each with a verbatim quote and page. The verifier then checks every quote against the document text and every value against its quote (enum fields against the allowed set). Unverified values are withheld (status unverified, value never shown), unstated fields are unknown, a not_ceqa_notice document yields no fields, and a model error fails closed with no draft. Only verified values reach the draft manifest; the primary form is marked only when exactly one document identified itself as the requested form.
  • ceqa_preflight/ai/cli.py: ceqa-preflight ai extract PKG --filing-type NOE [--format console|json] [--output FILE] [--write-manifest FILE]. States the data flow before running; never overwrites a manifest; the written draft carries a header saying it is a draft, not a finding.
  • ceqa_preflight/ai/provenance.py: provider/model/prompt-version/tool-version/time on every output.
  • ceqa_preflight/ai/messages.py: all user-facing strings in one module for the i18n seam (The i18n release gate blocking the first tagged release has none of its five items built #39).

Default path unchanged

check console output on examples/noe-fictional-package is byte-identical to main (verified by diff). tests/test_ai_client.py::test_default_cli_path_never_imports_the_provider_sdk runs a subprocess that imports the CLI and the catalog and asserts anthropic is not in sys.modules. The SDK is an optional extra (ceqa-preflight[ai], [ai-bedrock]), present in the dev group only so evals can run.

Live smoke test (not an eval; the eval lands in a later PR)

Run against one real CEQAnet NOE (SCH 2026080811) on Bedrock global.anthropic.claude-sonnet-4-6 (the only model this account can reach; Sonnet 5 returns 403): 9 of 13 fields found with verified quotes, all matching CEQAnet's own metadata (title, lead agency, county, city, location, applicant, contact, exemption status, §15301 citation); SCH number, NOD fields, and signature date honestly unknown; 0 withheld.

Verification

174 tests, 94.23% branch coverage, ruff / mypy --strict / bandit clean locally.

🤖 Generated with Claude Code

… every citation

Adds corpus/, the hashed, dated plain text of every source the rule catalog
cites, split into addressable passages, with scripts/build_corpus.py to
rebuild it and a loader (ceqa_preflight.ai.corpus) that refuses text that
does not match its manifest. It is the only text the opt-in AI explanation
layer (ADR 0002) may quote, and it ships inside the wheel.

Source citations now carry a kind (official, technical_reference,
project_advisory) and the HTML report labels each link with it. FILE-004
and FILE-005, self-cited because no official guidance states their
thresholds, now link to the 2026-07-27 source-review addendum that explains
them instead of the repository root, and render as a project advisory rule,
not an official source.

Closes #38.
…manifest

The first command of the opt-in ai group (ADR 0002). Each PDF's text layer
is read through a bounded, process-isolated extractor; the configured
model (Anthropic API or Amazon Bedrock through the public anthropic SDK,
claude-sonnet-5 by default) is asked to copy out the facts a manifest
carries; and every proposed value is verified against a verbatim quote
from the document before it is shown. Unverified values are withheld and
counted, unstated fields are unknown, image-only PDFs are never sent, and
any model error fails closed with no draft. The output is a draft manifest
for a person to review; check is unchanged, never invokes this, and a test
proves the default path never imports the provider SDK.
@ChelseaKR
ChelseaKR merged commit 9358b1f into main Aug 22, 2026
6 checks passed
@ChelseaKR
ChelseaKR deleted the feat/ai-extract branch August 22, 2026 02:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant