import { ref } from 'vue' import { createOperationFeedback } from '../services/operationFeedback.js' const LOW_RATING_MAX = 3 function pickText(...values) { for (const value of values) { const normalized = String(value || '').trim() if (normalized) { return normalized } } return '' } function resolveUserId(currentUser = {}) { return pickText( currentUser.username, currentUser.email, currentUser.name, 'anonymous' ) } export function normalizeOperationFeedbackContext(context = {}, currentUser = {}) { const traceSummary = context.trace_summary || context.traceSummary || null const result = context.result && typeof context.result === 'object' ? context.result : {} const resultSummary = pickText( context.result_summary, context.resultSummary, result.answer, result.message ) return { runId: pickText(context.run_id, context.runId), conversationId: pickText(context.conversation_id, context.conversationId), userId: pickText(context.user_id, context.userId, resolveUserId(currentUser)), agent: pickText(context.selected_agent, context.selectedAgent, context.agent), source: pickText(context.source, 'user_message'), sessionType: pickText(context.session_type, context.sessionType), operationType: pickText(context.operation_type, context.operationType, 'assistant_round'), operationStatus: pickText(context.operation_status, context.operationStatus, context.status), routeReason: pickText(context.route_reason, context.routeReason), entrySource: pickText(context.entry_source, context.entrySource), traceSummary, resultSummary: resultSummary.slice(0, 500) } } export function buildOperationFeedbackPayload(context = {}, feedback = {}, currentUser = {}) { const normalizedContext = normalizeOperationFeedbackContext(context, currentUser) const rating = Number(feedback.rating || 0) const reason = String(feedback.reason || '').trim() return { run_id: normalizedContext.runId || null, conversation_id: normalizedContext.conversationId || null, user_id: normalizedContext.userId || null, agent: normalizedContext.agent || null, source: normalizedContext.source || null, session_type: normalizedContext.sessionType || null, operation_type: normalizedContext.operationType || 'assistant_round', operation_status: normalizedContext.operationStatus || null, rating, reason: reason || null, context_json: { entry_source: normalizedContext.entrySource, route_reason: normalizedContext.routeReason, trace_summary: normalizedContext.traceSummary, result_summary: normalizedContext.resultSummary, low_rating: rating > 0 && rating <= LOW_RATING_MAX } } } export function useOperationFeedback({ currentUser, toast } = {}) { const operationFeedbackDialog = ref({ open: false, submitting: false, error: '', context: null }) const promptedRunIds = new Set() function openOperationFeedback(context = {}) { const normalized = normalizeOperationFeedbackContext(context, currentUser?.value || {}) if (!normalized.runId || promptedRunIds.has(normalized.runId)) { return } if (normalized.operationStatus && normalized.operationStatus !== 'succeeded') { return } promptedRunIds.add(normalized.runId) globalThis.setTimeout(() => { operationFeedbackDialog.value = { open: true, submitting: false, error: '', context: normalized } }, 320) } function closeOperationFeedback() { if (operationFeedbackDialog.value.submitting) { return } operationFeedbackDialog.value = { ...operationFeedbackDialog.value, open: false, error: '' } } async function submitOperationFeedbackRating(feedback = {}) { const rating = Number(feedback.rating || 0) if (!Number.isInteger(rating) || rating < 1 || rating > 5) { operationFeedbackDialog.value = { ...operationFeedbackDialog.value, error: '请选择 1 到 5 星评分。' } return } const context = operationFeedbackDialog.value.context || {} operationFeedbackDialog.value = { ...operationFeedbackDialog.value, submitting: true, error: '' } try { await createOperationFeedback( buildOperationFeedbackPayload(context, feedback, currentUser?.value || {}) ) operationFeedbackDialog.value = { open: false, submitting: false, error: '', context: null } toast?.('评价已记录,后续会纳入智能体质量统计。') } catch (error) { operationFeedbackDialog.value = { ...operationFeedbackDialog.value, submitting: false, error: error?.message || '评价提交失败,请稍后重试。' } } } return { operationFeedbackDialog, openOperationFeedback, closeOperationFeedback, submitOperationFeedbackRating } }