/** * JARVIS API Service * Centralized axios-based API calls to the FastAPI backend. */ import axios from 'axios' // Base URL — uses Vite proxy in dev, env var in production const BASE_URL = import.meta.env.VITE_API_URL || '/api' const api = axios.create({ baseURL: BASE_URL, timeout: 120_000, // 2 min — model inference can be slow }) // ── Request interceptor ──────────────────────────── api.interceptors.request.use( (config) => { // Could add auth headers here in future return config }, (error) => Promise.reject(error), ) // ── Response interceptor ─────────────────────────── api.interceptors.response.use( (res) => res, (error) => { const message = error.response?.data?.detail || error.response?.data?.message || error.message || 'Unknown error' return Promise.reject(new Error(message)) }, ) // ─────────────────────────────────────────────────── // CHAT // ─────────────────────────────────────────────────── export const chatAPI = { /** * Send a chat message and receive AI response. * @param {string} message * @param {string|null} sessionId * @param {string|null} imageCaption */ sendMessage: async (message, sessionId = null, imageCaption = null) => { const res = await api.post('/chat/message', { message, session_id: sessionId, image_caption: imageCaption, }) return res.data }, /** * Get conversation history for a session. */ getHistory: async (sessionId, limit = 50) => { const res = await api.get(`/chat/history/${sessionId}`, { params: { limit }, }) return res.data }, } // ─────────────────────────────────────────────────── // IMAGE // ─────────────────────────────────────────────────── export const imageAPI = { /** * Upload image file for analysis + optional question. * @param {File} file * @param {string|null} question * @param {string|null} sessionId */ analyzeImage: async (file, question = null, sessionId = null) => { const formData = new FormData() formData.append('file', file) if (question) formData.append('question', question) if (sessionId) formData.append('session_id', sessionId) const res = await api.post('/image/analyze', formData, { headers: { 'Content-Type': 'multipart/form-data' }, timeout: 180_000, // vision models can be slow }) return res.data }, /** * Quick caption only — no LLM. */ quickCaption: async (file) => { const formData = new FormData() formData.append('file', file) const res = await api.post('/image/caption', formData, { headers: { 'Content-Type': 'multipart/form-data' }, }) return res.data }, } // ─────────────────────────────────────────────────── // VOICE // ─────────────────────────────────────────────────── export const voiceAPI = { /** * Transcribe audio Blob → text. * @param {Blob} audioBlob * @param {string} language optional ISO language code */ transcribe: async (audioBlob, language = null) => { const formData = new FormData() formData.append('file', audioBlob, 'recording.webm') if (language) formData.append('language', language) const res = await api.post('/voice/transcribe', formData, { headers: { 'Content-Type': 'multipart/form-data' }, timeout: 60_000, }) return res.data }, /** * Full voice round-trip: audio → text → AI → audio. * @param {Blob} audioBlob * @param {string|null} sessionId * @param {boolean} ttsEnabled */ voiceInteract: async (audioBlob, sessionId = null, ttsEnabled = true) => { const formData = new FormData() formData.append('file', audioBlob, 'recording.webm') if (sessionId) formData.append('session_id', sessionId) formData.append('tts_enabled', ttsEnabled.toString()) const res = await api.post('/voice/interact', formData, { headers: { 'Content-Type': 'multipart/form-data' }, timeout: 180_000, }) return res.data }, /** * Text-to-speech: returns audio blob. * @param {string} text */ speak: async (text) => { const res = await api.post( '/voice/speak', { text }, { responseType: 'blob', timeout: 60_000 }, ) return res.data // audio/wav Blob }, } // ─────────────────────────────────────────────────── // TOOLS // ─────────────────────────────────────────────────── export const toolsAPI = { /** * Execute a tool directly. * @param {string} toolName * @param {string} argument */ executeTool: async (toolName, argument) => { const res = await api.post('/tools/execute', { tool_name: toolName, argument, }) return res.data }, listTools: async () => { const res = await api.get('/tools/list') return res.data }, } // ─────────────────────────────────────────────────── // SESSIONS // ─────────────────────────────────────────────────── export const sessionsAPI = { listSessions: async () => { const res = await api.get('/sessions/') return res.data }, deleteSession: async (sessionId) => { const res = await api.delete(`/sessions/${sessionId}`) return res.data }, renameSession: async (sessionId, title) => { const res = await api.patch(`/sessions/${sessionId}`, { title }) return res.data }, } // ─────────────────────────────────────────────────── // HEALTH // ─────────────────────────────────────────────────── export const healthAPI = { check: async () => { const res = await api.get('/health') return res.data }, } export default api