Files
999/lib/g4f/providers/chatgpt_oss.dart
T
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

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}');
}
}
}