complete method
Sends request to the LLM and returns its response.
Implementation
@override
Future<LLMResponse> complete(LLMRequest request) async {
final url = Uri.parse('$baseUrl/chat/completions');
final body = jsonEncode({
'model': request.model ?? model,
'messages': request.messages.map((m) => m.toJson()).toList(),
'max_tokens': request.maxTokens,
'temperature': request.temperature,
if (request.stopSequences.isNotEmpty) 'stop': request.stopSequences,
});
final headers = <String, String>{
'Content-Type': 'application/json',
'Authorization': 'Bearer $apiKey',
if (organizationId != null) 'OpenAI-Organization': organizationId!,
};
_logger.debug('POST $url (model: ${request.model ?? model})');
try {
final response = await _client
.post(url, headers: headers, body: body)
.timeout(const Duration(seconds: 60));
if (response.statusCode != 200) {
throw LLMProviderException(
'HTTP ${response.statusCode}: ${response.body}',
statusCode: response.statusCode,
);
}
final json = jsonDecode(response.body) as Map<String, dynamic>;
final choices = json['choices'] as List<dynamic>;
if (choices.isEmpty) {
throw const LLMProviderException('No choices returned by LLM');
}
final choice = choices.first as Map<String, dynamic>;
final message = choice['message'] as Map<String, dynamic>;
final text = message['content'] as String? ?? '';
final usage = json['usage'] as Map<String, dynamic>? ?? {};
return LLMResponse(
text: text,
promptTokens: (usage['prompt_tokens'] as int?) ?? 0,
completionTokens: (usage['completion_tokens'] as int?) ?? 0,
model: json['model'] as String? ?? model,
finishReason: choice['finish_reason'] as String?,
);
} on LLMProviderException {
rethrow;
} catch (e, st) {
_logger.error('LLM request failed', e, st);
throw LLMProviderException('Network error: $e');
}
}