File size: 6,971 Bytes
fe9a0c4
 
 
 
 
 
 
 
 
831f7e7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5bd3ab2
 
 
 
 
 
fe9a0c4
5bd3ab2
47fea40
 
 
 
 
5bd3ab2
47fea40
5bd3ab2
fe9a0c4
5bd3ab2
fe9a0c4
5bd3ab2
47fea40
5bd3ab2
 
 
 
 
 
47fea40
5bd3ab2
 
 
47fea40
 
 
 
5bd3ab2
 
 
 
47fea40
 
 
 
 
5bd3ab2
 
 
 
831f7e7
5bd3ab2
 
 
47fea40
5bd3ab2
 
 
 
 
 
fe9a0c4
 
 
831f7e7
fe9a0c4
5bd3ab2
 
 
fe9a0c4
5bd3ab2
47fea40
 
5bd3ab2
fe9a0c4
5bd3ab2
47fea40
 
 
 
 
5bd3ab2
 
 
 
 
 
 
 
 
 
 
 
47fea40
 
 
5bd3ab2
 
47fea40
5bd3ab2
 
 
 
 
47fea40
 
 
 
 
 
 
 
 
 
5bd3ab2
47fea40
 
 
 
 
 
 
 
5bd3ab2
 
47fea40
 
 
 
 
fe9a0c4
 
 
47fea40
 
 
 
fe9a0c4
 
5bd3ab2
fe9a0c4
 
 
47fea40
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
import "https://deno.land/x/[email protected]/mod.ts";
import { serve } from "https://deno.land/[email protected]/http/server.ts";
import { Mistral } from 'npm:@mistralai/mistralai';

const corsHeaders = {
  'Access-Control-Allow-Origin': '*',
  'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',
};

const languagePrompts = {
  en: {
    systemPrompt: "You are helping generate words for a word-guessing game. Generate a single word in English related to the theme",
    requirements: "The word should be:\n- A single word (no spaces or hyphens)\n- Common enough that people would know it\n- Specific enough to be interesting\n- Related to the theme\n- Between 4 and 12 letters\n- A noun\n- NOT be any of these previously used words:"
  },
  fr: {
    systemPrompt: "Vous aidez à générer des mots pour un jeu de devinettes. Générez un seul mot en français lié au thème",
    requirements: "Le mot doit être :\n- Un seul mot (pas d'espaces ni de traits d'union)\n- Assez courant pour que les gens le connaissent\n- Suffisamment spécifique pour être intéressant\n- En rapport avec le thème\n- Entre 4 et 12 lettres\n- Un nom\n- NE PAS être l'un de ces mots déjà utilisés :"
  },
  de: {
    systemPrompt: "Sie helfen bei der Generierung von Wörtern für ein Worträtselspiel. Generieren Sie ein einzelnes Wort auf Deutsch zum Thema",
    requirements: "Das Wort sollte:\n- Ein einzelnes Wort sein (keine Leerzeichen oder Bindestriche)\n- Häufig genug, dass Menschen es kennen\n- Spezifisch genug, um interessant zu sein\n- Zum Thema passen\n- Zwischen 4 und 12 Buchstaben lang sein\n- Ein Substantiv sein\n- NICHT eines dieser bereits verwendeten Wörter sein:"
  },
  it: {
    systemPrompt: "Stai aiutando a generare parole per un gioco di indovinelli. Genera una singola parola in italiano legata al tema",
    requirements: "La parola deve essere:\n- Una singola parola (senza spazi o trattini)\n- Abbastanza comune da essere conosciuta\n- Sufficientemente specifica da essere interessante\n- Correlata al tema\n- Tra 4 e 12 lettere\n- Un sostantivo\n- NON essere una di queste parole già utilizzate:"
  },
  es: {
    systemPrompt: "Estás ayudando a generar palabras para un juego de adivinanzas. Genera una sola palabra en español relacionada con el tema",
    requirements: "La palabra debe ser:\n- Una sola palabra (sin espacios ni guiones)\n- Lo suficientemente común para que la gente la conozca\n- Lo suficientemente específica para ser interesante\n- Relacionada con el tema\n- Entre 4 y 12 letras\n- Un sustantivo\n- NO ser ninguna de estas palabras ya utilizadas:"
  }
};

