UNPKG

@yoyo-org/progressive-json

Version:

Stream and render JSON data as it arrives - perfect for AI responses, large datasets, and real-time updates

170 lines (169 loc) 8.4 kB
/** * Domain-specific abstractions for Talmud text streaming * This module provides high-level hooks and utilities tailored for * Talmud analysis streaming use cases */ import { useCallback, useMemo } from "react"; import { useProgressiveJson } from "../useProgressiveJson"; /** * High-level hook for Talmud daf analysis streaming * Abstracts away the complexity of progressive JSON and provides * a clean, declarative API for Talmud-specific streaming */ export function useTalmudStream(options = {}) { const { baseUrl = "http://proxyman.debug:8000", onAnalysisComplete } = options; // Progressive JSON stream with Talmud-specific transforms const { store, rawStore, isStreaming, streamError, startFetching, stop, updateOptions } = useProgressiveJson({ url: "", // Will be set dynamically enabled: false, // Will be enabled when analysis starts // Transform: enhance sugyot with streaming metadata transform: useCallback((data) => { if (!data.sugyot || !data.progress) return data; const enhancedSugyot = data.sugyot.map((sugya, index) => { var _a; const isCurrentSugya = data.progress.current_sugya === index + 1; const isGeneratingBiur = data.progress.current_step === "generating_biur"; return { ...sugya, isStreaming: isCurrentSugya && isGeneratingBiur, streamingBiur: isCurrentSugya && isGeneratingBiur ? sugya.biur : "", completeBiur: !isCurrentSugya || !isGeneratingBiur ? sugya.biur : "", biurProgress: isCurrentSugya && isGeneratingBiur ? (((_a = sugya.biur) === null || _a === void 0 ? void 0 : _a.length) || 0) / 500 * 100 : // Rough progress estimate 100 }; }); return { ...data, sugyot: enhancedSugyot }; }, []), // Select: focus on the most relevant data for UI select: useCallback((data) => ({ daf: data.daf, masechet: data.masechet, sugyot: data.sugyot, progress: data.progress, metadata: data.metadata }), []), // Compare: smart change detection compare: useCallback((prev, next) => { var _a, _b, _c, _d, _e, _f; if (!prev || !next) return false; // Check key fields that matter for UI updates return (((_a = prev.progress) === null || _a === void 0 ? void 0 : _a.current_sugya) === ((_b = next.progress) === null || _b === void 0 ? void 0 : _b.current_sugya) && ((_c = prev.progress) === null || _c === void 0 ? void 0 : _c.current_step) === ((_d = next.progress) === null || _d === void 0 ? void 0 : _d.current_step) && ((_e = prev.sugyot) === null || _e === void 0 ? void 0 : _e.length) === ((_f = next.sugyot) === null || _f === void 0 ? void 0 : _f.length) && prev.analysis_raw === next.analysis_raw); }, []), // Lifecycle hooks onStreamStart: useCallback(() => { // Could trigger UI loading states }, []), onStreamEnd: useCallback((data) => { onAnalysisComplete === null || onAnalysisComplete === void 0 ? void 0 : onAnalysisComplete(data); }, [onAnalysisComplete]), onStreamError: useCallback((error) => { console.error("Talmud stream error:", error); }, []) }); // Compute derived streaming state const streamingState = useMemo(() => { var _a, _b, _c, _d, _e, _f, _g, _h; if (!store) { return { sugiyot: [], isAnalyzing: false, analysisProgress: 0, totalSugiyot: 0, currentStreamingSugya: null, error: null, currentStep: "", statusMessage: "", isStreamingRaw: isStreaming, rawContent: (rawStore === null || rawStore === void 0 ? void 0 : rawStore.analysis_raw) || "" }; } const analysisProgress = ((_a = store.progress) === null || _a === void 0 ? void 0 : _a.total_sugiyot) > 0 ? (store.progress.current_sugya / store.progress.total_sugiyot) * 100 : 0; const currentStreamingSugya = ((_b = store.progress) === null || _b === void 0 ? void 0 : _b.current_step) === "generating_biur" && store.progress.current_sugya > 0 ? store.progress.current_sugya - 1 : null; return { sugiyot: store.sugyot || [], isAnalyzing: ((_c = store.progress) === null || _c === void 0 ? void 0 : _c.status) !== "completed" && ((_d = store.progress) === null || _d === void 0 ? void 0 : _d.status) !== "error", analysisProgress, totalSugiyot: ((_e = store.progress) === null || _e === void 0 ? void 0 : _e.total_sugiyot) || 0, currentStreamingSugya, error: ((_f = store.progress) === null || _f === void 0 ? void 0 : _f.status) === "error" ? store.progress.message : (streamError === null || streamError === void 0 ? void 0 : streamError.message) || null, currentStep: ((_g = store.progress) === null || _g === void 0 ? void 0 : _g.current_step) || "", statusMessage: ((_h = store.progress) === null || _h === void 0 ? void 0 : _h.message) || "", isStreamingRaw: isStreaming, rawContent: (rawStore === null || rawStore === void 0 ? void 0 : rawStore.analysis_raw) || "" }; }, [store, rawStore, isStreaming, streamError]); // High-level API methods const startAnalysis = useCallback((dafNumber, masechet = "berakhot", includeBiur = true, biurLevel = 2) => { const url = `${baseUrl}/stream/analyze-daf?daf_number=${dafNumber}&masechet=${masechet}&include_biur=${includeBiur}&biur_level=${biurLevel}`; updateOptions({ url, enabled: true }); }, [baseUrl, updateOptions]); const stopAnalysis = useCallback(() => { stop(); updateOptions({ enabled: false }); }, [stop, updateOptions]); const restartAnalysis = useCallback(() => { stop(); setTimeout(() => startFetching(), 100); // Brief delay to ensure cleanup }, [stop, startFetching]); return { // Streaming state ...streamingState, // Raw data access rawData: rawStore, processedData: store, // Control methods startAnalysis, stopAnalysis, restartAnalysis, // Advanced control updateStreamOptions: updateOptions }; } /** * Utility hook for streaming individual biur generation * Provides a simpler API for biur-specific streaming */ export function useBiurStream(options = {}) { const { baseUrl = "http://proxyman.debug:8000" } = options; const { store, isStreaming, streamError, updateOptions } = useProgressiveJson({ url: "", enabled: false, transform: useCallback((data) => data, []), compare: useCallback((prev, next) => { return (prev === null || prev === void 0 ? void 0 : prev.biur) === (next === null || next === void 0 ? void 0 : next.biur) && (prev === null || prev === void 0 ? void 0 : prev.complete) === (next === null || next === void 0 ? void 0 : next.complete); }, []) }); const generateBiur = useCallback((dafNumber, sugyaIndex, level, sugyaTitle, sugyaContent, masechet = "berakhot") => { const params = new URLSearchParams({ daf_number: dafNumber, sugya_index: sugyaIndex.toString(), level: level.toString(), sugya_title: sugyaTitle, sugya_content: sugyaContent, masechet }); const url = `${baseUrl}/stream/generate-biur?${params}`; updateOptions({ url, enabled: true }); }, [baseUrl, updateOptions]); return { biur: (store === null || store === void 0 ? void 0 : store.biur) || "", isComplete: (store === null || store === void 0 ? void 0 : store.complete) || false, isStreaming, error: (streamError === null || streamError === void 0 ? void 0 : streamError.message) || null, generateBiur }; }