@startuml skinparam style strictuml skinparam defaultFontName "DM Sans" skinparam defaultFontSize 14 skinparam titleFontName "Volkhov" skinparam titleFontSize 20 skinparam backgroundColor #FAFCF9 skinparam defaultFontColor #28342A skinparam titleFontColor #28342A skinparam ArrowColor #628A5B skinparam NoteBackgroundColor #EAF0E8 skinparam NoteBorderColor #547461 skinparam ActivityBackgroundColor #FAFCF9 skinparam ActivityBorderColor #547461 skinparam ActivityDiamondBackgroundColor #FAFCF9 skinparam ActivityDiamondBorderColor #628A5B skinparam ActivityBarColor #628A5B skinparam SwimlaneBorderColor #547461 skinparam SwimlaneBorderThickness 0.3 title The Biergarten Data Pipeline (Current — Synchronous Data Path) |#F2F6F0|main.cc| start :Create BoundedChannel log_channel; :Spawn log dispatcher thread\n(LogDispatcher::Run()); note right The only concurrency in the current pipeline: a dedicated thread drains log_channel and forwards entries to spdlog for the entire run. Joined during shutdown after log_channel closes. end note :ParseArguments(argc, argv); if (Are arguments valid?) then (no) :Log error / usage info; stop else (yes) endif :Init OpenSSL global state & LlamaBackendState; :di::make_injector(...); note right Binds ILogger, IEnrichmentService, DataGenerator, IExportService, IPromptFormatter, WebClient based on --mocked vs. model-backed mode. end note :injector.create>(); :BiergartenPipelineOrchestrator::Run(); note right Run() itself is synchronous — sampling, enrichment, generation, and export all happen on this thread. end note |#EAF0E8|BiergartenPipelineOrchestrator| :Initialize SQLite export; |#E0EAE0|SqliteExportService| :GetUtcTimestamp() from SystemDateTimeProvider; :Initialize(); note right Builds a fresh biergarten_seed_.sqlite filename Appends a numeric suffix if the timestamp already exists Opens DB Connection Executes Schema DDL Begins Transaction end note |#EAF0E8|BiergartenPipelineOrchestrator| :QueryCitiesWithCountries(); |#E2EBDC|JsonLoader| :JsonLoader::LoadLocations("locations.json"); :std::ranges::sample(all_locations, --location-count); note right --location-count defaults to 10. end note |#EAF0E8|BiergartenPipelineOrchestrator| while (For each sampled Location?) is (Remaining cities) |#DCE8D8|IEnrichmentService| :GetLocationContext(loc); note right WikipediaEnrichmentService fetches a generic "brewing" extract and a "beer in {country}" extract (not a city/region-specific page). MockEnrichmentService (--mocked) returns an empty string instead. end note |#EAF0E8|BiergartenPipelineOrchestrator| if (Lookup failed?) then (yes) :Log warning, skip city; else (no) :Store EnrichedCity{Location, region_context}; endif endwhile (Done) |#EAF0E8|BiergartenPipelineOrchestrator| :GenerateBreweries(enriched_cities); |#E5EDE1|DataGenerator| while (For each EnrichedCity?) is (Remaining cities) if (Generator Mode) then (MockGenerator) :DeterministicHash & Format; else (LlamaGenerator) :PrepareRegionContext; :prompt_directory_->Load("BREWERY_GENERATION"); note right Resolves to BREWERY_GENERATION.md inside --prompt-dir. end note repeat :Infer(system_prompt, user_prompt, max_tokens, kBreweryJsonGrammar); :ValidateBreweryJson(raw, brewery); if (Is JSON Valid?) then (yes) break else (no) :Attempt++; :Append validation error to retry prompt; endif repeat while (Attempt < 3?) is (yes) note right max_tokens is fixed across retries — it is not raised on truncation. end note endif |#EAF0E8|BiergartenPipelineOrchestrator| if (Generation successful?) then (yes) |#E0EAE0|SqliteExportService| :ProcessRecord(GeneratedBrewery); if (Location in cache?) then (yes) :Reuse location_id; else (no) :Insert Location & Cache ID; endif :Insert Brewery (FK: location_id); if (Exception caught during insert?) then (yes) |#EAF0E8|BiergartenPipelineOrchestrator| :Log warning "SQLite export failed"; note right Data loss is prevented per-record. The pipeline continues running. end note else (no) endif else (no) :Log warning "Generation failed, skipping..."; endif |#E5EDE1|DataGenerator| endwhile (Done) |#E0EAE0|SqliteExportService| :Finalize(); note right Commits Transaction Closes Database Connection end note |#F2F6F0|main.cc| :Close log_channel; :Join log dispatcher thread; :Return 0; stop @enduml