Spaces:
Running
Running
| // web/src/hooks/useModel.js | |
| import { useState, useEffect, useRef, useCallback } from "react"; | |
| import { read_audio } from "@huggingface/transformers"; | |
| export function useModel() { | |
| const [status, setStatus] = useState("idle"); // idle | webgpu-available | webgpu-unavailable | loading | ready | generating | error | |
| const [loadProgress, setLoadProgress] = useState(null); | |
| const [error, setError] = useState(null); | |
| const workerRef = useRef(null); | |
| const callbacksRef = useRef(null); | |
| useEffect(() => { | |
| const worker = new Worker(new URL("../worker.js", import.meta.url), { | |
| type: "module", | |
| }); | |
| worker.onmessage = (e) => { | |
| const { type, ...data } = e.data; | |
| switch (type) { | |
| case "status": | |
| setStatus(data.status); | |
| if (data.status === "ready") setLoadProgress(null); | |
| break; | |
| case "progress": | |
| setLoadProgress(data); | |
| break; | |
| case "error": | |
| setError(data.message); | |
| setStatus("error"); | |
| callbacksRef.current?.onComplete?.("", data.message); | |
| break; | |
| case "update": | |
| callbacksRef.current?.onUpdate?.(data.text); | |
| break; | |
| case "complete": | |
| setStatus("ready"); | |
| callbacksRef.current?.onComplete?.(data.text); | |
| callbacksRef.current = null; | |
| break; | |
| } | |
| }; | |
| workerRef.current = worker; | |
| return () => worker.terminate(); | |
| }, []); | |
| const checkWebGPU = useCallback(() => { | |
| workerRef.current?.postMessage({ type: "check" }); | |
| }, []); | |
| const loadModel = useCallback(() => { | |
| workerRef.current?.postMessage({ type: "load" }); | |
| }, []); | |
| const generate = useCallback(async ({ messages, imageUrl, audioUrl, enableThinking, onUpdate, onComplete }) => { | |
| callbacksRef.current = { onUpdate, onComplete }; | |
| let audioData = null; | |
| if (audioUrl) { | |
| try { | |
| audioData = await read_audio(audioUrl, 16000); | |
| } catch (err) { | |
| console.error("Audio decode failed:", err); | |
| } | |
| } | |
| const msg = { | |
| type: "generate", | |
| messages, | |
| imageUrl: imageUrl || null, | |
| audioData, | |
| enableThinking: enableThinking || false, | |
| }; | |
| workerRef.current?.postMessage(msg, audioData ? [audioData.buffer] : []); | |
| }, []); | |
| const interrupt = useCallback(() => { | |
| workerRef.current?.postMessage({ type: "interrupt" }); | |
| }, []); | |
| return { status, loadProgress, error, checkWebGPU, loadModel, generate, interrupt }; | |
| } | |