autollama
Version:
Modern JavaScript-first RAG framework with contextual embeddings, professional CLI, and one-command deployment
162 lines (141 loc) • 4.63 kB
text/typescript
import { useState, useEffect, useCallback } from 'react';
import { FileData, PipelineStats, UsePipelineState } from '../types';
const API_BASE = '/api';
export function usePipelineState(): UsePipelineState {
const [files, setFiles] = useState<FileData[]>([]);
const [selectedFile, setSelectedFile] = useState<FileData | null>(null);
const [stats, setStats] = useState<PipelineStats>({
totalCost: 0,
throughput: 0,
activeFiles: 0,
totalFiles: 0,
recentFiles: [],
systemHealth: {
api: false,
bm25: false,
qdrant: false,
postgres: false
}
});
const [isConnected, setIsConnected] = useState(false);
const [lastUpdate, setLastUpdate] = useState<string | null>(null);
// Fetch initial data
const fetchFiles = useCallback(async () => {
try {
const response = await fetch(`${API_BASE}/documents`);
if (response.ok) {
const { documents } = await response.json();
const filesArray = documents.map((doc: any) => ({
id: doc.id,
name: doc.title || doc.url,
status: 'completed', // Assuming all fetched documents are complete
progress: 100,
totalChunks: doc.chunk_count || 0,
processedChunks: doc.chunk_count || 0,
completedChunks: doc.chunk_count || 0,
startedAt: doc.created_at || new Date().toISOString(),
lastActivity: doc.updated_at || new Date().toISOString(),
completedAt: doc.updated_at,
url: doc.url,
chunks: [] // Chunks will be fetched on demand
}));
setFiles(filesArray);
setStats(prev => ({
...prev,
totalFiles: filesArray.length,
activeFiles: filesArray.filter(f => f.status === 'processing').length,
recentFiles: filesArray.slice(0, 10)
}));
setIsConnected(true);
}
} catch (error) {
console.error('Failed to fetch files:', error);
setIsConnected(false);
}
}, []);
// Fetch system health
const fetchHealth = useCallback(async () => {
try {
const [apiHealth, bm25Health] = await Promise.allSettled([
fetch(`${API_BASE}/health`),
fetch(`${API_BASE}/health`) // We'll add BM25 health endpoint later
]);
setStats(prev => ({
...prev,
systemHealth: {
api: apiHealth.status === 'fulfilled' && apiHealth.value.ok,
bm25: bm25Health.status === 'fulfilled' && bm25Health.value.ok,
qdrant: true, // We'll implement proper health checks
postgres: true
}
}));
} catch (error) {
console.error('Health check failed:', error);
}
}, []);
// Update file with real-time data
const updateFile = useCallback((fileUpdate: Partial<FileData> & { id: string }) => {
setFiles(prev => prev.map(file =>
file.id === fileUpdate.id
? { ...file, ...fileUpdate, lastActivity: new Date().toISOString() }
: file
));
setLastUpdate(new Date().toISOString());
}, []);
// Add new file (for new processing sessions)
const addFile = useCallback((newFile: FileData) => {
setFiles(prev => {
const exists = prev.find(f => f.id === newFile.id);
if (exists) {
return prev.map(f => f.id === newFile.id ? newFile : f);
}
return [newFile, ...prev];
});
setStats(prev => ({
...prev,
totalFiles: prev.totalFiles + 1,
activeFiles: prev.activeFiles + (newFile.status === 'processing' ? 1 : 0)
}));
}, []);
// Initial data fetch
useEffect(() => {
fetchFiles();
fetchHealth();
// Refresh data periodically
const interval = setInterval(() => {
fetchFiles();
fetchHealth();
}, 30000); // 30 seconds
return () => clearInterval(interval);
}, [fetchFiles, fetchHealth]);
// Listen for SSE updates (for legacy compatibility)
useEffect(() => {
const eventSource = new EventSource(`${API_BASE}/stream`);
eventSource.onmessage = (event) => {
try {
const data = JSON.parse(event.data);
// Handle SSE updates here if needed
setLastUpdate(new Date().toISOString());
} catch (error) {
console.error('SSE parse error:', error);
}
};
eventSource.onerror = () => {
setIsConnected(false);
};
eventSource.onopen = () => {
setIsConnected(true);
};
return () => {
eventSource.close();
};
}, []);
return {
files,
selectedFile,
setSelectedFile,
stats,
isConnected,
lastUpdate
};
}