import { useRuntimeConfig } from '#imports' export interface ServerRuntimeConfig { openaiKey: string openaiProject?: string openaiBaseUrl?: string llmModel: string ttsModel: string voiceId: string openaipApiKey?: string usePiper: boolean piperPort: number useSpeaches: boolean speachesBaseUrl?: string speechModelId: string } let cachedConfig: ServerRuntimeConfig | null = null let warnedMissingOpenAIKey = false function toBoolean(value: unknown, fallback = false): boolean { if (typeof value === 'boolean') { return value } if (typeof value === 'string') { const normalized = value.trim().toLowerCase() if (!normalized) { return fallback } if (['1', 'true', 'yes', 'on'].includes(normalized)) { return true } if (['0', 'false', 'no', 'off'].includes(normalized)) { return false } } return fallback } function toNumber(value: unknown, fallback: number): number { if (typeof value === 'number' && Number.isFinite(value)) { return value } if (typeof value === 'string' && value.trim()) { const parsed = Number.parseInt(value, 10) if (!Number.isNaN(parsed)) { return parsed } } return fallback } export function getServerRuntimeConfig(): ServerRuntimeConfig { if (cachedConfig) { return cachedConfig } const runtimeConfig = useRuntimeConfig() const openaiKey = String(runtimeConfig.openaiKey || '').trim() if (!openaiKey && !warnedMissingOpenAIKey) { console.warn('[OpenSquawk] OPENAI_API_KEY is missing. Some AI features are unavailable without a key.') warnedMissingOpenAIKey = true } const openaiProject = String(runtimeConfig.openaiProject || '').trim() || undefined const openaiBaseUrl = String(runtimeConfig.openaiBaseUrl || '').trim() || undefined const config: ServerRuntimeConfig = { openaiKey, openaiProject, openaiBaseUrl, llmModel: String(runtimeConfig.llmModel || '').trim() || 'gpt-5-nano', ttsModel: String(runtimeConfig.ttsModel || '').trim() || 'tts-1', voiceId: String(runtimeConfig.defaultVoiceId || '').trim() || 'alloy', openaipApiKey: String(runtimeConfig.openaipApiKey || '').trim() || undefined, usePiper: toBoolean(runtimeConfig.usePiper), piperPort: toNumber(runtimeConfig.piperPort, 5001), useSpeaches: toBoolean(runtimeConfig.useSpeaches), speachesBaseUrl: String(runtimeConfig.speachesBaseUrl || '').trim() || undefined, speechModelId: String(runtimeConfig.speechModelId || '').trim() || 'speaches-ai/piper-en_US-ryan-low', } cachedConfig = config return config } export function resetServerRuntimeConfigCache() { cachedConfig = null }