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
13 changes: 7 additions & 6 deletions app/api/configs/[config_id]/versions/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
11 changes: 2 additions & 9 deletions app/components/assessment/PromptAndConfigStep.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -479,7 +472,7 @@ export default function PromptAndConfigStep({
disabled={!canProceed}
className="!rounded-lg !px-6"
>
Next: Review
Next: Post Processing
</Button>
</div>
</div>
Expand Down
18 changes: 18 additions & 0 deletions app/components/assessment/ReviewStep.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand All @@ -48,6 +50,8 @@ export default function ReviewStep({
promptTemplate,
outputSchema,
configs,
prefilterConfig,
postProcessingConfig,
} = formState;
const [openSections, setOpenSections] = useState<Set<number>>(
() => new Set(INITIAL_REVIEW_OPEN_SECTIONS),
Expand Down Expand Up @@ -102,6 +106,13 @@ export default function ReviewStep({
onEdit={() => onEditStep(1)}
/>

<PrefilterReview
prefilterConfig={prefilterConfig}
isOpen={openSections.has(REVIEW_SECTIONS.prefilter)}
onToggle={() => toggleSection(REVIEW_SECTIONS.prefilter)}
onEdit={() => onEditStep(2)}
/>

<InputReview
systemInstruction={systemInstruction}
promptTemplate={promptTemplate}
Expand All @@ -122,6 +133,13 @@ export default function ReviewStep({
onToggle={() => toggleSection(REVIEW_SECTIONS.schema)}
onEdit={() => onEditStep(3)}
/>

<PostProcessingReview
postProcessingConfig={postProcessingConfig}
isOpen={openSections.has(REVIEW_SECTIONS.postProcessing)}
onToggle={() => toggleSection(REVIEW_SECTIONS.postProcessing)}
onEdit={() => onEditStep(4)}
/>
</div>

<SubmitReview
Expand Down
104 changes: 104 additions & 0 deletions app/components/assessment/review/PostProcessingReview.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
"use client";

import { POST_PROCESSING_FILTER_OPS } from "@/app/lib/assessment/constants";
import type { PostProcessingConfig } from "@/app/lib/types/assessment";
import ReviewSection from "./ReviewSection";

interface PostProcessingReviewProps {
postProcessingConfig: PostProcessingConfig | null;
isOpen: boolean;
onToggle: () => void;
onEdit: () => void;
}

const OP_LABELS: Record<string, string> = 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 (
<ReviewSection
title="Post Processing"
isOpen={isOpen}
onToggle={onToggle}
onEdit={onEdit}
badge={
isSkipped ? "Skipped" : `${ruleCount} rule${ruleCount > 1 ? "s" : ""}`
}
>
<div className="space-y-3 pt-2">
{isSkipped ? (
<div className="rounded-md bg-neutral-50 p-3 text-xs text-neutral-500">
Skipped — no post-processing configured.
</div>
) : (
<>
{computed.length > 0 && (
<div>
<div className="text-[11px] font-medium text-neutral-500">
Computed columns
</div>
<div className="mt-1 space-y-1">
{computed.map((col, i) => (
<div key={i} className="font-mono text-xs text-neutral-700">
<span className="text-neutral-900">
{col.name || "(unnamed)"}
</span>
<span className="text-neutral-400"> = </span>
{col.formula || "(empty)"}
</div>
))}
</div>
</div>
)}
{filters.length > 0 && (
<div>
<div className="text-[11px] font-medium text-neutral-500">
Filters
</div>
<div className="mt-1 space-y-1">
{filters.map((rule, i) => (
<div key={i} className="font-mono text-xs text-neutral-700">
<span className="text-neutral-900">{rule.column}</span>{" "}
{OP_LABELS[rule.op] ?? rule.op}
{rule.value !== undefined && rule.value !== ""
? ` ${rule.value}`
: ""}
</div>
))}
</div>
</div>
)}
{sorts.length > 0 && (
<div>
<div className="text-[11px] font-medium text-neutral-500">
Sort
</div>
<div className="mt-1 space-y-1">
{sorts.map((rule, i) => (
<div key={i} className="font-mono text-xs text-neutral-700">
{i + 1}.{" "}
<span className="text-neutral-900">{rule.column}</span>{" "}
{rule.direction === "asc" ? "↑ Asc" : "↓ Desc"}
</div>
))}
</div>
</div>
)}
</>
)}
</div>
</ReviewSection>
);
}
116 changes: 116 additions & 0 deletions app/components/assessment/review/PrefilterReview.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<div className="mt-1 flex flex-wrap gap-1.5">
{columns.map((col) => (
<span
key={col}
className="rounded bg-white px-1.5 py-0.5 font-mono text-[11px] text-neutral-700"
>
{col}
</span>
))}
</div>
);
}

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 (
<ReviewSection
title="Eliminatory"
isOpen={isOpen}
onToggle={onToggle}
onEdit={onEdit}
badge={
isSkipped
? "Skipped"
: `${enabledCount} filter${enabledCount > 1 ? "s" : ""}`
}
>
<div className="space-y-3 pt-2">
{isSkipped ? (
<div className="rounded-md bg-neutral-50 p-3 text-xs text-neutral-500">
Skipped — no pre-filters enabled.
</div>
) : (
<>
{topicRelevance && (
<div className="rounded-md bg-neutral-50 p-3">
<div className="text-sm font-semibold text-neutral-900">
Topic Relevance
</div>
<div className="mt-2">
<div className="text-[11px] font-medium text-neutral-500">
Columns
</div>
<ColumnChips columns={topicRelevance.columns} />
</div>
{topicRelevance.attachment_columns &&
topicRelevance.attachment_columns.length > 0 && (
<div className="mt-2">
<div className="text-[11px] font-medium text-neutral-500">
Documents
</div>
<ColumnChips
columns={topicRelevance.attachment_columns}
/>
</div>
)}
<div className="mt-2">
<div className="text-[11px] font-medium text-neutral-500">
Prompt
</div>
<pre
className="mt-1 whitespace-pre-wrap font-mono text-xs text-neutral-700"
style={{
display: "-webkit-box",
WebkitLineClamp: 3,
WebkitBoxOrient: "vertical",
overflow: "hidden",
}}
>
{topicRelevance.prompt}
</pre>
</div>
</div>
)}
{duplicateDetection && (
<div className="rounded-md bg-neutral-50 p-3">
<div className="text-sm font-semibold text-neutral-900">
Duplicate Detection
</div>
<div className="mt-2">
<div className="text-[11px] font-medium text-neutral-500">
Columns
</div>
<ColumnChips columns={duplicateDetection.columns} />
</div>
</div>
)}
</>
)}
</div>
</ReviewSection>
);
}
8 changes: 5 additions & 3 deletions app/lib/assessment/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<number>(
Expand Down
3 changes: 2 additions & 1 deletion app/lib/utils/assessmentFetcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<ConfigVersionResponse>(
`/api/configs/${existingConfig.id}/versions`,
`/api/configs/${existingConfig.id}/versions?${query.toString()}`,
apiKey,
{ method: "POST", body: JSON.stringify(versionCreate) },
);
Expand Down
Loading