diff --git a/.gitattributes b/.gitattributes index 73387e2213f2142fc4199f6cb230e29c019adc74..c0a6fadf26577a0e7249eb01c2fcadf66d10a7c4 100644 --- a/.gitattributes +++ b/.gitattributes @@ -51,9 +51,3 @@ public/avatars/ready_player_me_1.glb filter=lfs diff=lfs merge=lfs -text public/avatars/ready_player_me_2.glb filter=lfs diff=lfs merge=lfs -text public/avatars/vuong.glb filter=lfs diff=lfs merge=lfs -text public/avatars/lisamy.glb filter=lfs diff=lfs merge=lfs -text -public/avatars/viverse_avatar_model_209370.vrm filter=lfs diff=lfs merge=lfs -text -public/avatars/scene.glb filter=lfs diff=lfs merge=lfs -text -public/avatars/scene[[:space:]](1).glb filter=lfs diff=lfs merge=lfs -text -public/avatars/scene[[:space:]](3).glb filter=lfs diff=lfs merge=lfs -text -public/avatars/vuong1.glb filter=lfs diff=lfs merge=lfs -text -public/avatars/lisamy1.glb filter=lfs diff=lfs merge=lfs -text diff --git a/.gitignore b/.gitignore index ba79468dcce09f55e21b9932f154f890f82574ef..a14702c409d3f34f9ab001e572dfeaf0da434bba 100644 --- a/.gitignore +++ b/.gitignore @@ -31,4 +31,4 @@ report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json .idea # Finder (MacOS) folder config -.DS_Store \ No newline at end of file +.DS_Store diff --git a/.rebuild-trigger b/.rebuild-trigger deleted file mode 100644 index 64506f1f3b979579d4398fbdbd006562e1d1b0a2..0000000000000000000000000000000000000000 --- a/.rebuild-trigger +++ /dev/null @@ -1 +0,0 @@ -Trigger rebuild - fix picker + thumbnails \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md index 9656d126e15d7a44f35d7f9187bf56317505653e..ebda995143b4aa0c8477347866f7bb3fb1952ca5 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -108,4 +108,4 @@ Then, run index.ts bun --hot ./index.ts ``` -For more information, read the Bun API docs in `node_modules/bun-types/docs/**.mdx`. \ No newline at end of file +For more information, read the Bun API docs in `node_modules/bun-types/docs/**.mdx`. diff --git a/Dockerfile b/Dockerfile index 25f3b81b7ef439a81622afe9e3bd9ee3637cfe6f..2dd0b41557b7cefc585e6ef52ea269954a5382ca 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,32 +1,27 @@ -# Multi-service: Bun server + Python sidecar for dataset uploads +# Single container: Bun serves the bundled front-end AND the /api/session +# proxy. HF Spaces (sdk: docker) routes traffic to app_port, set to 7860. FROM oven/bun:1-slim WORKDIR /app -# Install Python and supervisor for sidecar -RUN apt-get update && apt-get install -y python3 python3-pip python3-venv supervisor && rm -rf /var/lib/apt/lists/* - -# Create virtual environment for Python -RUN python3 -m venv /app/venv - -# Copy package files for Bun +# Copy just the lockfile and package.json first for layer caching COPY package.json bun.lock ./ +# Retry install once if it fails (network flakiness) RUN bun install --frozen-lockfile --production || (sleep 3 && bun install --frozen-lockfile --production) -# Copy Python requirements and install in venv -COPY requirements.txt ./ -RUN /app/venv/bin/pip install --no-cache-dir -r requirements.txt - -# Copy application code +# Copy the rest of the application COPY . . -# Supervisor config -RUN echo '[supervisord]\nnodaemon=true\n\n[program:sidecar]\ncommand=/app/venv/bin/python upload_service.py\nstdout_logfile=/dev/stdout\nstdout_logfile_maxbytes=0\nstderr_logfile=/dev/stderr\nstderr_logfile_maxbytes=0\nautostart=true\nautorestart=true\n\n[program:bun]\ncommand=bun index.ts\nstdout_logfile=/dev/stdout\nstdout_logfile_maxbytes=0\nstderr_logfile=/dev/stderr\nstderr_logfile_maxbytes=0\nautostart=true\nautorestart=true\n' > /etc/supervisor/conf.d/supervisord.conf - ENV NODE_ENV=production ENV PORT=7860 -ENV SIDECAR_URL=http://localhost:7861 +# Point at a speech-to-speech backend by setting ONE of these in the Space +# settings (index.ts prefers LOAD_BALANCER_URL): +# LOAD_BALANCER_URL a load balancer's base URL (a secret) — talks to the pool directly +# SESSION_PROXY_URL another deployment's /api to piggyback (e.g. a public demo Space) +# With neither set, the app runs in direct mode (paste a ws:// URL in Settings). + +EXPOSE 7860 -EXPOSE 7860 7861 +USER bun -CMD ["supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"] +CMD ["bun", "index.ts"] \ No newline at end of file diff --git a/README.md b/README.md index 3ad9b2b71e5faf84b25d141d99894c012a84af39..0063a6d2def620b3ecc17bf4f68e48b017d8c543 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,96 @@ emoji: 🗣️ colorFrom: indigo colorTo: purple sdk: docker -pinned: false app_port: 7860 +pinned: false +thumbnail: https://huggingface.co/spaces/victor/gemma-avatar/resolve/main/thumbnail.webp +short_description: Talk to Gemma 4 face to face, with a 3D lip-synced avatar +models: +- google/gemma-4-31B-it +- nvidia/parakeet-tdt-1.1b +- Qwen/Qwen3-TTS-12Hz-1.7B-CustomVoice tags: - ml-intern --- -# Gemma Multi-Avatar +# Gemma Avatar + +Realtime voice chat with a 3D talking-head avatar. Same AI stack as the +[smolagents/hf-realtime-voice](https://huggingface.co/spaces/smolagents/hf-realtime-voice) +Space ([blog post](https://huggingface.co/blog/cerebras-gemma4-voice-ai)), but the +orb visualization is replaced by a [TalkingHead](https://github.com/met4citizen/TalkingHead) +3D avatar with real-time audio-driven lip-sync. + +## The pipeline + +``` +you speak → silero-VAD → parakeet-tdt-1.1b (STT) → gemma-4-31B-it on Cerebras → Qwen3-TTS → avatar speaks +``` + +Transport is the OpenAI Realtime GA protocol over WebSocket against Hugging +Face's speech-to-speech backend: mic PCM16 @ 16 kHz goes up as +`input_audio_buffer.append`, TTS PCM16 @ 16 kHz comes back as +`response.output_audio.delta`, transcripts stream alongside. + +## How the avatar works + +- **Rendering / body language** — [TalkingHead](https://github.com/met4citizen/TalkingHead) + (three.js). Blinking, breathing, idle sway, moods, hand gestures, and emoji + expressions are its built-in animation system. +- **Lip-sync** — the backend sends raw PCM only (no word timings, no visemes), + so the mouth is driven from the audio itself with + [HeadAudio](https://github.com/met4citizen/HeadAudio): an AudioWorklet that + classifies MFCC frames into Oculus visemes (~50 ms latency, fully in-browser). + The s2s playback worklet is routed into TalkingHead's audio graph + (`audioAnalyzerNode → audioSpeechGainNode → reverb → speakers`) and HeadAudio + taps the speech gain node. +- **The model plays the avatar** — three function tools are declared to the + backend: `set_mood`, `make_hand_gesture`, `make_facial_expression`. Gemma + calls them mid-conversation (smiles when greeting, shrugs when unsure, + thumbs-up when agreeing). +- **Choreography** — client statuses drive presence: the avatar makes eye + contact when you start talking, gestures with its hands on new utterances, + and barge-in clears the playback buffer so the mouth settles instantly. + +## Run it + +```bash +bun install + +# Pick a backend (one of): +LOAD_BALANCER_URL=https://… bun run dev # a speech-to-speech load balancer +SESSION_PROXY_URL=https://…/api bun run dev # piggyback another deployment's /api (dev) +bun run dev # direct mode: paste a ws:// URL in Settings +``` + +Open http://localhost:3000 and tap **Start talking**. + +\`?fakemic=1\` starts a session with a silent synthetic mic (useful for testing +the full loop without a microphone — trigger a reply from the console with +\`getClient().requestResponse()\`). + +## Layout + +``` +index.ts Bun server: HTML import + /api/session proxy + static assets +index.html App shell (avatar hero, caption, subtitles, settings) +src/app.js Session wiring, tool executor, UI state +src/avatar.js AvatarStage: TalkingHead + HeadAudio + choreography +src/s2s/s2s-ws-client.js Realtime WS client (vendored from the Space; orb removed, + injectable output node + worklet base URL + shared-ctx close) +src/s2s/codec.js PCM/base64 + transcript helpers (vendored, unchanged) +src/vendor/headaudio.min.mjs HeadAudio node class (bundled) +public/worklets/ mic-capture + audio-playback AudioWorklets (vendored, unchanged) +public/vendor/ HeadAudio worklet processor + viseme model (runtime-loaded) +public/avatars/brunette.glb Default avatar (Ready Player Me; CC BY-NC 4.0 — non-commercial) +``` -3D talking-head avatar with real-time voice/text chat. +## Notes -Users can switch between ~20 `.glb` avatar models, capture snapshots as thumbnails, and chat with a Gemma 4 speech-to-speech backend. +- The avatar GLB must have a Mixamo-compatible rig plus ARKit and Oculus-viseme + blend shapes. Ready Player Me avatars work with + \`?morphTargets=ARKit,Oculus%20Visemes\` on the GLB URL. +- TalkingHead owns the AudioContext; the s2s client is handed \`head.audioCtx\` + and never closes it. +- Everything animation-related runs on requestAnimationFrame — a backgrounded + tab freezes the avatar (audio keeps playing). diff --git a/_appdbg.txt b/_appdbg.txt deleted file mode 100644 index 1b82b24b391ee07745c3b84321dbffdd0021c333..0000000000000000000000000000000000000000 --- a/_appdbg.txt +++ /dev/null @@ -1,99 +0,0 @@ -addChatMessage("user", q); - void maybeSwitchTopic(q); - if (!sendTextViaSession(q)) setCaption( ---- -addChatMessage(role, text, newsItem = null) { - const msg = document.createElement("div"); - msg.cla ---- -addChatMessage("user", text); - chatInput.value = ""; - - // New topic? re-filter the news list and r ---- -addChatMessage("assistant", `✅ Đã đăng bài AI tổng hợp (${selected.length} tin nhắn) lên VNEWS:\n${s ---- -addChatMessage("assistant", `⚠️ VNEWS: ${data.error || "không đăng được bài"}.`); - } - } catch (e ---- -addChatMessage("assistant", "⚠️ Lỗi khi kết nối VNEWS để đăng bài."); - } - rewriteBtn.textContent = ---- -addChatMessage("assistant", normalized, newsItem); - } - }); - - c.addEventListener("response-finis ---- -=== _attachClientEvents === -_attachClientEvents(c) { - c.addEventListener("status", (e) => { - onStatus(e.detail.status); - }); - - c.addEventListener("queue", (e) => { - const { position } = e.detail; - setCaption(position > 0 ? `#${position} IN LINE…` : "ALMOST THERE…", "live"); - }); - - c.addEventListener("transcript", (e) => { - const { role, text } = e.detail; - if (role === "assistant" && text) { - const normalized = smartNormalize(text); - showSubtitles(normalized); - // Attach the next image of the CURRENT news item (loops through its images). - const img = nextNewsImage(); - const newsItem = curNews ? { ...curNews, image: img || curNews.image } : null; - addChatMessage("assistant", normalized, newsItem); - } - }); - - c.addEventListener("response-finished", () => { - autoGreetingSent = true; - fadeSubtitles(); - if (preFetchedNews.length > 0) { - showNewsPanel(preFetchedNews.slice(0, NEWS_PAGE_SIZE)); - } - }); - - c.addEventListener("toolcall", (e) => { - const { name, arguments: args, callId } = e.detail; - runTool(name, args, callId); - }); - - c.addEventListener("server-error", (e) => { - console.warn("server error:", e.detail.error); - }); - - c.addEventListener("error", () => { - void endSession(); - }); -} - -async function endSession(silent = false) { - const c = client; - client = null; - sessionInProgress = false; - autoGreetingSent = false; - - if (c) { - if (c.options.micStream) { - for (const track of c.options.micStream?.getTracks() ?? []) { - track.stop(); - } - } - await c.close().catch(() => {}); - } - - stage.setConversationState("idle"); - subtitles.classList.remove("visible"); - - if (newsPanel) { - newsPanel.hidden = true; - } - - if (!silent) { - setCaption(CAPTIONS.idle); - setMainButton("start", \ No newline at end of file diff --git a/_applog.txt b/_applog.txt deleted file mode 100644 index 790832216be3376dabe1e98d790b0a9f7b8f2c0e..0000000000000000000000000000000000000000 --- a/_applog.txt +++ /dev/null @@ -1 +0,0 @@ -HTTP 404 {"error":"Sorry, we can't find the page you are looking for."} \ No newline at end of file diff --git a/_applog2.txt b/_applog2.txt deleted file mode 100644 index 70da45d1e2d5242ae344b3e6837870790da14f75..0000000000000000000000000000000000000000 --- a/_applog2.txt +++ /dev/null @@ -1,46 +0,0 @@ -app HTTP 404 {"error":"Sorry, we can't find the page you are looking for."} -=== build (last 2500) === -home/user/app","timestamp":"2026-07-19T05:26:49.606Z"} - -data: {"data":"CACHED\n","timestamp":"2026-07-19T05:26:49.606Z"} - -data: {"data":"--> RUN apt-get update && apt-get install -y fakeroot && mv /usr/bin/apt-get /usr/bin/.apt-get && echo '#!/usr/bin/env sh\\nfakeroot /usr/bin/.apt-get $@' > /usr/bin/apt-get && chmod +x /usr/bin/apt-get && \trm -rf /var/lib/apt/lists/* && \tuseradd -m -u 1000 user","timestamp":"2026-07-19T05:26:49.606Z"} - -data: {"data":"CACHED\n","timestamp":"2026-07-19T05:26:49.606Z"} - -data: {"data":"--> RUN pip freeze > /tmp/freeze.txt","timestamp":"2026-07-19T05:26:49.606Z"} - -data: {"data":"CACHED\n","timestamp":"2026-07-19T05:26:49.606Z"} - -data: {"data":"--> COPY --chown=1000:1000 --from=root / /","timestamp":"2026-07-19T05:26:49.606Z"} - -data: {"data":"CACHED\n","timestamp":"2026-07-19T05:26:49.606Z"} - -data: {"data":"--> RUN \tapt-get update && \tapt-get install -y curl && \tcurl -fsSL https://deb.nodesource.com/setup_20.x | bash - && \tapt-get install -y nodejs && \trm -rf /var/lib/apt/lists/* && apt-get clean","timestamp":"2026-07-19T05:26:49.606Z"} - -data: {"data":"CACHED\n","timestamp":"2026-07-19T05:26:49.606Z"} - -data: {"data":"--> RUN apt-get update && apt-get install -y \tgit \tgit-lfs \tffmpeg \tlibsm6 \tlibxext6 \tcmake \trsync \tlibgl1 \t&& rm -rf /var/lib/apt/lists/* \t&& git lfs install","timestamp":"2026-07-19T05:26:49.606Z"} - -data: {"data":"CACHED\n","timestamp":"2026-07-19T05:26:49.606Z"} - -data: {"data":"--> RUN --mount=target=/tmp/requirements.txt,source=requirements.txt pip install --no-cache-dir -r /tmp/requirements.txt \"torch<=2.11.0\" gradio[oauth,mcp]==6.20.0 \"uvicorn>=0.14.0\" \"websockets>=10.4\" spaces==0.51.0","timestamp":"2026-07-19T05:26:49.606Z"} - -data: {"data":"CACHED\n","timestamp":"2026-07-19T05:26:49.606Z"} - -data: {"data":"--> COPY --link --chown=1000 ./ /home/user/app","timestamp":"2026-07-19T05:26:49.606Z"} - -data: {"data":"DONE 0.0s\n","timestamp":"2026-07-19T05:26:49.606Z"} - -data: {"data":"--> COPY --from=pipfreeze --link --chown=1000 /tmp/freeze.txt /tmp/freeze.txt","timestamp":"2026-07-19T05:26:49.606Z"} - -data: {"data":"DONE 0.0s\n","timestamp":"2026-07-19T05:26:49.606Z"} - -data: {"data":"--> Pushing image","timestamp":"2026-07-19T05:26:49.606Z"} - -data: {"data":"DONE 0.9s\n","timestamp":"2026-07-19T05:26:50.461Z"} - -data: {"data":"--> Exporting cache","timestamp":"2026-07-19T05:26:50.461Z"} - -data: {"data":"DONE 0.6s\n","timestamp":"2026-07-19T05:26:50.885Z"} - diff --git a/_applog3.txt b/_applog3.txt deleted file mode 100644 index c4a89c8fc78a0fc80702b7f6a950aa3a08035b8b..0000000000000000000000000000000000000000 --- a/_applog3.txt +++ /dev/null @@ -1,56 +0,0 @@ -app HTTP 404 {"error":"Sorry, we can't find the page you are looking for."} -=== build (last 3000) === -438Z"} - -data: {"data":"--> RUN chown -R 1000:1000 /var/","timestamp":"2026-07-19T08:50:16.651Z"} - -data: {"data":"CACHED\n","timestamp":"2026-07-19T08:50:16.651Z"} - -data: {"data":"--> RUN apt-get update && apt-get install -y \tgit \tgit-lfs \tffmpeg \tlibsm6 \tlibxext6 \tcmake \trsync \tlibgl1 \t&& rm -rf /var/lib/apt/lists/* \t&& git lfs install","timestamp":"2026-07-19T08:50:16.651Z"} - -data: {"data":"CACHED\n","timestamp":"2026-07-19T08:50:16.651Z"} - -data: {"data":"--> RUN \tapt-get update && \tapt-get install -y curl && \tcurl -fsSL https://deb.nodesource.com/setup_20.x | bash - && \tapt-get install -y nodejs && \trm -rf /var/lib/apt/lists/* && apt-get clean","timestamp":"2026-07-19T08:50:16.651Z"} - -data: {"data":"CACHED\n","timestamp":"2026-07-19T08:50:16.651Z"} - -data: {"data":"--> RUN pip freeze > /tmp/freeze.txt","timestamp":"2026-07-19T08:50:16.651Z"} - -data: {"data":"CACHED\n","timestamp":"2026-07-19T08:50:16.651Z"} - -data: {"data":"--> WORKDIR /home/user/app","timestamp":"2026-07-19T08:50:16.651Z"} - -data: {"data":"CACHED\n","timestamp":"2026-07-19T08:50:16.651Z"} - -data: {"data":"--> RUN pip install --no-cache-dir pip -U && pip install --no-cache-dir datasets \"huggingface-hub>=0.30\" \"hf-transfer>=0.1.4\" \"protobuf<4\" \"click<8.1\" torch==2.11.0","timestamp":"2026-07-19T08:50:16.651Z"} - -data: {"data":"CACHED\n","timestamp":"2026-07-19T08:50:16.651Z"} - -data: {"data":"--> RUN apt-get update && apt-get install -y fakeroot && mv /usr/bin/apt-get /usr/bin/.apt-get && echo '#!/usr/bin/env sh\\nfakeroot /usr/bin/.apt-get $@' > /usr/bin/apt-get && chmod +x /usr/bin/apt-get && \trm -rf /var/lib/apt/lists/* && \tuseradd -m -u 1000 user","timestamp":"2026-07-19T08:50:16.651Z"} - -data: {"data":"CACHED\n","timestamp":"2026-07-19T08:50:16.651Z"} - -data: {"data":"--> COPY --chown=1000:1000 --from=root / /","timestamp":"2026-07-19T08:50:16.651Z"} - -data: {"data":"CACHED\n","timestamp":"2026-07-19T08:50:16.651Z"} - -data: {"data":"--> RUN --mount=target=/tmp/requirements.txt,source=requirements.txt pip install --no-cache-dir -r /tmp/requirements.txt \"torch<=2.11.0\" gradio[oauth,mcp]==6.20.0 \"uvicorn>=0.14.0\" \"websockets>=10.4\" spaces==0.51.0","timestamp":"2026-07-19T08:50:16.651Z"} - -data: {"data":"CACHED\n","timestamp":"2026-07-19T08:50:16.651Z"} - -data: {"data":"--> COPY --link --chown=1000 ./ /home/user/app","timestamp":"2026-07-19T08:50:16.651Z"} - -data: {"data":"DONE 0.0s\n","timestamp":"2026-07-19T08:50:16.651Z"} - -data: {"data":"--> COPY --from=pipfreeze --link --chown=1000 /tmp/freeze.txt /tmp/freeze.txt","timestamp":"2026-07-19T08:50:16.651Z"} - -data: {"data":"DONE 0.0s\n","timestamp":"2026-07-19T08:50:16.651Z"} - -data: {"data":"--> Pushing image","timestamp":"2026-07-19T08:50:16.651Z"} - -data: {"data":"DONE 0.8s\n","timestamp":"2026-07-19T08:50:17.469Z"} - -data: {"data":"--> Exporting cache","timestamp":"2026-07-19T08:50:17.469Z"} - -data: {"data":"DONE 0.6s\n","timestamp":"2026-07-19T08:50:17.948Z"} - diff --git a/_avatarjs.txt b/_avatarjs.txt deleted file mode 100644 index ef96cd8c1248326c4a5a50c29d8d14f315f4a811..0000000000000000000000000000000000000000 --- a/_avatarjs.txt +++ /dev/null @@ -1,16 +0,0 @@ -LEN 13225 ---- .glb @ 6101 --- -.head); - await head.showAvatar( - { url: opt.avatarUrl ?? "/avatars/vuong.glb", body: opt.body ?? "F", avatarMood: "neutral" }, - (ev) => { - if (ev.lengthComputable) { - const pct = Math.min(100, Math.round((ev.loaded / ev.total) * 100)); - if (t ---- image @ -1 --- ---- thumbnail @ -1 --- ---- img @ -1 --- ---- avatarName @ -1 --- ---- avatar.name @ -1 --- ---- src @ -1 --- ---- public/avatars @ -1 --- \ No newline at end of file diff --git a/_avfiles.txt b/_avfiles.txt deleted file mode 100644 index 548929aa95a9685c24e031ee9dfe09cc1f3e5c5b..0000000000000000000000000000000000000000 --- a/_avfiles.txt +++ /dev/null @@ -1,3 +0,0 @@ -avatar files: ['public/avatars/Ella.glb', 'public/avatars/Lisa.glb', 'public/avatars/avatarsdk.glb', 'public/avatars/avaturn.glb', 'public/avatars/bruce_avaturn.glb', 'public/avatars/brunette-t.glb', 'public/avatars/brunette.glb', 'public/avatars/david.glb', 'public/avatars/fabio_avaturn.glb', 'public/avatars/julia.glb', 'public/avatars/lisamy.glb', 'public/avatars/mpfb.glb', 'public/avatars/ready_player_me_1.glb', 'public/avatars/ready_player_me_2.glb', 'public/avatars/vuong.glb', 'src/avatar.js'] -top dirs: ['public', 'src'] -total: 85 \ No newline at end of file diff --git a/_avlist.txt b/_avlist.txt deleted file mode 100644 index ab372b8d113c0184fb3f5e083f7dc3ee825e1131..0000000000000000000000000000000000000000 --- a/_avlist.txt +++ /dev/null @@ -1,7 +0,0 @@ -===== src/app.js (len 59372) ===== -[avatarList @ 8893] = false; let config = { lb: false, allowDirect: true }; let avatarList = []; let sessionInProgress = false; let autoGreetingSent = false; let preFetchedNews = []; let latestNewsUrl = null; let latestNewsSource = null; // ── News box state -[avatars @ 24432] chAvatarList() { try { const resp = await fetch("/api/avatars"); if (resp.ok) avatarList = (await resp.json()).avatars || []; } catch {} } function populateAvatarSelects(selectedName) { for (const sel of [chatAvatarSelect, se -[/avatars/ @ 25480] await stage.init({ avatarUrl: settings.avatar ? `/avatars/${settings.avatar}` : undefined, onprogress: (ev) => { if (ev.lengthComputable) { loading.textContent = `Loading avatar ${Math.min(100, Math.round( -[case " @ 42381] tatus === "closed" ? "" : "live"); switch (status) { case "idle": case "closed": setMainButton("start", "Start talking"); break; case "error": setMainButton("start", "Retry"); break; case "creating -src/index.html ERR RemoteEntryNotFoundError('404 Client Error. (Request ID: Root=1-6a5cad35-4660b29256e6cd0345252f48;ce084157-0252-44b9-b0f -===== index.html (len 6890) ===== \ No newline at end of file diff --git a/_chk.txt b/_chk.txt deleted file mode 100644 index 6344a4f6ac570038626739b8510b901de5ced441..0000000000000000000000000000000000000000 --- a/_chk.txt +++ /dev/null @@ -1,9 +0,0 @@ -len 22595 -has gemma-2-2b-it: False -has SUMMARY_ENDPOINT: False -has router.huggingface: True -has Step 1: True -MODEL line: const MODEL = Bun.env.SUMMARY_MODEL || "Qwen/Qwen2.5-7B-Instruct"; - let out = ""; - try { - c \ No newline at end of file diff --git a/_chk2.txt b/_chk2.txt deleted file mode 100644 index f7f20dc1e3e3ed57e5a5634f09718a38714b9538..0000000000000000000000000000000000000000 --- a/_chk2.txt +++ /dev/null @@ -1,22 +0,0 @@ -stage: RUNNING -error: None -hw: {"current": "zero-a10g", "requested": "zero-a10g"} -/ HTTP 404 - -
- - - - - - - - []) : Promise.resolve([]); -2017: let ok = false; -2018: try { -2019: const initPromise = stage.init({ -2020: avatarUrl: settings.avatar ? `/avatars/${settings.avatar}` : undefined, -2021: onprogress: (ev) => { -2022: if (ev.lengthComputable) { -2023: loading.textContent = `Loading avatar ${Math.min(100, Math.round((ev.loaded / ev.total) * 100))}%`; -2024: } -2025: } -2026: }); -2027: const timeout = new Promise((_, rej) => setTimeout(() => rej(new Error("init timeout")), 25000)); -2028: await Promise.race([initPromise, timeout]); -2029: stage.resume(); // start render loop so the avatar is actually visible (not a single black frame) -2030: ok = true; -2031: } catch (err) { -2032: console.error("[startWithSelection] init failed/timeout:", err); -2033: } finally { -2034: try { preFetchedNews = await newsPromise; } catch { preFetchedNews = []; } -2035: newsSeq = preFetchedNews.slice(); -2036: newsSeqIdx = -1; -2037: newsTopicQuery = ""; -2038: loading.classList.remove("active"); -2039: if (ok) setCaption(CAPTIONS.idle); -2040: else setCaption("AVATAR KHÔNG HIỂN THỊ — THỬ CHỌN AVATAR KHÁC", "error"); -2041: setMainButton("start", "Start talking"); -2042: } -2043: } -2044: const pickerStartBtn = document.getElementById("picker-start"); -2045: if (pickerStartBtn) pickerStartBtn.addEventListener("click", () => void startWithSelection()); -=== index.html topic-tags line === -H50: \ No newline at end of file diff --git a/_del.txt b/_del.txt deleted file mode 100644 index 78f27bdc643ec96cbc74819ebeb42f7cb557d086..0000000000000000000000000000000000000000 --- a/_del.txt +++ /dev/null @@ -1,10 +0,0 @@ -delete HTTP 404 - - - -Cannot DELETE /api/repos/bep40/summarizer- - diff --git a/_diag.txt b/_diag.txt deleted file mode 100644 index 252b3750d43cc201ba14724e7401db31c1f0d0c3..0000000000000000000000000000000000000000 --- a/_diag.txt +++ /dev/null @@ -1,9 +0,0 @@ -=== /api/summarize === -fallback: True -title: Người dùng: Gemma 3 ra mắt hôm nay hỗ trợ 140 ngôn ngữ và chạy tốt trên điện tho -body: • Người dùng: Gemma 3 ra mắt hôm nay hỗ trợ 140 ngôn ngữ và chạy tốt trên điện thoại. -Avatar: Đúng, nó có các bản từ 1B đến 27B, rất nhẹ. - -=== VNEWS /api/publish === -/api/publish -> HTTP 400 {"error":"Thieu noi dung bai viet"} -/api/wall -> OK: {"post": {"id": "1784377022902", "title": "t", "text": "x", "source": "user", "video": null, "img": "https://vnexpress.net/image1.jpg", "images": [], "created": 1784377022, "created_str": "12:17 18/07/2026"}, "ok": true} \ No newline at end of file diff --git a/_fb.txt b/_fb.txt deleted file mode 100644 index 42912233ee680c6e9bcbbf523e717ff68a2523b4..0000000000000000000000000000000000000000 --- a/_fb.txt +++ /dev/null @@ -1,24 +0,0 @@ -t = out.slice(mm.index + mm[0].length).trim(); } - if (!title) title = bodyText.slice(0, 80); - return Response.json({ title, body: bodyText || out }); - } - // ── No-model fallback: light paraphrase (NEVER verbatim copy) ── - // Re-flow the chat lines into a short prose summary with a title, - // instead of echoing the raw transcript. - const lines = cleanText.split(/\n+/).map((s) => s.trim()).filter(Boolean); - const titleFb = lines[0] ? (lines[0].length > 80 ? lines[0].slice(0, 77) + "..." : lines[0]) : "Tóm tắt cuộc trò chuyện"; - const bodyFb = lines - .map((l) => l.charAt(0).toUpperCase() + l.slice(1)) - .join(" ") - .replace(/\s+/g, " ") - .trim(); - return Response.json({ title: titleFb, body: bodyFb || cleanText, fallback: true }); - } catch (e) { - console.warn(`[summarize] ${e}`); - return Response.json({ error: "summarize failed" }, { status: 502 }); - } - }, - }, - "/api/avatars": { - GET: async () => { - \ No newline at end of file diff --git a/_final_test.txt b/_final_test.txt deleted file mode 100644 index 45addfdd80d26e8e70aeb2990ed3d9d717696353..0000000000000000000000000000000000000000 --- a/_final_test.txt +++ /dev/null @@ -1,5 +0,0 @@ -OK in 0.1s -fallback: True -title: Người dùng: Gemma 3 ra mắt hôm nay hỗ trợ 140 ngôn ngữ và chạy tốt trên điện tho -body: • Người dùng: Gemma 3 ra mắt hôm nay hỗ trợ 140 ngôn ngữ và chạy tốt trên điện thoại. -Avatar: Đúng, nó có các bản từ 1B đến 27B, rất nhẹ. \ No newline at end of file diff --git a/_glbchk.txt b/_glbchk.txt deleted file mode 100644 index 5a028fd2f290652fa446e9f610469c3c41275a49..0000000000000000000000000000000000000000 --- a/_glbchk.txt +++ /dev/null @@ -1,5 +0,0 @@ -Ella.glb downloaded: /root/.cache/huggingface/hub/spaces--bep40--gemma-multi-avatar/snapshots/93b62270a930420118377bd4fde3537c7016cdb5/public/avatars/Ella.glb size=2857KB -trimesh loaded. geometry count: 9 -has SKIN: True -has anim: True -hasImages/texture: True \ No newline at end of file diff --git a/_hw.txt b/_hw.txt deleted file mode 100644 index 46e03c01926d7617a05372a92ef7289bc89cf7a7..0000000000000000000000000000000000000000 --- a/_hw.txt +++ /dev/null @@ -1,4 +0,0 @@ -settings {'hardware': 'zero-a10g', 'zero_gpu': True}: 200 {} -settings {'hardware': 'zero-a10g'}: 200 {} -runtime: {"current": null, "requested": "cpu-basic"} -stage: RUNTIME_ERROR \ No newline at end of file diff --git a/_hw2.txt b/_hw2.txt deleted file mode 100644 index be11b37b1c535e68eb27674f0a42f52019424403..0000000000000000000000000000000000000000 --- a/_hw2.txt +++ /dev/null @@ -1,4 +0,0 @@ -/api/spaces/bep40/summarizer/hardware HTTP 401 {"error":"Invalid username or password."} -/api/spaces/bep40/summarizer/settings PUT: 200 {} -runtime hw: {"current": null, "requested": "cpu-basic"} -stage: RUNTIME_ERROR \ No newline at end of file diff --git a/_hw3.txt b/_hw3.txt deleted file mode 100644 index 3d72869e33dac7809d45017cdac29028feea6e6b..0000000000000000000000000000000000000000 --- a/_hw3.txt +++ /dev/null @@ -1,3 +0,0 @@ -hw: 200 {} -create-hw HTTP 409 {"error":"You already created this space repo: bep40/summarizer","url":"https://huggingface.co/spaces/bep40/summarizer"} -hw now: {"current": null, "requested": "cpu-basic"} \ No newline at end of file diff --git a/_idxchk.txt b/_idxchk.txt deleted file mode 100644 index 7687ad820d7a16fba1a71f228e95a389c33d7854..0000000000000000000000000000000000000000 --- a/_idxchk.txt +++ /dev/null @@ -1,6 +0,0 @@ -len 23470 -has 'fallback: true': True -has 'No-model fallback': True -has 're-flow': False -has SUMMARY_ENDPOINT: True -has 'bullet': True \ No newline at end of file diff --git a/_logs3.txt b/_logs3.txt deleted file mode 100644 index 6f1d713509230b6c50efff6cf5fa9deaa4c83d0d..0000000000000000000000000000000000000000 --- a/_logs3.txt +++ /dev/null @@ -1,7 +0,0 @@ -=== build === -data: {"data":"===== Build Queued at 2026-07-18 15:51:06 / Commit SHA: e1a9445 =====\n","timestamp":"2026-07-18T15:51:06Z"} - -data: {"data":"\n--> ERROR: docker.io/huggingface/zero-gpu:latest: not found\n","timestamp":"2026-07-18T15:51:10.975Z"} - - -app HTTP 404 {"error":"Sorry, we can't find the page you are looking for."} \ No newline at end of file diff --git a/_logs4.txt b/_logs4.txt deleted file mode 100644 index 3d0670c890d1e9163437d9e1e16fbd141869ed42..0000000000000000000000000000000000000000 --- a/_logs4.txt +++ /dev/null @@ -1,40 +0,0 @@ -state ERR NameError("name 'json' is not defined") -app HTTP 404 {"error":"Sorry, we can't find the page you are looking for."} -=== build === -data: {"data":"===== Build Queued at 2026-07-19 01:41:01 / Commit SHA: cb0f05e =====\n","timestamp":"2026-07-19T01:41:01Z"} - -data: {"data":"--> FROM docker.io/library/python:3.12.12@sha256:cb770de9f47e77715f13434d96f7ebaae7ad3a1f4fd9c8b338549bf99c9980ab","timestamp":"2026-07-19T01:41:06.284Z"} - -data: {"data":"DONE 0.0s\n","timestamp":"2026-07-19T01:41:06.284Z"} - -data: {"data":"--> WORKDIR /home/user/app","timestamp":"2026-07-19T01:41:06.386Z"} - -data: {"data":"CACHED\n","timestamp":"2026-07-19T01:41:06.386Z"} - -data: {"data":"--> RUN apt-get update && apt-get install -y \tgit \tgit-lfs \tffmpeg \tlibsm6 \tlibxext6 \tcmake \trsync \tlibgl1 \t&& rm -rf /var/lib/apt/lists/* \t&& git lfs install","timestamp":"2026-07-19T01:41:06.386Z"} - -data: {"data":"CACHED\n","timestamp":"2026-07-19T01:41:06.386Z"} - -data: {"data":"--> RUN pip install --no-cache-dir pip -U && pip install --no-cache-dir datasets \"huggingface-hub>=0.30\" \"hf-transfer>=0.1.4\" \"protobuf<4\" \"click<8.1\" torch==2.11.0","timestamp":"2026-07-19T01:41:06.386Z"} - -data: {"data":"CACHED\n","timestamp":"2026-07-19T01:41:06.386Z"} - -data: {"data":"--> RUN apt-get update && apt-get install -y fakeroot && mv /usr/bin/apt-get /usr/bin/.apt-get && echo '#!/usr/bin/env sh\\nfakeroot /usr/bin/.apt-get $@' > /usr/bin/apt-get && chmod +x /usr/bin/apt-get && \trm -rf /var/lib/apt/lists/* && \tuseradd -m -u 1000 user","timestamp":"2026-07-19T01:41:06.386Z"} - -data: {"data":"CACHED\n","timestamp":"2026-07-19T01:41:06.386Z"} - -data: {"data":"--> RUN \tapt-get update && \tapt-get install -y curl && \tcurl -fsSL https://deb.nodesource.com/setup_20.x | bash - && \tapt-get install -y nodejs && \trm -rf /var/lib/apt/lists/* && apt-get clean","timestamp":"2026-07-19T01:41:06.386Z"} - -data: {"data":"CACHED\n","timestamp":"2026-07-19T01:41:06.386Z"} - -data: {"data":"--> COPY --chown=1000:1000 --from=root / /","timestamp":"2026-07-19T01:41:06.386Z"} - -data: {"data":"CACHED\n","timestamp":"2026-07-19T01:41:06.386Z"} - -data: {"data":"--> RUN chown -R 1000:1000 /var/","timestamp":"2026-07-19T01:41:06.386Z"} - -data: {"data":"CACHED\n\n--> Restoring cache","timestamp":"2026-07-19T01:41:06.386Z"} - -data: {"data":"DONE 24.0s\n\n--> RUN --mount=target=/tmp/requirements.txt,source=requirements.txt pip install --no-cache-dir -r /tmp/requirements.txt \"torch<=2.11.0\" gradio[oauth,mcp]==6.20.0 \"uvicorn>=0.14.0\" \"websockets>=10.4\" spaces==0.51.0","timestamp":"2026-07-19T01:41:30.478Z"} - -d \ No newline at end of file diff --git a/_mintest.txt b/_mintest.txt deleted file mode 100644 index df886d7caceec4e8236a275a9bf31179ccea32d8..0000000000000000000000000000000000000000 --- a/_mintest.txt +++ /dev/null @@ -1,10 +0,0 @@ -stage: RUNTIME_ERROR -HTTP 404 - - - - - HTTP 403 -