Files
999/lib/services/ai_oracle_service.dart
T

197 lines
9.0 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 'dart:convert';
import 'package:http/http.dart' as http;
import '../models/history_entry_model.dart';
import '../models/word_model.dart';
class AiOracleService {
static final AiOracleService _instance = AiOracleService._internal();
factory AiOracleService() => _instance;
AiOracleService._internal();
/// 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 'Нет знаков для трактовки.';
// 1. Try Live Online Endpoints (Fast timeout)
try {
final prompt = _buildMultiWordPrompt(
question: question,
words: words,
);
final onlineResult = await _tryFetchOnlineAi(prompt);
if (onlineResult != null && onlineResult.trim().length > 30) {
return onlineResult.trim();
}
} catch (_) {}
// 2. High-Dimensional Deep Semantic Synthesizer (Instant & Deep)
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 = '''Ты — Сакральный Оракул 999. Ответь на вопрос вопрошающего: «$userMessage»
Контекст:
Вопрос: «${session.question ?? 'Поиск пути'}»
Выпавшие знаки:
$wordsNarrative
${newlyAddedWord != null ? "Дополнительный знак: #${newlyAddedWord.formattedSessionNumber} «${newlyAddedWord.word.toUpperCase()}» (${newlyAddedWord.symbolicMeaning})" : ""}
Дай мудрый, глубокий и прямой ответ (2-4 предложения).''';
try {
final onlineResult = await _tryFetchOnlineAi(prompt);
if (onlineResult != null && onlineResult.trim().length > 20) {
return onlineResult.trim();
}
} catch (_) {}
return _synthesizeChatReply(session, userMessage, newlyAddedWord);
}
Future<String?> _tryFetchOnlineAi(String prompt) async {
const workingModels = [
'gpt-4o-mini',
'gpt-4o',
'command-r',
'command-r7b',
'Yqcloud',
'HuggingSpace',
'WeWordle',
];
final endpoints = [
'http://localhost:8080/v1/chat/completions',
'http://127.0.0.1:8080/v1/chat/completions',
];
for (final ep in endpoints) {
for (final modelName in workingModels) {
try {
final res = await http.post(
Uri.parse(ep),
headers: {'Content-Type': 'application/json'},
body: jsonEncode({
'model': modelName,
'messages': [{'role': 'user', 'content': prompt}],
'temperature': 0.7,
}),
).timeout(const Duration(seconds: 10));
if (res.statusCode == 200) {
final data = jsonDecode(utf8.decode(res.bodyBytes));
final content = data['choices']?[0]?['message']?['content'] as String?;
if (content != null && content.trim().length > 15) {
return content.trim();
}
}
} catch (_) {}
}
}
return null;
}
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 '''Ты — Священный Оракул 999. Составь цельное, глубокое, психологически точное толкование выпавших знаков жребия.
$qStr
Выпавшие знаки:
$wordsStr
Требования:
- Свяжи выпавшие знаки в единую осмысленную историю-трансформацию.
- Укажи скрытую причину, ключевой вызов и вектор исхода.
- Ответ на русском языке (3-5 предложений), образный, сильный, без шаблонных фраз.''';
}
/// High-dimensional Deep Semantic Synthesizer
String _synthesizeDeepSacredNarrative(List<SessionWord> words, String question) {
final sb = StringBuffer();
final first = words.first;
final last = words.last;
final hasQuestion = question.trim().isNotEmpty;
// Introduction / Framing
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();
}
// 3 or more words
sb.write('разворачивается цепь из ${words.length} взаимосвязанных знаков. ');
sb.write('Исток ситуации заложен в знаке #${first.formattedSessionNumber} «${first.word.toUpperCase()}» — ${first.symbolicMeaning.toLowerCase()} ');
// Middle transitions
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 ');
}
// Resolution / Outcome
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), Оракул отвечает на ваш вопрос: суть ответа лежит в осознании скрытых взаимосвязей. Отпустите страх неопределенности и доверьтесь логике происходящих изменений.';
}
/// Legacy single word helper
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);
}
}