Formatted includes/data_generation

This commit is contained in:
Aaron Po
2026-07-19 04:10:32 -04:00
parent 522381357a
commit efd0d4f2aa
3 changed files with 228 additions and 225 deletions

View File

@@ -14,28 +14,28 @@
* @brief Interface for data generator implementations. * @brief Interface for data generator implementations.
*/ */
class DataGenerator { class DataGenerator {
public: public:
virtual ~DataGenerator() = default; virtual ~DataGenerator() = default;
/** /**
* @brief Generates brewery data for a location. * @brief Generates brewery data for a location.
* *
* @param city Enriched city the brewery is associated with. * @param city Enriched city the brewery is associated with.
* @return Brewery generation result. * @return Brewery generation result.
*/ */
virtual BreweryResult GenerateBrewery(const EnrichedCity& city) = 0; virtual BreweryResult GenerateBrewery(const EnrichedCity& city) = 0;
/** /**
* @brief Generates a user profile grounded in a sampled name and persona. * @brief Generates a user profile grounded in a sampled name and persona.
* *
* @param city Enriched city the user is associated with. * @param city Enriched city the user is associated with.
* @param persona Persona archetype grounding the generated bio. * @param persona Persona archetype grounding the generated bio.
* @param name Sampled first/last name (and gender) * @param name Sampled first/last name (and gender)
* @return User generation result. * @return User generation result.
*/ */
virtual UserResult GenerateUser(const EnrichedCity& city, virtual UserResult GenerateUser(const EnrichedCity& city,
const UserPersona& persona, const UserPersona& persona,
const Name& name) = 0; const Name& name) = 0;
}; };
#endif // BIERGARTEN_PIPELINE_INCLUDES_DATA_GENERATION_DATA_GENERATOR_H_ #endif // BIERGARTEN_PIPELINE_INCLUDES_DATA_GENERATION_DATA_GENERATOR_H_

View File

