// Standalone harness for bailing_parser.hpp (no full build needed). #include #include #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 = "web_search\n" "limit\n" "3query\n" "Proxmox GPU passthrough troubleshooting 2026\n" ""; 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()); 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("web_search\nq").empty()); CHECK(!bailing::has_complete_block("web_search\nq")); // 3. Tag name as function name is rescued like any identifier (client decides). json weird = bailing::parse_tool_calls( "tool_call\nq\nx\n"); CHECK(weird.size() == 1 && weird[0]["function"]["name"] == "tool_call"); // 4. Garbage function name rejected. CHECK(bailing::parse_tool_calls("prompt: ANT").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()); if (failures == 0) std::cout << "ALL BAILING PARSER CHECKS PASSED\n"; return failures; }