feat: embed a Flutter G4F client with Cronet, cookie jar, and origin WebView
Port the Oracle off the Python g4f container onto on-device providers: Chromium HTTP via Cronet, a shared cookie session, IterList retry, hosted OpenAI-compat aggregators, and same-origin WebView fallbacks instead of CORS fetch.
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
import 'http_client.dart';
|
||||
import 'http_util.dart';
|
||||
import 'models.dart';
|
||||
import 'providers/base_provider.dart';
|
||||
|
||||
/// Port of g4f `IterListProvider`: try each provider until one returns content.
|
||||
class IterListProvider {
|
||||
IterListProvider(this.providers);
|
||||
|
||||
final List<BaseProvider> providers;
|
||||
|
||||
Future<String> create({
|
||||
required List<Message> messages,
|
||||
String? model,
|
||||
}) async {
|
||||
final exceptions = <String, Object>{};
|
||||
final ordered = [
|
||||
...providers.where((p) => p.working && !p.isWebView),
|
||||
...providers.where((p) => p.working && p.isWebView),
|
||||
];
|
||||
|
||||
for (final provider in ordered) {
|
||||
final alias = provider.resolveModel(model);
|
||||
try {
|
||||
await warmupOrigin(provider.url);
|
||||
final result = await provider.complete(alias, messages);
|
||||
if (looksLikeModelOutput(result)) {
|
||||
return result.trim();
|
||||
}
|
||||
exceptions[provider.name] = 'unusable text (${result.trim().length} chars)';
|
||||
} catch (e) {
|
||||
exceptions[provider.name] = e;
|
||||
print('Provider ${provider.name} failed: $e');
|
||||
}
|
||||
}
|
||||
|
||||
if (exceptions.isEmpty) {
|
||||
throw Exception('No G4F providers available.');
|
||||
}
|
||||
if (exceptions.length == 1) {
|
||||
throw Exception('${exceptions.keys.first}: ${exceptions.values.first}');
|
||||
}
|
||||
throw Exception(
|
||||
'RetryProvider failed:\n${exceptions.entries.map((e) => '${e.key}: ${e.value}').join('\n')}',
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user