@@ -26,110 +26,112 @@ struct llama_context;
* @brief Data generator implementation backed by llama.cpp. * @brief Data generator implementation backed by llama.cpp.
*/ */
class LlamaGenerator final : public DataGenerator { class LlamaGenerator final : public DataGenerator {
public: public:
/** /**
* @brief Constructs a generator using parsed application options and loads * @brief Constructs a generator using parsed application options and loads
* the configured model immediately. * the configured model immediately.
* *
* @param options Parsed application options. * @param options Parsed application options.
* @param model_path Filesystem path to GGUF model assets. * @param model_path Filesystem path to GGUF model assets.
* @param prompt_formatter Formatter that produces model-specific prompts. * @param prompt_formatter Formatter that produces model-specific prompts.
* @param prompt_directory Directory service for loading named prompt files. * @param prompt_directory Directory service for loading named prompt files.
*/ */
LlamaGenerator(const ApplicationOptions& options, LlamaGenerator(const ApplicationOptions& options,
const std::string& model_path, std::shared_ptr<ILogger> logger, const std::string& model_path,
std::unique_ptr<IPromptFormatter> prompt_formatter, std::shared_ptr<ILogger> logger,
std::unique_ptr<IPromptDirectory> prompt_directory); std::unique_ptr<IPromptFormatter> prompt_formatter,
std::unique_ptr<IPromptDirectory> prompt_directory);
~LlamaGenerator() override; ~LlamaGenerator() override;
LlamaGenerator(const LlamaGenerator&) = delete; LlamaGenerator(const LlamaGenerator&) = delete;
LlamaGenerator& operator=(const LlamaGenerator&) = delete; LlamaGenerator& operator=(const LlamaGenerator&) = delete;
LlamaGenerator(LlamaGenerator&&) = delete; LlamaGenerator(LlamaGenerator&&) = delete;
LlamaGenerator& operator=(LlamaGenerator&&) = delete; LlamaGenerator& operator=(LlamaGenerator&&) = delete;
/** /**
* @brief Generates brewery data for a specific location. * @brief Generates brewery data for a specific location.
* *
* @param location City object. * @param location City object.
* @param region_context Additional regional context. * @param region_context Additional regional context.
* @return Generated brewery result. * @return Generated brewery result.
*/ */
BreweryResult GenerateBrewery(const EnrichedCity&) override; BreweryResult GenerateBrewery(const EnrichedCity&) override;
/** /**
* @brief Generates a user profile grounded in a sampled name and persona. * @brief Generates a user profile grounded in a sampled name and persona.
* *
* @param city Enriched city the user is associated with. * @param city Enriched city the user is associated with.
* @param persona Persona archetype grounding the generated bio. * @param persona Persona archetype grounding the generated bio.
* @param name Sampled first/last name -- not LLM-invented. * @param name Sampled first/last name -- not LLM-invented.
* @return Generated user profile. * @return Generated user profile.
*/ */
UserResult GenerateUser(const EnrichedCity& city, const UserPersona& persona, UserResult GenerateUser(const EnrichedCity& city, const UserPersona& persona,
const Name& name) override; const Name& name) override;
private: private:
static constexpr int32_t kDefaultMaxTokens = 10000; static constexpr int32_t kDefaultMaxTokens = 10000;
static constexpr float kDefaultSamplingTopP = 0.95F; static constexpr float kDefaultSamplingTopP = 0.95F;
static constexpr uint32_t kDefaultSamplingTopK = 64; static constexpr uint32_t kDefaultSamplingTopK = 64;
static constexpr uint32_t kDefaultContextSize = 8192; static constexpr uint32_t kDefaultContextSize = 8192;
struct ModelDeleter { struct ModelDeleter {
void operator()(llama_model* model) const noexcept; void operator()(llama_model* model) const noexcept;
}; };
struct ContextDeleter { struct ContextDeleter {
void operator()(llama_context* context) const noexcept; void operator()(llama_context* context) const noexcept;
}; };
using ModelHandle = std::unique_ptr<llama_model, ModelDeleter>; using ModelHandle = std::unique_ptr<llama_model, ModelDeleter>;
using ContextHandle = std::unique_ptr<llama_context, ContextDeleter>; using ContextHandle = std::unique_ptr<llama_context, ContextDeleter>;
/** /**
* @brief Loads model and prepares inference context. * @brief Loads model and prepares inference context.
* *
* @param model_path Filesystem path to GGUF model. * @param model_path Filesystem path to GGUF model.
*/ */
void Load(const std::string& model_path); void Load(const std::string& model_path);
/** /**
* @brief Infers text from separate system and user prompts. * @brief Infers text from separate system and user prompts.
* *
* This helps chat-capable models preserve system-role behavior instead of * This helps chat-capable models preserve system-role behavior instead of
* concatenating system text into user input. * concatenating system text into user input.
* *
* @param system_prompt System role prompt. * @param system_prompt System role prompt.
* @param prompt User prompt. * @param prompt User prompt.
* @param max_tokens Maximum tokens to generate. * @param max_tokens Maximum tokens to generate.
* @param grammar Optional GBNF grammar constraining generated output. * @param grammar Optional GBNF grammar constraining generated output.
* @return Generated text. * @return Generated text.
*/ */
std::string Infer(const std::string& system_prompt, const std::string& prompt, std::string Infer(const std::string& system_prompt,
int max_tokens = kDefaultMaxTokens, const std::string& prompt,
std::string_view grammar = {}); int max_tokens = kDefaultMaxTokens,
std::string_view grammar = {});
/** /**
* @brief Runs inference on an already-formatted prompt. * @brief Runs inference on an already-formatted prompt.
* *
* @param formatted_prompt Prompt preformatted for model chat template. * @param formatted_prompt Prompt preformatted for model chat template.
* @param max_tokens Maximum tokens to generate. * @param max_tokens Maximum tokens to generate.
* @param grammar Optional GBNF grammar constraining generated output. * @param grammar Optional GBNF grammar constraining generated output.
* @return Generated text. * @return Generated text.
*/ */
std::string InferFormatted(const std::string& formatted_prompt, std::string InferFormatted(const std::string& formatted_prompt,
int max_tokens = kDefaultMaxTokens, int max_tokens = kDefaultMaxTokens,
std::string_view grammar = {}); std::string_view grammar = {});
ModelHandle model_; ModelHandle model_;
ContextHandle context_; ContextHandle context_;
float sampling_temperature_ = 1.0F; float sampling_temperature_ = 1.0F;
float sampling_top_p_ = kDefaultSamplingTopP; float sampling_top_p_ = kDefaultSamplingTopP;
uint32_t sampling_top_k_ = kDefaultSamplingTopK; uint32_t sampling_top_k_ = kDefaultSamplingTopK;
std::mt19937 rng_; std::mt19937 rng_;
uint32_t n_ctx_ = kDefaultContextSize; uint32_t n_ctx_ = kDefaultContextSize;
int n_gpu_layers_ = 0; int n_gpu_layers_ = 0;
std::shared_ptr<ILogger> logger_; std::shared_ptr<ILogger> logger_;
std::unique_ptr<IPromptFormatter> prompt_formatter_; std::unique_ptr<IPromptFormatter> prompt_formatter_;
std::unique_ptr<IPromptDirectory> prompt_directory_; std::unique_ptr<IPromptDirectory> prompt_directory_;
}; };
#endif // BIERGARTEN_PIPELINE_INCLUDES_DATA_GENERATION_LLAMA_GENERATOR_H_ #endif // BIERGARTEN_PIPELINE_INCLUDES_DATA_GENERATION_LLAMA_GENERATOR_H_

