From 2095077535d2a04e3c63afcb09d94dc823388b5d Mon Sep 17 00:00:00 2001 From: Ayush8923 <80516839+Ayush8923@users.noreply.github.com> Date: Tue, 24 Mar 2026 14:30:17 +0530 Subject: [PATCH 1/7] feat(*): support new model configuration --- app/components/ConfigDrawer.tsx | 32 +-- .../prompt-editor/ConfigEditorPane.tsx | 32 +-- .../prompt-editor/CurrentConfigTab.tsx | 15 +- app/configurations/prompt-editor/page.tsx | 235 +++++++++++------- app/configurations/prompt-editor/types.ts | 2 +- app/lib/models.ts | 43 ++++ app/lib/utils.ts | 7 +- 7 files changed, 222 insertions(+), 144 deletions(-) create mode 100644 app/lib/models.ts diff --git a/app/components/ConfigDrawer.tsx b/app/components/ConfigDrawer.tsx index 320ebaa..b987955 100644 --- a/app/components/ConfigDrawer.tsx +++ b/app/components/ConfigDrawer.tsx @@ -6,8 +6,9 @@ "use client"; -import React, { useState } from 'react'; +import React, { useEffect, useState } from 'react'; import { colors } from '@/app/lib/colors'; +import { MODEL_OPTIONS, isGpt5Model } from '@/app/lib/models'; import { SavedConfig } from './SimplifiedConfigEditor'; import { Tool } from '@/app/lib/configTypes'; @@ -39,27 +40,6 @@ interface ConfigDrawerProps { onApplyConfig: (configId: string) => void; } -const MODEL_OPTIONS = { - openai: [ - { value: 'gpt-4o', label: 'GPT-4o' }, - { value: 'gpt-4o-mini', label: 'GPT-4o Mini' }, - { value: 'gpt-4-turbo', label: 'GPT-4 Turbo' }, - { value: 'gpt-4', label: 'GPT-4' }, - { value: 'gpt-3.5-turbo', label: 'GPT-3.5 Turbo' }, - ], - // anthropic: [ - // { value: 'claude-3-5-sonnet-20241022', label: 'Claude 3.5 Sonnet' }, - // { value: 'claude-3-opus-20240229', label: 'Claude 3 Opus' }, - // { value: 'claude-3-sonnet-20240229', label: 'Claude 3 Sonnet' }, - // { value: 'claude-3-haiku-20240307', label: 'Claude 3 Haiku' }, - // ], - // google: [ - // { value: 'gemini-1.5-pro', label: 'Gemini 1.5 Pro' }, - // { value: 'gemini-1.5-flash', label: 'Gemini 1.5 Flash' }, - // { value: 'gemini-pro', label: 'Gemini Pro' }, - // ], -}; - function generateDiff(text1: string, text2: string): { left: DiffLine[], right: DiffLine[] } { const lines1 = text1.split('\n'); const lines2 = text2.split('\n'); @@ -106,8 +86,10 @@ export default function ConfigDrawer({ const [expandedConfigs, setExpandedConfigs] = useState>(new Set()); const [isCreatingNew, setIsCreatingNew] = useState(false); + const isGpt5 = isGpt5Model(currentConfig.modelName); + // Sync isCreatingNew with selectedConfigId - React.useEffect(() => { + useEffect(() => { if (selectedConfigId) { setIsCreatingNew(false); } @@ -525,6 +507,7 @@ export default function ConfigDrawer({ {/* Temperature */} + {!isGpt5 && (
+ )} {/* Tools Section */}
@@ -653,6 +637,7 @@ export default function ConfigDrawer({ }} />
+ {!isGpt5 && (
+ )} ))} diff --git a/app/components/prompt-editor/ConfigEditorPane.tsx b/app/components/prompt-editor/ConfigEditorPane.tsx index fea263c..26e3d1d 100644 --- a/app/components/prompt-editor/ConfigEditorPane.tsx +++ b/app/components/prompt-editor/ConfigEditorPane.tsx @@ -1,7 +1,8 @@ -import React, { useState, useMemo } from 'react'; +import { useState, useMemo } from 'react'; import { colors } from '@/app/lib/colors'; import { ConfigBlob, Tool } from '@/app/configurations/prompt-editor/types'; import { SavedConfig, formatRelativeTime } from '@/app/lib/useConfigs'; +import { MODEL_OPTIONS, isGpt5Model } from '@/app/lib/models'; interface ConfigEditorPaneProps { configBlob: ConfigBlob; @@ -28,28 +29,6 @@ interface ConfigGroupForDropdown { versions: SavedConfig[]; } -// Provider-specific models -const MODEL_OPTIONS = { - openai: [ - { value: 'gpt-4o', label: 'GPT-4o' }, - { value: 'gpt-4o-mini', label: 'GPT-4o Mini' }, - { value: 'gpt-4-turbo', label: 'GPT-4 Turbo' }, - { value: 'gpt-4', label: 'GPT-4' }, - { value: 'gpt-3.5-turbo', label: 'GPT-3.5 Turbo' }, - ], - // anthropic: [ - // { value: 'claude-3-5-sonnet-20241022', label: 'Claude 3.5 Sonnet' }, - // { value: 'claude-3-opus-20240229', label: 'Claude 3 Opus' }, - // { value: 'claude-3-sonnet-20240229', label: 'Claude 3 Sonnet' }, - // { value: 'claude-3-haiku-20240307', label: 'Claude 3 Haiku' }, - // ], - // google: [ - // { value: 'gemini-1.5-pro', label: 'Gemini 1.5 Pro' }, - // { value: 'gemini-1.5-flash', label: 'Gemini 1.5 Flash' }, - // { value: 'gemini-pro', label: 'Gemini Pro' }, - // ], -}; - export default function ConfigEditorPane({ configBlob, onConfigChange, @@ -70,6 +49,7 @@ export default function ConfigEditorPane({ const provider = configBlob.completion.provider; const params = configBlob.completion.params; + const isGpt5 = isGpt5Model(params.model); const tools = (params.tools || []) as Tool[]; // Group configs by config_id for nested dropdown @@ -531,7 +511,8 @@ export default function ConfigEditorPane({ - {/* Temperature */} + {/* Temperature - hidden for GPT-5 models */} + {!isGpt5 && (
+ )} {/* Tools */}
@@ -626,6 +608,7 @@ export default function ConfigEditorPane({ }} />
+ {!isGpt5 && (
+ )}
))} diff --git a/app/components/prompt-editor/CurrentConfigTab.tsx b/app/components/prompt-editor/CurrentConfigTab.tsx index 1e3a2f5..ad84c87 100644 --- a/app/components/prompt-editor/CurrentConfigTab.tsx +++ b/app/components/prompt-editor/CurrentConfigTab.tsx @@ -1,6 +1,7 @@ -import React, { useState } from 'react'; +import { useState } from 'react'; import { colors } from '@/app/lib/colors'; import { Config, Tool } from '@/app/configurations/prompt-editor/types'; +import { MODEL_OPTIONS, isGpt5Model } from '@/app/lib/models'; interface CurrentConfigTabProps { configs: Config[]; @@ -46,6 +47,7 @@ export default function CurrentConfigTab({ onUseCurrentPrompt, }: CurrentConfigTabProps) { const [isCreatingNew, setIsCreatingNew] = useState(false); + const isGpt5 = isGpt5Model(model); const addTool = () => { onToolsChange([ @@ -223,10 +225,9 @@ export default function CurrentConfigTab({ backgroundColor: colors.bg.primary, }} > - - - - + {MODEL_OPTIONS.openai.map((m) => ( + + ))} @@ -282,6 +283,7 @@ export default function CurrentConfigTab({ {/* Temperature Slider */} + {!isGpt5 && (
+ )} {/* Tools Section */}
@@ -410,6 +413,7 @@ export default function CurrentConfigTab({ }} />
+ {!isGpt5 && (
+ )} ))} diff --git a/app/configurations/prompt-editor/page.tsx b/app/configurations/prompt-editor/page.tsx index d7e0a7b..db66752 100644 --- a/app/configurations/prompt-editor/page.tsx +++ b/app/configurations/prompt-editor/page.tsx @@ -7,25 +7,28 @@ * Supports URL query params for cross-navigation from Config Library/Evaluations. */ -"use client" -import React, { useState, useEffect, Suspense } from 'react'; -import { useSearchParams } from 'next/navigation'; -import Sidebar from '@/app/components/Sidebar'; -import { colors } from '@/app/lib/colors'; -import { ConfigBlob, Tool } from './types'; -import { hasConfigChanges } from './utils'; +"use client"; + +import { useState, useEffect, Suspense } from "react"; +import { useSearchParams } from "next/navigation"; +import Sidebar from "@/app/components/Sidebar"; +import { colors } from "@/app/lib/colors"; +import { ConfigBlob, Tool } from "./types"; +import { hasConfigChanges } from "./utils"; +import { ConfigCreate, ConfigVersionCreate } from "@/app/lib/configTypes"; +import Header from "@/app/components/prompt-editor/Header"; +import HistorySidebar from "@/app/components/prompt-editor/HistorySidebar"; +import PromptEditorPane from "@/app/components/prompt-editor/PromptEditorPane"; +import ConfigEditorPane from "@/app/components/prompt-editor/ConfigEditorPane"; +import DiffView from "@/app/components/prompt-editor/DiffView"; +import { useToast } from "@/app/components/Toast"; +import Loader from "@/app/components/Loader"; import { - ConfigCreate, - ConfigVersionCreate, -} from '@/app/lib/configTypes'; -import Header from '@/app/components/prompt-editor/Header'; -import HistorySidebar from '@/app/components/prompt-editor/HistorySidebar'; -import PromptEditorPane from '@/app/components/prompt-editor/PromptEditorPane'; -import ConfigEditorPane from '@/app/components/prompt-editor/ConfigEditorPane'; -import DiffView from '@/app/components/prompt-editor/DiffView'; -import { useToast } from '@/app/components/Toast'; -import Loader from '@/app/components/Loader'; -import { useConfigs, invalidateConfigCache, SavedConfig } from '@/app/lib/useConfigs'; + useConfigs, + invalidateConfigCache, + SavedConfig, +} from "@/app/lib/useConfigs"; +import { isGpt5Model } from "@/app/lib/models"; function PromptEditorContent() { const toast = useToast(); @@ -33,24 +36,24 @@ function PromptEditorContent() { const [sidebarCollapsed, setSidebarCollapsed] = useState(false); // URL query params for cross-navigation - const urlConfigId = searchParams.get('config'); - const urlVersion = searchParams.get('version'); - const showHistory = searchParams.get('history') === 'true'; - const isNewConfig = searchParams.get('new') === 'true'; + const urlConfigId = searchParams.get("config"); + const urlVersion = searchParams.get("version"); + const showHistory = searchParams.get("history") === "true"; + const isNewConfig = searchParams.get("new") === "true"; // Evaluation context to preserve (when coming from evaluations page) - const urlDatasetId = searchParams.get('dataset'); - const urlExperimentName = searchParams.get('experiment'); - const fromEvaluations = searchParams.get('from') === 'evaluations'; + const urlDatasetId = searchParams.get("dataset"); + const urlExperimentName = searchParams.get("experiment"); + const fromEvaluations = searchParams.get("from") === "evaluations"; // Default config for new versions const defaultConfig: ConfigBlob = { completion: { - provider: 'openai', - type: 'text', + provider: "openai", + type: "text", params: { - model: 'gpt-4o-mini', - instructions: '', + model: "gpt-4o-mini", + instructions: "", temperature: 0.7, tools: [], }, @@ -58,41 +61,51 @@ function PromptEditorContent() { }; // Use shared configs hook with caching - const { configs: savedConfigs, isLoading, refetch: refetchConfigs } = useConfigs(); + const { + configs: savedConfigs, + isLoading, + refetch: refetchConfigs, + } = useConfigs(); const [isSaving, setIsSaving] = useState(false); const initialLoadComplete = !isLoading && savedConfigs.length >= 0; // Current working state - const [currentContent, setCurrentContent] = useState('You are a helpful AI assistant.\nYou provide clear and concise answers.\nYou are polite and professional.'); - const [currentConfigBlob, setCurrentConfigBlob] = useState(defaultConfig); - const [currentConfigName, setCurrentConfigName] = useState(''); - const [selectedConfigId, setSelectedConfigId] = useState(''); // Selected version ID - const [currentConfigParentId, setCurrentConfigParentId] = useState(''); // Parent config ID for evaluation + const [currentContent, setCurrentContent] = useState( + "You are a helpful AI assistant.\nYou provide clear and concise answers.\nYou are polite and professional.", + ); + const [currentConfigBlob, setCurrentConfigBlob] = + useState(defaultConfig); + const [currentConfigName, setCurrentConfigName] = useState(""); + const [selectedConfigId, setSelectedConfigId] = useState(""); // Selected version ID + const [currentConfigParentId, setCurrentConfigParentId] = + useState(""); // Parent config ID for evaluation const [currentConfigVersion, setCurrentConfigVersion] = useState(0); // Version number for evaluation - const [provider, setProvider] = useState('openai'); + const [provider, setProvider] = useState("openai"); const [temperature, setTemperature] = useState(0.7); const [tools, setTools] = useState([]); // UI state const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false); - const [commitMessage, setCommitMessage] = useState(''); + const [commitMessage, setCommitMessage] = useState(""); const [showHistorySidebar, setShowHistorySidebar] = useState(true); // Default open, or from URL param const [showConfigPane, setShowConfigPane] = useState(true); // Config pane collapse state // History viewing state - const [selectedVersion, setSelectedVersion] = useState(null); + const [selectedVersion, setSelectedVersion] = useState( + null, + ); const [compareWith, setCompareWith] = useState(null); // Get API key from localStorage const getApiKey = (): string | null => { try { - const stored = localStorage.getItem('kaapi_api_keys'); + const stored = localStorage.getItem("kaapi_api_keys"); if (stored) { const keys = JSON.parse(stored); return keys.length > 0 ? keys[0].key : null; } } catch (e) { - console.error('Failed to get API key:', e); + console.error("Failed to get API key:", e); } return null; }; @@ -103,13 +116,13 @@ function PromptEditorContent() { // If new config is requested, reset to defaults if (isNewConfig) { - setCurrentContent(''); + setCurrentContent(""); setCurrentConfigBlob(defaultConfig); - setProvider('openai'); + setProvider("openai"); setTemperature(0.7); - setSelectedConfigId(''); - setCurrentConfigName(''); - setCurrentConfigParentId(''); + setSelectedConfigId(""); + setCurrentConfigName(""); + setCurrentConfigParentId(""); setCurrentConfigVersion(0); setTools([]); return; @@ -123,14 +136,17 @@ function PromptEditorContent() { if (urlVersion) { // Find specific version targetConfig = savedConfigs.find( - c => c.config_id === urlConfigId && c.version === parseInt(urlVersion) + (c) => + c.config_id === urlConfigId && c.version === parseInt(urlVersion), ); } else { // Find latest version for this config - const configVersions = savedConfigs.filter(c => c.config_id === urlConfigId); + const configVersions = savedConfigs.filter( + (c) => c.config_id === urlConfigId, + ); if (configVersions.length > 0) { targetConfig = configVersions.reduce((latest, current) => - current.version > latest.version ? current : latest + current.version > latest.version ? current : latest, ); } } @@ -165,7 +181,14 @@ function PromptEditorContent() { } } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [initialLoadComplete, savedConfigs, urlConfigId, urlVersion, showHistory, isNewConfig]); + }, [ + initialLoadComplete, + savedConfigs, + urlConfigId, + urlVersion, + showHistory, + isNewConfig, + ]); // Detect unsaved changes useEffect(() => { @@ -175,7 +198,7 @@ function PromptEditorContent() { return; } - const selectedConfig = savedConfigs.find(c => c.id === selectedConfigId); + const selectedConfig = savedConfigs.find((c) => c.id === selectedConfigId); if (!selectedConfig) { setHasUnsavedChanges(true); return; @@ -197,19 +220,26 @@ function PromptEditorContent() { }); setHasUnsavedChanges(promptChanged || configChanged); - }, [selectedConfigId, currentContent, currentConfigBlob, provider, temperature, tools, savedConfigs]); - + }, [ + selectedConfigId, + currentContent, + currentConfigBlob, + provider, + temperature, + tools, + savedConfigs, + ]); // Save current configuration const handleSaveConfig = async () => { if (!currentConfigName.trim()) { - toast.error('Please enter a configuration name'); + toast.error("Please enter a configuration name"); return; } const apiKey = getApiKey(); if (!apiKey) { - toast.error('No API key found. Please add an API key in the Keystore.'); + toast.error("No API key found. Please add an API key in the Keystore."); return; } @@ -233,25 +263,34 @@ function PromptEditorContent() { } }); + const model = currentConfigBlob.completion.params.model; + const gpt5 = isGpt5Model(model); + const configBlob: ConfigBlob = { completion: { provider: currentConfigBlob.completion.provider, - type: currentConfigBlob.completion.type || 'text', // Default to 'text' + type: currentConfigBlob.completion.type || "text", // Default to 'text' params: { - model: currentConfigBlob.completion.params.model, + model, instructions: currentContent, // Store prompt as instructions - temperature: currentConfigBlob.completion.params.temperature, + // GPT-5 models don't support temperature + ...(!gpt5 && { + temperature: currentConfigBlob.completion.params.temperature, + }), // Flatten tools array to direct fields for backend - support multiple knowledge bases ...(allKnowledgeBaseIds.length > 0 && { knowledge_base_ids: allKnowledgeBaseIds, - max_num_results: maxNumResults, + // GPT-5 models don't support max_num_results + ...(!gpt5 && { max_num_results: maxNumResults }), }), }, }, }; // Check if updating existing config (same name exists) - const existingConfig = savedConfigs.find(c => c.name === currentConfigName.trim()); + const existingConfig = savedConfigs.find( + (c) => c.name === currentConfigName.trim(), + ); if (existingConfig) { // Create new version for existing config @@ -260,37 +299,44 @@ function PromptEditorContent() { commit_message: commitMessage.trim() || `Updated prompt and config`, }; - const response = await fetch(`/api/configs/${existingConfig.config_id}/versions`, { - method: 'POST', - headers: { - 'X-API-KEY': apiKey, - 'Content-Type': 'application/json', + const response = await fetch( + `/api/configs/${existingConfig.config_id}/versions`, + { + method: "POST", + headers: { + "X-API-KEY": apiKey, + "Content-Type": "application/json", + }, + body: JSON.stringify(versionCreate), }, - body: JSON.stringify(versionCreate), - }); + ); const data = await response.json(); if (!data.success) { - toast.error(`Failed to create version: ${data.error || 'Unknown error'}`); + toast.error( + `Failed to create version: ${data.error || "Unknown error"}`, + ); return; } - toast.success(`Configuration "${currentConfigName}" updated! New version created.`); + toast.success( + `Configuration "${currentConfigName}" updated! New version created.`, + ); } else { // Create new config const configCreate: ConfigCreate = { name: currentConfigName.trim(), description: `${provider} configuration with prompt`, config_blob: configBlob, - commit_message: commitMessage.trim() || 'Initial version', + commit_message: commitMessage.trim() || "Initial version", }; - const response = await fetch('/api/configs', { - method: 'POST', + const response = await fetch("/api/configs", { + method: "POST", headers: { - 'X-API-KEY': apiKey, - 'Content-Type': 'application/json', + "X-API-KEY": apiKey, + "Content-Type": "application/json", }, body: JSON.stringify(configCreate), }); @@ -298,11 +344,15 @@ function PromptEditorContent() { const data = await response.json(); if (!data.success || !data.data) { - toast.error(`Failed to create config: ${data.error || 'Unknown error'}`); + toast.error( + `Failed to create config: ${data.error || "Unknown error"}`, + ); return; } - toast.success(`Configuration "${currentConfigName}" created successfully!`); + toast.success( + `Configuration "${currentConfigName}" created successfully!`, + ); } // Invalidate config cache and refresh from shared hook @@ -311,10 +361,10 @@ function PromptEditorContent() { // Reset unsaved changes flag and commit message after successful save setHasUnsavedChanges(false); - setCommitMessage(''); + setCommitMessage(""); } catch (e) { - console.error('Failed to save config:', e); - toast.error('Failed to save configuration. Please try again.'); + console.error("Failed to save config:", e); + toast.error("Failed to save configuration. Please try again."); } finally { setIsSaving(false); } @@ -324,19 +374,19 @@ function PromptEditorContent() { const handleLoadConfigById = (configId: string) => { if (!configId) { // Reset to new config - setCurrentContent(''); + setCurrentContent(""); setCurrentConfigBlob(defaultConfig); - setProvider('openai'); + setProvider("openai"); setTemperature(0.7); - setSelectedConfigId(''); - setCurrentConfigName(''); - setCurrentConfigParentId(''); + setSelectedConfigId(""); + setCurrentConfigName(""); + setCurrentConfigParentId(""); setCurrentConfigVersion(0); setTools([]); return; } - const config = savedConfigs.find(c => c.id === configId); + const config = savedConfigs.find((c) => c.id === configId); if (!config) return; setCurrentContent(config.promptContent); @@ -362,11 +412,16 @@ function PromptEditorContent() { setTools(config.tools || []); }; - return ( -
+
- +
= { + openai: [ + // GPT-5 family + { value: 'gpt-5.4', label: 'GPT-5.4' }, + { value: 'gpt-5.4-pro', label: 'GPT-5.4 Pro' }, + { value: 'gpt-5.4-mini', label: 'GPT-5.4 Mini' }, + { value: 'gpt-5.4-nano', label: 'GPT-5.4 Nano' }, + { value: 'gpt-5', label: 'GPT-5' }, + { value: 'gpt-5-mini', label: 'GPT-5 Mini' }, + { value: 'gpt-5-nano', label: 'GPT-5 Nano' }, + // GPT-4 family + { value: 'gpt-4.1', label: 'GPT-4.1' }, + { value: 'gpt-4o', label: 'GPT-4o' }, + { value: 'gpt-4o-mini', label: 'GPT-4o Mini' }, + { value: 'gpt-4-turbo', label: 'GPT-4 Turbo' }, + { value: 'gpt-4', label: 'GPT-4' }, + { value: 'gpt-3.5-turbo', label: 'GPT-3.5 Turbo' }, + ], + // anthropic: [ + // { value: 'claude-3-5-sonnet-20241022', label: 'Claude 3.5 Sonnet' }, + // { value: 'claude-3-opus-20240229', label: 'Claude 3 Opus' }, + // { value: 'claude-3-sonnet-20240229', label: 'Claude 3 Sonnet' }, + // { value: 'claude-3-haiku-20240307', label: 'Claude 3 Haiku' }, + // ], + // google: [ + // { value: 'gemini-1.5-pro', label: 'Gemini 1.5 Pro' }, + // { value: 'gemini-1.5-flash', label: 'Gemini 1.5 Flash' }, + // { value: 'gemini-pro', label: 'Gemini Pro' }, + // ], +}; + +/** + * Returns true if the given model ID is a GPT-5 family model. + * GPT-5 models do not support temperature or max_num_results parameters. + */ +export function isGpt5Model(model: string): boolean { + return model.startsWith('gpt-5'); +} diff --git a/app/lib/utils.ts b/app/lib/utils.ts index 3327498..5ceace0 100644 --- a/app/lib/utils.ts +++ b/app/lib/utils.ts @@ -2,7 +2,12 @@ import { Credential, ProviderDef } from "@/app/lib/types/credentials"; import { formatDistanceToNow } from "date-fns"; export function timeAgo(dateStr: string): string { - return formatDistanceToNow(new Date(dateStr), { addSuffix: true }); + const date = + dateStr.includes("Z") || dateStr.includes("+") + ? new Date(dateStr) + : new Date(dateStr + "Z"); + + return formatDistanceToNow(date, { addSuffix: true }); } export function getExistingForProvider( From c9b737a500eef728559b8c05369b2f87f6d117a0 Mon Sep 17 00:00:00 2001 From: Ayush8923 <80516839+Ayush8923@users.noreply.github.com> Date: Thu, 26 Mar 2026 00:17:44 +0530 Subject: [PATCH 2/7] fix(*): remove the temprature and database ID from the UI --- app/components/ConfigSelector.tsx | 414 ++++++++++++++++++++---------- app/evaluations/page.tsx | 5 +- app/lib/useConfigs.ts | 128 +++++---- 3 files changed, 357 insertions(+), 190 deletions(-) diff --git a/app/components/ConfigSelector.tsx b/app/components/ConfigSelector.tsx index b53b0b9..f1280ff 100644 --- a/app/components/ConfigSelector.tsx +++ b/app/components/ConfigSelector.tsx @@ -3,13 +3,23 @@ * Allows selecting a saved config with "Edit in Prompt Editor" link */ -"use client" +"use client"; -import { useState, useRef, useLayoutEffect } from 'react'; -import { useRouter } from 'next/navigation'; -import { colors } from '@/app/lib/colors'; -import { useConfigs, SavedConfig, formatRelativeTime } from '@/app/lib/useConfigs'; -import { ChevronUpIcon, ChevronDownIcon, EditIcon, GearIcon, CheckIcon } from '@/app/components/icons'; +import { useState, useRef, useLayoutEffect } from "react"; +import { useRouter } from "next/navigation"; +import { colors } from "@/app/lib/colors"; +import { + useConfigs, + SavedConfig, + formatRelativeTime, +} from "@/app/lib/useConfigs"; +import { + ChevronUpIcon, + ChevronDownIcon, + EditIcon, + GearIcon, + CheckIcon, +} from "@/app/components/icons"; interface ConfigSelectorProps { selectedConfigId: string; @@ -35,7 +45,7 @@ export default function ConfigSelector({ const router = useRouter(); const { configs, configGroups, isLoading, error } = useConfigs(); const [isDropdownOpen, setIsDropdownOpen] = useState(false); - const [searchQuery, setSearchQuery] = useState(''); + const [searchQuery, setSearchQuery] = useState(""); const [promptExpanded, setPromptExpanded] = useState(false); const [isPromptOverflowing, setIsPromptOverflowing] = useState(false); const promptRef = useRef(null); @@ -53,13 +63,13 @@ export default function ConfigSelector({ // Find currently selected config const selectedConfig = configs.find( - c => c.config_id === selectedConfigId && c.version === selectedVersion + (c) => c.config_id === selectedConfigId && c.version === selectedVersion, ); // Filter config groups based on search query const filteredConfigGroups = searchQuery.trim() - ? configGroups.filter(group => - group.name.toLowerCase().includes(searchQuery.toLowerCase()) + ? configGroups.filter((group) => + group.name.toLowerCase().includes(searchQuery.toLowerCase()), ) : configGroups; @@ -67,28 +77,28 @@ export default function ConfigSelector({ const handleSelect = (config: SavedConfig) => { onConfigSelect(config.config_id, config.version); setIsDropdownOpen(false); - setSearchQuery(''); // Clear search on selection + setSearchQuery(""); // Clear search on selection }; // Handle dropdown close const handleCloseDropdown = () => { setIsDropdownOpen(false); - setSearchQuery(''); // Clear search on close + setSearchQuery(""); // Clear search on close }; // Build URL params preserving evaluation context const buildEditorUrl = (configId?: string, version?: number) => { const params = new URLSearchParams(); if (configId && version) { - params.set('config', configId); - params.set('version', version.toString()); + params.set("config", configId); + params.set("version", version.toString()); } else { - params.set('new', 'true'); + params.set("new", "true"); } // Preserve evaluation context - if (datasetId) params.set('dataset', datasetId); - if (experimentName) params.set('experiment', experimentName); - params.set('from', 'evaluations'); // Mark that we came from evaluations + if (datasetId) params.set("dataset", datasetId); + if (experimentName) params.set("experiment", experimentName); + params.set("from", "evaluations"); // Mark that we came from evaluations return `/configurations/prompt-editor?${params.toString()}`; }; @@ -100,26 +110,42 @@ export default function ConfigSelector({ // Navigate to Config Library const handleBrowseLibrary = () => { const params = new URLSearchParams(); - if (datasetId) params.set('dataset', datasetId); - if (experimentName) params.set('experiment', experimentName); - params.set('from', 'evaluations'); + if (datasetId) params.set("dataset", datasetId); + if (experimentName) params.set("experiment", experimentName); + params.set("from", "evaluations"); router.push(`/configurations?${params.toString()}`); }; if (isLoading) { return (
-
-

- {compact ? 'Configuration *' : 'Select Configuration'} +
+

+ {compact ? "Configuration *" : "Select Configuration"}

Loading configurations...
@@ -130,17 +156,28 @@ export default function ConfigSelector({ if (error) { return (
-
-

- {compact ? 'Configuration *' : 'Select Configuration'} -

-
+

+ {compact ? "Configuration *" : "Select Configuration"} +

+
+
{error}
@@ -149,14 +186,27 @@ export default function ConfigSelector({ return (
{/* Header */} -
+
-

- {compact ? 'Configuration *' : 'Select Configuration'} +

+ {compact ? "Configuration *" : "Select Configuration"}

{!compact && ( @@ -173,8 +223,12 @@ export default function ConfigSelector({ border: `1px solid ${colors.border}`, color: colors.text.primary, }} - onMouseEnter={(e) => e.currentTarget.style.backgroundColor = colors.bg.secondary} - onMouseLeave={(e) => e.currentTarget.style.backgroundColor = colors.bg.primary} + onMouseEnter={(e) => + (e.currentTarget.style.backgroundColor = colors.bg.secondary) + } + onMouseLeave={(e) => + (e.currentTarget.style.backgroundColor = colors.bg.primary) + } > Browse Library @@ -186,11 +240,15 @@ export default function ConfigSelector({ border: `1px solid ${colors.border}`, color: colors.text.primary, }} - onMouseEnter={(e) => e.currentTarget.style.backgroundColor = colors.bg.secondary} - onMouseLeave={(e) => e.currentTarget.style.backgroundColor = colors.bg.primary} + onMouseEnter={(e) => + (e.currentTarget.style.backgroundColor = colors.bg.secondary) + } + onMouseLeave={(e) => + (e.currentTarget.style.backgroundColor = colors.bg.primary) + } > - {selectedConfig ? 'Edit Config' : 'Create Config'} + {selectedConfig ? "Edit Config" : "Create Config"}
@@ -199,13 +257,19 @@ export default function ConfigSelector({ {configGroups.length === 0 ? (
-

+

No configurations found

@@ -214,9 +278,16 @@ export default function ConfigSelector({ @@ -224,7 +295,7 @@ export default function ConfigSelector({ ) : ( <> {/* Dropdown Selector */} -

+
{isDropdownOpen ? ( /* Search Input when dropdown is open */ {filteredConfigGroups.length === 0 ? ( -
- {searchQuery ? `No configurations match "${searchQuery}"` : 'No configurations available'} +
+ {searchQuery + ? `No configurations match "${searchQuery}"` + : "No configurations available"}
) : ( filteredConfigGroups.map((group) => ( -
- {/* Config group header */} -
- {group.name} ({group.totalVersions} version{group.totalVersions !== 1 ? 's' : ''}) -
- {/* Versions */} - {group.versions.map((version) => ( -
- {selectedConfig?.id === version.id && ( - - )} - - ))} -
- )))} + {selectedConfig?.id === version.id && ( + + )} + + ))} +
+ )) + )}
)}
@@ -344,36 +462,65 @@ export default function ConfigSelector({ {/* Configuration Details */}
-
+
Provider & Model
-
+
{selectedConfig.provider}/{selectedConfig.modelName}
-
-
- Temperature -
-
- {selectedConfig.temperature.toFixed(2)} + {selectedConfig.temperature !== undefined && ( +
+
+ Temperature +
+
+ {selectedConfig.temperature.toFixed(2)} +
-
+ )} {selectedConfig.tools && selectedConfig.tools.length > 0 && ( <>
-
+
Knowledge Base IDs
-
- {selectedConfig.tools.map(tool => tool.knowledge_base_ids).flat().join(', ') || 'None'} +
+ {selectedConfig.tools + .map((tool) => tool.knowledge_base_ids) + .flat() + .join(", ") || "None"}
-
+
Max Results
-
+
{selectedConfig.tools[0].max_num_results}
@@ -382,32 +529,34 @@ export default function ConfigSelector({
{/* Prompt Preview */} -
+
-
+
Prompt Preview
{selectedConfig.instructions && isPromptOverflowing && ( )}
- {selectedConfig.instructions || 'No instructions set'} + {selectedConfig.instructions || "No instructions set"}
@@ -417,10 +566,7 @@ export default function ConfigSelector({ {/* Click outside to close dropdown */} {isDropdownOpen && ( -
+
)}
); diff --git a/app/evaluations/page.tsx b/app/evaluations/page.tsx index 36b016e..e837f66 100644 --- a/app/evaluations/page.tsx +++ b/app/evaluations/page.tsx @@ -258,11 +258,8 @@ function SimplifiedEvalContent() { throw new Error(errorData.error || errorData.message || `Evaluation failed with status ${response.status}`); } - const data = await response.json(); - const evalId = data.id || data.data?.id || data.eval_id || 'unknown'; - setIsEvaluating(false); - toast.success(`Evaluation created! ${evalId !== 'unknown' ? `Job ID: ${evalId}` : ''}`); + toast.success(`Evaluation created!`); return true; } catch (error: unknown) { toast.error(`Failed to run evaluation: ${error instanceof Error ? error.message : 'Unknown error'}`); diff --git a/app/lib/useConfigs.ts b/app/lib/useConfigs.ts index 7618088..ed28290 100644 --- a/app/lib/useConfigs.ts +++ b/app/lib/useConfigs.ts @@ -8,7 +8,7 @@ * - In-memory cache to avoid redundant fetches within same session */ -import { useState, useEffect, useCallback, useRef } from 'react'; +import { useState, useEffect, useCallback, useRef } from "react"; import { ConfigPublic, ConfigVersionPublic, @@ -16,7 +16,8 @@ import { ConfigListResponse, ConfigVersionListResponse, ConfigVersionResponse, -} from './configTypes'; +} from "./configTypes"; +import { isGpt5Model } from "./models"; // ============ TYPES ============ @@ -32,8 +33,8 @@ export interface SavedConfig { promptContent: string; // Same as instructions for compatibility modelName: string; provider: string; - type: 'text' | 'stt' | 'tts'; // Config type - always present in UI (defaults to 'text') - temperature: number; + type: "text" | "stt" | "tts"; // Config type - always present in UI (defaults to 'text') + temperature?: number; // undefined for models that don't support temperature (e.g. GPT-5) vectorStoreIds: string; tools?: Tool[]; commit_message?: string | null; @@ -59,22 +60,22 @@ interface ConfigCache { // ============ CONSTANTS ============ -const CACHE_KEY = 'kaapi_configs_cache'; +const CACHE_KEY = "kaapi_configs_cache"; const CACHE_MAX_AGE_MS = 5 * 60 * 1000; // 5 minutes - cache is considered stale after this // ============ HELPER FUNCTIONS ============ // Get API key from localStorage const getApiKey = (): string | null => { - if (typeof window === 'undefined') return null; + if (typeof window === "undefined") return null; try { - const stored = localStorage.getItem('kaapi_api_keys'); + const stored = localStorage.getItem("kaapi_api_keys"); if (stored) { const keys = JSON.parse(stored); return keys.length > 0 ? keys[0].key : null; } } catch (e) { - console.error('Failed to get API key:', e); + console.error("Failed to get API key:", e); } return null; }; @@ -82,7 +83,7 @@ const getApiKey = (): string | null => { // Flatten config version for UI const flattenConfigVersion = ( config: ConfigPublic, - version: ConfigVersionPublic + version: ConfigVersionPublic, ): SavedConfig => { const blob = version.config_blob; const params = blob.completion.params; @@ -100,9 +101,10 @@ const flattenConfigVersion = ( : [params.knowledge_base_ids]; kbIds.forEach((kbId: string) => { - if (kbId) { // Only add non-empty IDs + if (kbId) { + // Only add non-empty IDs tools.push({ - type: 'file_search', + type: "file_search", knowledge_base_ids: [kbId], // Each tool gets one ID for UI max_num_results: params.max_num_results || 20, }); @@ -117,13 +119,15 @@ const flattenConfigVersion = ( description: config.description, version: version.version, timestamp: version.inserted_at, - instructions: params.instructions || '', - promptContent: params.instructions || '', - modelName: params.model || '', + instructions: params.instructions || "", + promptContent: params.instructions || "", + modelName: params.model || "", provider: blob.completion.provider, - type: blob.completion.type || 'text', // Default to 'text' for backward compatibility - temperature: params.temperature ?? 0.7, - vectorStoreIds: tools[0]?.knowledge_base_ids?.[0] || '', + type: blob.completion.type || "text", // Default to 'text' for backward compatibility + temperature: isGpt5Model(params.model) + ? params.temperature + : (params.temperature ?? 0.7), + vectorStoreIds: tools[0]?.knowledge_base_ids?.[0] || "", tools: tools, commit_message: version.commit_message, }; @@ -157,37 +161,37 @@ const groupConfigs = (configs: SavedConfig[]): ConfigGroup[] => { // Load cache from localStorage const loadCache = (): ConfigCache | null => { - if (typeof window === 'undefined') return null; + if (typeof window === "undefined") return null; try { const cached = localStorage.getItem(CACHE_KEY); if (cached) { return JSON.parse(cached); } } catch (e) { - console.error('Failed to load config cache:', e); + console.error("Failed to load config cache:", e); } return null; }; // Save cache to localStorage const saveCache = (cache: ConfigCache): void => { - if (typeof window === 'undefined') return; + if (typeof window === "undefined") return; try { localStorage.setItem(CACHE_KEY, JSON.stringify(cache)); } catch (e) { - console.error('Failed to save config cache:', e); + console.error("Failed to save config cache:", e); } }; // Clear cache export const clearConfigCache = (): void => { - if (typeof window === 'undefined') return; + if (typeof window === "undefined") return; try { localStorage.removeItem(CACHE_KEY); // Also clear in-memory cache inMemoryCache = null; } catch (e) { - console.error('Failed to clear config cache:', e); + console.error("Failed to clear config cache:", e); } }; @@ -213,7 +217,9 @@ export function useConfigs(): UseConfigsResult { const fetchInProgress = useRef(false); // Store refetch function in ref for background validation - const refetchRef = useRef<((force: boolean) => Promise) | undefined>(undefined); + const refetchRef = useRef<((force: boolean) => Promise) | undefined>( + undefined, + ); const fetchConfigs = useCallback(async (force: boolean = false) => { // Prevent concurrent fetches @@ -221,7 +227,7 @@ export function useConfigs(): UseConfigsResult { const apiKey = getApiKey(); if (!apiKey) { - setError('No API key found. Please add an API key in the Keystore.'); + setError("No API key found. Please add an API key in the Keystore."); setIsLoading(false); return; } @@ -230,8 +236,8 @@ export function useConfigs(): UseConfigsResult { const validateCacheInBackground = async (cache: ConfigCache) => { try { // Fetch just the config list (lightweight call) - const response = await fetch('/api/configs', { - headers: { 'X-API-KEY': apiKey }, + const response = await fetch("/api/configs", { + headers: { "X-API-KEY": apiKey }, }); const data: ConfigListResponse = await response.json(); @@ -256,7 +262,7 @@ export function useConfigs(): UseConfigsResult { // Check for deleted configs if (!needsRefresh) { - const currentIds = new Set(data.data.map(c => c.id)); + const currentIds = new Set(data.data.map((c) => c.id)); for (const cachedId of Object.keys(currentMeta)) { if (!currentIds.has(cachedId)) { needsRefresh = true; @@ -271,10 +277,14 @@ export function useConfigs(): UseConfigsResult { const cached = currentMeta[config.id]; if (cached) { try { - const versionsResponse = await fetch(`/api/configs/${config.id}/versions`, { - headers: { 'X-API-KEY': apiKey }, - }); - const versionsData: ConfigVersionListResponse = await versionsResponse.json(); + const versionsResponse = await fetch( + `/api/configs/${config.id}/versions`, + { + headers: { "X-API-KEY": apiKey }, + }, + ); + const versionsData: ConfigVersionListResponse = + await versionsResponse.json(); if (versionsData.success && versionsData.data) { if (versionsData.data.length !== cached.version_count) { needsRefresh = true; @@ -298,7 +308,7 @@ export function useConfigs(): UseConfigsResult { } } } catch { - console.error('Failed to validate cache'); + console.error("Failed to validate cache"); } }; @@ -351,8 +361,8 @@ export function useConfigs(): UseConfigsResult { saveCache(newCache); inMemoryCache = newCache; } catch { - console.error('Failed to load saved configs'); - setError('Failed to load configurations. Please try again.'); + console.error("Failed to load saved configs"); + setError("Failed to load configurations. Please try again."); } finally { setIsLoading(false); fetchInProgress.current = false; @@ -385,17 +395,20 @@ interface FetchResult { async function fetchAllConfigs(apiKey: string): Promise { // Fetch all configs - const response = await fetch('/api/configs', { - headers: { 'X-API-KEY': apiKey }, + const response = await fetch("/api/configs", { + headers: { "X-API-KEY": apiKey }, }); const data: ConfigListResponse = await response.json(); if (!data.success || !data.data) { - throw new Error(data.error || 'Failed to fetch configs'); + throw new Error(data.error || "Failed to fetch configs"); } const allVersions: SavedConfig[] = []; - const configMeta: Record = {}; + const configMeta: Record< + string, + { updated_at: string; version_count: number } + > = {}; // Fetch versions for all configs in parallel (batched) const BATCH_SIZE = 5; // Fetch 5 configs at a time @@ -405,10 +418,14 @@ async function fetchAllConfigs(apiKey: string): Promise { const batch = configs.slice(i, i + BATCH_SIZE); const batchPromises = batch.map(async (config) => { try { - const versionsResponse = await fetch(`/api/configs/${config.id}/versions`, { - headers: { 'X-API-KEY': apiKey }, - }); - const versionsData: ConfigVersionListResponse = await versionsResponse.json(); + const versionsResponse = await fetch( + `/api/configs/${config.id}/versions`, + { + headers: { "X-API-KEY": apiKey }, + }, + ); + const versionsData: ConfigVersionListResponse = + await versionsResponse.json(); if (versionsData.success && versionsData.data) { // Store metadata for cache validation @@ -422,15 +439,19 @@ async function fetchAllConfigs(apiKey: string): Promise { try { const versionResponse = await fetch( `/api/configs/${config.id}/versions/${versionItem.version}`, - { headers: { 'X-API-KEY': apiKey } } + { headers: { "X-API-KEY": apiKey } }, ); - const versionData: ConfigVersionResponse = await versionResponse.json(); + const versionData: ConfigVersionResponse = + await versionResponse.json(); if (versionData.success && versionData.data) { return flattenConfigVersion(config, versionData.data); } } catch (e) { - console.error(`Failed to fetch version ${versionItem.version}:`, e); + console.error( + `Failed to fetch version ${versionItem.version}:`, + e, + ); } return null; }); @@ -445,7 +466,7 @@ async function fetchAllConfigs(apiKey: string): Promise { }); const batchResults = await Promise.all(batchPromises); - batchResults.forEach(versions => allVersions.push(...versions)); + batchResults.forEach((versions) => allVersions.push(...versions)); } return { configs: allVersions, configMeta }; @@ -459,12 +480,15 @@ export const formatRelativeTime = (timestamp: string | number): string => { const now = Date.now(); let date: number; - if (typeof timestamp === 'string') { + if (typeof timestamp === "string") { // If timestamp doesn't include timezone info, assume it's UTC // and append 'Z' to ensure it's interpreted as UTC - const utcTimestamp = timestamp.endsWith('Z') || timestamp.includes('+') || timestamp.includes('T') && timestamp.split('T')[1].includes('-') - ? timestamp - : timestamp + 'Z'; + const utcTimestamp = + timestamp.endsWith("Z") || + timestamp.includes("+") || + (timestamp.includes("T") && timestamp.split("T")[1].includes("-")) + ? timestamp + : timestamp + "Z"; date = new Date(utcTimestamp).getTime(); } else { date = timestamp; @@ -475,7 +499,7 @@ export const formatRelativeTime = (timestamp: string | number): string => { const hours = Math.floor(diff / 3600000); const days = Math.floor(diff / 86400000); - if (minutes < 1) return 'just now'; + if (minutes < 1) return "just now"; if (minutes < 60) return `${minutes}m ago`; if (hours < 24) return `${hours}h ago`; if (days < 30) return `${days}d ago`; From 653964a01269a4423eadc07a37bf1fb67f000e59 Mon Sep 17 00:00:00 2001 From: Ayush8923 <80516839+Ayush8923@users.noreply.github.com> Date: Sat, 28 Mar 2026 11:42:44 +0530 Subject: [PATCH 3/7] Merge branch 'main' of https://github.com/ProjectTech4DevAI/kaapi-frontend into feat/support-new-models-configuration --- app/components/ConfigCard.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/components/ConfigCard.tsx b/app/components/ConfigCard.tsx index 0152ea4..7ace4d4 100644 --- a/app/components/ConfigCard.tsx +++ b/app/components/ConfigCard.tsx @@ -121,12 +121,11 @@ export default function ConfigCard({ > Temp: - {latestVersion.temperature.toFixed(2)} + {latestVersion.temperature?.toFixed(2) ?? "N/A"}
- {/* Tools Dropdown */} {latestVersion.tools && latestVersion.tools.length > 0 && (
); } -// Wrapper component with Suspense + export default function SimplifiedEval() { return ( }>