mirror of
https://github.com/aaronpo97/the-biergarten-app.git
synced 2026-06-01 01:54:00 +00:00
Enhance ValidateBreweryJson to include reasoning output and update GenerateBrewery to use user_prompt
This commit is contained in:
@@ -58,6 +58,7 @@ void AppendTokenPiece(const llama_vocab* vocab, llama_token token,
|
|||||||
*/
|
*/
|
||||||
std::optional<std::string> ValidateBreweryJson(const std::string& raw,
|
std::optional<std::string> ValidateBreweryJson(const std::string& raw,
|
||||||
std::string& name_out,
|
std::string& name_out,
|
||||||
std::string& description_out);
|
std::string& description_out,
|
||||||
|
std::string& reasoning_out);
|
||||||
|
|
||||||
#endif // BIERGARTEN_PIPELINE_INCLUDES_DATA_GENERATION_LLAMA_GENERATOR_HELPERS_H_
|
#endif // BIERGARTEN_PIPELINE_INCLUDES_DATA_GENERATION_LLAMA_GENERATOR_HELPERS_H_
|
||||||
|
|||||||
@@ -43,11 +43,8 @@ BreweryResult LlamaGenerator::GenerateBrewery(
|
|||||||
const std::string system_prompt =
|
const std::string system_prompt =
|
||||||
LoadBrewerySystemPrompt("prompts/system.md");
|
LoadBrewerySystemPrompt("prompts/system.md");
|
||||||
|
|
||||||
/**
|
|
||||||
* User prompt: provides geographic context to guide generation towards
|
std::string user_prompt = std::format(
|
||||||
* culturally relevant and locally-inspired brewery attributes
|
|
||||||
*/
|
|
||||||
std::string prompt = std::format(
|
|
||||||
"## CITY:\n{}\n\n## COUNTRY:\n{}\n\n## CONTEXT:\n{}",
|
"## CITY:\n{}\n\n## COUNTRY:\n{}\n\n## CONTEXT:\n{}",
|
||||||
location.city, location.country, safe_region_context);
|
location.city, location.country, safe_region_context);
|
||||||
|
|
||||||
@@ -70,7 +67,7 @@ BreweryResult LlamaGenerator::GenerateBrewery(
|
|||||||
for (int attempt = 0; attempt < max_attempts; ++attempt) {
|
for (int attempt = 0; attempt < max_attempts; ++attempt) {
|
||||||
constexpr int max_tokens = 1052;
|
constexpr int max_tokens = 1052;
|
||||||
// Generate brewery data from LLM
|
// Generate brewery data from LLM
|
||||||
raw = this->Infer(system_prompt, prompt, max_tokens, kBreweryJsonGrammar);
|
raw = this->Infer(system_prompt, user_prompt, max_tokens, kBreweryJsonGrammar);
|
||||||
spdlog::debug("LlamaGenerator: raw output (attempt {}): {}", attempt + 1,
|
spdlog::debug("LlamaGenerator: raw output (attempt {}): {}", attempt + 1,
|
||||||
raw);
|
raw);
|
||||||
|
|
||||||
@@ -78,10 +75,16 @@ BreweryResult LlamaGenerator::GenerateBrewery(
|
|||||||
|
|
||||||
std::string name;
|
std::string name;
|
||||||
std::string description;
|
std::string description;
|
||||||
|
std::string reasoning;
|
||||||
const std::optional<std::string> validation_error =
|
const std::optional<std::string> validation_error =
|
||||||
ValidateBreweryJson(raw, name, description);
|
ValidateBreweryJson(raw, name, description, reasoning);
|
||||||
if (!validation_error.has_value()) {
|
if (!validation_error.has_value()) {
|
||||||
// Success: return parsed brewery data
|
// Success: return parsed brewery data
|
||||||
|
|
||||||
|
spdlog::info(
|
||||||
|
"LlamaGenerator: successfully generated brewery data on attempt {}:\n reasoning='{}',\n name='{}',\n description='{}'",
|
||||||
|
attempt + 1, reasoning, name, description);
|
||||||
|
|
||||||
return BreweryResult{.name = std::move(name),
|
return BreweryResult{.name = std::move(name),
|
||||||
.description = std::move(description)};
|
.description = std::move(description)};
|
||||||
}
|
}
|
||||||
@@ -93,7 +96,7 @@ BreweryResult LlamaGenerator::GenerateBrewery(
|
|||||||
attempt + 1, *validation_error);
|
attempt + 1, *validation_error);
|
||||||
|
|
||||||
// Update prompt with error details to guide LLM toward correct output.
|
// Update prompt with error details to guide LLM toward correct output.
|
||||||
prompt = std::format(
|
user_prompt = std::format(
|
||||||
R"(Your previous response was invalid. Error: {}
|
R"(Your previous response was invalid. Error: {}
|
||||||
Return ONLY valid JSON with exactly these keys, in this exact order: {{"reasoning": "<brief planning summary>", "name": "<brewery name>", "description": "<single-paragraph description>"}}.
|
Return ONLY valid JSON with exactly these keys, in this exact order: {{"reasoning": "<brief planning summary>", "name": "<brewery name>", "description": "<single-paragraph description>"}}.
|
||||||
Do not include markdown, comments, extra keys, or literal placeholder values.
|
Do not include markdown, comments, extra keys, or literal placeholder values.
|
||||||
|
|||||||
@@ -201,7 +201,8 @@ void AppendTokenPiece(const llama_vocab* vocab, llama_token token,
|
|||||||
|
|
||||||
std::optional<std::string> ValidateBreweryJson(const std::string& raw,
|
std::optional<std::string> ValidateBreweryJson(const std::string& raw,
|
||||||
std::string& name_out,
|
std::string& name_out,
|
||||||
std::string& description_out) {
|
std::string& description_out,
|
||||||
|
std::string& reasoning_out) {
|
||||||
auto validate_object = [&](const boost::json::value& json_value,
|
auto validate_object = [&](const boost::json::value& json_value,
|
||||||
std::string& error_out) -> bool {
|
std::string& error_out) -> bool {
|
||||||
if (!json_value.is_object()) {
|
if (!json_value.is_object()) {
|
||||||
@@ -209,7 +210,14 @@ std::optional<std::string> ValidateBreweryJson(const std::string& raw,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const auto& obj = json_value.get_object();
|
const auto& obj = json_value.get_object();
|
||||||
|
|
||||||
|
if (!obj.contains("reasoning") || !obj.at("reasoning").is_string()) {
|
||||||
|
error_out = "JSON field 'reasoning' is missing or not a string";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!obj.contains("name") || !obj.at("name").is_string()) {
|
if (!obj.contains("name") || !obj.at("name").is_string()) {
|
||||||
error_out = "JSON field 'name' is missing or not a string";
|
error_out = "JSON field 'name' is missing or not a string";
|
||||||
return false;
|
return false;
|
||||||
@@ -219,6 +227,12 @@ std::optional<std::string> ValidateBreweryJson(const std::string& raw,
|
|||||||
error_out = "JSON field 'description' is missing or not a string";
|
error_out = "JSON field 'description' is missing or not a string";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
const auto& reasoning_value = obj.at("reasoning").as_string();
|
||||||
|
reasoning_out = Trim(std::string_view(reasoning_value.data(), reasoning_value.size()));
|
||||||
|
if (reasoning_out.empty()) {
|
||||||
|
error_out = "JSON field 'reasoning' must not be empty";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
const auto& name_value = obj.at("name").as_string();
|
const auto& name_value = obj.at("name").as_string();
|
||||||
const auto& description_value = obj.at("description").as_string();
|
const auto& description_value = obj.at("description").as_string();
|
||||||
@@ -239,15 +253,16 @@ std::optional<std::string> ValidateBreweryJson(const std::string& raw,
|
|||||||
std::string name_lower = name_out;
|
std::string name_lower = name_out;
|
||||||
std::string description_lower = description_out;
|
std::string description_lower = description_out;
|
||||||
|
|
||||||
std::ranges::transform(name_lower, name_lower.begin(),
|
|
||||||
[](unsigned char character) {
|
|
||||||
return static_cast<char>(std::tolower(character));
|
|
||||||
});
|
|
||||||
|
|
||||||
std::ranges::transform(description_lower, description_lower.begin(),
|
auto string_to_lower = [](std::string& str_out) {
|
||||||
[](unsigned char character) {
|
std::ranges::transform(str_out, str_out.begin(),
|
||||||
return static_cast<char>(std::tolower(character));
|
[](unsigned char character) {
|
||||||
});
|
return static_cast<char>(std::tolower(character));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
string_to_lower(name_lower);
|
||||||
|
string_to_lower(description_lower);
|
||||||
|
|
||||||
if (name_lower == "string" || description_lower == "string") {
|
if (name_lower == "string" || description_lower == "string") {
|
||||||
error_out = "JSON appears to be a schema placeholder, not content";
|
error_out = "JSON appears to be a schema placeholder, not content";
|
||||||
|
|||||||
Reference in New Issue
Block a user