Felix Zieger
commited on
Commit
·
47fea40
1
Parent(s):
5bd3ab2
larger models again
Browse files- src/components/GameContainer.tsx +2 -2
- src/components/HighScoreBoard.tsx +3 -0
- src/components/game/GuessDisplay.tsx +66 -14
- src/components/game/ThemeSelector.tsx +9 -29
- src/lib/words.ts +232 -3
- src/services/mistralService.ts +10 -3
- supabase/functions/detect-fraud/index.ts +52 -34
- supabase/functions/generate-themed-word/index.ts +57 -16
- supabase/functions/generate-word/index.ts +2 -2
- supabase/functions/guess-word/index.ts +1 -1
- supabase/functions/validate-sentence/index.ts +1 -1
src/components/GameContainer.tsx
CHANGED
@@ -78,7 +78,7 @@ export const GameContainer = () => {
|
|
78 |
setCurrentTheme(theme);
|
79 |
try {
|
80 |
const word = theme === "standard" ?
|
81 |
-
getRandomWord() :
|
82 |
await getThemedWord(theme, usedWords, language);
|
83 |
setCurrentWord(word);
|
84 |
setGameState("building-sentence");
|
@@ -158,7 +158,7 @@ export const GameContainer = () => {
|
|
158 |
const getNewWord = async () => {
|
159 |
try {
|
160 |
const word = currentTheme === "standard" ?
|
161 |
-
getRandomWord() :
|
162 |
await getThemedWord(currentTheme, usedWords, language);
|
163 |
setCurrentWord(word);
|
164 |
setGameState("building-sentence");
|
|
|
78 |
setCurrentTheme(theme);
|
79 |
try {
|
80 |
const word = theme === "standard" ?
|
81 |
+
getRandomWord(language) :
|
82 |
await getThemedWord(theme, usedWords, language);
|
83 |
setCurrentWord(word);
|
84 |
setGameState("building-sentence");
|
|
|
158 |
const getNewWord = async () => {
|
159 |
try {
|
160 |
const word = currentTheme === "standard" ?
|
161 |
+
getRandomWord(language) :
|
162 |
await getThemedWord(currentTheme, usedWords, language);
|
163 |
setCurrentWord(word);
|
164 |
setGameState("building-sentence");
|
src/components/HighScoreBoard.tsx
CHANGED
@@ -37,6 +37,7 @@ interface HighScoreBoardProps {
|
|
37 |
onClose: () => void;
|
38 |
onPlayAgain: () => void;
|
39 |
sessionId: string;
|
|
|
40 |
}
|
41 |
|
42 |
const ITEMS_PER_PAGE = 5;
|
@@ -60,6 +61,7 @@ export const HighScoreBoard = ({
|
|
60 |
avgWordsPerRound,
|
61 |
onClose,
|
62 |
sessionId,
|
|
|
63 |
}: HighScoreBoardProps) => {
|
64 |
const [playerName, setPlayerName] = useState("");
|
65 |
const [isSubmitting, setIsSubmitting] = useState(false);
|
@@ -188,6 +190,7 @@ export const HighScoreBoard = ({
|
|
188 |
}
|
189 |
|
190 |
setHasSubmitted(true);
|
|
|
191 |
setPlayerName("");
|
192 |
} catch (error) {
|
193 |
console.error("Error submitting score:", error);
|
|
|
37 |
onClose: () => void;
|
38 |
onPlayAgain: () => void;
|
39 |
sessionId: string;
|
40 |
+
onScoreSubmitted?: () => void;
|
41 |
}
|
42 |
|
43 |
const ITEMS_PER_PAGE = 5;
|
|
|
61 |
avgWordsPerRound,
|
62 |
onClose,
|
63 |
sessionId,
|
64 |
+
onScoreSubmitted,
|
65 |
}: HighScoreBoardProps) => {
|
66 |
const [playerName, setPlayerName] = useState("");
|
67 |
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
|
190 |
}
|
191 |
|
192 |
setHasSubmitted(true);
|
193 |
+
onScoreSubmitted?.();
|
194 |
setPlayerName("");
|
195 |
} catch (error) {
|
196 |
console.error("Error submitting score:", error);
|
src/components/game/GuessDisplay.tsx
CHANGED
@@ -10,6 +10,16 @@ import { useState, useEffect } from "react";
|
|
10 |
import { useTranslation } from "@/hooks/useTranslation";
|
11 |
import { supabase } from "@/integrations/supabase/client";
|
12 |
import { House } from "lucide-react";
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
13 |
|
14 |
interface GuessDisplayProps {
|
15 |
sentence: string[];
|
@@ -37,6 +47,8 @@ export const GuessDisplay = ({
|
|
37 |
const isGuessCorrect = () => aiGuess.toLowerCase() === currentWord.toLowerCase();
|
38 |
const isCheating = () => aiGuess === 'CHEATING';
|
39 |
const [isDialogOpen, setIsDialogOpen] = useState(false);
|
|
|
|
|
40 |
const t = useTranslation();
|
41 |
|
42 |
useEffect(() => {
|
@@ -48,7 +60,8 @@ export const GuessDisplay = ({
|
|
48 |
target_word: currentWord,
|
49 |
description: sentence.join(' '),
|
50 |
ai_guess: aiGuess,
|
51 |
-
is_correct: isGuessCorrect()
|
|
|
52 |
});
|
53 |
|
54 |
if (error) {
|
@@ -64,39 +77,60 @@ export const GuessDisplay = ({
|
|
64 |
saveGameResult();
|
65 |
}, []);
|
66 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
67 |
return (
|
68 |
<motion.div
|
69 |
initial={{ opacity: 0 }}
|
70 |
animate={{ opacity: 1 }}
|
71 |
className="text-center relative space-y-6"
|
72 |
>
|
73 |
-
<div className="flex items-center justify-between
|
74 |
<Button
|
75 |
variant="ghost"
|
76 |
size="icon"
|
77 |
-
onClick={
|
78 |
className="text-gray-600 hover:text-primary"
|
79 |
>
|
80 |
<House className="h-5 w-5" />
|
81 |
</Button>
|
|
|
82 |
<div className="bg-primary/10 px-3 py-1 rounded-lg">
|
83 |
<span className="text-sm font-medium text-primary">
|
84 |
{t.game.round} {currentScore + 1}
|
85 |
</span>
|
86 |
</div>
|
87 |
-
<div className="w-8" /> {/* Spacer for centering */}
|
88 |
</div>
|
89 |
|
90 |
-
<div>
|
91 |
-
<
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
<p className="p-4 text-2xl font-bold tracking-wider text-secondary">
|
97 |
-
{currentWord}
|
98 |
-
</p>
|
99 |
-
</div>
|
100 |
</div>
|
101 |
</div>
|
102 |
|
@@ -148,6 +182,7 @@ export const GuessDisplay = ({
|
|
148 |
onPlayAgain();
|
149 |
}}
|
150 |
sessionId={sessionId}
|
|
|
151 |
/>
|
152 |
</DialogContent>
|
153 |
</Dialog>
|
@@ -160,6 +195,23 @@ export const GuessDisplay = ({
|
|
160 |
</>
|
161 |
)}
|
162 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
163 |
</motion.div>
|
164 |
);
|
165 |
};
|
|
|
10 |
import { useTranslation } from "@/hooks/useTranslation";
|
11 |
import { supabase } from "@/integrations/supabase/client";
|
12 |
import { House } from "lucide-react";
|
13 |
+
import {
|
14 |
+
AlertDialog,
|
15 |
+
AlertDialogAction,
|
16 |
+
AlertDialogCancel,
|
17 |
+
AlertDialogContent,
|
18 |
+
AlertDialogDescription,
|
19 |
+
AlertDialogFooter,
|
20 |
+
AlertDialogHeader,
|
21 |
+
AlertDialogTitle,
|
22 |
+
} from "@/components/ui/alert-dialog";
|
23 |
|
24 |
interface GuessDisplayProps {
|
25 |
sentence: string[];
|
|
|
47 |
const isGuessCorrect = () => aiGuess.toLowerCase() === currentWord.toLowerCase();
|
48 |
const isCheating = () => aiGuess === 'CHEATING';
|
49 |
const [isDialogOpen, setIsDialogOpen] = useState(false);
|
50 |
+
const [showConfirmDialog, setShowConfirmDialog] = useState(false);
|
51 |
+
const [hasSubmittedScore, setHasSubmittedScore] = useState(false);
|
52 |
const t = useTranslation();
|
53 |
|
54 |
useEffect(() => {
|
|
|
60 |
target_word: currentWord,
|
61 |
description: sentence.join(' '),
|
62 |
ai_guess: aiGuess,
|
63 |
+
is_correct: isGuessCorrect(),
|
64 |
+
session_id: sessionId
|
65 |
});
|
66 |
|
67 |
if (error) {
|
|
|
77 |
saveGameResult();
|
78 |
}, []);
|
79 |
|
80 |
+
const handleHomeClick = () => {
|
81 |
+
console.log('Home button clicked', { currentScore, hasSubmittedScore });
|
82 |
+
if (currentScore > 0 && !hasSubmittedScore) {
|
83 |
+
setShowConfirmDialog(true);
|
84 |
+
} else {
|
85 |
+
if (onBack) {
|
86 |
+
console.log('Navigating back to welcome screen');
|
87 |
+
onBack();
|
88 |
+
}
|
89 |
+
}
|
90 |
+
};
|
91 |
+
|
92 |
+
const handleScoreSubmitted = () => {
|
93 |
+
console.log('Score submitted, updating state');
|
94 |
+
setHasSubmittedScore(true);
|
95 |
+
};
|
96 |
+
|
97 |
+
const handleConfirmHome = () => {
|
98 |
+
console.log('Confirmed navigation to home');
|
99 |
+
setShowConfirmDialog(false);
|
100 |
+
if (onBack) {
|
101 |
+
onBack();
|
102 |
+
}
|
103 |
+
};
|
104 |
+
|
105 |
return (
|
106 |
<motion.div
|
107 |
initial={{ opacity: 0 }}
|
108 |
animate={{ opacity: 1 }}
|
109 |
className="text-center relative space-y-6"
|
110 |
>
|
111 |
+
<div className="flex items-center justify-between">
|
112 |
<Button
|
113 |
variant="ghost"
|
114 |
size="icon"
|
115 |
+
onClick={handleHomeClick}
|
116 |
className="text-gray-600 hover:text-primary"
|
117 |
>
|
118 |
<House className="h-5 w-5" />
|
119 |
</Button>
|
120 |
+
<h2 className="text-2xl font-semibold text-gray-900">Think in Sync</h2>
|
121 |
<div className="bg-primary/10 px-3 py-1 rounded-lg">
|
122 |
<span className="text-sm font-medium text-primary">
|
123 |
{t.game.round} {currentScore + 1}
|
124 |
</span>
|
125 |
</div>
|
|
|
126 |
</div>
|
127 |
|
128 |
+
<div className="space-y-2">
|
129 |
+
<p className="text-sm text-gray-600">{t.guess.goalDescription}</p>
|
130 |
+
<div className="overflow-hidden rounded-lg bg-secondary/10">
|
131 |
+
<p className="p-4 text-2xl font-bold tracking-wider text-secondary">
|
132 |
+
{currentWord}
|
133 |
+
</p>
|
|
|
|
|
|
|
|
|
134 |
</div>
|
135 |
</div>
|
136 |
|
|
|
182 |
onPlayAgain();
|
183 |
}}
|
184 |
sessionId={sessionId}
|
185 |
+
onScoreSubmitted={handleScoreSubmitted}
|
186 |
/>
|
187 |
</DialogContent>
|
188 |
</Dialog>
|
|
|
195 |
</>
|
196 |
)}
|
197 |
</div>
|
198 |
+
|
199 |
+
<AlertDialog open={showConfirmDialog} onOpenChange={setShowConfirmDialog}>
|
200 |
+
<AlertDialogContent>
|
201 |
+
<AlertDialogHeader>
|
202 |
+
<AlertDialogTitle>{t.game.leaveGameTitle}</AlertDialogTitle>
|
203 |
+
<AlertDialogDescription>
|
204 |
+
{t.game.leaveGameDescription}
|
205 |
+
</AlertDialogDescription>
|
206 |
+
</AlertDialogHeader>
|
207 |
+
<AlertDialogFooter>
|
208 |
+
<AlertDialogCancel>{t.game.cancel}</AlertDialogCancel>
|
209 |
+
<AlertDialogAction onClick={handleConfirmHome}>
|
210 |
+
{t.game.confirm}
|
211 |
+
</AlertDialogAction>
|
212 |
+
</AlertDialogFooter>
|
213 |
+
</AlertDialogContent>
|
214 |
+
</AlertDialog>
|
215 |
</motion.div>
|
216 |
);
|
217 |
};
|
src/components/game/ThemeSelector.tsx
CHANGED
@@ -22,23 +22,13 @@ export const ThemeSelector = ({ onThemeSelect, onBack }: ThemeSelectorProps) =>
|
|
22 |
const t = useTranslation();
|
23 |
const { language } = useContext(LanguageContext);
|
24 |
|
25 |
-
useEffect(() => {
|
26 |
-
if (language !== 'en') {
|
27 |
-
setSelectedTheme("technology");
|
28 |
-
}
|
29 |
-
}, [language]);
|
30 |
-
|
31 |
useEffect(() => {
|
32 |
const handleKeyPress = (e: KeyboardEvent) => {
|
33 |
if (e.target instanceof HTMLInputElement) return;
|
34 |
|
35 |
switch(e.key.toLowerCase()) {
|
36 |
case 'a':
|
37 |
-
|
38 |
-
setSelectedTheme("standard");
|
39 |
-
} else {
|
40 |
-
setSelectedTheme("technology");
|
41 |
-
}
|
42 |
break;
|
43 |
case 'b':
|
44 |
setSelectedTheme("sports");
|
@@ -112,24 +102,14 @@ export const ThemeSelector = ({ onThemeSelect, onBack }: ThemeSelectorProps) =>
|
|
112 |
|
113 |
<p className="text-gray-600 text-center">{t.themes.subtitle}</p>
|
114 |
|
115 |
-
<div className="space-y-4">
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
>
|
122 |
-
|
123 |
-
</Button>
|
124 |
-
) : (
|
125 |
-
<Button
|
126 |
-
variant={selectedTheme === "technology" ? "default" : "outline"}
|
127 |
-
className="w-full justify-between"
|
128 |
-
onClick={() => setSelectedTheme("technology")}
|
129 |
-
>
|
130 |
-
{t.themes.technology} <span className="text-sm opacity-50">{t.themes.pressKey} A</span>
|
131 |
-
</Button>
|
132 |
-
)}
|
133 |
|
134 |
<Button
|
135 |
variant={selectedTheme === "sports" ? "default" : "outline"}
|
|
|
22 |
const t = useTranslation();
|
23 |
const { language } = useContext(LanguageContext);
|
24 |
|
|
|
|
|
|
|
|
|
|
|
|
|
25 |
useEffect(() => {
|
26 |
const handleKeyPress = (e: KeyboardEvent) => {
|
27 |
if (e.target instanceof HTMLInputElement) return;
|
28 |
|
29 |
switch(e.key.toLowerCase()) {
|
30 |
case 'a':
|
31 |
+
setSelectedTheme("standard");
|
|
|
|
|
|
|
|
|
32 |
break;
|
33 |
case 'b':
|
34 |
setSelectedTheme("sports");
|
|
|
102 |
|
103 |
<p className="text-gray-600 text-center">{t.themes.subtitle}</p>
|
104 |
|
105 |
+
<div className="space-y-4">
|
106 |
+
<Button
|
107 |
+
variant={selectedTheme === "standard" ? "default" : "outline"}
|
108 |
+
className="w-full justify-between"
|
109 |
+
onClick={() => setSelectedTheme("standard")}
|
110 |
+
>
|
111 |
+
{t.themes.standard} <span className="text-sm opacity-50">{t.themes.pressKey} A</span>
|
112 |
+
</Button>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
113 |
|
114 |
<Button
|
115 |
variant={selectedTheme === "sports" ? "default" : "outline"}
|
src/lib/words.ts
CHANGED
@@ -1,4 +1,4 @@
|
|
1 |
-
export const
|
2 |
"DOG",
|
3 |
"CAT",
|
4 |
"SUN",
|
@@ -103,6 +103,235 @@ export const words = [
|
|
103 |
"BALLON"
|
104 |
];
|
105 |
|
106 |
-
export const
|
107 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
108 |
};
|
|
|
1 |
+
export const englishWords = [
|
2 |
"DOG",
|
3 |
"CAT",
|
4 |
"SUN",
|
|
|
103 |
"BALLON"
|
104 |
];
|
105 |
|
106 |
+
export const germanWords = [
|
107 |
+
"HUND",
|
108 |
+
"KATZE",
|
109 |
+
"SONNE",
|
110 |
+
"REGEN",
|
111 |
+
"BAUM",
|
112 |
+
"STERN",
|
113 |
+
"MOND",
|
114 |
+
"FISCH",
|
115 |
+
"VOGEL",
|
116 |
+
"WOLKE",
|
117 |
+
"HIMMEL",
|
118 |
+
"WIND",
|
119 |
+
"SCHNEE",
|
120 |
+
"BLUME",
|
121 |
+
"WASSER",
|
122 |
+
"OZEAN",
|
123 |
+
"FLUSS",
|
124 |
+
"BERG",
|
125 |
+
"WALD",
|
126 |
+
"HAUS",
|
127 |
+
"KERZE",
|
128 |
+
"GARTEN",
|
129 |
+
"BRÜCKE",
|
130 |
+
"INSEL",
|
131 |
+
"LICHT",
|
132 |
+
"DONNER",
|
133 |
+
"LÄCHELN",
|
134 |
+
"FREUND",
|
135 |
+
"FAMILIE",
|
136 |
+
"APFEL",
|
137 |
+
"BANANE",
|
138 |
+
"AUTO",
|
139 |
+
"BOOT",
|
140 |
+
"BALL",
|
141 |
+
"KUCHEN",
|
142 |
+
"FROSCH",
|
143 |
+
"PFERD",
|
144 |
+
"LÖWE",
|
145 |
+
"AFFE",
|
146 |
+
"PANDA",
|
147 |
+
"FLUGZEUG",
|
148 |
+
"ZUG",
|
149 |
+
"BONBON",
|
150 |
+
"SPRINGEN",
|
151 |
+
"SPIELEN",
|
152 |
+
"SCHLAFEN",
|
153 |
+
"LACHEN",
|
154 |
+
"TRAUM",
|
155 |
+
"GLÜCK",
|
156 |
+
"FARBE"
|
157 |
+
];
|
158 |
+
|
159 |
+
export const frenchWords = [
|
160 |
+
"CHIEN",
|
161 |
+
"CHAT",
|
162 |
+
"SOLEIL",
|
163 |
+
"PLUIE",
|
164 |
+
"ARBRE",
|
165 |
+
"ÉTOILE",
|
166 |
+
"LUNE",
|
167 |
+
"POISSON",
|
168 |
+
"OISEAU",
|
169 |
+
"NUAGE",
|
170 |
+
"CIEL",
|
171 |
+
"VENT",
|
172 |
+
"NEIGE",
|
173 |
+
"FLEUR",
|
174 |
+
"EAU",
|
175 |
+
"OCÉAN",
|
176 |
+
"RIVIÈRE",
|
177 |
+
"MONTAGNE",
|
178 |
+
"FORÊT",
|
179 |
+
"MAISON",
|
180 |
+
"BOUGIE",
|
181 |
+
"JARDIN",
|
182 |
+
"PONT",
|
183 |
+
"ÎLE",
|
184 |
+
"LUMIÈRE",
|
185 |
+
"TONNERRE",
|
186 |
+
"SOURIRE",
|
187 |
+
"AMI",
|
188 |
+
"FAMILLE",
|
189 |
+
"POMME",
|
190 |
+
"BANANE",
|
191 |
+
"VOITURE",
|
192 |
+
"BATEAU",
|
193 |
+
"BALLON",
|
194 |
+
"GÂTEAU",
|
195 |
+
"GRENOUILLE",
|
196 |
+
"CHEVAL",
|
197 |
+
"LION",
|
198 |
+
"SINGE",
|
199 |
+
"PANDA",
|
200 |
+
"AVION",
|
201 |
+
"TRAIN",
|
202 |
+
"BONBON",
|
203 |
+
"SAUTER",
|
204 |
+
"JOUER",
|
205 |
+
"DORMIR",
|
206 |
+
"RIRE",
|
207 |
+
"RÊVE",
|
208 |
+
"BONHEUR",
|
209 |
+
"COULEUR"
|
210 |
+
];
|
211 |
+
|
212 |
+
export const italianWords = [
|
213 |
+
"CANE",
|
214 |
+
"GATTO",
|
215 |
+
"SOLE",
|
216 |
+
"PIOGGIA",
|
217 |
+
"ALBERO",
|
218 |
+
"STELLA",
|
219 |
+
"LUNA",
|
220 |
+
"PESCE",
|
221 |
+
"UCCELLO",
|
222 |
+
"NUVOLA",
|
223 |
+
"CIELO",
|
224 |
+
"VENTO",
|
225 |
+
"NEVE",
|
226 |
+
"FIORE",
|
227 |
+
"ACQUA",
|
228 |
+
"OCEANO",
|
229 |
+
"FIUME",
|
230 |
+
"MONTAGNA",
|
231 |
+
"FORESTA",
|
232 |
+
"CASA",
|
233 |
+
"CANDELA",
|
234 |
+
"GIARDINO",
|
235 |
+
"PONTE",
|
236 |
+
"ISOLA",
|
237 |
+
"LUCE",
|
238 |
+
"TUONO",
|
239 |
+
"SORRISO",
|
240 |
+
"AMICO",
|
241 |
+
"FAMIGLIA",
|
242 |
+
"MELA",
|
243 |
+
"BANANA",
|
244 |
+
"AUTO",
|
245 |
+
"BARCA",
|
246 |
+
"PALLA",
|
247 |
+
"TORTA",
|
248 |
+
"RANA",
|
249 |
+
"CAVALLO",
|
250 |
+
"LEONE",
|
251 |
+
"SCIMMIA",
|
252 |
+
"PANDA",
|
253 |
+
"AEREO",
|
254 |
+
"TRENO",
|
255 |
+
"CARAMELLA",
|
256 |
+
"SALTARE",
|
257 |
+
"GIOCARE",
|
258 |
+
"DORMIRE",
|
259 |
+
"RIDERE",
|
260 |
+
"SOGNO",
|
261 |
+
"FELICITÀ",
|
262 |
+
"COLORE"
|
263 |
+
];
|
264 |
+
|
265 |
+
export const spanishWords = [
|
266 |
+
"PERRO",
|
267 |
+
"GATO",
|
268 |
+
"SOL",
|
269 |
+
"LLUVIA",
|
270 |
+
"ÁRBOL",
|
271 |
+
"ESTRELLA",
|
272 |
+
"LUNA",
|
273 |
+
"PEZ",
|
274 |
+
"PÁJARO",
|
275 |
+
"NUBE",
|
276 |
+
"CIELO",
|
277 |
+
"VIENTO",
|
278 |
+
"NIEVE",
|
279 |
+
"FLOR",
|
280 |
+
"AGUA",
|
281 |
+
"OCÉANO",
|
282 |
+
"RÍO",
|
283 |
+
"MONTAÑA",
|
284 |
+
"BOSQUE",
|
285 |
+
"CASA",
|
286 |
+
"VELA",
|
287 |
+
"JARDÍN",
|
288 |
+
"PUENTE",
|
289 |
+
"ISLA",
|
290 |
+
"LUZ",
|
291 |
+
"TRUENO",
|
292 |
+
"SONRISA",
|
293 |
+
"AMIGO",
|
294 |
+
"FAMILIA",
|
295 |
+
"MANZANA",
|
296 |
+
"PLÁTANO",
|
297 |
+
"COCHE",
|
298 |
+
"BARCO",
|
299 |
+
"PELOTA",
|
300 |
+
"PASTEL",
|
301 |
+
"RANA",
|
302 |
+
"CABALLO",
|
303 |
+
"LEÓN",
|
304 |
+
"MONO",
|
305 |
+
"PANDA",
|
306 |
+
"AVIÓN",
|
307 |
+
"TREN",
|
308 |
+
"CARAMELO",
|
309 |
+
"SALTAR",
|
310 |
+
"JUGAR",
|
311 |
+
"DORMIR",
|
312 |
+
"REÍR",
|
313 |
+
"SUEÑO",
|
314 |
+
"FELICIDAD",
|
315 |
+
"COLOR"
|
316 |
+
];
|
317 |
+
|
318 |
+
export const getRandomWord = (language: string = 'en') => {
|
319 |
+
let wordList;
|
320 |
+
switch (language) {
|
321 |
+
case 'de':
|
322 |
+
wordList = germanWords;
|
323 |
+
break;
|
324 |
+
case 'fr':
|
325 |
+
wordList = frenchWords;
|
326 |
+
break;
|
327 |
+
case 'it':
|
328 |
+
wordList = italianWords;
|
329 |
+
break;
|
330 |
+
case 'es':
|
331 |
+
wordList = spanishWords;
|
332 |
+
break;
|
333 |
+
default:
|
334 |
+
wordList = englishWords;
|
335 |
+
}
|
336 |
+
return wordList[Math.floor(Math.random() * wordList.length)];
|
337 |
};
|
src/services/mistralService.ts
CHANGED
@@ -31,8 +31,11 @@ export const generateAIResponse = async (currentWord: string, currentSentence: s
|
|
31 |
export const guessWord = async (sentence: string, language: string): Promise<string> => {
|
32 |
console.log('Processing guess for sentence:', sentence);
|
33 |
|
34 |
-
//
|
35 |
const words = sentence.trim().split(/\s+/);
|
|
|
|
|
|
|
36 |
if (words.length < 3) {
|
37 |
console.log('Short description detected, checking for fraud...');
|
38 |
|
@@ -40,7 +43,7 @@ export const guessWord = async (sentence: string, language: string): Promise<str
|
|
40 |
const { data: fraudData, error: fraudError } = await supabase.functions.invoke('detect-fraud', {
|
41 |
body: {
|
42 |
sentence,
|
43 |
-
targetWord
|
44 |
language
|
45 |
}
|
46 |
});
|
@@ -60,7 +63,11 @@ export const guessWord = async (sentence: string, language: string): Promise<str
|
|
60 |
console.log('Calling guess-word function with sentence:', sentence, 'language:', language);
|
61 |
|
62 |
const { data, error } = await supabase.functions.invoke('guess-word', {
|
63 |
-
body: {
|
|
|
|
|
|
|
|
|
64 |
});
|
65 |
|
66 |
if (error) {
|
|
|
31 |
export const guessWord = async (sentence: string, language: string): Promise<string> => {
|
32 |
console.log('Processing guess for sentence:', sentence);
|
33 |
|
34 |
+
// Extract the target word from the sentence (assuming it's the first word)
|
35 |
const words = sentence.trim().split(/\s+/);
|
36 |
+
const targetWord = words[0].toLowerCase();
|
37 |
+
|
38 |
+
// Check for potential fraud if the sentence has less than 3 words
|
39 |
if (words.length < 3) {
|
40 |
console.log('Short description detected, checking for fraud...');
|
41 |
|
|
|
43 |
const { data: fraudData, error: fraudError } = await supabase.functions.invoke('detect-fraud', {
|
44 |
body: {
|
45 |
sentence,
|
46 |
+
targetWord,
|
47 |
language
|
48 |
}
|
49 |
});
|
|
|
63 |
console.log('Calling guess-word function with sentence:', sentence, 'language:', language);
|
64 |
|
65 |
const { data, error } = await supabase.functions.invoke('guess-word', {
|
66 |
+
body: {
|
67 |
+
sentence,
|
68 |
+
targetWord, // Pass the target word to prevent guessing it
|
69 |
+
language
|
70 |
+
}
|
71 |
});
|
72 |
|
73 |
if (error) {
|
supabase/functions/detect-fraud/index.ts
CHANGED
@@ -6,6 +6,8 @@ const corsHeaders = {
|
|
6 |
'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',
|
7 |
};
|
8 |
|
|
|
|
|
9 |
serve(async (req) => {
|
10 |
// Handle CORS preflight requests
|
11 |
if (req.method === 'OPTIONS') {
|
@@ -26,46 +28,53 @@ serve(async (req) => {
|
|
26 |
|
27 |
while (retryCount < maxRetries) {
|
28 |
try {
|
|
|
|
|
29 |
const response = await client.chat.complete({
|
30 |
-
model: "mistral-
|
31 |
messages: [
|
32 |
{
|
33 |
role: "system",
|
34 |
content: `You are a fraud detection system for a word guessing game.
|
35 |
The game is being played in ${language}.
|
36 |
-
Your task is to detect if a player is trying to cheat by:
|
37 |
-
1.
|
38 |
-
2.
|
39 |
|
40 |
Examples for cheating:
|
41 |
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
|
52 |
Synonyms and names of instances of a class are legitimate descriptions.
|
53 |
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
|
|
|
|
|
|
|
|
|
|
69 |
Respond with ONLY "cheating" or "legitimate" (no punctuation or explanation).`
|
70 |
},
|
71 |
{
|
@@ -81,6 +90,10 @@ serve(async (req) => {
|
|
81 |
temperature: 0.1
|
82 |
});
|
83 |
|
|
|
|
|
|
|
|
|
84 |
const verdict = response.choices[0].message.content.trim().toLowerCase();
|
85 |
console.log('Fraud detection verdict:', verdict);
|
86 |
|
@@ -92,24 +105,29 @@ serve(async (req) => {
|
|
92 |
console.error(`Attempt ${retryCount + 1} failed:`, error);
|
93 |
lastError = error;
|
94 |
|
95 |
-
if
|
96 |
-
|
97 |
-
|
98 |
-
|
|
|
|
|
|
|
99 |
retryCount++;
|
100 |
continue;
|
101 |
}
|
102 |
|
|
|
103 |
throw error;
|
104 |
}
|
105 |
}
|
106 |
|
|
|
107 |
throw new Error(`Failed after ${maxRetries} attempts. Last error: ${lastError?.message}`);
|
108 |
|
109 |
} catch (error) {
|
110 |
console.error('Error in fraud detection:', error);
|
111 |
|
112 |
-
const errorMessage = error.message?.includes('rate limit')
|
113 |
? "The AI service is currently busy. Please try again in a few moments."
|
114 |
: "Sorry, there was an error checking for fraud. Please try again.";
|
115 |
|
@@ -119,7 +137,7 @@ serve(async (req) => {
|
|
119 |
details: error.message
|
120 |
}),
|
121 |
{
|
122 |
-
status: error.message?.includes('rate limit') ? 429 : 500,
|
123 |
headers: { ...corsHeaders, 'Content-Type': 'application/json' }
|
124 |
}
|
125 |
);
|
|
|
6 |
'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',
|
7 |
};
|
8 |
|
9 |
+
const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
|
10 |
+
|
11 |
serve(async (req) => {
|
12 |
// Handle CORS preflight requests
|
13 |
if (req.method === 'OPTIONS') {
|
|
|
28 |
|
29 |
while (retryCount < maxRetries) {
|
30 |
try {
|
31 |
+
console.log(`Attempt ${retryCount + 1} to check for fraud`);
|
32 |
+
|
33 |
const response = await client.chat.complete({
|
34 |
+
model: "mistral-large-latest",
|
35 |
messages: [
|
36 |
{
|
37 |
role: "system",
|
38 |
content: `You are a fraud detection system for a word guessing game.
|
39 |
The game is being played in ${language}.
|
40 |
+
Your task is to detect if a player is trying to cheat by one of two methods:
|
41 |
+
1. The Player's description is a misspelling of the target word
|
42 |
+
2. The Player's description is a sentence without spaces
|
43 |
|
44 |
Examples for cheating:
|
45 |
|
46 |
+
Target word: hand
|
47 |
+
Player's description: hnd
|
48 |
+
Language: en
|
49 |
+
CORRECT ANSWER: cheating
|
50 |
+
|
51 |
+
Target word: barfuß
|
52 |
+
Player's description: germanwordforbarefoot
|
53 |
+
Language: de
|
54 |
+
CORRECT ANSWER: cheating
|
55 |
|
56 |
Synonyms and names of instances of a class are legitimate descriptions.
|
57 |
|
58 |
+
Target word: laptop
|
59 |
+
Player's description: notebook
|
60 |
+
Language: en
|
61 |
+
CORRECT ANSWER: legitimate
|
62 |
+
|
63 |
+
Target word: play
|
64 |
+
Player's description: children often
|
65 |
+
Language: en
|
66 |
+
CORRECT ANSWER: legitimate
|
67 |
+
|
68 |
+
Target word: Pfankuchen
|
69 |
+
Player's description: Berliner
|
70 |
+
Language: de
|
71 |
+
CORRECT ANSWER: legitimate
|
72 |
+
|
73 |
+
Target word: Burrito
|
74 |
+
Player's description: Wrap
|
75 |
+
Language: es
|
76 |
+
CORRECT ANSWER: legitimate
|
77 |
+
|
78 |
Respond with ONLY "cheating" or "legitimate" (no punctuation or explanation).`
|
79 |
},
|
80 |
{
|
|
|
90 |
temperature: 0.1
|
91 |
});
|
92 |
|
93 |
+
if (!response?.choices?.[0]?.message?.content) {
|
94 |
+
throw new Error('Invalid response format from Mistral API');
|
95 |
+
}
|
96 |
+
|
97 |
const verdict = response.choices[0].message.content.trim().toLowerCase();
|
98 |
console.log('Fraud detection verdict:', verdict);
|
99 |
|
|
|
105 |
console.error(`Attempt ${retryCount + 1} failed:`, error);
|
106 |
lastError = error;
|
107 |
|
108 |
+
// Check if it's a rate limit or service unavailable error
|
109 |
+
if (error.message?.includes('rate limit') ||
|
110 |
+
error.message?.includes('503') ||
|
111 |
+
error.message?.includes('Service unavailable')) {
|
112 |
+
const waitTime = Math.pow(2, retryCount) * 1000; // Exponential backoff
|
113 |
+
console.log(`Service unavailable or rate limited, waiting ${waitTime}ms before retry`);
|
114 |
+
await sleep(waitTime);
|
115 |
retryCount++;
|
116 |
continue;
|
117 |
}
|
118 |
|
119 |
+
// If it's not a retryable error, throw immediately
|
120 |
throw error;
|
121 |
}
|
122 |
}
|
123 |
|
124 |
+
// If we've exhausted all retries
|
125 |
throw new Error(`Failed after ${maxRetries} attempts. Last error: ${lastError?.message}`);
|
126 |
|
127 |
} catch (error) {
|
128 |
console.error('Error in fraud detection:', error);
|
129 |
|
130 |
+
const errorMessage = error.message?.includes('rate limit') || error.message?.includes('503')
|
131 |
? "The AI service is currently busy. Please try again in a few moments."
|
132 |
: "Sorry, there was an error checking for fraud. Please try again.";
|
133 |
|
|
|
137 |
details: error.message
|
138 |
}),
|
139 |
{
|
140 |
+
status: error.message?.includes('rate limit') || error.message?.includes('503') ? 429 : 500,
|
141 |
headers: { ...corsHeaders, 'Content-Type': 'application/json' }
|
142 |
}
|
143 |
);
|
supabase/functions/generate-themed-word/index.ts
CHANGED
@@ -38,28 +38,42 @@ const openRouterModels = [
|
|
38 |
];
|
39 |
|
40 |
async function tryMistral(theme: string, usedWords: string[], language: string) {
|
|
|
|
|
|
|
|
|
|
|
41 |
const client = new Mistral({
|
42 |
-
apiKey:
|
43 |
});
|
44 |
|
45 |
const prompts = languagePrompts[language as keyof typeof languagePrompts] || languagePrompts.en;
|
46 |
|
47 |
const response = await client.chat.complete({
|
48 |
-
model: "mistral-
|
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:
|
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 |
|
@@ -68,7 +82,7 @@ async function tryOpenRouter(theme: string, usedWords: string[], language: strin
|
|
68 |
const response = await fetch("https://openrouter.ai/api/v1/chat/completions", {
|
69 |
method: "POST",
|
70 |
headers: {
|
71 |
-
"Authorization": `Bearer ${
|
72 |
"HTTP-Referer": "https://think-in-sync.com",
|
73 |
"X-Title": "Think in Sync",
|
74 |
"Content-Type": "application/json"
|
@@ -85,10 +99,16 @@ async function tryOpenRouter(theme: string, usedWords: string[], language: strin
|
|
85 |
});
|
86 |
|
87 |
if (!response.ok) {
|
88 |
-
|
|
|
89 |
}
|
90 |
|
91 |
const data = await response.json();
|
|
|
|
|
|
|
|
|
|
|
92 |
return data.choices[0].message.content.trim();
|
93 |
}
|
94 |
|
@@ -101,33 +121,54 @@ serve(async (req) => {
|
|
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 |
-
|
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 |
-
|
117 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
118 |
return new Response(
|
119 |
-
JSON.stringify({
|
120 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
121 |
);
|
122 |
}
|
|
|
|
|
|
|
|
|
|
|
123 |
} catch (error) {
|
124 |
console.error('Error generating themed word:', error);
|
125 |
return new Response(
|
126 |
-
JSON.stringify({
|
|
|
|
|
|
|
127 |
{
|
128 |
status: 500,
|
129 |
headers: { ...corsHeaders, 'Content-Type': 'application/json' }
|
130 |
}
|
131 |
);
|
132 |
}
|
133 |
-
});
|
|
|
38 |
];
|
39 |
|
40 |
async function tryMistral(theme: string, usedWords: string[], language: string) {
|
41 |
+
const mistralKey = Deno.env.get('MISTRAL_API_KEY');
|
42 |
+
if (!mistralKey) {
|
43 |
+
throw new Error('Mistral API key not configured');
|
44 |
+
}
|
45 |
+
|
46 |
const client = new Mistral({
|
47 |
+
apiKey: mistralKey,
|
48 |
});
|
49 |
|
50 |
const prompts = languagePrompts[language as keyof typeof languagePrompts] || languagePrompts.en;
|
51 |
|
52 |
const response = await client.chat.complete({
|
53 |
+
model: "mistral-large-latest",
|
54 |
messages: [
|
55 |
{
|
56 |
role: "system",
|
57 |
content: `${prompts.systemPrompt} "${theme}".\n${prompts.requirements} ${usedWords.join(', ')}\n\nRespond with just the word in UPPERCASE, nothing else.`
|
58 |
}
|
59 |
],
|
60 |
+
maxTokens: 50,
|
61 |
temperature: 0.99
|
62 |
});
|
63 |
|
64 |
+
if (!response?.choices?.[0]?.message?.content) {
|
65 |
+
throw new Error('Invalid response from Mistral API');
|
66 |
+
}
|
67 |
+
|
68 |
return response.choices[0].message.content.trim();
|
69 |
}
|
70 |
|
71 |
async function tryOpenRouter(theme: string, usedWords: string[], language: string) {
|
72 |
+
const openRouterKey = Deno.env.get('OPENROUTER_API_KEY');
|
73 |
+
if (!openRouterKey) {
|
74 |
+
throw new Error('OpenRouter API key not configured');
|
75 |
+
}
|
76 |
+
|
77 |
const prompts = languagePrompts[language as keyof typeof languagePrompts] || languagePrompts.en;
|
78 |
const randomModel = openRouterModels[Math.floor(Math.random() * openRouterModels.length)];
|
79 |
|
|
|
82 |
const response = await fetch("https://openrouter.ai/api/v1/chat/completions", {
|
83 |
method: "POST",
|
84 |
headers: {
|
85 |
+
"Authorization": `Bearer ${openRouterKey}`,
|
86 |
"HTTP-Referer": "https://think-in-sync.com",
|
87 |
"X-Title": "Think in Sync",
|
88 |
"Content-Type": "application/json"
|
|
|
99 |
});
|
100 |
|
101 |
if (!response.ok) {
|
102 |
+
const errorText = await response.text();
|
103 |
+
throw new Error(`OpenRouter API error: ${response.status} - ${errorText}`);
|
104 |
}
|
105 |
|
106 |
const data = await response.json();
|
107 |
+
|
108 |
+
if (!data?.choices?.[0]?.message?.content) {
|
109 |
+
throw new Error('Invalid response from OpenRouter API');
|
110 |
+
}
|
111 |
+
|
112 |
return data.choices[0].message.content.trim();
|
113 |
}
|
114 |
|
|
|
121 |
const { theme, usedWords = [], language = 'en' } = await req.json();
|
122 |
console.log('Generating word for theme:', theme, 'language:', language, 'excluding:', usedWords);
|
123 |
|
124 |
+
let word;
|
125 |
+
let error;
|
126 |
+
|
127 |
try {
|
128 |
console.log('Attempting with Mistral...');
|
129 |
+
word = await tryMistral(theme, usedWords, language);
|
130 |
console.log('Successfully generated word with Mistral:', word);
|
|
|
|
|
|
|
|
|
131 |
} catch (mistralError) {
|
132 |
console.error('Mistral error:', mistralError);
|
133 |
console.log('Falling back to OpenRouter...');
|
134 |
|
135 |
+
try {
|
136 |
+
word = await tryOpenRouter(theme, usedWords, language);
|
137 |
+
console.log('Successfully generated word with OpenRouter:', word);
|
138 |
+
} catch (openRouterError) {
|
139 |
+
console.error('OpenRouter error:', openRouterError);
|
140 |
+
error = openRouterError;
|
141 |
+
}
|
142 |
+
}
|
143 |
+
|
144 |
+
if (!word) {
|
145 |
return new Response(
|
146 |
+
JSON.stringify({
|
147 |
+
error: 'Failed to generate word with both Mistral and OpenRouter',
|
148 |
+
details: error?.message || 'Unknown error'
|
149 |
+
}),
|
150 |
+
{
|
151 |
+
status: 500,
|
152 |
+
headers: { ...corsHeaders, 'Content-Type': 'application/json' }
|
153 |
+
}
|
154 |
);
|
155 |
}
|
156 |
+
|
157 |
+
return new Response(
|
158 |
+
JSON.stringify({ word }),
|
159 |
+
{ headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
|
160 |
+
);
|
161 |
} catch (error) {
|
162 |
console.error('Error generating themed word:', error);
|
163 |
return new Response(
|
164 |
+
JSON.stringify({
|
165 |
+
error: 'Error generating themed word',
|
166 |
+
details: error.message
|
167 |
+
}),
|
168 |
{
|
169 |
status: 500,
|
170 |
headers: { ...corsHeaders, 'Content-Type': 'application/json' }
|
171 |
}
|
172 |
);
|
173 |
}
|
174 |
+
});
|
supabase/functions/generate-word/index.ts
CHANGED
@@ -49,14 +49,14 @@ async function tryMistral(currentWord: string, existingSentence: string, languag
|
|
49 |
const prompts = languagePrompts[language as keyof typeof languagePrompts] || languagePrompts.en;
|
50 |
|
51 |
const response = await client.chat.complete({
|
52 |
-
model: "mistral-
|
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:
|
60 |
temperature: 0.5
|
61 |
});
|
62 |
|
|
|
49 |
const prompts = languagePrompts[language as keyof typeof languagePrompts] || languagePrompts.en;
|
50 |
|
51 |
const response = await client.chat.complete({
|
52 |
+
model: "mistral-large-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: 50,
|
60 |
temperature: 0.5
|
61 |
});
|
62 |
|
supabase/functions/guess-word/index.ts
CHANGED
@@ -44,7 +44,7 @@ async function tryMistral(sentence: string, language: string) {
|
|
44 |
const prompts = languagePrompts[language as keyof typeof languagePrompts] || languagePrompts.en;
|
45 |
|
46 |
const response = await client.chat.complete({
|
47 |
-
model: "mistral-
|
48 |
messages: [
|
49 |
{
|
50 |
role: "system",
|
|
|
44 |
const prompts = languagePrompts[language as keyof typeof languagePrompts] || languagePrompts.en;
|
45 |
|
46 |
const response = await client.chat.complete({
|
47 |
+
model: "mistral-large-latest",
|
48 |
messages: [
|
49 |
{
|
50 |
role: "system",
|
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-large-latest",
|
24 |
messages: [
|
25 |
{
|
26 |
role: "system",
|