@startuml BiergartenPipeline title Biergarten Pipeline - Class and Composition Diagram top to bottom direction skinparam shadowing false skinparam classAttributeIconSize 0 skinparam packageStyle rectangle package "Composition root" { class Main <> { +main(argc: int, argv: char**): int } class CurlGlobalState { +CurlGlobalState() +~CurlGlobalState() } class LlamaBackendState { +LlamaBackendState() +~LlamaBackendState() } note right of Main Binds with Boost.DI: - WebClient -> CURLWebClient - IEnrichmentService -> WikipediaService - DataGenerator -> MockGenerator or LlamaGenerator - std::string -> model_path - LlamaGenerator receives ApplicationOptions and model_path directly end note } package "Core orchestration" { class BiergartenDataGenerator { -context_service_: std::shared_ptr -generator_: std::unique_ptr -generated_breweries_: std::vector +BiergartenDataGenerator(context_service: std::shared_ptr, generator: std::unique_ptr) +Run(): bool {static} -QueryCitiesWithCountries(): std::vector -GenerateBreweries(cities: const std::vector&): void -LogResults(): void } } package "Data models" { class ApplicationOptions <> { +model_path: std::string +use_mocked: bool +temperature: float +top_p: float +top_k: uint32_t +n_ctx: uint32_t +seed: int } class Location <> { +city: std::string +state_province: std::string +iso3166_2: std::string +country: std::string +iso3166_1: std::string +latitude: double +longitude: double } class BreweryResult <> { +name: std::string +description: std::string } class UserResult <> { +username: std::string +bio: std::string } class EnrichedCity <> { +location: Location +region_context: std::string } class GeneratedBrewery <> { +location: Location +brewery: BreweryResult } } package "Generation" { interface DataGenerator { +GenerateBrewery(location: const Location&, region_context: const std::string&): BreweryResult +GenerateUser(locale: const std::string&): UserResult } class MockGenerator { +GenerateBrewery(location: const Location&, region_context: const std::string&): BreweryResult +GenerateUser(locale: const std::string&): UserResult } class LlamaGenerator { +LlamaGenerator(options: const ApplicationOptions&, model_path: const std::string&) +GenerateBrewery(location: const Location&, region_context: const std::string&): BreweryResult +GenerateUser(locale: const std::string&): UserResult } } package "HTTP" { interface WebClient { +Get(url: const std::string&): std::string +UrlEncode(value: const std::string&): std::string } class CURLWebClient { +Get(url: const std::string&): std::string +UrlEncode(value: const std::string&): std::string } } package "JSON handling" { class JsonLoader { {static} +LoadLocations(filepath: const std::string&): std::vector } } package "Wikipedia" { interface IEnrichmentService { +GetLocationContext(loc: const Location&): std::string } class WikipediaService { +WikipediaService(client: std::unique_ptr) +GetLocationContext(loc: const Location&): std::string } } Main --> CurlGlobalState Main --> LlamaBackendState Main --> ApplicationOptions Main --> BiergartenDataGenerator Main ..> IEnrichmentService : DI binding Main ..> DataGenerator : DI factory Main ..> CURLWebClient : DI binding BiergartenDataGenerator *-- GeneratedBrewery BiergartenDataGenerator ..> JsonLoader : LoadLocations() BiergartenDataGenerator --> IEnrichmentService : context lookup BiergartenDataGenerator --> DataGenerator : brewery generation BiergartenDataGenerator ..> EnrichedCity BiergartenDataGenerator ..> Location BiergartenDataGenerator ..> BreweryResult DataGenerator <|.. MockGenerator DataGenerator <|.. LlamaGenerator WebClient <|.. CURLWebClient IEnrichmentService <|.. WikipediaService WikipediaService *-- WebClient : unique_ptr note right of BiergartenDataGenerator Current behavior: samples up to four locations per run. Enrichment runs once per sampled city. If a lookup throws, that city is skipped. Empty context is retained and still passed to the generator. end note @enduml