ik_llama_opt/examples/server/test-bailing-parser.cpp

62 lines
2.8 KiB
C++

// Standalone harness for bailing_parser.hpp (no full build needed).
#include <cassert>
#include <iostream>
#include "parsers/bailing_parser.hpp"
static int failures = 0;
#define CHECK(cond) do { if (!(cond)) { \
std::cout << "FAIL line " << __LINE__ << ": " #cond "\n"; failures++; } } while (0)
int main() {
// 1. Exact live-captured trapped call (Ling-3.0-Flash via ik_llama).
std::string trapped =
"<tool_call>web_search\n"
"<arg_key>limit</arg_key>\n"
"<arg_value>3</arg_value><arg_key>query</arg_key>\n"
"<arg_value>Proxmox GPU passthrough troubleshooting 2026</arg_value>\n"
"</tool_call>";
json calls = bailing::parse_tool_calls(trapped);
CHECK(calls.size() == 1);
CHECK(calls[0]["function"]["name"] == "web_search");
json args = json::parse(calls[0]["function"]["arguments"].get<std::string>());
CHECK(args["query"] == "Proxmox GPU passthrough troubleshooting 2026");
CHECK(args["limit"] == "3"); // raw strings; client coerces downstream
CHECK(calls[0]["id"] == ""); // filled later by set_tool_call_ids
CHECK(bailing::has_complete_block(trapped));
// 2. Truncated block: no rescue.
CHECK(bailing::parse_tool_calls("<tool_call>web_search\n<arg_key>q").empty());
CHECK(!bailing::has_complete_block("<tool_call>web_search\n<arg_key>q"));
// 3. Tag name as function name is rescued like any identifier (client decides).
json weird = bailing::parse_tool_calls(
"<tool_call>tool_call\n<arg_key>q</arg_key>\n<arg_value>x</arg_value>\n</tool_call>");
CHECK(weird.size() == 1 && weird[0]["function"]["name"] == "tool_call");
// 4. Garbage function name rejected.
CHECK(bailing::parse_tool_calls("<tool_call>prompt: ANT</role></tool_call>").empty());
// 5. Multiple blocks, cap respected.
std::string multi;
for (int i = 0; i < 20; i++) multi += trapped;
CHECK(bailing::parse_tool_calls(multi).size() == 8);
// 6. Plain text: nothing.
CHECK(bailing::parse_tool_calls("Just thinking aloud here.").empty());
CHECK(bailing::parse_tool_calls("").empty());
// 7. Strip removes only executed (complete) blocks, preserves narration.
std::string mixed = "Let me search for that now.\n" + trapped + "\nDone thinking.";
std::string stripped = bailing::remove_executed_blocks(mixed);
CHECK(stripped.find("<tool_call>") == std::string::npos);
CHECK(stripped.find("Let me search for that now.") != std::string::npos);
CHECK(stripped.find("Done thinking.") != std::string::npos);
// Truncated fragments survive the strip.
std::string partial = "thinking <tool_call>web_search\n<arg_key>q";
CHECK(bailing::remove_executed_blocks(partial) == partial);
if (failures == 0) std::cout << "ALL BAILING PARSER CHECKS PASSED\n";
return failures;
}