const openRouterModels = [
  'sophosympatheia/rogue-rose-103b-v0.2:free',
  'google/gemini-2.0-flash-exp:free',
  'meta-llama/llama-3.1-70b-instruct:free',
  'microsoft/phi-3-medium-128k-instruct:free'
];

async function tryMistral(theme: string, usedWords: string[], language: string) {
  const mistralKey = Deno.env.get('MISTRAL_API_KEY');
  if (!mistralKey) {
    throw new Error('Mistral API key not configured');
  }

  const client = new Mistral({
    apiKey: mistralKey,
  });

  const prompts = languagePrompts[language as keyof typeof languagePrompts] || languagePrompts.en;

  const response = await client.chat.complete({
    model: "mistral-large-latest",
    messages: [
      {
        role: "system",
        content: `${prompts.systemPrompt} "${theme}".\n${prompts.requirements} ${usedWords.join(', ')}\n\nRespond with just the word in UPPERCASE, nothing else.`
      }
    ],
    maxTokens: 50,
    temperature: 0.99
  });

  if (!response?.choices?.[0]?.message?.content) {
    throw new Error('Invalid response from Mistral API');
  }

  return response.choices[0].message.content.trim();
}

async function tryOpenRouter(theme: string, usedWords: string[], language: string) {
  const openRouterKey = Deno.env.get('OPENROUTER_API_KEY');
  if (!openRouterKey) {
    throw new Error('OpenRouter API key not configured');
  }

  const prompts = languagePrompts[language as keyof typeof languagePrompts] || languagePrompts.en;
  const randomModel = openRouterModels[Math.floor(Math.random() * openRouterModels.length)];

  console.log('Trying OpenRouter with model:', randomModel);

  const response = await fetch("https://openrouter.ai/api/v1/chat/completions", {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${openRouterKey}`,
      "HTTP-Referer": "https://think-in-sync.com",
      "X-Title": "Think in Sync",
      "Content-Type": "application/json"
    },
    body: JSON.stringify({
      model: randomModel,
      messages: [
        {
          role: "system",
          content: `${prompts.systemPrompt} "${theme}".\n${prompts.requirements} ${usedWords.join(', ')}\n\nRespond with just the word in UPPERCASE, nothing else.`
        }
      ]
    })
  });

  if (!response.ok) {
    const errorText = await response.text();
    throw new Error(`OpenRouter API error: ${response.status} - ${errorText}`);
  }

  const data = await response.json();
  
  if (!data?.choices?.[0]?.message?.content) {
    throw new Error('Invalid response from OpenRouter API');
  }

  return data.choices[0].message.content.trim();
}

serve(async (req) => {
  if (req.method === 'OPTIONS') {
    return new Response(null, { headers: corsHeaders });
  }

  try {
    const { theme, usedWords = [], language = 'en' } = await req.json();
    console.log('Generating word for theme:', theme, 'language:', language, 'excluding:', usedWords);

    let word;
    let error;

    try {
      console.log('Attempting with Mistral...');
      word = await tryMistral(theme, usedWords, language);
      console.log('Successfully generated word with Mistral:', word);
    } catch (mistralError) {
      console.error('Mistral error:', mistralError);
      console.log('Falling back to OpenRouter...');
      
      try {
        word = await tryOpenRouter(theme, usedWords, language);
        console.log('Successfully generated word with OpenRouter:', word);
      } catch (openRouterError) {
        console.error('OpenRouter error:', openRouterError);
        error = openRouterError;
      }
    }

    if (!word) {
      return new Response(
        JSON.stringify({ 
          error: 'Failed to generate word with both Mistral and OpenRouter',
          details: error?.message || 'Unknown error'
        }),
        { 
          status: 500,
          headers: { ...corsHeaders, 'Content-Type': 'application/json' }
        }
      );
    }

    return new Response(
      JSON.stringify({ word }),
      { headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
    );
  } catch (error) {
    console.error('Error generating themed word:', error);
    return new Response(
      JSON.stringify({ 
        error: 'Error generating themed word',
        details: error.message 
      }),
      { 
        status: 500,
        headers: { ...corsHeaders, 'Content-Type': 'application/json' }
      }
    );
  }
});