- 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>
134 lines
3.1 KiB
TypeScript
134 lines
3.1 KiB
TypeScript
/**
|
|
* 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,
|
|
};
|
|
}
|