Skip to content

feat(agent): multi-section configs, local payloads, GEPA optimize, reflexion/judge gates - #62

Merged
SkyeAv merged 2 commits into
mainfrom
feat/agent-multisection-local-gepa
Jul 31, 2026
Merged

feat(agent): multi-section configs, local payloads, GEPA optimize, reflexion/judge gates#62
SkyeAv merged 2 commits into
mainfrom
feat/agent-multisection-local-gepa

Conversation

@SkyeAv

@SkyeAv SkyeAv commented Jul 31, 2026

Copy link
Copy Markdown
Owner

Expands the autonomous tablassert agent pipeline from one-section-per-paper to a richer, still-deterministic supervisor: multi-section configs, local (non-open-access) payloads, first-class GEPA prompt optimization, and opt-in reflexion/judge gates — with the docs/cli.md flag table brought back in sync.

Multi-section configs (one per paper)

  • Shape: a config is now {template, sections}template carries only shared provenance (repo + publication, no source); each entry in sections owns its own source (local path and source.url, plus sheet/row_slice/delimiter) and statement, so one paper maps every supplementary table/worksheet.
  • Validation: validate_table_config replaces validate_section as the final-answer gate and validates every section; a config is accepted only when all sections are schema-valid.
  • Coverage: map_coverage measures each section (_measure_section) and reports an aggregate — overall (mean), min (weakest), measured (true iff all measured), plus a per-section breakdown; propose_config_edit edits each section from its own coverage entry (_propose_multi_section).
  • Fixture: adds tests/agent_fixtures/GENE_DISEASE/ (a gene~disease config in multi-section shape with PMID provenance) to keep the offline heuristic judge and validation honest on a distinct config; covered by the new tests/test_agent_multisection.py (+340).

Local payloads (--local / -l)

  • Why: only the open-access subset of PMC is fetchable from the bucket; --local runs the same derive/build/improve pipeline on an article held locally (e.g. a non-open-access paper).
  • Forms: one DIR applied to every id, or per-article PMCid=DIR mappings (parse_local in cli.py, _resolve_local_dir in agent.py). When set, the supervisor locates files locally and skips the PMC-AWS fetch; a missing dir fails loud (exit 2).

GEPA prompt optimization (--optimize / -o)

  • First-class path: tablassert agent --optimize runs dspy.GEPA with a real reflection LM (make_dspy_lm) and persists the optimized instructions (save_optimized_instructions) instead of running the supervisor; reload via --instructions-file.
  • Knobs: --instructions-out (default <state-dir>/optimized_instructions.yaml), --max-metric-calls (budget, default 8), and --dataset (YAML/JSON {table_summary, coverage_feedback} examples via load_gepa_dataset).

Reflexion improver & semantic judge (opt-in)

  • --reflexion: a tier-2 LLM reflexion improver (llm_propose_config_edit, make_prompt_callable) for edits that may change predicate/source when the deterministic proposer stalls — same model config, built lazily.
  • --judge-model / --judge-threshold: a semantic judge scores the output; when --judge-model is set, MAPPED additionally requires the normalized score to clear --judge-threshold (0.5 when unset). Without it, the coverage gate alone decides.

Robustness

  • PDF context: pdfminer.six (new [agent] dep) extracts a .pdf main text into data-fenced context (_extract_pdf_text), so PDF-only articles still give the agent main-text context.
  • BUILT_UNMEASURED: a new terminal non-failure for a config that builds but whose fullmap coverage can't be measured (unreproducible source frame) — neither MAPPED nor SKIPPED; the best config is still written and reusable.
  • Multi-cwd coverage: a relative source.local is resolved against the build workdir as well as the cwd (_candidate_cwds) before a config is declared unmeasurable.

