import '../models.dart'; /// Base abstract class for all G4F providers. /// Mirrors `AsyncGeneratorProvider` in xtekky/gpt4free. abstract class BaseProvider { /// User-friendly name of the provider String get name; String get label => name; String get url => ''; /// Whether the provider is currently believed to be working bool get working; /// List of supported models by this provider List get models; /// Optional fallback default model if none specified String get defaultModel; /// Whether this provider needs a hidden WebView (slow, device-only). bool get isWebView => false; /// Per-provider deadline. Python g4f defaults to ~120s; we keep a minute. Duration get requestTimeout => const Duration(seconds: 60); /// g4f `model_aliases` (e.g. gpt-4o → provider default). Map get modelAliases => const {}; String resolveModel(String? model) { if (model == null || model.isEmpty) return defaultModel; if (models.contains(model)) return model; final alias = modelAliases[model]; if (alias != null) return alias; return defaultModel; } /// Whether the provider can emit incremental tokens. bool get supportsStream => false; /// Full reply. The Oracle never streams — it waits for a complete text. Future complete(String model, List messages) async { final buf = StringBuffer(); await for (final chunk in createAsyncGenerator( model, messages, stream: false, ).timeout(requestTimeout)) { buf.write(chunk); } return buf.toString(); } /// Convert chat history to OpenAI-style JSON maps. List> toOpenAiMessages(List messages) { return [ for (final m in messages) {'role': m.role.name, 'content': m.content}, ]; } /// Helper to get last user message String getLastUserMessage(List messages) { for (var i = messages.length - 1; i >= 0; i--) { if (messages[i].role == Role.user) { return messages[i].content; } } return ''; } /// Helper to extract system prompt if supported by the provider String extractSystemMessage(List messages) { final sys = messages.where((m) => m.role == Role.system); if (sys.isNotEmpty) { return sys.map((m) => m.content).join('\n'); } return ''; } /// Helper to format a full prompt string (for providers without native chat context) String formatPrompt(List messages, {bool includeSystem = true}) { final sb = StringBuffer(); for (final m in messages) { if (m.role == Role.system) { if (includeSystem) sb.writeln('System: ${m.content}'); } else if (m.role == Role.user) { sb.writeln('User: ${m.content}'); } else { sb.writeln('Assistant: ${m.content}'); } } return sb.toString(); } /// Main interface to execute a chat completion synchronously/asynchronously. /// Streams chunked output if `stream` is supported. Stream createAsyncGenerator( String model, List messages, { bool stream = false, }); }