colpali-backend-api / hono-proxy /client-example.ts
vk98's picture
Initial backend deployment - Hono proxy + ColPali embedding API
5dfbe50
/**
* Example client for integrating with the Hono proxy from Next.js
* Place this in your Next.js app at: lib/api-client.ts
*/
const API_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:4000/api';
export interface SearchResult {
root: {
children: Array<{
id: string;
relevance: number;
fields: {
id: string;
title: string;
page_number: number;
text: string;
image: string; // base64
image_url: string; // Added by proxy
full_image_url: string; // Added by proxy
};
}>;
};
}
export interface ChatMessage {
role: 'user' | 'assistant' | 'system';
content: string;
}
class ColPaliClient {
private async fetchWithTimeout(url: string, options: RequestInit, timeout = 30000) {
const controller = new AbortController();
const id = setTimeout(() => controller.abort(), timeout);
try {
const response = await fetch(url, {
...options,
signal: controller.signal,
});
return response;
} finally {
clearTimeout(id);
}
}
async search(query: string, limit = 10): Promise<SearchResult> {
const response = await this.fetchWithTimeout(`${API_URL}/search`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ query, limit }),
});
if (!response.ok) {
throw new Error(`Search failed: ${response.statusText}`);
}
return response.json();
}
async* chat(messages: ChatMessage[], context: string[] = []) {
const response = await fetch(`${API_URL}/chat`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ messages, context }),
});
if (!response.ok) {
throw new Error(`Chat failed: ${response.statusText}`);
}
const reader = response.body?.getReader();
if (!reader) throw new Error('No response body');
const decoder = new TextDecoder();
let buffer = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split('\\n');
buffer = lines.pop() || '';
for (const line of lines) {
if (line.startsWith('data: ')) {
const data = line.slice(6);
if (data === '[DONE]') return;
try {
const parsed = JSON.parse(data);
yield parsed;
} catch (e) {
console.error('Failed to parse SSE data:', e);
}
}
}
}
}
async getSimilarityMap(docId: string, query: string) {
const response = await this.fetchWithTimeout(`${API_URL}/search/similarity-map`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ docId, query }),
});
if (!response.ok) {
throw new Error(`Similarity map failed: ${response.statusText}`);
}
return response.json();
}
getImageUrl(docId: string, type: 'thumbnail' | 'full' = 'thumbnail'): string {
return `${API_URL}/search/image/${docId}/${type}`;
}
async checkHealth() {
const response = await this.fetchWithTimeout(`${API_URL.replace('/api', '')}/health`, {
method: 'GET',
}, 5000);
return response.json();
}
}
// Export singleton instance
export const colpaliClient = new ColPaliClient();
// Usage examples:
/*
// In your Next.js component or API route:
// Search
const results = await colpaliClient.search('annual report 2023', 20);
// Display images directly from proxy URLs
results.root.children.forEach(hit => {
const imageUrl = hit.fields.image_url; // Proxy URL for thumbnail
const fullImageUrl = hit.fields.full_image_url; // Proxy URL for full image
});
// Chat with streaming
const messages = [{ role: 'user', content: 'What is the revenue?' }];
for await (const chunk of colpaliClient.chat(messages)) {
console.log(chunk);
}
// Get image URL for direct use in <img> tags
const imageUrl = colpaliClient.getImageUrl('doc123', 'thumbnail');
// Check system health
const health = await colpaliClient.checkHealth();
*/