fbf04d90db
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.
99 lines
2.9 KiB
Dart
99 lines
2.9 KiB
Dart
import 'dart:convert';
|
|
import 'dart:math';
|
|
import 'package:crypto/crypto.dart';
|
|
import 'package:http/http.dart' as http;
|
|
import '../http_client.dart';
|
|
import '../models.dart';
|
|
import '../sse.dart';
|
|
import 'base_provider.dart';
|
|
|
|
class ChatGptOss extends BaseProvider {
|
|
@override
|
|
String get name => 'ChatGptOss';
|
|
|
|
@override
|
|
bool get working => true;
|
|
|
|
@override
|
|
List<String> get models => ['gpt-oss-120b', 'gpt-5-nano'];
|
|
|
|
@override
|
|
String get defaultModel => 'gpt-oss-120b';
|
|
|
|
String _generateFingerprint() {
|
|
final seed = '${DateTime.now().millisecondsSinceEpoch / 1000.0}-${Random().nextDouble()}';
|
|
return md5.convert(utf8.encode(seed)).toString();
|
|
}
|
|
|
|
@override
|
|
Stream<String> createAsyncGenerator(
|
|
String model,
|
|
List<Message> messages, {
|
|
bool stream = false,
|
|
}) async* {
|
|
final prompt = formatPrompt(messages);
|
|
final url = Uri.parse('https://chat-gpt-oss.com/api/message');
|
|
|
|
final payload = {
|
|
"conversation_id": null,
|
|
"model": model,
|
|
"content": prompt,
|
|
"reasoning_effort": "low",
|
|
};
|
|
|
|
final req = http.Request('POST', url);
|
|
req.headers.addAll({
|
|
"accept": "text/event-stream",
|
|
"accept-language": "en-US,en;q=0.9",
|
|
"cache-control": "no-cache",
|
|
"content-type": "application/json",
|
|
"origin": "https://chat-gpt-oss.com",
|
|
"pragma": "no-cache",
|
|
"referer": "https://chat-gpt-oss.com/",
|
|
"sec-ch-ua": '"Chromium";v="148", "Google Chrome";v="148", "Not/A)Brand";v="99"',
|
|
"sec-ch-ua-mobile": "?0",
|
|
"sec-ch-ua-platform": '"Linux"',
|
|
"sec-fetch-dest": "empty",
|
|
"sec-fetch-mode": "cors",
|
|
"sec-fetch-site": "same-origin",
|
|
"user-agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36",
|
|
"x-fingerprint": _generateFingerprint(),
|
|
});
|
|
|
|
req.body = jsonEncode(payload);
|
|
|
|
final response = await g4fHttp.send(req);
|
|
|
|
if (response.statusCode == 200) {
|
|
String? currentEvent;
|
|
await for (final line in splitSseLines(response.stream.transform(utf8.decoder))) {
|
|
final trimmed = line.trim();
|
|
if (trimmed.isEmpty) {
|
|
currentEvent = null;
|
|
continue;
|
|
}
|
|
if (trimmed.startsWith('event:')) {
|
|
currentEvent = trimmed.substring(6).trim();
|
|
if (currentEvent == 'summary') {
|
|
return;
|
|
}
|
|
continue;
|
|
}
|
|
if (trimmed.startsWith('data:') && currentEvent == 'message') {
|
|
final dataStr = trimmed.substring(5).trim();
|
|
if (dataStr.isEmpty) continue;
|
|
try {
|
|
final jsonChunk = jsonDecode(dataStr);
|
|
final content = jsonChunk['content'] as String?;
|
|
if (content != null && content.isNotEmpty) {
|
|
yield content;
|
|
}
|
|
} catch (_) {}
|
|
}
|
|
}
|
|
} else {
|
|
throw Exception('$name returned status ${response.statusCode}');
|
|
}
|
|
}
|
|
}
|