Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
378 changes: 247 additions & 131 deletions app/components/ConfigModal.tsx

Large diffs are not rendered by default.

25 changes: 9 additions & 16 deletions app/configurations/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,31 +19,25 @@ import { SavedConfig } from '@/app/lib/types/configs';
import ConfigCard from '@/app/components/ConfigCard';
import { LoaderBox } from '@/app/components/Loader';
import { EvalJob } from '@/app/components/types';
import { useAuth } from '@/app/lib/context/AuthContext';
import { useApp } from '@/app/lib/context/AppContext';
import { apiFetch } from '@/app/lib/apiClient';

export default function ConfigLibraryPage() {
const router = useRouter();
const [sidebarCollapsed, setSidebarCollapsed] = useState(false);
const { sidebarCollapsed, setSidebarCollapsed } = useApp();
const { activeKey } = useAuth();
const { configGroups, isLoading, error, refetch, isCached, loadMoreConfigs, hasMoreConfigs, isLoadingMore } = useConfigs({ pageSize: 10 });
const [searchQuery, setSearchQuery] = useState('');
const [evaluationCounts, setEvaluationCounts] = useState<Record<string, number>>({});

// Fetch evaluation counts for each config
useEffect(() => {
const fetchEvaluationCounts = async () => {
try {
const stored = localStorage.getItem('kaapi_api_keys');
if (!stored) return;

const keys = JSON.parse(stored);
if (keys.length === 0) return;

const response = await fetch('/api/evaluations', {
headers: { 'X-API-KEY': keys[0].key },
});

if (!response.ok) return;
if (!activeKey) return;

const data = await response.json();
try {
const data = await apiFetch<EvalJob[] | { data: EvalJob[] }>('/api/evaluations', activeKey.key);
const jobs: EvalJob[] = Array.isArray(data) ? data : (data.data || []);

// Count evaluations per config_id
Expand All @@ -61,7 +55,7 @@ export default function ConfigLibraryPage() {
};

fetchEvaluationCounts();
}, []);
}, [activeKey]);

// Filter configs based on search query
const filteredConfigs = configGroups.filter((group) =>
Expand All @@ -84,7 +78,6 @@ export default function ConfigLibraryPage() {
<Sidebar collapsed={sidebarCollapsed} activeRoute="/configurations" />

<div className="flex-1 flex flex-col overflow-hidden">
{/* Header */}
<div
className="border-b px-6 py-4"
style={{ backgroundColor: colors.bg.primary, borderColor: colors.border }}
Expand Down
14 changes: 10 additions & 4 deletions app/configurations/prompt-editor/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,19 @@
import DiffView from "@/app/components/prompt-editor/DiffView";
import { useToast } from "@/app/components/Toast";
import Loader from "@/app/components/Loader";
import { useApp } from "@/app/lib/context/AppContext";
import { useAuth } from "@/app/lib/context/AuthContext";
import { useConfigs } from "@/app/hooks/useConfigs";
import { SavedConfig } from "@/app/lib/types/configs";
import { getApiKey, invalidateConfigCache } from "@/app/lib/utils";
import { invalidateConfigCache } from "@/app/lib/utils";
import { configState } from "@/app/lib/store/configStore";
import { apiFetch } from "@/app/lib/apiClient";

function PromptEditorContent() {
const toast = useToast();
const searchParams = useSearchParams();
const [sidebarCollapsed, setSidebarCollapsed] = useState(false);
const { sidebarCollapsed, setSidebarCollapsed } = useApp();
const { activeKey } = useAuth();

// URL query params for cross-navigation
const urlConfigId = searchParams.get("config");
Expand Down Expand Up @@ -116,6 +119,8 @@
);
const [compareWith, setCompareWith] = useState<SavedConfig | null>(null);

const getApiKey = (): string | null => activeKey?.key ?? null;

// Populate the editor from a fully-loaded SavedConfig
const applyConfig = React.useCallback(
(config: SavedConfig, selectInHistory?: boolean) => {
Expand All @@ -141,11 +146,13 @@
setCurrentConfigVersion(config.version);
setTools(config.tools || []);
setExpandedConfigs((prev) =>
prev.has(currentConfigParentId) ? prev : new Set([...prev, currentConfigParentId]),
prev.has(currentConfigParentId)
? prev
: new Set([...prev, currentConfigParentId]),
);
if (selectInHistory) setSelectedVersion(config);
},
[],

Check warning on line 155 in app/configurations/prompt-editor/page.tsx

View workflow job for this annotation

GitHub Actions / lint-and-build

React Hook React.useCallback has a missing dependency: 'currentConfigParentId'. Either include it or remove the dependency array. You can also replace multiple useState variables with useReducer if 'setExpandedConfigs' needs the current value of 'currentConfigParentId'
);

// Load a config directly from a SavedConfig object (no savedConfigs lookup needed)
Expand All @@ -168,7 +175,7 @@
loadVersionsForConfig(config.config_id);
applyConfig(config);
},
[applyConfig, loadVersionsForConfig],

Check warning on line 178 in app/configurations/prompt-editor/page.tsx

View workflow job for this annotation

GitHub Actions / lint-and-build

React Hook React.useCallback has a missing dependency: 'defaultConfig'. Either include it or remove the dependency array
);

// Initialize editor from URL params — runs once, on first load completion
Expand Down Expand Up @@ -216,7 +223,7 @@
if (config) applyConfig(config, showHistory);
setEditorReady(true);
})();
}, [

Check warning on line 226 in app/configurations/prompt-editor/page.tsx

View workflow job for this annotation

GitHub Actions / lint-and-build

React Hook useEffect has a missing dependency: 'defaultConfig'. Either include it or remove the dependency array
initialLoadComplete,
urlConfigId,
urlVersion,
Expand Down Expand Up @@ -274,7 +281,6 @@
savedConfigs,
]);


