sendChatRequest method
Future<Map<String, dynamic> >
sendChatRequest(
- List<
Map< messages,String, dynamic> > - List<
Map< ? tools, {String, dynamic> > - double? temperature,
- int? maxCompletionTokens,
- double? topP,
- bool? stream,
- String? reasoningEffort,
- dynamic stop,
- CancellationToken? cancellationToken,
override
Sends a single, complete chat request to the LLM.
Implementation
@override
Future<Map<String, dynamic>> sendChatRequest(
List<Map<String, dynamic>> messages,
List<Map<String, dynamic>>? tools, {
double? temperature,
int? maxCompletionTokens,
double? topP,
bool? stream,
String? reasoningEffort,
dynamic stop,
CancellationToken? cancellationToken,
}) async {
sdkLogger.info(
'Sending chat request to Gemini (model: $model)',
tag: 'API',
extra: {
'model': model,
'message_count': messages.length,
'has_tools': tools != null && tools.isNotEmpty,
},
);
final geminiData = _convertToGeminiFormat(messages, tools);
final generationConfig = {
'maxOutputTokens': maxCompletionTokens ?? maxTokens,
if ((temperature ?? this.temperature) != null)
'temperature': temperature ?? this.temperature,
if (topP != null) 'topP': topP,
if (stop != null) 'stopSequences': stop is List ? stop : [stop],
};
final bodyMap = {
if (geminiData['systemInstruction'] != null)
'systemInstruction': geminiData['systemInstruction'],
'contents': geminiData['contents'],
if (geminiData['tools'] != null) 'tools': geminiData['tools'],
'generationConfig': generationConfig,
};
const int maxRetries = 3;
const Duration baseDelay = Duration(seconds: 1);
for (int attempt = 1; attempt <= maxRetries; attempt++) {
if (cancellationToken?.isCancelled == true) {
throw VanturaCancellationException();
}
final url = '$baseUrl/models/$model:generateContent?key=$apiKey';
try {
final stopwatch = Stopwatch()..start();
final response = await _httpClient.post(
Uri.parse(url),
headers: _headers,
body: jsonEncode(bodyMap),
);
stopwatch.stop();
sdkLogger.logPerformance(
'Gemini API request ($model)',
stopwatch.elapsed,
context: {'status_code': response.statusCode},
);
if (response.statusCode == 200) {
return _convertFromGeminiResponse(jsonDecode(response.body));
} else if (response.statusCode == 429 || response.statusCode >= 500) {
if (attempt == maxRetries) {
throw VanturaApiException(
'Gemini API error after $maxRetries attempts',
statusCode: response.statusCode,
responseBody: response.body,
);
}
final retrySeconds = attempt * 2;
sdkLogger.warning(
'Gemini API hit ${response.statusCode}. Retrying in ${retrySeconds}s... (Attempt $attempt/$maxRetries)',
tag: 'API',
);
if (onRetry != null) {
onRetry!(
attempt,
Duration(seconds: retrySeconds),
'HTTP ${response.statusCode}',
);
}
await Future.delayed(Duration(seconds: retrySeconds));
continue;
} else {
throw VanturaApiException(
'Gemini API error',
statusCode: response.statusCode,
responseBody: response.body,
);
}
} on http.ClientException catch (_) {
if (attempt == maxRetries) rethrow;
await Future.delayed(baseDelay * attempt);
}
}
throw Exception('Unexpected error in Gemini request');
}