execute method

Future<AIResponse> execute({
  1. required Conversation conversation,
  2. required List<AIMessage> contextMessages,
  3. List<AITool>? tools,
  4. int depth = 0,
})

Processes a conversation, executing tool calls recursively until done.

conversation The active conversation with messages. tools Available tools the AI can call. contextMessages Pre-trimmed messages to send to the provider. Returns the final AIResponse (one without tool calls).

Implementation

Future<AIResponse> execute({
  required Conversation conversation,
  required List<AIMessage> contextMessages,
  List<AITool>? tools,
  int depth = 0,
}) async {
  if (depth >= maxIterations) {
    throw StateError(
      'Tool execution loop exceeded maximum depth of $maxIterations iterations. '
      'The model may be stuck in an infinite tool calling cycle.',
    );
  }

  final response = await provider.complete(contextMessages, tools: tools);

  if (response.toolCalls != null && response.toolCalls!.isNotEmpty) {
    // Record the assistant's tool request
    conversation.addMessage(AIMessage.assistant(
      response.content,
      toolCalls: response.toolCalls,
    ));

    // Execute each requested tool
    for (final tc in response.toolCalls!) {
      final tool = _findToolByName(tools, tc.name);

      String result;
      if (tool != null && tool.execute != null) {
        try {
          result = await tool.execute!(tc.arguments);
        } catch (e) {
          result = 'Error executing tool: $e';
        }
      } else {
        result =
            'Error: Tool ${tc.name} not found or has no execute function.';
      }

      conversation.addMessage(AIMessage.toolResult(tc.id, result));
    }

    // Recurse with updated context (caller should re-trim if needed)
    final updatedContext = conversation.activeMessages;
    return execute(
      conversation: conversation,
      contextMessages: updatedContext,
      tools: tools,
      depth: depth + 1,
    );
  }

  // No tool calls — final response
  conversation.addMessage(AIMessage.assistant(response.content));
  return response;
}