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.
194 lines
10 KiB
Dart
194 lines
10 KiB
Dart
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);
|
||
}
|
||
}
|