// Save current configuration
const handleSaveConfig = async () => {
if (!currentConfigName.trim()) {
Expand Down
27 changes: 7 additions & 20 deletions app/datasets/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@
"use client"
import { useState, useEffect } from 'react';

import { APIKey, STORAGE_KEY } from '../keystore/page';
import Sidebar from '../components/Sidebar';
import { useToast } from '../components/Toast';
import { useAuth } from '@/app/lib/context/AuthContext';
import { useApp } from '@/app/lib/context/AppContext';
import type { APIKey } from '@/app/keystore/page';
import Sidebar from '@/app/components/Sidebar';
import { useToast } from '@/app/components/Toast';

// Backend response interface
export interface Dataset {
Expand All @@ -28,7 +30,7 @@ export const DATASETS_STORAGE_KEY = 'kaapi_datasets';

export default function Datasets() {
const toast = useToast();
const [sidebarCollapsed, setSidebarCollapsed] = useState(false);
const { sidebarCollapsed, setSidebarCollapsed } = useApp();
const [isModalOpen, setIsModalOpen] = useState(false);
const [datasets, setDatasets] = useState<Dataset[]>([]);
const [selectedFile, setSelectedFile] = useState<File | null>(null);
Expand All @@ -37,27 +39,12 @@ export default function Datasets() {
const [isUploading, setIsUploading] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const [apiKey, setApiKey] = useState<APIKey | null>(null);
const { activeKey: apiKey } = useAuth();

// Pagination state
const [currentPage, setCurrentPage] = useState(1);
const itemsPerPage = 10;

// Load API key from localStorage
useEffect(() => {
const stored = localStorage.getItem(STORAGE_KEY);
if (stored) {
try {
const keys = JSON.parse(stored);
if (keys.length > 0) {
setApiKey(keys[0]);
}
} catch (e) {
console.error('Failed to load API key:', e);
}
}
}, []);

// Fetch datasets from backend when API key is available
useEffect(() => {
if (apiKey) {
Expand Down
29 changes: 8 additions & 21 deletions app/document/page.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
"use client";

import { useState, useEffect } from 'react';
import { APIKey, STORAGE_KEY } from '../keystore/page';
import Sidebar from '../components/Sidebar';
import { useToast } from '../components/Toast';
import { formatDate } from '../components/utils';
import { useAuth } from '@/app/lib/context/AuthContext';
import { useApp } from '@/app/lib/context/AppContext';
import type { APIKey } from '@/app/keystore/page';
import Sidebar from '@/app/components/Sidebar';
import { useToast } from '@/app/components/Toast';
import { formatDate } from '@/app/components/utils';

// Backend response interface
export interface Document {
Expand All @@ -19,7 +21,7 @@

export default function DocumentPage() {
const toast = useToast();
const [sidebarCollapsed, setSidebarCollapsed] = useState(false);
const { sidebarCollapsed, setSidebarCollapsed } = useApp();
const [isModalOpen, setIsModalOpen] = useState(false);
const [documents, setDocuments] = useState<Document[]>([]);
const [selectedDocument, setSelectedDocument] = useState<Document | null>(null);
Expand All @@ -28,27 +30,12 @@
const [isUploading, setIsUploading] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const [apiKey, setApiKey] = useState<APIKey | null>(null);
const { activeKey: apiKey } = useAuth();

// Pagination state
const [currentPage, setCurrentPage] = useState(1);
const [itemsPerPage] = useState(10);

// Load API key from localStorage
useEffect(() => {
const stored = localStorage.getItem(STORAGE_KEY);
if (stored) {
try {
const keys = JSON.parse(stored);
if (keys.length > 0) {
setApiKey(keys[0]);
}
} catch (e) {
console.error('Failed to load API key:', e);
}
}
}, []);

// Fetch documents from backend when API key is available
useEffect(() => {
if (apiKey) {
Expand Down Expand Up @@ -692,7 +679,7 @@
</p>
</div>
) : (
<img

Check warning on line 682 in app/document/page.tsx

View workflow job for this annotation

GitHub Actions / lint-and-build

Using `<img>` could result in slower LCP and higher bandwidth. Consider using `<Image />` from `next/image` or a custom image loader to automatically optimize images. This may incur additional usage or cost from your provider. See: https://nextjs.org/docs/messages/no-img-element
src={document.signed_url}
alt={document.fname}
className="max-w-full h-auto rounded"
Expand Down
24 changes: 8 additions & 16 deletions app/evaluations/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@

import { useState, useEffect, useCallback } from "react";
import { useRouter, useParams } from "next/navigation";
import { APIKey, STORAGE_KEY } from "@/app/keystore/page";
import { useAuth } from "@/app/lib/context/AuthContext";
import { useApp } from "@/app/lib/context/AppContext";
import {
EvalJob,
AssistantConfig,
Expand Down Expand Up @@ -39,9 +40,9 @@ export default function EvaluationReport() {
>(undefined);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const [apiKeys, setApiKeys] = useState<APIKey[]>([]);
const { apiKeys } = useAuth();
const { sidebarCollapsed, setSidebarCollapsed } = useApp();
const [selectedKeyId, setSelectedKeyId] = useState<string>("");
const [sidebarCollapsed, setSidebarCollapsed] = useState(false);
const [isConfigModalOpen, setIsConfigModalOpen] = useState(false);
const [exportFormat, setExportFormat] = useState<"row" | "grouped">("row");
const [isResyncing, setIsResyncing] = useState(false);
Expand All @@ -64,21 +65,12 @@ export default function EvaluationReport() {
return `"${sanitized}"`;
};

// Load API keys from localStorage
// Set initial selected key from context
useEffect(() => {
const stored = localStorage.getItem(STORAGE_KEY);
if (stored) {
try {
const keys = JSON.parse(stored);
setApiKeys(keys);
if (keys.length > 0) {
setSelectedKeyId(keys[0].id);
}
} catch (e) {
console.error("Failed to load API keys:", e);
}
if (apiKeys.length > 0 && !selectedKeyId) {
setSelectedKeyId(apiKeys[0].id);
}
}, []);
}, [apiKeys, selectedKeyId]);

// Fetch job details
const fetchJobDetails = useCallback(async () => {
Expand Down
24 changes: 8 additions & 16 deletions app/evaluations/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@
import { useState, useEffect, useCallback, Suspense } from 'react';
import { colors } from '@/app/lib/colors';
import { useSearchParams } from 'next/navigation'
import { APIKey, STORAGE_KEY } from '@/app/keystore/page';
import { Dataset } from '@/app/datasets/page';
import Sidebar from '@/app/components/Sidebar';
import TabNavigation from '@/app/components/TabNavigation';
import { useToast } from '@/app/components/Toast';
import { useAuth } from '@/app/lib/context/AuthContext';
import { useApp } from '@/app/lib/context/AppContext';
import Loader from '@/app/components/Loader';
import DatasetsTab from '@/app/components/evaluations/DatasetsTab';
import EvaluationsTab from '@/app/components/evaluations/EvaluationsTab';
Expand All @@ -32,8 +33,8 @@ function SimplifiedEvalContent() {
return (tabParam === 'evaluations' || tabParam === 'datasets') ? tabParam as Tab : 'datasets';
});

const [sidebarCollapsed, setSidebarCollapsed] = useState(false);
const [apiKeys, setApiKeys] = useState<APIKey[]>([]);
const { sidebarCollapsed, setSidebarCollapsed } = useApp();
const { apiKeys } = useAuth();
const [selectedKeyId, setSelectedKeyId] = useState<string>('');

// Dataset creation state
Expand Down Expand Up @@ -62,21 +63,12 @@ function SimplifiedEvalContent() {
});
const [isEvaluating, setIsEvaluating] = useState(false);

// Load API keys
// Set initial selected key from context
useEffect(() => {
const stored = localStorage.getItem(STORAGE_KEY);
if (stored) {
try {
const keys = JSON.parse(stored);
setApiKeys(keys);
if (keys.length > 0) {
setSelectedKeyId(keys[0].id);
}
} catch (e) {
console.error('Failed to load API keys:', e);
}
if (apiKeys.length > 0 && !selectedKeyId) {
setSelectedKeyId(apiKeys[0].id);
}
}, []);
}, [apiKeys, selectedKeyId]);

// Fetch datasets from backend
const loadStoredDatasets = useCallback(async () => {
Expand Down
31 changes: 6 additions & 25 deletions app/keystore/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

import { useState, useEffect } from 'react';
import Sidebar from '../components/Sidebar';
import { useAuth } from '@/app/lib/context/AuthContext';
import { useApp } from '@/app/lib/context/AppContext';

export interface APIKey {
id: string;
Expand All @@ -20,35 +22,14 @@ export interface APIKey {
export const STORAGE_KEY = 'kaapi_api_keys';

export default function KaapiKeystore() {
const [sidebarCollapsed, setSidebarCollapsed] = useState(false);
const { sidebarCollapsed, setSidebarCollapsed } = useApp();
const [isModalOpen, setIsModalOpen] = useState(false);
const [apiKeys, setApiKeys] = useState<APIKey[]>([]);
const { apiKeys, addKey, removeKey: removeApiKey } = useAuth();
const [newKeyLabel, setNewKeyLabel] = useState('');
const [newKeyValue, setNewKeyValue] = useState('');
const [newKeyProvider, setNewKeyProvider] = useState('Kaapi');
const [visibleKeys, setVisibleKeys] = useState<Set<string>>(new Set());

// Load API keys from localStorage on mount
useEffect(() => {
const stored = localStorage.getItem(STORAGE_KEY);
if (stored) {
try {
// eslint-disable-next-line react-hooks/set-state-in-effect
setApiKeys(JSON.parse(stored));
} catch (e) {
console.error('Failed to load API keys:', e);
}
}
}, []);

// Save API keys to localStorage whenever they change
useEffect(() => {
if (apiKeys.length > 0) {
localStorage.setItem(STORAGE_KEY, JSON.stringify(apiKeys));
} else {
localStorage.removeItem(STORAGE_KEY);
}
}, [apiKeys]);

const providers = ['Kaapi'];

Expand All @@ -66,7 +47,7 @@ export default function KaapiKeystore() {
createdAt: new Date().toISOString(),
};

setApiKeys([...apiKeys, newKey]);
addKey(newKey);
setNewKeyLabel('');
setNewKeyValue('');
setNewKeyProvider('Kaapi');
Expand All @@ -76,7 +57,7 @@ export default function KaapiKeystore() {
};

const handleDeleteKey = (id: string) => {
setApiKeys(apiKeys.filter(key => key.id !== id));
removeApiKey(id);
setVisibleKeys(prev => {
const next = new Set(prev);
next.delete(id);
Expand Down
Loading
Loading