import 'dart:async'; import 'dart:convert'; import 'package:http/http.dart' as http; import '../models/word_model.dart'; class AiOracleService { static final AiOracleService _instance = AiOracleService._internal(); factory AiOracleService() => _instance; AiOracleService._internal(); // Configurable local or remote G4F server URL String g4fServerUrl = 'http://localhost:1337/v1/chat/completions'; /// Generates an esoteric, archetypal interpretation weaving the user's question, the drawn word, its session number, and its symbolic meaning. Future interpret({ required WordItem word, required String question, String? spreadType, String? positionTitle, }) async { final cleanQuestion = question.trim().isEmpty ? 'Общий совет и скрытые тенденции момента' : question.trim(); final systemPrompt = 'Ты — мудрый и глубокий Оракул 999 Сакральных Символов. ' 'Твоя задача — связать выпавший сакральный знак, его номер и архетипический смысл с конкретным вопросом вопрошающего. ' 'Дай глубокий, связный, образный и практичный ответ на русском языке. ' 'Структурируй толкование: ' '1) Суть послания через призму вопроса. ' '2) Скрытый вызов / предупреждение. ' '3) Практический духовный совет / ключ к действию. ' 'Пиши емко, выразительно, без лишней «воды», на 2-4 абзаца.'; final userPrompt = 'Вопрос вопрошающего: «$cleanQuestion»\n' 'Выпавший знак: #${word.formattedSessionNumber} — «${word.word.toUpperCase()}» (${word.partOfSpeech})\n' '${positionTitle != null ? "Позиция в раскладе: $positionTitle\n" : ""}' 'Базовое символическое значение знака: ${word.description}\n\n' 'Сотки глубокую трактовку этого знака именно для заданного вопроса.'; // Try Provider 1: Local / Configured G4F Server try { final g4fResponse = await _callOpenAiCompatible( url: g4fServerUrl, systemPrompt: systemPrompt, userPrompt: userPrompt, timeout: const Duration(seconds: 4), ); if (g4fResponse != null && g4fResponse.isNotEmpty) { return g4fResponse; } } catch (_) {} // Try Provider 2: Pollinations AI OpenAI-compatible Gateway (Free GPT-4o-mini tier) try { final pollResponse = await _callOpenAiCompatible( url: 'https://text.pollinations.ai/openai/chat/completions', systemPrompt: systemPrompt, userPrompt: userPrompt, timeout: const Duration(seconds: 10), ); if (pollResponse != null && pollResponse.isNotEmpty) { return pollResponse; } } catch (_) {} // Try Provider 3: Pollinations AI Raw Text Gateway (GET / POST) try { final rawResponse = await _callPollinationsRaw( systemPrompt: systemPrompt, userPrompt: userPrompt, timeout: const Duration(seconds: 8), ); if (rawResponse != null && rawResponse.isNotEmpty) { return rawResponse; } } catch (_) {} // Provider 4 (Fallback): Offline Sacred Synthesis Engine return _generateOfflineInterpretation( word: word, question: cleanQuestion, positionTitle: positionTitle, ); } /// Helper for OpenAI / G4F compatible JSON completions Future _callOpenAiCompatible({ required String url, required String systemPrompt, required String userPrompt, required Duration timeout, }) async { final response = await http .post( Uri.parse(url), headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36', }, body: jsonEncode({ 'model': 'gpt-4o-mini', 'messages': [ {'role': 'system', 'content': systemPrompt}, {'role': 'user', 'content': userPrompt}, ], 'temperature': 0.7, 'max_tokens': 700, }), ) .timeout(timeout); if (response.statusCode >= 200 && response.statusCode < 300) { final data = jsonDecode(utf8.decode(response.bodyBytes)); if (data is Map && data.containsKey('choices')) { final choices = data['choices'] as List; if (choices.isNotEmpty) { final first = choices.first; if (first is Map && first.containsKey('message')) { final content = first['message']['content'] as String?; if (content != null && content.trim().isNotEmpty) { return content.trim(); } } } } } return null; } /// Helper for Pollinations Raw text generation Future _callPollinationsRaw({ required String systemPrompt, required String userPrompt, required Duration timeout, }) async { final combined = '$systemPrompt\n\n$userPrompt'; final uri = Uri.parse('https://text.pollinations.ai/').replace( queryParameters: { 'model': 'openai', 'json': 'false', 'seed': DateTime.now().millisecondsSinceEpoch.toString(), }, ); final response = await http .post( uri, headers: { 'Content-Type': 'text/plain; charset=utf-8', 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64)', }, body: utf8.encode(combined), ) .timeout(timeout); if (response.statusCode >= 200 && response.statusCode < 300) { final text = utf8.decode(response.bodyBytes).trim(); if (text.isNotEmpty && !text.startsWith('Error')) { return text; } } return null; } /// Offline fallback generator in case of network unavailability String _generateOfflineInterpretation({ required WordItem word, required String question, String? positionTitle, }) { final pos = word.partOfSpeech.toLowerCase(); String focus = 'ключевую сферу кристаллизации'; if (pos.contains('глагол')) { focus = 'необходимый вектор действия и трансформации'; } else if (pos.contains('прилаг')) { focus = 'вибрационное качество и внутреннее состояние'; } else if (pos.contains('нареч')) { focus = 'модус и темп развертывания судьбы'; } return '🔮 Послание знака #${word.formattedSessionNumber} «${word.word.toUpperCase()}» на ваш запрос «$question»:\n\n' '1. Смысловой узел: Символ указывает на $focus в текущей ситуации. ${word.description}\n\n' '2. Внутренний вызов: Обратите внимание на то, как энергия слова «${word.word}» соотносится с вашими скрытыми ожиданиями. ' 'Не пытайтесь форсировать события вопреки естественному ритму.\n\n' '3. Ключ к действию: Примите качества этого знака как ориентир для принятия решения в ближайшие дни.'; } }