import 'dotenv/config'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import cors from 'cors'; import express from 'express'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const app = express(); app.disable('x-powered-by'); const PORT = parseInt(process.env.PORT, 10) || 3000; // Default placeholder API key. Set OPENROUTER_API_KEY in your environment or .env file. const REPO_API_KEY = 'your-openrouter-api-key'; const OPENROUTER_API_KEY = process.env.OPENROUTER_API_KEY || REPO_API_KEY; const JEV_DECISIONS_URL = 'https://openrouter.ai/api/alpha/decisions'; const MODEL = 'typesafe/jev-1.13'; if (!process.env.OPENROUTER_API_KEY || process.env.OPENROUTER_API_KEY === REPO_API_KEY) { console.warn('[jev-8ball] OPENROUTER_API_KEY is not set or using default placeholder. Please configure your OpenRouter API key.'); } app.use(cors()); app.use(express.json()); app.use(express.static(path.join(__dirname, 'public'))); // Classic Magic 8-Ball aphorisms dictionary const APHORISMS = { certain: { text: "It is certain.", sentiment: "affirmative" }, decidedly: { text: "It is decidedly so.", sentiment: "affirmative" }, without_doubt: { text: "Without a doubt.", sentiment: "affirmative" }, yes_definitely: { text: "Yes definitely.", sentiment: "affirmative" }, rely: { text: "You may rely on it.", sentiment: "affirmative" }, as_i_see_it: { text: "As I see it, yes.", sentiment: "affirmative" }, most_likely: { text: "Most likely.", sentiment: "affirmative" }, outlook_good: { text: "Outlook good.", sentiment: "affirmative" }, yes: { text: "Yes.", sentiment: "affirmative" }, signs_yes: { text: "Signs point to yes.", sentiment: "affirmative" }, reply_hazy: { text: "Reply hazy, try again.", sentiment: "neutral" }, ask_again: { text: "Ask again later.", sentiment: "neutral" }, better_not: { text: "Better not tell you now.", sentiment: "neutral" }, cannot_predict: { text: "Cannot predict now.", sentiment: "neutral" }, concentrate: { text: "Concentrate and ask again.", sentiment: "neutral" }, dont_count: { text: "Don't count on it.", sentiment: "negative" }, my_reply_no: { text: "My reply is no.", sentiment: "negative" }, sources_no: { text: "My sources say no.", sentiment: "negative" }, outlook_bad: { text: "Outlook not so good.", sentiment: "negative" }, very_doubtful: { text: "Very doubtful.", sentiment: "negative" } }; // GET /api/status app.get('/api/status', (req, res) => { res.json({ status: 'online', model: MODEL, engine: 'TypeSafe Jev (Non-autoregressive System One)', hasKey: Boolean(OPENROUTER_API_KEY), keySource: process.env.OPENROUTER_API_KEY ? 'env' : 'repo' }); }); // POST /api/ask app.post('/api/ask', async (req, res) => { if (!OPENROUTER_API_KEY) { return res.status(503).json({ error: 'Oracle is not configured. Set OPENROUTER_API_KEY.' }); } const { question } = req.body; if (!question || typeof question !== 'string' || !question.trim()) { return res.status(400).json({ error: 'Question is required.' }); } const cleanQuestion = question.trim().slice(0, 300); const startTime = performance.now(); try { const payload = { model: MODEL, state: { question: cleanQuestion, directive: "You are the definitive Magic 8-Ball oracle. Every query demands a bold, prophetic, decisive verdict (affirmative or negative). You must actively choose a clear side. Do NOT default to hazy, ambiguous, or neutral answers unless the user's question is completely incomprehensible gibberish or an impossible paradox." }, questions: { favorable: { type: "noul", instructions: "Determine whether the fortune leans affirmative/favorable (1.0) or negative/adverse (0.0). Make a firm choice and avoid sitting in the ambiguous 0.45 - 0.55 zone.", criteria: { true: "The fortune favors yes, affirmative, or an auspicious outcome.", false: "The fortune favors no, negative, or an inauspicious outcome." } }, sentiment: { type: "choice", instructions: "Declare the oracle's verdict as either affirmative or negative. Reserve neutral strictly for non-questions or unparseable gibberish.", criteria: { affirmative: "Auspicious, affirmative, or confirming prophecy.", negative: "Inauspicious, negative, or adverse prophecy.", neutral: "Strictly unparseable gibberish or logical paradox." } }, aphorism: { type: "choice", instructions: "Select the authentic Magic 8-Ball prophecy that best fits the question.", criteria: { certain: "It is certain.", yes_definitely: "Yes definitely.", signs_yes: "Signs point to yes.", outlook_good: "Outlook good.", most_likely: "Most likely.", my_reply_no: "My reply is no.", very_doubtful: "Very doubtful.", outlook_bad: "Outlook not so good.", dont_count: "Don't count on it.", reply_hazy: "Reply hazy, try again (strictly reserved for total gibberish)." } } } }; const response = await fetch(JEV_DECISIONS_URL, { method: 'POST', headers: { 'Authorization': `Bearer ${OPENROUTER_API_KEY}`, 'Content-Type': 'application/json', 'HTTP-Referer': 'https://github.com/oiupoyt/jev-8ball', 'X-Title': 'Jev 8-Ball Oracle (Dev)' }, body: JSON.stringify(payload) }); const latency = Math.round(performance.now() - startTime); if (!response.ok) { const errText = await response.text(); console.error('OpenRouter Decision Error:', response.status, errText); return res.status(response.status).json({ error: 'OpenRouter decision error', details: errText, fallback: fallbackDecision(cleanQuestion) }); } const data = await response.json(); const answers = data.answers || {}; const favorableNoul = answers.favorable?.noul ?? 0.5; let sentimentChoice = answers.sentiment?.choice; if (!sentimentChoice || sentimentChoice === "neutral") { sentimentChoice = favorableNoul >= 0.5 ? "affirmative" : "negative"; } let aphorismKey = answers.aphorism?.choice; // Prevent unwanted hazy response if noul shows conviction if (aphorismKey === "reply_hazy" || aphorismKey === "cannot_predict") { if (favorableNoul >= 0.5) { aphorismKey = favorableNoul > 0.75 ? "certain" : "signs_yes"; } else { aphorismKey = favorableNoul < 0.3 ? "my_reply_no" : "outlook_bad"; } } let answerText = APHORISMS[aphorismKey]?.text; if (!answerText) { if (sentimentChoice === 'affirmative') { answerText = favorableNoul > 0.75 ? "It is certain." : "Signs point to yes."; } else if (sentimentChoice === 'negative') { answerText = favorableNoul < 0.25 ? "My reply is no." : "Outlook not so good."; } else { answerText = favorableNoul >= 0.5 ? "Signs point to yes." : "Outlook not so good."; } } return res.json({ question: cleanQuestion, answer: answerText, sentiment: sentimentChoice, noul: favorableNoul, confidence: answers.sentiment?.confidence ?? answers.aphorism?.confidence ?? 0.85, probabilities: answers.sentiment?.probabilities || {}, aphorismKey: aphorismKey || 'signs_yes', latency, cost: data.usage?.cost ?? 0, usage: data.usage || {}, model: data.model || MODEL, raw: data }); } catch (err) { console.error('Server exception during Jev request:', err); const latency = Math.round(performance.now() - startTime); return res.json({ question: cleanQuestion, ...fallbackDecision(cleanQuestion), latency, offline: true }); } }); // Calibrated fallback in case of offline/network interruption function fallbackDecision(question) { let hash = 0; for (let i = 0; i < question.length; i++) { hash = (hash << 5) - hash + question.charCodeAt(i); hash |= 0; } const keys = Object.keys(APHORISMS); const selectedKey = keys[Math.abs(hash) % keys.length]; const item = APHORISMS[selectedKey]; return { answer: item.text, sentiment: item.sentiment, noul: item.sentiment === 'affirmative' ? 0.78 : item.sentiment === 'negative' ? 0.22 : 0.5, confidence: 0.8, probabilities: { [item.sentiment]: 0.8 }, aphorismKey: selectedKey, model: 'typesafe/jev-1.13 (offline-cache)' }; } // SPA fallback to index.html app.use((req, res) => { res.sendFile(path.join(__dirname, 'public', 'index.html')); }); app.listen(PORT, () => { console.log(`[jev-8ball] Server running at http://localhost:${PORT}`); console.log(`[jev-8ball] Model: ${MODEL} via OpenRouter Decisions API`); });