| 726 | {"current_entries", tms.current_entries}, |
| 727 | {"current_bytes", tms.current_bytes}, |
| 728 | }}, |
| 729 | // The C++ daemon is linked in-process; if /props is responding, |
| 730 | // the daemon is alive by construction. |
| 731 | {"daemon", {{"alive", true}}}, |
| 732 | {"api", {{"endpoints", kApiEndpoints}}}, |
| 733 | // Capability flags surfaced for clients that don't want to crack |
| 734 | // open `reasoning` / `speculative` / etc. — matches the Python |
| 735 | // server's _capabilities() helper. |
| 736 | {"capabilities", { |
| 737 | {"reasoning_supported", reasoning_supported}, |
| 738 | {"speculative_supported", speculative_supported}, |
| 739 | {"tools_supported", tools_supported}, |
| 740 | }}, |
| 741 | }; |
| 742 | return body; |
| 743 | } |
| 744 | |
| 745 | // Normalize Anthropic's `system` field (top-level on /v1/messages and |
| 746 | // /v1/messages/count_tokens) into a leading `{role:"system", content:...}` |
| 747 | // entry on `messages`. Accepts either a flat string or an array of typed |
| 748 | // blocks (`[{type:"text", text:"..."}]`), and strips any |
| 749 | // `x-anthropic-billing-header:`-prefixed block injected by Claude Code so |
| 750 | // it never reaches the model or the token counter. |
| 751 | // |
| 752 | // Side-effect: prepends a system message to `messages` when the body has |
| 753 | // a non-empty `system` field after billing-header filtering. No-op |
| 754 | // otherwise. Both endpoints call this with identical semantics — having |
| 755 | // one helper guarantees token counting and generation can't drift. |
| 756 | static void normalize_anthropic_system(const json & body, json & messages) { |
| 757 | if (!body.contains("system")) return; |
| 758 | // Delegate strip to the pure fn; insert as system message. |
| 759 | std::string text = dflash::common::normalize_system_for_cache(body["system"]); |
| 760 | if (!text.empty()) { |
| 761 | json sys_msg = {{"role", "system"}, {"content", text}}; |
| 762 | messages.insert(messages.begin(), sys_msg); |
| 763 | } |
| 764 | } |
| 765 | |
| 766 | json parse_responses_arguments(const json & item) { |
| 767 | if (!item.contains("arguments")) return json::object(); |
| 768 | const auto & arguments = item["arguments"]; |
| 769 | if (arguments.is_object()) return arguments; |
| 770 | if (arguments.is_string()) { |
| 771 | try { |
| 772 | return json::parse(arguments.get<std::string>()); |
| 773 | } catch (const std::exception &) { |
| 774 | return json::object(); |
| 775 | } |
| 776 | } |
| 777 | return json::object(); |
| 778 | } |
| 779 | |
| 780 | std::string render_tool_call_xml(const std::string & name, const json & arguments) { |
| 781 | std::string out = "<function=" + name + ">\n"; |
| 782 | if (arguments.is_object()) { |
| 783 | for (const auto & [key, value] : arguments.items()) { |
| 784 | out += "<parameter=" + key + ">\n"; |
| 785 | out += value.is_string() ? value.get<std::string>() : value.dump(); |