vk98's picture
Initial backend deployment - Hono proxy + ColPali embedding API
5dfbe50
import { Hono } from 'hono';
import { z } from 'zod';
import { config } from '../config';
import { cache, cacheKeys } from '../services/cache';
const searchApp = new Hono();
// Search request schema for GET requests
const searchQuerySchema = z.object({
query: z.string().min(1).max(500),
ranking: z.enum(['hybrid', 'colpali', 'bm25']).optional().default('hybrid'),
});
// Main search endpoint - matches Next.js /api/colpali-search
searchApp.get('/', async (c) => {
try {
const query = c.req.query('query');
const ranking = c.req.query('ranking') || 'hybrid';
const validation = searchQuerySchema.safeParse({ query, ranking });
if (!validation.success) {
return c.json({ error: 'Invalid request', details: validation.error.issues }, 400);
}
const validatedData = validation.data;
// Check cache
const cacheKey = `search:${validatedData.query}:${validatedData.ranking}`;
const cachedResult = cache.get(cacheKey);
if (cachedResult) {
c.header('X-Cache', 'HIT');
return c.json(cachedResult);
}
// Proxy to backend /fetch_results endpoint
const searchUrl = `${config.backendUrl}/fetch_results?query=${encodeURIComponent(validatedData.query)}&ranking=${validatedData.ranking}`;
const response = await fetch(searchUrl);
if (!response.ok) {
throw new Error(`Backend returned ${response.status}`);
}
const data = await response.json();
// Cache the result
cache.set(cacheKey, data);
c.header('X-Cache', 'MISS');
return c.json(data);
} catch (error) {
console.error('Search error:', error);
return c.json({
error: 'Search failed',
message: error instanceof Error ? error.message : 'Unknown error'
}, 500);
}
});
// Full image endpoint - matches Next.js /api/full-image
searchApp.get('/full-image', async (c) => {
try {
const docId = c.req.query('docId');
if (!docId) {
return c.json({ error: 'docId is required' }, 400);
}
// Check cache
const cacheKey = `fullimage:${docId}`;
const cachedImage = cache.get<{ base64_image: string }>(cacheKey);
if (cachedImage) {
c.header('X-Cache', 'HIT');
return c.json(cachedImage);
}
// Proxy to backend
const imageUrl = `${config.backendUrl}/full_image?doc_id=${encodeURIComponent(docId)}`;
const response = await fetch(imageUrl);
if (!response.ok) {
throw new Error(`Backend returned ${response.status}`);
}
const data = await response.json();
// Cache for 24 hours
cache.set(cacheKey, data, 86400);
c.header('X-Cache', 'MISS');
return c.json(data);
} catch (error) {
console.error('Full image error:', error);
return c.json({
error: 'Failed to fetch image',
message: error instanceof Error ? error.message : 'Unknown error'
}, 500);
}
});
// Query suggestions endpoint - matches Next.js /api/query-suggestions
searchApp.get('/suggestions', async (c) => {
try {
const query = c.req.query('query');
if (!query) {
return c.json({ suggestions: [] });
}
// Check cache
const cacheKey = `suggestions:${query}`;
const cachedSuggestions = cache.get(cacheKey);
if (cachedSuggestions) {
c.header('X-Cache', 'HIT');
return c.json(cachedSuggestions);
}
// Proxy to backend
const suggestionsUrl = `${config.backendUrl}/suggestions?query=${encodeURIComponent(query)}`;
const response = await fetch(suggestionsUrl);
if (!response.ok) {
throw new Error(`Backend returned ${response.status}`);
}
const data = await response.json();
// Cache for 5 minutes
cache.set(cacheKey, data, 300);
c.header('X-Cache', 'MISS');
return c.json(data);
} catch (error) {
console.error('Suggestions error:', error);
return c.json({
error: 'Failed to fetch suggestions',
suggestions: []
}, 500);
}
});
// Similarity maps endpoint - matches Next.js /api/similarity-maps
searchApp.get('/similarity-maps', async (c) => {
try {
const queryId = c.req.query('queryId');
const idx = c.req.query('idx');
const token = c.req.query('token');
const tokenIdx = c.req.query('tokenIdx');
if (!queryId || !idx || !token || !tokenIdx) {
return c.json({ error: 'Missing required parameters' }, 400);
}
// Note: Similarity maps are dynamic, so no caching
const simMapUrl = `${config.backendUrl}/get_sim_map?query_id=${encodeURIComponent(queryId)}&idx=${idx}&token=${encodeURIComponent(token)}&token_idx=${tokenIdx}`;
const response = await fetch(simMapUrl);
if (!response.ok) {
throw new Error(`Backend returned ${response.status}`);
}
// Backend returns HTML, so we need to return it as text
const html = await response.text();
return c.html(html);
} catch (error) {
console.error('Similarity map error:', error);
return c.json({
error: 'Failed to generate similarity map',
message: error instanceof Error ? error.message : 'Unknown error'
}, 500);
}
});
export { searchApp };