Skip to content

Specialist routing

The Router class in src/pipeline/router.py determines which specialist enrichment strategies apply to a given extraction result.

How routing works

The router receives an ExtractionResult and a UserTableSchema. It builds a compact summary of the extraction data — table roles, headers, row counts, sample cell values, context types, and content snippets — and sends it to the fast Gemini model (gemini-3-flash-preview) with the ROUTER prompt. Gemini returns a GeminiRoutingResult containing a list of StrategyType values and reasoning.

def route(self, extraction_result, schema) -> list[StrategyType]:
    summary = self._build_summary(extraction_result, schema)
    result = self.gemini.request(summary, GeminiRoutingResult, model=ModelType.FAST)
    return result.strategies if result.strategies else []

The summary includes sample cell values from the first three rows of each main table (up to five columns). This allows the router to detect patterns like compound references (GL-03/GMT-01) without sending full table data.

The five strategy types

Each strategy type maps to a specialist prompt and a specific enrichment approach. The router selects a strategy only when clear evidence exists in the extraction data.

Strategy Prompt constant Task Evidence required
auxiliary_table AUXILIARY_TABLE_ENRICHMENT Match reference codes to auxiliary table rows At least one AUXILIARY table with rows
text_rule TEXT_RULE_ENRICHMENT Apply general notes, code requirements, and performance specs Text context with actionable rules or conditional statements
image_legend LEGEND_ENRICHMENT Match style codes to legend diagram interpretations Image context with style codes, operability types, or configurations
dimension_card DIMENSION_ENRICHMENT Extract dimensions from item cards and detail drawings Image context with specific measurements or dimensions
multi_label MULTI_LABEL_ENRICHMENT Resolve compound references and shared item cards Cell values with / or , separators, or image context with multiple type mark labels

T1: Auxiliary table enrichment

The auxiliary table specialist handles cross-referencing between the main schedule and auxiliary reference tables. It matches reference codes (e.g., GL-03, HW-05) from main schedule columns to auxiliary table rows and pulls matching data into the target schema columns. For compound references like GL-03/GMT-01, it resolves the primary component and notes secondaries in Special Notes.

T2: Text rule enrichment

The text rule specialist applies document-level rules extracted as text context items. These include IBC code requirements (e.g., "tempered glass within 24 inches of a door"), performance specifications (U-value, STC, SHGC targets), material requirements, and general notes with conditional statements. Results appear primarily in Special Notes.

T3: Legend enrichment

The legend specialist matches style codes or type designations from the main schedule to legend diagram interpretations in image context. It fills operability (using constrained enum values for WindowOperabilityType / DoorOperabilityType), glass arrangement configuration, and panel attributes. It supports subtype inheritance — when a row like P1a has no direct legend match, it inherits from the base type P1.

T4: Dimension enrichment

The dimension specialist extracts Width, Height, Rough Opening Measurements, and other dimensional values from item card diagrams and detail drawings in image context. It matches drawings to schedule rows via type marks or labels.

T5: Multi-label enrichment

The multi-label specialist handles two patterns:

  1. Compound cell values — Main schedule cells containing / or , separating multiple reference codes (e.g., GL-03/GMT-01). The specialist determines primary vs. secondary components using construction domain knowledge (GL- prefix is typically primary for fenestration).
  2. Shared item cards — A single image context diagram that labels multiple schedule items (e.g., W39A-PTHP / W39B-NO PTHP). The specialist maps variant-specific attributes to the correct rows by matching card labels to __row_id__ values.

Monolithic fallback

When the router returns an empty strategy list — meaning no specialist has clear supporting evidence — the enricher falls back to the monolithic ENRICHMENT prompt. This single Gemini call receives all tables, context, and schema, and attempts to fill every column from any available source. It uses the same priority order (main table, auxiliary tables, image context, text context) but in a single pass rather than through specialists.

Routing decision flow

The following diagram shows how the router's output determines the enrichment path.

flowchart TB
    ER["ExtractionResult + Schema"] --> ROUTER["Router<br/>(Gemini Flash)"]
    ROUTER --> CHECK{strategies<br/>returned?}

    CHECK -->|empty| MONO["Monolithic ENRICHMENT<br/>(single Gemini Pro call)"]
    MONO --> OUT["list[EnrichedRow]"]

    CHECK -->|non-empty| SPLIT["Fan out to specialists"]
    SPLIT --> T1["T1: Auxiliary table"]
    SPLIT --> T2["T2: Text rule"]
    SPLIT --> T3["T3: Legend"]
    SPLIT --> T4["T4: Dimension"]
    SPLIT --> T5["T5: Multi-label"]

    T1 --> MERGE["_merge_specialist_results()"]
    T2 --> MERGE
    T3 --> MERGE
    T4 --> MERGE
    T5 --> MERGE
    MERGE --> OUT

Not every specialist runs on every document. The router only selects strategies with clear evidence, so a typical run activates two to three specialists.