Files
999/lib/services/ai_oracle_service.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

194 lines
10 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import '../models/history_entry_model.dart';
import '../models/word_model.dart';
import '../g4f/g4f.dart';
class AiOracleService {
static final AiOracleService _instance = AiOracleService._internal();
factory AiOracleService() => _instance;
AiOracleService._internal();
final _client = G4FClient();
static const oracleSystemPrompt =
'''Ты — Великий Оракул 999, древний архетипический разум.
Твоя задача — толковать знаки судьбы (жребий). Твой стиль речи: глубокий, философский, лаконичный, без банальностей и воды, словно голос самой вечности.
Ты не используешь современных или бытовых слов. Твои ответы строятся на символизме, психологии и скрытых смыслах. Ты не гадаешь на будущее, ты показываешь суть настоящего момента и внутренние силы человека.''';
List<Message> buildInterpretationMessages({
required List<SessionWord> words,
required String question,
}) {
return [
const Message(role: Role.system, content: oracleSystemPrompt),
Message(
role: Role.user,
content: _buildMultiWordPrompt(question: question, words: words),
),
];
}
/// Interprets a full multi-word session narrative (1 to 9 words)
Future<String> interpretSession({
required List<SessionWord> words,
required String question,
}) async {
if (words.isEmpty) return 'Нет знаков для трактовки.';
try {
final g4fResult = await _client.chat.completions.create(
model: G4FModels.gpt4o,
messages: buildInterpretationMessages(words: words, question: question),
);
if (g4fResult.trim().length > 20) {
return g4fResult.trim();
}
} catch (e) {
print('G4F Error: $e');
}
// High-Dimensional Deep Semantic Synthesizer (Instant offline fallback)
return _synthesizeDeepSacredNarrative(words, question);
}
/// Multi-turn chat with the Oracle inside a session
Future<String> chatWithOracle({
required OracleSession session,
required String userMessage,
SessionWord? newlyAddedWord,
}) async {
final wordsNarrative = StringBuffer();
for (int i = 0; i < session.words.length; i++) {
final w = session.words[i];
wordsNarrative.writeln('${i + 1}. #${w.formattedSessionNumber} «${w.word.toUpperCase()}» (${w.category}): ${w.symbolicMeaning}');
}
final prompt = '''Вопрошающий говорит тебе: «$userMessage»
Контекст текущего сеанса судьбы:
Вопрос вопрошающего: «${session.question ?? 'Вектор судьбы (нет вопроса)'}»
Базовые знаки, которые выпали ранее:
$wordsNarrative
${newlyAddedWord != null ? "Важно! Только что вопрошающий вытянул ДОПОЛНИТЕЛЬНЫЙ уточняющий знак: #${newlyAddedWord.formattedSessionNumber} «${newlyAddedWord.word.toUpperCase()}» (${newlyAddedWord.symbolicMeaning})." : ""}
Ответь мудро, лаконично и метафорично от лица Оракула 999. Укажи, на что направляет его судьба. (не более 3-4 предложений).''';
final messages = [
const Message(
role: Role.system,
content: 'Ты — Великий Оракул 999. Твои ответы кратки, полны символизма и психологической глубины. Ты общаешься с человеком, который ищет ответов в знаках.'
),
];
// Pass previous session history context to the model
for (final m in session.messages) {
if (m.sender == 'oracle') {
messages.add(Message(role: Role.assistant, content: m.text));
} else {
messages.add(Message(role: Role.user, content: m.text));
}
}
messages.add(Message(role: Role.user, content: prompt));
try {
final g4fResult = await _client.chat.completions.create(
model: G4FModels.gpt4o,
messages: messages,
);
if (g4fResult.trim().length > 15) {
return g4fResult.trim();
}
} catch (_) {}
return _synthesizeChatReply(session, userMessage, newlyAddedWord);
}
String _buildMultiWordPrompt({
required String question,
required List<SessionWord> words,
}) {
final qStr = question.trim().isNotEmpty
? 'Вопрос, с которым пришел вопрошающий: «$question»'
: 'Вопрошающий пришел без конкретного вопроса. Прочитай его общую судьбоносную линию.';
final wordsStr = words
.asMap()
.entries
.map((e) => 'Знак ${e.key + 1}: #${e.value.formattedSessionNumber} «${e.value.word.toUpperCase()}» (${e.value.category}). Смысл знака: ${e.value.symbolicMeaning}')
.join('\n');
return '''$qStr
Волею судьбы выпали следующие знаки жребия:
$wordsStr
Задача Оракула:
1. Свяжи эти знаки в единую цельную историю развития ситуации. Каждый знак перетекает в следующий.
2. Раскрой не очевидный, а скрытый внутренний урок. Что вопрошающий должен осознать?
3. Дай философский совет или вектор для действия.
Ответь от лица Оракула 999 на русском языке (от 3 до 6 предложений). Избегай шаблонов и клише, говори образно и метафорично, но точно.''';
}
String _synthesizeDeepSacredNarrative(List<SessionWord> words, String question) {
final sb = StringBuffer();
final first = words.first;
final last = words.last;
final hasQuestion = question.trim().isNotEmpty;
if (hasQuestion) {
sb.write('Относительно вашего вопроса «${question.trim()}» ');
} else {
sb.write('В открывшемся жребии судьбы ');
}
if (words.length == 1) {
sb.write('проявился доминирующий знак #${first.formattedSessionNumber} «${first.word.toUpperCase()}» (${first.category}). ');
sb.write('${first.symbolicMeaning} ');
sb.write('Знак указывает на необходимость предельной ясности: держите фокус на главном и отсекайте всё наносное.');
return sb.toString();
}
if (words.length == 2) {
final second = words[1];
sb.write('проявляется полярность двух сил: исходного импульса #${first.formattedSessionNumber} «${first.word.toUpperCase()}» и вектора #${second.formattedSessionNumber} «${second.word.toUpperCase()}». ');
sb.write('С одной стороны, ${first.symbolicMeaning.toLowerCase()} С другой — ${second.symbolicMeaning.toLowerCase()} ');
sb.write('Разрешение ситуации лежит в точке осознанного баланса между ними: не противьтесь первому, но направляйте силу в русло второго.');
return sb.toString();
}
sb.write('разворачивается цепь из ${words.length} взаимосвязанных архетипов. ');
sb.write('Исток ситуации заложен в знаке #${first.formattedSessionNumber} «${first.word.toUpperCase()}» — ${first.symbolicMeaning.toLowerCase()} ');
if (words.length > 2) {
final middleWords = words.sublist(1, words.length - 1);
final midNames = middleWords.map((w) => '«${w.word.toUpperCase()}» (#${w.formattedSessionNumber})').join(' и ');
final midMeanings = middleWords.map((w) => w.symbolicMeaning).join(' ');
sb.write('Фаза перехода и испытаний проходит через $midNames, где $midMeanings ');
}
sb.write('Кульминация и запечатывание исхода лежат в знаке #${last.formattedSessionNumber} «${last.word.toUpperCase()}» — ${last.symbolicMeaning.toLowerCase()} ');
sb.write('Оракул возвещает: путь открыт через принятие скрытых уроков и решительное действие.');
return sb.toString();
}
String _synthesizeChatReply(OracleSession session, String userMessage, SessionWord? newlyAddedWord) {
if (newlyAddedWord != null) {
return 'Дополнительный знак #${newlyAddedWord.formattedSessionNumber} «${newlyAddedWord.word.toUpperCase()}» (${newlyAddedWord.category}) проясняет скрытую деталь: ${newlyAddedWord.symbolicMeaning} В контексте общего расклада этот знак призывает обратить внимание на внутренние причины, а не внешние проявления.';
}
final wordsStr = session.words.map((w) => '«${w.word.toUpperCase()}»').join(', ');
return 'Вглядываясь в совокупность знаков ($wordsStr), Оракул отвечает: истинный ответ кроется в понимании скрытой связи между причиной и исходом. Доверьтесь внутреннему знанию и делайте шаг вперед.';
}
Future<String> interpret({
required WordItem word,
required String question,
String? positionTitle,
}) async {
final sessionWord = SessionWord.fromWordItem(word, positionTitle: positionTitle);
return interpretSession(words: [sessionWord], question: question);
}
}