# costpilot-civil/lv-price-engine architecture

> Architecture diagram of the GitHub repository costpilot-civil/lv-price-engine, generated by GitDiagram from its file tree, README and a sample of source files (last updated 2026-09-30). It is an AI-made overview: check details against the source.

- Interactive diagram: https://gitdiagram.com/costpilot-civil/lv-price-engine
- Repository: https://github.com/costpilot-civil/lv-price-engine

## Overview

lv-price-engine matches GAEB tender positions to supplier commodities and recommends material prices, with uncertain matches flagged for review. The main runtime loads positions and price candidates, builds retrieval text, runs vector and BM25 retrieval, fuses and reranks candidates, assigns a match status, and exports results. A separate documented material-understanding capability identifies material types and extracts configured attributes; the sampled matching pipeline does not show it integrated into that runtime. Source evidence is strongest for the main matching and startup flow; unsampled subsystem links are omitted or marked unverified.

Main entry points: [`src/matching/match_pipeline.py`](https://github.com/costpilot-civil/lv-price-engine/blob/main/src/matching/match_pipeline.py) (Match Pipeline), [`main.py`](https://github.com/costpilot-civil/lv-price-engine/blob/main/main.py) (Application Startup), [`src/database/repository.py`](https://github.com/costpilot-civil/lv-price-engine/blob/main/src/database/repository.py) (Candidate Repository), [`src/db_article_importing/db_article_importer.py`](https://github.com/costpilot-civil/lv-price-engine/blob/main/src/db_article_importing/db_article_importer.py) (Article Importer)

## Diagram (Mermaid)

```mermaid
flowchart TD

subgraph group_input["Position Input"]
  node_gaeb["GAEB Loader<br/>[gaeb_lv_loader.py]"]
  node_positions["LV Positions<br/>[lv_position.py]"]
end

subgraph group_catalog["Price Catalog"]
  node_article_xml["Artikel XML Parser"]
  node_article_models["Parsed Articles"]
  node_article_import["Article Importer"]
  node_db[("PostgreSQL<br/>[schema.sql]")]
  node_connector["DB Connector<br/>[connector.py]"]
  node_repository["Candidate Repository<br/>[repository.py]"]
  node_candidates["Commodity Candidates"]
end

subgraph group_understanding["Material Understanding"]
  node_material_config["Material Config Loader<br/>[config_loader.py]"]
  node_registry["Material Registry<br/>[registry.py]"]
  node_classifier["Material Classifier<br/>[classifier.py]"]
  node_attribute_extractor["Attribute Extractor"]
  node_material_extractor["Material Extractor"]
  node_material_template["Material Templates"]
end

subgraph group_matching["Candidate Matching"]
  node_match_pipeline["Match Pipeline<br/>[match_pipeline.py]"]
  node_text_builder["Text Builder<br/>[text_builder.py]"]
  node_vector["Vector Retrieval"]
  node_bm25["BM25 Retrieval<br/>[bm25_retriever.py]"]
  node_fusion["RRF Fusion<br/>[rrf_fusion.py]"]
  node_reranker["Cross-Encoder Reranker"]
  node_models["Inference Models<br/>[model_loader.py]"]
  node_match_results["Match Results<br/>[match_result.py]"]
end

subgraph group_delivery["Results"]
  node_exporter["Result Exporter<br/>[result_exporter.py]"]
  node_main["Application Startup<br/>[main.py]"]
end

node_estimator(("Estimator"))

node_estimator -->|"provides file"| node_gaeb
node_gaeb -->|"parses positions"| node_positions
node_article_xml -->|"produces parsed data"| node_article_models
node_article_import -->|"imports articles"| node_repository
node_article_import -.->|"parses XML"| node_article_xml
node_repository -->|"connects through"| node_connector
node_repository -->|"reads and writes"| node_db
node_article_import -.->|"uses parsed records"| node_article_models
node_repository -->|"builds candidates"| node_candidates
node_material_config -->|"creates templates"| node_material_template
node_registry -->|"stores templates"| node_material_template
node_classifier -->|"looks up materials"| node_registry
node_material_extractor -->|"identifies material"| node_classifier
node_material_extractor -->|"extracts attributes"| node_attribute_extractor
node_material_extractor -.->|"analyzes position text"| node_positions
node_main -->|"loads candidates"| node_repository
node_main -->|"loads positions"| node_gaeb
node_main -->|"loads models"| node_models
node_main -->|"runs matching"| node_match_pipeline
node_match_pipeline -->|"builds query text"| node_text_builder
node_match_pipeline -->|"builds candidate text"| node_text_builder
node_match_pipeline -->|"retrieves vectors"| node_vector
node_match_pipeline -->|"retrieves keywords"| node_bm25
node_match_pipeline -->|"fuses rankings"| node_fusion
node_match_pipeline -->|"reranks candidates"| node_reranker
node_match_pipeline -->|"assigns statuses"| node_match_results
node_match_pipeline -->|"searches candidates"| node_candidates
node_match_pipeline -->|"processes positions"| node_positions
node_main -->|"exports inputs and results"| node_exporter
node_exporter -->|"formats results"| node_match_results

click node_gaeb "https://github.com/costpilot-civil/lv-price-engine/blob/main/src/ingestion/gaeb_lv_loader.py"
click node_positions "https://github.com/costpilot-civil/lv-price-engine/blob/main/src/models/lv_position/lv_position.py"
click node_article_xml "https://github.com/costpilot-civil/lv-price-engine/blob/main/src/db_article_importing/db_article_xml_parser.py"
click node_article_models "https://github.com/costpilot-civil/lv-price-engine/blob/main/src/db_article_importing/db_article_models.py"
click node_article_import "https://github.com/costpilot-civil/lv-price-engine/blob/main/src/db_article_importing/db_article_importer.py"
click node_db "https://github.com/costpilot-civil/lv-price-engine/blob/main/database/schema.sql"
click node_connector "https://github.com/costpilot-civil/lv-price-engine/blob/main/src/database/connector.py"
click node_repository "https://github.com/costpilot-civil/lv-price-engine/blob/main/src/database/repository.py"
click node_candidates "https://github.com/costpilot-civil/lv-price-engine/blob/main/src/models/database_candidate/commodity_candidate.py"
click node_material_config "https://github.com/costpilot-civil/lv-price-engine/blob/main/src/material/config_loader.py"
click node_registry "https://github.com/costpilot-civil/lv-price-engine/blob/main/src/material/registry.py"
click node_classifier "https://github.com/costpilot-civil/lv-price-engine/blob/main/src/material/classifier.py"
click node_attribute_extractor "https://github.com/costpilot-civil/lv-price-engine/blob/main/src/material/attribute_extractor.py"
click node_material_extractor "https://github.com/costpilot-civil/lv-price-engine/blob/main/src/material/material_extractor.py"
click node_material_template "https://github.com/costpilot-civil/lv-price-engine/blob/main/src/models/material/material_template.py"
click node_match_pipeline "https://github.com/costpilot-civil/lv-price-engine/blob/main/src/matching/match_pipeline.py"
click node_text_builder "https://github.com/costpilot-civil/lv-price-engine/blob/main/src/text_processing/text_builder.py"
click node_vector "https://github.com/costpilot-civil/lv-price-engine/blob/main/src/retrieval/vector_retriever.py"
click node_bm25 "https://github.com/costpilot-civil/lv-price-engine/blob/main/src/retrieval/bm25_retriever.py"
click node_fusion "https://github.com/costpilot-civil/lv-price-engine/blob/main/src/fusion/rrf_fusion.py"
click node_reranker "https://github.com/costpilot-civil/lv-price-engine/blob/main/src/ranking/cross_encoder_reranker.py"
click node_models "https://github.com/costpilot-civil/lv-price-engine/blob/main/src/models/inference_model/model_loader.py"
click node_match_results "https://github.com/costpilot-civil/lv-price-engine/blob/main/src/models/results/match_result.py"
click node_exporter "https://github.com/costpilot-civil/lv-price-engine/blob/main/src/exporter/result_exporter.py"
click node_main "https://github.com/costpilot-civil/lv-price-engine/blob/main/main.py"

classDef toneNeutral fill:#f8fafc,stroke:#334155,stroke-width:1.5px,color:#0f172a
classDef toneBlue fill:#dbeafe,stroke:#2563eb,stroke-width:1.5px,color:#172554
classDef toneAmber fill:#fef3c7,stroke:#d97706,stroke-width:1.5px,color:#78350f
classDef toneMint fill:#dcfce7,stroke:#16a34a,stroke-width:1.5px,color:#14532d
classDef toneRose fill:#ffe4e6,stroke:#e11d48,stroke-width:1.5px,color:#881337
classDef toneIndigo fill:#e0e7ff,stroke:#4f46e5,stroke-width:1.5px,color:#312e81
classDef toneTeal fill:#ccfbf1,stroke:#0f766e,stroke-width:1.5px,color:#134e4a
class node_gaeb,node_positions toneBlue
class node_article_xml,node_article_models,node_article_import,node_db,node_connector,node_repository,node_candidates toneAmber
class node_material_config,node_registry,node_classifier,node_attribute_extractor,node_material_extractor,node_material_template toneMint
class node_match_pipeline,node_text_builder,node_vector,node_bm25,node_fusion,node_reranker,node_models,node_match_results toneRose
class node_exporter,node_main,node_estimator toneIndigo
```

## Components

### Position Input

- [GAEB Loader](https://github.com/costpilot-civil/lv-price-engine/blob/main/src/ingestion/gaeb_lv_loader.py): `src/ingestion/gaeb_lv_loader.py`
- [LV Positions](https://github.com/costpilot-civil/lv-price-engine/blob/main/src/models/lv_position/lv_position.py): `src/models/lv_position/lv_position.py`

### Price Catalog

- [Artikel XML Parser](https://github.com/costpilot-civil/lv-price-engine/blob/main/src/db_article_importing/db_article_xml_parser.py): `src/db_article_importing/db_article_xml_parser.py`
- [Parsed Articles](https://github.com/costpilot-civil/lv-price-engine/blob/main/src/db_article_importing/db_article_models.py): `src/db_article_importing/db_article_models.py`
- [Article Importer](https://github.com/costpilot-civil/lv-price-engine/blob/main/src/db_article_importing/db_article_importer.py): `src/db_article_importing/db_article_importer.py`
- [PostgreSQL](https://github.com/costpilot-civil/lv-price-engine/blob/main/database/schema.sql): `database/schema.sql`
- [DB Connector](https://github.com/costpilot-civil/lv-price-engine/blob/main/src/database/connector.py): `src/database/connector.py`
- [Candidate Repository](https://github.com/costpilot-civil/lv-price-engine/blob/main/src/database/repository.py): `src/database/repository.py`
- [Commodity Candidates](https://github.com/costpilot-civil/lv-price-engine/blob/main/src/models/database_candidate/commodity_candidate.py): `src/models/database_candidate/commodity_candidate.py`

### Material Understanding

- [Material Config Loader](https://github.com/costpilot-civil/lv-price-engine/blob/main/src/material/config_loader.py): `src/material/config_loader.py`
- [Material Registry](https://github.com/costpilot-civil/lv-price-engine/blob/main/src/material/registry.py): `src/material/registry.py`
- [Material Classifier](https://github.com/costpilot-civil/lv-price-engine/blob/main/src/material/classifier.py): `src/material/classifier.py`
- [Attribute Extractor](https://github.com/costpilot-civil/lv-price-engine/blob/main/src/material/attribute_extractor.py): `src/material/attribute_extractor.py`
- [Material Extractor](https://github.com/costpilot-civil/lv-price-engine/blob/main/src/material/material_extractor.py): `src/material/material_extractor.py`
- [Material Templates](https://github.com/costpilot-civil/lv-price-engine/blob/main/src/models/material/material_template.py): `src/models/material/material_template.py`

### Candidate Matching

- [Match Pipeline](https://github.com/costpilot-civil/lv-price-engine/blob/main/src/matching/match_pipeline.py): `src/matching/match_pipeline.py`
- [Text Builder](https://github.com/costpilot-civil/lv-price-engine/blob/main/src/text_processing/text_builder.py): `src/text_processing/text_builder.py`
- [Vector Retrieval](https://github.com/costpilot-civil/lv-price-engine/blob/main/src/retrieval/vector_retriever.py): `src/retrieval/vector_retriever.py`
- [BM25 Retrieval](https://github.com/costpilot-civil/lv-price-engine/blob/main/src/retrieval/bm25_retriever.py): `src/retrieval/bm25_retriever.py`
- [RRF Fusion](https://github.com/costpilot-civil/lv-price-engine/blob/main/src/fusion/rrf_fusion.py): `src/fusion/rrf_fusion.py`
- [Cross-Encoder Reranker](https://github.com/costpilot-civil/lv-price-engine/blob/main/src/ranking/cross_encoder_reranker.py): `src/ranking/cross_encoder_reranker.py`
- [Inference Models](https://github.com/costpilot-civil/lv-price-engine/blob/main/src/models/inference_model/model_loader.py): `src/models/inference_model/model_loader.py`
- [Match Results](https://github.com/costpilot-civil/lv-price-engine/blob/main/src/models/results/match_result.py): `src/models/results/match_result.py`

### Results

- [Result Exporter](https://github.com/costpilot-civil/lv-price-engine/blob/main/src/exporter/result_exporter.py): `src/exporter/result_exporter.py`
- [Application Startup](https://github.com/costpilot-civil/lv-price-engine/blob/main/main.py): `main.py`

### Other components

- **Estimator**

## Connections

- Estimator → GAEB Loader: provides file (evidence: `README.md`)
- GAEB Loader → LV Positions: parses positions (evidence: `src/ingestion/gaeb_lv_loader.py`)
- Artikel XML Parser → Parsed Articles: produces parsed data (evidence: `src/db_article_importing/db_article_xml_parser.py`)
- Article Importer → Candidate Repository: imports articles (evidence: `src/db_article_importing/db_article_importer.py`)
- Article Importer → Artikel XML Parser: parses XML
- Candidate Repository → DB Connector: connects through (evidence: `src/database/repository.py`)
- Candidate Repository → PostgreSQL: reads and writes (evidence: `src/database/repository.py`)
- Article Importer → Parsed Articles: uses parsed records
- Candidate Repository → Commodity Candidates: builds candidates (evidence: `src/database/repository.py`)
- Material Config Loader → Material Templates: creates templates (evidence: `src/material/config_loader.py`)
- Material Registry → Material Templates: stores templates (evidence: `src/material/registry.py`)
- Material Classifier → Material Registry: looks up materials (evidence: `src/material/classifier.py`)
- Material Extractor → Material Classifier: identifies material (evidence: `src/material/material_extractor.py`)
- Material Extractor → Attribute Extractor: extracts attributes (evidence: `src/material/material_extractor.py`)
- Material Extractor → LV Positions: analyzes position text
- Application Startup → Candidate Repository: loads candidates (evidence: `main.py`)
- Application Startup → GAEB Loader: loads positions (evidence: `main.py`)
- Application Startup → Inference Models: loads models (evidence: `main.py`)
- Application Startup → Match Pipeline: runs matching (evidence: `main.py`)
- Match Pipeline → Text Builder: builds query text (evidence: `src/matching/match_pipeline.py`)
- Match Pipeline → Text Builder: builds candidate text (evidence: `src/matching/match_pipeline.py`)
- Match Pipeline → Vector Retrieval: retrieves vectors (evidence: `src/matching/match_pipeline.py`)
- Match Pipeline → BM25 Retrieval: retrieves keywords (evidence: `src/matching/match_pipeline.py`)
- Match Pipeline → RRF Fusion: fuses rankings (evidence: `src/matching/match_pipeline.py`)
- Match Pipeline → Cross-Encoder Reranker: reranks candidates (evidence: `src/matching/match_pipeline.py`)
- Match Pipeline → Match Results: assigns statuses (evidence: `src/matching/match_pipeline.py`)
- Match Pipeline → Commodity Candidates: searches candidates (evidence: `src/matching/match_pipeline.py`)
- Match Pipeline → LV Positions: processes positions (evidence: `src/matching/match_pipeline.py`)
- Application Startup → Result Exporter: exports inputs and results (evidence: `main.py`)
- Result Exporter → Match Results: formats results (evidence: `src/exporter/result_exporter.py`)
