Skip to content

Commit

Permalink
Add o1 route
Browse files Browse the repository at this point in the history
  • Loading branch information
SuveenE committed Jan 13, 2025
1 parent 6c4f2d3 commit e3ca93c
Show file tree
Hide file tree
Showing 4 changed files with 150 additions and 9 deletions.
82 changes: 82 additions & 0 deletions app/api/o1/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { NextResponse } from "next/server";
import OpenAI from "openai";
import { GameState } from "@/types/game";
import { generatePrompt, getSystemPromptO1 } from "@/utils/prompts";
import { delay } from "@/utils/gameUtils";
import { observeOpenAI } from "langfuse";

const MAX_RETRIES = 2;

const createOpenAIClient = async (
role: "CLUE_GIVER" | "GUESSER",
sessionId: string,
gameState: GameState,
) => {
return observeOpenAI(
new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
}),
{
generationName: role === "CLUE_GIVER" ? "clue giver" : "guesser",
metadata: {
currentTeam: gameState.currentTeam,
},
sessionId: sessionId,
},
);
};

export async function POST(request: Request) {
try {
const body = await request.json();
const {
role,
sessionId,
gameState,
}: {
role: "CLUE_GIVER" | "GUESSER";
sessionId: string;
gameState: GameState;
} = body;

const openai = await createOpenAIClient(role, sessionId, gameState);

const prompt = getSystemPromptO1(role) + generatePrompt(role, gameState);

for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
try {
const completion = await openai.chat.completions.create({
model: "o1-mini-2024-09-12",
messages: [
{
role: "user",
content: prompt,
},
],
});

return NextResponse.json({
response: completion.choices[0].message.content,
});
} catch (error) {
console.error(
`GPT API Error (attempt ${attempt + 1}/${MAX_RETRIES + 1}):`,
error,
);
await delay(1000);
if (attempt === MAX_RETRIES) break;
}
}

return NextResponse.json(
{ error: "Failed to process request after multiple attempts" },
{ status: 500 },
);
} catch (error) {
console.error("Request processing error:", error);
return NextResponse.json(
{ error: "Failed to process request" },
{ status: 500 },
);
}
}
42 changes: 34 additions & 8 deletions app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import GitHubLink from "@/components/GitHubLink";
import CustomGameDialog from "@/components/CustomGameDialog";
import { WORD_LIST } from "@/data/wordsList";
import testGame from "@/data/testGame.json";
// import { ClueResponse, ClueResponseSchema, GuessResponse } from "@/types/requests";

export default function Home() {
const [gameState, setGameState] = useState<GameState | null>(null);
Expand Down Expand Up @@ -117,13 +118,25 @@ export default function Home() {
return;
}

const { response: clueData } = await clueResponse.json();
// gpt-4o
const { response: clueDataFinal } = await clueResponse.json();

//o1 models
// const { response: clueData } = await clueResponse.json() as {
// response: ClueResponse | string;
// };
// const clueDataString = typeof clueData === "string"
// ? clueData.replace(/^```json\n|\n```$/g, '')
// : JSON.stringify(clueData);

// const clueDataFinal = typeof clueData === "string" ? JSON.parse(clueDataString) : clueData;

const currentTurn: GameTurn = {
team: gameState.currentTeam,
clue: {
word: clueData.word,
number: clueData.number,
reasoning: clueData.reasoning,
word: clueDataFinal.word,
number: clueDataFinal.number,
reasoning: clueDataFinal.reasoning,
},
guesses: [],
};
Expand Down Expand Up @@ -158,10 +171,22 @@ export default function Home() {
return;
}

const { response: guessData } = await guessResponse.json();
// gpt-4o
const { response: guessDataFinal } = await guessResponse.json();

//o1 models
// const { response: guessData } = await guessResponse.json() as {
// response: GuessResponse | string;
// };

// const guessDataString = typeof guessData === "string"
// ? guessData.replace(/^```json\n|\n```$/g, '')
// : JSON.stringify(guessData);

// const guessDataFinal = typeof guessData === "string" ? JSON.parse(guessDataString) : guessData;

// Handle skip (only allowed after at least one guess)
if (guessData.skip && currentTurn.guesses.length > 0) {
if (guessDataFinal.skip && currentTurn.guesses.length > 0) {
currentTurn.guesses.push({ word: "SKIP", wasCorrect: false });
setGameState((prev) => ({
...prev!,
Expand All @@ -176,15 +201,16 @@ export default function Home() {

// Process the guess
const cardIndex = gameState.cards.findIndex(
(card) => card.word.toLowerCase() === guessData.words.toLowerCase(),
(card) =>
card.word.toLowerCase() === guessDataFinal.words.toLowerCase(),
);

if (cardIndex !== -1) {
const card = gameState.cards[cardIndex];
if (!card.revealed) {
const wasCorrect = card.type === gameState.currentTeam;
currentTurn.guesses.push({
word: guessData.words,
word: guessDataFinal.words,
wasCorrect,
});

Expand Down
3 changes: 3 additions & 0 deletions types/requests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,6 @@ export const GuessResponseSchema = z.object({
skip: z.boolean().optional(),
reasoning: z.string().optional(),
});

export type ClueResponse = z.infer<typeof ClueResponseSchema>;
export type GuessResponse = z.infer<typeof GuessResponseSchema>;
32 changes: 31 additions & 1 deletion utils/prompts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,33 @@ export function getSystemPrompt(role: "CLUE_GIVER" | "GUESSER"): string {
}
}

export function getSystemPromptO1(role: "CLUE_GIVER" | "GUESSER"): string {
if (role === "CLUE_GIVER") {
return `You are playing Codenames as a Spymaster. Your role is to give one-word clues that can help your team guess multiple words while avoiding the opponent's words and the assassin.
Try to finish the game as soon as possible by connecting multiple words with clever clues.
Your response must be in the following format:
{
"word": "your one-word clue",
"number": number of words this clue relates to,
"reasoning": "(optional) explanation of your clue"
}
Do not include the word json in your response.`;
} else {
return `You are playing Codenames as a Guesser. Your role is to guess ONE word at a time based on the clue given by your Spymaster.
Your response must be in the following format:
{
"words": "the word you want to guess",
"skip": (optional) boolean indicating if you want to skip,
"reasoning": "(optional) explanation of your guess"
}
Do not include the word json in your response.`;
}
}

export function generatePrompt(
role: "CLUE_GIVER" | "GUESSER",
gameState: GameState,
Expand Down Expand Up @@ -44,7 +71,10 @@ export function generatePrompt(
)
.join("\n ")}
Be creative and take calculated risks - it's better to give ambitious clues that could help win faster.`;
Be creative and take calculated risks - it's better to give ambitious clues that could help win faster.
`;
} else {
const currentTurn = gameState.history[gameState.history.length - 1];
const guessesLeft =
Expand Down

0 comments on commit e3ca93c

Please sign in to comment.