import { API_URL, API_KEY, TTS_URL, TTS_STATUS_URL } from './config.js' import { useSettingsStore } from '../stores/settings' function generateRequestId() { if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') { return crypto.randomUUID() } return `${Date.now()}-${Math.random().toString(16).slice(2)}` } function getCancelUrl(apiUrl) { const normalized = String(apiUrl || '').replace(/\/+$/, '') if (!normalized) return '/v1/completions/cancel' if (normalized.endsWith('/v1/completions')) { return `${normalized}/cancel` } return `${normalized}/cancel` } function normalizeAbortReason(reason) { if (typeof reason === 'string' && reason.trim()) { return reason.trim().slice(0, 64) } return 'abort' } async function sendCancelRequest(cancelUrl, requestId, reason) { try { await fetch(cancelUrl, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-API-Key': API_KEY, }, body: JSON.stringify({ request_id: requestId, reason, }), }) } catch { // Cancel request failed silently } } export async function fetchSuggestion(prefix, suffix, languageId, signal, apiUrl = API_URL) { let normalizedLanguageId = 'markdown' if (typeof languageId === 'string' && languageId.trim()) { normalizedLanguageId = languageId.trim() } else if (languageId && typeof languageId === 'object' && 'aborted' in languageId) { signal = languageId } if (typeof signal === 'string') { apiUrl = signal signal = undefined } const requestId = generateRequestId() const cancelUrl = getCancelUrl(apiUrl) const onAbort = () => { const reason = normalizeAbortReason(signal?.reason) void sendCancelRequest(cancelUrl, requestId, reason) } if (signal) { if (signal.aborted) { onAbort() } else { signal.addEventListener('abort', onAbort, { once: true }) } } try { const settings = useSettingsStore() const headers = { 'Content-Type': 'application/json', 'X-Request-Id': requestId, 'X-API-Key': API_KEY, } const body = { prefix, suffix, languageId: normalizedLanguageId, model_thinking: settings.modelThinking, privacy_mode: settings.privacyMode, user_preferences: { language: settings.language, currency: settings.currency, timezone: settings.detectedTimezone, }, } const res = await fetch(apiUrl, { method: 'POST', headers, body: JSON.stringify(body), signal, }) if (!res.ok) { const errorText = await res.text() throw new Error(`HTTP ${res.status}: ${errorText}`) } const data = await res.json() return data.content || '' } catch (e) { if (e.name === 'AbortError') { // ignore abort } else { throw e } } finally { if (signal) { signal.removeEventListener('abort', onAbort) } } } export async function fetchTTS(text, voice = 'af_bella', rate = 1.0, apiUrl = TTS_URL) { const res = await fetch(apiUrl, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-API-Key': API_KEY, }, body: JSON.stringify({ text, voice, rate, format: 'wav' }), }) if (!res.ok) { const errorText = await res.text() throw new Error(`TTS HTTP ${res.status}: ${errorText}`) } return res.json() } export async function fetchTTSStatus(apiUrl = TTS_STATUS_URL) { const res = await fetch(apiUrl, { headers: { 'X-API-Key': API_KEY }, }) if (!res.ok) { throw new Error(`TTS Status HTTP ${res.status}`) } return res.json() }