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,74 @@
|
||||
import 'dart:convert';
|
||||
import '../http_client.dart';
|
||||
import '../http_util.dart';
|
||||
import '../models.dart';
|
||||
import '../sse.dart';
|
||||
import 'base_provider.dart';
|
||||
|
||||
/// Port of g4f.Provider.You (default youchat mode, no login).
|
||||
class YouChat extends BaseProvider {
|
||||
@override
|
||||
String get name => 'You';
|
||||
|
||||
@override
|
||||
bool get working => true;
|
||||
|
||||
@override
|
||||
List<String> get models => ['gpt-3.5-turbo', 'gpt-4o'];
|
||||
|
||||
@override
|
||||
String get defaultModel => 'gpt-3.5-turbo';
|
||||
|
||||
@override
|
||||
Stream<String> createAsyncGenerator(
|
||||
String model,
|
||||
List<Message> messages, {
|
||||
bool stream = false,
|
||||
}) async* {
|
||||
final res = await g4fHttp
|
||||
.post(
|
||||
Uri.parse('https://you.com/api/streamingSearch'),
|
||||
headers: browserHeaders(
|
||||
origin: 'https://you.com',
|
||||
referer: 'https://you.com/search?fromSearchBar=true&tbm=youchat',
|
||||
accept: 'text/event-stream',
|
||||
),
|
||||
body: {
|
||||
'q': formatPrompt(messages),
|
||||
'domain': 'youchat',
|
||||
'selectedChatMode': 'default',
|
||||
'chat': '[]',
|
||||
},
|
||||
)
|
||||
.timeout(requestTimeout);
|
||||
if (res.statusCode != 200) {
|
||||
throw Exception('You ${res.statusCode}');
|
||||
}
|
||||
final raw = res.body;
|
||||
final buf = StringBuffer();
|
||||
String? event;
|
||||
for (final line in raw.split(RegExp(r'\r?\n'))) {
|
||||
if (line.startsWith('event:')) {
|
||||
event = line.substring(6).trim();
|
||||
} else if (line.startsWith('data:')) {
|
||||
final dataStr = line.substring(5).trim();
|
||||
try {
|
||||
final data = jsonDecode(dataStr);
|
||||
if (data is Map) {
|
||||
if (event == 'youChatToken' && data['youChatToken'] is String) {
|
||||
buf.write(data['youChatToken']);
|
||||
} else if (event == 'youChatUpdate' && data['t'] is String) {
|
||||
buf.write(data['t']);
|
||||
}
|
||||
}
|
||||
} catch (_) {}
|
||||
}
|
||||
}
|
||||
var text = buf.toString().trim();
|
||||
if (!looksLikeModelOutput(text)) {
|
||||
text = extractAllSseContent(raw).join().trim();
|
||||
}
|
||||
if (!looksLikeModelOutput(text)) throw Exception('You empty');
|
||||
yield text;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user