sendStreamingChatRequest method

  1. @override
Stream<Map<String, dynamic>> sendStreamingChatRequest(
  1. List<Map<String, dynamic>> messages,
  2. List<Map<String, dynamic>>? tools, {
  3. double? temperature,
  4. int? maxCompletionTokens,
  5. double? topP,
  6. String? reasoningEffort,
  7. dynamic stop,
  8. CancellationToken? cancellationToken,
})
override

Sends a streaming chat request to the LLM, yielding chunks as they arrive.

Implementation

@override
Stream<Map<String, dynamic>> sendStreamingChatRequest(
  List<Map<String, dynamic>> messages,
  List<Map<String, dynamic>>? tools, {
  double? temperature,
  int? maxCompletionTokens,
  double? topP,
  String? reasoningEffort,
  dynamic stop,
  CancellationToken? cancellationToken,
}) async* {
  sdkLogger.info(
    'Sending streaming request to OpenRouter (model: $model)',
    tag: 'OPENROUTER',
  );

  final body = jsonEncode({
    'model': model,
    'messages': messages,
    'tools': tools,
    'tool_choice': 'auto',
    'stream': true,
    if ((temperature ?? this.temperature) != null)
      'temperature': temperature ?? this.temperature,
    if ((maxCompletionTokens ?? this.maxCompletionTokens) != null)
      'max_tokens': maxCompletionTokens ?? this.maxCompletionTokens,
    if ((topP ?? this.topP) != null) 'top_p': topP ?? this.topP,
    if ((reasoningEffort ?? this.reasoningEffort) != null)
      'reasoning_effort': reasoningEffort ?? this.reasoningEffort,
    if ((stop ?? this.stop) != null) 'stop': stop ?? this.stop,
  });

  final request = http.Request('POST', Uri.parse(_baseUrl));
  request.headers.addAll(_getHeaders());
  request.body = body;

  final response = await _httpClient.send(request);

  if (response.statusCode != 200) {
    final errorBody = await response.stream.bytesToString();
    throw VanturaApiException(
      'OpenRouter streaming failed',
      statusCode: response.statusCode,
      responseBody: errorBody,
    );
  }

  yield* response.stream
      .takeWhile((_) => !(cancellationToken?.isCancelled ?? false))
      .transform(utf8.decoder)
      .transform(const LineSplitter())
      .where((line) => line.trim().isNotEmpty)
      .map((line) {
        if (line.startsWith('data: ')) {
          final data = line.substring(6).trim();
          if (data == '[DONE]') return null;
          try {
            return jsonDecode(data) as Map<String, dynamic>;
          } catch (e) {
            return null;
          }
        }
        return null;
      })
      .where((json) => json != null)
      .cast<Map<String, dynamic>>();
}