import React from 'react' import { QueryClient, QueryClientProvider, useMutation, useQuery, useQueryClient } from '@tanstack/react-query' import { queryDocument, queryDocumentStream, ingestDocument, listDocuments, listChunks, deleteDocument, deleteChunk, listPromptProfiles, getPromptProfile, activatePromptProfile, updatePrompt, updateAllPrompts, resetPrompts } from './api' import type { QueryRequest, QueryResponse, QueryStreamEvent, SourceMetadata, IngestResponse, DocumentListResponse, ChunkInfo, DeleteResponse, PromptProfileListResponse, PromptSetResponse, PromptUpdateRequest, PromptBatchUpdateRequest, PromptActivateResponse, PromptStatusResponse } from '../types' import { useState, useCallback, useRef } from 'react' export const queryClient = new QueryClient() export const useQueryDocument = () => { return useMutation({ mutationFn: queryDocument, }) } export interface QueryStreamState { extractedQuestions: string[] | null answer: string | null sources: SourceMetadata[] | null phase: 'idle' | 'decomposing' | 'retrieving' | 'filtering' | 'generating' | 'completed' | 'error' error: Error | null } export const useQueryDocumentStream = () => { const [state, setState] = useState({ extractedQuestions: null, answer: null, sources: null, phase: 'idle', error: null, }) const abortRef = useRef(null) const mutate = useCallback(async (request: QueryRequest) => { setState({ extractedQuestions: null, answer: null, sources: null, phase: 'decomposing', error: null, }) abortRef.current = new AbortController() try { await queryDocumentStream(request, (event: QueryStreamEvent) => { switch (event.phase) { case 'decomposed': setState(prev => ({ ...prev, extractedQuestions: event.extracted_questions ?? null, phase: 'retrieving', })) break case 'retrieving': setState(prev => ({ ...prev, phase: 'retrieving' })) break case 'filtering': setState(prev => ({ ...prev, phase: 'filtering' })) break case 'generating': setState(prev => ({ ...prev, phase: 'generating' })) break case 'completed': setState(prev => ({ ...prev, answer: event.answer ?? null, sources: event.sources ?? null, phase: 'completed', })) break case 'error': setState(prev => ({ ...prev, phase: 'error', error: new Error(event.message ?? 'Unknown error'), })) break } }, abortRef.current.signal) } catch (err) { if (err instanceof Error && err.name === 'AbortError') { setState(prev => ({ ...prev, phase: 'idle' })) return } setState(prev => ({ ...prev, phase: 'error', error: err instanceof Error ? err : new Error(String(err)), })) } }, []) const reset = useCallback(() => { abortRef.current?.abort() setState({ extractedQuestions: null, answer: null, sources: null, phase: 'idle', error: null, }) }, []) return { ...state, mutate, reset } } export const useIngestDocument = () => { return useMutation({ mutationFn: ingestDocument, }) } export const useDocuments = () => { return useQuery({ queryKey: ['documents'], queryFn: listDocuments, }) } export const useDocumentChunks = (documentId: string | null) => { return useQuery({ queryKey: ['documents', documentId, 'chunks'], queryFn: () => listChunks(documentId!), enabled: documentId !== null, }) } export const useDeleteDocument = () => { const queryClient = useQueryClient() return useMutation({ mutationFn: deleteDocument, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['documents'] }) }, }) } export const useDeleteChunk = () => { const queryClient = useQueryClient() return useMutation({ mutationFn: deleteChunk, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['documents'] }) }, }) } export const usePromptProfiles = () => { return useQuery({ queryKey: ['prompts', 'profiles'], queryFn: listPromptProfiles, }) } export const usePromptProfile = (name: string | null) => { return useQuery({ queryKey: ['prompts', 'profiles', name], queryFn: () => getPromptProfile(name!), enabled: name !== null, }) } export const useActivateProfile = () => { const queryClient = useQueryClient() return useMutation({ mutationFn: activatePromptProfile, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['prompts'] }) }, }) } export const useUpdatePrompt = () => { const queryClient = useQueryClient() return useMutation({ mutationFn: ({ name, step, request }) => updatePrompt(name, step, request), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['prompts'] }) }, }) } export const useUpdateAllPrompts = () => { const queryClient = useQueryClient() return useMutation({ mutationFn: ({ name, request }) => updateAllPrompts(name, request), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['prompts'] }) }, }) } export const useResetPrompts = () => { const queryClient = useQueryClient() return useMutation({ mutationFn: ({ name, step }) => resetPrompts(name, step), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['prompts'] }) }, }) } export const AppQueryProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => { return {children} }