rewrite method
Asks the model to rewrite text according to instruction.
Plain text in, plain text out — no tool call, and nothing is executed as a result. Used to polish a generated changelog, where the model's job is editorial rather than deciding what the CLI does.
Throws AiException on transport or protocol failure.
Implementation
@override
Future<String> rewrite({
required String instruction,
required String text,
}) async {
final body = await post(
'${config.baseUrl}/chat/completions',
{
'model': config.model,
'max_tokens': config.maxTokens,
'messages': [
{'role': 'system', 'content': instruction},
{'role': 'user', 'content': text},
],
},
{
'Content-Type': 'application/json',
if (config.hasApiKey) 'Authorization': 'Bearer ${config.apiKey}',
},
);
final choices = body['choices'];
if (choices is! List || choices.isEmpty) {
throw AiException('$name returned no choices');
}
// Shape first: reading a field off a non-map choice would surface as a raw
// Dart type error rather than "this endpoint sent something unexpected".
final choice = choices.first;
if (choice is! Map) {
throw AiException('$name returned a malformed choice');
}
if (choice['finish_reason'] == 'length') {
throw AiException(
'the reply was cut off by the token limit — raise `max-tokens` '
'(currently ${config.maxTokens}) and try again',
);
}
final message = choice['message'];
if (message is! Map) {
throw AiException('$name returned a malformed choice');
}
final content = message['content']?.toString().trim() ?? '';
if (content.isEmpty) throw AiException('$name returned nothing to use');
return content;
}