Spaces:
Running
Running
| /** | |
| * 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<string | null>(null); | |
| const lastMoveTime = ref<number>(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<void> => { | |
| 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<Position | null> => { | |
| 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 | |
| }; | |
| } | |