2025-03-16 17:17:01 +08:00
# include "toolcall.h"
namespace humanus {
// Process current state and decide next actions using tools
bool ToolCallAgent : : think ( ) {
// Get response with tool options
auto response = llm - > ask_tool (
2025-04-06 16:32:51 +08:00
memory - > get_messages ( memory - > current_request ) ,
2025-03-19 18:44:54 +08:00
system_prompt ,
next_step_prompt ,
2025-03-16 17:17:01 +08:00
available_tools . to_params ( ) ,
tool_choice
) ;
tool_calls = ToolCall : : from_json_list ( response [ " tool_calls " ] ) ;
// Log response info
2025-03-20 01:12:15 +08:00
logger - > info ( " ✨ " + name + " 's thoughts: " + response [ " content " ] . get < std : : string > ( ) ) ;
2025-03-16 17:17:01 +08:00
logger - > info (
2025-03-17 14:07:41 +08:00
" 🛠️ " + name + " selected " + std : : to_string ( tool_calls . size ( ) ) + " tool(s) to use "
2025-03-16 17:17:01 +08:00
) ;
if ( tool_calls . size ( ) > 0 ) {
std : : string tools_str ;
for ( const auto & tool_call : tool_calls ) {
tools_str + = tool_call . function . name + " " ;
}
logger - > info (
" 🧰 Tools being prepared: " + tools_str
) ;
}
2025-04-08 23:26:53 +08:00
if ( state ! = AgentState : : RUNNING ) {
return false ;
}
2025-03-16 17:17:01 +08:00
try {
// Handle different tool_choices modes
if ( tool_choice = = " none " ) {
if ( tool_calls . size ( ) > 0 ) {
logger - > warn ( " 🤔 Hmm, " + name + " tried to use tools when they weren't available! " ) ;
}
if ( ! response [ " content " ] . empty ( ) ) {
memory - > add_message ( Message : : assistant_message ( response [ " content " ] ) ) ;
return true ;
}
return false ;
}
// Create and add assistant message
2025-03-20 01:12:15 +08:00
auto assistant_msg = Message : : assistant_message ( response [ " content " ] , tool_calls ) ;
2025-03-16 17:17:01 +08:00
memory - > add_message ( assistant_msg ) ;
if ( tool_choice = = " required " & & tool_calls . empty ( ) ) {
2025-03-16 22:56:03 +08:00
return true ; // Will be handled in act()
2025-03-16 17:17:01 +08:00
}
// For 'auto' mode, continue with content if no commands but content exists
if ( tool_choice = = " auto " & & ! tool_calls . empty ( ) ) {
2025-03-16 22:56:03 +08:00
return ! response [ " content " ] . empty ( ) ;
2025-03-16 17:17:01 +08:00
}
return ! tool_calls . empty ( ) ;
} catch ( const std : : exception & e ) {
logger - > error ( " 🚨 Oops! The " + name + " 's thinking process hit a snag: " + std : : string ( e . what ( ) ) ) ;
return false ;
}
}
// Execute tool calls and handle their results
std : : string ToolCallAgent : : act ( ) {
if ( tool_calls . empty ( ) ) {
if ( tool_choice = = " required " ) {
2025-03-19 18:44:54 +08:00
throw std : : runtime_error ( " Required tools but none selected " ) ;
2025-03-16 17:17:01 +08:00
}
// Return last message content if no tool calls
2025-03-23 14:35:54 +08:00
return memory - > get_messages ( ) . empty ( ) | | memory - > get_messages ( ) . back ( ) . content . empty ( ) ? " No content or commands to execute " : memory - > get_messages ( ) . back ( ) . content . dump ( ) ;
2025-03-16 17:17:01 +08:00
}
std : : vector < std : : string > results ;
for ( const auto & tool_call : tool_calls ) {
2025-04-08 23:26:53 +08:00
if ( state ! = AgentState : : RUNNING ) {
break ;
}
2025-03-16 22:56:03 +08:00
auto result = execute_tool ( tool_call ) ;
2025-03-16 17:17:01 +08:00
logger - > info (
2025-03-26 19:28:02 +08:00
" 🎯 Tool ` " + tool_call . function . name + " ` completed its mission! Result: " + result . substr ( 0 , 500 ) + ( result . size ( ) > 500 ? " ... " : " " )
2025-03-16 17:17:01 +08:00
) ;
// Add tool response to memory
Message tool_msg = Message : : tool_message (
2025-03-16 22:56:03 +08:00
result , tool_call . id , tool_call . function . name
2025-03-16 17:17:01 +08:00
) ;
memory - > add_message ( tool_msg ) ;
2025-03-16 22:56:03 +08:00
results . push_back ( result ) ;
2025-03-16 17:17:01 +08:00
}
std : : string result_str ;
for ( const auto & result : results ) {
result_str + = result + " \n \n " ;
}
2025-04-08 23:26:53 +08:00
if ( state ! = AgentState : : RUNNING ) {
result_str + = " Agent is not running, so no more tool calls will be executed. \n \n " ;
}
2025-03-16 17:17:01 +08:00
return result_str ;
}
2025-03-16 22:56:03 +08:00
// Execute a single tool call with robust error handling
2025-03-16 17:17:01 +08:00
std : : string ToolCallAgent : : execute_tool ( ToolCall tool_call ) {
if ( tool_call . empty ( ) | | tool_call . function . empty ( ) | | tool_call . function . name . empty ( ) ) {
return " Error: Invalid command format " ;
}
std : : string name = tool_call . function . name ;
if ( available_tools . tools_map . find ( name ) = = available_tools . tools_map . end ( ) ) {
2025-03-26 19:28:02 +08:00
return " Error: Unknown tool ` " + name + " `. Please use one of the following tools: " +
2025-03-17 14:07:41 +08:00
std : : accumulate ( available_tools . tools_map . begin ( ) , available_tools . tools_map . end ( ) , std : : string ( ) ,
[ ] ( const std : : string & a , const auto & b ) {
return a + ( a . empty ( ) ? " " : " , " ) + b . first ;
} ) ;
2025-03-16 17:17:01 +08:00
}
try {
// Parse arguments
json args = tool_call . function . arguments ;
2025-03-16 22:56:03 +08:00
if ( args . is_string ( ) ) {
args = json : : parse ( args . get < std : : string > ( ) ) ;
}
2025-03-16 17:17:01 +08:00
// Execute the tool
2025-03-26 19:28:02 +08:00
logger - > info ( " 🔧 Activating tool: ` " + name + " `... " ) ;
2025-03-16 17:17:01 +08:00
ToolResult result = available_tools . execute ( name , args ) ;
// Format result for display
2025-03-16 22:56:03 +08:00
auto observation = result . empty ( ) ?
2025-03-16 17:17:01 +08:00
" Cmd ` " + name + " ` completed with no output " :
" Observed output of cmd ` " + name + " ` executed: \n " + result . to_string ( ) ;
// Handle special tools like `finish`
_handle_special_tool ( name , result ) ;
return observation ;
2025-03-18 16:40:16 +08:00
} catch ( const json : : exception & /* e */ ) {
2025-03-16 17:17:01 +08:00
std : : string error_msg = " Error parsing arguments for " + name + " : Invalid JSON format " ;
logger - > error (
2025-03-26 19:28:02 +08:00
" 📝 Oops! The arguments for ` " + name + " ` don't make sense - invalid JSON "
2025-03-16 17:17:01 +08:00
) ;
return " Error: " + error_msg ;
} catch ( const std : : exception & e ) {
2025-03-26 19:28:02 +08:00
std : : string error_msg = " ⚠️ Tool ` " + name + " ` encountered a problem: " + std : : string ( e . what ( ) ) ;
2025-03-16 17:17:01 +08:00
logger - > error ( error_msg ) ;
return " Error: " + error_msg ;
}
}
// Handle special tool execution and state changes
void ToolCallAgent : : _handle_special_tool ( const std : : string & name , const ToolResult & result , const json & kwargs ) {
if ( ! _is_special_tool ( name ) ) {
return ;
}
if ( _should_finish_execution ( name , result , kwargs ) ) {
2025-03-26 19:28:02 +08:00
logger - > info ( " 🏁 Special tool ` " + name + " ` has completed the task! " ) ;
2025-03-16 17:17:01 +08:00
state = AgentState : : FINISHED ;
}
}
// Determine if tool execution should finish the agent
bool ToolCallAgent : : _should_finish_execution ( const std : : string & name , const ToolResult & result , const json & kwargs ) {
2025-03-16 22:56:03 +08:00
return true ; // Currently, all special tools (terminate) finish the agent
2025-03-16 17:17:01 +08:00
}
bool ToolCallAgent : : _is_special_tool ( const std : : string & name ) {
return special_tool_names . find ( name ) ! = special_tool_names . end ( ) ;
}
}