Spaces:
Build error
Build error
| /** | |
| * Feedback Loop API Routes | |
| * | |
| * POST /v1/feedback — Submit a rating for a response | |
| * GET /v1/feedback/stats — Get learning statistics | |
| * | |
| * The feedback loop learns from user ratings via Exponential Moving Average. | |
| * Learned adjustments are applied to future AutoTune parameter computations. | |
| */ | |
| import { Router } from 'express' | |
| import { | |
| createInitialFeedbackState, | |
| processFeedback, | |
| computeHeuristics, | |
| getFeedbackStats, | |
| type FeedbackRecord, | |
| type FeedbackState, | |
| } from '../../src/lib/autotune-feedback' | |
| import type { ContextType, AutoTuneParams } from '../../src/lib/autotune' | |
| import { updateSharedProfiles } from './autotune' | |
| export const feedbackRoutes = Router() | |
| // In-memory feedback state (resets on restart — research preview) | |
| let feedbackState: FeedbackState = createInitialFeedbackState() | |
| const VALID_CONTEXTS: ContextType[] = ['code', 'creative', 'analytical', 'conversational', 'chaotic'] | |
| feedbackRoutes.post('/', (req, res) => { | |
| try { | |
| const { | |
| message_id, | |
| context_type, | |
| model = 'unknown', | |
| persona = 'default', | |
| rating, | |
| params, | |
| response_text, | |
| } = req.body | |
| // Validate | |
| if (!message_id || typeof message_id !== 'string') { | |
| res.status(400).json({ error: 'message_id (string) is required' }) | |
| return | |
| } | |
| if (!VALID_CONTEXTS.includes(context_type)) { | |
| res.status(400).json({ | |
| error: `Invalid context_type. Must be one of: ${VALID_CONTEXTS.join(', ')}`, | |
| }) | |
| return | |
| } | |
| if (rating !== 1 && rating !== -1) { | |
| res.status(400).json({ error: 'rating must be 1 (positive) or -1 (negative)' }) | |
| return | |
| } | |
| if (!params || typeof params !== 'object') { | |
| res.status(400).json({ error: 'params (AutoTuneParams object) is required' }) | |
| return | |
| } | |
| // Compute heuristics from response text if provided | |
| const heuristics = response_text | |
| ? computeHeuristics(String(response_text)) | |
| : { responseLength: 0, repetitionScore: 0, averageSentenceLength: 0, vocabularyDiversity: 0 } | |
| const record: FeedbackRecord = { | |
| messageId: message_id, | |
| timestamp: Date.now(), | |
| contextType: context_type, | |
| model: String(model), | |
| persona: String(persona), | |
| params: params as AutoTuneParams, | |
| rating, | |
| heuristics, | |
| } | |
| // Process and update state | |
| feedbackState = processFeedback(feedbackState, record) | |
| // Sync learned profiles to the AutoTune route | |
| updateSharedProfiles(feedbackState.learnedProfiles) | |
| res.json({ | |
| accepted: true, | |
| total_feedback: feedbackState.history.length, | |
| context_type, | |
| learned: feedbackState.learnedProfiles[context_type].sampleCount >= 3, | |
| }) | |
| } catch (err: any) { | |
| res.status(500).json({ error: err.message }) | |
| } | |
| }) | |
| feedbackRoutes.get('/stats', (_req, res) => { | |
| const stats = getFeedbackStats(feedbackState) | |
| res.json({ | |
| total_feedback: stats.totalFeedback, | |
| positive_rate: stats.positiveRate, | |
| context_breakdown: stats.contextBreakdown, | |
| oldest_record: stats.oldestRecord, | |
| newest_record: stats.newestRecord, | |
| }) | |
| }) | |