diff --git a/functions/api/ask.js b/functions/api/ask.js index 93bd108..5877c60 100644 --- a/functions/api/ask.js +++ b/functions/api/ask.js @@ -55,36 +55,42 @@ export async function onRequestPost(context) { try { const payload = { model: MODEL, - state: { question: cleanQuestion }, + 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: "Is the underlying outcome, recommendation, or premise of the user's question affirmative or likely to happen?", + 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 outcome leans affirmative, positive, or advisable.", - false: "The outcome leans doubtful, negative, or unadvisable." + true: "The fortune favors yes, affirmative, or an auspicious outcome.", + false: "The fortune favors no, negative, or an inauspicious outcome." } }, sentiment: { type: "choice", - instructions: "Classify the overall orientation of the oracle's verdict.", + instructions: "Declare the oracle's verdict as either affirmative or negative. Reserve neutral strictly for non-questions or unparseable gibberish.", criteria: { - affirmative: "A clearly favorable, encouraging, or positive answer.", - neutral: "Uncertain, ambiguous, hazy, or premature to determine.", - negative: "An unfavorable, contrary, or discouraging answer." + 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 most accurate Magic 8-Ball statement to deliver.", + instructions: "Select the authentic Magic 8-Ball prophecy that best fits the question.", criteria: { - certain: "It is certain or without a doubt.", - yes_definitely: "Yes definitely or decidedly so.", - signs_yes: "Signs point to yes or most likely.", - reply_hazy: "Reply hazy, try again.", - cannot_predict: "Cannot predict now or ask again later.", - my_reply_no: "My reply is no or outlook not so good.", - very_doubtful: "Very doubtful or don't count on it." + 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)." } } } @@ -119,17 +125,29 @@ export async function onRequestPost(context) { const answers = data.answers || {}; const favorableNoul = answers.favorable?.noul ?? 0.5; - const sentimentChoice = answers.sentiment?.choice || (favorableNoul > 0.6 ? "affirmative" : favorableNoul < 0.4 ? "negative" : "neutral"); - const aphorismKey = answers.aphorism?.choice; + 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.8 ? "It is certain." : "Signs point to yes."; + answerText = favorableNoul > 0.75 ? "It is certain." : "Signs point to yes."; } else if (sentimentChoice === "negative") { - answerText = favorableNoul < 0.2 ? "My reply is no." : "Outlook not so good."; + answerText = favorableNoul < 0.25 ? "My reply is no." : "Outlook not so good."; } else { - answerText = "Reply hazy, try again."; + answerText = favorableNoul >= 0.5 ? "Signs point to yes." : "Outlook not so good."; } } diff --git a/public/css/style.css b/public/css/style.css index a669458..6480085 100644 --- a/public/css/style.css +++ b/public/css/style.css @@ -147,13 +147,13 @@ h1 { color: var(--text-muted); } -/* ─── 8-BALL STAGE & VESSEL ─── */ +/* ─── HEXAGONAL 3D FIGURE STAGE & VESSEL ─── */ .ball-stage { display: flex; justify-content: center; align-items: center; - margin: 10px 0 30px; - perspective: 900px; + margin: 14px 0 32px; + perspective: 1000px; } .ball-wrapper { @@ -162,30 +162,78 @@ h1 { flex-direction: column; align-items: center; cursor: pointer; + perspective: 1000px; } -.ball-sphere { - width: 230px; - height: 230px; - border-radius: 50% !important; /* Keep ball spherical */ - background: radial-gradient(circle at 35% 30%, #2e2e2e 0%, #171717 40%, #090909 75%, #000000 100%); - box-shadow: 0 20px 44px rgba(0, 0, 0, 0.85), inset 0 2px 4px rgba(255, 255, 255, 0.12), inset 0 -6px 14px rgba(0, 0, 0, 0.9); +/* Hexagonal 3D Prism Figure */ +.hex-figure-3d { + width: 210px; + height: 210px; + position: relative; + transform-style: preserve-3d; + transition: transform 0.2s cubic-bezier(0.2, 0, 0, 1); + margin-bottom: 6px; +} + +/* Hex Prism Cap - Front / Back */ +.hex-cap { + position: absolute; + inset: 0; + clip-path: polygon(50% 0%, 93.3% 25%, 93.3% 75%, 50% 100%, 6.7% 75%, 6.7% 25%); +} + +.hex-cap-top { + transform: translateZ(-28px); + background: radial-gradient(circle at 50% 50%, #171717 0%, #0c0c0c 60%, #000 100%); + border: 1px solid rgba(255, 255, 255, 0.08); + box-shadow: inset 0 0 30px rgba(0, 0, 0, 0.9); +} + +.hex-cap-front { + transform: translateZ(28px); + background: radial-gradient(circle at 35% 30%, #2a2a2a 0%, #151515 45%, #0a0a0a 75%, #020202 100%); + border: 1px solid rgba(255, 255, 255, 0.15); + box-shadow: + 0 15px 35px rgba(0, 0, 0, 0.9), + inset 0 2px 4px rgba(255, 255, 255, 0.2), + inset 0 -6px 14px rgba(0, 0, 0, 0.9); display: flex; align-items: center; justify-content: center; - position: relative; - transition: transform 0.15s ease-out; - transform-style: preserve-3d; } -/* Fluid Viewport Window */ +/* 6 Hexagonal Prism Facet Edges around the sides */ +.hex-facet { + position: absolute; + top: 50%; + left: 50%; + width: 105px; + height: 56px; + margin-left: -52.5px; + margin-top: -28px; + background: linear-gradient(to bottom, #222, #0d0d0d 60%, #050505); + border-top: 1px solid rgba(255, 255, 255, 0.16); + border-bottom: 1px solid rgba(0, 0, 0, 0.8); + box-sizing: border-box; + opacity: 0.92; + pointer-events: none; +} + +.hex-facet-0 { transform: rotateZ(0deg) rotateX(90deg) translateZ(90.9px); } +.hex-facet-1 { transform: rotateZ(60deg) rotateX(90deg) translateZ(90.9px); filter: brightness(0.85); } +.hex-facet-2 { transform: rotateZ(120deg) rotateX(90deg) translateZ(90.9px); filter: brightness(0.65); } +.hex-facet-3 { transform: rotateZ(180deg) rotateX(90deg) translateZ(90.9px); filter: brightness(0.5); } +.hex-facet-4 { transform: rotateZ(240deg) rotateX(90deg) translateZ(90.9px); filter: brightness(0.7); } +.hex-facet-5 { transform: rotateZ(300deg) rotateX(90deg) translateZ(90.9px); filter: brightness(0.9); } + +/* Hex Fluid Viewport Window */ .fluid-window { - width: 140px; - height: 140px; - border-radius: 50% !important; /* Inner circular liquid reservoir */ - background: radial-gradient(circle, #081226 0%, #040916 65%, #010308 100%); - border: 2px solid rgba(255, 255, 255, 0.08); - box-shadow: inset 0 4px 18px rgba(0, 0, 0, 0.95), 0 0 10px rgba(0, 0, 0, 0.5); + width: 142px; + height: 142px; + clip-path: polygon(50% 0%, 93.3% 25%, 93.3% 75%, 50% 100%, 6.7% 75%, 6.7% 25%); + background: radial-gradient(circle, #08142a 0%, #040916 65%, #010308 100%); + border: 1px solid rgba(56, 189, 248, 0.15); + box-shadow: inset 0 4px 22px rgba(0, 0, 0, 0.95), 0 0 14px rgba(0, 0, 0, 0.8); position: relative; overflow: hidden; display: flex; @@ -196,11 +244,11 @@ h1 { .fluid-ambient { position: absolute; inset: 0; - background: radial-gradient(circle at 50% 30%, rgba(56, 189, 248, 0.08) 0%, transparent 70%); + background: radial-gradient(circle at 50% 30%, rgba(56, 189, 248, 0.12) 0%, transparent 70%); pointer-events: none; } -/* Floating Triangle Die */ +/* Floating Hexagonal Die Plate */ .floating-die { position: relative; width: 120px; @@ -231,14 +279,21 @@ h1 { } .die-inner { fill: #08142a; + stroke: rgba(56, 189, 248, 0.2); + stroke-width: 1; +} +.die-accent-line { + stroke: rgba(56, 189, 248, 0.3); + stroke-width: 1; + stroke-dasharray: 2 2; } .die-text-wrap { position: relative; z-index: 2; - width: 78px; + width: 82px; text-align: center; - margin-top: 14px; + margin-top: 0; } .die-text { @@ -247,17 +302,17 @@ h1 { letter-spacing: 0.04em; color: #bae6fd; text-transform: uppercase; - line-height: 1.25; + line-height: 1.28; display: inline-block; - text-shadow: 0 0 6px rgba(56, 189, 248, 0.4); + text-shadow: 0 0 8px rgba(56, 189, 248, 0.45); transition: opacity 0.4s ease, transform 0.4s ease; } /* Submerged state */ .floating-die.submerged { - transform: scale(0.35) translateY(18px) rotate(15deg) !important; + transform: scale(0.28) translateY(24px) rotate(35deg) !important; opacity: 0; - filter: blur(4px); + filter: blur(5px); animation: none; } @@ -266,30 +321,71 @@ h1 { animation: dieSurface 1.1s cubic-bezier(0.175, 0.885, 0.32, 1.275) forwards; } @keyframes dieSurface { - 0% { transform: scale(0.4) translateY(20px) rotate(-20deg); opacity: 0; filter: blur(4px); } - 60% { transform: scale(1.06) translateY(-2px) rotate(2deg); opacity: 0.9; filter: blur(0); } + 0% { transform: scale(0.35) translateY(24px) rotate(-30deg); opacity: 0; filter: blur(5px); } + 60% { transform: scale(1.08) translateY(-2px) rotate(2deg); opacity: 0.95; filter: blur(0); } 100% { transform: scale(1) translateY(0) rotate(0deg); opacity: 1; filter: blur(0); } } -/* Shake Animation on Ball */ -.ball-sphere.shaking { - animation: eightBallShake 0.75s cubic-bezier(0.36, 0.07, 0.19, 0.97) both; +/* ─── 3D ROLL-IN-PLACE ANIMATION ─── */ +.hex-figure-3d.rolling { + animation: hexRollInPlace 1.25s cubic-bezier(0.45, 0.05, 0.25, 1) forwards; } -@keyframes eightBallShake { - 10%, 90% { transform: translate3d(-3px, 0, 0) rotate(-2deg); } - 20%, 80% { transform: translate3d(4px, 0, 0) rotate(2deg); } - 30%, 50%, 70% { transform: translate3d(-6px, 0, 0) rotate(-3deg); } - 40%, 60% { transform: translate3d(6px, 0, 0) rotate(3deg); } +@keyframes hexRollInPlace { + 0% { + transform: rotateX(0deg) rotateY(0deg) rotateZ(0deg) scale(1); + } + 15% { + transform: rotateX(70deg) rotateY(-80deg) rotateZ(-45deg) translateY(-8px) scale(1.04); + } + 35% { + transform: rotateX(190deg) rotateY(160deg) rotateZ(120deg) translateY(-14px) scale(1.08); + } + 55% { + transform: rotateX(310deg) rotateY(380deg) rotateZ(260deg) translateY(-8px) scale(1.05); + } + 75% { + transform: rotateX(375deg) rotateY(560deg) rotateZ(350deg) translateY(-2px) scale(1.01); + } + 90% { + transform: rotateX(358deg) rotateY(724deg) rotateZ(362deg) translateY(0px) scale(0.99); + } + 100% { + transform: rotateX(360deg) rotateY(720deg) rotateZ(360deg) translateY(0px) scale(1); + } } -/* Floor Shadow */ +/* Synchronized Shadow Expansion / Compression during Roll */ .ball-shadow { - width: 170px; - height: 20px; + width: 180px; + height: 22px; border-radius: 50% !important; - background: radial-gradient(ellipse, rgba(0, 0, 0, 0.6) 0%, transparent 70%); - margin-top: 14px; + background: radial-gradient(ellipse, rgba(0, 0, 0, 0.75) 0%, transparent 70%); + margin-top: 8px; + transition: transform 0.2s ease, opacity 0.2s ease; +} + +.ball-shadow.rolling-shadow { + animation: shadowRollSync 1.25s cubic-bezier(0.45, 0.05, 0.25, 1) forwards; +} + +@keyframes shadowRollSync { + 0%, 100% { + transform: scale(1); + opacity: 0.75; + } + 35% { + transform: scale(0.72); + opacity: 0.35; + } + 55% { + transform: scale(0.85); + opacity: 0.5; + } + 85% { + transform: scale(1.05); + opacity: 0.85; + } } /* ─── PROMPT FORM ─── */ diff --git a/public/index.html b/public/index.html index dd50d71..7f0e86a 100644 --- a/public/index.html +++ b/public/index.html @@ -35,30 +35,49 @@ - +
-
-
- -
-
- -
- - - - - -
- CONCENTRATE
& ASK
+
+ +
+ +
+ +
+
+
+
+
+
+ +
+
+
+ +
+ + + + + + + + + + + + + +
+ CONCENTRATE
& ASK
+
+
- -
-
+
diff --git a/public/js/app.js b/public/js/app.js index 1d03484..0bc5aeb 100644 --- a/public/js/app.js +++ b/public/js/app.js @@ -93,24 +93,34 @@ osc.stop(this.ctx.currentTime + duration); } catch (e) {} } - shakeSound() { + rollSound() { if (this.muted) return; try { this.init(); if (!this.ctx) return; - // Fluid swish pulse + // Whirring resonant gyroscopic roll sweep const osc = this.ctx.createOscillator(); const gain = this.ctx.createGain(); - osc.type = 'triangle'; - osc.frequency.setValueAtTime(90, this.ctx.currentTime); - osc.frequency.linearRampToValueAtTime(160, this.ctx.currentTime + 0.2); - osc.frequency.linearRampToValueAtTime(80, this.ctx.currentTime + 0.4); - gain.gain.setValueAtTime(0.12, this.ctx.currentTime); - gain.gain.exponentialRampToValueAtTime(0.001, this.ctx.currentTime + 0.45); - osc.connect(gain); + osc.type = 'sawtooth'; + osc.frequency.setValueAtTime(60, this.ctx.currentTime); + osc.frequency.exponentialRampToValueAtTime(240, this.ctx.currentTime + 0.4); + osc.frequency.exponentialRampToValueAtTime(95, this.ctx.currentTime + 1.1); + + const filter = this.ctx.createBiquadFilter(); + filter.type = 'lowpass'; + filter.frequency.setValueAtTime(450, this.ctx.currentTime); + filter.frequency.linearRampToValueAtTime(900, this.ctx.currentTime + 0.5); + filter.frequency.linearRampToValueAtTime(300, this.ctx.currentTime + 1.1); + + gain.gain.setValueAtTime(0.08, this.ctx.currentTime); + gain.gain.linearRampToValueAtTime(0.16, this.ctx.currentTime + 0.35); + gain.gain.exponentialRampToValueAtTime(0.001, this.ctx.currentTime + 1.15); + + osc.connect(filter); + filter.connect(gain); gain.connect(this.ctx.destination); osc.start(); - osc.stop(this.ctx.currentTime + 0.45); + osc.stop(this.ctx.currentTime + 1.15); } catch (e) {} } revealChime(sentiment = 'affirmative') { @@ -133,6 +143,7 @@ // ─── DOM ELEMENTS ─── const ballWrapper = document.getElementById('ballWrapper'); const ballSphere = document.getElementById('ballSphere'); + const ballShadow = document.getElementById('ballShadow'); const floatingDie = document.getElementById('floatingDie'); const dieText = document.getElementById('dieText'); const askForm = document.getElementById('askForm'); @@ -210,7 +221,7 @@ } }); - // Clicking ball shakes it if input is non-empty, or prompts user + // Clicking hexagonal figure rolls it if input is non-empty, or prompts user ballWrapper.addEventListener('click', () => { if (isSubmitting) return; if (questionInput.value.trim()) { @@ -271,12 +282,13 @@ btnText.style.display = 'none'; btnSpinner.style.display = 'inline-block'; - // 1. Shake Ball & Submerge Die - ballSphere.classList.add('shaking'); + // 1. Roll Hexagonal Figure in Place & Submerge Die + ballSphere.classList.add('rolling'); + if (ballShadow) ballShadow.classList.add('rolling-shadow'); floatingDie.className = 'floating-die submerged'; - audio.shakeSound(); + audio.rollSound(); - const minShakeTime = new Promise(resolve => setTimeout(resolve, 750)); + const minRollTime = new Promise(resolve => setTimeout(resolve, 1250)); try { // 2. Call /api/ask @@ -286,10 +298,12 @@ body: JSON.stringify({ question }) }).then(r => r.json()); - const [_, result] = await Promise.all([minShakeTime, apiCall]); + const [_, result] = await Promise.all([minRollTime, apiCall]); - // Remove shaking - ballSphere.classList.remove('shaking'); + // Remove rolling classes + ballSphere.classList.remove('rolling'); + if (ballShadow) ballShadow.classList.remove('rolling-shadow'); + ballSphere.style.transform = 'rotateX(0deg) rotateY(0deg)'; if (result.error && !result.fallback) { showToast(result.error || 'Decision failed'); @@ -303,8 +317,10 @@ } catch (err) { console.error('Request failed:', err); - ballSphere.classList.remove('shaking'); - dieText.innerHTML = 'REPLY HAZY
TRY AGAIN'; + ballSphere.classList.remove('rolling'); + if (ballShadow) ballShadow.classList.remove('rolling-shadow'); + ballSphere.style.transform = 'rotateX(0deg) rotateY(0deg)'; + dieText.innerHTML = 'SIGNS POINT
TO YES'; floatingDie.className = 'floating-die surfacing'; showToast('Network timeout. Please retry.'); } finally { diff --git a/server.js b/server.js index d6ac1c2..59f4ff1 100644 --- a/server.js +++ b/server.js @@ -64,36 +64,42 @@ app.post('/api/ask', async (req, res) => { try { const payload = { model: MODEL, - state: { question: cleanQuestion }, + 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: "Is the underlying outcome, recommendation, or premise of the user's question affirmative or likely to happen?", + 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 outcome leans affirmative, positive, or advisable.", - false: "The outcome leans doubtful, negative, or unadvisable." + true: "The fortune favors yes, affirmative, or an auspicious outcome.", + false: "The fortune favors no, negative, or an inauspicious outcome." } }, sentiment: { type: "choice", - instructions: "Classify the overall orientation of the oracle's verdict.", + instructions: "Declare the oracle's verdict as either affirmative or negative. Reserve neutral strictly for non-questions or unparseable gibberish.", criteria: { - affirmative: "A clearly favorable, encouraging, or positive answer.", - neutral: "Uncertain, ambiguous, hazy, or premature to determine.", - negative: "An unfavorable, contrary, or discouraging answer." + 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 most accurate Magic 8-Ball statement to deliver.", + instructions: "Select the authentic Magic 8-Ball prophecy that best fits the question.", criteria: { - certain: "It is certain or without a doubt.", - yes_definitely: "Yes definitely or decidedly so.", - signs_yes: "Signs point to yes or most likely.", - reply_hazy: "Reply hazy, try again.", - cannot_predict: "Cannot predict now or ask again later.", - my_reply_no: "My reply is no or outlook not so good.", - very_doubtful: "Very doubtful or don't count on it." + 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)." } } } @@ -105,7 +111,7 @@ app.post('/api/ask', async (req, res) => { 'Authorization': `Bearer ${OPENROUTER_API_KEY}`, 'Content-Type': 'application/json', 'HTTP-Referer': 'https://github.com/oiupoyt/jev-8ball', - 'X-Title': 'Jev 8-Ball Oracle' + 'X-Title': 'Jev 8-Ball Oracle (Dev)' }, body: JSON.stringify(payload) }); @@ -114,9 +120,9 @@ app.post('/api/ask', async (req, res) => { if (!response.ok) { const errText = await response.text(); - console.error('OpenRouter Jev error:', response.status, errText); + console.error('OpenRouter Decision Error:', response.status, errText); return res.status(response.status).json({ - error: 'OpenRouter Jev decision failure', + error: 'OpenRouter decision error', details: errText, fallback: fallbackDecision(cleanQuestion) }); @@ -126,17 +132,29 @@ app.post('/api/ask', async (req, res) => { const answers = data.answers || {}; const favorableNoul = answers.favorable?.noul ?? 0.5; - const sentimentChoice = answers.sentiment?.choice || (favorableNoul > 0.6 ? 'affirmative' : favorableNoul < 0.4 ? 'negative' : 'neutral'); - const aphorismKey = answers.aphorism?.choice; + 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.8 ? "It is certain." : "Signs point to yes."; + answerText = favorableNoul > 0.75 ? "It is certain." : "Signs point to yes."; } else if (sentimentChoice === 'negative') { - answerText = favorableNoul < 0.2 ? "My reply is no." : "Outlook not so good."; + answerText = favorableNoul < 0.25 ? "My reply is no." : "Outlook not so good."; } else { - answerText = "Reply hazy, try again."; + answerText = favorableNoul >= 0.5 ? "Signs point to yes." : "Outlook not so good."; } } diff --git a/worker.mjs b/worker.mjs index 8a3a7d4..8ea7eab 100644 --- a/worker.mjs +++ b/worker.mjs @@ -88,36 +88,42 @@ export default { try { const payload = { model: MODEL, - state: { question: cleanQuestion }, + 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: "Is the underlying outcome, recommendation, or premise of the user's question affirmative or likely to happen?", + 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 outcome leans affirmative, positive, or advisable.", - false: "The outcome leans doubtful, negative, or unadvisable." + true: "The fortune favors yes, affirmative, or an auspicious outcome.", + false: "The fortune favors no, negative, or an inauspicious outcome." } }, sentiment: { type: "choice", - instructions: "Classify the overall orientation of the oracle's verdict.", + instructions: "Declare the oracle's verdict as either affirmative or negative. Reserve neutral strictly for non-questions or unparseable gibberish.", criteria: { - affirmative: "A clearly favorable, encouraging, or positive answer.", - neutral: "Uncertain, ambiguous, hazy, or premature to determine.", - negative: "An unfavorable, contrary, or discouraging answer." + 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 most accurate Magic 8-Ball statement to deliver.", + instructions: "Select the authentic Magic 8-Ball prophecy that best fits the question.", criteria: { - certain: "It is certain or without a doubt.", - yes_definitely: "Yes definitely or decidedly so.", - signs_yes: "Signs point to yes or most likely.", - reply_hazy: "Reply hazy, try again.", - cannot_predict: "Cannot predict now or ask again later.", - my_reply_no: "My reply is no or outlook not so good.", - very_doubtful: "Very doubtful or don't count on it." + 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)." } } } @@ -152,17 +158,34 @@ export default { const answers = data.answers || {}; const favorableNoul = answers.favorable?.noul ?? 0.5; - const sentimentChoice = answers.sentiment?.choice || (favorableNoul > 0.6 ? "affirmative" : favorableNoul < 0.4 ? "negative" : "neutral"); - const aphorismKey = answers.aphorism?.choice; + let sentimentChoice = answers.sentiment?.choice; + if (!sentimentChoice || sentimentChoice === "neutral") { + // If the model gave neutral, infer from noul or aphorism + if (favorableNoul >= 0.5) { + sentimentChoice = "affirmative"; + } else { + sentimentChoice = "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.8 ? "It is certain." : "Signs point to yes."; + answerText = favorableNoul > 0.75 ? "It is certain." : "Signs point to yes."; } else if (sentimentChoice === "negative") { - answerText = favorableNoul < 0.2 ? "My reply is no." : "Outlook not so good."; + answerText = favorableNoul < 0.25 ? "My reply is no." : "Outlook not so good."; } else { - answerText = "Reply hazy, try again."; + answerText = favorableNoul >= 0.5 ? "Signs point to yes." : "Outlook not so good."; } }