1
This commit is contained in:
228
prisma/hooks/useAppLogic.ts
Normal file
228
prisma/hooks/useAppLogic.ts
Normal file
@@ -0,0 +1,228 @@
|
||||
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { ModelOption, AppConfig, ChatMessage } from '../types';
|
||||
import { STORAGE_KEYS, DEFAULT_CONFIG, getValidThinkingLevels } from '../config';
|
||||
import { useDeepThink } from './useDeepThink';
|
||||
import { useChatSessions } from './useChatSessions';
|
||||
import { setInterceptorUrl } from '../interceptor';
|
||||
|
||||
export const useAppLogic = () => {
|
||||
// Session Management
|
||||
const {
|
||||
sessions,
|
||||
currentSessionId,
|
||||
setCurrentSessionId,
|
||||
createSession,
|
||||
updateSessionMessages,
|
||||
deleteSession,
|
||||
getSession
|
||||
} = useChatSessions();
|
||||
|
||||
// UI State
|
||||
const [isSidebarOpen, setIsSidebarOpen] = useState(true);
|
||||
const [isSettingsOpen, setIsSettingsOpen] = useState(false);
|
||||
|
||||
// Active Chat State
|
||||
const [messages, setMessages] = useState<ChatMessage[]>([]);
|
||||
const [query, setQuery] = useState('');
|
||||
|
||||
// App Configuration with Persistence
|
||||
const [selectedModel, setSelectedModel] = useState<ModelOption>(() => {
|
||||
const cached = localStorage.getItem(STORAGE_KEYS.MODEL);
|
||||
return (cached as ModelOption) || 'gemini-3-flash-preview';
|
||||
});
|
||||
|
||||
const [config, setConfig] = useState<AppConfig>(() => {
|
||||
const cached = localStorage.getItem(STORAGE_KEYS.SETTINGS);
|
||||
if (cached) {
|
||||
try {
|
||||
return { ...DEFAULT_CONFIG, ...JSON.parse(cached) };
|
||||
} catch (e) {
|
||||
return DEFAULT_CONFIG;
|
||||
}
|
||||
}
|
||||
return DEFAULT_CONFIG;
|
||||
});
|
||||
|
||||
// Deep Think Engine
|
||||
const {
|
||||
appState,
|
||||
managerAnalysis,
|
||||
experts,
|
||||
finalOutput,
|
||||
synthesisThoughts,
|
||||
runDynamicDeepThink,
|
||||
stopDeepThink,
|
||||
resetDeepThink,
|
||||
processStartTime,
|
||||
processEndTime
|
||||
} = useDeepThink();
|
||||
|
||||
// Network Interceptor Sync
|
||||
useEffect(() => {
|
||||
if (config.enableCustomApi && config.customBaseUrl) {
|
||||
setInterceptorUrl(config.customBaseUrl);
|
||||
} else {
|
||||
setInterceptorUrl(null);
|
||||
}
|
||||
return () => setInterceptorUrl(null);
|
||||
}, [config.enableCustomApi, config.customBaseUrl]);
|
||||
|
||||
// Persistence Effects
|
||||
useEffect(() => {
|
||||
localStorage.setItem(STORAGE_KEYS.SETTINGS, JSON.stringify(config));
|
||||
}, [config]);
|
||||
|
||||
useEffect(() => {
|
||||
localStorage.setItem(STORAGE_KEYS.MODEL, selectedModel);
|
||||
}, [selectedModel]);
|
||||
|
||||
useEffect(() => {
|
||||
const cachedSessionId = localStorage.getItem(STORAGE_KEYS.SESSION_ID);
|
||||
if (cachedSessionId && sessions.some(s => s.id === cachedSessionId)) {
|
||||
setCurrentSessionId(cachedSessionId);
|
||||
}
|
||||
}, [sessions, setCurrentSessionId]);
|
||||
|
||||
useEffect(() => {
|
||||
if (currentSessionId) {
|
||||
localStorage.setItem(STORAGE_KEYS.SESSION_ID, currentSessionId);
|
||||
} else {
|
||||
localStorage.removeItem(STORAGE_KEYS.SESSION_ID);
|
||||
}
|
||||
}, [currentSessionId]);
|
||||
|
||||
// Handle Model Constraints
|
||||
useEffect(() => {
|
||||
const validLevels = getValidThinkingLevels(selectedModel);
|
||||
setConfig(prev => {
|
||||
const newPlanning = validLevels.includes(prev.planningLevel) ? prev.planningLevel : 'low';
|
||||
const newExpert = validLevels.includes(prev.expertLevel) ? prev.expertLevel : 'low';
|
||||
const newSynthesis = validLevels.includes(prev.synthesisLevel) ? prev.synthesisLevel : 'high';
|
||||
|
||||
if (newPlanning !== prev.planningLevel || newExpert !== prev.expertLevel || newSynthesis !== prev.synthesisLevel) {
|
||||
return {
|
||||
...prev,
|
||||
planningLevel: newPlanning as any,
|
||||
expertLevel: newExpert as any,
|
||||
synthesisLevel: newSynthesis as any,
|
||||
};
|
||||
}
|
||||
return prev;
|
||||
});
|
||||
}, [selectedModel]);
|
||||
|
||||
// Sync Messages when switching sessions
|
||||
useEffect(() => {
|
||||
if (currentSessionId) {
|
||||
const session = getSession(currentSessionId);
|
||||
if (session) {
|
||||
setMessages(session.messages);
|
||||
setSelectedModel(session.model || 'gemini-3-flash-preview');
|
||||
}
|
||||
} else {
|
||||
setMessages([]);
|
||||
}
|
||||
}, [currentSessionId, getSession]);
|
||||
|
||||
// Handle AI Completion
|
||||
useEffect(() => {
|
||||
if (appState === 'completed') {
|
||||
const finalizedMessage: ChatMessage = {
|
||||
id: `ai-${Date.now()}`,
|
||||
role: 'model',
|
||||
content: finalOutput,
|
||||
analysis: managerAnalysis,
|
||||
experts: experts,
|
||||
synthesisThoughts: synthesisThoughts,
|
||||
isThinking: false,
|
||||
totalDuration: (processStartTime && processEndTime) ? (processEndTime - processStartTime) : undefined
|
||||
};
|
||||
|
||||
const newMessages = [...messages, finalizedMessage];
|
||||
setMessages(newMessages);
|
||||
|
||||
if (currentSessionId) {
|
||||
updateSessionMessages(currentSessionId, newMessages);
|
||||
} else {
|
||||
createSession(newMessages, selectedModel);
|
||||
}
|
||||
|
||||
resetDeepThink();
|
||||
}
|
||||
}, [appState, finalOutput, managerAnalysis, experts, synthesisThoughts, resetDeepThink, processStartTime, processEndTime, currentSessionId, messages, selectedModel, createSession, updateSessionMessages]);
|
||||
|
||||
const handleRun = useCallback(() => {
|
||||
if (!query.trim()) return;
|
||||
|
||||
const userMsg: ChatMessage = {
|
||||
id: `user-${Date.now()}`,
|
||||
role: 'user',
|
||||
content: query
|
||||
};
|
||||
|
||||
const newMessages = [...messages, userMsg];
|
||||
setMessages(newMessages);
|
||||
|
||||
let activeSessionId = currentSessionId;
|
||||
if (!activeSessionId) {
|
||||
activeSessionId = createSession(newMessages, selectedModel);
|
||||
} else {
|
||||
updateSessionMessages(activeSessionId, newMessages);
|
||||
}
|
||||
|
||||
runDynamicDeepThink(query, messages, selectedModel, config);
|
||||
setQuery('');
|
||||
}, [query, messages, currentSessionId, selectedModel, config, createSession, updateSessionMessages, runDynamicDeepThink]);
|
||||
|
||||
const handleNewChat = useCallback(() => {
|
||||
stopDeepThink();
|
||||
setCurrentSessionId(null);
|
||||
setMessages([]);
|
||||
setQuery('');
|
||||
resetDeepThink();
|
||||
if (window.innerWidth < 1024) setIsSidebarOpen(false);
|
||||
}, [stopDeepThink, setCurrentSessionId, resetDeepThink]);
|
||||
|
||||
const handleSelectSession = useCallback((id: string) => {
|
||||
stopDeepThink();
|
||||
resetDeepThink();
|
||||
setCurrentSessionId(id);
|
||||
if (window.innerWidth < 1024) setIsSidebarOpen(false);
|
||||
}, [stopDeepThink, resetDeepThink, setCurrentSessionId]);
|
||||
|
||||
const handleDeleteSession = useCallback((id: string, e: React.MouseEvent) => {
|
||||
e.stopPropagation();
|
||||
deleteSession(id);
|
||||
if (currentSessionId === id) {
|
||||
handleNewChat();
|
||||
}
|
||||
}, [deleteSession, currentSessionId, handleNewChat]);
|
||||
|
||||
return {
|
||||
sessions,
|
||||
currentSessionId,
|
||||
messages,
|
||||
query,
|
||||
setQuery,
|
||||
selectedModel,
|
||||
setSelectedModel,
|
||||
config,
|
||||
setConfig,
|
||||
isSidebarOpen,
|
||||
setIsSidebarOpen,
|
||||
isSettingsOpen,
|
||||
setIsSettingsOpen,
|
||||
appState,
|
||||
managerAnalysis,
|
||||
experts,
|
||||
finalOutput,
|
||||
processStartTime,
|
||||
processEndTime,
|
||||
handleRun,
|
||||
handleNewChat,
|
||||
handleSelectSession,
|
||||
handleDeleteSession,
|
||||
stopDeepThink
|
||||
};
|
||||
};
|
||||
@@ -1,52 +1,37 @@
|
||||
import { useState, useRef, useCallback } from 'react';
|
||||
|
||||
import { useCallback } from 'react';
|
||||
import { getAI } from '../api';
|
||||
import { getThinkingBudget } from '../config';
|
||||
import { AppConfig, ModelOption, AppState, AnalysisResult, ExpertResult, ChatMessage } from '../types';
|
||||
import { AppConfig, ModelOption, ExpertResult, ChatMessage } from '../types';
|
||||
|
||||
import { executeManagerAnalysis } from '../services/deepThink/manager';
|
||||
import { executeManagerAnalysis, executeManagerReview } from '../services/deepThink/manager';
|
||||
import { streamExpertResponse } from '../services/deepThink/expert';
|
||||
import { streamSynthesisResponse } from '../services/deepThink/synthesis';
|
||||
import { useDeepThinkState } from './useDeepThinkState';
|
||||
|
||||
export const useDeepThink = () => {
|
||||
const [appState, setAppState] = useState<AppState>('idle');
|
||||
const [managerAnalysis, setManagerAnalysis] = useState<AnalysisResult | null>(null);
|
||||
const [experts, setExperts] = useState<ExpertResult[]>([]);
|
||||
const [finalOutput, setFinalOutput] = useState('');
|
||||
const [synthesisThoughts, setSynthesisThoughts] = useState('');
|
||||
|
||||
// Timing state
|
||||
const [processStartTime, setProcessStartTime] = useState<number | null>(null);
|
||||
const [processEndTime, setProcessEndTime] = useState<number | null>(null);
|
||||
const {
|
||||
appState, setAppState,
|
||||
managerAnalysis, setManagerAnalysis,
|
||||
experts, expertsDataRef,
|
||||
finalOutput, setFinalOutput,
|
||||
synthesisThoughts, setSynthesisThoughts,
|
||||
processStartTime, setProcessStartTime,
|
||||
processEndTime, setProcessEndTime,
|
||||
abortControllerRef,
|
||||
resetDeepThink,
|
||||
stopDeepThink,
|
||||
updateExpertAt,
|
||||
setInitialExperts,
|
||||
appendExperts
|
||||
} = useDeepThinkState();
|
||||
|
||||
// Refs for data consistency during high-frequency streaming updates
|
||||
const expertsDataRef = useRef<ExpertResult[]>([]);
|
||||
const abortControllerRef = useRef<AbortController | null>(null);
|
||||
|
||||
const stopDeepThink = useCallback(() => {
|
||||
if (abortControllerRef.current) {
|
||||
abortControllerRef.current.abort();
|
||||
abortControllerRef.current = null;
|
||||
}
|
||||
setAppState('idle');
|
||||
setProcessEndTime(Date.now());
|
||||
}, []);
|
||||
|
||||
const resetDeepThink = useCallback(() => {
|
||||
setAppState('idle');
|
||||
setManagerAnalysis(null);
|
||||
setExperts([]);
|
||||
expertsDataRef.current = [];
|
||||
setFinalOutput('');
|
||||
setSynthesisThoughts('');
|
||||
setProcessStartTime(null);
|
||||
setProcessEndTime(null);
|
||||
abortControllerRef.current = null;
|
||||
}, []);
|
||||
|
||||
// Helper: Orchestrate a single expert's lifecycle (Start -> Stream -> End)
|
||||
/**
|
||||
* Orchestrates a single expert's lifecycle (Start -> Stream -> End)
|
||||
*/
|
||||
const runExpertLifecycle = async (
|
||||
expert: ExpertResult,
|
||||
index: number,
|
||||
globalIndex: number,
|
||||
ai: any,
|
||||
model: ModelOption,
|
||||
context: string,
|
||||
@@ -55,17 +40,10 @@ export const useDeepThink = () => {
|
||||
): Promise<ExpertResult> => {
|
||||
if (signal.aborted) return expert;
|
||||
|
||||
// 1. Mark as thinking
|
||||
const startTime = Date.now();
|
||||
expertsDataRef.current[index] = {
|
||||
...expert,
|
||||
status: 'thinking',
|
||||
startTime
|
||||
};
|
||||
setExperts([...expertsDataRef.current]);
|
||||
updateExpertAt(globalIndex, { status: 'thinking', startTime });
|
||||
|
||||
try {
|
||||
// 2. Stream execution via service
|
||||
let fullContent = "";
|
||||
let fullThoughts = "";
|
||||
|
||||
@@ -79,44 +57,27 @@ export const useDeepThink = () => {
|
||||
(textChunk, thoughtChunk) => {
|
||||
fullContent += textChunk;
|
||||
fullThoughts += thoughtChunk;
|
||||
|
||||
// Update Ref & State live
|
||||
expertsDataRef.current[index] = {
|
||||
...expertsDataRef.current[index],
|
||||
thoughts: fullThoughts,
|
||||
content: fullContent
|
||||
};
|
||||
setExperts([...expertsDataRef.current]);
|
||||
updateExpertAt(globalIndex, { thoughts: fullThoughts, content: fullContent });
|
||||
}
|
||||
);
|
||||
|
||||
if (signal.aborted) return expertsDataRef.current[index];
|
||||
if (signal.aborted) return expertsDataRef.current[globalIndex];
|
||||
|
||||
// 3. Mark as completed
|
||||
expertsDataRef.current[index] = {
|
||||
...expertsDataRef.current[index],
|
||||
status: 'completed',
|
||||
endTime: Date.now()
|
||||
};
|
||||
setExperts([...expertsDataRef.current]);
|
||||
|
||||
return expertsDataRef.current[index];
|
||||
updateExpertAt(globalIndex, { status: 'completed', endTime: Date.now() });
|
||||
return expertsDataRef.current[globalIndex];
|
||||
|
||||
} catch (error) {
|
||||
console.error(`Expert ${expert.role} error:`, error);
|
||||
if (!signal.aborted) {
|
||||
expertsDataRef.current[index] = {
|
||||
...expertsDataRef.current[index],
|
||||
status: 'error',
|
||||
content: "Failed to generate response.",
|
||||
endTime: Date.now()
|
||||
};
|
||||
setExperts([...expertsDataRef.current]);
|
||||
updateExpertAt(globalIndex, { status: 'error', content: "Failed to generate response.", endTime: Date.now() });
|
||||
}
|
||||
return expertsDataRef.current[index];
|
||||
return expertsDataRef.current[globalIndex];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Main Orchestration logic
|
||||
*/
|
||||
const runDynamicDeepThink = async (
|
||||
query: string,
|
||||
history: ChatMessage[],
|
||||
@@ -125,20 +86,16 @@ export const useDeepThink = () => {
|
||||
) => {
|
||||
if (!query.trim()) return;
|
||||
|
||||
// Reset previous run
|
||||
if (abortControllerRef.current) {
|
||||
abortControllerRef.current.abort();
|
||||
}
|
||||
if (abortControllerRef.current) abortControllerRef.current.abort();
|
||||
abortControllerRef.current = new AbortController();
|
||||
const signal = abortControllerRef.current.signal;
|
||||
|
||||
// Reset UI state
|
||||
setAppState('analyzing');
|
||||
setManagerAnalysis(null);
|
||||
setExperts([]);
|
||||
expertsDataRef.current = [];
|
||||
setInitialExperts([]);
|
||||
setFinalOutput('');
|
||||
setSynthesisThoughts('');
|
||||
|
||||
setProcessStartTime(Date.now());
|
||||
setProcessEndTime(null);
|
||||
|
||||
@@ -152,33 +109,8 @@ export const useDeepThink = () => {
|
||||
`${msg.role === 'user' ? 'User' : 'Model'}: ${msg.content}`
|
||||
).join('\n');
|
||||
|
||||
// --- 1. Initialize Primary Expert IMMEDIATELY ---
|
||||
const primaryExpert: ExpertResult = {
|
||||
id: 'expert-0',
|
||||
role: "Primary Responder",
|
||||
description: "Directly addresses the user's original query.",
|
||||
temperature: 1,
|
||||
prompt: query,
|
||||
status: 'pending'
|
||||
};
|
||||
|
||||
expertsDataRef.current = [primaryExpert];
|
||||
setExperts([primaryExpert]);
|
||||
|
||||
// --- 2. Start Parallel Execution ---
|
||||
// --- Phase 1: Planning & Initial Experts ---
|
||||
|
||||
// Task A: Run Primary Expert (Index 0)
|
||||
const primaryExpertTask = runExpertLifecycle(
|
||||
primaryExpert,
|
||||
0,
|
||||
ai,
|
||||
model,
|
||||
recentHistory,
|
||||
getThinkingBudget(config.expertLevel, model),
|
||||
signal
|
||||
);
|
||||
|
||||
// Task B: Run Manager Analysis via Service
|
||||
const managerTask = executeManagerAnalysis(
|
||||
ai,
|
||||
model,
|
||||
@@ -187,60 +119,97 @@ export const useDeepThink = () => {
|
||||
getThinkingBudget(config.planningLevel, model)
|
||||
);
|
||||
|
||||
// Wait for Manager Analysis
|
||||
const analysisJson = await managerTask;
|
||||
const primaryExpert: ExpertResult = {
|
||||
id: 'expert-0',
|
||||
role: "Primary Responder",
|
||||
description: "Directly addresses the user's original query.",
|
||||
temperature: 1,
|
||||
prompt: query,
|
||||
status: 'pending',
|
||||
round: 1
|
||||
};
|
||||
|
||||
setInitialExperts([primaryExpert]);
|
||||
|
||||
const primaryTask = runExpertLifecycle(
|
||||
primaryExpert, 0, ai, model, recentHistory,
|
||||
getThinkingBudget(config.expertLevel, model), signal
|
||||
);
|
||||
|
||||
const analysisJson = await managerTask;
|
||||
if (signal.aborted) return;
|
||||
setManagerAnalysis(analysisJson);
|
||||
|
||||
// --- 3. Initialize & Run Supplementary Experts ---
|
||||
|
||||
const generatedExperts: ExpertResult[] = analysisJson.experts.map((exp, idx) => ({
|
||||
const round1Experts: ExpertResult[] = analysisJson.experts.map((exp, idx) => ({
|
||||
...exp,
|
||||
id: `expert-${idx + 1}`,
|
||||
status: 'pending'
|
||||
id: `expert-r1-${idx + 1}`,
|
||||
status: 'pending',
|
||||
round: 1
|
||||
}));
|
||||
|
||||
// Update state: Keep Primary (0) and append new ones
|
||||
const currentPrimary = expertsDataRef.current[0];
|
||||
const allExperts = [currentPrimary, ...generatedExperts];
|
||||
expertsDataRef.current = allExperts;
|
||||
setExperts([...allExperts]);
|
||||
|
||||
appendExperts(round1Experts);
|
||||
setAppState('experts_working');
|
||||
|
||||
// Task C: Run Supplementary Experts (Offset indices by 1)
|
||||
const supplementaryTasks = generatedExperts.map((exp, idx) =>
|
||||
runExpertLifecycle(
|
||||
exp,
|
||||
idx + 1,
|
||||
ai,
|
||||
model,
|
||||
recentHistory,
|
||||
getThinkingBudget(config.expertLevel, model),
|
||||
signal
|
||||
)
|
||||
const round1Tasks = round1Experts.map((exp, idx) =>
|
||||
runExpertLifecycle(exp, idx + 1, ai, model, recentHistory,
|
||||
getThinkingBudget(config.expertLevel, model), signal)
|
||||
);
|
||||
|
||||
// --- 4. Wait for ALL Experts ---
|
||||
const allResults = await Promise.all([primaryExpertTask, ...supplementaryTasks]);
|
||||
await Promise.all([primaryTask, ...round1Tasks]);
|
||||
if (signal.aborted) return;
|
||||
|
||||
// --- Phase 2: Recursive Loop (Optional) ---
|
||||
let roundCounter = 1;
|
||||
const MAX_ROUNDS = 3;
|
||||
let loopActive = config.enableRecursiveLoop ?? false;
|
||||
|
||||
while (loopActive && roundCounter < MAX_ROUNDS) {
|
||||
if (signal.aborted) return;
|
||||
setAppState('reviewing');
|
||||
|
||||
const reviewResult = await executeManagerReview(
|
||||
ai, model, query, expertsDataRef.current,
|
||||
getThinkingBudget(config.planningLevel, model)
|
||||
);
|
||||
|
||||
if (signal.aborted) return;
|
||||
if (reviewResult.satisfied) {
|
||||
loopActive = false;
|
||||
} else {
|
||||
roundCounter++;
|
||||
const nextRoundExperts = (reviewResult.refined_experts || []).map((exp, idx) => ({
|
||||
...exp, id: `expert-r${roundCounter}-${idx}`, status: 'pending' as const, round: roundCounter
|
||||
}));
|
||||
|
||||
if (nextRoundExperts.length === 0) {
|
||||
loopActive = false;
|
||||
break;
|
||||
}
|
||||
|
||||
const startIndex = expertsDataRef.current.length;
|
||||
appendExperts(nextRoundExperts);
|
||||
setAppState('experts_working');
|
||||
|
||||
const nextRoundTasks = nextRoundExperts.map((exp, idx) =>
|
||||
runExpertLifecycle(exp, startIndex + idx, ai, model, recentHistory,
|
||||
getThinkingBudget(config.expertLevel, model), signal)
|
||||
);
|
||||
|
||||
await Promise.all(nextRoundTasks);
|
||||
}
|
||||
}
|
||||
|
||||
if (signal.aborted) return;
|
||||
|
||||
// --- 5. Synthesis ---
|
||||
// --- Phase 3: Synthesis ---
|
||||
setAppState('synthesizing');
|
||||
|
||||
let fullFinalText = '';
|
||||
let fullFinalThoughts = '';
|
||||
|
||||
await streamSynthesisResponse(
|
||||
ai,
|
||||
model,
|
||||
query,
|
||||
recentHistory,
|
||||
allResults,
|
||||
getThinkingBudget(config.synthesisLevel, model),
|
||||
signal,
|
||||
ai, model, query, recentHistory, expertsDataRef.current,
|
||||
getThinkingBudget(config.synthesisLevel, model), signal,
|
||||
(textChunk, thoughtChunk) => {
|
||||
fullFinalText += textChunk;
|
||||
fullFinalThoughts += thoughtChunk;
|
||||
@@ -255,9 +224,7 @@ export const useDeepThink = () => {
|
||||
}
|
||||
|
||||
} catch (e: any) {
|
||||
if (signal.aborted) {
|
||||
console.log('Operation aborted by user');
|
||||
} else {
|
||||
if (!signal.aborted) {
|
||||
console.error(e);
|
||||
setAppState('idle');
|
||||
setProcessEndTime(Date.now());
|
||||
|
||||
73
prisma/hooks/useDeepThinkState.ts
Normal file
73
prisma/hooks/useDeepThinkState.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
|
||||
import { useState, useRef, useCallback } from 'react';
|
||||
import { AppState, AnalysisResult, ExpertResult } from '../types';
|
||||
|
||||
export const useDeepThinkState = () => {
|
||||
const [appState, setAppState] = useState<AppState>('idle');
|
||||
const [managerAnalysis, setManagerAnalysis] = useState<AnalysisResult | null>(null);
|
||||
const [experts, setExperts] = useState<ExpertResult[]>([]);
|
||||
const [finalOutput, setFinalOutput] = useState('');
|
||||
const [synthesisThoughts, setSynthesisThoughts] = useState('');
|
||||
|
||||
// Timing state
|
||||
const [processStartTime, setProcessStartTime] = useState<number | null>(null);
|
||||
const [processEndTime, setProcessEndTime] = useState<number | null>(null);
|
||||
|
||||
// Refs for data consistency
|
||||
const expertsDataRef = useRef<ExpertResult[]>([]);
|
||||
const abortControllerRef = useRef<AbortController | null>(null);
|
||||
|
||||
const resetDeepThink = useCallback(() => {
|
||||
setAppState('idle');
|
||||
setManagerAnalysis(null);
|
||||
setExperts([]);
|
||||
expertsDataRef.current = [];
|
||||
setFinalOutput('');
|
||||
setSynthesisThoughts('');
|
||||
setProcessStartTime(null);
|
||||
setProcessEndTime(null);
|
||||
abortControllerRef.current = null;
|
||||
}, []);
|
||||
|
||||
const stopDeepThink = useCallback(() => {
|
||||
if (abortControllerRef.current) {
|
||||
abortControllerRef.current.abort();
|
||||
abortControllerRef.current = null;
|
||||
}
|
||||
setAppState('idle');
|
||||
setProcessEndTime(Date.now());
|
||||
}, []);
|
||||
|
||||
const updateExpertAt = useCallback((index: number, update: Partial<ExpertResult> | ((prev: ExpertResult) => ExpertResult)) => {
|
||||
const current = expertsDataRef.current[index];
|
||||
const next = typeof update === 'function' ? update(current) : { ...current, ...update };
|
||||
expertsDataRef.current[index] = next;
|
||||
setExperts([...expertsDataRef.current]);
|
||||
}, []);
|
||||
|
||||
const setInitialExperts = useCallback((initialList: ExpertResult[]) => {
|
||||
expertsDataRef.current = initialList;
|
||||
setExperts(initialList);
|
||||
}, []);
|
||||
|
||||
const appendExperts = useCallback((newList: ExpertResult[]) => {
|
||||
expertsDataRef.current = [...expertsDataRef.current, ...newList];
|
||||
setExperts([...expertsDataRef.current]);
|
||||
}, []);
|
||||
|
||||
return {
|
||||
appState, setAppState,
|
||||
managerAnalysis, setManagerAnalysis,
|
||||
experts, setExperts, expertsDataRef,
|
||||
finalOutput, setFinalOutput,
|
||||
synthesisThoughts, setSynthesisThoughts,
|
||||
processStartTime, setProcessStartTime,
|
||||
processEndTime, setProcessEndTime,
|
||||
abortControllerRef,
|
||||
resetDeepThink,
|
||||
stopDeepThink,
|
||||
updateExpertAt,
|
||||
setInitialExperts,
|
||||
appendExperts
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user