-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsession.cpp
More file actions
171 lines (137 loc) · 6.1 KB
/
Copy pathsession.cpp
File metadata and controls
171 lines (137 loc) · 6.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
#include "session.hpp"
#include <fmt/core.h>
namespace claw::runtime {
Session::Session(
api::Client& client,
tools::ToolRegistry& registry,
SessionConfig config
) : m_client(client), m_registry(registry), m_config(std::move(config)) {}
void Session::set_tool_event_callback(ToolEventCallback cb) {
m_tool_event_cb = std::move(cb);
}
const std::vector<api::Message>& Session::get_history() const {
return m_messages;
}
UsageStats Session::get_usage() const {
return m_usage;
}
void Session::clear_history() {
m_messages.clear();
m_usage = {};
}
void Session::compact_history() {
if (m_messages.size() > m_config.max_history_turns) {
size_t trim = m_messages.size() - m_config.max_history_turns;
m_messages.erase(m_messages.begin(),
m_messages.begin() + static_cast<long>(trim));
}
}
// ── Core Agentic Turn Loop ───────────────────────────────────────────
//
// This is the heart of the agent. It implements the loop:
//
// 1. Add user message to history
// 2. Send full history + tool definitions to the API
// 3. If stop_reason == "tool_use":
// a. Add the assistant's response (with tool_use blocks) to history
// b. Execute each requested tool
// c. Add tool results to history as a "user" message
// d. Go to step 2
// 4. If stop_reason == "end_turn":
// a. Add the assistant's response to history
// b. Return the text to the user
std::string Session::run_turn(const std::string& user_input) {
if (user_input.empty()) return "";
// Step 1: Add user message
m_messages.push_back(api::Message::user_text(user_input));
compact_history();
// Get tool definitions once (they don't change mid-turn)
nlohmann::json tools_json = m_registry.get_tool_definitions();
std::string final_text;
int iterations = 0;
// Step 2–4: The agentic loop
while (iterations < m_config.max_tool_iterations) {
iterations++;
m_usage.api_calls++;
auto response = m_client.send_request(
m_config.system_prompt, m_messages, tools_json
);
if (!response) {
// API call failed — remove the user message and report
if (!m_messages.empty()) m_messages.pop_back();
return "[Error] Failed to get a response from the API.";
}
// Track usage
m_usage.input_tokens += response->input_tokens;
m_usage.output_tokens += response->output_tokens;
// Collect any text the model produced in this turn
std::string turn_text = response->get_text();
if (!turn_text.empty()) {
if (!final_text.empty()) final_text += "\n";
final_text += turn_text;
}
// ── Step 3: Handle tool_use ──────────────────────────────────
if (response->has_tool_calls()) {
// Add the full assistant message (text + tool_use blocks) to history
api::Message assistant_msg;
assistant_msg.role = "assistant";
assistant_msg.content = response->content;
m_messages.push_back(std::move(assistant_msg));
// Execute each tool and collect results
auto tool_calls = response->get_tool_calls();
std::vector<api::ToolResultBlock> results;
for (const auto& call : tool_calls) {
m_usage.tool_calls++;
auto result = execute_tool(call);
results.push_back(std::move(result));
}
// Add tool results as a "user" message (Anthropic API format)
m_messages.push_back(api::Message::tool_results(results));
compact_history();
// Continue the loop — the model will process tool results
continue;
}
// ── Step 4: end_turn — we're done ────────────────────────────
api::Message assistant_msg;
assistant_msg.role = "assistant";
assistant_msg.content = response->content;
m_messages.push_back(std::move(assistant_msg));
break;
}
if (iterations >= m_config.max_tool_iterations) {
final_text += "\n[Warning] Reached maximum tool iteration limit.";
}
return final_text;
}
// ── Tool Execution ───────────────────────────────────────────────────
api::ToolResultBlock Session::execute_tool(const api::ToolUseBlock& call) {
api::ToolResultBlock result;
result.tool_use_id = call.id;
auto* tool = m_registry.get_tool(call.name);
if (!tool) {
result.content = fmt::format("[Error] Unknown tool: {}", call.name);
result.is_error = true;
return result;
}
// Notify the CLI layer about the tool execution (for display)
if (m_tool_event_cb) {
m_tool_event_cb(
call.name,
call.input.dump(),
"" // output not yet known
);
}
try {
result.content = tool->execute(call.input);
} catch (const std::exception& e) {
result.content = fmt::format("[Error] Tool '{}' threw exception: {}",
call.name, e.what());
result.is_error = true;
}
// Notify CLI with the result
if (m_tool_event_cb) {
m_tool_event_cb(call.name, call.input.dump(), result.content);
}
return result;
}
} // namespace claw::runtime