7f91366201
Wire cleromancy rolls and the once-per-UTC-day 000-999 word shuffle to RANDOM.ORG JSON-RPC, with a local CSPRNG fallback. Add settings for interpreter personas and a cleaner default oracle prompt. Replace proper names and bureaucratic filler in the 1000-word lexicon with sacred symbols.
205 lines
7.7 KiB
Dart
205 lines
7.7 KiB
Dart
import '../models/history_entry_model.dart';
|
|
import '../models/interpreter_persona.dart';
|
|
import '../models/word_model.dart';
|
|
import '../g4f/g4f.dart';
|
|
import 'settings_repository.dart';
|
|
|
|
class AiOracleService {
|
|
static final AiOracleService _instance = AiOracleService._internal();
|
|
factory AiOracleService() => _instance;
|
|
AiOracleService._internal();
|
|
|
|
final _client = G4FClient();
|
|
|
|
InterpreterPersona get persona => SettingsRepository().persona;
|
|
|
|
List<Message> buildInterpretationMessages({
|
|
required List<SessionWord> words,
|
|
required String question,
|
|
InterpreterPersona? persona,
|
|
}) {
|
|
final voice = persona ?? this.persona;
|
|
return [
|
|
Message(role: Role.system, content: voice.systemPrompt),
|
|
Message(
|
|
role: Role.user,
|
|
content: _buildMultiWordPrompt(
|
|
question: question,
|
|
words: words,
|
|
persona: voice,
|
|
),
|
|
),
|
|
];
|
|
}
|
|
|
|
/// 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 voice = persona;
|
|
final prompt = '''Вопрошающий говорит тебе: «$userMessage»
|
|
|
|
Контекст сеанса:
|
|
Вопрос: «${session.question ?? 'без конкретного вопроса'}»
|
|
Знаки:
|
|
$wordsNarrative
|
|
${newlyAddedWord != null ? "Только что вытянут уточняющий знак: #${newlyAddedWord.formattedSessionNumber} «${newlyAddedWord.word.toUpperCase()}» (${newlyAddedWord.symbolicMeaning})." : ""}
|
|
|
|
Ответь в своём голосе, 3–4 предложения. Привяжи ответ к знакам и вопросу. Без тумана. Один ясный вывод или шаг.''';
|
|
|
|
final messages = [
|
|
Message(role: Role.system, content: voice.systemPrompt),
|
|
];
|
|
|
|
// 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,
|
|
required InterpreterPersona persona,
|
|
}) {
|
|
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
|
|
|
|
${persona.userTask}''';
|
|
}
|
|
|
|
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()}». ');
|
|
sb.write('${first.symbolicMeaning} ');
|
|
sb.write('Практический шаг: держите фокус на этом и отсеките лишнее на ближайшее время.');
|
|
return _withPersonaPrefix(sb.toString());
|
|
}
|
|
|
|
if (words.length == 2) {
|
|
final second = words[1];
|
|
sb.write('два полюса — «${first.word.toUpperCase()}» и «${second.word.toUpperCase()}». ');
|
|
sb.write('${first.symbolicMeaning} ${second.symbolicMeaning} ');
|
|
sb.write('Шаг: не выбирайте войну между ними — направьте первое во второе.');
|
|
return _withPersonaPrefix(sb.toString());
|
|
}
|
|
|
|
sb.write('цепь из ${words.length} знаков. Начало — «${first.word.toUpperCase()}»: ${first.symbolicMeaning} ');
|
|
|
|
if (words.length > 2) {
|
|
final middleWords = words.sublist(1, words.length - 1);
|
|
final midNames = middleWords.map((w) => '«${w.word.toUpperCase()}»').join(', ');
|
|
sb.write('Переход через $midNames. ');
|
|
}
|
|
|
|
sb.write('Исход держит «${last.word.toUpperCase()}»: ${last.symbolicMeaning} ');
|
|
sb.write('Шаг: примите урок начала и сделайте одно действие в сторону исхода.');
|
|
|
|
return _withPersonaPrefix(sb.toString());
|
|
}
|
|
|
|
String _withPersonaPrefix(String body) {
|
|
switch (persona.id) {
|
|
case 'orc':
|
|
return 'Слушай. $body';
|
|
case 'techpriest':
|
|
return 'Диагностика. $body';
|
|
case 'analyst':
|
|
return 'Рабочая гипотеза. $body';
|
|
default:
|
|
return body;
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|