mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-05-02 13:40:46 +00:00
145 lines
4.4 KiB
TypeScript
145 lines
4.4 KiB
TypeScript
import { useState, useCallback, useEffect } from 'react';
|
|
import {
|
|
type Config,
|
|
logUserFeedback,
|
|
UserFeedbackEvent,
|
|
type UserFeedbackRating,
|
|
} from '@qwen-code/qwen-code-core';
|
|
import { StreamingState, MessageType, type HistoryItem } from '../types.js';
|
|
import type { LoadedSettings } from '../../config/settings.js';
|
|
import type { SessionStatsState } from '../contexts/SessionContext.js';
|
|
|
|
export interface UseFeedbackDialogProps {
|
|
config: Config;
|
|
settings: LoadedSettings;
|
|
streamingState: StreamingState;
|
|
history: HistoryItem[];
|
|
sessionStats: SessionStatsState;
|
|
}
|
|
|
|
export const useFeedbackDialog = ({
|
|
config,
|
|
settings,
|
|
streamingState,
|
|
history,
|
|
sessionStats,
|
|
}: UseFeedbackDialogProps) => {
|
|
// Feedback dialog state
|
|
const [isFeedbackDialogOpen, setIsFeedbackDialogOpen] = useState(false);
|
|
const [feedbackShownForSession, setFeedbackShownForSession] = useState(false);
|
|
|
|
const openFeedbackDialog = useCallback(() => {
|
|
setIsFeedbackDialogOpen(true);
|
|
setFeedbackShownForSession(true);
|
|
}, []);
|
|
|
|
const closeFeedbackDialog = useCallback(
|
|
() => setIsFeedbackDialogOpen(false),
|
|
[],
|
|
);
|
|
|
|
const submitFeedback = useCallback(
|
|
(rating: number) => {
|
|
// Calculate session duration and turn count
|
|
const sessionDurationMs =
|
|
Date.now() - sessionStats.sessionStartTime.getTime();
|
|
let lastUserMessageIndex = -1;
|
|
for (let i = history.length - 1; i >= 0; i--) {
|
|
if (history[i].type === MessageType.USER) {
|
|
lastUserMessageIndex = i;
|
|
break;
|
|
}
|
|
}
|
|
const turnCount =
|
|
lastUserMessageIndex === -1 ? 0 : history.length - lastUserMessageIndex;
|
|
|
|
// Create and log the feedback event
|
|
const feedbackEvent = new UserFeedbackEvent(
|
|
sessionStats.sessionId,
|
|
rating as UserFeedbackRating,
|
|
sessionDurationMs,
|
|
turnCount,
|
|
config.getModel(),
|
|
config.getApprovalMode(),
|
|
);
|
|
|
|
logUserFeedback(config, feedbackEvent);
|
|
closeFeedbackDialog();
|
|
},
|
|
[config, sessionStats, history, closeFeedbackDialog],
|
|
);
|
|
|
|
// Track when to show feedback dialog
|
|
useEffect(() => {
|
|
let timeoutId: NodeJS.Timeout;
|
|
|
|
if (streamingState === StreamingState.Idle && history.length > 0) {
|
|
// Find the last user message and check if there's AI response after it
|
|
let lastUserMessageIndex = -1;
|
|
let hasAIResponseAfterLastUser = false;
|
|
|
|
for (let i = history.length - 1; i >= 0; i--) {
|
|
if (history[i].type === MessageType.USER) {
|
|
lastUserMessageIndex = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Check if there's any AI response (GEMINI message) after the last user message
|
|
if (lastUserMessageIndex !== -1) {
|
|
for (let i = lastUserMessageIndex + 1; i < history.length; i++) {
|
|
if (history[i].type === MessageType.GEMINI) {
|
|
hasAIResponseAfterLastUser = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
const sessionDurationMs =
|
|
Date.now() - sessionStats.sessionStartTime.getTime();
|
|
|
|
// Show feedback dialog if:
|
|
// 1. Telemetry is enabled (required for feedback submission)
|
|
// 2. User feedback is enabled in settings
|
|
// 3. There's an AI response after the last user message (real AI conversation)
|
|
// 4. Session duration > 10 seconds (meaningful interaction)
|
|
// 5. Not already shown for this session
|
|
// 6. Random chance (25% probability)
|
|
// Note: We check !isFeedbackDialogOpen to ensure it's not already open
|
|
if (
|
|
config.getUsageStatisticsEnabled() && // Only show if telemetry is enabled
|
|
settings.merged.ui?.enableUserFeedback !== false && // Default to true if not set
|
|
hasAIResponseAfterLastUser &&
|
|
sessionDurationMs > 10000 && // 10 seconds minimum for meaningful interaction
|
|
!feedbackShownForSession &&
|
|
Math.random() < 0.25 // 25% probability
|
|
) {
|
|
timeoutId = setTimeout(() => {
|
|
openFeedbackDialog();
|
|
}, 1000); // Delay to ensure user has time to see the completion
|
|
}
|
|
}
|
|
|
|
return () => {
|
|
if (timeoutId) {
|
|
clearTimeout(timeoutId);
|
|
}
|
|
};
|
|
}, [
|
|
streamingState,
|
|
history,
|
|
sessionStats,
|
|
isFeedbackDialogOpen,
|
|
feedbackShownForSession,
|
|
openFeedbackDialog,
|
|
settings.merged.ui?.enableUserFeedback,
|
|
config,
|
|
]);
|
|
|
|
return {
|
|
isFeedbackDialogOpen,
|
|
openFeedbackDialog,
|
|
closeFeedbackDialog,
|
|
submitFeedback,
|
|
};
|
|
};
|