summaryrefslogtreecommitdiff
path: root/api/ai.ts
diff options
context:
space:
mode:
Diffstat (limited to 'api/ai.ts')
-rw-r--r--api/ai.ts93
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!;
+};