| 401 | } |
| 402 | |
| 403 | void analyze_reasoning::compare_reasoning_scope() { |
| 404 | json assistant_reasoning_content = json{ |
| 405 | { "role", "assistant" }, |
| 406 | { "content", ASSISTANT_MSG }, |
| 407 | { "reasoning_content", THINKING_CONTENT } |
| 408 | }; |
| 409 | |
| 410 | json assistant_reasoning_tools = json{ |
| 411 | { "role", "assistant" }, |
| 412 | { "content", nullptr }, |
| 413 | { "reasoning_content", THINKING_CONTENT }, |
| 414 | { "tool_calls", |
| 415 | json::array({ build_tool_call(FUN_FIRST, json{ { ARG_FIRST, "VVVV" }, { ARG_SECOND, "XXXX" } }) }) } |
| 416 | }; |
| 417 | |
| 418 | template_params params; |
| 419 | params.messages = json::array({ user_msg, assistant_reasoning_content }); |
| 420 | params.tools = tools; |
| 421 | params.add_generation_prompt = false; |
| 422 | params.enable_thinking = true; |
| 423 | |
| 424 | auto comparison = compare_variants( |
| 425 | *tmpl, params, [&](template_params & p) { p.messages = json::array({ user_msg, assistant_reasoning_tools }); }); |
| 426 | |
| 427 | if (!comparison) { |
| 428 | LOG_DBG(ANSI_ORANGE "%s: Template application failed\n" ANSI_RESET, __func__); |
| 429 | return; |
| 430 | } |
| 431 | |
| 432 | std::string reasoning_content = THINKING_CONTENT; |
| 433 | |
| 434 | // Check if reasoning only appears in variant B (with tools) |
| 435 | bool reasoning_in_A = comparison->output_A.find(reasoning_content) != std::string::npos; |
| 436 | bool reasoning_in_B = comparison->output_B.find(reasoning_content) != std::string::npos; |
| 437 | |
| 438 | if (!reasoning_in_A && reasoning_in_B) { |
| 439 | mode = reasoning_mode::TOOLS_ONLY; |
| 440 | LOG_DBG(ANSI_ORANGE "%s: Detected TOOLS_ONLY reasoning mode\n" ANSI_RESET, __func__); |
| 441 | |
| 442 | auto parser_wrapped = build_tagged_peg_parser([&](common_peg_parser_builder &p) { |
| 443 | return p.tag("pre", p.marker() + p.space()) + p.literal(reasoning_content) + p.space() + p.tag("post", (p.marker() + p.space())); |
| 444 | }); |
| 445 | auto result = parser_wrapped.parse_anywhere_and_extract(comparison->output_B); |
| 446 | if (result.result.success()) { |
| 447 | start = result.tags["pre"]; |
| 448 | end = trim_trailing_whitespace(result.tags["post"]); |
| 449 | } else { |
| 450 | auto parser_delimiter = build_tagged_peg_parser([&](common_peg_parser_builder &p) { |
| 451 | return p.literal(reasoning_content) + p.space() + p.optional(p.tag("post", (p.marker() + p.space()))); |
| 452 | }); |
| 453 | result = parser_delimiter.parse_anywhere_and_extract(comparison->output_B); |
| 454 | if (result.result.success()) { |
| 455 | end = trim_trailing_whitespace(result.tags["post"]); |
| 456 | } else { |
| 457 | LOG_DBG(ANSI_ORANGE "%s: Unable to extract reasoning markers, falling back to reasoning = NONE\n" ANSI_RESET, __func__); |
| 458 | mode = reasoning_mode::NONE; |
| 459 | } |
| 460 | } |
nothing calls this directly
no test coverage detected