Skip to content
Closed
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
12 changes: 10 additions & 2 deletions app/api/evaluations/datasets/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import { NextRequest, NextResponse } from 'next/server';
/**
* GET /api/evaluations/datasets
*
* Proxy endpoint to fetch all datasets from the backend.
* Proxy endpoint to fetch text datasets only from the backend.
* This endpoint filters and returns only datasets with type='text'.
*/
export async function GET(request: NextRequest) {
try {
Expand Down Expand Up @@ -36,7 +37,14 @@ export async function GET(request: NextRequest) {
return NextResponse.json(data, { status: response.status });
}

return NextResponse.json(data, { status: 200 });
// Filter to only return text datasets
const filteredData = Array.isArray(data)
? data.filter((dataset: any) => dataset.type === 'text')
: data.data
? { ...data, data: data.data.filter((dataset: any) => dataset.type === 'text') }
: data;
Comment on lines +43 to +45
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Guard nested payload shape before calling .filter().

If data.data is present but not an array, Line 44 throws at runtime. Add an Array.isArray(data.data) check before filtering.

Proposed fix
-const filteredData = Array.isArray(data)
-  ? data.filter((dataset: any) => dataset.type === 'text')
-  : data.data
-    ? { ...data, data: data.data.filter((dataset: any) => dataset.type === 'text') }
-    : data;
+const filteredData = Array.isArray(data)
+  ? data.filter((dataset: any) => dataset.type === 'text')
+  : Array.isArray(data?.data)
+    ? { ...data, data: data.data.filter((dataset: any) => dataset.type === 'text') }
+    : data;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
: data.data
? { ...data, data: data.data.filter((dataset: any) => dataset.type === 'text') }
: data;
const filteredData = Array.isArray(data)
? data.filter((dataset: any) => dataset.type === 'text')
: Array.isArray(data?.data)
? { ...data, data: data.data.filter((dataset: any) => dataset.type === 'text') }
: data;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/api/evaluations/datasets/route.ts` around lines 43 - 45, The code assumes
data.data is an array before calling .filter; update the conditional in the
datasets response assignment to guard with Array.isArray(data.data) so you only
call data.data.filter when Array.isArray(data.data) is true (otherwise leave
data unchanged); locate the expression that builds the final result (the ternary
using data.data ? { ...data, data: data.data.filter(...) } : data) and add the
Array.isArray check to prevent runtime errors.


return NextResponse.json(filteredData, { status: 200 });
} catch (error: any) {
console.error('Proxy error:', error);
return NextResponse.json(
Expand Down
36 changes: 12 additions & 24 deletions app/api/evaluations/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ import { NextRequest, NextResponse } from 'next/server';

/**
* GET /api/evaluations
* Fetches all evaluation jobs
*
* Proxy endpoint to fetch text evaluations only from the backend.
* This endpoint filters and returns only evaluations with type='text'.
*/
export async function GET(request: NextRequest) {
try {
Expand All @@ -22,31 +24,17 @@ export async function GET(request: NextRequest) {

const data = await response.json();

// Log the structure to help debug score visibility issues
//TODO Fix it later
if (data && Array.isArray(data)) {
console.log('[GET /api/evaluations] Sample evaluation structure:', {
firstItem: data[0] ? {
id: data[0].id,
hasScore: !!data[0].score,
hasScores: !!data[0].scores,
scoreKeys: data[0].score ? Object.keys(data[0].score) : [],
scoresKeys: data[0].scores ? Object.keys(data[0].scores) : []
} : 'No items'
});
} else if (data && data.data && Array.isArray(data.data)) {
console.log('[GET /api/evaluations] Sample evaluation structure (nested):', {
firstItem: data.data[0] ? {
id: data.data[0].id,
hasScore: !!data.data[0].score,
hasScores: !!data.data[0].scores,
scoreKeys: data.data[0].score ? Object.keys(data.data[0].score) : [],
scoresKeys: data.data[0].scores ? Object.keys(data.data[0].scores) : []
} : 'No items'
});
if (!response.ok) {
return NextResponse.json(data, { status: response.status });
}

return NextResponse.json(data, { status: response.status });
const filteredData = Array.isArray(data)
? data.filter((item: any) => item.type === 'text')
: data.data
? { ...data, data: data.data.filter((item: any) => item.type === 'text') }
: data;

return NextResponse.json(filteredData, { status: response.status });
} catch (error) {
console.error('Proxy error:', error);
return NextResponse.json(
Expand Down
24 changes: 20 additions & 4 deletions app/api/evaluations/stt/datasets/route.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import { NextResponse, NextRequest } from 'next/server';



export async function GET(request:
/**
* GET /api/evaluations/stt/datasets
*
* Proxy endpoint to fetch STT datasets only from the backend.
* This endpoint filters and returns only datasets with type='stt'.
*/
export async function GET(request:
Request) {
const backendUrl = process.env.NEXT_PUBLIC_BACKEND_URL || 'http://localhost:8000';
const apiKey = request.headers.get('X-API-KEY');
Expand All @@ -15,7 +19,19 @@ export async function GET(request:
});

const data = await response.json();
return NextResponse.json(data, { status: response.status });

if (!response.ok) {
return NextResponse.json(data, { status: response.status });
}

// Filter to only return STT datasets (additional safety check)
const filteredData = Array.isArray(data)
? data.filter((dataset: any) => dataset.type === 'stt')
: data.data
? { ...data, data: data.data.filter((dataset: any) => dataset.type === 'stt') }
: data;
Comment on lines +27 to +32
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

STT filtering misses the data.datasets shape and lacks array guards.

This code only filters top-level arrays and data.data. But the STT page also handles data.datasets; in that response shape, filtering is skipped entirely. Also, calling .filter without Array.isArray checks risks runtime errors.

Proposed fix
-    const filteredData = Array.isArray(data)
-      ? data.filter((dataset: any) => dataset.type === 'stt')
-      : data.data
-        ? { ...data, data: data.data.filter((dataset: any) => dataset.type === 'stt') }
-        : data;
+    const filteredData = Array.isArray(data)
+      ? data.filter((dataset: any) => dataset.type === 'stt')
+      : Array.isArray(data?.datasets)
+        ? { ...data, datasets: data.datasets.filter((dataset: any) => dataset.type === 'stt') }
+        : Array.isArray(data?.data)
+          ? { ...data, data: data.data.filter((dataset: any) => dataset.type === 'stt') }
+          : data;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Filter to only return STT datasets (additional safety check)
const filteredData = Array.isArray(data)
? data.filter((dataset: any) => dataset.type === 'stt')
: data.data
? { ...data, data: data.data.filter((dataset: any) => dataset.type === 'stt') }
: data;
// Filter to only return STT datasets (additional safety check)
const filteredData = Array.isArray(data)
? data.filter((dataset: any) => dataset.type === 'stt')
: Array.isArray(data?.datasets)
? { ...data, datasets: data.datasets.filter((dataset: any) => dataset.type === 'stt') }
: Array.isArray(data?.data)
? { ...data, data: data.data.filter((dataset: any) => dataset.type === 'stt') }
: data;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/api/evaluations/stt/datasets/route.ts` around lines 27 - 32, The STT
filtering logic in the route uses filteredData and currently only handles
top-level arrays and data.data, which misses the data.datasets shape and calls
.filter without array guards; update the filteredData computation to check for
Array.isArray on data, data.data, and data.datasets and apply dataset =>
dataset.type === 'stt' only when each is an array (preserve non-array branches
unchanged), referencing the filteredData variable and the dataset.type predicate
so all response shapes (data, data.data, data.datasets) are safely filtered for
'stt'.


return NextResponse.json(filteredData, { status: response.status });
} catch (error) {
return NextResponse.json(
{ success: false, error: error, data: null },
Expand Down
46 changes: 23 additions & 23 deletions app/evaluations/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
* SimplifiedEval.tsx - Simplified One-Click Evaluation Flow
*
* Two-tab structure:
* 1. Upload Tab: Upload QnA dataset → Run evaluation
* 2. Results Tab: View evaluation results with metrics and detailed logs
* 1. Datasets Tab: Upload QnA dataset → Run evaluation
* 2. Evaluations Tab: View evaluation results with metrics and detailed logs
*/

"use client"
Expand All @@ -24,17 +24,17 @@ import ConfigSelector from '../components/ConfigSelector';
import { useToast } from '../components/Toast';
import Loader, { LoaderBox } from '../components/Loader';

type Tab = 'upload' | 'results';
type Tab = 'datasets' | 'evaluations';

function SimplifiedEvalContent() {
const router = useRouter();
const searchParams = useSearchParams();
const toast = useToast();

// Initialize activeTab from URL query parameter, default to 'upload'
// Initialize activeTab from URL query parameter, default to 'datasets'
const [activeTab, setActiveTab] = useState<Tab>(() => {
const tabParam = searchParams.get('tab');
return (tabParam === 'results' || tabParam === 'upload') ? tabParam as Tab : 'upload';
return (tabParam === 'evaluations' || tabParam === 'datasets') ? tabParam as Tab : 'datasets';
});

const [isEvaluating, setIsEvaluating] = useState(false);
Expand Down Expand Up @@ -266,9 +266,9 @@ function SimplifiedEvalContent() {
// Extract the evaluation ID from response (could be data.id or data.data.id or data.eval_id)
const evalId = data.id || data.data?.id || data.eval_id || 'unknown';

// Redirect to results tab to view evaluation status
// Redirect to evaluations tab to view evaluation status
setIsEvaluating(false);
setActiveTab('results');
setActiveTab('evaluations');

// Show success message
toast.success(`Evaluation job created successfully! ${evalId !== 'unknown' ? `Job ID: ${evalId}` : ''}`);
Expand Down Expand Up @@ -341,8 +341,8 @@ function SimplifiedEvalContent() {
{/* Tab Navigation */}
<TabNavigation
tabs={[
{ id: 'upload', label: 'Datasets' },
{ id: 'results', label: 'Evaluations' }
{ id: 'datasets', label: 'Datasets' },
{ id: 'evaluations', label: 'Evaluations' }
]}
activeTab={activeTab}
onTabChange={(tabId) => setActiveTab(tabId as Tab)}
Expand All @@ -351,8 +351,8 @@ function SimplifiedEvalContent() {
{/* Tab Content */}
<div className="flex-1 overflow-auto p-6" style={{ backgroundColor: '#fafafa' }}>
<div className="max-w-7xl mx-auto space-y-6 page-transition">
{activeTab === 'upload' ? (
<UploadTab
{activeTab === 'datasets' ? (
<DatasetsTab
isEvaluating={isEvaluating}
apiKeys={apiKeys}
selectedKeyId={selectedKeyId}
Expand All @@ -372,7 +372,7 @@ function SimplifiedEvalContent() {
}}
/>
) : (
<ResultsTab apiKeys={apiKeys} selectedKeyId={selectedKeyId} />
<EvaluationsTab apiKeys={apiKeys} selectedKeyId={selectedKeyId} />
)}
</div>
</div>
Expand Down Expand Up @@ -402,8 +402,8 @@ function SimplifiedEvalContent() {
);
}

// ============ UPLOAD TAB COMPONENT ============
interface UploadTabProps {
// ============ DATASETS TAB COMPONENT ============
interface DatasetsTabProps {
isEvaluating: boolean;
apiKeys: APIKey[];
selectedKeyId: string;
Expand All @@ -420,7 +420,7 @@ interface UploadTabProps {
onConfigSelect: (configId: string, configVersion: number) => void;
}

function UploadTab({
function DatasetsTab({
isEvaluating,
apiKeys,
selectedKeyId,
Expand All @@ -435,7 +435,7 @@ function UploadTab({
onExperimentNameChange,
onRunEvaluation,
onConfigSelect,
}: UploadTabProps) {
}: DatasetsTabProps) {
const selectedKey = apiKeys.find(k => k.id === selectedKeyId);
const selectedDataset = storedDatasets.find(d => d.dataset_id.toString() === selectedDatasetId);

Expand Down Expand Up @@ -480,8 +480,8 @@ function UploadTab({
<li>Select a stored dataset or upload a new CSV file (format: question,answer columns)</li>
<li>Configure evaluation settings (experiment name required, other fields optional)</li>
<li>Click <blockquote>Run Evaluation </blockquote> to start the evaluation process</li>
<li>Wait for processing to complete (automatic redirect to results)</li>
<li>View detailed results and metrics in the Results tab</li>
<li>Wait for processing to complete (automatic redirect to evaluations tab)</li>
<li>View detailed results and metrics in the Evaluations tab</li>
</ol>
</div>
)}
Expand Down Expand Up @@ -827,13 +827,13 @@ function UploadTab({
// ============ TYPES ============
// Types are now imported from '../components/types'

// ============ RESULTS TAB COMPONENT ============
interface ResultsTabProps {
// ============ EVALUATIONS TAB COMPONENT ============
interface EvaluationsTabProps {
apiKeys: APIKey[];
selectedKeyId: string;
}

function ResultsTab({ apiKeys, selectedKeyId }: ResultsTabProps) {
function EvaluationsTab({ apiKeys, selectedKeyId }: EvaluationsTabProps) {
const [evalJobs, setEvalJobs] = useState<EvalJob[]>([]);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
Expand Down Expand Up @@ -875,7 +875,7 @@ function ResultsTab({ apiKeys, selectedKeyId }: ResultsTabProps) {

// Debug logging for score visibility
if (jobs.length > 0) {
console.log('[ResultsTab] Sample job data:', {
console.log('[EvaluationsTab] Sample job data:', {
id: jobs[0].id,
run_name: jobs[0].run_name,
hasScore: !!jobs[0].score,
Expand Down Expand Up @@ -1014,7 +1014,7 @@ function ResultsTab({ apiKeys, selectedKeyId }: ResultsTabProps) {
borderColor: '#e5e5e5',
boxShadow: '0 1px 3px rgba(0, 0, 0, 0.04)'
}}>
<p style={{ color: '#737373' }}>No evaluation jobs found. Create one from the Upload tab!</p>
<p style={{ color: '#737373' }}>No evaluation jobs found. Create one from the Datasets tab!</p>
</div>
)}

Expand Down