View File

@@ -16,128 +16,129 @@
* @brief Mock generator used for deterministic, model-free outputs. * @brief Mock generator used for deterministic, model-free outputs.
*/ */
class MockGenerator final : public DataGenerator { class MockGenerator final : public DataGenerator {
public: public:
/** /**
* @brief Generates deterministic brewery data for a location. * @brief Generates deterministic brewery data for a location.
* *
* @param enriched_city * @param enriched_city
* @return Generated brewery result. * @return Generated brewery result.
*/ */
BreweryResult GenerateBrewery(const EnrichedCity& enriched_city) override; BreweryResult GenerateBrewery(const EnrichedCity& enriched_city) override;
/** /**
* @brief Generates deterministic user data grounded in a sampled name and * @brief Generates deterministic user data grounded in a sampled name and
* persona. * persona.
* @return Generated user result. * @return Generated user result.
*/ */
UserResult GenerateUser(const EnrichedCity& city, const UserPersona& persona, UserResult GenerateUser(const EnrichedCity& city, const UserPersona& persona,
const Name& name) override; const Name& name) override;
private: private:
/** /**
* @brief Combines two strings into a stable hash value. * @brief Combines two strings into a stable hash value.
* *
* @param location City and country names. * @param location City and country names.
* @return Deterministic hash value. * @return Deterministic hash value.
*/ */
static size_t DeterministicHash(const City& location); static size_t DeterministicHash(const City& location);
/** /**
* @brief Combines city, persona, and name into a stable hash value. * @brief Combines city, persona, and name into a stable hash value.
* *
* @param location City and country names. * @param location City and country names.
* @param persona Persona archetype. * @param persona Persona archetype.
* @param name Sampled first/last name. * @param name Sampled first/last name.
* @return Deterministic hash value. * @return Deterministic hash value.
*/ */
static size_t DeterministicHash(const City& location, static size_t DeterministicHash(const City& location,
const UserPersona& persona, const Name& name); const UserPersona& persona,
const Name& name);
// Hash stride constants for deterministic distribution across fixed-size // Hash stride constants for deterministic distribution across fixed-size
// arrays. These coprime strides spread hash values uniformly without // arrays. These coprime strides spread hash values uniformly without
// clustering, ensuring diverse output across different hash inputs. // clustering, ensuring diverse output across different hash inputs.
static constexpr size_t kNounHashStride = 7; static constexpr size_t kNounHashStride = 7;
static constexpr size_t kDescriptionHashStride = 13; static constexpr size_t kDescriptionHashStride = 13;
static constexpr size_t kBioHashStride = 11; static constexpr size_t kBioHashStride = 11;
static constexpr size_t kActivityWeightHashStride = 17; static constexpr size_t kActivityWeightHashStride = 17;
static constexpr size_t kActivityWeightHashRange = 1000; static constexpr size_t kActivityWeightHashRange = 1000;
static constexpr std::array<std::string_view, 18> kBreweryAdjectives = { static constexpr std::array<std::string_view, 18> kBreweryAdjectives = {
"Craft", "Heritage", "Local", "Artisan", "Pioneer", "Golden", "Craft", "Heritage", "Local", "Artisan", "Pioneer", "Golden",
"Modern", "Classic", "Summit", "Northern", "Riverstone", "Barrel", "Modern", "Classic", "Summit", "Northern", "Riverstone", "Barrel",
"Hinterland", "Harbor", "Wild", "Granite", "Copper", "Maple"}; "Hinterland", "Harbor", "Wild", "Granite", "Copper", "Maple"};
static constexpr std::array<std::string_view, 18> kBreweryNouns = { static constexpr std::array<std::string_view, 18> kBreweryNouns = {
"Brewing Co.", "Brewery", "Bier Haus", "Taproom", "Works", "Brewing Co.", "Brewery", "Bier Haus", "Taproom", "Works",
"House", "Fermentery", "Ale Co.", "Cellars", "Collective", "House", "Fermentery", "Ale Co.", "Cellars", "Collective",
"Project", "Foundry", "Malthouse", "Public House", "Co-op", "Project", "Foundry", "Malthouse", "Public House", "Co-op",
"Lab", "Beer Hall", "Guild"}; "Lab", "Beer Hall", "Guild"};
static constexpr std::array<std::string_view, 18> kBreweryDescriptions = { static constexpr std::array<std::string_view, 18> kBreweryDescriptions = {
"Handcrafted pale ales and seasonal IPAs with local ingredients.", "Handcrafted pale ales and seasonal IPAs with local ingredients.",
"Traditional lagers and experimental sours in small batches.", "Traditional lagers and experimental sours in small batches.",
"Award-winning stouts and wildly hoppy blonde ales.", "Award-winning stouts and wildly hoppy blonde ales.",
"Craft brewery specializing in Belgian-style triples and dark " "Craft brewery specializing in Belgian-style triples and dark "
"porters.", "porters.",
"Modern brewery blending tradition with bold experimental flavors.", "Modern brewery blending tradition with bold experimental flavors.",
"Neighborhood-focused taproom pouring crisp pilsners and citrusy " "Neighborhood-focused taproom pouring crisp pilsners and citrusy "
"pale " "pale "
"ales.", "ales.",
"Small-batch brewery known for barrel-aged releases and smoky " "Small-batch brewery known for barrel-aged releases and smoky "
"lagers.", "lagers.",
"Independent brewhouse pairing farmhouse ales with rotating food " "Independent brewhouse pairing farmhouse ales with rotating food "
"pop-ups.", "pop-ups.",
"Community brewpub making balanced bitters, saisons, and hazy IPAs.", "Community brewpub making balanced bitters, saisons, and hazy IPAs.",
"Experimental nanobrewery exploring local yeast and regional " "Experimental nanobrewery exploring local yeast and regional "
"grains.", "grains.",
"Family-run brewery producing smooth amber ales and robust porters.", "Family-run brewery producing smooth amber ales and robust porters.",
"Urban brewery crafting clean lagers and bright, fruit-forward " "Urban brewery crafting clean lagers and bright, fruit-forward "
"sours.", "sours.",
"Riverfront brewhouse featuring oak-matured ales and seasonal " "Riverfront brewhouse featuring oak-matured ales and seasonal "
"blends.", "blends.",
"Modern taproom focused on sessionable lagers and classic pub " "Modern taproom focused on sessionable lagers and classic pub "
"styles.", "styles.",
"Brewery rooted in tradition with a lineup of malty reds and crisp " "Brewery rooted in tradition with a lineup of malty reds and crisp "
"lagers.", "lagers.",
"Creative brewery offering rotating collaborations and limited " "Creative brewery offering rotating collaborations and limited "
"draft-only " "draft-only "
"pours.", "pours.",
"Locally inspired brewery serving approachable ales with bold hop " "Locally inspired brewery serving approachable ales with bold hop "
"character.", "character.",
"Destination taproom known for balanced IPAs and cocoa-rich " "Destination taproom known for balanced IPAs and cocoa-rich "
"stouts."}; "stouts."};
static constexpr std::array<std::string_view, 18> kUsernames = { static constexpr std::array<std::string_view, 18> kUsernames = {
"hopseeker", "malttrail", "yeastwhisper", "lagerlane", "hopseeker", "malttrail", "yeastwhisper", "lagerlane",
"barrelbound", "foamfinder", "taphunter", "graingeist", "barrelbound", "foamfinder", "taphunter", "graingeist",
"brewscout", "aleatlas", "caskcompass", "hopsandmaps", "brewscout", "aleatlas", "caskcompass", "hopsandmaps",
"mashpilot", "pintnomad", "fermentfriend", "stoutsignal", "mashpilot", "pintnomad", "fermentfriend", "stoutsignal",
"sessionwander", "kettlekeeper"}; "sessionwander", "kettlekeeper"};
static constexpr std::array<std::string_view, 18> kBios = { static constexpr std::array<std::string_view, 18> kBios = {
"Always chasing balanced IPAs and crisp lagers across local taprooms.", "Always chasing balanced IPAs and crisp lagers across local taprooms.",
"Weekend brewery explorer with a soft spot for dark, roasty stouts.", "Weekend brewery explorer with a soft spot for dark, roasty stouts.",
"Documenting tiny brewpubs, fresh pours, and unforgettable beer " "Documenting tiny brewpubs, fresh pours, and unforgettable beer "
"gardens.", "gardens.",
"Fan of farmhouse ales, food pairings, and long tasting flights.", "Fan of farmhouse ales, food pairings, and long tasting flights.",
"Collecting favorite pilsners one city at a time.", "Collecting favorite pilsners one city at a time.",
"Hops-first drinker who still saves room for classic malt-forward " "Hops-first drinker who still saves room for classic malt-forward "
"styles.", "styles.",
"Finding hidden tap lists and sharing the best seasonal releases.", "Finding hidden tap lists and sharing the best seasonal releases.",
"Brewery road-tripper focused on local ingredients and clean " "Brewery road-tripper focused on local ingredients and clean "
"fermentation.", "fermentation.",
"Always comparing house lagers and ranking patio pint vibes.", "Always comparing house lagers and ranking patio pint vibes.",
"Curious about yeast strains, barrel programs, and cellar experiments.", "Curious about yeast strains, barrel programs, and cellar experiments.",
"Believes every neighborhood deserves a great community taproom.", "Believes every neighborhood deserves a great community taproom.",
"Looking for session beers that taste great from first sip to last.", "Looking for session beers that taste great from first sip to last.",
"Belgian ale enthusiast who never skips a new saison.", "Belgian ale enthusiast who never skips a new saison.",
"Hazy IPA critic with deep respect for a perfectly clear pilsner.", "Hazy IPA critic with deep respect for a perfectly clear pilsner.",
"Visits breweries for the stories, stays for the flagship pours.", "Visits breweries for the stories, stays for the flagship pours.",
"Craft beer fan mapping tasting notes and favorite brew routes.", "Craft beer fan mapping tasting notes and favorite brew routes.",
"Always ready to trade recommendations for underrated local breweries.", "Always ready to trade recommendations for underrated local breweries.",
"Keeping a running list of must-try collab releases and tap " "Keeping a running list of must-try collab releases and tap "
"takeovers."}; "takeovers."};
}; };
#endif // BIERGARTEN_PIPELINE_INCLUDES_DATA_GENERATION_MOCK_GENERATOR_H_ #endif // BIERGARTEN_PIPELINE_INCLUDES_DATA_GENERATION_MOCK_GENERATOR_H_