/** * Vue Composable for Trigo AI Agent * * Provides reactive interface to the ONNX-based AI agent for move generation. * Features: * - Lazy initialization (only loads when needed) * - Reactive state tracking (ready, thinking, errors) * - Automatic cleanup on unmount * - Move generation with error handling * - Uses tree agent with evaluation mode for efficient parallel move evaluation */ import { ref, onUnmounted } from "vue"; import { TrigoTreeAgent } from "@inc/trigoTreeAgent"; import { OnnxInferencer } from "@/services/onnxInferencer"; import type { TrigoGame } from "@inc/trigo/game"; import type { Position } from "@inc/trigo/types"; /** * Composable for using the Trigo AI agent in Vue components */ export function useTrigoAgent() { // Reactive state const isReady = ref(false); const isThinking = ref(false); const error = ref(null); const lastMoveTime = ref(0); // Agent instance (created lazily) let agent: TrigoTreeAgent | null = null; let inferencer: OnnxInferencer | null = null; /** * Initialize the AI agent * - Loads ONNX evaluation model and prepares for inference * - Call this when entering VS AI mode * - Safe to call multiple times (won't re-initialize) */ const initialize = async (): Promise => { if (isReady.value) { console.log("[useTrigoAgent] Already initialized"); return; } error.value = null; try { console.log("[useTrigoAgent] Initializing tree agent with evaluation mode..."); // Create OnnxInferencer with evaluation model inferencer = new OnnxInferencer({ modelPath: "/onnx/GPT2CausalLM_ep0015_evaluation.onnx", vocabSize: 259, seqLen: 2048 // Evaluation models support longer sequences }); // Initialize ONNX model await inferencer.initialize(); // Create tree agent with the inferencer agent = new TrigoTreeAgent(inferencer); isReady.value = true; console.log("[useTrigoAgent] ✓ Tree agent ready"); } catch (err) { const errorMessage = err instanceof Error ? err.message : "Failed to initialize AI agent"; error.value = errorMessage; console.error("[useTrigoAgent] Initialization failed:", err); throw err; } }; /** * Generate the next move for the AI player * * @param game - Current game instance * @returns The selected move position, or null if no valid moves or pass move * @throws Error if agent is not initialized */ const generateMove = async (game: TrigoGame): Promise => { if (!agent) { throw new Error("AI agent not initialized. Call initialize() first."); } if (!isReady.value) { throw new Error("AI agent is not ready yet."); } if (isThinking.value) { console.warn("[useTrigoAgent] Already generating a move, ignoring request"); return null; } error.value = null; isThinking.value = true; try { console.log("[useTrigoAgent] Generating move with tree agent..."); const startTime = performance.now(); // Use tree agent to select best move const move = await agent.selectBestMove(game); const elapsed = performance.now() - startTime; lastMoveTime.value = elapsed; console.log(`[useTrigoAgent] ✓ Move generated in ${elapsed.toFixed(2)}ms`); // Convert Move to Position (return null if pass move) if (!move || move.isPass) { console.log("[useTrigoAgent] AI chose to pass"); return null; } if (move.x !== undefined && move.y !== undefined && move.z !== undefined) { return { x: move.x, y: move.y, z: move.z }; } console.warn("[useTrigoAgent] Move has undefined coordinates:", move); return null; } catch (err) { const errorMessage = err instanceof Error ? err.message : "Failed to generate move"; error.value = errorMessage; console.error("[useTrigoAgent] Move generation failed:", err); throw err; } finally { isThinking.value = false; } }; /** * Check if the agent is initialized and ready */ const checkIsReady = (): boolean => { return agent !== null && inferencer !== null; }; /** * Clean up resources when component unmounts */ const cleanup = (): void => { if (agent || inferencer) { console.log("[useTrigoAgent] Cleaning up..."); agent = null; inferencer = null; isReady.value = false; isThinking.value = false; error.value = null; lastMoveTime.value = 0; } }; // Automatic cleanup on component unmount onUnmounted(() => { cleanup(); }); return { // State isReady, isThinking, error, lastMoveTime, // Methods initialize, generateMove, checkIsReady, cleanup }; }