run method

Future<AiAgentResult> run(
  1. List<AiChatMessage> messages, {
  2. required String model,
  3. double? temperature,
  4. int? maxTokens,
})

Runs the agent: streams a completion, executes any tool calls, feeds results back, and repeats until the model produces a final text response or maxIterations is hit.

Returns the final assistant text plus the full conversation.

Implementation

Future<AiAgentResult> run(
  List<AiChatMessage> messages, {
  required String model,
  double? temperature,
  int? maxTokens,
}) async {
  final conversation = [...messages];
  final fullText = StringBuffer();

  for (var iteration = 0; iteration < maxIterations; iteration++) {
    final textBuffer = StringBuffer();
    final toolCalls = <AiToolCallRequest>[];

    await for (final event in client.streamChat(
      conversation,
      model: model,
      temperature: temperature,
      maxTokens: maxTokens,
      extraBody: tools.isEmpty
          ? const {}
          : {
              'tools': [for (final t in tools) t.toRequestJson()],
            },
    )) {
      onEvent?.call(event);
      if (event is AiError) {
        return AiAgentResult(
          text: textBuffer.toString(),
          messages: conversation,
          iterations: iteration,
          error: event,
        );
      }
      if (event is AiTextDelta) {
        textBuffer.write(event.text);
      }
      if (event is AiToolCall) {
        toolCalls.add(
          AiToolCallRequest(
            id: event.id,
            name: event.name,
            argumentsJson: event.argumentsJson,
          ),
        );
      }
    }

    fullText.write(textBuffer.toString());

    // No tool calls → final response, we're done.
    if (toolCalls.isEmpty) {
      conversation.add(AiChatMessage.assistant(textBuffer.toString()));
      return AiAgentResult(
        text: textBuffer.toString(),
        messages: conversation,
        iterations: iteration,
      );
    }

    // Append the assistant's tool-call message (keeping any text the
    // model produced alongside the calls so the history stays faithful).
    conversation.add(
      AiChatMessage.assistantWithToolCalls(
        toolCalls,
        content: textBuffer.toString(),
      ),
    );

    // Execute each tool and append results.
    for (final call in toolCalls) {
      final tool = tools.where((t) => t.name == call.name).firstOrNull;
      final result = tool == null
          ? 'Error: unknown tool "${call.name}"'
          : await tool.runFromJson(call.argumentsJson);
      conversation.add(
        AiChatMessage.toolResult(toolCallId: call.id, content: result),
      );
    }
  }

  // Max iterations hit — return what we have.
  return AiAgentResult(
    text: fullText.toString(),
    messages: conversation,
    iterations: maxIterations,
    error: AiError('maxIterations ($maxIterations) reached'),
  );
}