Felix Zieger
commited on
Commit
·
5bd3ab2
1
Parent(s):
bf1b7c7
fallback AI model
Browse files
supabase/functions/detect-fraud/index.ts
CHANGED
|
@@ -27,7 +27,7 @@ serve(async (req) => {
|
|
| 27 |
while (retryCount < maxRetries) {
|
| 28 |
try {
|
| 29 |
const response = await client.chat.complete({
|
| 30 |
-
model: "mistral-
|
| 31 |
messages: [
|
| 32 |
{
|
| 33 |
role: "system",
|
|
|
|
| 27 |
while (retryCount < maxRetries) {
|
| 28 |
try {
|
| 29 |
const response = await client.chat.complete({
|
| 30 |
+
model: "mistral-medium-latest",
|
| 31 |
messages: [
|
| 32 |
{
|
| 33 |
role: "system",
|
supabase/functions/generate-themed-word/index.ts
CHANGED
|
@@ -30,56 +30,104 @@ const languagePrompts = {
|
|
| 30 |
}
|
| 31 |
};
|
| 32 |
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
|
|
|
|
|
|
| 37 |
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
|
|
|
| 41 |
|
| 42 |
-
|
| 43 |
-
apiKey: Deno.env.get('MISTRAL_API_KEY'),
|
| 44 |
-
});
|
| 45 |
|
| 46 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 47 |
|
| 48 |
-
|
| 49 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 50 |
messages: [
|
| 51 |
{
|
| 52 |
role: "system",
|
| 53 |
content: `${prompts.systemPrompt} "${theme}".\n${prompts.requirements} ${usedWords.join(', ')}\n\nRespond with just the word in UPPERCASE, nothing else.`
|
| 54 |
}
|
| 55 |
-
]
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
});
|
| 59 |
|
| 60 |
-
|
| 61 |
-
|
|
|
|
| 62 |
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 72 |
} catch (error) {
|
| 73 |
console.error('Error generating themed word:', error);
|
| 74 |
return new Response(
|
| 75 |
JSON.stringify({ error: error.message }),
|
| 76 |
{
|
| 77 |
status: 500,
|
| 78 |
-
headers: {
|
| 79 |
-
...corsHeaders,
|
| 80 |
-
'Content-Type': 'application/json'
|
| 81 |
-
}
|
| 82 |
}
|
| 83 |
);
|
| 84 |
}
|
| 85 |
-
});
|
|
|
|
| 30 |
}
|
| 31 |
};
|
| 32 |
|
| 33 |
+
const openRouterModels = [
|
| 34 |
+
'sophosympatheia/rogue-rose-103b-v0.2:free',
|
| 35 |
+
'google/gemini-2.0-flash-exp:free',
|
| 36 |
+
'meta-llama/llama-3.1-70b-instruct:free',
|
| 37 |
+
'microsoft/phi-3-medium-128k-instruct:free'
|
| 38 |
+
];
|
| 39 |
|
| 40 |
+
async function tryMistral(theme: string, usedWords: string[], language: string) {
|
| 41 |
+
const client = new Mistral({
|
| 42 |
+
apiKey: Deno.env.get('MISTRAL_API_KEY'),
|
| 43 |
+
});
|
| 44 |
|
| 45 |
+
const prompts = languagePrompts[language as keyof typeof languagePrompts] || languagePrompts.en;
|
|
|
|
|
|
|
| 46 |
|
| 47 |
+
const response = await client.chat.complete({
|
| 48 |
+
model: "mistral-medium-latest",
|
| 49 |
+
messages: [
|
| 50 |
+
{
|
| 51 |
+
role: "system",
|
| 52 |
+
content: `${prompts.systemPrompt} "${theme}".\n${prompts.requirements} ${usedWords.join(', ')}\n\nRespond with just the word in UPPERCASE, nothing else.`
|
| 53 |
+
}
|
| 54 |
+
],
|
| 55 |
+
maxTokens: 300,
|
| 56 |
+
temperature: 0.99
|
| 57 |
+
});
|
| 58 |
+
|
| 59 |
+
return response.choices[0].message.content.trim();
|
| 60 |
+
}
|
| 61 |
+
|
| 62 |
+
async function tryOpenRouter(theme: string, usedWords: string[], language: string) {
|
| 63 |
+
const prompts = languagePrompts[language as keyof typeof languagePrompts] || languagePrompts.en;
|
| 64 |
+
const randomModel = openRouterModels[Math.floor(Math.random() * openRouterModels.length)];
|
| 65 |
+
|
| 66 |
+
console.log('Trying OpenRouter with model:', randomModel);
|
| 67 |
|
| 68 |
+
const response = await fetch("https://openrouter.ai/api/v1/chat/completions", {
|
| 69 |
+
method: "POST",
|
| 70 |
+
headers: {
|
| 71 |
+
"Authorization": `Bearer ${Deno.env.get('OPENROUTER_API_KEY')}`,
|
| 72 |
+
"HTTP-Referer": "https://think-in-sync.com",
|
| 73 |
+
"X-Title": "Think in Sync",
|
| 74 |
+
"Content-Type": "application/json"
|
| 75 |
+
},
|
| 76 |
+
body: JSON.stringify({
|
| 77 |
+
model: randomModel,
|
| 78 |
messages: [
|
| 79 |
{
|
| 80 |
role: "system",
|
| 81 |
content: `${prompts.systemPrompt} "${theme}".\n${prompts.requirements} ${usedWords.join(', ')}\n\nRespond with just the word in UPPERCASE, nothing else.`
|
| 82 |
}
|
| 83 |
+
]
|
| 84 |
+
})
|
| 85 |
+
});
|
|
|
|
| 86 |
|
| 87 |
+
if (!response.ok) {
|
| 88 |
+
throw new Error(`OpenRouter API error: ${response.status}`);
|
| 89 |
+
}
|
| 90 |
|
| 91 |
+
const data = await response.json();
|
| 92 |
+
return data.choices[0].message.content.trim();
|
| 93 |
+
}
|
| 94 |
+
|
| 95 |
+
serve(async (req) => {
|
| 96 |
+
if (req.method === 'OPTIONS') {
|
| 97 |
+
return new Response(null, { headers: corsHeaders });
|
| 98 |
+
}
|
| 99 |
+
|
| 100 |
+
try {
|
| 101 |
+
const { theme, usedWords = [], language = 'en' } = await req.json();
|
| 102 |
+
console.log('Generating word for theme:', theme, 'language:', language, 'excluding:', usedWords);
|
| 103 |
+
|
| 104 |
+
try {
|
| 105 |
+
console.log('Attempting with Mistral...');
|
| 106 |
+
const word = await tryMistral(theme, usedWords, language);
|
| 107 |
+
console.log('Successfully generated word with Mistral:', word);
|
| 108 |
+
return new Response(
|
| 109 |
+
JSON.stringify({ word }),
|
| 110 |
+
{ headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
|
| 111 |
+
);
|
| 112 |
+
} catch (mistralError) {
|
| 113 |
+
console.error('Mistral error:', mistralError);
|
| 114 |
+
console.log('Falling back to OpenRouter...');
|
| 115 |
+
|
| 116 |
+
const word = await tryOpenRouter(theme, usedWords, language);
|
| 117 |
+
console.log('Successfully generated word with OpenRouter:', word);
|
| 118 |
+
return new Response(
|
| 119 |
+
JSON.stringify({ word }),
|
| 120 |
+
{ headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
|
| 121 |
+
);
|
| 122 |
+
}
|
| 123 |
} catch (error) {
|
| 124 |
console.error('Error generating themed word:', error);
|
| 125 |
return new Response(
|
| 126 |
JSON.stringify({ error: error.message }),
|
| 127 |
{
|
| 128 |
status: 500,
|
| 129 |
+
headers: { ...corsHeaders, 'Content-Type': 'application/json' }
|
|
|
|
|
|
|
|
|
|
| 130 |
}
|
| 131 |
);
|
| 132 |
}
|
| 133 |
+
});
|
supabase/functions/generate-word/index.ts
CHANGED
|
@@ -34,6 +34,82 @@ const languagePrompts = {
|
|
| 34 |
}
|
| 35 |
};
|
| 36 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 37 |
serve(async (req) => {
|
| 38 |
if (req.method === 'OPTIONS') {
|
| 39 |
return new Response(null, { headers: corsHeaders });
|
|
@@ -44,79 +120,34 @@ serve(async (req) => {
|
|
| 44 |
console.log('Generating word for:', { currentWord, currentSentence, language });
|
| 45 |
|
| 46 |
const existingSentence = currentSentence || '';
|
| 47 |
-
const prompts = languagePrompts[language as keyof typeof languagePrompts] || languagePrompts.en;
|
| 48 |
-
|
| 49 |
-
const client = new Mistral({
|
| 50 |
-
apiKey: Deno.env.get('MISTRAL_API_KEY'),
|
| 51 |
-
});
|
| 52 |
-
|
| 53 |
-
const maxRetries = 3;
|
| 54 |
-
let retryCount = 0;
|
| 55 |
-
let lastError = null;
|
| 56 |
-
|
| 57 |
-
while (retryCount < maxRetries) {
|
| 58 |
-
try {
|
| 59 |
-
const response = await client.chat.complete({
|
| 60 |
-
model: "mistral-large-latest",
|
| 61 |
-
messages: [
|
| 62 |
-
{
|
| 63 |
-
role: "system",
|
| 64 |
-
content: `${prompts.systemPrompt} "${currentWord}". ${prompts.task} ${prompts.instruction} "${existingSentence}". Do not add quotes or backticks. Just answer with the sentence.`
|
| 65 |
-
}
|
| 66 |
-
],
|
| 67 |
-
maxTokens: 300,
|
| 68 |
-
temperature: 0.5
|
| 69 |
-
});
|
| 70 |
-
|
| 71 |
-
const aiResponse = response.choices[0].message.content.trim();
|
| 72 |
-
console.log('AI full response:', aiResponse);
|
| 73 |
-
|
| 74 |
-
const newWord = aiResponse
|
| 75 |
-
.slice(existingSentence.length)
|
| 76 |
-
.trim()
|
| 77 |
-
.split(' ')[0]
|
| 78 |
-
.replace(/[.,!?]$/, '');
|
| 79 |
-
|
| 80 |
-
console.log('Extracted new word:', newWord);
|
| 81 |
-
|
| 82 |
-
return new Response(
|
| 83 |
-
JSON.stringify({ word: newWord }),
|
| 84 |
-
{ headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
|
| 85 |
-
);
|
| 86 |
-
} catch (error) {
|
| 87 |
-
console.error(`Attempt ${retryCount + 1} failed:`, error);
|
| 88 |
-
lastError = error;
|
| 89 |
-
|
| 90 |
-
if (error.message?.includes('rate limit') || error.status === 429) {
|
| 91 |
-
const waitTime = Math.pow(2, retryCount) * 1000;
|
| 92 |
-
console.log(`Rate limit hit, waiting ${waitTime}ms before retry`);
|
| 93 |
-
await new Promise(resolve => setTimeout(resolve, waitTime));
|
| 94 |
-
retryCount++;
|
| 95 |
-
continue;
|
| 96 |
-
}
|
| 97 |
-
|
| 98 |
-
throw error;
|
| 99 |
-
}
|
| 100 |
-
}
|
| 101 |
-
|
| 102 |
-
throw new Error(`Failed after ${maxRetries} attempts. Last error: ${lastError?.message}`);
|
| 103 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 104 |
} catch (error) {
|
| 105 |
console.error('Error generating word:', error);
|
| 106 |
-
|
| 107 |
-
const errorMessage = error.message?.includes('rate limit')
|
| 108 |
-
? "The AI service is currently busy. Please try again in a few moments."
|
| 109 |
-
: "Sorry, there was an error generating the word. Please try again.";
|
| 110 |
-
|
| 111 |
return new Response(
|
| 112 |
-
JSON.stringify({
|
| 113 |
-
error: errorMessage,
|
| 114 |
-
details: error.message
|
| 115 |
-
}),
|
| 116 |
{
|
| 117 |
-
status:
|
| 118 |
headers: { ...corsHeaders, 'Content-Type': 'application/json' }
|
| 119 |
}
|
| 120 |
);
|
| 121 |
}
|
| 122 |
-
});
|
|
|
|
| 34 |
}
|
| 35 |
};
|
| 36 |
|
| 37 |
+
const openRouterModels = [
|
| 38 |
+
'sophosympatheia/rogue-rose-103b-v0.2:free',
|
| 39 |
+
'google/gemini-2.0-flash-exp:free',
|
| 40 |
+
'meta-llama/llama-3.1-70b-instruct:free',
|
| 41 |
+
'microsoft/phi-3-medium-128k-instruct:free'
|
| 42 |
+
];
|
| 43 |
+
|
| 44 |
+
async function tryMistral(currentWord: string, existingSentence: string, language: string) {
|
| 45 |
+
const client = new Mistral({
|
| 46 |
+
apiKey: Deno.env.get('MISTRAL_API_KEY'),
|
| 47 |
+
});
|
| 48 |
+
|
| 49 |
+
const prompts = languagePrompts[language as keyof typeof languagePrompts] || languagePrompts.en;
|
| 50 |
+
|
| 51 |
+
const response = await client.chat.complete({
|
| 52 |
+
model: "mistral-medium-latest",
|
| 53 |
+
messages: [
|
| 54 |
+
{
|
| 55 |
+
role: "system",
|
| 56 |
+
content: `${prompts.systemPrompt} "${currentWord}". ${prompts.task} ${prompts.instruction} "${existingSentence}". Do not add quotes or backticks. Just answer with the sentence.`
|
| 57 |
+
}
|
| 58 |
+
],
|
| 59 |
+
maxTokens: 300,
|
| 60 |
+
temperature: 0.5
|
| 61 |
+
});
|
| 62 |
+
|
| 63 |
+
const aiResponse = response.choices[0].message.content.trim();
|
| 64 |
+
console.log('Mistral full response:', aiResponse);
|
| 65 |
+
|
| 66 |
+
return aiResponse
|
| 67 |
+
.slice(existingSentence.length)
|
| 68 |
+
.trim()
|
| 69 |
+
.split(' ')[0]
|
| 70 |
+
.replace(/[.,!?]$/, '');
|
| 71 |
+
}
|
| 72 |
+
|
| 73 |
+
async function tryOpenRouter(currentWord: string, existingSentence: string, language: string) {
|
| 74 |
+
const prompts = languagePrompts[language as keyof typeof languagePrompts] || languagePrompts.en;
|
| 75 |
+
const randomModel = openRouterModels[Math.floor(Math.random() * openRouterModels.length)];
|
| 76 |
+
|
| 77 |
+
console.log('Trying OpenRouter with model:', randomModel);
|
| 78 |
+
|
| 79 |
+
const response = await fetch("https://openrouter.ai/api/v1/chat/completions", {
|
| 80 |
+
method: "POST",
|
| 81 |
+
headers: {
|
| 82 |
+
"Authorization": `Bearer ${Deno.env.get('OPENROUTER_API_KEY')}`,
|
| 83 |
+
"HTTP-Referer": "https://think-in-sync.com",
|
| 84 |
+
"X-Title": "Think in Sync",
|
| 85 |
+
"Content-Type": "application/json"
|
| 86 |
+
},
|
| 87 |
+
body: JSON.stringify({
|
| 88 |
+
model: randomModel,
|
| 89 |
+
messages: [
|
| 90 |
+
{
|
| 91 |
+
role: "system",
|
| 92 |
+
content: `${prompts.systemPrompt} "${currentWord}". ${prompts.task} ${prompts.instruction} "${existingSentence}". Do not add quotes or backticks. Just answer with the sentence.`
|
| 93 |
+
}
|
| 94 |
+
]
|
| 95 |
+
})
|
| 96 |
+
});
|
| 97 |
+
|
| 98 |
+
if (!response.ok) {
|
| 99 |
+
throw new Error(`OpenRouter API error: ${response.status}`);
|
| 100 |
+
}
|
| 101 |
+
|
| 102 |
+
const data = await response.json();
|
| 103 |
+
const aiResponse = data.choices[0].message.content.trim();
|
| 104 |
+
console.log('OpenRouter full response:', aiResponse);
|
| 105 |
+
|
| 106 |
+
return aiResponse
|
| 107 |
+
.slice(existingSentence.length)
|
| 108 |
+
.trim()
|
| 109 |
+
.split(' ')[0]
|
| 110 |
+
.replace(/[.,!?]$/, '');
|
| 111 |
+
}
|
| 112 |
+
|
| 113 |
serve(async (req) => {
|
| 114 |
if (req.method === 'OPTIONS') {
|
| 115 |
return new Response(null, { headers: corsHeaders });
|
|
|
|
| 120 |
console.log('Generating word for:', { currentWord, currentSentence, language });
|
| 121 |
|
| 122 |
const existingSentence = currentSentence || '';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 123 |
|
| 124 |
+
try {
|
| 125 |
+
console.log('Attempting with Mistral...');
|
| 126 |
+
const word = await tryMistral(currentWord, existingSentence, language);
|
| 127 |
+
console.log('Successfully generated word with Mistral:', word);
|
| 128 |
+
return new Response(
|
| 129 |
+
JSON.stringify({ word }),
|
| 130 |
+
{ headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
|
| 131 |
+
);
|
| 132 |
+
} catch (mistralError) {
|
| 133 |
+
console.error('Mistral error:', mistralError);
|
| 134 |
+
console.log('Falling back to OpenRouter...');
|
| 135 |
+
|
| 136 |
+
const word = await tryOpenRouter(currentWord, existingSentence, language);
|
| 137 |
+
console.log('Successfully generated word with OpenRouter:', word);
|
| 138 |
+
return new Response(
|
| 139 |
+
JSON.stringify({ word }),
|
| 140 |
+
{ headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
|
| 141 |
+
);
|
| 142 |
+
}
|
| 143 |
} catch (error) {
|
| 144 |
console.error('Error generating word:', error);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 145 |
return new Response(
|
| 146 |
+
JSON.stringify({ error: error.message }),
|
|
|
|
|
|
|
|
|
|
| 147 |
{
|
| 148 |
+
status: 500,
|
| 149 |
headers: { ...corsHeaders, 'Content-Type': 'application/json' }
|
| 150 |
}
|
| 151 |
);
|
| 152 |
}
|
| 153 |
+
});
|
supabase/functions/guess-word/index.ts
CHANGED
|
@@ -29,85 +29,112 @@ const languagePrompts = {
|
|
| 29 |
}
|
| 30 |
};
|
| 31 |
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
|
|
|
| 37 |
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
|
|
|
| 41 |
|
| 42 |
-
|
| 43 |
-
apiKey: Deno.env.get('MISTRAL_API_KEY'),
|
| 44 |
-
});
|
| 45 |
|
| 46 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 47 |
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
let lastError = null;
|
| 51 |
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
model: "mistral-large-latest",
|
| 56 |
-
messages: [
|
| 57 |
-
{
|
| 58 |
-
role: "system",
|
| 59 |
-
content: `${prompts.systemPrompt} Respond with ONLY the word you think is being described, in uppercase letters. Do not add any explanation or punctuation.`
|
| 60 |
-
},
|
| 61 |
-
{
|
| 62 |
-
role: "user",
|
| 63 |
-
content: `${prompts.instruction} "${sentence}"`
|
| 64 |
-
}
|
| 65 |
-
],
|
| 66 |
-
maxTokens: 50,
|
| 67 |
-
temperature: 0.1
|
| 68 |
-
});
|
| 69 |
|
| 70 |
-
|
| 71 |
-
console.log('AI guess:', guess);
|
| 72 |
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 87 |
}
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
|
|
|
|
|
|
|
|
|
|
| 92 |
|
| 93 |
-
|
|
|
|
|
|
|
| 94 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 95 |
} catch (error) {
|
| 96 |
console.error('Error generating guess:', error);
|
| 97 |
-
|
| 98 |
-
const errorMessage = error.message?.includes('rate limit')
|
| 99 |
-
? "The AI service is currently busy. Please try again in a few moments."
|
| 100 |
-
: "Sorry, there was an error generating the guess. Please try again.";
|
| 101 |
-
|
| 102 |
return new Response(
|
| 103 |
-
JSON.stringify({
|
| 104 |
-
error: errorMessage,
|
| 105 |
-
details: error.message
|
| 106 |
-
}),
|
| 107 |
{
|
| 108 |
-
status:
|
| 109 |
headers: { ...corsHeaders, 'Content-Type': 'application/json' }
|
| 110 |
}
|
| 111 |
);
|
| 112 |
}
|
| 113 |
-
});
|
|
|
|
| 29 |
}
|
| 30 |
};
|
| 31 |
|
| 32 |
+
const openRouterModels = [
|
| 33 |
+
'sophosympatheia/rogue-rose-103b-v0.2:free',
|
| 34 |
+
'google/gemini-2.0-flash-exp:free',
|
| 35 |
+
'meta-llama/llama-3.1-70b-instruct:free',
|
| 36 |
+
'microsoft/phi-3-medium-128k-instruct:free'
|
| 37 |
+
];
|
| 38 |
|
| 39 |
+
async function tryMistral(sentence: string, language: string) {
|
| 40 |
+
const client = new Mistral({
|
| 41 |
+
apiKey: Deno.env.get('MISTRAL_API_KEY'),
|
| 42 |
+
});
|
| 43 |
|
| 44 |
+
const prompts = languagePrompts[language as keyof typeof languagePrompts] || languagePrompts.en;
|
|
|
|
|
|
|
| 45 |
|
| 46 |
+
const response = await client.chat.complete({
|
| 47 |
+
model: "mistral-medium-latest",
|
| 48 |
+
messages: [
|
| 49 |
+
{
|
| 50 |
+
role: "system",
|
| 51 |
+
content: `${prompts.systemPrompt} Respond with ONLY the word you think is being described, in uppercase letters. Do not add any explanation or punctuation.`
|
| 52 |
+
},
|
| 53 |
+
{
|
| 54 |
+
role: "user",
|
| 55 |
+
content: `${prompts.instruction} "${sentence}"`
|
| 56 |
+
}
|
| 57 |
+
],
|
| 58 |
+
maxTokens: 50,
|
| 59 |
+
temperature: 0.1
|
| 60 |
+
});
|
| 61 |
|
| 62 |
+
return response.choices[0].message.content.trim().toUpperCase();
|
| 63 |
+
}
|
|
|
|
| 64 |
|
| 65 |
+
async function tryOpenRouter(sentence: string, language: string) {
|
| 66 |
+
const prompts = languagePrompts[language as keyof typeof languagePrompts] || languagePrompts.en;
|
| 67 |
+
const randomModel = openRouterModels[Math.floor(Math.random() * openRouterModels.length)];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 68 |
|
| 69 |
+
console.log('Trying OpenRouter with model:', randomModel);
|
|
|
|
| 70 |
|
| 71 |
+
const response = await fetch("https://openrouter.ai/api/v1/chat/completions", {
|
| 72 |
+
method: "POST",
|
| 73 |
+
headers: {
|
| 74 |
+
"Authorization": `Bearer ${Deno.env.get('OPENROUTER_API_KEY')}`,
|
| 75 |
+
"HTTP-Referer": "https://think-in-sync.com",
|
| 76 |
+
"X-Title": "Think in Sync",
|
| 77 |
+
"Content-Type": "application/json"
|
| 78 |
+
},
|
| 79 |
+
body: JSON.stringify({
|
| 80 |
+
model: randomModel,
|
| 81 |
+
messages: [
|
| 82 |
+
{
|
| 83 |
+
role: "system",
|
| 84 |
+
content: `${prompts.systemPrompt} Respond with ONLY the word you think is being described, in uppercase letters. Do not add any explanation or punctuation.`
|
| 85 |
+
},
|
| 86 |
+
{
|
| 87 |
+
role: "user",
|
| 88 |
+
content: `${prompts.instruction} "${sentence}"`
|
| 89 |
}
|
| 90 |
+
]
|
| 91 |
+
})
|
| 92 |
+
});
|
| 93 |
+
|
| 94 |
+
if (!response.ok) {
|
| 95 |
+
throw new Error(`OpenRouter API error: ${response.status}`);
|
| 96 |
+
}
|
| 97 |
|
| 98 |
+
const data = await response.json();
|
| 99 |
+
return data.choices[0].message.content.trim().toUpperCase();
|
| 100 |
+
}
|
| 101 |
|
| 102 |
+
serve(async (req) => {
|
| 103 |
+
if (req.method === 'OPTIONS') {
|
| 104 |
+
return new Response(null, { headers: corsHeaders });
|
| 105 |
+
}
|
| 106 |
+
|
| 107 |
+
try {
|
| 108 |
+
const { sentence, language = 'en' } = await req.json();
|
| 109 |
+
console.log('Trying to guess word from sentence:', sentence, 'language:', language);
|
| 110 |
+
|
| 111 |
+
try {
|
| 112 |
+
console.log('Attempting with Mistral...');
|
| 113 |
+
const guess = await tryMistral(sentence, language);
|
| 114 |
+
console.log('Successfully generated guess with Mistral:', guess);
|
| 115 |
+
return new Response(
|
| 116 |
+
JSON.stringify({ guess }),
|
| 117 |
+
{ headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
|
| 118 |
+
);
|
| 119 |
+
} catch (mistralError) {
|
| 120 |
+
console.error('Mistral error:', mistralError);
|
| 121 |
+
console.log('Falling back to OpenRouter...');
|
| 122 |
+
|
| 123 |
+
const guess = await tryOpenRouter(sentence, language);
|
| 124 |
+
console.log('Successfully generated guess with OpenRouter:', guess);
|
| 125 |
+
return new Response(
|
| 126 |
+
JSON.stringify({ guess }),
|
| 127 |
+
{ headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
|
| 128 |
+
);
|
| 129 |
+
}
|
| 130 |
} catch (error) {
|
| 131 |
console.error('Error generating guess:', error);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 132 |
return new Response(
|
| 133 |
+
JSON.stringify({ error: error.message }),
|
|
|
|
|
|
|
|
|
|
| 134 |
{
|
| 135 |
+
status: 500,
|
| 136 |
headers: { ...corsHeaders, 'Content-Type': 'application/json' }
|
| 137 |
}
|
| 138 |
);
|
| 139 |
}
|
| 140 |
+
});
|
supabase/functions/validate-sentence/index.ts
CHANGED
|
@@ -20,7 +20,7 @@ serve(async (req) => {
|
|
| 20 |
});
|
| 21 |
|
| 22 |
const response = await client.chat.complete({
|
| 23 |
-
model: "mistral-
|
| 24 |
messages: [
|
| 25 |
{
|
| 26 |
role: "system",
|
|
|
|
| 20 |
});
|
| 21 |
|
| 22 |
const response = await client.chat.complete({
|
| 23 |
+
model: "mistral-medium-latest",
|
| 24 |
messages: [
|
| 25 |
{
|
| 26 |
role: "system",
|