import 'dart:convert'; import 'package:http/http.dart' as http; import '../http_client.dart'; import '../http_util.dart'; import '../models.dart'; import 'base_provider.dart'; /// Port of g4f HuggingSpace / CohereForAI C4AI Command (HF Space). class CohereSpace extends BaseProvider { @override String get name => 'CohereSpace'; @override bool get working => true; @override List get models => [ 'command-a-03-2025', 'command-r-plus-08-2024', 'command-r-08-2024', ]; @override String get defaultModel => 'command-a-03-2025'; static const _url = 'https://coherelabs-c4ai-command.hf.space'; @override Stream createAsyncGenerator( String model, List messages, { bool stream = false, }) async* { final useModel = models.contains(model) ? model : defaultModel; final headers = browserHeaders( origin: _url, referer: '$_url/', contentType: 'application/json', ); final conv = await g4fHttp .post( Uri.parse('$_url/conversation'), headers: headers, body: jsonEncode({ 'model': useModel, 'preprompt': extractSystemMessage(messages), }), ) .timeout(const Duration(seconds: 15)); if (conv.statusCode != 200) { throw Exception('CohereSpace conv ${conv.statusCode}'); } final convJson = jsonDecode(conv.body); final convId = (convJson is Map ? (convJson['conversationId'] ?? convJson['id']) : null) ?.toString(); if (convId == null) throw Exception('CohereSpace no conversation'); final cookie = cookieHeaderFromSetCookie(conv.headers['set-cookie']); final req = http.MultipartRequest('POST', Uri.parse('$_url/conversation/$convId')); req.headers.addAll({ ...browserHeaders(origin: _url, referer: '$_url/'), if (cookie != null) 'Cookie': cookie, }); req.fields['data'] = jsonEncode({ 'inputs': formatPrompt(messages.where((m) => m.role != Role.system).toList()), 'id': 0, 'is_retry': false, 'is_continue': false, 'web_search': false, 'tools': [], }); final res = await g4fHttp.send(req).timeout(requestTimeout); if (res.statusCode != 200) { throw Exception('CohereSpace chat ${res.statusCode}'); } final raw = utf8.decode(await res.stream.toBytes().timeout(requestTimeout)); final buf = StringBuffer(); for (final line in raw.split(RegExp(r'\r?\n'))) { if (line.trim().isEmpty) continue; try { final data = jsonDecode(line); if (data is Map) { if (data['type'] == 'stream' && data['token'] is String) { buf.write((data['token'] as String).replaceAll('\u0000', '')); } else if (data['type'] == 'finalAnswer' && data['text'] is String) { if (buf.isEmpty) buf.write(data['text']); } } } catch (_) {} } final text = buf.toString().trim(); if (!looksLikeModelOutput(text)) throw Exception('CohereSpace empty'); yield text; } }