mirror of
https://github.com/aaronpo97/the-biergarten-app.git
synced 2026-10-14 17:54:42 +00:00
* Remove restating comments in backend * Remove restating comments in frontend stories * Remove restating comments in pipeline * Remove restating comments in pipeline scripts * Restore location-clear comment in BreweryRepository * Remove stale/restating comments found in second pass * Restore mock curated-data branch comment in main.cc * Remove more restating/filler comments in backend * Document account-management endpoints, compose files, and env generator * Squashed commit of the following: commit a8a5b8abd9969859c695092fae6990d03b2135ed Author: Aaron Po <aaron.po97@gmail.com> Date: Sun Aug 23 23:53:56 2026 -0400 Add reverse geocoding for brewery street addresses (#245) * Add geocoding to seed application, remove postal codes from generator Update comments * Update pipeline class diagram for OpenAI generator * Update pipeline activity diagram for brewery coordinates and OpenAI backend * Add mailpit, production, db-only, and minimal environments to deployment diagram * Fix authentication flow diagram for Identity migration * Add account-management flows to authentication diagram * Fix database schema diagram note referencing removed AuthRepository
1 line
97 KiB
XML
1 line
97 KiB
XML
<?plantuml 1.2026.2?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentStyleType="text/css" data-diagram-type="CLASS" height="1526px" preserveAspectRatio="none" style="width:6680px;height:1526px;background:#FAFCF9;" version="1.1" viewBox="0 0 6680 1526" width="6680px" zoomAndPan="magnify"><title>The Biergarten Data Pipeline - Class Diagram</title><defs/><g><rect fill="#FAFCF9" height="1526" style="stroke:none;stroke-width:1;" width="6680" x="0" y="0"/><g class="title" data-source-line="25"><text fill="#28342A" font-family="'Volkhov'" font-size="20" font-weight="bold" lengthAdjust="spacing" textLength="445.1895" x="3110.6595" y="31.38">The Biergarten Data Pipeline - Class Diagram</text></g><!--class BiergartenPipelineOrchestrator--><g class="entity" data-qualified-name="BiergartenPipelineOrchestrator" data-source-line="27" id="ent0002"><rect fill="#FAFCF9" height="292.9508" rx="2.5" ry="2.5" style="stroke:#547461;stroke-width:0.5;" width="441.0784" x="2654.23" y="55.24"/><rect fill="#EAF0E8" height="29.0679" rx="2.5" ry="2.5" style="stroke:#EAF0E8;stroke-width:0.5;" width="441.0784" x="2654.23" y="55.24"/><rect fill="#EAF0E8" height="2.5" style="stroke:#EAF0E8;stroke-width:0.5;" width="441.0784" x="2654.23" y="81.8079"/><rect fill="none" height="292.9508" rx="2.5" ry="2.5" style="stroke:#547461;stroke-width:0.5;" width="441.0784" x="2654.23" y="55.24"/><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="207.2832" x="2771.1276" y="75.2059">BiergartenPipelineOrchestrator</text><line style="stroke:#547461;stroke-width:0.5;" x1="2655.23" x2="3094.3084" y1="84.3079" y2="84.3079"/><g data-visibility-modifier="PRIVATE_FIELD"><rect fill="none" height="6" style="stroke:#C82930;stroke-width:1;" width="6" x="2662.23" y="96.3419"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="226.7292" x="2673.23" y="103.2738">logger_ : std::shared_ptr<ILogger></text><g data-visibility-modifier="PRIVATE_FIELD"><rect fill="none" height="6" style="stroke:#C82930;stroke-width:1;" width="6" x="2662.23" y="115.4098"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="361.4086" x="2673.23" y="122.3417">context_service_ : std::unique_ptr<IEnrichmentService></text><g data-visibility-modifier="PRIVATE_FIELD"><rect fill="none" height="6" style="stroke:#C82930;stroke-width:1;" width="6" x="2662.23" y="134.4777"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="297.4289" x="2673.23" y="141.4097">generator_ : std::unique_ptr<DataGenerator></text><g data-visibility-modifier="PRIVATE_FIELD"><rect fill="none" height="6" style="stroke:#C82930;stroke-width:1;" width="6" x="2662.23" y="153.5456"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="284.325" x="2673.23" y="160.4776">exporter_ : std::unique_ptr<IExportService></text><g data-visibility-modifier="PRIVATE_FIELD"><rect fill="none" height="6" style="stroke:#C82930;stroke-width:1;" width="6" x="2662.23" y="172.6135"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="406.0964" x="2673.23" y="179.5455">curated_data_service_ : std::unique_ptr<ICuratedDataService></text><g data-visibility-modifier="PRIVATE_FIELD"><rect fill="none" height="6" style="stroke:#C82930;stroke-width:1;" width="6" x="2662.23" y="191.6814"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="271.487" x="2673.23" y="198.6134">application_options_ : ApplicationOptions</text><g data-visibility-modifier="PRIVATE_FIELD"><rect fill="none" height="6" style="stroke:#C82930;stroke-width:1;" width="6" x="2662.23" y="210.7494"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="342.6067" x="2673.23" y="217.6813">generated_breweries_ : std::vector<BreweryRecord></text><g data-visibility-modifier="PRIVATE_FIELD"><rect fill="none" height="6" style="stroke:#C82930;stroke-width:1;" width="6" x="2662.23" y="229.8173"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="289.1829" x="2673.23" y="236.7492">generated_users_ : std::vector<UserRecord></text><line style="stroke:#547461;stroke-width:0.5;" x1="2655.23" x2="3094.3084" y1="244.8512" y2="244.8512"/><g data-visibility-modifier="PUBLIC_METHOD"><ellipse cx="2665.23" cy="259.8852" fill="#84BE84" rx="3" ry="3" style="stroke:#038048;stroke-width:1;"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="74.6057" x="2673.23" y="263.8172">Run() : bool</text><g data-visibility-modifier="PRIVATE_METHOD"><rect fill="#F24D5C" height="6" style="stroke:#C82930;stroke-width:1;" width="6" x="2662.23" y="275.9531"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="232.2871" x="2673.23" y="282.8851">QueryLocations() : std::vector<City></text><g data-visibility-modifier="PRIVATE_METHOD"><rect fill="#F24D5C" height="6" style="stroke:#C82930;stroke-width:1;" width="6" x="2662.23" y="295.021"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="416.0784" x="2673.23" y="301.953">GenerateBreweries(cities : std::span<const EnrichedCity>) : void</text><g data-visibility-modifier="PRIVATE_METHOD"><rect fill="#F24D5C" height="6" style="stroke:#C82930;stroke-width:1;" width="6" x="2662.23" y="314.0889"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="387.7286" x="2673.23" y="321.0209">GenerateUsers(cities : std::span<const EnrichedCity>) : void</text><g data-visibility-modifier="PRIVATE_METHOD"><rect fill="#F24D5C" height="6" style="stroke:#C82930;stroke-width:1;" width="6" x="2662.23" y="333.1569"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="158.2274" x="2673.23" y="340.0888">LogResults() const : void</text></g><g class="entity" data-qualified-name="GMN3" data-source-line="44" id="ent0004"><path d="M3130.8,149.05 L3130.8,254.3896 L3494.7427,254.3896 L3494.7427,159.05 L3484.7427,149.05 L3130.8,149.05" fill="#EAF0E8" style="stroke:#547461;stroke-width:0.5;"/><path d="M3484.7427,149.05 L3484.7427,159.05 L3494.7427,159.05 L3484.7427,149.05" fill="#EAF0E8" style="stroke:#547461;stroke-width:1;"/><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="342.9427" x="3136.8" y="169.0159">GenerateBreweries and GenerateUsers both call the</text><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="306.4308" x="3136.8" y="188.0838">free function GetRandomCoordsWithinRange()</text><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="339.4848" x="3136.8" y="207.1517">(biergarten_pipeline_orchestrator/coords.h) to jitter</text><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="324.3508" x="3136.8" y="226.2197">a coordinate pair uniformly within 5km of the city</text><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="287.6709" x="3136.8" y="245.2876">centre for each generated record's address.</text></g><!--class LogLevel--><g class="entity" data-qualified-name="LogLevel" data-source-line="51" id="ent0006"><rect fill="#FAFCF9" height="140.4075" rx="2.5" ry="2.5" style="stroke:#547461;stroke-width:0.5;" width="95.5617" x="117.99" y="1341.3"/><rect fill="#EAF0E8" height="48.1358" rx="2.5" ry="2.5" style="stroke:#EAF0E8;stroke-width:0.5;" width="95.5617" x="117.99" y="1341.3"/><rect fill="#EAF0E8" height="2.5" style="stroke:#EAF0E8;stroke-width:0.5;" width="95.5617" x="117.99" y="1386.9358"/><rect fill="none" height="140.4075" rx="2.5" ry="2.5" style="stroke:#547461;stroke-width:0.5;" width="95.5617" x="117.99" y="1341.3"/><text fill="#28342A" font-family="DM Sans" font-size="14" font-style="italic" lengthAdjust="spacing" textLength="93.5617" x="118.99" y="1361.2659">«enumeration»</text><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="58.2678" x="136.6369" y="1380.3338">LogLevel</text><line style="stroke:#547461;stroke-width:0.5;" x1="118.99" x2="212.5517" y1="1389.4358" y2="1389.4358"/><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="43.9878" x="123.99" y="1408.4017">Debug</text><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="26.6839" x="123.99" y="1427.4697">Info</text><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="35.3079" x="123.99" y="1446.5376">Warn</text><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="33.5999" x="123.99" y="1465.6055">Error</text><line style="stroke:#547461;stroke-width:0.5;" x1="118.99" x2="212.5517" y1="1473.7075" y2="1473.7075"/></g><!--class PipelinePhase--><g class="entity" data-qualified-name="PipelinePhase" data-source-line="58" id="ent0007"><rect fill="#FAFCF9" height="216.6792" rx="2.5" ry="2.5" style="stroke:#547461;stroke-width:0.5;" width="197.5413" x="277" y="1303.16"/><rect fill="#EAF0E8" height="48.1358" rx="2.5" ry="2.5" style="stroke:#EAF0E8;stroke-width:0.5;" width="197.5413" x="277" y="1303.16"/><rect fill="#EAF0E8" height="2.5" style="stroke:#EAF0E8;stroke-width:0.5;" width="197.5413" x="277" y="1348.7958"/><rect fill="none" height="216.6792" rx="2.5" ry="2.5" style="stroke:#547461;stroke-width:0.5;" width="197.5413" x="277" y="1303.16"/><text fill="#28342A" font-family="DM Sans" font-size="14" font-style="italic" lengthAdjust="spacing" textLength="93.5617" x="328.9898" y="1323.1259">«enumeration»</text><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="91.9376" x="329.8018" y="1342.1938">PipelinePhase</text><line style="stroke:#547461;stroke-width:0.5;" x1="278" x2="473.5413" y1="1351.2958" y2="1351.2958"/><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="48.6918" x="283" y="1370.2617">Startup</text><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="75.8937" x="283" y="1389.3297">Enrichment</text><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="104.6776" x="283" y="1408.3976">UserGeneration</text><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="185.5413" x="283" y="1427.4655">BreweryAndBeerGeneration</text><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="125.9155" x="283" y="1446.5334">CheckinGeneration</text><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="116.5495" x="283" y="1465.6013">RatingGeneration</text><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="116.4935" x="283" y="1484.6692">FollowGeneration</text><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="66.0517" x="283" y="1503.7372">Teardown</text><line style="stroke:#547461;stroke-width:0.5;" x1="278" x2="473.5413" y1="1511.8391" y2="1511.8391"/></g><!--class LogDTO--><g class="entity" data-qualified-name="LogDTO" data-source-line="69" id="ent0008"><rect fill="#FAFCF9" height="102.2717" rx="2.5" ry="2.5" style="stroke:#547461;stroke-width:0.5;" width="167.6874" x="17.92" y="1091.29"/><rect fill="#EAF0E8" height="29.0679" rx="2.5" ry="2.5" style="stroke:#EAF0E8;stroke-width:0.5;" width="167.6874" x="17.92" y="1091.29"/><rect fill="#EAF0E8" height="2.5" style="stroke:#EAF0E8;stroke-width:0.5;" width="167.6874" x="17.92" y="1117.8579"/><rect fill="none" height="102.2717" rx="2.5" ry="2.5" style="stroke:#547461;stroke-width:0.5;" width="167.6874" x="17.92" y="1091.29"/><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="53.3538" x="75.0868" y="1111.2559">LogDTO</text><line style="stroke:#547461;stroke-width:0.5;" x1="18.92" x2="184.6074" y1="1120.3579" y2="1120.3579"/><g data-visibility-modifier="PUBLIC_FIELD"><ellipse cx="28.92" cy="1135.3919" fill="none" rx="3" ry="3" style="stroke:#038048;stroke-width:1;"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="99.4276" x="36.92" y="1139.3238">level : LogLevel</text><g data-visibility-modifier="PUBLIC_FIELD"><ellipse cx="28.92" cy="1154.4598" fill="none" rx="3" ry="3" style="stroke:#038048;stroke-width:1;"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="142.6874" x="36.92" y="1158.3917">phase : PipelinePhase</text><g data-visibility-modifier="PUBLIC_FIELD"><ellipse cx="28.92" cy="1173.5277" fill="none" rx="3" ry="3" style="stroke:#038048;stroke-width:1;"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="136.0795" x="36.92" y="1177.4597">message : std::string</text><line style="stroke:#547461;stroke-width:0.5;" x1="18.92" x2="184.6074" y1="1185.5616" y2="1185.5616"/></g><!--class LogEntry--><g class="entity" data-qualified-name="LogEntry" data-source-line="75" id="ent0009"><rect fill="#FAFCF9" height="159.4754" rx="2.5" ry="2.5" style="stroke:#547461;stroke-width:0.5;" width="352.6968" x="220.42" y="1062.69"/><rect fill="#EAF0E8" height="29.0679" rx="2.5" ry="2.5" style="stroke:#EAF0E8;stroke-width:0.5;" width="352.6968" x="220.42" y="1062.69"/><rect fill="#EAF0E8" height="2.5" style="stroke:#EAF0E8;stroke-width:0.5;" width="352.6968" x="220.42" y="1089.2579"/><rect fill="none" height="159.4754" rx="2.5" ry="2.5" style="stroke:#547461;stroke-width:0.5;" width="352.6968" x="220.42" y="1062.69"/><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="58.8278" x="367.3545" y="1082.6559">LogEntry</text><line style="stroke:#547461;stroke-width:0.5;" x1="221.42" x2="572.1168" y1="1091.7579" y2="1091.7579"/><g data-visibility-modifier="PUBLIC_FIELD"><ellipse cx="231.42" cy="1106.7919" fill="none" rx="3" ry="3" style="stroke:#038048;stroke-width:1;"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="327.6968" x="239.42" y="1110.7238">timestamp : std::chrono::system_clock::time_point</text><g data-visibility-modifier="PUBLIC_FIELD"><ellipse cx="231.42" cy="1125.8598" fill="none" rx="3" ry="3" style="stroke:#038048;stroke-width:1;"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="180.5293" x="239.42" y="1129.7917">origin : std::source_location</text><g data-visibility-modifier="PUBLIC_FIELD"><ellipse cx="231.42" cy="1144.9277" fill="none" rx="3" ry="3" style="stroke:#038048;stroke-width:1;"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="164.7654" x="239.42" y="1148.8597">thread_id : std::thread::id</text><g data-visibility-modifier="PUBLIC_FIELD"><ellipse cx="231.42" cy="1163.9956" fill="none" rx="3" ry="3" style="stroke:#038048;stroke-width:1;"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="99.4276" x="239.42" y="1167.9276">level : LogLevel</text><g data-visibility-modifier="PUBLIC_FIELD"><ellipse cx="231.42" cy="1183.0635" fill="none" rx="3" ry="3" style="stroke:#038048;stroke-width:1;"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="142.6874" x="239.42" y="1186.9955">phase : PipelinePhase</text><g data-visibility-modifier="PUBLIC_FIELD"><ellipse cx="231.42" cy="1202.1314" fill="none" rx="3" ry="3" style="stroke:#038048;stroke-width:1;"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="136.0795" x="239.42" y="1206.0634">message : std::string</text><line style="stroke:#547461;stroke-width:0.5;" x1="221.42" x2="572.1168" y1="1214.1654" y2="1214.1654"/></g><!--class ILogger--><g class="entity" data-qualified-name="ILogger" data-source-line="84" id="ent0010"><rect fill="#FAFCF9" height="102.2717" rx="2.5" ry="2.5" style="stroke:#547461;stroke-width:0.5;" width="213.1873" x="779.17" y="448.26"/><rect fill="#EAF0E8" height="48.1358" rx="2.5" ry="2.5" style="stroke:#EAF0E8;stroke-width:0.5;" width="213.1873" x="779.17" y="448.26"/><rect fill="#EAF0E8" height="2.5" style="stroke:#EAF0E8;stroke-width:0.5;" width="213.1873" x="779.17" y="493.8958"/><rect fill="none" height="102.2717" rx="2.5" ry="2.5" style="stroke:#547461;stroke-width:0.5;" width="213.1873" x="779.17" y="448.26"/><text fill="#28342A" font-family="DM Sans" font-size="14" font-style="italic" lengthAdjust="spacing" textLength="68.0117" x="851.7578" y="468.2259">«interface»</text><text fill="#28342A" font-family="'DM Sans'" font-size="14" font-style="italic" lengthAdjust="spacing" textLength="47.8798" x="861.8237" y="487.2938">ILogger</text><line style="stroke:#547461;stroke-width:0.5;" x1="780.17" x2="991.3573" y1="496.3958" y2="496.3958"/><line style="stroke:#547461;stroke-width:0.5;" x1="780.17" x2="991.3573" y1="504.3958" y2="504.3958"/><g data-visibility-modifier="PUBLIC_METHOD"><ellipse cx="790.17" cy="519.4298" fill="#84BE84" rx="3" ry="3" style="stroke:#038048;stroke-width:1;"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="188.1873" x="798.17" y="523.3617">Log(payload : LogDTO) : void</text><g data-visibility-modifier="PRIVATE_METHOD"><rect fill="#F24D5C" height="6" style="stroke:#C82930;stroke-width:1;" width="6" x="787.17" y="535.4977"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" font-style="italic" lengthAdjust="spacing" textLength="182.4753" x="798.17" y="542.4297">DoLog(entry : LogEntry) : void</text></g><!--class LogProducer--><g class="entity" data-qualified-name="LogProducer" data-source-line="89" id="ent0011"><rect fill="#FAFCF9" height="83.2038" rx="2.5" ry="2.5" style="stroke:#547461;stroke-width:0.5;" width="293.5329" x="7" y="774.54"/><rect fill="#EAF0E8" height="29.0679" rx="2.5" ry="2.5" style="stroke:#EAF0E8;stroke-width:0.5;" width="293.5329" x="7" y="774.54"/><rect fill="#EAF0E8" height="2.5" style="stroke:#EAF0E8;stroke-width:0.5;" width="293.5329" x="7" y="801.1079"/><rect fill="none" height="83.2038" rx="2.5" ry="2.5" style="stroke:#547461;stroke-width:0.5;" width="293.5329" x="7" y="774.54"/><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="84.7977" x="111.3676" y="794.5059">LogProducer</text><line style="stroke:#547461;stroke-width:0.5;" x1="8" x2="299.5329" y1="803.6079" y2="803.6079"/><g data-visibility-modifier="PRIVATE_FIELD"><rect fill="none" height="6" style="stroke:#C82930;stroke-width:1;" width="6" x="15" y="815.6419"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="268.5329" x="26" y="822.5738">channel_ : BoundedChannel<LogEntry>&</text><line style="stroke:#547461;stroke-width:0.5;" x1="8" x2="299.5329" y1="830.6758" y2="830.6758"/><g data-visibility-modifier="PRIVATE_METHOD"><rect fill="#F24D5C" height="6" style="stroke:#C82930;stroke-width:1;" width="6" x="15" y="842.7098"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="194.7252" x="26" y="849.6417">DoLog(entry : LogEntry) : void</text></g><!--class LogDispatcher--><g class="entity" data-qualified-name="LogDispatcher" data-source-line="94" id="ent0012"><rect fill="#FAFCF9" height="102.2717" rx="2.5" ry="2.5" style="stroke:#547461;stroke-width:0.5;" width="334.6508" x="335.44" y="765.01"/><rect fill="#EAF0E8" height="29.0679" rx="2.5" ry="2.5" style="stroke:#EAF0E8;stroke-width:0.5;" width="334.6508" x="335.44" y="765.01"/><rect fill="#EAF0E8" height="2.5" style="stroke:#EAF0E8;stroke-width:0.5;" width="334.6508" x="335.44" y="791.5779"/><rect fill="none" height="102.2717" rx="2.5" ry="2.5" style="stroke:#547461;stroke-width:0.5;" width="334.6508" x="335.44" y="765.01"/><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="95.5216" x="455.0046" y="784.9759">LogDispatcher</text><line style="stroke:#547461;stroke-width:0.5;" x1="336.44" x2="669.0908" y1="794.0779" y2="794.0779"/><g data-visibility-modifier="PRIVATE_FIELD"><rect fill="none" height="6" style="stroke:#C82930;stroke-width:1;" width="6" x="343.44" y="806.1119"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="268.5329" x="354.44" y="813.0438">channel_ : BoundedChannel<LogEntry>&</text><line style="stroke:#547461;stroke-width:0.5;" x1="336.44" x2="669.0908" y1="821.1458" y2="821.1458"/><g data-visibility-modifier="PUBLIC_METHOD"><ellipse cx="346.44" cy="836.1798" fill="#84BE84" rx="3" ry="3" style="stroke:#038048;stroke-width:1;"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="73.2477" x="354.44" y="840.1117">Run() : void</text><g data-visibility-modifier="PRIVATE_METHOD"><rect fill="#F24D5C" height="6" style="stroke:#C82930;stroke-width:1;" width="6" x="343.44" y="852.2477"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="309.6508" x="354.44" y="859.1797">ToSpdlogLevel(level) : spdlog::level::level_enum</text></g><!--class IEnrichmentService--><g class="entity" data-qualified-name="IEnrichmentService" data-source-line="100" id="ent0013"><rect fill="#FAFCF9" height="83.2038" rx="2.5" ry="2.5" style="stroke:#547461;stroke-width:0.5;" width="376.9026" x="1139.32" y="457.79"/><rect fill="#EAF0E8" height="48.1358" rx="2.5" ry="2.5" style="stroke:#EAF0E8;stroke-width:0.5;" width="376.9026" x="1139.32" y="457.79"/><rect fill="#EAF0E8" height="2.5" style="stroke:#EAF0E8;stroke-width:0.5;" width="376.9026" x="1139.32" y="503.4258"/><rect fill="none" height="83.2038" rx="2.5" ry="2.5" style="stroke:#547461;stroke-width:0.5;" width="376.9026" x="1139.32" y="457.79"/><text fill="#28342A" font-family="DM Sans" font-size="14" font-style="italic" lengthAdjust="spacing" textLength="68.0117" x="1293.7654" y="477.7559">«interface»</text><text fill="#28342A" font-family="'DM Sans'" font-size="14" font-style="italic" lengthAdjust="spacing" textLength="118.5656" x="1268.4885" y="496.8238">IEnrichmentService</text><line style="stroke:#547461;stroke-width:0.5;" x1="1140.32" x2="1515.2226" y1="505.9258" y2="505.9258"/><line style="stroke:#547461;stroke-width:0.5;" x1="1140.32" x2="1515.2226" y1="513.9258" y2="513.9258"/><g data-visibility-modifier="PUBLIC_METHOD"><ellipse cx="1150.32" cy="528.9598" fill="#84BE84" rx="3" ry="3" style="stroke:#038048;stroke-width:1;"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="351.9026" x="1158.32" y="532.8917">GetLocationContext(loc : const Location&) : std::string</text></g><!--class MockEnrichmentService--><g class="entity" data-qualified-name="MockEnrichmentService" data-source-line="104" id="ent0014"><rect fill="#FAFCF9" height="64.1358" rx="2.5" ry="2.5" style="stroke:#547461;stroke-width:0.5;" width="376.9026" x="705.32" y="784.07"/><rect fill="#EAF0E8" height="29.0679" rx="2.5" ry="2.5" style="stroke:#EAF0E8;stroke-width:0.5;" width="376.9026" x="705.32" y="784.07"/><rect fill="#EAF0E8" height="2.5" style="stroke:#EAF0E8;stroke-width:0.5;" width="376.9026" x="705.32" y="810.6379"/><rect fill="none" height="64.1358" rx="2.5" ry="2.5" style="stroke:#547461;stroke-width:0.5;" width="376.9026" x="705.32" y="784.07"/><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="157.9614" x="814.7906" y="804.0359">MockEnrichmentService</text><line style="stroke:#547461;stroke-width:0.5;" x1="706.32" x2="1081.2226" y1="813.1379" y2="813.1379"/><line style="stroke:#547461;stroke-width:0.5;" x1="706.32" x2="1081.2226" y1="821.1379" y2="821.1379"/><g data-visibility-modifier="PUBLIC_METHOD"><ellipse cx="716.32" cy="836.1719" fill="#84BE84" rx="3" ry="3" style="stroke:#038048;stroke-width:1;"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="351.9026" x="724.32" y="840.1038">GetLocationContext(loc : const Location&) : std::string</text></g><!--class WikipediaEnrichmentService--><g class="entity" data-qualified-name="WikipediaEnrichmentService" data-source-line="108" id="ent0015"><rect fill="#FAFCF9" height="121.3396" rx="2.5" ry="2.5" style="stroke:#547461;stroke-width:0.5;" width="422.0526" x="1116.74" y="755.47"/><rect fill="#EAF0E8" height="29.0679" rx="2.5" ry="2.5" style="stroke:#EAF0E8;stroke-width:0.5;" width="422.0526" x="1116.74" y="755.47"/><rect fill="#EAF0E8" height="2.5" style="stroke:#EAF0E8;stroke-width:0.5;" width="422.0526" x="1116.74" y="782.0379"/><rect fill="none" height="121.3396" rx="2.5" ry="2.5" style="stroke:#547461;stroke-width:0.5;" width="422.0526" x="1116.74" y="755.47"/><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="186.8992" x="1234.3167" y="775.4359">WikipediaEnrichmentService</text><line style="stroke:#547461;stroke-width:0.5;" x1="1117.74" x2="1537.7926" y1="784.5379" y2="784.5379"/><g data-visibility-modifier="PRIVATE_FIELD"><rect fill="none" height="6" style="stroke:#C82930;stroke-width:1;" width="6" x="1124.74" y="796.5719"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="235.619" x="1135.74" y="803.5038">client_ : std::unique_ptr<WebClient></text><g data-visibility-modifier="PRIVATE_FIELD"><rect fill="none" height="6" style="stroke:#C82930;stroke-width:1;" width="6" x="1124.74" y="815.6398"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="397.0526" x="1135.74" y="822.5717">extract_cache_ : std::unordered_map<std::string, std::string></text><line style="stroke:#547461;stroke-width:0.5;" x1="1117.74" x2="1537.7926" y1="830.6737" y2="830.6737"/><g data-visibility-modifier="PUBLIC_METHOD"><ellipse cx="1127.74" cy="845.7077" fill="#84BE84" rx="3" ry="3" style="stroke:#038048;stroke-width:1;"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="351.9026" x="1135.74" y="849.6397">GetLocationContext(loc : const Location&) : std::string</text><g data-visibility-modifier="PRIVATE_METHOD"><rect fill="#F24D5C" height="6" style="stroke:#C82930;stroke-width:1;" width="6" x="1124.74" y="861.7756"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="318.2048" x="1135.74" y="868.7076">FetchExtract(query : std::string_view) : std::string</text></g><!--class WebClient--><g class="entity" data-qualified-name="WebClient" data-source-line="115" id="ent0016"><rect fill="#FAFCF9" height="102.2717" rx="2.5" ry="2.5" style="stroke:#547461;stroke-width:0.5;" width="340.4188" x="1320.56" y="1091.29"/><rect fill="#EAF0E8" height="48.1358" rx="2.5" ry="2.5" style="stroke:#EAF0E8;stroke-width:0.5;" width="340.4188" x="1320.56" y="1091.29"/><rect fill="#EAF0E8" height="2.5" style="stroke:#EAF0E8;stroke-width:0.5;" width="340.4188" x="1320.56" y="1136.9258"/><rect fill="none" height="102.2717" rx="2.5" ry="2.5" style="stroke:#547461;stroke-width:0.5;" width="340.4188" x="1320.56" y="1091.29"/><text fill="#28342A" font-family="DM Sans" font-size="14" font-style="italic" lengthAdjust="spacing" textLength="68.0117" x="1456.7635" y="1111.2559">«interface»</text><text fill="#28342A" font-family="'DM Sans'" font-size="14" font-style="italic" lengthAdjust="spacing" textLength="62.2578" x="1459.6405" y="1130.3238">WebClient</text><line style="stroke:#547461;stroke-width:0.5;" x1="1321.56" x2="1659.9788" y1="1139.4258" y2="1139.4258"/><line style="stroke:#547461;stroke-width:0.5;" x1="1321.56" x2="1659.9788" y1="1147.4258" y2="1147.4258"/><g data-visibility-modifier="PUBLIC_METHOD"><ellipse cx="1331.56" cy="1162.4598" fill="#84BE84" rx="3" ry="3" style="stroke:#038048;stroke-width:1;"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="253.721" x="1339.56" y="1166.3917">Get(url : const std::string&) : std::string</text><g data-visibility-modifier="PUBLIC_METHOD"><ellipse cx="1331.56" cy="1181.5277" fill="#84BE84" rx="3" ry="3" style="stroke:#038048;stroke-width:1;"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="315.4188" x="1339.56" y="1185.4597">UrlEncode(value : const std::string&) : std::string</text></g><!--class HttpWebClient--><g class="entity" data-qualified-name="HttpWebClient" data-source-line="120" id="ent0017"><rect fill="#FAFCF9" height="83.2038" rx="2.5" ry="2.5" style="stroke:#547461;stroke-width:0.5;" width="340.4188" x="1320.56" y="1369.9"/><rect fill="#EAF0E8" height="29.0679" rx="2.5" ry="2.5" style="stroke:#EAF0E8;stroke-width:0.5;" width="340.4188" x="1320.56" y="1369.9"/><rect fill="#EAF0E8" height="2.5" style="stroke:#EAF0E8;stroke-width:0.5;" width="340.4188" x="1320.56" y="1396.4679"/><rect fill="none" height="83.2038" rx="2.5" ry="2.5" style="stroke:#547461;stroke-width:0.5;" width="340.4188" x="1320.56" y="1369.9"/><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="96.2916" x="1442.6236" y="1389.8659">HttpWebClient</text><line style="stroke:#547461;stroke-width:0.5;" x1="1321.56" x2="1659.9788" y1="1398.9679" y2="1398.9679"/><line style="stroke:#547461;stroke-width:0.5;" x1="1321.56" x2="1659.9788" y1="1406.9679" y2="1406.9679"/><g data-visibility-modifier="PUBLIC_METHOD"><ellipse cx="1331.56" cy="1422.0019" fill="#84BE84" rx="3" ry="3" style="stroke:#038048;stroke-width:1;"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="253.721" x="1339.56" y="1425.9338">Get(url : const std::string&) : std::string</text><g data-visibility-modifier="PUBLIC_METHOD"><ellipse cx="1331.56" cy="1441.0698" fill="#84BE84" rx="3" ry="3" style="stroke:#038048;stroke-width:1;"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="315.4188" x="1339.56" y="1445.0017">UrlEncode(value : const std::string&) : std::string</text></g><!--class DataGenerator--><g class="entity" data-qualified-name="DataGenerator" data-source-line="125" id="ent0018"><rect fill="#FAFCF9" height="102.2717" rx="2.5" ry="2.5" style="stroke:#547461;stroke-width:0.5;" width="724.1434" x="2512.69" y="448.26"/><rect fill="#EAF0E8" height="48.1358" rx="2.5" ry="2.5" style="stroke:#EAF0E8;stroke-width:0.5;" width="724.1434" x="2512.69" y="448.26"/><rect fill="#EAF0E8" height="2.5" style="stroke:#EAF0E8;stroke-width:0.5;" width="724.1434" x="2512.69" y="493.8958"/><rect fill="none" height="102.2717" rx="2.5" ry="2.5" style="stroke:#547461;stroke-width:0.5;" width="724.1434" x="2512.69" y="448.26"/><text fill="#28342A" font-family="DM Sans" font-size="14" font-style="italic" lengthAdjust="spacing" textLength="68.0117" x="2840.7558" y="468.2259">«interface»</text><text fill="#28342A" font-family="'DM Sans'" font-size="14" font-style="italic" lengthAdjust="spacing" textLength="93.0856" x="2828.2189" y="487.2938">DataGenerator</text><line style="stroke:#547461;stroke-width:0.5;" x1="2513.69" x2="3235.8334" y1="496.3958" y2="496.3958"/><line style="stroke:#547461;stroke-width:0.5;" x1="2513.69" x2="3235.8334" y1="504.3958" y2="504.3958"/><g data-visibility-modifier="PUBLIC_METHOD"><ellipse cx="2523.69" cy="519.4298" fill="#84BE84" rx="3" ry="3" style="stroke:#038048;stroke-width:1;"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="395.8904" x="2531.69" y="523.3617">GenerateBrewery(city : const EnrichedCity&) : BreweryResult</text><g data-visibility-modifier="PUBLIC_METHOD"><ellipse cx="2523.69" cy="538.4977" fill="#84BE84" rx="3" ry="3" style="stroke:#038048;stroke-width:1;"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="699.1434" x="2531.69" y="542.4297">GenerateUser(city : const EnrichedCity&, persona : const UserPersona&, name : const Name&) : UserResult</text></g><!--class MockGenerator--><g class="entity" data-qualified-name="MockGenerator" data-source-line="130" id="ent0019"><rect fill="#FAFCF9" height="121.3396" rx="2.5" ry="2.5" style="stroke:#547461;stroke-width:0.5;" width="724.1434" x="3108.69" y="755.47"/><rect fill="#EAF0E8" height="29.0679" rx="2.5" ry="2.5" style="stroke:#EAF0E8;stroke-width:0.5;" width="724.1434" x="3108.69" y="755.47"/><rect fill="#EAF0E8" height="2.5" style="stroke:#EAF0E8;stroke-width:0.5;" width="724.1434" x="3108.69" y="782.0379"/><rect fill="none" height="121.3396" rx="2.5" ry="2.5" style="stroke:#547461;stroke-width:0.5;" width="724.1434" x="3108.69" y="755.47"/><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="102.9416" x="3419.2909" y="775.4359">MockGenerator</text><line style="stroke:#547461;stroke-width:0.5;" x1="3109.69" x2="3831.8334" y1="784.5379" y2="784.5379"/><line style="stroke:#547461;stroke-width:0.5;" x1="3109.69" x2="3831.8334" y1="792.5379" y2="792.5379"/><g data-visibility-modifier="PUBLIC_METHOD"><ellipse cx="3119.69" cy="807.5719" fill="#84BE84" rx="3" ry="3" style="stroke:#038048;stroke-width:1;"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="395.8904" x="3127.69" y="811.5038">GenerateBrewery(city : const EnrichedCity&) : BreweryResult</text><g data-visibility-modifier="PUBLIC_METHOD"><ellipse cx="3119.69" cy="826.6398" fill="#84BE84" rx="3" ry="3" style="stroke:#038048;stroke-width:1;"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="699.1434" x="3127.69" y="830.5717">GenerateUser(city : const EnrichedCity&, persona : const UserPersona&, name : const Name&) : UserResult</text><g data-visibility-modifier="PRIVATE_METHOD"><rect fill="#F24D5C" height="6" style="stroke:#C82930;stroke-width:1;" width="6" x="3116.69" y="842.7077"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="346.1206" x="3127.69" y="849.6397">DeterministicHash(location : const Location&) : size_t</text><g data-visibility-modifier="PRIVATE_METHOD"><rect fill="#F24D5C" height="6" style="stroke:#C82930;stroke-width:1;" width="6" x="3116.69" y="861.7756"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="697.3375" x="3127.69" y="868.7076">DeterministicHash(location : const Location&, persona : const UserPersona&, name : const Name&) : size_t</text></g><!--class LlamaGenerator--><g class="entity" data-qualified-name="LlamaGenerator" data-source-line="137" id="ent0020"><rect fill="#FAFCF9" height="235.7471" rx="2.5" ry="2.5" style="stroke:#547461;stroke-width:0.5;" width="397.9727" x="2675.78" y="698.27"/><rect fill="#EAF0E8" height="29.0679" rx="2.5" ry="2.5" style="stroke:#EAF0E8;stroke-width:0.5;" width="397.9727" x="2675.78" y="698.27"/><rect fill="#EAF0E8" height="2.5" style="stroke:#EAF0E8;stroke-width:0.5;" width="397.9727" x="2675.78" y="724.8379"/><rect fill="none" height="235.7471" rx="2.5" ry="2.5" style="stroke:#547461;stroke-width:0.5;" width="397.9727" x="2675.78" y="698.27"/><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="107.3236" x="2821.1045" y="718.2359">LlamaGenerator</text><line style="stroke:#547461;stroke-width:0.5;" x1="2676.78" x2="3072.7527" y1="727.3379" y2="727.3379"/><g data-visibility-modifier="PRIVATE_FIELD"><rect fill="none" height="6" style="stroke:#C82930;stroke-width:1;" width="6" x="2683.78" y="739.3719"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="147.2094" x="2694.78" y="746.3038">model_ : ModelHandle</text><g data-visibility-modifier="PRIVATE_FIELD"><rect fill="none" height="6" style="stroke:#C82930;stroke-width:1;" width="6" x="2683.78" y="758.4398"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="164.8774" x="2694.78" y="765.3717">context_ : ContextHandle</text><g data-visibility-modifier="PRIVATE_FIELD"><rect fill="none" height="6" style="stroke:#C82930;stroke-width:1;" width="6" x="2683.78" y="777.5077"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="372.9727" x="2694.78" y="784.4397">prompt_formatter_ : std::unique_ptr<IPromptFormatter></text><g data-visibility-modifier="PRIVATE_FIELD"><rect fill="none" height="6" style="stroke:#C82930;stroke-width:1;" width="6" x="2683.78" y="796.5756"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="362.6687" x="2694.78" y="803.5076">prompt_directory_ : std::unique_ptr<IPromptDirectory></text><g data-visibility-modifier="PRIVATE_FIELD"><rect fill="none" height="6" style="stroke:#C82930;stroke-width:1;" width="6" x="2683.78" y="815.6435"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="126.3495" x="2694.78" y="822.5755">rng_ : std::mt19937</text><line style="stroke:#547461;stroke-width:0.5;" x1="2676.78" x2="3072.7527" y1="830.6775" y2="830.6775"/><g data-visibility-modifier="PUBLIC_METHOD"><ellipse cx="2686.78" cy="845.7114" fill="#84BE84" rx="3" ry="3" style="stroke:#038048;stroke-width:1;"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="241.737" x="2694.78" y="849.6434">GenerateBrewery(...) : BreweryResult</text><g data-visibility-modifier="PUBLIC_METHOD"><ellipse cx="2686.78" cy="864.7794" fill="#84BE84" rx="3" ry="3" style="stroke:#038048;stroke-width:1;"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="193.7732" x="2694.78" y="868.7113">GenerateUser(...) : UserResult</text><g data-visibility-modifier="PRIVATE_METHOD"><rect fill="#F24D5C" height="6" style="stroke:#C82930;stroke-width:1;" width="6" x="2683.78" y="880.8473"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="284.3809" x="2694.78" y="887.7792">Load(model_path : const std::string&) : void</text><g data-visibility-modifier="PRIVATE_METHOD"><rect fill="#F24D5C" height="6" style="stroke:#C82930;stroke-width:1;" width="6" x="2683.78" y="899.9152"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="128.8695" x="2694.78" y="906.8472">Infer(...) : std::string</text><g data-visibility-modifier="PRIVATE_METHOD"><rect fill="#F24D5C" height="6" style="stroke:#C82930;stroke-width:1;" width="6" x="2683.78" y="918.9831"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="197.9453" x="2694.78" y="925.9151">InferFormatted(...) : std::string</text></g><!--class OpenAIGenerator--><g class="entity" data-qualified-name="OpenAIGenerator" data-source-line="150" id="ent0021"><rect fill="#FAFCF9" height="197.6113" rx="2.5" ry="2.5" style="stroke:#547461;stroke-width:0.5;" width="724.1434" x="1573.69" y="717.34"/><rect fill="#EAF0E8" height="29.0679" rx="2.5" ry="2.5" style="stroke:#EAF0E8;stroke-width:0.5;" width="724.1434" x="1573.69" y="717.34"/><rect fill="#EAF0E8" height="2.5" style="stroke:#EAF0E8;stroke-width:0.5;" width="724.1434" x="1573.69" y="743.9079"/><rect fill="none" height="197.6113" rx="2.5" ry="2.5" style="stroke:#547461;stroke-width:0.5;" width="724.1434" x="1573.69" y="717.34"/><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="117.3616" x="1877.0809" y="737.3059">OpenAIGenerator</text><line style="stroke:#547461;stroke-width:0.5;" x1="1574.69" x2="2296.8334" y1="746.4079" y2="746.4079"/><g data-visibility-modifier="PRIVATE_FIELD"><rect fill="none" height="6" style="stroke:#C82930;stroke-width:1;" width="6" x="1581.69" y="758.4419"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="132.3415" x="1592.69" y="765.3738">api_key_ : std::string</text><g data-visibility-modifier="PRIVATE_FIELD"><rect fill="none" height="6" style="stroke:#C82930;stroke-width:1;" width="6" x="1581.69" y="777.5098"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="125.2155" x="1592.69" y="784.4417">model_ : std::string</text><g data-visibility-modifier="PRIVATE_FIELD"><rect fill="none" height="6" style="stroke:#C82930;stroke-width:1;" width="6" x="1581.69" y="796.5777"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="226.7292" x="1592.69" y="803.5097">logger_ : std::shared_ptr<ILogger></text><g data-visibility-modifier="PRIVATE_FIELD"><rect fill="none" height="6" style="stroke:#C82930;stroke-width:1;" width="6" x="1581.69" y="815.6456"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="362.6687" x="1592.69" y="822.5776">prompt_directory_ : std::unique_ptr<IPromptDirectory></text><g data-visibility-modifier="PRIVATE_FIELD"><rect fill="none" height="6" style="stroke:#C82930;stroke-width:1;" width="6" x="1581.69" y="834.7135"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="269.3449" x="1592.69" y="841.6455">web_client_ : std::unique_ptr<WebClient></text><line style="stroke:#547461;stroke-width:0.5;" x1="1574.69" x2="2296.8334" y1="849.7475" y2="849.7475"/><g data-visibility-modifier="PUBLIC_METHOD"><ellipse cx="1584.69" cy="864.7814" fill="#84BE84" rx="3" ry="3" style="stroke:#038048;stroke-width:1;"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="395.8904" x="1592.69" y="868.7134">GenerateBrewery(city : const EnrichedCity&) : BreweryResult</text><g data-visibility-modifier="PUBLIC_METHOD"><ellipse cx="1584.69" cy="883.8494" fill="#84BE84" rx="3" ry="3" style="stroke:#038048;stroke-width:1;"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="699.1434" x="1592.69" y="887.7813">GenerateUser(city : const EnrichedCity&, persona : const UserPersona&, name : const Name&) : UserResult</text><g data-visibility-modifier="PRIVATE_METHOD"><rect fill="#F24D5C" height="6" style="stroke:#C82930;stroke-width:1;" width="6" x="1581.69" y="899.9173"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="255.499" x="1592.69" y="906.8492">CallChatCompletionsApi(...) : std::string</text></g><g class="entity" data-qualified-name="GMN22" data-source-line="162" id="ent0023"><path d="M2332.54,763.47 L2332.54,868.8096 L2641.0009,868.8096 L2641.0009,773.47 L2631.0009,763.47 L2332.54,763.47" fill="#EAF0E8" style="stroke:#547461;stroke-width:0.5;"/><path d="M2631.0009,763.47 L2631.0009,773.47 L2641.0009,773.47 L2631.0009,763.47" fill="#EAF0E8" style="stroke:#547461;stroke-width:1;"/><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="279.8589" x="2338.54" y="783.4359">Chat Completions with Structured Outputs</text><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="287.4609" x="2338.54" y="802.5038">(response_format: json_schema, strict: true)</text><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="270.283" x="2338.54" y="821.5717">guarantees schema-valid JSON, replacing</text><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="258.453" x="2338.54" y="840.6397">llama.cpp's GBNF grammar constraint -</text><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="198.1833" x="2338.54" y="859.7076">no IPromptFormatter needed.</text></g><!--class IPromptFormatter--><g class="entity" data-qualified-name="IPromptFormatter" data-source-line="169" id="ent0025"><rect fill="#FAFCF9" height="83.2038" rx="2.5" ry="2.5" style="stroke:#547461;stroke-width:0.5;" width="579.2441" x="2585.14" y="1100.82"/><rect fill="#EAF0E8" height="48.1358" rx="2.5" ry="2.5" style="stroke:#EAF0E8;stroke-width:0.5;" width="579.2441" x="2585.14" y="1100.82"/><rect fill="#EAF0E8" height="2.5" style="stroke:#EAF0E8;stroke-width:0.5;" width="579.2441" x="2585.14" y="1146.4558"/><rect fill="none" height="83.2038" rx="2.5" ry="2.5" style="stroke:#547461;stroke-width:0.5;" width="579.2441" x="2585.14" y="1100.82"/><text fill="#28342A" font-family="DM Sans" font-size="14" font-style="italic" lengthAdjust="spacing" textLength="68.0117" x="2840.7562" y="1120.7859">«interface»</text><text fill="#28342A" font-family="'DM Sans'" font-size="14" font-style="italic" lengthAdjust="spacing" textLength="113.1196" x="2818.2023" y="1139.8538">IPromptFormatter</text><line style="stroke:#547461;stroke-width:0.5;" x1="2586.14" x2="3163.3841" y1="1148.9558" y2="1148.9558"/><line style="stroke:#547461;stroke-width:0.5;" x1="2586.14" x2="3163.3841" y1="1156.9558" y2="1156.9558"/><g data-visibility-modifier="PUBLIC_METHOD"><ellipse cx="2596.14" cy="1171.9898" fill="#84BE84" rx="3" ry="3" style="stroke:#038048;stroke-width:1;"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="554.2441" x="2604.14" y="1175.9217">Format(system_prompt : std::string_view, user_prompt : std::string_view) : std::string</text></g><!--class Gemma4JinjaPromptFormatter--><g class="entity" data-qualified-name="Gemma4JinjaPromptFormatter" data-source-line="173" id="ent0026"><rect fill="#FAFCF9" height="64.1358" rx="2.5" ry="2.5" style="stroke:#547461;stroke-width:0.5;" width="579.2441" x="2585.14" y="1379.43"/><rect fill="#EAF0E8" height="29.0679" rx="2.5" ry="2.5" style="stroke:#EAF0E8;stroke-width:0.5;" width="579.2441" x="2585.14" y="1379.43"/><rect fill="#EAF0E8" height="2.5" style="stroke:#EAF0E8;stroke-width:0.5;" width="579.2441" x="2585.14" y="1405.9979"/><rect fill="none" height="64.1358" rx="2.5" ry="2.5" style="stroke:#547461;stroke-width:0.5;" width="579.2441" x="2585.14" y="1379.43"/><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="203.4053" x="2773.0594" y="1399.3959">Gemma4JinjaPromptFormatter</text><line style="stroke:#547461;stroke-width:0.5;" x1="2586.14" x2="3163.3841" y1="1408.4979" y2="1408.4979"/><line style="stroke:#547461;stroke-width:0.5;" x1="2586.14" x2="3163.3841" y1="1416.4979" y2="1416.4979"/><g data-visibility-modifier="PUBLIC_METHOD"><ellipse cx="2596.14" cy="1431.5319" fill="#84BE84" rx="3" ry="3" style="stroke:#038048;stroke-width:1;"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="554.2441" x="2604.14" y="1435.4638">Format(system_prompt : std::string_view, user_prompt : std::string_view) : std::string</text></g><!--class IPromptDirectory--><g class="entity" data-qualified-name="IPromptDirectory" data-source-line="177" id="ent0027"><rect fill="#FAFCF9" height="83.2038" rx="2.5" ry="2.5" style="stroke:#547461;stroke-width:0.5;" width="278.665" x="2085.43" y="1100.82"/><rect fill="#EAF0E8" height="48.1358" rx="2.5" ry="2.5" style="stroke:#EAF0E8;stroke-width:0.5;" width="278.665" x="2085.43" y="1100.82"/><rect fill="#EAF0E8" height="2.5" style="stroke:#EAF0E8;stroke-width:0.5;" width="278.665" x="2085.43" y="1146.4558"/><rect fill="none" height="83.2038" rx="2.5" ry="2.5" style="stroke:#547461;stroke-width:0.5;" width="278.665" x="2085.43" y="1100.82"/><text fill="#28342A" font-family="DM Sans" font-size="14" font-style="italic" lengthAdjust="spacing" textLength="68.0117" x="2190.7566" y="1120.7859">«interface»</text><text fill="#28342A" font-family="'DM Sans'" font-size="14" font-style="italic" lengthAdjust="spacing" textLength="107.4216" x="2171.0517" y="1139.8538">IPromptDirectory</text><line style="stroke:#547461;stroke-width:0.5;" x1="2086.43" x2="2363.095" y1="1148.9558" y2="1148.9558"/><line style="stroke:#547461;stroke-width:0.5;" x1="2086.43" x2="2363.095" y1="1156.9558" y2="1156.9558"/><g data-visibility-modifier="PUBLIC_METHOD"><ellipse cx="2096.43" cy="1171.9898" fill="#84BE84" rx="3" ry="3" style="stroke:#038048;stroke-width:1;"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="253.665" x="2104.43" y="1175.9217">Load(key : std::string_view) : std::string</text></g><!--class PromptDirectory--><g class="entity" data-qualified-name="PromptDirectory" data-source-line="181" id="ent0028"><rect fill="#FAFCF9" height="102.2717" rx="2.5" ry="2.5" style="stroke:#547461;stroke-width:0.5;" width="370.0707" x="2039.73" y="1360.36"/><rect fill="#EAF0E8" height="29.0679" rx="2.5" ry="2.5" style="stroke:#EAF0E8;stroke-width:0.5;" width="370.0707" x="2039.73" y="1360.36"/><rect fill="#EAF0E8" height="2.5" style="stroke:#EAF0E8;stroke-width:0.5;" width="370.0707" x="2039.73" y="1386.9279"/><rect fill="none" height="102.2717" rx="2.5" ry="2.5" style="stroke:#547461;stroke-width:0.5;" width="370.0707" x="2039.73" y="1360.36"/><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="110.1516" x="2169.6896" y="1380.3259">PromptDirectory</text><line style="stroke:#547461;stroke-width:0.5;" x1="2040.73" x2="2408.8007" y1="1389.4279" y2="1389.4279"/><g data-visibility-modifier="PRIVATE_FIELD"><rect fill="none" height="6" style="stroke:#C82930;stroke-width:1;" width="6" x="2047.73" y="1401.4619"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="223.1592" x="2058.73" y="1408.3938">prompt_dir_ : std::filesystem::path</text><g data-visibility-modifier="PRIVATE_FIELD"><rect fill="none" height="6" style="stroke:#C82930;stroke-width:1;" width="6" x="2047.73" y="1420.5298"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="345.0707" x="2058.73" y="1427.4617">cache_ : std::unordered_map<std::string, std::string></text><line style="stroke:#547461;stroke-width:0.5;" x1="2040.73" x2="2408.8007" y1="1435.5637" y2="1435.5637"/><g data-visibility-modifier="PUBLIC_METHOD"><ellipse cx="2050.73" cy="1450.5977" fill="#84BE84" rx="3" ry="3" style="stroke:#038048;stroke-width:1;"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="253.665" x="2058.73" y="1454.5297">Load(key : std::string_view) : std::string</text></g><!--class ICuratedDataService--><g class="entity" data-qualified-name="ICuratedDataService" data-source-line="187" id="ent0029"><rect fill="#FAFCF9" height="140.4075" rx="2.5" ry="2.5" style="stroke:#547461;stroke-width:0.5;" width="824.6913" x="3867.42" y="429.19"/><rect fill="#EAF0E8" height="48.1358" rx="2.5" ry="2.5" style="stroke:#EAF0E8;stroke-width:0.5;" width="824.6913" x="3867.42" y="429.19"/><rect fill="#EAF0E8" height="2.5" style="stroke:#EAF0E8;stroke-width:0.5;" width="824.6913" x="3867.42" y="474.8258"/><rect fill="none" height="140.4075" rx="2.5" ry="2.5" style="stroke:#547461;stroke-width:0.5;" width="824.6913" x="3867.42" y="429.19"/><text fill="#28342A" font-family="DM Sans" font-size="14" font-style="italic" lengthAdjust="spacing" textLength="68.0117" x="4245.7598" y="449.1559">«interface»</text><text fill="#28342A" font-family="'DM Sans'" font-size="14" font-style="italic" lengthAdjust="spacing" textLength="127.1195" x="4216.2059" y="468.2238">ICuratedDataService</text><line style="stroke:#547461;stroke-width:0.5;" x1="3868.42" x2="4691.1113" y1="477.3258" y2="477.3258"/><line style="stroke:#547461;stroke-width:0.5;" x1="3868.42" x2="4691.1113" y1="485.3258" y2="485.3258"/><g data-visibility-modifier="PUBLIC_METHOD"><ellipse cx="3878.42" cy="500.3598" fill="#84BE84" rx="3" ry="3" style="stroke:#038048;stroke-width:1;"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="547.8599" x="3886.42" y="504.2917">LoadLocations(filepath : const std::filesystem::path&) : const std::vector<Location>&</text><g data-visibility-modifier="PUBLIC_METHOD"><ellipse cx="3878.42" cy="519.4277" fill="#84BE84" rx="3" ry="3" style="stroke:#038048;stroke-width:1;"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="573.8019" x="3886.42" y="523.3597">LoadPersonas(filepath : const std::filesystem::path&) : const std::vector<UserPersona>&</text><g data-visibility-modifier="PUBLIC_METHOD"><ellipse cx="3878.42" cy="538.4956" fill="#84BE84" rx="3" ry="3" style="stroke:#038048;stroke-width:1;"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="799.6913" x="3886.42" y="542.4276">LoadForenamesByCountry(filepath : const std::filesystem::path&) : const std::unordered_map<std::string, forename_list>&</text><g data-visibility-modifier="PUBLIC_METHOD"><ellipse cx="3878.42" cy="557.5635" fill="#84BE84" rx="3" ry="3" style="stroke:#038048;stroke-width:1;"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="786.5734" x="3886.42" y="561.4955">LoadSurnamesByCountry(filepath : const std::filesystem::path&) : const std::unordered_map<std::string, surname_list>&</text></g><!--class JsonLoader--><g class="entity" data-qualified-name="JsonLoader" data-source-line="194" id="ent0030"><rect fill="#FAFCF9" height="140.4075" rx="2.5" ry="2.5" style="stroke:#547461;stroke-width:0.5;" width="824.6913" x="5035.42" y="745.94"/><rect fill="#EAF0E8" height="29.0679" rx="2.5" ry="2.5" style="stroke:#EAF0E8;stroke-width:0.5;" width="824.6913" x="5035.42" y="745.94"/><rect fill="#EAF0E8" height="2.5" style="stroke:#EAF0E8;stroke-width:0.5;" width="824.6913" x="5035.42" y="772.5079"/><rect fill="none" height="140.4075" rx="2.5" ry="2.5" style="stroke:#547461;stroke-width:0.5;" width="824.6913" x="5035.42" y="745.94"/><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="73.5977" x="5410.9668" y="765.9059">JsonLoader</text><line style="stroke:#547461;stroke-width:0.5;" x1="5036.42" x2="5859.1113" y1="775.0079" y2="775.0079"/><g data-visibility-modifier="PRIVATE_FIELD"><rect fill="none" height="6" style="stroke:#C82930;stroke-width:1;" width="6" x="5043.42" y="787.0419"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="92.9316" x="5054.42" y="793.9738">cache_ : cache</text><line style="stroke:#547461;stroke-width:0.5;" x1="5036.42" x2="5859.1113" y1="802.0758" y2="802.0758"/><g data-visibility-modifier="PUBLIC_METHOD"><ellipse cx="5046.42" cy="817.1098" fill="#84BE84" rx="3" ry="3" style="stroke:#038048;stroke-width:1;"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="547.8599" x="5054.42" y="821.0417">LoadLocations(filepath : const std::filesystem::path&) : const std::vector<Location>&</text><g data-visibility-modifier="PUBLIC_METHOD"><ellipse cx="5046.42" cy="836.1777" fill="#84BE84" rx="3" ry="3" style="stroke:#038048;stroke-width:1;"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="573.8019" x="5054.42" y="840.1097">LoadPersonas(filepath : const std::filesystem::path&) : const std::vector<UserPersona>&</text><g data-visibility-modifier="PUBLIC_METHOD"><ellipse cx="5046.42" cy="855.2456" fill="#84BE84" rx="3" ry="3" style="stroke:#038048;stroke-width:1;"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="799.6913" x="5054.42" y="859.1776">LoadForenamesByCountry(filepath : const std::filesystem::path&) : const std::unordered_map<std::string, forename_list>&</text><g data-visibility-modifier="PUBLIC_METHOD"><ellipse cx="5046.42" cy="874.3135" fill="#84BE84" rx="3" ry="3" style="stroke:#038048;stroke-width:1;"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="786.5734" x="5054.42" y="878.2455">LoadSurnamesByCountry(filepath : const std::filesystem::path&) : const std::unordered_map<std::string, surname_list>&</text></g><g class="entity" data-qualified-name="GMN31" data-source-line="203" id="ent0032"><path d="M5894.62,773.01 L5894.62,859.2816 L6192.903,859.2816 L6192.903,783.01 L6182.903,773.01 L5894.62,773.01" fill="#EAF0E8" style="stroke:#547461;stroke-width:0.5;"/><path d="M6182.903,773.01 L6182.903,783.01 L6192.903,783.01 L6182.903,773.01" fill="#EAF0E8" style="stroke:#547461;stroke-width:1;"/><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="277.283" x="5900.62" y="792.9759">Each Load* method memoizes its result in</text><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="235.6051" x="5900.62" y="812.0438">cache_ on first call; later calls on the</text><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="253.889" x="5900.62" y="831.1117">same instance return the cached value</text><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="125.9015" x="5900.62" y="850.1797">without re-parsing.</text></g><!--class MockCuratedDataService--><g class="entity" data-qualified-name="MockCuratedDataService" data-source-line="209" id="ent0034"><rect fill="#FAFCF9" height="197.6113" rx="2.5" ry="2.5" style="stroke:#547461;stroke-width:0.5;" width="824.6913" x="3867.42" y="717.34"/><rect fill="#EAF0E8" height="29.0679" rx="2.5" ry="2.5" style="stroke:#EAF0E8;stroke-width:0.5;" width="824.6913" x="3867.42" y="717.34"/><rect fill="#EAF0E8" height="2.5" style="stroke:#EAF0E8;stroke-width:0.5;" width="824.6913" x="3867.42" y="743.9079"/><rect fill="none" height="197.6113" rx="2.5" ry="2.5" style="stroke:#547461;stroke-width:0.5;" width="824.6913" x="3867.42" y="717.34"/><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="165.7454" x="4196.893" y="737.3059">MockCuratedDataService</text><line style="stroke:#547461;stroke-width:0.5;" x1="3868.42" x2="4691.1113" y1="746.4079" y2="746.4079"/><g data-visibility-modifier="PRIVATE_FIELD"><rect fill="none" height="6" style="stroke:#C82930;stroke-width:1;" width="6" x="3875.42" y="758.4419"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="217.4892" x="3886.42" y="765.3738">locations_ : std::vector<Location></text><g data-visibility-modifier="PRIVATE_FIELD"><rect fill="none" height="6" style="stroke:#C82930;stroke-width:1;" width="6" x="3875.42" y="777.5098"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="247.2951" x="3886.42" y="784.4417">personas_ : std::vector<UserPersona></text><g data-visibility-modifier="PRIVATE_FIELD"><rect fill="none" height="6" style="stroke:#C82930;stroke-width:1;" width="6" x="3875.42" y="796.5777"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="480.4081" x="3886.42" y="803.5097">forenames_by_country_ : std::unordered_map<std::string, forename_list></text><g data-visibility-modifier="PRIVATE_FIELD"><rect fill="none" height="6" style="stroke:#C82930;stroke-width:1;" width="6" x="3875.42" y="815.6456"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="468.7603" x="3886.42" y="822.5776">surnames_by_country_ : std::unordered_map<std::string, surname_list></text><line style="stroke:#547461;stroke-width:0.5;" x1="3868.42" x2="4691.1113" y1="830.6796" y2="830.6796"/><g data-visibility-modifier="PUBLIC_METHOD"><ellipse cx="3878.42" cy="845.7135" fill="#84BE84" rx="3" ry="3" style="stroke:#038048;stroke-width:1;"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="547.8599" x="3886.42" y="849.6455">LoadLocations(filepath : const std::filesystem::path&) : const std::vector<Location>&</text><g data-visibility-modifier="PUBLIC_METHOD"><ellipse cx="3878.42" cy="864.7814" fill="#84BE84" rx="3" ry="3" style="stroke:#038048;stroke-width:1;"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="573.8019" x="3886.42" y="868.7134">LoadPersonas(filepath : const std::filesystem::path&) : const std::vector<UserPersona>&</text><g data-visibility-modifier="PUBLIC_METHOD"><ellipse cx="3878.42" cy="883.8494" fill="#84BE84" rx="3" ry="3" style="stroke:#038048;stroke-width:1;"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="799.6913" x="3886.42" y="887.7813">LoadForenamesByCountry(filepath : const std::filesystem::path&) : const std::unordered_map<std::string, forename_list>&</text><g data-visibility-modifier="PUBLIC_METHOD"><ellipse cx="3878.42" cy="902.9173" fill="#84BE84" rx="3" ry="3" style="stroke:#038048;stroke-width:1;"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="786.5734" x="3886.42" y="906.8492">LoadSurnamesByCountry(filepath : const std::filesystem::path&) : const std::unordered_map<std::string, surname_list>&</text></g><g class="entity" data-qualified-name="GMN35" data-source-line="221" id="ent0036"><path d="M4726.83,782.54 L4726.83,849.7437 L5000.697,849.7437 L5000.697,792.54 L4990.697,782.54 L4726.83,782.54" fill="#EAF0E8" style="stroke:#547461;stroke-width:0.5;"/><path d="M4990.697,782.54 L4990.697,792.54 L5000.697,792.54 L4990.697,782.54" fill="#EAF0E8" style="stroke:#547461;stroke-width:1;"/><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="235.0311" x="4732.83" y="802.5059">Fixed 4-location / 3-persona dataset</text><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="252.867" x="4732.83" y="821.5738">(US, DE, FR, BE) used in --mocked runs;</text><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="210.2372" x="4732.83" y="840.6417">filepath arguments are ignored.</text></g><!--class IExportService--><g class="entity" data-qualified-name="IExportService" data-source-line="226" id="ent0038"><rect fill="#FAFCF9" height="140.4075" rx="2.5" ry="2.5" style="stroke:#547461;stroke-width:0.5;" width="410.0405" x="5044.75" y="429.19"/><rect fill="#EAF0E8" height="48.1358" rx="2.5" ry="2.5" style="stroke:#EAF0E8;stroke-width:0.5;" width="410.0405" x="5044.75" y="429.19"/><rect fill="#EAF0E8" height="2.5" style="stroke:#EAF0E8;stroke-width:0.5;" width="410.0405" x="5044.75" y="474.8258"/><rect fill="none" height="140.4075" rx="2.5" ry="2.5" style="stroke:#547461;stroke-width:0.5;" width="410.0405" x="5044.75" y="429.19"/><text fill="#28342A" font-family="DM Sans" font-size="14" font-style="italic" lengthAdjust="spacing" textLength="68.0117" x="5215.7644" y="449.1559">«interface»</text><text fill="#28342A" font-family="'DM Sans'" font-size="14" font-style="italic" lengthAdjust="spacing" textLength="87.8077" x="5205.8664" y="468.2238">IExportService</text><line style="stroke:#547461;stroke-width:0.5;" x1="5045.75" x2="5453.7905" y1="477.3258" y2="477.3258"/><line style="stroke:#547461;stroke-width:0.5;" x1="5045.75" x2="5453.7905" y1="485.3258" y2="485.3258"/><g data-visibility-modifier="PUBLIC_METHOD"><ellipse cx="5055.75" cy="500.3598" fill="#84BE84" rx="3" ry="3" style="stroke:#038048;stroke-width:1;"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="102.4656" x="5063.75" y="504.2917">Initialize() : void</text><g data-visibility-modifier="PUBLIC_METHOD"><ellipse cx="5055.75" cy="519.4277" fill="#84BE84" rx="3" ry="3" style="stroke:#038048;stroke-width:1;"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="385.0405" x="5063.75" y="523.3597">ProcessRecord(brewery : const BreweryRecord&) : uint64_t</text><g data-visibility-modifier="PUBLIC_METHOD"><ellipse cx="5055.75" cy="538.4956" fill="#84BE84" rx="3" ry="3" style="stroke:#038048;stroke-width:1;"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="335.9847" x="5063.75" y="542.4276">ProcessRecord(user : const UserRecord&) : uint64_t</text><g data-visibility-modifier="PUBLIC_METHOD"><ellipse cx="5055.75" cy="557.5635" fill="#84BE84" rx="3" ry="3" style="stroke:#038048;stroke-width:1;"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="96.3196" x="5063.75" y="561.4955">Finalize() : void</text></g><!--class SqliteExportService--><g class="entity" data-qualified-name="SqliteExportService" data-source-line="233" id="ent0039"><rect fill="#FAFCF9" height="331.0867" rx="2.5" ry="2.5" style="stroke:#547461;stroke-width:0.5;" width="445.4885" x="6228.02" y="650.6"/><rect fill="#EAF0E8" height="29.0679" rx="2.5" ry="2.5" style="stroke:#EAF0E8;stroke-width:0.5;" width="445.4885" x="6228.02" y="650.6"/><rect fill="#EAF0E8" height="2.5" style="stroke:#EAF0E8;stroke-width:0.5;" width="445.4885" x="6228.02" y="677.1679"/><rect fill="none" height="331.0867" rx="2.5" ry="2.5" style="stroke:#547461;stroke-width:0.5;" width="445.4885" x="6228.02" y="650.6"/><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="126.2795" x="6387.6245" y="670.5659">SqliteExportService</text><line style="stroke:#547461;stroke-width:0.5;" x1="6229.02" x2="6672.5085" y1="679.6679" y2="679.6679"/><g data-visibility-modifier="PRIVATE_FIELD"><rect fill="none" height="6" style="stroke:#C82930;stroke-width:1;" width="6" x="6236.02" y="691.7019"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="384.1306" x="6247.02" y="698.6338">date_time_provider_ : std::unique_ptr<IDateTimeProvider></text><g data-visibility-modifier="PRIVATE_FIELD"><rect fill="none" height="6" style="stroke:#C82930;stroke-width:1;" width="6" x="6236.02" y="710.7698"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="210.4472" x="6247.02" y="717.7017">run_timestamp_utc_ : std::string</text><g data-visibility-modifier="PRIVATE_FIELD"><rect fill="none" height="6" style="stroke:#C82930;stroke-width:1;" width="6" x="6236.02" y="729.8377"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="246.1471" x="6247.02" y="736.7697">database_path_ : std::filesystem::path</text><g data-visibility-modifier="PRIVATE_FIELD"><rect fill="none" height="6" style="stroke:#C82930;stroke-width:1;" width="6" x="6236.02" y="748.9056"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="231.4751" x="6247.02" y="755.8376">db_handle_ : SqliteDatabaseHandle</text><g data-visibility-modifier="PRIVATE_FIELD"><rect fill="none" height="6" style="stroke:#C82930;stroke-width:1;" width="6" x="6236.02" y="767.9735"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="301.4329" x="6247.02" y="774.9055">insert_location_stmt_ : SqliteStatementHandle</text><g data-visibility-modifier="PRIVATE_FIELD"><rect fill="none" height="6" style="stroke:#C82930;stroke-width:1;" width="6" x="6236.02" y="787.0414"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="303.0988" x="6247.02" y="793.9734">insert_brewery_stmt_ : SqliteStatementHandle</text><g data-visibility-modifier="PRIVATE_FIELD"><rect fill="none" height="6" style="stroke:#C82930;stroke-width:1;" width="6" x="6236.02" y="806.1094"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="278.025" x="6247.02" y="813.0413">insert_user_stmt_ : SqliteStatementHandle</text><g data-visibility-modifier="PRIVATE_FIELD"><rect fill="none" height="6" style="stroke:#C82930;stroke-width:1;" width="6" x="6236.02" y="825.1773"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="160.6634" x="6247.02" y="832.1092">transaction_open_ : bool</text><g data-visibility-modifier="PRIVATE_FIELD"><rect fill="none" height="6" style="stroke:#C82930;stroke-width:1;" width="6" x="6236.02" y="844.2452"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="420.4885" x="6247.02" y="851.1772">location_cache_ : std::unordered_map<std::string, sqlite3_int64></text><line style="stroke:#547461;stroke-width:0.5;" x1="6229.02" x2="6672.5085" y1="859.2791" y2="859.2791"/><g data-visibility-modifier="PUBLIC_METHOD"><ellipse cx="6239.02" cy="874.3131" fill="#84BE84" rx="3" ry="3" style="stroke:#038048;stroke-width:1;"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="102.4656" x="6247.02" y="878.2451">Initialize() : void</text><g data-visibility-modifier="PUBLIC_METHOD"><ellipse cx="6239.02" cy="893.381" fill="#84BE84" rx="3" ry="3" style="stroke:#038048;stroke-width:1;"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="385.0405" x="6247.02" y="897.313">ProcessRecord(brewery : const BreweryRecord&) : uint64_t</text><g data-visibility-modifier="PUBLIC_METHOD"><ellipse cx="6239.02" cy="912.4489" fill="#84BE84" rx="3" ry="3" style="stroke:#038048;stroke-width:1;"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="335.9847" x="6247.02" y="916.3809">ProcessRecord(user : const UserRecord&) : uint64_t</text><g data-visibility-modifier="PUBLIC_METHOD"><ellipse cx="6239.02" cy="931.5169" fill="#84BE84" rx="3" ry="3" style="stroke:#038048;stroke-width:1;"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="96.3196" x="6247.02" y="935.4488">Finalize() : void</text><g data-visibility-modifier="PRIVATE_METHOD"><rect fill="#F24D5C" height="6" style="stroke:#C82930;stroke-width:1;" width="6" x="6236.02" y="947.5848"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="154.3634" x="6247.02" y="954.5167">InitializeSchema() : void</text><g data-visibility-modifier="PRIVATE_METHOD"><rect fill="#F24D5C" height="6" style="stroke:#C82930;stroke-width:1;" width="6" x="6236.02" y="966.6527"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="391.5365" x="6247.02" y="973.5847">ResolveLocationId(location : const Location&) : sqlite3_int64</text></g><!--class IDateTimeProvider--><g class="entity" data-qualified-name="IDateTimeProvider" data-source-line="251" id="ent0040"><rect fill="#FAFCF9" height="83.2038" rx="2.5" ry="2.5" style="stroke:#547461;stroke-width:0.5;" width="229.5672" x="6335.98" y="1100.82"/><rect fill="#EAF0E8" height="48.1358" rx="2.5" ry="2.5" style="stroke:#EAF0E8;stroke-width:0.5;" width="229.5672" x="6335.98" y="1100.82"/><rect fill="#EAF0E8" height="2.5" style="stroke:#EAF0E8;stroke-width:0.5;" width="229.5672" x="6335.98" y="1146.4558"/><rect fill="none" height="83.2038" rx="2.5" ry="2.5" style="stroke:#547461;stroke-width:0.5;" width="229.5672" x="6335.98" y="1100.82"/><text fill="#28342A" font-family="DM Sans" font-size="14" font-style="italic" lengthAdjust="spacing" textLength="68.0117" x="6416.7577" y="1120.7859">«interface»</text><text fill="#28342A" font-family="'DM Sans'" font-size="14" font-style="italic" lengthAdjust="spacing" textLength="115.5276" x="6392.9998" y="1139.8538">IDateTimeProvider</text><line style="stroke:#547461;stroke-width:0.5;" x1="6336.98" x2="6564.5472" y1="1148.9558" y2="1148.9558"/><line style="stroke:#547461;stroke-width:0.5;" x1="6336.98" x2="6564.5472" y1="1156.9558" y2="1156.9558"/><g data-visibility-modifier="PUBLIC_METHOD"><ellipse cx="6346.98" cy="1171.9898" fill="#84BE84" rx="3" ry="3" style="stroke:#038048;stroke-width:1;"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="204.5672" x="6354.98" y="1175.9217">GetUtcTimestamp() : std::string</text></g><!--class SystemDateTimeProvider--><g class="entity" data-qualified-name="SystemDateTimeProvider" data-source-line="255" id="ent0041"><rect fill="#FAFCF9" height="64.1358" rx="2.5" ry="2.5" style="stroke:#547461;stroke-width:0.5;" width="229.5672" x="6335.98" y="1379.43"/><rect fill="#EAF0E8" height="29.0679" rx="2.5" ry="2.5" style="stroke:#EAF0E8;stroke-width:0.5;" width="229.5672" x="6335.98" y="1379.43"/><rect fill="#EAF0E8" height="2.5" style="stroke:#EAF0E8;stroke-width:0.5;" width="229.5672" x="6335.98" y="1405.9979"/><rect fill="none" height="64.1358" rx="2.5" ry="2.5" style="stroke:#547461;stroke-width:0.5;" width="229.5672" x="6335.98" y="1379.43"/><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="166.7114" x="6367.4079" y="1399.3959">SystemDateTimeProvider</text><line style="stroke:#547461;stroke-width:0.5;" x1="6336.98" x2="6564.5472" y1="1408.4979" y2="1408.4979"/><line style="stroke:#547461;stroke-width:0.5;" x1="6336.98" x2="6564.5472" y1="1416.4979" y2="1416.4979"/><g data-visibility-modifier="PUBLIC_METHOD"><ellipse cx="6346.98" cy="1431.5319" fill="#84BE84" rx="3" ry="3" style="stroke:#038048;stroke-width:1;"/></g><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="204.5672" x="6354.98" y="1435.4638">GetUtcTimestamp() : std::string</text></g><!--link BiergartenPipelineOrchestrator to GMN3--><g class="link" data-entity-1="ent0002" data-entity-2="ent0004" data-link-type="association" data-source-line="44" id="lnk5"><path d="M3095.48,201.72 C3107.22,201.72 3118.96,201.72 3130.7,201.72" fill="none" id="BiergartenPipelineOrchestrator-GMN3" style="stroke:#628A5B;stroke-width:1;stroke-dasharray:7,7;"/></g><!--link OpenAIGenerator to GMN22--><g class="link" data-entity-1="ent0021" data-entity-2="ent0023" data-link-type="association" data-source-line="162" id="lnk24"><path d="M2298.17,816.14 C2309.46,816.14 2320.76,816.14 2332.06,816.14" fill="none" id="OpenAIGenerator-GMN22" style="stroke:#628A5B;stroke-width:1;stroke-dasharray:7,7;"/></g><!--link JsonLoader to GMN31--><g class="link" data-entity-1="ent0030" data-entity-2="ent0032" data-link-type="association" data-source-line="203" id="lnk33"><path d="M5860.43,816.14 C5871.77,816.14 5883.11,816.14 5894.44,816.14" fill="none" id="JsonLoader-GMN31" style="stroke:#628A5B;stroke-width:1;stroke-dasharray:7,7;"/></g><!--link MockCuratedDataService to GMN35--><g class="link" data-entity-1="ent0034" data-entity-2="ent0036" data-link-type="association" data-source-line="221" id="lnk37"><path d="M4692.39,816.14 C4703.77,816.14 4715.15,816.14 4726.53,816.14" fill="none" id="MockCuratedDataService-GMN35" style="stroke:#628A5B;stroke-width:1;stroke-dasharray:7,7;"/></g><!--reverse link BiergartenPipelineOrchestrator to ILogger--><g class="link" data-entity-1="ent0002" data-entity-2="ent0010" data-link-type="composition" data-source-line="260" id="lnk42"><path codeLine="260" d="M2641.9842,221.0888 C2307.7842,249.8488 1666.67,314.84 1121.77,429.19 C1079.04,438.16 1032.79,451 992.66,463.2" fill="none" id="BiergartenPipelineOrchestrator-backto-ILogger" style="stroke:#628A5B;stroke-width:1;"/><polygon fill="#628A5B" points="2653.94,220.06,2647.6191,216.5891,2641.9842,221.0888,2648.3051,224.5597,2653.94,220.06" style="stroke:#628A5B;stroke-width:1;"/><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="34.8319" x="1358.68" y="394.1559">owns</text></g><!--reverse link BiergartenPipelineOrchestrator to IEnrichmentService--><g class="link" data-entity-1="ent0002" data-entity-2="ent0013" data-link-type="composition" data-source-line="261" id="lnk43"><path codeLine="261" d="M2642.113,251.7712 C2445.533,292.7912 2161.79,350.88 1903.77,399.19 C1774.73,423.35 1629.01,448.43 1516.42,467.33" fill="none" id="BiergartenPipelineOrchestrator-backto-IEnrichmentService" style="stroke:#628A5B;stroke-width:1;"/><polygon fill="#628A5B" points="2653.86,249.32,2647.1694,246.6299,2642.113,251.7712,2648.8036,254.4612,2653.86,249.32" style="stroke:#628A5B;stroke-width:1;"/><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="34.8319" x="2010.33" y="394.1559">owns</text></g><!--reverse link BiergartenPipelineOrchestrator to DataGenerator--><g class="link" data-entity-1="ent0002" data-entity-2="ent0018" data-link-type="composition" data-source-line="262" id="lnk44"><path codeLine="262" d="M2874.77,360.39 C2874.77,396.25 2874.77,420.28 2874.77,448.04" fill="none" id="BiergartenPipelineOrchestrator-backto-DataGenerator" style="stroke:#628A5B;stroke-width:1;"/><polygon fill="#628A5B" points="2874.77,348.39,2870.77,354.39,2874.77,360.39,2878.77,354.39,2874.77,348.39" style="stroke:#628A5B;stroke-width:1;"/><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="34.8319" x="2875.77" y="394.1559">owns</text></g><!--reverse link BiergartenPipelineOrchestrator to IExportService--><g class="link" data-entity-1="ent0002" data-entity-2="ent0038" data-link-type="composition" data-source-line="263" id="lnk45"><path codeLine="263" d="M3106.9349,346.2154 C3112.9549,348.4254 3107.73,346.33 3113.77,348.19 C3283.46,400.45 4532.91,413.39 4709.77,429.19 C4820.62,439.09 4943.74,454.53 5044.39,468.31" fill="none" id="BiergartenPipelineOrchestrator-backto-IExportService" style="stroke:#628A5B;stroke-width:1;"/><polygon fill="#628A5B" points="3095.67,342.08,3099.924,347.9027,3106.9349,346.2154,3102.6809,340.3927,3095.67,342.08" style="stroke:#628A5B;stroke-width:1;"/><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="34.8319" x="3782.54" y="394.1559">owns</text></g><!--reverse link BiergartenPipelineOrchestrator to ICuratedDataService--><g class="link" data-entity-1="ent0002" data-entity-2="ent0029" data-link-type="composition" data-source-line="264" id="lnk46"><path codeLine="264" d="M3106.6603,345.7951 C3112.7403,348.2451 3107.69,346.06 3113.77,348.19 C3355.11,432.87 3639.56,470.64 3867.1,487.13" fill="none" id="BiergartenPipelineOrchestrator-backto-ICuratedDataService" style="stroke:#628A5B;stroke-width:1;"/><polygon fill="#628A5B" points="3095.53,341.31,3099.6001,347.2626,3106.6603,345.7951,3102.5902,339.8424,3095.53,341.31" style="stroke:#628A5B;stroke-width:1;"/><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="34.8319" x="3267.22" y="394.1559">owns</text></g><!--reverse link LogEntry to LogLevel--><g class="link" data-entity-1="ent0009" data-entity-2="ent0006" data-link-type="composition" data-source-line="266" id="lnk47"><path codeLine="266" d="M320.5492,1231.5455 C283.6892,1274.1555 246.97,1316.62 213.81,1354.96" fill="none" id="LogEntry-backto-LogLevel" style="stroke:#628A5B;stroke-width:1;"/><polygon fill="#628A5B" points="328.4,1222.47,321.4494,1224.3908,320.5492,1231.5455,327.4998,1229.6247,328.4,1222.47" style="stroke:#628A5B;stroke-width:1;"/></g><!--reverse link LogEntry to PipelinePhase--><g class="link" data-entity-1="ent0009" data-entity-2="ent0007" data-link-type="composition" data-source-line="267" id="lnk48"><path codeLine="267" d="M389.6112,1234.4332 C387.6312,1259.6632 386.34,1276.05 384.22,1302.95" fill="none" id="LogEntry-backto-PipelinePhase" style="stroke:#628A5B;stroke-width:1;"/><polygon fill="#628A5B" points="390.55,1222.47,386.0928,1228.1386,389.6112,1234.4332,394.0683,1228.7645,390.55,1222.47" style="stroke:#628A5B;stroke-width:1;"/></g><!--reverse link LogDTO to LogLevel--><g class="link" data-entity-1="ent0008" data-entity-2="ent0006" data-link-type="composition" data-source-line="268" id="lnk49"><path codeLine="268" d="M116.6774,1205.6394 C126.6474,1247.2294 138.07,1294.94 149.11,1341" fill="none" id="LogDTO-backto-LogLevel" style="stroke:#628A5B;stroke-width:1;"/><polygon fill="#628A5B" points="113.88,1193.97,111.3889,1200.7371,116.6774,1205.6394,119.1685,1198.8722,113.88,1193.97" style="stroke:#628A5B;stroke-width:1;"/></g><!--reverse link LogDTO to PipelinePhase--><g class="link" data-entity-1="ent0008" data-entity-2="ent0007" data-link-type="composition" data-source-line="269" id="lnk50"><path codeLine="269" d="M162.2224,1202.3468 C196.8924,1236.1468 235.03,1273.32 276.69,1313.92" fill="none" id="LogDTO-backto-PipelinePhase" style="stroke:#628A5B;stroke-width:1;"/><polygon fill="#628A5B" points="153.63,1193.97,155.1339,1201.0225,162.2224,1202.3468,160.7185,1195.2942,153.63,1193.97" style="stroke:#628A5B;stroke-width:1;"/></g><!--reverse link ILogger to LogProducer--><g class="link" data-entity-1="ent0010" data-entity-2="ent0011" data-link-type="extension" data-source-line="270" id="lnk51"><path codeLine="270" d="M761.0863,514.1308 C641.9163,530.4408 464.47,566.06 317.77,650.6 C263.08,682.11 213.96,736.59 184.05,774.18" fill="none" id="ILogger-backto-LogProducer" style="stroke:#628A5B;stroke-width:1;stroke-dasharray:7,7;"/><polygon fill="none" points="778.92,511.69,760.2727,508.1862,761.8998,520.0753,778.92,511.69" style="stroke:#628A5B;stroke-width:1;"/><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="78.2177" x="419.33" y="615.5659">implements</text></g><!--link LogProducer to LogEntry--><g class="link" data-entity-1="ent0011" data-entity-2="ent0009" data-link-type="dependency" data-source-line="271" id="lnk52"><path codeLine="271" d="M184.54,858.21 C222.86,909.34 285.5315,992.9888 333.8515,1057.4588" fill="none" id="LogProducer-to-LogEntry" style="stroke:#628A5B;stroke-width:1;stroke-dasharray:7,7;"/><polygon fill="#628A5B" points="337.45,1062.26,335.2531,1052.6593,334.4513,1058.259,328.8515,1057.4572,337.45,1062.26" style="stroke:#628A5B;stroke-width:1;"/><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="36.3579" x="312.23" y="1027.6559">emits</text></g><!--link LogDispatcher to LogEntry--><g class="link" data-entity-1="ent0012" data-entity-2="ent0009" data-link-type="dependency" data-source-line="272" id="lnk53"><path codeLine="272" d="M486.32,867.46 C469.31,919.49 444.3747,995.7871 424.5147,1056.5271" fill="none" id="LogDispatcher-to-LogEntry" style="stroke:#628A5B;stroke-width:1;stroke-dasharray:7,7;"/><polygon fill="#628A5B" points="422.65,1062.23,429.2489,1054.9187,424.2039,1057.4776,421.6451,1052.4325,422.65,1062.23" style="stroke:#628A5B;stroke-width:1;"/><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="66.8917" x="438.16" y="1027.6559">consumes</text></g><!--reverse link IEnrichmentService to WikipediaEnrichmentService--><g class="link" data-entity-1="ent0013" data-entity-2="ent0015" data-link-type="extension" data-source-line="274" id="lnk54"><path codeLine="274" d="M1327.77,559.22 C1327.77,613.61 1327.77,692.26 1327.77,755" fill="none" id="IEnrichmentService-backto-WikipediaEnrichmentService" style="stroke:#628A5B;stroke-width:1;stroke-dasharray:7,7;"/><polygon fill="none" points="1327.77,541.22,1321.77,559.22,1333.77,559.22,1327.77,541.22" style="stroke:#628A5B;stroke-width:1;"/><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="78.2177" x="1328.77" y="615.5659">implements</text></g><!--reverse link IEnrichmentService to MockEnrichmentService--><g class="link" data-entity-1="ent0013" data-entity-2="ent0014" data-link-type="extension" data-source-line="275" id="lnk55"><path codeLine="275" d="M1246.2775,551.1119 C1199.9375,580.4219 1152.23,611.75 1098.77,650.6 C1038.22,694.6 971.12,749.75 930.71,783.71" fill="none" id="IEnrichmentService-backto-MockEnrichmentService" style="stroke:#628A5B;stroke-width:1;stroke-dasharray:7,7;"/><polygon fill="none" points="1261.49,541.49,1243.0702,546.041,1249.4848,556.1827,1261.49,541.49" style="stroke:#628A5B;stroke-width:1;"/><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="78.2177" x="1167.68" y="615.5659">implements</text></g><!--reverse link WikipediaEnrichmentService to WebClient--><g class="link" data-entity-1="ent0015" data-entity-2="ent0016" data-link-type="composition" data-source-line="276" id="lnk56"><path codeLine="276" d="M1363.2396,887.7216 C1394.1496,949.2116 1436.34,1033.15 1465.4,1090.97" fill="none" id="WikipediaEnrichmentService-backto-WebClient" style="stroke:#628A5B;stroke-width:1;"/><polygon fill="#628A5B" points="1357.85,877,1356.9709,884.1573,1363.2396,887.7216,1364.1186,880.5642,1357.85,877" style="stroke:#628A5B;stroke-width:1;"/><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="34.8319" x="1434.39" y="1027.6559">owns</text></g><!--reverse link WebClient to HttpWebClient--><g class="link" data-entity-1="ent0016" data-entity-2="ent0017" data-link-type="extension" data-source-line="278" id="lnk57"><path codeLine="278" d="M1490.77,1211.97 C1490.77,1262.94 1490.77,1322.62 1490.77,1369.77" fill="none" id="WebClient-backto-HttpWebClient" style="stroke:#628A5B;stroke-width:1;stroke-dasharray:7,7;"/><polygon fill="none" points="1490.77,1193.97,1484.77,1211.97,1496.77,1211.97,1490.77,1193.97" style="stroke:#628A5B;stroke-width:1;"/><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="78.2177" x="1491.77" y="1268.1259">implements</text></g><!--reverse link DataGenerator to MockGenerator--><g class="link" data-entity-1="ent0018" data-entity-2="ent0019" data-link-type="extension" data-source-line="280" id="lnk58"><path codeLine="280" d="M2986.4367,559.3658 C3091.7367,614.9758 3244.9,695.86 3356.91,755.01" fill="none" id="DataGenerator-backto-MockGenerator" style="stroke:#628A5B;stroke-width:1;stroke-dasharray:7,7;"/><polygon fill="none" points="2970.52,550.96,2983.6348,564.6713,2989.2387,554.0602,2970.52,550.96" style="stroke:#628A5B;stroke-width:1;"/><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="78.2177" x="3102.86" y="615.5659">implements</text></g><!--reverse link DataGenerator to LlamaGenerator--><g class="link" data-entity-1="ent0018" data-entity-2="ent0020" data-link-type="extension" data-source-line="281" id="lnk59"><path codeLine="281" d="M2874.77,568.96 C2874.77,608.58 2874.77,647.31 2874.77,698.04" fill="none" id="DataGenerator-backto-LlamaGenerator" style="stroke:#628A5B;stroke-width:1;stroke-dasharray:7,7;"/><polygon fill="none" points="2874.77,550.96,2868.77,568.96,2880.77,568.96,2874.77,550.96" style="stroke:#628A5B;stroke-width:1;"/><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="78.2177" x="2875.77" y="615.5659">implements</text></g><!--reverse link DataGenerator to OpenAIGenerator--><g class="link" data-entity-1="ent0018" data-entity-2="ent0021" data-link-type="extension" data-source-line="282" id="lnk60"><path codeLine="282" d="M2638.9717,555.3825 C2534.4817,581.6125 2425.72,611.87 2314.77,650.6 C2259.57,669.86 2201.11,693.54 2147.05,716.87" fill="none" id="DataGenerator-backto-OpenAIGenerator" style="stroke:#628A5B;stroke-width:1;stroke-dasharray:7,7;"/><polygon fill="none" points="2656.43,551,2637.5108,549.5631,2640.4325,561.202,2656.43,551" style="stroke:#628A5B;stroke-width:1;"/><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="78.2177" x="2467.43" y="615.5659">implements</text></g><!--reverse link LlamaGenerator to IPromptFormatter--><g class="link" data-entity-1="ent0020" data-entity-2="ent0025" data-link-type="composition" data-source-line="284" id="lnk61"><path codeLine="284" d="M2874.77,946.21 C2874.77,1004.27 2874.77,1058.7 2874.77,1100.35" fill="none" id="LlamaGenerator-backto-IPromptFormatter" style="stroke:#628A5B;stroke-width:1;"/><polygon fill="#628A5B" points="2874.77,934.21,2870.77,940.21,2874.77,946.21,2878.77,940.21,2874.77,934.21" style="stroke:#628A5B;stroke-width:1;"/><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="29.9599" x="2875.77" y="1027.6559">uses</text></g><!--reverse link LlamaGenerator to IPromptDirectory--><g class="link" data-entity-1="ent0020" data-entity-2="ent0027" data-link-type="composition" data-source-line="285" id="lnk62"><path codeLine="285" d="M2723.2258,941.4217 C2699.2458,958.5017 2683.77,967.97 2658.77,981.69 C2565.15,1033.05 2451.66,1074.16 2364.22,1101.81" fill="none" id="LlamaGenerator-backto-IPromptDirectory" style="stroke:#628A5B;stroke-width:1;"/><polygon fill="#628A5B" points="2733,934.46,2725.7923,934.6828,2723.2258,941.4217,2730.4335,941.1989,2733,934.46" style="stroke:#628A5B;stroke-width:1;"/><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="29.9599" x="2592.28" y="1027.6559">uses</text></g><!--reverse link OpenAIGenerator to IPromptDirectory--><g class="link" data-entity-1="ent0021" data-entity-2="ent0027" data-link-type="composition" data-source-line="287" id="lnk63"><path codeLine="287" d="M2031.2442,924.2784 C2086.5642,986.3484 2147.18,1054.37 2188.39,1100.6" fill="none" id="OpenAIGenerator-backto-IPromptDirectory" style="stroke:#628A5B;stroke-width:1;"/><polygon fill="#628A5B" points="2023.26,915.32,2024.266,922.4606,2031.2442,924.2784,2030.2382,917.1378,2023.26,915.32" style="stroke:#628A5B;stroke-width:1;"/><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="29.9599" x="2124.04" y="1027.6559">uses</text></g><!--reverse link OpenAIGenerator to WebClient--><g class="link" data-entity-1="ent0021" data-entity-2="ent0016" data-link-type="composition" data-source-line="288" id="lnk64"><path codeLine="288" d="M1791.3417,922.387 C1711.9117,980.267 1624.41,1044.03 1560.21,1090.82" fill="none" id="OpenAIGenerator-backto-WebClient" style="stroke:#628A5B;stroke-width:1;"/><polygon fill="#628A5B" points="1801.04,915.32,1793.8352,915.6207,1791.3417,922.387,1798.5465,922.0863,1801.04,915.32" style="stroke:#628A5B;stroke-width:1;"/><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="29.9599" x="1661.32" y="1027.6559">uses</text></g><!--reverse link IPromptFormatter to Gemma4JinjaPromptFormatter--><g class="link" data-entity-1="ent0025" data-entity-2="ent0026" data-link-type="extension" data-source-line="290" id="lnk65"><path codeLine="290" d="M2874.77,1202.28 C2874.77,1255.91 2874.77,1330.89 2874.77,1379.25" fill="none" id="IPromptFormatter-backto-Gemma4JinjaPromptFormatter" style="stroke:#628A5B;stroke-width:1;stroke-dasharray:7,7;"/><polygon fill="none" points="2874.77,1184.28,2868.77,1202.28,2880.77,1202.28,2874.77,1184.28" style="stroke:#628A5B;stroke-width:1;"/><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="78.2177" x="2875.77" y="1268.1259">implements</text></g><!--reverse link IPromptDirectory to PromptDirectory--><g class="link" data-entity-1="ent0027" data-entity-2="ent0028" data-link-type="extension" data-source-line="291" id="lnk66"><path codeLine="291" d="M2224.77,1202.28 C2224.77,1249.48 2224.77,1309.16 2224.77,1360.07" fill="none" id="IPromptDirectory-backto-PromptDirectory" style="stroke:#628A5B;stroke-width:1;stroke-dasharray:7,7;"/><polygon fill="none" points="2224.77,1184.28,2218.77,1202.28,2230.77,1202.28,2224.77,1184.28" style="stroke:#628A5B;stroke-width:1;"/><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="78.2177" x="2225.77" y="1268.1259">implements</text></g><!--reverse link ICuratedDataService to JsonLoader--><g class="link" data-entity-1="ent0029" data-entity-2="ent0030" data-link-type="extension" data-source-line="293" id="lnk67"><path codeLine="293" d="M4701.0688,573.658 C4810.4488,595.908 4910.58,619.41 5017.77,650.6 C5107.66,676.75 5205.31,713.42 5284.79,745.5" fill="none" id="ICuratedDataService-backto-JsonLoader" style="stroke:#628A5B;stroke-width:1;stroke-dasharray:7,7;"/><polygon fill="none" points="4683.43,570.07,4699.8727,579.5376,4702.2648,567.7785,4683.43,570.07" style="stroke:#628A5B;stroke-width:1;"/><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="78.2177" x="4899.83" y="615.5659">implements</text></g><!--reverse link ICuratedDataService to MockCuratedDataService--><g class="link" data-entity-1="ent0029" data-entity-2="ent0034" data-link-type="extension" data-source-line="294" id="lnk68"><path codeLine="294" d="M4279.77,587.86 C4279.77,631.03 4279.77,669.21 4279.77,716.9" fill="none" id="ICuratedDataService-backto-MockCuratedDataService" style="stroke:#628A5B;stroke-width:1;stroke-dasharray:7,7;"/><polygon fill="none" points="4279.77,569.86,4273.77,587.86,4285.77,587.86,4279.77,569.86" style="stroke:#628A5B;stroke-width:1;"/><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="78.2177" x="4280.77" y="615.5659">implements</text></g><!--reverse link IExportService to SqliteExportService--><g class="link" data-entity-1="ent0038" data-entity-2="ent0039" data-link-type="extension" data-source-line="296" id="lnk69"><path codeLine="296" d="M5472.9092,507.3124 C5672.3292,518.9924 5962.6,552.05 6210.77,650.6 C6216.4,652.84 6222.03,655.23 6227.65,657.75" fill="none" id="IExportService-backto-SqliteExportService" style="stroke:#628A5B;stroke-width:1;stroke-dasharray:7,7;"/><polygon fill="none" points="5454.94,506.26,5472.5584,513.3022,5473.26,501.3227,5454.94,506.26" style="stroke:#628A5B;stroke-width:1;"/><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="78.2177" x="6112.68" y="615.5659">implements</text></g><!--reverse link SqliteExportService to IDateTimeProvider--><g class="link" data-entity-1="ent0039" data-entity-2="ent0040" data-link-type="composition" data-source-line="297" id="lnk70"><path codeLine="297" d="M6450.77,994.13 C6450.77,1038.26 6450.77,1070.1 6450.77,1100.58" fill="none" id="SqliteExportService-backto-IDateTimeProvider" style="stroke:#628A5B;stroke-width:1;"/><polygon fill="#628A5B" points="6450.77,982.13,6446.77,988.13,6450.77,994.13,6454.77,988.13,6450.77,982.13" style="stroke:#628A5B;stroke-width:1;"/><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="34.8319" x="6451.77" y="1027.6559">owns</text></g><!--reverse link IDateTimeProvider to SystemDateTimeProvider--><g class="link" data-entity-1="ent0040" data-entity-2="ent0041" data-link-type="extension" data-source-line="298" id="lnk71"><path codeLine="298" d="M6450.77,1202.28 C6450.77,1255.91 6450.77,1330.89 6450.77,1379.25" fill="none" id="IDateTimeProvider-backto-SystemDateTimeProvider" style="stroke:#628A5B;stroke-width:1;stroke-dasharray:7,7;"/><polygon fill="none" points="6450.77,1184.28,6444.77,1202.28,6456.77,1202.28,6450.77,1184.28" style="stroke:#628A5B;stroke-width:1;"/><text fill="#28342A" font-family="'DM Sans'" font-size="14" lengthAdjust="spacing" textLength="78.2177" x="6451.77" y="1268.1259">implements</text></g><?plantuml-src xHbTSzisybs_0fERQUNQKfl4IHFLunbBiX9dajXdEPT73aIkHCGam82W7JNN_twx04cH52NBRdipdkcBAGFxXVt2xYAz5ZBXciSiDSi8yAyMlidYg9EMEm7CUHQPYPBc8u-1FHb_O5CkqoVjC5FnEx2dXvLD8qm4A_J_gEWwL3TFsY0iyhDVAbipxbyljCfaC5AHqknVavF9QFAwdNSEykpLyyDd9stqDm6SQArkyysNprwTl1XsAhj-nDEKVUymDjmW3MDlWGUWr_PFJoQ_dBuYJALnFrzzSVZhuSkdkDh2bx5rGV-eIYELWNPXxc3MIjUgXbs5m8O2z89h0vADkU7iGYGG2GcinqPM0MF15oX0f-FqiO8l8C-r7mAw43V8WoJiiKWj5g0zDa3N2WQ3DEGQ0Yyn-kZilTqwjc0-YWRVZ9U2lX4-bF2P55ypSF2d4dqpZ06zn44vr0L8i1pRaEWWRme01m_V4eL2wtOUThD6tyyG6MKEaDHM-KOEa5ZM2F0aYOJFZL3IKmbzKi8_MIsVkzNgSP3XJCCjQ05foUq6V3p6qT1kB2_1HpCVDx0o574DunCkhi3tsMKckti8CrCgilZ_pf3WU-N4IUrU5NyapD8nofK9mqAuhYyCVae39rmUeJLJmvoz8B38HF56YQ16XCHw405qdKj8CUX9L0USxw97s_ZGOX4QfkPtU2bIMpiHupAeYuXwCY7pUHGn4mBYp3K0csVI9tKXhBb471MF56eu_InCA2IkBA2xXy3TMIc2b-GoUAeYnC--nUk7UymezaKOz4x4uyok2ubYi8GBpT3Rvah7qPBTMXxinNLCPqIX66fnYKW-XeT6sJ00WVlXoZ6OjlR_4OyN11hIjDy1F2SfgmXerEfxk8687Hs1p6B2m-CT7zjG7iCiM-3tJCuLVZvpBV5pYlbBaz8TXKB55o5FePNCrA0YieHGot36VqZDkSeHbZAOy_4J6GoXiJKAmSTaM5kxn1zoKLkQg2XIjxMbA-0wKBVIvbEZCz_GcSTNvrQqVHRPqmzAHTZ5n9vbK3-RtObHYtm1fUVYtIaNTTgdQ8rbJjq8H30yJWe4FzHAAaHSfWPYpu-KVpqO49YNA67riiyKUZ9Qkk2XCetf9yeZrT4DDTuzdWWA8BSm6DXW-UjEXHA1ddCVM9wwqS3bMcxUVUBIJVWoKZnmF56zjTZzpcVazxxvWuqLGOFLqQ1Krrubbal7lD0go7mehXK_v592H7bkI1SW9WctSbHGEVx10jw3nLXWxZ4OblTaiagdvHclr3G9ebpVNMi0crBjuc1W5_AFHp7IKE_QJTUgQKmxHSOUkUkpYtw1R5n2BFP-s5itfJlp1_ItTLP_4V7FuXfTAn2ydGDgDnAurdQJVeRPo6uM5xPr5y_7V5QzUMsDWpTUp9EZYWW7LNcE_zHXYFi4q2LEdGZThtHDre6y6m6tUzkYfJpE9YjsCnsLmbJ8jCYpppxfw5JwAe3k3Oyos0clDCfROvALEFzN0LRwgDLa6tHIlPANL24iIpxLgi0oAiiXAWXg-7IhR44-O0bUxahoSfiGBjmQxafgLuejwbqiErTBMLwrI5eTwN4TmcP9G1F4GehK2FyjJyDkSSDi219iqxpxepvCrfpPAbL7FEPrRVTOZ0vfC_K7-l4Mox48Qfq5RkMnNzbCj8eJur5HnSscdk320aqAcECgPY2qhP2NMp371Op3r9Wp2k3OF7tz-lclhVxIx_TtS8mSgc7FztZxTfrAy4eBDyPeUM5XLRSYrao1ThCuVz06LThcF05vSjOq3a-4Tmqh7LLebtPhRkpOE3xS3hSmytQyWnvNC8-mJy521LlzE8d0DdGdYTXWjdgpr32VBRQvOHLAjlF0AfuAt0mjmiupasI6UjSkTXK9Wa0UJmFs1SNsKZnTp0_oqTA08IfGMxJ8kEOOaDXmEP0UtYTOnBwRddyym6ubYRZld26YWE_xIOBDovlXnmcZAKJCjTE3vdY1i1x2ISMQmSea0DPjrLwdKcSrWTlk8hVRpMjqvsuj5S01eyPxy_x6Y7a3U9J3Tq9-uKrn_cu1rZHHHiY6oXwJ2eRnVKxNfFkz4RHbwCr51Ew8Wm6bB9Ux7rpntK_MYYBMXpWRTR4Qbv3mwtcsUIGRgvNzVBPIaCbx2QAS1_oV95n97HNQw4v0QIGTBaVOr60Jyq0kMymnptbu4LO55TxJJF_Th5F7ek1SEk8xL0E9i6hk2j-oF_unwkCoQltQMXcNvaeqzgB_Vc8nc505-8aLrf0f4oR5YuKkIYPeTfBx09QgSw5H3fhm_SOYJicNVgUqvIP-ATsy0eNb4bE21hpyf9kx4Os0sLu8uUXwL9b1Y5x2TOhYhy_Pg5jeoJF5m3ntmEOCjtGfbq9pRsgRz1R-OG4BiwNUREdvpaAxfDIwkIsft0ptfLIptZ-1zfW3hTrpQTWglc4G7FRAtkzdzhnNrBBqSf825QVTJzC3DZuzO9FB0pOytQEQ9S2mOhrUZAHfIft9z3UQi1T7utgHqV0enLyOWGiKVcCzLtkzQRstpwGm0ilCtw4wiDkdEiM7D7Ll95ttyhAgqMiFBbQT6P9-UUYP5cGgmshb-oQqYP0DIShRQler4WRgnt7f0NM9pR0T4Mjr8u8DFIjQ1wuGwY87oblFJ7hb5DhBZD_MU96rPcWlspbkhy-2cHVQPfh0dCpZ7BlIP6FM16tAcRMNcjYi4AO6HIK3hsFaPjWTmLR0TqDZoo1JxkSFSY2zwajOAURknQTbzDopjZr-16xcUlf2mgbjlkgxs4sgw0QAv7eMt3KFgggWKL4tNN7J5FIJyQyAt-nkR2YcrWVNQEvCvSUoT-KH7ZDoTr0eaXHJrXZG6mAG_XBrymMiXtJkUER-gTShtqC6JDtAT2UCzQduxiZr0UfzcDOIoZqGMwgL7BjJFd8HOFd4L5kjlp7bBsvDy6Aj3bo-DFsttwyz0msOe9c4lHawrOr-_tZry3PW40i7K7dTQO2GF-CLWsTfUtWXnblUCcfoRC9lVsYfeMvXGLfP3UqBlLTU5OX5VR9V8zrf3DmBYQekL05lWMvCNEy2Rmu1wz8qY5d_Qau-1bGOf5j0LnE57BJJPBeDk0ssgZn7SNqyH8VRCg-f-yBQI8MmrmMgggPr-a1ebUwrpcGJmeOMey6kKIyHOblbKSDg0x2wNil9kQUsN02MKNiYh_7w7m00?></g></svg> |