mirror of
https://github.com/aaronpo97/the-biergarten-app.git
synced 2026-06-01 01:54:00 +00:00
193 lines
6.1 KiB
Plaintext
193 lines
6.1 KiB
Plaintext
@startuml future_possible_activity
|
|
skinparam defaultFontName "DM Sans"
|
|
skinparam defaultFontSize 13
|
|
skinparam titleFontName "Volkhov"
|
|
skinparam titleFontSize 20
|
|
skinparam backgroundColor #FAFCF9
|
|
skinparam defaultFontColor #28342A
|
|
skinparam titleFontColor #28342A
|
|
skinparam ArrowColor #628A5B
|
|
skinparam ActivityBackgroundColor #EAF0E8
|
|
skinparam ActivityBorderColor #547461
|
|
skinparam ActivityDiamondBackgroundColor #DCE8D8
|
|
skinparam ActivityDiamondBorderColor #547461
|
|
skinparam NoteBackgroundColor #EAF0E8
|
|
skinparam NoteBorderColor #547461
|
|
|
|
title The Biergarten Data Pipeline — Activity Diagram
|
|
|
|
|Main|
|
|
start
|
|
:ParseArguments(argc, argv);
|
|
if (Invalid args?) then (yes)
|
|
:spdlog::error;
|
|
stop
|
|
else (no)
|
|
endif
|
|
:Init CurlGlobalState & LlamaBackendState;
|
|
:Build DI injector;
|
|
|
|
:JsonLoader::LoadLocations("locations.json");
|
|
:JsonLoader::LoadBeerStyles("beer-styles.json");
|
|
|
|
:EnrichmentService::PreWarmBeerStyleCache(beer_styles);
|
|
note right
|
|
**NEW**: Beer styles do not need location context.
|
|
Wikipedia summaries for the entire palette are
|
|
fetched and cached globally at startup.
|
|
end note
|
|
|
|
:Initialize SqliteExportService;
|
|
note right
|
|
Opens SQLite connection.
|
|
Begins a single transaction
|
|
covering all five fixture types.
|
|
end note
|
|
:BiergartenPipelineOrchestrator::Run();
|
|
|
|
' ═══════════════════════════════════════════
|
|
' PHASE 0 — USER GENERATION
|
|
' ═══════════════════════════════════════════
|
|
|Orchestrator|
|
|
:RunUserPhase(sampled_locations);
|
|
:Create BoundedChannels\n(user_llm_ch, user_exp_ch);
|
|
|
|
fork
|
|
|Orchestrator|
|
|
:Loop: Send Locations → user_llm_ch;
|
|
:Close user_llm_ch;
|
|
fork again
|
|
|LLM Worker|
|
|
while (user_llm_ch has items?) is (yes)
|
|
:Receive Location;
|
|
:GenerateUser(location)\nvia DataGenerator;
|
|
:Send GeneratedUser → user_exp_ch;
|
|
endwhile (no)
|
|
:Close user_exp_ch;
|
|
fork again
|
|
|SQLite Worker|
|
|
while (user_exp_ch has items?) is (yes)
|
|
:Receive GeneratedUser;
|
|
:ProcessUser(user) → sqlite3_int64;
|
|
:Append → user_pool_;
|
|
endwhile (no)
|
|
end fork
|
|
|
|
|Orchestrator|
|
|
:Join LLM Worker, SQLite Worker;
|
|
|
|
' ═══════════════════════════════════════════
|
|
' PHASE 1 — BREWERY & BEER GENERATION
|
|
' Combined into a single dependent unit of work.
|
|
' ═══════════════════════════════════════════
|
|
:RunBreweryAndBeerPhase(sampled_locations);
|
|
:Create BoundedChannels\n(loc_ch, llm_ch, exp_ch);
|
|
|
|
fork
|
|
|Orchestrator|
|
|
:Loop: Send Locations → loc_ch;
|
|
:Close loc_ch;
|
|
fork again
|
|
|Enrichment Workers (xN)|
|
|
while (loc_ch has items?) is (yes)
|
|
:Receive Location;
|
|
:GetLocationContext(location,\nBreweryContextStrategy);
|
|
:Send EnrichedCity → llm_ch;
|
|
endwhile (no)
|
|
|Orchestrator|
|
|
:Join Enrichment Workers;
|
|
:Close llm_ch;
|
|
fork again
|
|
|LLM Worker|
|
|
while (llm_ch has items?) is (yes)
|
|
:Receive EnrichedCity;
|
|
|
|
:GenerateBrewery(location, context)\nvia DataGenerator;
|
|
|
|
:IBeerSelectionStrategy::SelectStyles(\n brewery, beer_style_palette_);
|
|
|
|
while (For each selected BeerStyle?) is (remaining)
|
|
:GetStyleContextFromCache(style);
|
|
note right
|
|
Guaranteed cache hit from startup.
|
|
end note
|
|
:GenerateBeer(brewery, style_context)\nvia DataGenerator;
|
|
:Attach GeneratedBeer to Brewery bundle;
|
|
endwhile (done)
|
|
|
|
:Send BreweryWithBeers Bundle → exp_ch;
|
|
note right
|
|
The next generation of a brewery is
|
|
entirely dependent on the current
|
|
brewery and its beers completing.
|
|
end note
|
|
endwhile (no)
|
|
:Close exp_ch;
|
|
fork again
|
|
|SQLite Worker|
|
|
while (exp_ch has items?) is (yes)
|
|
:Receive BreweryWithBeers Bundle;
|
|
:ProcessBrewery(brewery) → brewery_id;
|
|
:Append → brewery_pool_;
|
|
|
|
while (For each beer in bundle?) is (remaining)
|
|
:Set beer.brewery_id = brewery_id;
|
|
:ProcessBeer(beer) → sqlite3_int64;
|
|
:Append → beer_pool_;
|
|
endwhile (done)
|
|
endwhile (no)
|
|
end fork
|
|
|
|
|Orchestrator|
|
|
:Join LLM Worker, SQLite Worker;
|
|
note right
|
|
Both brewery_pool_ and beer_pool_
|
|
are now completely populated.
|
|
end note
|
|
|
|
' ═══════════════════════════════════════════
|
|
' PHASE 2 — CHECKIN GENERATION
|
|
' Sequential now that Breweries/Beers are done.
|
|
' ═══════════════════════════════════════════
|
|
:RunCheckinPhase();
|
|
:ICheckinDistributionStrategy::\nAssignActivityWeights(user_pool_);
|
|
|
|
while (For each GeneratedUser in user_pool_?) is (remaining)
|
|
:CheckinsForUser(user, brewery_pool_.size());
|
|
while (For each checkin index?) is (remaining)
|
|
:TimestampFor(user, index);
|
|
:Select brewery from brewery_pool_;
|
|
:GenerateCheckin(user, brewery, timestamp)\nvia DataGenerator;
|
|
:ProcessCheckin(checkin) → sqlite3_int64;
|
|
:Append → checkin_pool_;
|
|
endwhile (done)
|
|
endwhile (done)
|
|
|
|
' ═══════════════════════════════════════════
|
|
' PHASE 3 — RATING GENERATION
|
|
' ═══════════════════════════════════════════
|
|
:RunRatingPhase();
|
|
|
|
while (For each GeneratedCheckin in checkin_pool_?) is (remaining)
|
|
:Match brewery_id → select beer\nfrom beer_pool_ (same brewery_id);
|
|
if (Beer exists for brewery?) then (yes)
|
|
:GenerateRating(user, beer, checkin_id)\nvia DataGenerator;
|
|
:ProcessRating(rating);
|
|
else (no)
|
|
:Skip — brewery has no beers;
|
|
endif
|
|
endwhile (done)
|
|
|
|
' ═══════════════════════════════════════════
|
|
' TEARDOWN
|
|
' ═══════════════════════════════════════════
|
|
|Main|
|
|
:Finalize SqliteExportService;
|
|
note right
|
|
COMMIT covers all five fixture types.
|
|
end note
|
|
:spdlog::info "Pipeline complete in X ms";
|
|
stop
|
|
|
|
@enduml
|