From f7632a47f92b27347a9f328122e2118ffb2c106c Mon Sep 17 00:00:00 2001 From: Will Miao Date: Sat, 4 Jul 2026 16:50:56 +0800 Subject: [PATCH] feat(agent): enrich_hf_metadata with per-model progress and in-place card update - PostProcessor returns updates dict from enrich_hf_metadata - AgentService includes updated_data per model in WebSocket progress events - Convert preview_url to HTTP URL via config.get_preview_static_url() - LoraContextMenu: showEnhancedProgress + updateSingleItem per model - BulkContextMenu: same pattern, remove window.location.reload() - Guard empty updated_data and clean up callbacks on HTTP error --- py/services/agent/agent_service.py | 8 ++++ py/services/agent/post_processor.py | 1 + .../components/ContextMenu/BulkContextMenu.js | 40 +++++++++++++------ .../components/ContextMenu/LoraContextMenu.js | 40 +++++++++++++------ 4 files changed, 64 insertions(+), 25 deletions(-) diff --git a/py/services/agent/agent_service.py b/py/services/agent/agent_service.py index e834cb44..54473b93 100644 --- a/py/services/agent/agent_service.py +++ b/py/services/agent/agent_service.py @@ -26,6 +26,7 @@ import aiohttp import os +from ...config import config from ..llm_service import LLMService from ..websocket_manager import ws_manager from .post_processor import PostProcessor @@ -259,6 +260,7 @@ class AgentService: "execute_skill '%s': processing model %d/%d: %s", skill_name, processed + 1, total, model_path, ) + updated_data: Dict[str, Any] = {} try: from ...agent_cli import read_metadata metadata = await read_metadata(model_path) @@ -298,6 +300,11 @@ class AgentService: uf = model_result.get("updated_fields", []) if uf: updated_models.append({"path": model_path, "updated_fields": uf}) + updated_data = model_result.get("updates", {}) + if "preview_url" in updated_data and updated_data["preview_url"]: + updated_data["preview_url"] = config.get_preview_static_url( + updated_data["preview_url"] + ) else: errors.extend( model_result.get("errors", [model_result.get("error", "Unknown error")]) @@ -312,6 +319,7 @@ class AgentService: progress_callback, skill_name, status="processing", total=total, processed=processed, success=success_count, current_path=model_path, + updated_data=updated_data, ) result = SkillResult( diff --git a/py/services/agent/post_processor.py b/py/services/agent/post_processor.py index b8ccb966..06d239b6 100644 --- a/py/services/agent/post_processor.py +++ b/py/services/agent/post_processor.py @@ -253,6 +253,7 @@ class PostProcessor: "success": True, "updated_fields": updated_fields, "preview_downloaded": preview_downloaded, + "updates": updates, "errors": [], } diff --git a/static/js/components/ContextMenu/BulkContextMenu.js b/static/js/components/ContextMenu/BulkContextMenu.js index 71833b88..4b7b8652 100644 --- a/static/js/components/ContextMenu/BulkContextMenu.js +++ b/static/js/components/ContextMenu/BulkContextMenu.js @@ -377,7 +377,6 @@ export class BulkContextMenu extends BaseContextMenu { const { agentManager } = await import('../../managers/AgentManager.js'); - // Check if LLM is configured const configured = await agentManager.isLlmConfigured(); if (!configured) { showToast('toast.agent.llmNotConfigured', {}, 'warning'); @@ -386,23 +385,39 @@ export class BulkContextMenu extends BaseContextMenu { const modelPaths = [...state.selectedModels]; - // Connect WebSocket for progress agentManager.connect(); - // Set up one-time completion handler + const progressUI = state.loadingManager.showEnhancedProgress( + `Enriching metadata for ${modelPaths.length} models...` + ); + + const onProgress = (data) => { + if (data.status === 'processing' && data.current_path && data.updated_data && Object.keys(data.updated_data).length > 0) { + if (state.virtualScroller?.updateSingleItem) { + state.virtualScroller.updateSingleItem(data.current_path, data.updated_data); + } + const pct = data.total > 0 ? Math.floor((data.processed / data.total) * 100) : 0; + const name = data.current_path.split('/').pop(); + progressUI.updateProgress(pct, name, `Processing ${data.processed}/${data.total}: ${name}`); + } + }; + agentManager.onProgress(onProgress); + const onComplete = (data) => { - const idx = agentManager.completeCallbacks.indexOf(onComplete); - if (idx >= 0) agentManager.completeCallbacks.splice(idx, 1); + const pIdx = agentManager.progressCallbacks.indexOf(onProgress); + if (pIdx >= 0) agentManager.progressCallbacks.splice(pIdx, 1); + const cIdx = agentManager.completeCallbacks.indexOf(onComplete); + if (cIdx >= 0) agentManager.completeCallbacks.splice(cIdx, 1); if (data.status === 'completed') { + progressUI.complete(data.summary || 'Enrich complete'); showToast( 'toast.agent.enrichComplete', { summary: data.summary || 'Done' }, 'success' ); - // Soft reload to reflect updated metadata - window.location.reload(); } else if (data.status === 'error') { + state.loadingManager.hide(); showToast( 'toast.agent.enrichFailed', { error: data.error || 'Unknown error' }, @@ -412,15 +427,14 @@ export class BulkContextMenu extends BaseContextMenu { }; agentManager.onComplete(onComplete); - showToast( - 'toast.agent.enrichStarted', - { count: modelPaths.length }, - 'info' - ); - try { await agentManager.executeSkill('enrich_hf_metadata', modelPaths); } catch (error) { + const pIdx = agentManager.progressCallbacks.indexOf(onProgress); + if (pIdx >= 0) agentManager.progressCallbacks.splice(pIdx, 1); + const cIdx = agentManager.completeCallbacks.indexOf(onComplete); + if (cIdx >= 0) agentManager.completeCallbacks.splice(cIdx, 1); + state.loadingManager.hide(); showToast( 'toast.agent.enrichFailed', { error: error.message }, diff --git a/static/js/components/ContextMenu/LoraContextMenu.js b/static/js/components/ContextMenu/LoraContextMenu.js index ebdfb3a9..9fcf8df4 100644 --- a/static/js/components/ContextMenu/LoraContextMenu.js +++ b/static/js/components/ContextMenu/LoraContextMenu.js @@ -1,5 +1,6 @@ import { BaseContextMenu } from './BaseContextMenu.js'; import { ModelContextMenuMixin } from './ModelContextMenuMixin.js'; +import { state } from '../../state/index.js'; import { getModelApiClient, resetAndReload } from '../../api/modelApiFactory.js'; import { copyLoraSyntax, sendLoraToWorkflow, buildLoraSyntax, showToast } from '../../utils/uiHelpers.js'; import { showExcludeModal, showDeleteModal } from '../../utils/modalUtils.js'; @@ -78,39 +79,54 @@ export class LoraContextMenu extends BaseContextMenu { async enrichWithAgent(filePath) { const { agentManager } = await import('../../managers/AgentManager.js'); - // Check if LLM is configured const configured = await agentManager.isLlmConfigured(); if (!configured) { showToast('toast.agent.llmNotConfigured', {}, 'warning'); return; } - // Connect WebSocket for progress agentManager.connect(); - // Set up one-time completion handler + const progressUI = state.loadingManager.showEnhancedProgress( + 'Enriching metadata with AI...' + ); + + const onProgress = (data) => { + if (data.status === 'processing' && data.current_path && data.updated_data && Object.keys(data.updated_data).length > 0) { + if (state.virtualScroller?.updateSingleItem) { + state.virtualScroller.updateSingleItem(data.current_path, data.updated_data); + } + const pct = data.total > 0 ? Math.floor((data.processed / data.total) * 100) : 0; + const name = data.current_path.split('/').pop(); + progressUI.updateProgress(pct, name, `Processing ${name}`); + } + }; + agentManager.onProgress(onProgress); + const onComplete = (data) => { - const idx = agentManager.completeCallbacks.indexOf(onComplete); - if (idx >= 0) agentManager.completeCallbacks.splice(idx, 1); + const pIdx = agentManager.progressCallbacks.indexOf(onProgress); + if (pIdx >= 0) agentManager.progressCallbacks.splice(pIdx, 1); + const cIdx = agentManager.completeCallbacks.indexOf(onComplete); + if (cIdx >= 0) agentManager.completeCallbacks.splice(cIdx, 1); if (data.status === 'completed') { + progressUI.complete(data.summary || 'Enrich complete'); showToast('toast.agent.enrichComplete', { summary: data.summary || 'Done' }, 'success'); - // Soft reload to reflect updated metadata - if (typeof resetAndReload === 'function') { - resetAndReload(); - } } else if (data.status === 'error') { + state.loadingManager.hide(); showToast('toast.agent.enrichFailed', { error: data.error || 'Unknown error' }, 'error'); } }; agentManager.onComplete(onComplete); - // Show progress toast - showToast('toast.agent.enrichStarted', {}, 'info'); - try { await agentManager.executeSkill('enrich_hf_metadata', [filePath]); } catch (error) { + const pIdx = agentManager.progressCallbacks.indexOf(onProgress); + if (pIdx >= 0) agentManager.progressCallbacks.splice(pIdx, 1); + const cIdx = agentManager.completeCallbacks.indexOf(onComplete); + if (cIdx >= 0) agentManager.completeCallbacks.splice(cIdx, 1); + state.loadingManager.hide(); showToast('toast.agent.enrichFailed', { error: error.message }, 'error'); } }