streamChat method
Stream<Object>
streamChat(
- List<
AiChatMessage> messages, { - required String model,
- double? temperature,
- int? maxTokens,
- Map<
String, Object?> extraBody = const {},
override
Streams a chat completion as a sequence of typed events.
Implementation
@override
Stream<Object> streamChat(
List<AiChatMessage> messages, {
required String model,
double? temperature,
int? maxTokens,
Map<String, Object?> extraBody = const {},
}) async* {
// Anthropic takes the system prompt as a top-level parameter, not a
// message in the array.
final systemPrompt = messages
.where((m) => m.role == AiRole.system)
.map((m) => m.content)
.join('\n');
final chat = messages.where((m) => m.role != AiRole.system).toList();
final uri = Uri.parse('$baseUrl/messages');
final body = jsonEncode({
'model': model,
'max_tokens': maxTokens ?? 4096, // required by Anthropic
'messages': [for (final m in chat) m.toJson()],
'stream': true,
if (systemPrompt.isNotEmpty) 'system': systemPrompt,
?temperature: temperature,
...extraBody,
});
final request = http.Request('POST', uri)
..headers['Content-Type'] = 'application/json'
..headers['Accept'] = 'text/event-stream'
..headers['x-api-key'] = apiKey
..headers['anthropic-version'] = version
..body = body;
final response = await _client.send(request);
if (response.statusCode != 200) {
final errorBody = await response.stream.bytesToString();
yield AiError(
'Request failed with HTTP ${response.statusCode}: $errorBody',
statusCode: response.statusCode,
);
return;
}
final parser = SseParser();
final state = _AnthropicStreamState();
await for (final chunk in response.stream.transform(
const Utf8Decoder(allowMalformed: true),
)) {
for (final sseEvent in parser.addChunk(chunk)) {
for (final event in state.consume(sseEvent)) {
if (event is _DoneSignal) {
yield AiDone(
finishReason: event.finishReason ?? 'end_turn',
usage: AiUsage(
inputTokens: state.inputTokens,
outputTokens: state.outputTokens,
),
);
return;
}
yield event;
}
}
}
yield AiDone(
finishReason: 'end_turn',
usage: AiUsage(
inputTokens: state.inputTokens,
outputTokens: state.outputTokens,
),
);
}