asd
This commit is contained in:
@@ -55,9 +55,23 @@ export default function ImageQueueModal({
|
||||
useEffect(() => {
|
||||
if (!isOpen || !taskId) return;
|
||||
|
||||
let pollAttempts = 0;
|
||||
const maxPollAttempts = 300; // 5 minutes max (300 * 1 second)
|
||||
|
||||
const pollInterval = setInterval(async () => {
|
||||
pollAttempts++;
|
||||
|
||||
// Stop polling after max attempts
|
||||
if (pollAttempts > maxPollAttempts) {
|
||||
console.warn('Polling timeout reached, stopping');
|
||||
clearInterval(pollInterval);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
console.log(`[ImageQueueModal] Polling task status (attempt ${pollAttempts}):`, taskId);
|
||||
const data = await fetchAPI(`/v1/system/settings/task_progress/${taskId}/`);
|
||||
console.log(`[ImageQueueModal] Task status response:`, data);
|
||||
|
||||
// Check if data is valid (not HTML error page)
|
||||
if (!data || typeof data !== 'object') {
|
||||
@@ -67,14 +81,18 @@ export default function ImageQueueModal({
|
||||
|
||||
// Check state (task_progress returns 'state', not 'status')
|
||||
const taskState = data.state || data.status;
|
||||
console.log(`[ImageQueueModal] Task state:`, taskState);
|
||||
|
||||
if (taskState === 'SUCCESS' || taskState === 'FAILURE') {
|
||||
console.log(`[ImageQueueModal] Task completed with state:`, taskState);
|
||||
clearInterval(pollInterval);
|
||||
// Update final state
|
||||
if (taskState === 'SUCCESS' && data.result) {
|
||||
console.log(`[ImageQueueModal] Updating queue from result:`, data.result);
|
||||
updateQueueFromTaskResult(data.result);
|
||||
} else if (taskState === 'SUCCESS' && data.meta && data.meta.result) {
|
||||
// Some responses have result in meta
|
||||
console.log(`[ImageQueueModal] Updating queue from meta result:`, data.meta.result);
|
||||
updateQueueFromTaskResult(data.meta.result);
|
||||
}
|
||||
return;
|
||||
@@ -82,7 +100,10 @@ export default function ImageQueueModal({
|
||||
|
||||
// Update progress from task meta
|
||||
if (data.meta) {
|
||||
console.log(`[ImageQueueModal] Updating queue from meta:`, data.meta);
|
||||
updateQueueFromTaskMeta(data.meta);
|
||||
} else {
|
||||
console.log(`[ImageQueueModal] No meta data in response`);
|
||||
}
|
||||
} catch (error: any) {
|
||||
// Check if it's a JSON parse error (HTML response) or API error
|
||||
|
||||
@@ -16,10 +16,38 @@ import { useToast } from '../../components/ui/toast/ToastContainer';
|
||||
import { FileIcon, DownloadIcon, BoltIcon } from '../../icons';
|
||||
import { createImagesPageConfig } from '../../config/pages/images.config';
|
||||
import ImageQueueModal, { ImageQueueItem } from '../../components/common/ImageQueueModal';
|
||||
import { useResourceDebug } from '../../hooks/useResourceDebug';
|
||||
|
||||
export default function Images() {
|
||||
const toast = useToast();
|
||||
|
||||
// Resource Debug toggle - controls AI Function Logs
|
||||
const resourceDebugEnabled = useResourceDebug();
|
||||
|
||||
// AI Function Logs state
|
||||
const [aiLogs, setAiLogs] = useState<Array<{
|
||||
timestamp: string;
|
||||
type: 'request' | 'success' | 'error' | 'step';
|
||||
action: string;
|
||||
data: any;
|
||||
stepName?: string;
|
||||
percentage?: number;
|
||||
}>>([]);
|
||||
|
||||
// Helper function to add log entry (only if Resource Debug is enabled)
|
||||
const addAiLog = useCallback((log: {
|
||||
timestamp: string;
|
||||
type: 'request' | 'success' | 'error' | 'step';
|
||||
action: string;
|
||||
data: any;
|
||||
stepName?: string;
|
||||
percentage?: number;
|
||||
}) => {
|
||||
if (resourceDebugEnabled) {
|
||||
setAiLogs(prev => [...prev, log]);
|
||||
}
|
||||
}, [resourceDebugEnabled]);
|
||||
|
||||
// Data state
|
||||
const [images, setImages] = useState<ContentImagesGroup[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
@@ -237,16 +265,35 @@ export default function Images() {
|
||||
console.log('[Generate Images] Max in-article images from settings:', maxInArticleImages);
|
||||
|
||||
// STAGE 2: Start actual generation
|
||||
addAiLog({
|
||||
timestamp: new Date().toISOString(),
|
||||
type: 'request',
|
||||
action: 'generate_images',
|
||||
data: { imageIds, contentId, totalImages: imageIds.length }
|
||||
});
|
||||
|
||||
const result = await generateImages(imageIds, contentId);
|
||||
|
||||
if (result.success && result.task_id) {
|
||||
// Task started successfully - polling will be handled by ImageQueueModal
|
||||
setTaskId(result.task_id);
|
||||
console.log('[Generate Images] Stage 2: Task started with ID:', result.task_id);
|
||||
addAiLog({
|
||||
timestamp: new Date().toISOString(),
|
||||
type: 'success',
|
||||
action: 'generate_images',
|
||||
data: { task_id: result.task_id, message: 'Task queued successfully' }
|
||||
});
|
||||
} else {
|
||||
toast.error(result.error || 'Failed to start image generation');
|
||||
setIsQueueModalOpen(false);
|
||||
setTaskId(null);
|
||||
addAiLog({
|
||||
timestamp: new Date().toISOString(),
|
||||
type: 'error',
|
||||
action: 'generate_images',
|
||||
data: { error: result.error || 'Failed to start image generation' }
|
||||
});
|
||||
}
|
||||
|
||||
} catch (error: any) {
|
||||
@@ -354,6 +401,74 @@ export default function Images() {
|
||||
taskId={taskId}
|
||||
onUpdateQueue={setImageQueue}
|
||||
/>
|
||||
|
||||
{/* AI Function Logs - Display below table (only when Resource Debug is enabled) */}
|
||||
{resourceDebugEnabled && aiLogs.length > 0 && (
|
||||
<div className="mt-6 bg-gray-50 dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700 p-4">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<h3 className="text-sm font-semibold text-gray-900 dark:text-gray-100">
|
||||
AI Function Logs
|
||||
</h3>
|
||||
<button
|
||||
onClick={() => setAiLogs([])}
|
||||
className="text-xs text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200"
|
||||
>
|
||||
Clear Logs
|
||||
</button>
|
||||
</div>
|
||||
<div className="space-y-2 max-h-96 overflow-y-auto">
|
||||
{aiLogs.slice().reverse().map((log, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className={`p-3 rounded border text-xs font-mono ${
|
||||
log.type === 'request'
|
||||
? 'bg-blue-50 dark:bg-blue-900/20 border-blue-200 dark:border-blue-800'
|
||||
: log.type === 'success'
|
||||
? 'bg-green-50 dark:bg-green-900/20 border-green-200 dark:border-green-800'
|
||||
: log.type === 'error'
|
||||
? 'bg-red-50 dark:bg-red-900/20 border-red-200 dark:border-red-800'
|
||||
: 'bg-purple-50 dark:bg-purple-900/20 border-purple-200 dark:border-purple-800'
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-center justify-between mb-1">
|
||||
<div className="flex items-center gap-2 flex-wrap">
|
||||
<span className={`font-semibold ${
|
||||
log.type === 'request'
|
||||
? 'text-blue-700 dark:text-blue-300'
|
||||
: log.type === 'success'
|
||||
? 'text-green-700 dark:text-green-300'
|
||||
: log.type === 'error'
|
||||
? 'text-red-700 dark:text-red-300'
|
||||
: 'text-purple-700 dark:text-purple-300'
|
||||
}`}>
|
||||
[{log.type.toUpperCase()}]
|
||||
</span>
|
||||
<span className="text-gray-700 dark:text-gray-300">
|
||||
{log.action}
|
||||
</span>
|
||||
{log.stepName && (
|
||||
<span className="text-xs px-2 py-0.5 rounded bg-gray-200 dark:bg-gray-700 text-gray-600 dark:text-gray-400">
|
||||
{log.stepName}
|
||||
</span>
|
||||
)}
|
||||
{log.percentage !== undefined && (
|
||||
<span className="text-xs text-gray-500 dark:text-gray-400">
|
||||
{log.percentage}%
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<span className="text-gray-500 dark:text-gray-400">
|
||||
{new Date(log.timestamp).toLocaleTimeString()}
|
||||
</span>
|
||||
</div>
|
||||
<pre className="text-xs text-gray-700 dark:text-gray-300 whitespace-pre-wrap break-words">
|
||||
{JSON.stringify(log.data, null, 2)}
|
||||
</pre>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user