diff options
Diffstat (limited to 'api/ai.ts')
-rw-r--r-- | api/ai.ts | 93 |
1 files changed, 93 insertions, 0 deletions
diff --git a/api/ai.ts b/api/ai.ts new file mode 100644 index 0000000..b2a3ee7 --- /dev/null +++ b/api/ai.ts @@ -0,0 +1,93 @@ +import { OpenAI } from "openai"; + +const basePrompt = ` +You are the backend logic for a game like the New York Times’ *Connections*. + +A player gives you four words. Your job is to return the **most clever, satisfying category** that all four words fit into — as if you were designing a high-quality puzzle. + +🎯 Output must be JSON: +{"categoryName": "Short Title (≤5 words)", "reason": "Brief, clear explanation why each word fits"} + +🧠 Your answer should feel: +- **Clever and insightful** (not generic) +- **Tight and specific** (all 4 words must fit cleanly) +- **Surprising but satisfying** (think lateral thinking, not just surface meaning) + +🧩 Great connections are often based on: +1. **Grammar or structure** (e.g., homonyms, stress-shifting words) +2. **Wordplay** (prefixes, rhymes, common idioms) +3. **Cultural patterns** (slang, media, jokes, Jeopardy-style trivia) +4. **Domain-specific themes** (tech terms, sports slang, myth references) + +💡 Think like a puzzle maker. Test your idea: +- Would a smart, skeptical puzzle fan say “Ohhh, nice”? +- Does **each word** clearly belong? +- If not, scrap it and try another approach. + +Avoid weak categories like: +- “Verbs” ❌ (too broad) +- “Things you can flip” ❌ (tenuous logic) +- “Nice things” ❌ (vague) + +✔ Examples of great answers: +[record, permit, insult, reject] → "Stress-Shifting Words" +[day, head, toe, man] → "___ to ___" +[brew, java, mud, rocketfuel] → "Slang for Coffee" +[duck, bank, mail, plant] → "Nouns That Are Verbs" +[transexual, muslims, media, taxes] → "Fox News Scapegoats" + +⚠️ Don’t overreach. Do not invent connections — if it’s not clean, try a new angle. + +Mandatory check before submitting: +- Word 1: does it clearly fit? +- Word 2: does it clearly fit? +- Word 3: does it clearly fit? +- Word 4: does it clearly fit? + +If even one doesn’t, the category is wrong. Start over. + +Keep it **fun**, **tight**, and **clever**. Never lazy. Never vague. + +🎯 Output must be JSON: +{"categoryName": "Short Title (≤5 words)", "reason": "Brief, clear explanation why each word fits"} +`; + +let client: OpenAI; + +const getCompletion = async ({ + messages, +}: { + messages: string[]; +}): Promise<string> => { + if (!client) { + client = new OpenAI({ apiKey: process.env.OPENAI_API! }); + } + const completion = await client.chat.completions.create({ + model: "gpt-4.1", + messages: messages.map((message) => ({ + role: "developer", + content: message, + })), + }); + return completion.choices[0].message.content!; +}; + +export const getGroupName = async ( + words: string[] +): Promise<{ + categoryName: string; + reason: string; +}> => { + let candidate: { categoryName: string; reason: string }; + const messages = [ + basePrompt, + `Now, given these four words: ${[words.join(", ")]}`, + ]; + candidate = JSON.parse(await getCompletion({ messages })); + if (!candidate.categoryName || !candidate.reason) { + throw new Error(`Got invalid response!`); + } + console.log(`Got candidate: ${JSON.stringify(candidate)}`); + + return candidate!; +}; |