completeWithTools method

  1. @override
Future<ChatResponse> completeWithTools(
  1. List<ChatMessage> messages, {
  2. String? system,
  3. int? maxTokens,
  4. List<Tool> tools = const [],
})
override

Like ChatModel.complete, but the model may answer with ChatResponse.toolCalls instead of (or as well as) text.

Implementation

@override
Future<ChatResponse> completeWithTools(
  List<ChatMessage> messages, {
  String? system,
  int? maxTokens,
  List<Tool> tools = const [],
}) async {
  final json = await _http.postJson(
    joinUrl(baseUrl, 'chat/completions'),
    _auth(apiKey, headers),
    _body(messages, system, maxTokens, stream: false, tools: tools),
  );
  final choices = json['choices'];
  if (choices is! List || choices.isEmpty) {
    throw const LlmException('the response has no choices');
  }
  final choice = choices.first as Map;
  final message = choice['message'] as Map? ?? const {};
  final refusal = message['refusal'];
  if (refusal is String && refusal.isNotEmpty) {
    throw LlmRefusalException(refusal);
  }
  final calls = <ToolCall>[];
  for (final raw in (message['tool_calls'] as List? ?? const [])) {
    if (raw is! Map) continue;
    final function = raw['function'];
    if (function is! Map) continue;
    calls.add(
      ToolCall(
        id: '${raw['id']}',
        name: '${function['name']}',
        // Arguments arrive as a JSON *string* in this API.
        input: decodeToolInput(function['arguments']),
      ),
    );
  }
  return ChatResponse(
    message['content'] as String? ?? '',
    stopReason: choice['finish_reason'] as String?,
    model: json['model'] as String?,
    usage: _usage(json['usage']),
    toolCalls: calls,
  );
}