mirror of
https://github.com/aaronpo97/the-biergarten-app.git
synced 2026-10-15 01:54:43 +00:00
C++ code formatting
This commit is contained in:
@@ -51,10 +51,9 @@ class HttpWebClient final : public WebClient {
|
|||||||
* @param headers Additional request headers.
|
* @param headers Additional request headers.
|
||||||
* @return Response body on HTTP 2xx; throws std::runtime_error otherwise.
|
* @return Response body on HTTP 2xx; throws std::runtime_error otherwise.
|
||||||
*/
|
*/
|
||||||
std::string Post(
|
std::string Post(const std::string& url, const std::string& body,
|
||||||
const std::string& url, const std::string& body,
|
const std::vector<std::pair<std::string, std::string>>&
|
||||||
const std::vector<std::pair<std::string, std::string>>& headers)
|
headers) override;
|
||||||
override;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Percent-encodes a single URI component (query parameter value or
|
* @brief Percent-encodes a single URI component (query parameter value or
|
||||||
|
|||||||
@@ -121,8 +121,8 @@ std::optional<ApplicationOptions> ParseArguments(
|
|||||||
|
|
||||||
// Enforce mutual exclusivity before any further configuration is
|
// Enforce mutual exclusivity before any further configuration is
|
||||||
// applied.
|
// applied.
|
||||||
const int selected_generator_count =
|
const int selected_generator_count = (use_mocked ? 1 : 0) +
|
||||||
(use_mocked ? 1 : 0) + (!model_path.empty() ? 1 : 0) +
|
(!model_path.empty() ? 1 : 0) +
|
||||||
(use_openai ? 1 : 0);
|
(use_openai ? 1 : 0);
|
||||||
|
|
||||||
if (selected_generator_count > 1) {
|
if (selected_generator_count > 1) {
|
||||||
@@ -190,7 +190,8 @@ std::optional<ApplicationOptions> ParseArguments(
|
|||||||
: use_openai ? GeneratorMode::kOpenAI
|
: use_openai ? GeneratorMode::kOpenAI
|
||||||
: GeneratorMode::kLlama;
|
: GeneratorMode::kLlama;
|
||||||
options.generator.model_path = model_path;
|
options.generator.model_path = model_path;
|
||||||
options.generator.openai_model = var_map["openai-model"].as<std::string>();
|
options.generator.openai_model =
|
||||||
|
var_map["openai-model"].as<std::string>();
|
||||||
|
|
||||||
// Only populate sampling config when the user explicitly overrides at
|
// Only populate sampling config when the user explicitly overrides at
|
||||||
// least one value. Leaving it as std::nullopt lets LlamaGenerator fall
|
// least one value. Leaving it as std::nullopt lets LlamaGenerator fall
|
||||||
|
|||||||
@@ -43,6 +43,5 @@ std::vector<City> BiergartenPipelineOrchestrator::QueryLocations() {
|
|||||||
" Locations available: {}\n Sampled Locations: {}",
|
" Locations available: {}\n Sampled Locations: {}",
|
||||||
all_locations.size(), sampled_locations.size())});
|
all_locations.size(), sampled_locations.size())});
|
||||||
|
|
||||||
|
|
||||||
return sampled_locations;
|
return sampled_locations;
|
||||||
}
|
}
|
||||||
@@ -8,14 +8,13 @@
|
|||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <boost/json.hpp>
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
#include <format>
|
#include <format>
|
||||||
#include <span>
|
#include <span>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
|
||||||
#include <boost/json.hpp>
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
std::string Trim(std::string_view value) {
|
std::string Trim(std::string_view value) {
|
||||||
constexpr std::string_view whitespace = " \t\n\r\f\v";
|
constexpr std::string_view whitespace = " \t\n\r\f\v";
|
||||||
@@ -55,7 +54,8 @@ std::string CondenseWhitespace(std::string_view text) {
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Keeps truncation past the string's midpoint, preserving the critical opening content.
|
// Keeps truncation past the string's midpoint, preserving the critical opening
|
||||||
|
// content.
|
||||||
constexpr size_t kTruncationGuardDivisor = 2;
|
constexpr size_t kTruncationGuardDivisor = 2;
|
||||||
|
|
||||||
bool ReadRequiredTrimmedStringField(const boost::json::object& obj,
|
bool ReadRequiredTrimmedStringField(const boost::json::object& obj,
|
||||||
@@ -64,8 +64,7 @@ bool ReadRequiredTrimmedStringField(const boost::json::object& obj,
|
|||||||
const boost::json::value* field = obj.if_contains(key);
|
const boost::json::value* field = obj.if_contains(key);
|
||||||
if (field == nullptr || !field->is_string()) {
|
if (field == nullptr || !field->is_string()) {
|
||||||
if (error_out != nullptr) {
|
if (error_out != nullptr) {
|
||||||
*error_out =
|
*error_out = std::format("Missing or invalid string field: {}", key);
|
||||||
std::format("Missing or invalid string field: {}", key);
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,9 +6,11 @@
|
|||||||
*
|
*
|
||||||
* API docs last accessed 2026-08-23.
|
* API docs last accessed 2026-08-23.
|
||||||
* Docs: https://platform.openai.com/docs/api-reference/chat/create
|
* Docs: https://platform.openai.com/docs/api-reference/chat/create
|
||||||
* Structured Outputs: https://platform.openai.com/docs/guides/structured-outputs
|
* Structured Outputs:
|
||||||
|
* https://platform.openai.com/docs/guides/structured-outputs
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <boost/json.hpp>
|
||||||
#include <format>
|
#include <format>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -16,8 +18,6 @@
|
|||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <boost/json.hpp>
|
|
||||||
|
|
||||||
#include "data_generation/openai_generator.h"
|
#include "data_generation/openai_generator.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
@@ -48,7 +48,6 @@ std::string OpenAIGenerator::CallChatCompletionsApi(
|
|||||||
const std::string& system_prompt, const std::string& user_prompt,
|
const std::string& system_prompt, const std::string& user_prompt,
|
||||||
std::string_view json_schema, std::string_view schema_name,
|
std::string_view json_schema, std::string_view schema_name,
|
||||||
int max_tokens) {
|
int max_tokens) {
|
||||||
|
|
||||||
boost::system::error_code schema_error;
|
boost::system::error_code schema_error;
|
||||||
boost::json::value schema_value =
|
boost::json::value schema_value =
|
||||||
boost::json::parse(json_schema, schema_error);
|
boost::json::parse(json_schema, schema_error);
|
||||||
@@ -60,7 +59,8 @@ std::string OpenAIGenerator::CallChatCompletionsApi(
|
|||||||
schema_error.message());
|
schema_error.message());
|
||||||
}
|
}
|
||||||
|
|
||||||
// response_format: https://platform.openai.com/docs/guides/structured-outputs
|
// response_format:
|
||||||
|
// https://platform.openai.com/docs/guides/structured-outputs
|
||||||
boost::json::object json_schema_wrapper;
|
boost::json::object json_schema_wrapper;
|
||||||
json_schema_wrapper["name"] = std::string(schema_name);
|
json_schema_wrapper["name"] = std::string(schema_name);
|
||||||
json_schema_wrapper["strict"] = true;
|
json_schema_wrapper["strict"] = true;
|
||||||
@@ -127,14 +127,14 @@ std::string OpenAIGenerator::CallChatCompletionsApi(
|
|||||||
message_field != nullptr && message_field->is_string()) {
|
message_field != nullptr && message_field->is_string()) {
|
||||||
error_message = std::string(message_field->as_string());
|
error_message = std::string(message_field->as_string());
|
||||||
}
|
}
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(std::format(
|
||||||
std::format("OpenAIGenerator: API request failed: {}", error_message));
|
"OpenAIGenerator: API request failed: {}", error_message));
|
||||||
}
|
}
|
||||||
|
|
||||||
const boost::json::value* choices_field = response_obj.
|
const boost::json::value* choices_field =
|
||||||
if_contains("choices");
|
response_obj.if_contains("choices");
|
||||||
bool choices_field_missing_or_invalid =
|
bool choices_field_missing_or_invalid = choices_field == nullptr ||
|
||||||
choices_field == nullptr || !choices_field->is_array() ||
|
!choices_field->is_array() ||
|
||||||
choices_field->get_array().empty();
|
choices_field->get_array().empty();
|
||||||
|
|
||||||
if (choices_field_missing_or_invalid) {
|
if (choices_field_missing_or_invalid) {
|
||||||
@@ -161,12 +161,12 @@ std::string OpenAIGenerator::CallChatCompletionsApi(
|
|||||||
|
|
||||||
// Refusal field: https://platform.openai.com/docs/guides/structured-outputs
|
// Refusal field: https://platform.openai.com/docs/guides/structured-outputs
|
||||||
const boost::json::value* refusal_field = message_obj.if_contains("refusal");
|
const boost::json::value* refusal_field = message_obj.if_contains("refusal");
|
||||||
bool model_refused_request =
|
bool model_refused_request = refusal_field != nullptr &&
|
||||||
refusal_field != nullptr && refusal_field->is_string() &&
|
refusal_field->is_string() &&
|
||||||
!refusal_field->as_string().empty();
|
!refusal_field->as_string().empty();
|
||||||
if (model_refused_request) {
|
if (model_refused_request) {
|
||||||
throw std::runtime_error(std::format(
|
throw std::runtime_error(
|
||||||
"OpenAIGenerator: request refused by safety policies: {}",
|
std::format("OpenAIGenerator: request refused by safety policies: {}",
|
||||||
std::string(refusal_field->as_string())));
|
std::string(refusal_field->as_string())));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,8 +17,7 @@
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
std::string FormatLocalLanguageCodes(
|
std::string FormatLocalLanguageCodes(const std::vector<std::string>& codes) {
|
||||||
const std::vector<std::string>& codes) {
|
|
||||||
if (codes.empty()) {
|
if (codes.empty()) {
|
||||||
return "Not provided";
|
return "Not provided";
|
||||||
}
|
}
|
||||||
@@ -36,7 +35,8 @@ std::string FormatLocalLanguageCodes(
|
|||||||
|
|
||||||
constexpr int kBreweryMaxTokens = 2800;
|
constexpr int kBreweryMaxTokens = 2800;
|
||||||
|
|
||||||
// Structured outputs already guarantee schema-valid JSON, so retries here only cover transient failures or the model returning placeholder text.
|
// Structured outputs already guarantee schema-valid JSON, so retries here only
|
||||||
|
// cover transient failures or the model returning placeholder text.
|
||||||
constexpr int kMaxAttempts = 2;
|
constexpr int kMaxAttempts = 2;
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
@@ -51,7 +51,8 @@ BreweryResult OpenAIGenerator::GenerateBrewery(
|
|||||||
FormatLocalLanguageCodes(location.local_languages);
|
FormatLocalLanguageCodes(location.local_languages);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads the backend-agnostic brewery system prompt via the injected prompt directory.
|
* Loads the backend-agnostic brewery system prompt via the injected prompt
|
||||||
|
* directory.
|
||||||
*/
|
*/
|
||||||
const std::string system_prompt =
|
const std::string system_prompt =
|
||||||
prompt_directory_->Load("BREWERY_GENERATION");
|
prompt_directory_->Load("BREWERY_GENERATION");
|
||||||
@@ -70,9 +71,9 @@ BreweryResult OpenAIGenerator::GenerateBrewery(
|
|||||||
std::string last_error;
|
std::string last_error;
|
||||||
|
|
||||||
for (int attempt = 0; attempt < kMaxAttempts; ++attempt) {
|
for (int attempt = 0; attempt < kMaxAttempts; ++attempt) {
|
||||||
raw = CallChatCompletionsApi(system_prompt, user_prompt,
|
raw =
|
||||||
kBreweryJsonSchema, "brewery_result",
|
CallChatCompletionsApi(system_prompt, user_prompt, kBreweryJsonSchema,
|
||||||
kBreweryMaxTokens);
|
"brewery_result", kBreweryMaxTokens);
|
||||||
if (logger_) {
|
if (logger_) {
|
||||||
logger_->Log({.level = LogLevel::Debug,
|
logger_->Log({.level = LogLevel::Debug,
|
||||||
.phase = PipelinePhase::BreweryAndBeerGeneration,
|
.phase = PipelinePhase::BreweryAndBeerGeneration,
|
||||||
|
|||||||
@@ -16,7 +16,8 @@
|
|||||||
namespace {
|
namespace {
|
||||||
constexpr int kUserMaxTokens = 1200;
|
constexpr int kUserMaxTokens = 1200;
|
||||||
|
|
||||||
// Structured outputs already guarantee schema-valid JSON, so retries here only cover transient failures or placeholder text.
|
// Structured outputs already guarantee schema-valid JSON, so retries here only
|
||||||
|
// cover transient failures or placeholder text.
|
||||||
constexpr int kMaxAttempts = 2;
|
constexpr int kMaxAttempts = 2;
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
@@ -47,8 +48,7 @@ UserResult OpenAIGenerator::GenerateUser(const EnrichedCity& city,
|
|||||||
|
|
||||||
const std::string retry_context = std::format(
|
const std::string retry_context = std::format(
|
||||||
"Name: {} {}\nCity: {}, {}\nPersona: {}", name.first_name,
|
"Name: {} {}\nCity: {}, {}\nPersona: {}", name.first_name,
|
||||||
name.last_name, city.location.city, city.location.country,
|
name.last_name, city.location.city, city.location.country, persona.name);
|
||||||
persona.name);
|
|
||||||
|
|
||||||
std::string raw;
|
std::string raw;
|
||||||
std::string last_error;
|
std::string last_error;
|
||||||
|
|||||||
@@ -3,12 +3,12 @@
|
|||||||
* @brief OpenAIGenerator constructor.
|
* @brief OpenAIGenerator constructor.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "data_generation/openai_generator.h"
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "data_generation/openai_generator.h"
|
|
||||||
|
|
||||||
OpenAIGenerator::OpenAIGenerator(
|
OpenAIGenerator::OpenAIGenerator(
|
||||||
std::string api_key, std::string model, std::shared_ptr<ILogger> logger,
|
std::string api_key, std::string model, std::shared_ptr<ILogger> logger,
|
||||||
std::unique_ptr<IPromptDirectory> prompt_directory,
|
std::unique_ptr<IPromptDirectory> prompt_directory,
|
||||||
|
|||||||
@@ -166,10 +166,10 @@ int main(const int argc, char** argv) {
|
|||||||
inj.template create<std::unique_ptr<WebClient>>(),
|
inj.template create<std::unique_ptr<WebClient>>(),
|
||||||
log_producer);
|
log_producer);
|
||||||
}),
|
}),
|
||||||
di::bind<DataGenerator>().to(
|
di::bind<DataGenerator>().to([&options, &model_path, &sampling,
|
||||||
[&options, &model_path, &sampling, &prompt_directory,
|
&prompt_directory,
|
||||||
&log_producer](
|
&log_producer](const auto& inj)
|
||||||
const auto& inj) -> std::unique_ptr<DataGenerator> {
|
-> std::unique_ptr<DataGenerator> {
|
||||||
if (options.generator.mode == GeneratorMode::kMock) {
|
if (options.generator.mode == GeneratorMode::kMock) {
|
||||||
log_producer->Log({.level = LogLevel::Info,
|
log_producer->Log({.level = LogLevel::Info,
|
||||||
.phase = PipelinePhase::Startup,
|
.phase = PipelinePhase::Startup,
|
||||||
@@ -179,8 +179,7 @@ int main(const int argc, char** argv) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (options.generator.mode == GeneratorMode::kOpenAI) {
|
if (options.generator.mode == GeneratorMode::kOpenAI) {
|
||||||
log_producer->Log(
|
log_producer->Log({.level = LogLevel::Info,
|
||||||
{.level = LogLevel::Info,
|
|
||||||
.phase = PipelinePhase::Startup,
|
.phase = PipelinePhase::Startup,
|
||||||
.message = std::format(
|
.message = std::format(
|
||||||
"Generator: OpenAIGenerator | model={}",
|
"Generator: OpenAIGenerator | model={}",
|
||||||
@@ -194,7 +193,8 @@ int main(const int argc, char** argv) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef BIERGARTEN_MOCK_ONLY
|
#ifdef BIERGARTEN_MOCK_ONLY
|
||||||
// Only reachable when mode == Llama, which requires llama.cpp and is excluded from a BIERGARTEN_MOCK_ONLY build.
|
// Only reachable when mode == Llama, which requires llama.cpp and
|
||||||
|
// is excluded from a BIERGARTEN_MOCK_ONLY build.
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"LlamaGenerator is unavailable in a BIERGARTEN_MOCK_ONLY "
|
"LlamaGenerator is unavailable in a BIERGARTEN_MOCK_ONLY "
|
||||||
"build");
|
"build");
|
||||||
|
|||||||
@@ -125,8 +125,7 @@ const LocationsList& CuratedJsonDataService::LoadLocations() {
|
|||||||
if (!cache_.locations.empty()) {
|
if (!cache_.locations.empty()) {
|
||||||
return cache_.locations;
|
return cache_.locations;
|
||||||
}
|
}
|
||||||
boost::json::value root_val =
|
boost::json::value root_val = ParseJsonFile(filepaths_.locations_path);
|
||||||
ParseJsonFile(filepaths_.locations_path);
|
|
||||||
|
|
||||||
if (!root_val.is_object()) {
|
if (!root_val.is_object()) {
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
@@ -135,14 +134,12 @@ const LocationsList& CuratedJsonDataService::LoadLocations() {
|
|||||||
|
|
||||||
boost::json::object root_obj = root_val.as_object();
|
boost::json::object root_obj = root_val.as_object();
|
||||||
|
|
||||||
if (!root_obj.contains("cities"))
|
if (!root_obj.contains("cities")) {
|
||||||
{
|
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"Invalid locations JSON: root object must contain a 'cities' key");
|
"Invalid locations JSON: root object must contain a 'cities' key");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!root_obj["cities"].is_array())
|
if (!root_obj["cities"].is_array()) {
|
||||||
{
|
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"Invalid locations JSON: 'cities' key must be an array");
|
"Invalid locations JSON: 'cities' key must be an array");
|
||||||
}
|
}
|
||||||
@@ -179,8 +176,7 @@ const PersonasList& CuratedJsonDataService::LoadPersonas() {
|
|||||||
return cache_.personas;
|
return cache_.personas;
|
||||||
}
|
}
|
||||||
|
|
||||||
const boost::json::value root =
|
const boost::json::value root = ParseJsonFile(filepaths_.personas_path);
|
||||||
ParseJsonFile(filepaths_.personas_path);
|
|
||||||
|
|
||||||
if (!root.is_array()) {
|
if (!root.is_array()) {
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
@@ -215,8 +211,7 @@ const ForenamesByCountryMap& CuratedJsonDataService::LoadForenamesByCountry() {
|
|||||||
return cache_.forenames_by_country;
|
return cache_.forenames_by_country;
|
||||||
}
|
}
|
||||||
|
|
||||||
const boost::json::value root =
|
const boost::json::value root = ParseJsonFile(filepaths_.forenames_path);
|
||||||
ParseJsonFile(filepaths_.forenames_path);
|
|
||||||
|
|
||||||
if (!root.is_object()) {
|
if (!root.is_object()) {
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
@@ -263,8 +258,7 @@ const SurnamesByCountryMap& CuratedJsonDataService::LoadSurnamesByCountry() {
|
|||||||
return cache_.surnames_by_country;
|
return cache_.surnames_by_country;
|
||||||
}
|
}
|
||||||
|
|
||||||
const boost::json::value root =
|
const boost::json::value root = ParseJsonFile(filepaths_.surnames_path);
|
||||||
ParseJsonFile(filepaths_.surnames_path);
|
|
||||||
|
|
||||||
if (!root.is_object()) {
|
if (!root.is_object()) {
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
|
|||||||
@@ -180,8 +180,8 @@ uint64_t SqliteExportService::ProcessRecord(const BreweryRecord& brewery) {
|
|||||||
sqlite_export_service_internal::Bind(
|
sqlite_export_service_internal::Bind(
|
||||||
insert_brewery_address_stmt_,
|
insert_brewery_address_stmt_,
|
||||||
sqlite_export_service_internal::BoundParam<double>{
|
sqlite_export_service_internal::BoundParam<double>{
|
||||||
.index = sqlite_export_service_internal::
|
.index =
|
||||||
kBreweryAddressLatitudeBindIndex,
|
sqlite_export_service_internal::kBreweryAddressLatitudeBindIndex,
|
||||||
.value = brewery.address.latitude,
|
.value = brewery.address.latitude,
|
||||||
.action = "Failed to bind SQLite brewery address latitude"});
|
.action = "Failed to bind SQLite brewery address latitude"});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user