Files
Alma Alexyz fbf04d90db 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.
2026-09-01 22:05:38 +05:00

94 lines
2.6 KiB
Dart

import 'dart:convert';
import 'package:crypto/crypto.dart';
import 'package:http/http.dart' as http;
import '../http_client.dart';
import '../http_util.dart';
import '../models.dart';
import '../sse.dart';
import 'base_provider.dart';
/// ChatGPT-Next-Web / Free2GPT clones (`/api/generate` + md5 sign).
class Free2Gpt extends BaseProvider {
@override
String get name => 'Free2Gpt';
@override
bool get working => true;
@override
List<String> get models => ['gpt-3.5-turbo', 'gpt-4o-mini', 'gemini'];
@override
String get defaultModel => 'gpt-3.5-turbo';
static const _hosts = [
'https://chat.free2gpt.xyz',
'https://chatz.free2gpt.xyz',
'https://gpt.aifree.site',
'https://chat.aifree.site',
'https://gemini.free2gpt.com',
];
@override
Stream<String> createAsyncGenerator(
String model,
List<Message> messages, {
bool stream = false,
}) async* {
Object? last;
for (final host in _hosts) {
try {
yield await _generate(host, messages);
return;
} catch (e) {
last = e;
}
}
throw Exception('Free2Gpt failed: $last');
}
Future<String> _generate(String host, List<Message> messages) async {
final lastUser = getLastUserMessage(messages);
final t = DateTime.now().millisecondsSinceEpoch;
final sign = md5.convert(utf8.encode('$t:$lastUser:undefined')).toString();
final payload = jsonEncode({
'messages': toOpenAiMessages(messages),
'time': t,
'pass': null,
'sign': sign,
});
final urls = [
'$host/api/generate',
'$host/api/openai/v1/chat/completions',
];
Object? last;
for (final url in urls) {
try {
final req = http.Request('POST', Uri.parse(url));
req.headers.addAll(browserHeaders(
origin: host,
referer: '$host/',
contentType: 'application/json',
));
req.body = payload;
final res = await g4fHttp.send(req).timeout(const Duration(seconds: 14));
if (res.statusCode != 200) {
last = 'HTTP ${res.statusCode}';
continue;
}
final raw = utf8.decode(await res.stream.toBytes().timeout(const Duration(seconds: 14)));
final openai = extractOpenAiMessage(raw);
if (openai != null && looksLikeModelOutput(openai)) return openai.trim();
final sse = extractAllSseContent(raw).join();
if (looksLikeModelOutput(sse)) return sse.trim();
if (looksLikeModelOutput(raw)) return raw.trim();
last = 'unusable';
} catch (e) {
last = e;
}
}
throw Exception('$host failed: $last');
}
}