From 3488b54aa4e65edef31e3d745a064d2269a11228 Mon Sep 17 00:00:00 2001 From: itsrubberduck Date: Wed, 29 Jul 2026 08:54:13 +0200 Subject: [PATCH] feat(classroom): use local speech bridge for instructor TTS when available Mirrors the local-first routing live-atc already has via useRadioSpeech.ts: requestSayAudio now tries a locally running Speech Bridge (127.0.0.1:8765-8770) before falling back to the cloud /api/atc/say endpoint. --- app/pages/classroom.vue | 8 +- .../2026-07-29-classroom-local-bridge.md | 90 +++++++++++++++++++ 2 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 docs/plans/2026-07-29-classroom-local-bridge.md diff --git a/app/pages/classroom.vue b/app/pages/classroom.vue index ea9fe90..7863576 100644 --- a/app/pages/classroom.vue +++ b/app/pages/classroom.vue @@ -1487,6 +1487,7 @@ import { import {DEFAULT_AIRLINE_TELEPHONY, normalizeRadioPhrase, normalizeMetarPhrase} from '~~/shared/utils/radioSpeech' import {denormalizeSpokenAtc, looksLikeCallsignKey, normalizeForMatch} from '~~/shared/utils/sttMatch' import {useBugReport} from '~/composables/useBugReport' +import {postWithLocalFallback, useLocalSpeechBridge} from '~/composables/useLocalSpeechBridge' import BugReportDialog from '~/components/BugReportDialog.vue' definePageMeta({middleware: ['require-auth', 'require-classroom-intro']}) @@ -2850,6 +2851,7 @@ const promptReplayCount = ref(0) const replaySpeedHintSeen = ref(false) const api = useApi() +const {localUrl} = useLocalSpeechBridge() const isClient = typeof window !== 'undefined' const auth = useAuthStore() @@ -4327,7 +4329,11 @@ async function requestSayAudio(cacheKey: string, payload: Record { - const response: any = await api.post('/api/atc/say', payload) + const response: any = await postWithLocalFallback( + localUrl('/api/atc/say'), + payload, + () => api.post('/api/atc/say', payload), + ) const audioData = response?.audio if (!audioData?.base64) { throw new Error('Missing audio data') diff --git a/docs/plans/2026-07-29-classroom-local-bridge.md b/docs/plans/2026-07-29-classroom-local-bridge.md new file mode 100644 index 0000000..dc2ccb6 --- /dev/null +++ b/docs/plans/2026-07-29-classroom-local-bridge.md @@ -0,0 +1,90 @@ +# Classroom Local Speech Bridge Implementation Plan + +> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. + +**Goal:** Make classroom mode's instructor TTS use a locally running Speech Bridge when one is reachable, falling back to the existing cloud `/api/atc/say` path otherwise — same behavior live-atc already has. + +**Architecture:** Reuse the existing `app/composables/useLocalSpeechBridge.ts` composable unchanged. Wire its `useLocalSpeechBridge()` probe and `postWithLocalFallback()` helper into classroom's single TTS call site (`requestSayAudio` in `app/pages/classroom.vue`), mirroring exactly how `app/composables/useRadioSpeech.ts` already does it for live-atc. + +**Tech Stack:** Vue 3 `