From 92320004c0fb868ca6d0cb0b82662322f2061f8d Mon Sep 17 00:00:00 2001 From: Prashant Vasudevan <71649489+vprashrex@users.noreply.github.com> Date: Tue, 16 Jun 2026 22:56:12 +0530 Subject: [PATCH] Assessment: Review pipeline stages + config save/tag fixes --- app/api/configs/[config_id]/versions/route.ts | 13 +- .../assessment/PromptAndConfigStep.tsx | 11 +- app/components/assessment/ReviewStep.tsx | 18 +++ .../review/PostProcessingReview.tsx | 104 ++++++++++++++++ .../assessment/review/PrefilterReview.tsx | 116 ++++++++++++++++++ app/lib/assessment/constants.ts | 8 +- app/lib/utils/assessmentFetcher.ts | 3 +- 7 files changed, 254 insertions(+), 19 deletions(-) create mode 100644 app/components/assessment/review/PostProcessingReview.tsx create mode 100644 app/components/assessment/review/PrefilterReview.tsx diff --git a/app/api/configs/[config_id]/versions/route.ts b/app/api/configs/[config_id]/versions/route.ts index a01a2249..566b2a88 100644 --- a/app/api/configs/[config_id]/versions/route.ts +++ b/app/api/configs/[config_id]/versions/route.ts @@ -33,14 +33,15 @@ export async function POST( try { const body = await request.json(); - const { status, data } = await apiClient( - request, + const { searchParams } = new URL(request.url); + const endpoint = withQueryParams( `/api/v1/configs/${config_id}/versions`, - { - method: "POST", - body: JSON.stringify(body), - }, + searchParams, ); + const { status, data } = await apiClient(request, endpoint, { + method: "POST", + body: JSON.stringify(body), + }); return NextResponse.json(data, { status }); } catch (_error) { diff --git a/app/components/assessment/PromptAndConfigStep.tsx b/app/components/assessment/PromptAndConfigStep.tsx index 6f3889ac..f6b269ad 100644 --- a/app/components/assessment/PromptAndConfigStep.tsx +++ b/app/components/assessment/PromptAndConfigStep.tsx @@ -340,19 +340,12 @@ export default function PromptAndConfigStep({ } setIsSaving(true); try { - const existingConfig = - configCards.find( - (c) => - c.name.trim().toLowerCase() === configName.trim().toLowerCase(), - ) ?? null; const saved = await saveAssessmentConfig({ apiKey, configName: configName.trim(), commitMessage: commitMessage.trim(), configBlob: draft, - existingConfig: existingConfig - ? { id: existingConfig.id, name: existingConfig.name } - : null, + existingConfig: null, }); addSelection({ config_id: saved.config_id, @@ -479,7 +472,7 @@ export default function PromptAndConfigStep({ disabled={!canProceed} className="!rounded-lg !px-6" > - Next: Review + Next: Post Processing diff --git a/app/components/assessment/ReviewStep.tsx b/app/components/assessment/ReviewStep.tsx index 016d5c32..3f68cdd8 100644 --- a/app/components/assessment/ReviewStep.tsx +++ b/app/components/assessment/ReviewStep.tsx @@ -27,6 +27,8 @@ import ConfigsReview from "./review/ConfigsReview"; import DatasetReview from "./review/DatasetReview"; import ExperimentReview from "./review/ExperimentReview"; import InputReview from "./review/InputReview"; +import PostProcessingReview from "./review/PostProcessingReview"; +import PrefilterReview from "./review/PrefilterReview"; import SchemaReview from "./review/SchemaReview"; import SubmitReview from "./review/SubmitReview"; @@ -48,6 +50,8 @@ export default function ReviewStep({ promptTemplate, outputSchema, configs, + prefilterConfig, + postProcessingConfig, } = formState; const [openSections, setOpenSections] = useState>( () => new Set(INITIAL_REVIEW_OPEN_SECTIONS), @@ -102,6 +106,13 @@ export default function ReviewStep({ onEdit={() => onEditStep(1)} /> + toggleSection(REVIEW_SECTIONS.prefilter)} + onEdit={() => onEditStep(2)} + /> + toggleSection(REVIEW_SECTIONS.schema)} onEdit={() => onEditStep(3)} /> + + toggleSection(REVIEW_SECTIONS.postProcessing)} + onEdit={() => onEditStep(4)} + /> void; + onEdit: () => void; +} + +const OP_LABELS: Record = Object.fromEntries( + POST_PROCESSING_FILTER_OPS.map((op) => [op.value, op.label]), +); + +export default function PostProcessingReview({ + postProcessingConfig, + isOpen, + onToggle, + onEdit, +}: PostProcessingReviewProps) { + const computed = postProcessingConfig?.computed_columns ?? []; + const filters = postProcessingConfig?.filter ?? []; + const sorts = postProcessingConfig?.sort ?? []; + const ruleCount = computed.length + filters.length + sorts.length; + const isSkipped = ruleCount === 0; + + return ( + 1 ? "s" : ""}` + } + > +
+ {isSkipped ? ( +
+ Skipped — no post-processing configured. +
+ ) : ( + <> + {computed.length > 0 && ( +
+
+ Computed columns +
+
+ {computed.map((col, i) => ( +
+ + {col.name || "(unnamed)"} + + = + {col.formula || "(empty)"} +
+ ))} +
+
+ )} + {filters.length > 0 && ( +
+
+ Filters +
+
+ {filters.map((rule, i) => ( +
+ {rule.column}{" "} + {OP_LABELS[rule.op] ?? rule.op} + {rule.value !== undefined && rule.value !== "" + ? ` ${rule.value}` + : ""} +
+ ))} +
+
+ )} + {sorts.length > 0 && ( +
+
+ Sort +
+
+ {sorts.map((rule, i) => ( +
+ {i + 1}.{" "} + {rule.column}{" "} + {rule.direction === "asc" ? "↑ Asc" : "↓ Desc"} +
+ ))} +
+
+ )} + + )} +
+
+ ); +} diff --git a/app/components/assessment/review/PrefilterReview.tsx b/app/components/assessment/review/PrefilterReview.tsx new file mode 100644 index 00000000..542d4e0b --- /dev/null +++ b/app/components/assessment/review/PrefilterReview.tsx @@ -0,0 +1,116 @@ +"use client"; + +import type { PrefilterConfig } from "@/app/lib/types/assessment"; +import ReviewSection from "./ReviewSection"; + +interface PrefilterReviewProps { + prefilterConfig: PrefilterConfig | null; + isOpen: boolean; + onToggle: () => void; + onEdit: () => void; +} + +function ColumnChips({ columns }: { columns: string[] }) { + return ( +
+ {columns.map((col) => ( + + {col} + + ))} +
+ ); +} + +export default function PrefilterReview({ + prefilterConfig, + isOpen, + onToggle, + onEdit, +}: PrefilterReviewProps) { + const topicRelevance = prefilterConfig?.topic_relevance; + const duplicateDetection = prefilterConfig?.duplicate_detection; + const enabledCount = (topicRelevance ? 1 : 0) + (duplicateDetection ? 1 : 0); + const isSkipped = enabledCount === 0; + + return ( + 1 ? "s" : ""}` + } + > +
+ {isSkipped ? ( +
+ Skipped — no pre-filters enabled. +
+ ) : ( + <> + {topicRelevance && ( +
+
+ Topic Relevance +
+
+
+ Columns +
+ +
+ {topicRelevance.attachment_columns && + topicRelevance.attachment_columns.length > 0 && ( +
+
+ Documents +
+ +
+ )} +
+
+ Prompt +
+
+                    {topicRelevance.prompt}
+                  
+
+
+ )} + {duplicateDetection && ( +
+
+ Duplicate Detection +
+
+
+ Columns +
+ +
+
+ )} + + )} +
+
+ ); +} diff --git a/app/lib/assessment/constants.ts b/app/lib/assessment/constants.ts index ee6929f7..b376c129 100644 --- a/app/lib/assessment/constants.ts +++ b/app/lib/assessment/constants.ts @@ -135,9 +135,11 @@ export const ASSESSMENT_ROLE_OPTIONS = Object.values( export const REVIEW_SECTIONS = { dataset: 1, columns: 2, - input: 3, - configs: 4, - schema: 5, + prefilter: 3, + input: 4, + configs: 5, + schema: 6, + postProcessing: 7, } as const; export const INITIAL_REVIEW_OPEN_SECTIONS = new Set( diff --git a/app/lib/utils/assessmentFetcher.ts b/app/lib/utils/assessmentFetcher.ts index 4a878254..46635e72 100644 --- a/app/lib/utils/assessmentFetcher.ts +++ b/app/lib/utils/assessmentFetcher.ts @@ -221,8 +221,9 @@ export async function saveAssessmentConfig(params: { commit_message: params.commitMessage.trim() || "Updated assessment configuration", }; + const query = new URLSearchParams({ tag: ASSESSMENT_TAG }); const data = await apiFetch( - `/api/configs/${existingConfig.id}/versions`, + `/api/configs/${existingConfig.id}/versions?${query.toString()}`, apiKey, { method: "POST", body: JSON.stringify(versionCreate) }, );