Files
OpenSquawk/nuxt.config.ts
leubeem d6df3a3ce3 Wire /pm to Python backend for stateful ATC training sessions
Replace the LLM-per-request flow in /pm with a stateful Python backend
(OpenSquawk-LiveATC-api). The backend owns session state, does regex-first
routing with readback evaluation, and returns the next state + ATC speech.
The frontend keeps its local cursor (communicationsEngine) for TTS and
monitoring UI, but no longer calls /api/llm/decide.

Changes:

app/composables/useRadioBackend.ts (new)
  Typed Nuxt composable wrapping the Python REST API:
  createSession, transmit, deleteSession, fetchFlows.
  Base URL read from NUXT_PUBLIC_RADIO_BACKEND_URL (default 127.0.0.1:8000).

nuxt.config.ts
  Expose radioBackendUrl as a public runtime config key so the composable
  and communicationsEngine can both reach the Python backend.

shared/utils/communicationsEngine.ts
  - fetchRuntimeTree now accepts an optional baseUrl so it fetches from the
    Python backend instead of the Nuxt server when a URL is provided.
  - renderTpl handles both {var} (old MongoDB schema) and {{var}} (new YAML
    schema) — double-brace matched first to avoid partial matches.
  - stateSayTpl / stateUtteranceTpl helpers unify say_tpl|say_template and
    utterance_tpl|expected_pilot_template across both schema versions.
  - auto_transitions from the new YAML schema are included when collecting
    eligible transitions in collectAtcStatesUntilPilotTurn.

shared/types/decision.ts
  RuntimeDecisionState extended with say_template and expected_pilot_template
  fields (new YAML schema field names alongside the existing legacy names).

app/pages/pm.vue
  - startMonitoring: loads tree from Python backend, then creates a backend
    session (backendSessionId). Cursor synced to session.current_state.
  - handlePilotTransmission: calls radioBackend.transmit instead of
    /api/llm/decide. Applies auto_advanced_states via moveToSilent, then
    the final state. Speaks controller_say_template via TTS.
  - Both fetchRuntimeTree calls now pass radioBackendUrl so they hit the
    Python backend, not the Nuxt flow-from-MongoDB path.

AGENTS.md (new)
  Project guide updated to document the new two-backend architecture,
  the Python backend session lifecycle, and the dual template schema.

docs/plans/2026-05-06-pm-python-runtime-contract.md (new)
  Implementation plan and API contract written before the work started.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 17:49:28 +02:00

114 lines
3.8 KiB
TypeScript

// nuxt.config.ts
export default defineNuxtConfig({
compatibilityDate: '2025-07-15',
devtools: {enabled: false},
ssr: false,
typescript: {
strict: false,
typeCheck: true,
tsConfig: {
compilerOptions: {
noUncheckedIndexedAccess: false,
noImplicitOverride: false,
lib: ['ESNext', 'dom', 'dom.iterable', 'webworker'],
types: ['vite/client'],
},
vueCompilerOptions: {
strictTemplates: false,
},
},
},
modules: [
'vuetify-nuxt-module',
'@nuxtjs/tailwindcss',
'nuxt-aos',
'@pinia/nuxt',
'nuxt-mongoose',
'@nuxt/image',
'nuxt-module-hotjar',
],
hotjar: {
hotjarId: 6522897,
scriptVersion: 6,
debug: process.env.NODE_ENV !== 'production',
},
aos: {once: true, duration: 600, easing: 'ease-out'},
app: {head: {link: [{rel: 'icon', type: 'image/jpeg', href: '/img/icon-sm.jpeg'}]}},
runtimeConfig: {
openaiKey: process.env.OPENAI_API_KEY,
openaiProject: process.env.OPENAI_PROJECT,
openaiBaseUrl: process.env.OPENAI_BASE_URL,
llmModel: process.env.LLM_MODEL || 'gpt-5-nano',
ttsModel: process.env.TTS_MODEL || 'tts-1',
defaultVoiceId: process.env.VOICE_ID || 'alloy',
openaipApiKey: process.env.OPENAIP_API_KEY,
usePiper: process.env.USE_PIPER,
piperPort: process.env.PIPER_PORT,
useSpeaches: process.env.USE_SPEACHES,
speachesBaseUrl: process.env.SPEACHES_BASE_URL,
speechModelId: process.env.SPEECH_MODEL_ID,
domeLightWebhookUrl: process.env.DOME_LIGHT_WEBHOOK_URL || 'https://home.io.faktorxmensch.com/api/webhook/lidl_stab_3modi_8492',
jwtSecret: process.env.JWT_SECRET,
jwtRefreshSecret: process.env.JWT_REFRESH_SECRET || process.env.JWT_SECRET,
manualInvitePassword: process.env.MANUAL_INVITE_PASSWORD,
mongoose: {
uri: process.env.MONGODB_URI || 'mongodb://127.0.0.1:27017/opensquawk',
options: {},
},
public: {
apiDocumentationUrl: '/api-docs',
radioBackendUrl: process.env.NUXT_PUBLIC_RADIO_BACKEND_URL || 'http://127.0.0.1:8000',
},
},
vuetify: {
vuetifyOptions: {
theme: {
defaultTheme: 'opensquawkDark',
themes: {
opensquawkDark: {
dark: true,
colors: {
background: '#0b1020',
surface: '#0a0f1c',
primary: '#22d3ee',
secondary: '#0ea5e9',
info: '#22d3ee',
success: '#22c55e',
warning: '#f59e0b',
error: '#ef4444',
// Text-Kontrast
'on-background': '#ffffff',
'on-surface': '#ffffff'
}
}
}
}
}
},
css: [
'~/assets/css/global.css',
'~/assets/css/opensquawk-glass.css',
'~/assets/css/learn-theme.css'
],
nitro: {
experimental: {
websocket: true,
},
typescript: {
strict: false,
tsConfig: {
compilerOptions: {
noUncheckedIndexedAccess: false,
noImplicitOverride: false,
lib: ['ESNext', 'dom', 'dom.iterable', 'webworker'],
types: ['node', 'vite/client'],
},
},
},
},
image: {
provider: process.env.NUXT_IMAGE_PROVIDER || 'ipx',
},
})