diff --git a/static/js/components/ContextMenu/CheckpointContextMenu.js b/static/js/components/ContextMenu/CheckpointContextMenu.js index 0d06f618..0c11991f 100644 --- a/static/js/components/ContextMenu/CheckpointContextMenu.js +++ b/static/js/components/ContextMenu/CheckpointContextMenu.js @@ -25,6 +25,7 @@ export class CheckpointContextMenu extends BaseContextMenu { showMenu(x, y, card) { super.showMenu(x, y, card); this.updateExcludeMenuItem(); + this.updateEnrichMenuItem(card); // Update the "Move to other root" label based on current model type const moveOtherItem = this.menu.querySelector('[data-action="move-other"]'); diff --git a/static/js/components/ContextMenu/LoraContextMenu.js b/static/js/components/ContextMenu/LoraContextMenu.js index b9806cee..6a4bb9c5 100644 --- a/static/js/components/ContextMenu/LoraContextMenu.js +++ b/static/js/components/ContextMenu/LoraContextMenu.js @@ -1,8 +1,7 @@ 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 { copyLoraSyntax, sendLoraToWorkflow, buildLoraSyntax } from '../../utils/uiHelpers.js'; import { showExcludeModal, showDeleteModal } from '../../utils/modalUtils.js'; import { moveManager } from '../../managers/MoveManager.js'; @@ -27,16 +26,6 @@ export class LoraContextMenu extends BaseContextMenu { this.updateEnrichMenuItem(card); } - updateEnrichMenuItem(card) { - const enrichItem = this.menu?.querySelector('[data-action="enrich-hf-llm"]'); - if (!enrichItem) return; - const hasHfUrl = !!card.dataset.hf_url; - enrichItem.classList.toggle('disabled', !hasHfUrl); - enrichItem.title = hasHfUrl - ? '' - : 'Link this model to a HuggingFace repo first (Link Model \u2192 Link to HuggingFace)'; - } - handleMenuAction(action, menuItem) { // First try to handle with common actions if (ModelContextMenuMixin.handleCommonMenuActions.call(this, action)) { @@ -75,9 +64,6 @@ export class LoraContextMenu extends BaseContextMenu { case 'refresh-metadata': getModelApiClient().refreshSingleModelMetadata(this.currentCard.dataset.filepath); break; - case 'enrich-hf-llm': - this.enrichWithAgent(this.currentCard.dataset.filepath); - break; case 'exclude': showExcludeModal(this.currentCard.dataset.filepath); break; @@ -87,68 +73,6 @@ export class LoraContextMenu extends BaseContextMenu { } } - async enrichWithAgent(filePath) { - const { agentManager } = await import('../../managers/AgentManager.js'); - - const configured = await agentManager.isLlmConfigured(); - if (!configured) { - showToast('toast.agent.llmNotConfigured', {}, 'warning'); - return; - } - - agentManager.connect(); - - const progressUI = state.loadingManager.showEnhancedProgress( - 'Enriching metadata with AI...' - ); - - function cleanupCallbacks() { - 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); - const eIdx = agentManager.errorCallbacks.indexOf(onError); - if (eIdx >= 0) agentManager.errorCallbacks.splice(eIdx, 1); - } - - 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) => { - cleanupCallbacks(); - - if (data.status === 'completed') { - progressUI.complete(data.summary || 'Enrich complete'); - showToast('toast.agent.enrichComplete', { summary: data.summary || 'Done' }, 'success'); - } - }; - agentManager.onComplete(onComplete); - - const onError = (data) => { - cleanupCallbacks(); - state.loadingManager.hide(); - showToast('toast.agent.enrichFailed', { error: data.error || 'Unknown error' }, 'error'); - }; - agentManager.onError(onError); - - try { - await agentManager.executeSkill('enrich_hf_metadata', [filePath]); - } catch (error) { - cleanupCallbacks(); - state.loadingManager.hide(); - showToast('toast.agent.enrichFailed', { error: error.message }, 'error'); - } - } - sendLoraToWorkflow(replaceMode) { const card = this.currentCard; const usageTips = JSON.parse(card.dataset.usage_tips || '{}'); diff --git a/static/js/components/ContextMenu/ModelContextMenuMixin.js b/static/js/components/ContextMenu/ModelContextMenuMixin.js index abd33ed9..42a116c7 100644 --- a/static/js/components/ContextMenu/ModelContextMenuMixin.js +++ b/static/js/components/ContextMenu/ModelContextMenuMixin.js @@ -278,6 +278,79 @@ export const ModelContextMenuMixin = { setTimeout(() => urlInput.focus(), 50); }, + // HF metadata enrichment (AI agent) methods + updateEnrichMenuItem(card) { + const enrichItem = this.menu?.querySelector('[data-action="enrich-hf-llm"]'); + if (!enrichItem) return; + const hasHfUrl = !!card.dataset.hf_url; + enrichItem.classList.toggle('disabled', !hasHfUrl); + enrichItem.title = hasHfUrl + ? '' + : 'Link this model to a HuggingFace repo first (Link Model → Link to HuggingFace)'; + }, + + async enrichWithAgent(filePath) { + const { agentManager } = await import('../../managers/AgentManager.js'); + + const configured = await agentManager.isLlmConfigured(); + if (!configured) { + showToast('toast.agent.llmNotConfigured', {}, 'warning'); + return; + } + + agentManager.connect(); + + const progressUI = state.loadingManager.showEnhancedProgress( + 'Enriching metadata with AI...' + ); + + function cleanupCallbacks() { + 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); + const eIdx = agentManager.errorCallbacks.indexOf(onError); + if (eIdx >= 0) agentManager.errorCallbacks.splice(eIdx, 1); + } + + 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) => { + cleanupCallbacks(); + + if (data.status === 'completed') { + progressUI.complete(data.summary || 'Enrich complete'); + showToast('toast.agent.enrichComplete', { summary: data.summary || 'Done' }, 'success'); + } + }; + agentManager.onComplete(onComplete); + + const onError = (data) => { + cleanupCallbacks(); + state.loadingManager.hide(); + showToast('toast.agent.enrichFailed', { error: data.error || 'Unknown error' }, 'error'); + }; + agentManager.onError(onError); + + try { + await agentManager.executeSkill('enrich_hf_metadata', [filePath]); + } catch (error) { + cleanupCallbacks(); + state.loadingManager.hide(); + showToast('toast.agent.enrichFailed', { error: error.message }, 'error'); + } + }, + parseModelId(value) { if (value === undefined || value === null || value === '') { return null; @@ -388,6 +461,9 @@ export const ModelContextMenuMixin = { case 'link-hf': this.showLinkHfModal(); return true; + case 'enrich-hf-llm': + this.enrichWithAgent(this.currentCard.dataset.filepath); + return true; case 'set-nsfw': this.showNSFWLevelSelector(null, null, this.currentCard); return true; diff --git a/templates/checkpoints.html b/templates/checkpoints.html index 9f1d265f..1518f9a4 100644 --- a/templates/checkpoints.html +++ b/templates/checkpoints.html @@ -25,6 +25,9 @@ +