diff --git a/app/(main)/configurations/prompt-editor/page.tsx b/app/(main)/configurations/prompt-editor/page.tsx index 6c76e103..b867881d 100644 --- a/app/(main)/configurations/prompt-editor/page.tsx +++ b/app/(main)/configurations/prompt-editor/page.tsx @@ -23,6 +23,7 @@ import { useConfigPersistence } from "@/app/hooks/useConfigPersistence"; import { SavedConfig, ConfigVersionItems } from "@/app/lib/types/configs"; import { configState } from "@/app/lib/store/config"; import { DEFAULT_CONFIG } from "@/app/lib/constants"; +import { apiFetch } from "@/app/lib/apiClient"; function PromptEditorContent() { const searchParams = useSearchParams(); @@ -126,6 +127,29 @@ function PromptEditorContent() { [], ); + const refreshVersionsForConfig = React.useCallback( + async (configId: string) => { + if (!configId) return; + try { + const apiKey = activeKey?.key ?? ""; + const res = await apiFetch<{ + success: boolean; + data: ConfigVersionItems[]; + }>(`/api/configs/${configId}/versions`, apiKey); + if (res.success && res.data) { + configState.versionItemsCache[configId] = res.data; + setStableVersionItemsMap((prev) => ({ + ...prev, + [configId]: res.data, + })); + } + } catch (err) { + console.error("Failed to refresh version list:", err); + } + }, + [activeKey], + ); + const resetEditor = React.useCallback(() => { setCurrentContent(""); setCurrentConfigBlob(DEFAULT_CONFIG); @@ -247,7 +271,11 @@ function PromptEditorContent() { if (ok) { setHasUnsavedChanges(false); setCommitMessage(""); - if (wasNewConfig) resetEditor(); + if (wasNewConfig) { + resetEditor(); + } else if (currentConfigParentId) { + await refreshVersionsForConfig(currentConfigParentId); + } } }; diff --git a/app/(main)/evaluations/[id]/page.tsx b/app/(main)/evaluations/[id]/page.tsx index 045eda53..f4f4f27b 100644 --- a/app/(main)/evaluations/[id]/page.tsx +++ b/app/(main)/evaluations/[id]/page.tsx @@ -8,6 +8,7 @@ import { useState, useEffect, useCallback, useRef } from "react"; import { useRouter, useParams } from "next/navigation"; import { apiFetch } from "@/app/lib/apiClient"; +import { invalidateConfigCache } from "@/app/lib/utils"; import { useAuth } from "@/app/lib/context/AuthContext"; import { useApp } from "@/app/lib/context/AppContext"; import type { @@ -70,6 +71,7 @@ export default function EvaluationReport() { const [isConfigModalOpen, setIsConfigModalOpen] = useState(false); const [exportFormat, setExportFormat] = useState<"row" | "grouped">("row"); const [isResyncing, setIsResyncing] = useState(false); + const [isImprovingPrompt, setIsImprovingPrompt] = useState(false); const [showNoTracesModal, setShowNoTracesModal] = useState(false); const fetchJobDetails = useCallback(async () => { @@ -180,6 +182,51 @@ export default function EvaluationReport() { } }; + const handleIteratePrompt = async () => { + if (!isAuthenticated || !jobId) return; + setIsImprovingPrompt(true); + try { + const data = await apiFetch<{ + success: boolean; + error?: string; + data?: { + config_id?: string; + id?: string; + version?: number; + } | null; + }>(`/api/evaluations/${jobId}/improve-prompt`, apiKey, { + method: "POST", + body: JSON.stringify({}), + }); + + if (!data.success) { + toast.error(data.error || "Failed to iterate on prompt"); + return; + } + + invalidateConfigCache(); + toast.success("Prompt iteration created"); + + const newConfigId = data.data?.config_id ?? data.data?.id; + const newVersion = data.data?.version; + if (newConfigId) { + const versionParam = + typeof newVersion === "number" ? `&version=${newVersion}` : ""; + router.push( + `/configurations/prompt-editor?config=${encodeURIComponent( + newConfigId, + )}${versionParam}&from=evaluations`, + ); + } + } catch (err: unknown) { + toast.error( + `Failed to iterate on prompt: ${err instanceof Error ? err.message : "Unknown error"}`, + ); + } finally { + setIsImprovingPrompt(false); + } + }; + const handleResync = async () => { if (!isAuthenticated || !jobId) return; @@ -344,6 +391,30 @@ export default function EvaluationReport() { View Config Config +