Deva8's picture
Deploy VQA Space with model downloader
bb8f662
import axios from 'axios';
import { Platform } from 'react-native';
import { API_BASE_URL, API_ENDPOINTS, API_TIMEOUT } from '../config/api';
const apiClient = axios.create({
baseURL: API_BASE_URL,
timeout: API_TIMEOUT,
});
apiClient.interceptors.request.use(
(config) => {
console.log('API Request:', config.method?.toUpperCase(), config.url);
return config;
},
(error) => {
console.error('Request Error:', error);
return Promise.reject(error);
}
);
apiClient.interceptors.response.use(
(response) => {
console.log('API Response:', response.status, response.config.url);
return response;
},
(error) => {
console.error('Response Error:', error.response?.status, error.message);
return Promise.reject(error);
}
);
/**
* On web, expo-image-picker returns a blob: URL.
* Browsers require an actual Blob/File in FormData — the React Native
* { uri, name, type } shorthand only works in the native runtime.
* We fetch the blob on web and fall back to the native object on iOS/Android.
*/
const appendImageToFormData = async (formData, imageUri) => {
if (Platform.OS === 'web') {
// Fetch the blob: or data: URI and convert to a File object
const response = await fetch(imageUri);
const blob = await response.blob();
const filename = imageUri.split('/').pop() || 'image.jpg';
const ext = filename.split('.').pop() || 'jpg';
const mimeType = blob.type || `image/${ext}`;
const file = new File([blob], filename || `photo.${ext}`, { type: mimeType });
formData.append('image', file);
} else {
// React Native native runtime supports the { uri, name, type } shorthand
const filename = imageUri.split('/').pop() || 'photo.jpg';
const match = /\.(\w+)$/.exec(filename);
const type = match ? `image/${match[1]}` : 'image/jpeg';
formData.append('image', { uri: imageUri, name: filename, type });
}
};
export const askQuestion = async (imageUri, question) => {
try {
const formData = new FormData();
await appendImageToFormData(formData, imageUri);
formData.append('question', question);
// On web: do NOT set Content-Type manually — the browser adds the boundary automatically.
// On native: React Native's XHR needs the hint.
const headers = Platform.OS !== 'web' ? { 'Content-Type': 'multipart/form-data' } : {};
const response = await apiClient.post(API_ENDPOINTS.ANSWER, formData, { headers });
return response.data;
} catch (error) {
console.error('askQuestion error:', error);
if (error.response) {
throw new Error(error.response.data.detail || 'Server error');
} else if (error.request) {
throw new Error('Cannot connect to server. Please check if the backend is running.');
} else {
throw new Error(error.message || 'An error occurred');
}
}
};
export const checkHealth = async () => {
try {
const response = await apiClient.get(API_ENDPOINTS.HEALTH);
return response.data;
} catch (error) {
console.error('checkHealth error:', error);
throw error;
}
};
export const getModelsInfo = async () => {
try {
const response = await apiClient.get(API_ENDPOINTS.MODELS_INFO);
return response.data;
} catch (error) {
console.error('getModelsInfo error:', error);
throw error;
}
};
export default {
askQuestion,
checkHealth,
getModelsInfo,
};