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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ cd model-services\nonescape-mini
```

Detailed Windows, WSL, and SSH-server setup notes are in `docs/local-development.md`.
For the evaluation workflow demo, follow `docs/fullstack-evaluation-demo.md`.

## Documentation

Expand Down
4 changes: 4 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ Use these when setting up or operating the project.
server setup guidance.
- [Smoke Test Workflow](smoke-test-workflow.md): end-to-end verification after
services are running.
- [Full-Stack Evaluation Demo](fullstack-evaluation-demo.md): admin UI to Java
backend evaluation workflow without model weights.

## Architecture And Contracts

Expand Down Expand Up @@ -49,3 +51,5 @@ These documents are useful for understanding how the project evolved.
4. If discussing architecture, read
[Model Integration Framework](model-integration-framework.md) and
[Async Detection Jobs](async-detection-jobs.md).
5. If demonstrating the project, run through
[Full-Stack Evaluation Demo](fullstack-evaluation-demo.md).
133 changes: 133 additions & 0 deletions docs/fullstack-evaluation-demo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
# Full-Stack Evaluation Demo

This how-to shows how to demonstrate the evaluation workflow from the admin UI
through the Java backend and persistence layer. It uses the deterministic
evaluation model client, so it does not require model weights or a GPU.

## Audience

Use this document when preparing an interview demo, local verification, or a PR
review that needs to exercise evaluation creation and execution.

## Current Status

The evaluation workflow supports:

- Creating an evaluation run from a CSV manifest.
- Persisting run state and sample rows in the Java backend.
- Executing the run through the evaluation model-client boundary.
- Recording aggregate metrics: accuracy, precision, recall, and F1.
- Retrying failed runs through the same service boundary.
- Inspecting runs and wrong samples in `/admin/evaluations`.

The workflow does not download or require real model weights yet. GPU-backed
model inference remains deferred until the project runs on a prepared server.

## Prerequisites

- Node.js 24 or newer.
- Java and Maven for the Spring Boot backend.
- PostgreSQL and Redis for the default backend profile.
- Docker Compose if you want to run the full infrastructure from
`infra/docker-compose.yml`.

If Docker is not ready on the local machine, use `mvn -B test` to verify the
evaluation service with the H2-backed test profile. Full interactive UI
execution still needs a running backend.

## Demo Steps

1. Start the backend infrastructure.

```powershell
docker compose -f infra/docker-compose.yml up --build
```

2. Start the frontend in another terminal.

```powershell
npm install
npm run dev
```

3. Open the admin evaluation page.

```text
http://localhost:5173/admin/evaluations
```

4. Use the default manifest or paste a small manifest:

```csv
filename,groundTruthLabel
real_001.jpg,AUTHENTIC
fake_001.jpg,SYNTHETIC
```

5. Click `Create Evaluation`.

6. Select the created run and click `Run`.

7. Confirm the page displays:

- `COMPLETED` status.
- Completed sample count.
- Accuracy, precision, recall, and F1.
- Wrong or failed samples when predictions do not match labels.

8. Click `Retry` only when a run failed and should be executed again.

## Backend-Only Verification

When Docker is not available, verify the Java evaluation path with tests:

```powershell
cd backend-java
mvn -B test
```

Key tests:

- `EvaluationControllerTest`
- `EvaluationExecutionServiceTest`
- `EvaluationMetricsCalculatorTest`
- `EvaluationRepositoryTest`
- `DeterministicEvaluationModelClientTest`

These tests prove the service boundary, retry behavior, metric calculation, and
database mapping without requiring model weights.

## Frontend Error Behavior

If the frontend is running but the Java backend is not available, admin pages
show:

```text
Backend API unavailable. Start the Java backend and try again.
```

This message is intentionally clearer than the raw Vite proxy `502` response.
It does not hide real backend validation errors: JSON `message`, `error`, and
`detail` fields are still surfaced directly to the user.

## Verification

Run these checks before claiming the demo workflow is ready:

```powershell
npm run test
npm run lint
npm run build
cd backend-java
mvn -B test
```

For a full interactive demo, also create and run one evaluation from
`/admin/evaluations` after the backend infrastructure is running.

## Related Docs

- `docs/documentation-standards.md`
- `docs/model-integration-framework.md`
- `docs/smoke-test-workflow.md`
- `docs/project-improvement-roadmap.md`
59 changes: 49 additions & 10 deletions docs/project-worklog.md
Original file line number Diff line number Diff line change
Expand Up @@ -440,24 +440,63 @@ Why these matter:
local storage.
- They created the foundation for later evaluation and robustness work.

### 2026-07-09: Full-Stack Evaluation Demo Polish

Branch:

```text
feature/fullstack-evaluation-demo-smoke
```

Commit:

```text
See the PR commit history after merge.
```

What changed:

- Added a small frontend test boundary for API error formatting.
- Converted raw gateway failures into a clear backend availability message.
- Documented the admin evaluation demo path from frontend to Java backend.

Why:

- The evaluation workflow already existed, but the demo story needed a cleaner
operator experience when the backend is offline.
- Interviewers should be able to distinguish implemented evaluation execution
from deferred GPU model-weight work.

Verification:

- `npm run test`
- `npm run lint`
- `npm run build`
- `mvn -B test`

Deferred:

- Full interactive Docker demo remains pending until Docker is available.
- Real model weights remain deferred until a GPU server is prepared.

## Next Recommended Work

Continue Phase B from `docs/project-improvement-roadmap.md`:
Continue Phase B from `docs/project-improvement-roadmap.md` with a narrower
verification and insight branch:

```text
feature/evaluation-frontend
feature/evaluation-result-insights
```

Scope:

- Add an evaluation list and detail page.
- Display status, attempts, aggregate metrics, and sample rows.
- Show wrong-sample filtering first; confusion matrix can follow in a later
polish branch.
- Keep the existing frontend visual style.
- Add a small confusion matrix or label breakdown to `/admin/evaluations`.
- Add clearer empty states for runs with no wrong samples.
- Keep the deterministic model boundary until GPU weights are available.
- Preserve the current frontend visual style.

Reason:

The backend now has a measurable evaluation workflow. The next interview-visible
step is to make the evaluation result easy to inspect without changing the
project into a broad dashboard.
The project now has evaluation execution and a usable admin workbench. The next
interview-visible step is to make model quality easier to explain without
expanding scope into training, video detection, or heavy model operations.
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# Full-Stack Evaluation Demo Smoke Implementation Plan

> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** Make the existing evaluation backend and admin evaluation page easier to demo as one full-stack workflow without downloading model weights.

**Architecture:** Keep the Java backend as the business and persistence boundary, keep model execution behind the existing evaluation client interface, and keep the React admin UI visual style unchanged. This branch only improves frontend API error messaging, adds a small frontend test boundary, and documents a repeatable demo path.

**Tech Stack:** React 18, TypeScript, Vite, Node built-in test runner, Spring Boot Java backend, Maven, existing deterministic evaluation model client.

## Global Constraints

- Do not download model weights.
- Do not replace the current frontend visual style.
- Do not fake successful backend data in the frontend.
- Keep API calls centralized in `src/api/backend.ts`.
- Keep docs concise and aligned with `docs/documentation-standards.md`.
- Verify with `npm run test`, `npm run lint`, `npm run build`, and `mvn -B test`.

---

### Task 1: Add A Frontend API Error Parsing Test

**Files:**
- Modify: `package.json`
- Create: `src/api/errorMessage.test.ts`

**Interfaces:**
- Produces: expected behavior for `formatApiErrorMessage(status: number, bodyText: string): string`
- Consumes later: `src/api/errorMessage.ts`

- [ ] Add a `test` script using Node's built-in test runner:

```json
"test": "node --test --experimental-strip-types src/api/errorMessage.test.ts"
```

- [ ] Create `src/api/errorMessage.test.ts` with assertions for backend JSON errors, Vite proxy 502 errors, plain text errors, and empty responses.

- [ ] Run `npm run test`.

Expected: FAIL because `src/api/errorMessage.ts` does not exist yet.

### Task 2: Implement Reusable API Error Formatting

**Files:**
- Create: `src/api/errorMessage.ts`
- Modify: `src/api/backend.ts`

**Interfaces:**
- Produces: `formatApiErrorMessage(status: number, bodyText: string): string`
- `src/api/backend.ts` must call the formatter after `response.text()`.

- [ ] Implement `formatApiErrorMessage`.
- [ ] Parse backend JSON bodies with `message`, `error`, or `detail` fields.
- [ ] Map `502`, `503`, and `504` to:

```text
Backend API unavailable. Start the Java backend and try again.
```

- [ ] Preserve non-empty plain text errors for other statuses.
- [ ] Use `Request failed with status ${status}` for empty bodies.
- [ ] Run `npm run test`.

Expected: PASS.

### Task 3: Document The Demo Workflow

**Files:**
- Create: `docs/fullstack-evaluation-demo.md`
- Modify: `docs/README.md`
- Modify: `README.md`
- Modify: `docs/project-worklog.md`

**Interfaces:**
- Produces a durable runbook that explains how to demo evaluation creation, execution, metrics inspection, and known local environment limits.

- [ ] Add a how-to document with audience, current status, step-by-step run commands, verification checklist, and deferred Docker/GPU work.
- [ ] Link it from `docs/README.md`.
- [ ] Add a short pointer in root `README.md`.
- [ ] Add a dated worklog entry for the branch.

### Task 4: Verify And Publish

**Files:**
- All changed files.

**Interfaces:**
- Produces a pushed branch and PR.

- [ ] Run `npm run test`.
- [ ] Run `npm run lint`.
- [ ] Run `npm run build`.
- [ ] Run `mvn -B test` from `backend-java`.
- [ ] Run `git diff --check`.
- [ ] Commit as `feat: polish full-stack evaluation demo flow`.
- [ ] Push `feature/fullstack-evaluation-demo-smoke`.
- [ ] Open a PR and wait for CI.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"dev": "vite",
"build": "tsc -b && vite build",
"lint": "eslint .",
"test": "node --test --experimental-strip-types src/api/errorMessage.test.ts",
"preview": "vite preview"
},
"dependencies": {
Expand Down
6 changes: 4 additions & 2 deletions src/api/backend.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { formatApiErrorMessage } from './errorMessage';

export type DetectionStatus = 'QUEUED' | 'INFERENCING' | 'COMPLETED' | 'FAILED';
export type EvaluationStatus = 'QUEUED' | 'RUNNING' | 'COMPLETED' | 'FAILED';
export type ModelLabel = 'AUTHENTIC' | 'SYNTHETIC' | 'UNCERTAIN';
Expand Down Expand Up @@ -159,8 +161,8 @@ async function apiRequest<T>(path: string, init?: RequestInit): Promise<T> {
});

if (!response.ok) {
const message = await response.text();
throw new ApiError(message || `Request failed with status ${response.status}`, response.status);
const bodyText = await response.text();
throw new ApiError(formatApiErrorMessage(response.status, bodyText), response.status);
}

return response.json() as Promise<T>;
Expand Down
34 changes: 34 additions & 0 deletions src/api/errorMessage.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import assert from 'node:assert/strict';
import test from 'node:test';

import { formatApiErrorMessage } from './errorMessage.ts';

test('uses backend JSON message when present', () => {
const message = formatApiErrorMessage(400, '{"message":"Manifest must contain a header row."}');

assert.equal(message, 'Manifest must contain a header row.');
});

test('uses backend JSON error fallback when message is absent', () => {
const message = formatApiErrorMessage(404, '{"error":"Evaluation not found."}');

assert.equal(message, 'Evaluation not found.');
});

test('maps gateway failures to a backend availability hint', () => {
const message = formatApiErrorMessage(502, 'Bad Gateway');

assert.equal(message, 'Backend API unavailable. Start the Java backend and try again.');
});

test('preserves non-empty plain text errors for non-gateway statuses', () => {
const message = formatApiErrorMessage(409, 'Evaluation is already running.');

assert.equal(message, 'Evaluation is already running.');
});

test('falls back to status text when the response body is empty', () => {
const message = formatApiErrorMessage(418, '');

assert.equal(message, 'Request failed with status 418');
});
Loading
Loading