Spaces:
Paused
Paused
| import { OpenAI } from "openai"; | |
| // Configuration - use specified model with fallback to env vars | |
| export const MODEL = | |
| process.env.HF_MODEL || | |
| process.env.OPENAI_MODEL || | |
| "zai-org/GLM-4.7-Flash:novita"; | |
| const HF_TOKEN = process.env.HF_TOKEN; | |
| if (!HF_TOKEN) { | |
| throw new Error( | |
| "HF_TOKEN environment variable is required for Hugging Face Router authentication." | |
| ); | |
| } | |
| const SYS_PROMPT = ` | |
| # Welcome to the you own exploration game | |
| You are acting as an explorer in a 2D world game seen from a top-down perspective. Everything in this world, including objects and landmarks, is represented by emoji. | |
| The most important emoji is ๐ถ (it's you). | |
| ## Important concepts | |
| ### Turn-based | |
| You can take one action per turn, then you'll get the result of your action before you can take another action. | |
| ### Goals | |
| Your primary goal is to collect as many diamonds (๐) as possible. A good strategy is to explore efficiently and avoid revisiting the same tiles while searching for diamonds. | |
| ## Available Actions | |
| You must respond your next action in JSON format with what action you want to take. Give no explanations just return the JSON. | |
| ### Move | |
| This action allows you to move one emoji away from your current location, choosing between the four available direct tiles around you (๐ถ): "up", "right", "down", or "left". | |
| You are only allowed to move on the empty โฌ square, so always make sure that your target square is a โฌ. | |
| { "action": "move", "detail": move direction, can be "up", "right", "down", or "left" } | |
| ### Pick | |
| This action allows you to pick up an object from an adjacent tile (up, right, down, or left). | |
| The object will be added to your inventory and removed from the map. | |
| { "action": "pick", "detail": direction of the object to pick, can be "up", "right", "down", or "left" }`; | |
| // OpenAI client configured for HuggingFace router | |
| const client = new OpenAI({ | |
| baseURL: "https://router.huggingface.co/v1", | |
| apiKey: HF_TOKEN, | |
| }); | |
| export class AIService { | |
| /** | |
| * Get the next action from the AI agent | |
| * Returns a validated action object { action: "move"|"pick", detail: "up"|"down"|"left"|"right" } | |
| */ | |
| static async getNextAction( | |
| map: string, | |
| history: any[] | |
| ): Promise<{ action: string; detail: string }> { | |
| const messages: OpenAI.ChatCompletionMessageParam[] = [ | |
| { | |
| role: "system", | |
| content: SYS_PROMPT, | |
| }, | |
| ...history.slice(-15), | |
| { | |
| role: "user", | |
| content: `Please decide on what action to do next and provide the corresponding JSON response. Here is the current map: | |
| ${map} | |
| `, | |
| }, | |
| ]; | |
| // Log messages for debugging | |
| Bun.write("messages.json", JSON.stringify(messages, null, 2)); | |
| try { | |
| const chatCompletion = await client.chat.completions.create({ | |
| model: MODEL, | |
| messages, | |
| temperature: 0.7, | |
| }); | |
| let content = chatCompletion.choices[0].message.content?.trim() ?? ""; | |
| console.log("Raw AI response:", content); | |
| // Strip markdown code blocks if present | |
| if (content.startsWith("```")) { | |
| content = content.replace(/^```(?:json)?\s*/i, "").replace(/\s*```$/, ""); | |
| } | |
| // Parse and validate the response | |
| const parsed = JSON.parse(content); | |
| if (this.isValidAction(parsed)) { | |
| return { | |
| action: parsed.action, | |
| detail: parsed.detail.toLowerCase(), | |
| }; | |
| } | |
| throw new Error("Invalid action format"); | |
| } catch (error) { | |
| console.error("Error getting AI action:", error); | |
| // Return a safe fallback action if anything fails | |
| return { action: "move", detail: "right" }; | |
| } | |
| } | |
| private static isValidAction(obj: any): boolean { | |
| if (!obj || typeof obj !== "object" || typeof obj.action !== "string") { | |
| return false; | |
| } | |
| if (obj.action === "move" || obj.action === "pick") { | |
| return ( | |
| typeof obj.detail === "string" && | |
| ["up", "down", "left", "right"].includes(obj.detail.toLowerCase()) | |
| ); | |
| } | |
| return false; | |
| } | |
| } | |