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
This commit is contained in:
Will Miao
2026-07-04 16:50:56 +08:00
parent 646f1ddfb1
commit f7632a47f9
4 changed files with 64 additions and 25 deletions

View File

@@ -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(

View File

@@ -253,6 +253,7 @@ class PostProcessor:
"success": True,
"updated_fields": updated_fields,
"preview_downloaded": preview_downloaded,
"updates": updates,
"errors": [],
}

View File

@@ -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 },

View File

@@ -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');
}
}