Thanks to visit codestin.com
Credit goes to github.com

Skip to content

feat: Add Cognita provider and layout adapter for document parsing - #148

Open
RHL-RWT-01 wants to merge 2 commits into
run-llama:mainfrom
RHL-RWT-01:feat/cognita
Open

feat: Add Cognita provider and layout adapter for document parsing#148
RHL-RWT-01 wants to merge 2 commits into
run-llama:mainfrom
RHL-RWT-01:feat/cognita

Conversation

@RHL-RWT-01

@RHL-RWT-01 RHL-RWT-01 commented Sep 11, 2026

Copy link
Copy Markdown
  • Adds Cognita (https://cognita.rahulrawat.in) as a PARSE provider — a self-hosted, zero-dependency, pure-code document engine (no ML, no GPUs, no cloud calls) with deterministic, byte-identical output.
  • Provider calls the Cognita server's /v1/parse for the IR, renders faithful per-page Markdown via /v1/export, and emits layout_pages with normalized bboxes + Canonical17 labels (GFM tables converted to HTML so table metrics score). Includes a cognita pipeline, a CognitaLayoutAdapter (+COGNITA_LAYOUT enum), and 8 unit tests.
  • Full run (2,079 pages, pure-code): Overall 37.0 — Tables 33.3 / Charts 2.8 / Content 71.6 / Formatting 47.3 / Grounding 30.0; 100% parse success. Charts are low by design (pure-code doesn't transcribe chart data).

Copilot AI lite review requested due to automatic review settings September 11, 2026 16:15

Copilot AI 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.

🟡 Changes recommended

Unresolved critical and moderate findings affect parsing, retries, configuration, and layout evaluation.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Adds Cognita as a self-hosted PARSE provider with Markdown export, layout normalization, pipeline registration, and evaluation support.

Changes:

  • Adds Cognita API integration and output normalization.
  • Registers the Cognita pipeline, provider, layout enum, and adapter.
  • Adds helper unit tests.
File summaries
File Summary and final review findings
tests/parse_bench/inference/providers/parse/test_cognita.py Adds helper tests. Nit (1 vote): expand coverage to provider, normalization, and adapter behavior.
src/parse_bench/schemas/layout_detection_output.py Adds COGNITA_LAYOUT. Moderate (1 vote): add corresponding LAYOUT_MODEL_INFO metadata.
src/parse_bench/inference/providers/parse/cognita.py Implements the provider and normalization. Critical (2 votes): avoid converting table-like content inside fenced code blocks. Moderate (1 vote each): handle HTTP 429 as rate limiting, convert fallback page Markdown, support tables without outer pipes, and recurse through nested lists.
src/parse_bench/inference/providers/parse/__init__.py Registers the provider module.
src/parse_bench/inference/pipelines/parse.py Registers the Cognita pipeline. Moderate (3 votes): allow COGNITA_SERVER_URL to override pipeline configuration.
src/parse_bench/evaluation/layout_adapters/adapters.py Adds the Cognita layout adapter. Moderate (2 votes): use selected-page dimensions; moderate (1 vote): require Cognita-specific output or provider identity during fallback matching.
Review details

Suppressed comments (12)

src/parse_bench/evaluation/layout_adapters/adapters.py:3474

  • Using seg.confidence or 1.0 turns a valid confidence of 0.0 into a perfect score of 1.0. That changes Cognita's confidence semantics and can inflate score-weighted layout evaluation; preserve zero by checking for None explicitly.
                            score=float(seg.confidence or 1.0),

src/parse_bench/evaluation/layout_adapters/adapters.py:3427

  • This fallback matcher accepts every ParseOutput that has layout_pages. If provider resolution is unavailable, create_layout_adapter_for_result can therefore select Cognita for unrelated parse providers and label their output COGNITA_LAYOUT. Require a Cognita-specific raw-output marker such as page_markdowns/parse, or a provider identity check here.
    def matches(cls, inference_result: InferenceResult) -> bool:
        return isinstance(inference_result.output, ParseOutput) and bool(inference_result.output.layout_pages)

src/parse_bench/inference/providers/parse/cognita.py:207

  • This default sends every input document to the public https://api.cognita.rahulrawat.in service, which contradicts the PR's advertised self-hosted/no-cloud behavior. Cognita's documented default deployment also requires X-API-Key, but this pipeline does not configure one, so an unconfigured run can fail with 401; use a local default or require an explicitly configured endpoint and credential.
            self.base_config.get("server_url") or os.getenv("COGNITA_SERVER_URL") or "https://api.cognita.rahulrawat.in"

src/parse_bench/inference/providers/parse/cognita.py:330

  • Cognita table blocks store their cell content under table.rows; they do not populate block["text"]. This therefore creates a Table item with empty value/md, and the adapter's _build_vendor_content returns None, so to_attribution_blocks skips the table prediction entirely. Serialize the structured table to HTML/text (or preserve it in html/md) before constructing the LayoutItemIR.
                        type=_ITEM_TYPE_BY_LABEL.get(label, "text"),
                        value=str(block.get("text", "")),
                        md=str(block.get("text", "")),
                        bbox=seg,
                        layout_segments=[seg],

src/parse_bench/inference/providers/parse/cognita.py:260

  • When any per-page export returns a non-200 response or raises an HTTP error, this fallback concatenates only block["text"]. Cognita table blocks keep their content in table.rows, so those pages lose their tables; ParseBench evaluates page markdown for table metrics, making the run silently report incomplete output. Retry/fail the export or build a structured fallback that preserves table content.
        # Fall back to raw block text if export is unavailable for this page.
        return "\n\n".join(str(b.get("text", "")) for b in page.get("blocks", []) if b.get("text"))

src/parse_bench/inference/providers/parse/cognita.py:277

  • This performs one blocking /v1/export request per page after the initial parse. The advertised 2,079-page run therefore adds 2,079 sequential HTTP calls, increasing latency, server load, and the chance of partial export failures linearly with document size. Prefer a batched/per-document page export or avoid re-rendering when a page-level representation is already available.
            page_markdowns = [self._export_page_markdown(client, document, page) for page in pages]

src/parse_bench/inference/providers/parse/cognita.py:236

  • HTTP 429 falls through this 4xx branch and becomes ProviderPermanentError, but the inference runner retries only ProviderTransientError and ProviderRateLimitError. Rate-limited Cognita calls therefore fail immediately instead of participating in the standard retry/backoff path; classify 429 as ProviderRateLimitError and import that exception.
        if 400 <= resp.status_code < 500:
            raise ProviderPermanentError(f"Cognita parse failed ({resp.status_code}): {resp.text[:300]}")

src/parse_bench/inference/providers/parse/cognita.py:346

  • When the parse response has no whole-document Markdown, this fallback joins the raw page_markdowns even though each page was converted to HTML at line 311. Any fallback GFM tables therefore remain pipe syntax and are invisible to ParseBench's table metrics. Join the converted page Markdown or run _gfm_tables_to_html over this joined value.
        # Fall back to the whole-document markdown if per-page export produced nothing.
        if not full_markdown and page_markdowns:
            full_markdown = "\n\n".join(page_markdowns)

src/parse_bench/inference/providers/parse/cognita.py:85

  • Valid GFM tables may omit the outer pipes (A | B / --- | ---), but this regex requires both a leading and trailing pipe. Those tables pass through as Markdown and are not discoverable by the HTML-based table metrics. Match rows containing a pipe while keeping the separator-row check.
_PIPE_ROW_RE = re.compile(r"^\s*\|.*\|\s*$")

src/parse_bench/inference/providers/parse/cognita.py:195

  • Only direct children of a page-level list are emitted. Cognita's IR permits nested lists under list_item, so nested list/list-item blocks and their bboxes are silently omitted from layout_pages. Recurse through list children instead of flattening only one level.
        if str(b.get("type") or "").lower() == "list" and isinstance(b.get("children"), list):
            children = [c for c in b["children"] if isinstance(c, dict)]
            if children:
                out.extend(children)
                continue

src/parse_bench/schemas/layout_detection_output.py:327

  • The new enum member has no corresponding entry in LAYOUT_MODEL_INFO, unlike the existing layout models. Consumers that enumerate or index this metadata table will omit Cognita or raise a lookup error; add Cognita's display metadata alongside the other entries.
    COGNITA_LAYOUT = "cognita_layout"

tests/parse_bench/inference/providers/parse/test_cognita.py:15

  • The new tests cover only pure helper functions; they do not exercise CognitaProvider.run_inference/normalize or CognitaLayoutAdapter with a mocked response. HTTP status handling, export fallback, table-block mapping, page filtering, and confidence conversion can therefore regress without detection; add provider and adapter tests comparable to test_liteparse_layout.py.
def test_gfm_table_converted_to_html_prose_untouched() -> None:
    md = "# Title\n\nSome prose.\n\n| A | B |\n| --- | --- |\n| 1 | 2 |\n| 3 | 4 |\n\nAfter."
    out = _gfm_tables_to_html(md)
  • Files reviewed: 6/6 changed files
  • Comments generated: 3
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/parse_bench/inference/providers/parse/cognita.py
Comment thread src/parse_bench/evaluation/layout_adapters/adapters.py Outdated
Comment thread src/parse_bench/inference/pipelines/parse.py Outdated
- Skip fenced code blocks in the GFM->HTML table converter so pipe/
  separator lines inside code are not rewritten as <table>.
- Use the scored page (page_filter) as the reference frame in
  CognitaLayoutAdapter, so mixed-size documents normalize bboxes
  against the correct page dimensions.
- Drop the hard-coded server_url from the cognita pipeline config so
  COGNITA_SERVER_URL can override it (provider keeps default/env fallback).

Co-Authored-By: Claude Opus 4.8 <[email protected]>
@RHL-RWT-01

Copy link
Copy Markdown
Author

Hi @boyang-zhang1 , could you please approve the pending workflow for this PR?

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.

2 participants