Design

  • Deterministic supervisor preserved: the outer loop stays plain Python (smolagents' Merge Main and Dev #1 practice); reflexion and judge are strictly opt-in layers, and improve candidates are still accepted iff strictly better (monotonic).
  • Accepted caveat: real --optimize, --reflexion, and --judge-model runs need a live model; the offline suite exercises these paths via injectable stubs (gepa_cls, monkeypatched run_gepa/make_dspy_lm), so no network fires in CI.
  • Deferred: migration tooling for pre-existing single-section configs (a single-table paper is now one config with one section).

Docs

  • docs/cli.md — completed the SSOT agent flag table with all nine new flags (this is what the tests/test_docs_cli_coverage.py guardrails enforce).
  • docs/agent.md — multi-section model, --local, --optimize, and a new "Optional gates: reflexion improver & semantic judge" subsection; pdfminer.six pin documented.

Testing

  • uv run pytest -q655 passed, 29 skipped (skips are live-model/network paths); the 3 previously-failing test_docs_cli_coverage.py guardrails now pass.
  • uv run ruff check .All checks passed!
  • uv run ruff format --check .64 files already formatted
  • uv run pyright0 errors, 0 warnings, 0 informations
  • pre-commit (ruff / ruff-format / pyright / pytest) → all Passed on commit.

Questions for the reviewer

  • Scope. This bundles five capabilities (multi-section, local, GEPA, reflexion/judge, PDF/robustness) that landed together as fleet-produced WIP — prefer it as one PR, or split multi-section out from the optimization/gate work?
  • Judge default. --judge-threshold defaults to None in the CLI and falls back to 0.5 in code — is 0.5 the right floor, or should the threshold be required when --judge-model is set?
  • Config compatibility. Multi-section ({template, sections}) is now the only authored shape; is a migration path for existing single-section configs needed, or are there no configs in the wild yet?

Summary by CodeRabbit

  • New Features

    • Added support for multi-section table configurations, including per-section validation, coverage, editing, and reporting.
    • Added PDF main-text extraction and support for local data payloads.
    • Added optional reflexion and semantic-judging workflows.
    • Added CLI options for instruction files, optimization, datasets, metric limits, and custom models.
    • Added GEPA prompt optimization with instruction saving and loading.
  • Documentation

    • Expanded CLI and agent documentation with the new workflows, options, and multi-section examples.

…flexion/judge gates

- Multi-section configs: one {template, sections} config per paper; each section
  owns its own source/url/sheet. validate_table_config gates every section and
  map_coverage reports an aggregate (overall/min/per-section). New GENE_DISEASE fixture.
- --local/-l: run the pipeline on a local payload (one DIR for all ids, or PMCid=DIR
  mappings) instead of fetching from PMC-AWS; fails loud (exit 2) on a missing dir.
- --optimize/-o: first-class dspy.GEPA prompt optimization that persists optimized
  instructions (--instructions-out), reloadable via --instructions-file, bounded by
  --max-metric-calls over a --dataset.
- --reflexion: tier-2 LLM reflexion improver for when the deterministic proposer stalls.
- --judge-model/--judge-threshold: semantic judge gate; MAPPED additionally requires the
  score to clear the threshold (0.5 when unset).
- PDF main-text context via pdfminer.six; BUILT_UNMEASURED terminal non-failure; multi-cwd
  resolution of relative source.local.
- docs: complete the cli.md SSOT flag table and agent.md depth for all new flags (fixes
  the docs-coverage guardrails).

655 passed, 29 skipped; ruff + ruff-format + pyright clean.
@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@SkyeAv, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 17 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 82536237-53a5-45c3-bed7-92887727149b

📥 Commits

Reviewing files that changed from the base of the PR and between a10e6eb and 3687c20.

📒 Files selected for processing (5)
  • src/tablassert/agent.py
  • src/tablassert/cli.py
  • tests/test_agent_cli.py
  • tests/test_agent_eval.py
  • tests/test_agent_supervisor.py
📝 Walkthrough

Walkthrough

The agent now supports multi-section table configurations, PDF text extraction, local payloads, per-section coverage, reflexion and semantic gates, BUILT_UNMEASURED outcomes, expanded CLI options, and GEPA instruction optimization.

Changes

Agent workflow expansion

Layer / File(s) Summary
PDF and table context inputs
pyproject.toml, src/tablassert/agent.py, tests/test_agent_context.py, docs/agent.md
PDF article text extraction uses pdfminer.six. Table inspection and configuration derivation describe worksheets and multiple sections.
Multi-section validation and measurement
src/tablassert/agent.py, tests/test_agent_build.py, tests/test_agent_coverage.py, tests/test_agent_multisection.py, tests/agent_fixtures/GENE_DISEASE/reference_config.yaml
Validation, source resolution, coverage aggregation, and build auditing operate across independent sections.
Section-aware proposal generation
src/tablassert/agent.py, tests/test_agent_assembly.py, tests/test_agent_propose.py, tests/test_cover_agent_propose.py, docs/agent.md
Deterministic and reflexion-based proposals edit sections independently, rank distinct candidates, and validate complete configurations.
Supervision, gating, and persisted states
src/tablassert/agent.py, tests/test_agent_supervisor.py, tests/test_agent_multisection.py, tests/test_agent_eval.py, docs/agent.md
The supervisor adds sampled builds, semantic judging, local payload resolution, per-section history, provenance scoring, and BUILT_UNMEASURED handling.
CLI options and GEPA instruction workflow
src/tablassert/cli.py, src/tablassert/agent.py, tests/test_agent_cli.py, docs/cli.md, docs/agent.md
The CLI supports optional models, local inputs, GEPA datasets, instruction persistence, and optimization-only execution.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CLI
  participant run_supervisor
  participant propose_config_candidates
  participant build_and_audit
  participant judge_model
  CLI->>run_supervisor: pass workflow options
  run_supervisor->>propose_config_candidates: generate ranked candidates
  run_supervisor->>build_and_audit: run candidate build
  run_supervisor->>judge_model: request optional semantic score
  judge_model-->>run_supervisor: return gate decision
  run_supervisor-->>CLI: return final status and metrics
Loading

Possibly related PRs

  • SkyeAv/Tablassert#56: This PR extends the agent validation, coverage, supervisor, and evaluation workflows introduced there.
  • SkyeAv/Tablassert#60: This PR extends the same agent CLI, run_supervisor, and build_agent workflows.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 74.47% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the PR's primary features, including multi-section configs, local payloads, GEPA optimization, and optional model-based gates.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/agent-multisection-local-gepa

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🧹 Nitpick comments (2)
tests/test_agent_supervisor.py (1)

444-459: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add head: bool = False to fake_build.

The improve loop is not gated by measured=False. If candidate generation returns an edit, it calls build_and_audit(..., head=True), which this stub cannot accept and which would mark the record SKIPPED.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_agent_supervisor.py` around lines 444 - 459, Add the optional
head: bool = False parameter to the fake_build test stub, preserving its
existing return behavior so calls from the improve loop with head=True are
accepted and do not produce a skipped record.
src/tablassert/agent.py (1)

1571-1663: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract the shared coverage-entry lookup and heuristic bodies.

_propose_category repeats the columns_for closure from _propose_multi_section (Lines 1478-1489) verbatim, and _apply_category_to_node repeats the taxonomic/noise/exclude branches of _edit_node (Lines 1407-1429). A future change to one heuristic must be mirrored in two places.

Extract a module-level _columns_selector(report) helper and drive _edit_node from the category functions, so the full edit becomes "all categories plus the chemical fallback".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/tablassert/agent.py` around lines 1571 - 1663, Extract the duplicated
coverage lookup into a module-level _columns_selector(report) helper and use it
from _propose_category and _propose_multi_section. Refactor _edit_node to apply
the shared taxonomic, noise, and exclude category logic through
_apply_category_to_node, leaving only the chemical fallback as
full-edit-specific behavior. Preserve existing category ordering, rationale, and
multi-section coverage behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/tablassert/agent.py`:
- Around line 2614-2634: Validate full-build results before committing them in
the improve loop: in src/tablassert/agent.py lines 2614-2634, only accept and
assign the candidate when full_report["ok"] is true and its coverage is strictly
greater than the prior current_cov; otherwise continue to the next candidate.
Apply the same guard to full_report3 in src/tablassert/agent.py lines 2637-2658,
preserving current_config, coverage history, and best coverage when validation
fails.
- Around line 3229-3241: Update the file-reading logic around the YAML-loading
function so both p.read_text() and yaml.safe_load() execute inside the try
block. Catch OSError, UnicodeDecodeError, and yaml.YAMLError, returning None for
any unreadable file or invalid YAML while preserving the existing
parsed-instructions handling.

In `@src/tablassert/cli.py`:
- Around line 655-663: Check the stats error field on the result returned by
agent_mod.run_gepa before deriving or saving optimized instructions. When a GEPA
compilation error is present, avoid save_optimized_instructions and the success
message, report the failure, and return a non-zero CLI status; preserve the
existing save-and-success flow for successful results.
- Around line 635-644: Update the --local parsing loop over specs to validate
both the stripped PMC id and directory string before constructing Path or adding
to mapping. Reject either blank component with the existing stderr message and
SystemExit(2), while preserving normal PMCid=DIR handling.
- Line 545: Validate judge_threshold before invoking the supervisor: reject
non-finite values and any value outside the inclusive range [0, 1], and exit
with status 2 for invalid input. Keep valid thresholds, including None, on the
existing execution path.
- Around line 652-656: Update the optimize flow around agent_mod.make_dspy_lm
and agent_mod.run_gepa to pass the selected --backend value into the GEPA model
configuration. Ensure the constructed reflection LM uses the requested backend
rather than always defaulting to the OpenAI provider, while preserving the
existing resolved model ID, base, and key behavior.

---

Nitpick comments:
In `@src/tablassert/agent.py`:
- Around line 1571-1663: Extract the duplicated coverage lookup into a
module-level _columns_selector(report) helper and use it from _propose_category
and _propose_multi_section. Refactor _edit_node to apply the shared taxonomic,
noise, and exclude category logic through _apply_category_to_node, leaving only
the chemical fallback as full-edit-specific behavior. Preserve existing category
ordering, rationale, and multi-section coverage behavior.

In `@tests/test_agent_supervisor.py`:
- Around line 444-459: Add the optional head: bool = False parameter to the
fake_build test stub, preserving its existing return behavior so calls from the
improve loop with head=True are accepted and do not produce a skipped record.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: e46fc458-f4b8-45ae-a71b-c4254ca8732d

📥 Commits

Reviewing files that changed from the base of the PR and between ee49b62 and a10e6eb.

⛔ Files ignored due to path filters (2)
  • tests/agent_fixtures/GENE_DISEASE/source_table.csv is excluded by !**/*.csv
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (16)
  • docs/agent.md
  • docs/cli.md
  • pyproject.toml
  • src/tablassert/agent.py
  • src/tablassert/cli.py
  • tests/agent_fixtures/GENE_DISEASE/reference_config.yaml
  • tests/test_agent_assembly.py
  • tests/test_agent_build.py
  • tests/test_agent_cli.py
  • tests/test_agent_context.py
  • tests/test_agent_coverage.py
  • tests/test_agent_eval.py
  • tests/test_agent_multisection.py
  • tests/test_agent_propose.py
  • tests/test_agent_supervisor.py
  • tests/test_cover_agent_propose.py

Comment thread src/tablassert/agent.py
Comment thread src/tablassert/agent.py
Comment thread src/tablassert/cli.py
Comment thread src/tablassert/cli.py Outdated
Comment thread src/tablassert/cli.py
Comment thread src/tablassert/cli.py
…on, GEPA optimize

- Improve loop: commit a confirming full build IFF it succeeded (ok) AND its
  coverage is strictly greater than the prior best; a failing or lower-scoring
  full build (optimistic 5-row head sample) no longer regresses current_config,
  the monotonic coverage_history, or best_coverage (tier 1 + tier 2).
- load_optimized_instructions: read inside the try with explicit utf-8 and catch
  OSError/UnicodeDecodeError too, so an unreadable instructions file returns None
  instead of aborting the run.
- make_dspy_lm: honor --backend (openai/ prefix vs litellm pass-through); the
  --optimize path now forwards --backend instead of always routing through openai.
- cli agent: reject --judge-threshold outside [0,1] or non-finite (exit 2); reject
  --local PMCid=DIR with a blank id or dir (exit 2) so PMC1= is not the cwd;
  --optimize exits 1 without saving/reporting success when GEPA stats has an error.
- Extract _columns_selector to dedup the identical columns_for closure shared by
  the multi-section and per-category proposers (behavior-preserving).
- Tests: tier-1/tier-2 full-build rejection, judge-threshold/--local/--optimize
  validation, GEPA-error exit, make_dspy_lm backend, unreadable instructions; add
  head=False to a fake_build stub.
@SkyeAv
SkyeAv merged commit b7a136b into main Jul 31, 2026
5 checks passed
@SkyeAv
SkyeAv deleted the feat/agent-multisection-local-gepa branch July 31, 2026 21:09
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