feat: Add experiments framework and novelty-driven agent loop
- Add complete experiments directory with pilot study infrastructure - 5 experimental conditions (direct, expert-only, attribute-only, full-pipeline, random-perspective) - Human assessment tool with React frontend and FastAPI backend - AUT flexibility analysis with jump signal detection - Result visualization and metrics computation - Add novelty-driven agent loop module (experiments/novelty_loop/) - NoveltyDrivenTaskAgent with expert perspective perturbation - Three termination strategies: breakthrough, exhaust, coverage - Interactive CLI demo with colored output - Embedding-based novelty scoring - Add DDC knowledge domain classification data (en/zh) - Add CLAUDE.md project documentation - Update research report with experiment findings Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
133
experiments/assessment/frontend/src/hooks/useRatings.ts
Normal file
133
experiments/assessment/frontend/src/hooks/useRatings.ts
Normal file
@@ -0,0 +1,133 @@
|
||||
/**
|
||||
* Hook for managing rating submission.
|
||||
*/
|
||||
|
||||
import { useState, useCallback } from 'react';
|
||||
import type { RatingState, DimensionKey } from '../types';
|
||||
import * as api from '../services/api';
|
||||
|
||||
interface UseRatingsOptions {
|
||||
raterId: string | null;
|
||||
queryId: string | null;
|
||||
ideaId: string | null;
|
||||
onSuccess?: () => void;
|
||||
}
|
||||
|
||||
export function useRatings({ raterId, queryId, ideaId, onSuccess }: UseRatingsOptions) {
|
||||
const [ratings, setRatings] = useState<RatingState>({
|
||||
originality: null,
|
||||
elaboration: null,
|
||||
coherence: null,
|
||||
usefulness: null,
|
||||
});
|
||||
const [submitting, setSubmitting] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
// Set a single rating
|
||||
const setRating = useCallback((dimension: DimensionKey, value: number | null) => {
|
||||
setRatings((prev) => ({ ...prev, [dimension]: value }));
|
||||
}, []);
|
||||
|
||||
// Reset all ratings
|
||||
const resetRatings = useCallback(() => {
|
||||
setRatings({
|
||||
originality: null,
|
||||
elaboration: null,
|
||||
coherence: null,
|
||||
usefulness: null,
|
||||
});
|
||||
setError(null);
|
||||
}, []);
|
||||
|
||||
// Check if all ratings are set
|
||||
const isComplete = useCallback(() => {
|
||||
return (
|
||||
ratings.originality !== null &&
|
||||
ratings.elaboration !== null &&
|
||||
ratings.coherence !== null &&
|
||||
ratings.usefulness !== null
|
||||
);
|
||||
}, [ratings]);
|
||||
|
||||
// Submit rating
|
||||
const submit = useCallback(async () => {
|
||||
if (!raterId || !queryId || !ideaId) {
|
||||
setError('Missing required information');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isComplete()) {
|
||||
setError('Please rate all dimensions');
|
||||
return false;
|
||||
}
|
||||
|
||||
setSubmitting(true);
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
await api.submitRating({
|
||||
rater_id: raterId,
|
||||
idea_id: ideaId,
|
||||
query_id: queryId,
|
||||
originality: ratings.originality,
|
||||
elaboration: ratings.elaboration,
|
||||
coherence: ratings.coherence,
|
||||
usefulness: ratings.usefulness,
|
||||
skipped: false,
|
||||
});
|
||||
|
||||
resetRatings();
|
||||
onSuccess?.();
|
||||
return true;
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Failed to submit rating');
|
||||
return false;
|
||||
} finally {
|
||||
setSubmitting(false);
|
||||
}
|
||||
}, [raterId, queryId, ideaId, ratings, isComplete, resetRatings, onSuccess]);
|
||||
|
||||
// Skip idea
|
||||
const skip = useCallback(async () => {
|
||||
if (!raterId || !queryId || !ideaId) {
|
||||
setError('Missing required information');
|
||||
return false;
|
||||
}
|
||||
|
||||
setSubmitting(true);
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
await api.submitRating({
|
||||
rater_id: raterId,
|
||||
idea_id: ideaId,
|
||||
query_id: queryId,
|
||||
originality: null,
|
||||
elaboration: null,
|
||||
coherence: null,
|
||||
usefulness: null,
|
||||
skipped: true,
|
||||
});
|
||||
|
||||
resetRatings();
|
||||
onSuccess?.();
|
||||
return true;
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Failed to skip idea');
|
||||
return false;
|
||||
} finally {
|
||||
setSubmitting(false);
|
||||
}
|
||||
}, [raterId, queryId, ideaId, resetRatings, onSuccess]);
|
||||
|
||||
return {
|
||||
ratings,
|
||||
setRating,
|
||||
resetRatings,
|
||||
isComplete,
|
||||
submit,
|
||||
skip,
|
||||
submitting,
|
||||
error,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user