Spaces:
Sleeping
Sleeping
Sync from enhance: d795216 feat: implement patch response parser for search and replace functionality
9bd4242 | import { useCallback, useRef, useState } from 'react'; | |
| import { generateProblemFraming } from '../lib/api'; | |
| import { loadSettings } from '../lib/settings'; | |
| import { getActiveProvider, providerToCustomApiConfig } from '../lib/ai-providers'; | |
| import type { OutputMode, ProblemFramingPlan, Quality, ReferenceImage } from '../types/api'; | |
| import { useI18n } from '../i18n'; | |
| import { loadPrompts } from './usePrompts'; | |
| interface GenerationDraft { | |
| concept: string; | |
| quality: Quality; | |
| outputMode: OutputMode; | |
| referenceImages?: ReferenceImage[]; | |
| } | |
| interface StartPlanOptions { | |
| request: GenerationDraft; | |
| feedback?: string; | |
| } | |
| interface RefinePlanOptions { | |
| feedback: string; | |
| } | |
| interface UseProblemFramingResult { | |
| status: 'idle' | 'loading' | 'ready' | 'error'; | |
| plan: ProblemFramingPlan | null; | |
| error: string | null; | |
| draft: GenerationDraft | null; | |
| startPlan: (options: StartPlanOptions) => Promise<void>; | |
| refinePlan: (options: RefinePlanOptions) => Promise<void>; | |
| reset: () => void; | |
| } | |
| function hasIncompleteCustomProvider(provider: { apiUrl: string; apiKey: string; model: string } | null): boolean { | |
| if (!provider) { | |
| return false; | |
| } | |
| const hasAny = Boolean(provider.apiUrl.trim() || provider.apiKey.trim() || provider.model.trim()); | |
| const hasRequired = Boolean(provider.apiUrl.trim() && provider.apiKey.trim() && provider.model.trim()); | |
| return hasAny && !hasRequired; | |
| } | |
| export function useProblemFraming(): UseProblemFramingResult { | |
| const { locale, t } = useI18n(); | |
| const [status, setStatus] = useState<'idle' | 'loading' | 'ready' | 'error'>('idle'); | |
| const [plan, setPlan] = useState<ProblemFramingPlan | null>(null); | |
| const [error, setError] = useState<string | null>(null); | |
| const [draft, setDraft] = useState<GenerationDraft | null>(null); | |
| const [feedbackHistory, setFeedbackHistory] = useState<string[]>([]); | |
| const abortControllerRef = useRef<AbortController | null>(null); | |
| const callPlanner = useCallback(async ( | |
| request: GenerationDraft, | |
| feedback?: string, | |
| currentPlan?: ProblemFramingPlan | null, | |
| history?: string[] | |
| ) => { | |
| abortControllerRef.current?.abort(); | |
| abortControllerRef.current = new AbortController(); | |
| const settings = loadSettings(); | |
| const activeProvider = getActiveProvider(settings.api); | |
| const customApiConfig = providerToCustomApiConfig(activeProvider); | |
| if (hasIncompleteCustomProvider(activeProvider) && !customApiConfig) { | |
| throw new Error(t('settings.test.needUrlAndKey')); | |
| } | |
| return generateProblemFraming( | |
| { | |
| concept: request.concept, | |
| feedback, | |
| feedbackHistory: history && history.length ? history : undefined, | |
| currentPlan: currentPlan || undefined, | |
| locale, | |
| referenceImages: request.referenceImages, | |
| promptOverrides: loadPrompts(locale), | |
| customApiConfig: customApiConfig || undefined, | |
| }, | |
| abortControllerRef.current.signal | |
| ); | |
| }, [locale, t]); | |
| const startPlan = useCallback(async ({ request, feedback }: StartPlanOptions) => { | |
| setStatus('loading'); | |
| setError(null); | |
| setDraft(request); | |
| const trimmedFeedback = feedback?.trim(); | |
| const nextHistory = trimmedFeedback ? [trimmedFeedback] : []; | |
| setFeedbackHistory(nextHistory); | |
| try { | |
| const response = await callPlanner(request, trimmedFeedback, undefined, nextHistory); | |
| setPlan(response.plan); | |
| setStatus('ready'); | |
| } catch (err) { | |
| if (err instanceof Error && err.name === 'AbortError') { | |
| return; | |
| } | |
| setStatus('error'); | |
| setError(err instanceof Error ? err.message : t('generation.problemFramingFailed')); | |
| } | |
| }, [callPlanner, t]); | |
| const refinePlan = useCallback(async ({ feedback }: RefinePlanOptions) => { | |
| if (!draft) { | |
| return; | |
| } | |
| setStatus('loading'); | |
| setError(null); | |
| const trimmedFeedback = feedback.trim(); | |
| const nextHistory = trimmedFeedback ? [...feedbackHistory, trimmedFeedback] : feedbackHistory; | |
| try { | |
| const response = await callPlanner(draft, trimmedFeedback, plan, nextHistory); | |
| setPlan(response.plan); | |
| setFeedbackHistory(nextHistory); | |
| setStatus('ready'); | |
| } catch (err) { | |
| if (err instanceof Error && err.name === 'AbortError') { | |
| return; | |
| } | |
| setStatus('error'); | |
| setError(err instanceof Error ? err.message : t('generation.problemFramingFailed')); | |
| } | |
| }, [callPlanner, draft, feedbackHistory, plan, t]); | |
| const reset = useCallback(() => { | |
| abortControllerRef.current?.abort(); | |
| setStatus('idle'); | |
| setPlan(null); | |
| setError(null); | |
| setDraft(null); | |
| setFeedbackHistory([]); | |
| }, []); | |
| return { | |
| status, | |
| plan, | |
| error, | |
| draft, | |
| startPlan, | |
| refinePlan, | |
| reset, | |
| }; | |
| } | |