mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-07-03 07:51:16 -03:00
Introduce an agent skill framework for LLM-driven metadata enrichment: - AgentCLI (py/agent_cli/): in-process wrappers around internal services using standard relative imports, eliminating the need for sys.path hacks - LLMService: centralized BYOK (bring-your-own-key) LLM client supporting OpenAI, Ollama, and custom OpenAI-compatible endpoints - PostProcessor: deterministic engine that applies LLM output via AgentCLI (replaces old handler.py + _BASE_MODEL_ALIASES approach) - SkillRegistry: filesystem-based skill discovery (skill.yaml + prompt.md) - AgentService: orchestrates skill execution with WebSocket progress - Frontend AgentManager: WebSocket listeners, skill execution, config UI - Context menu entries (single + bulk) for "Enrich Metadata (Agent)" - Settings UI for AI Provider configuration (BYOK) - Full i18n support across 9 locales Bug fixes found during review: - aiohttp.web.json_response: status_code= -> status= - settings_modal cancelEditApiKey: wrong argument position - AgentManager.isLlmConfigured: allow Ollama without API key - PostProcessor._merge_tags: lowercase all tags to match TagUpdateService
197 lines
5.7 KiB
JavaScript
197 lines
5.7 KiB
JavaScript
/**
|
|
* AgentManager — WebSocket listener for agent skill progress events.
|
|
*
|
|
* Connects to the generic WebSocket endpoint and filters for
|
|
* `type: "agent_progress"` messages. Dispatches progress and completion
|
|
* events to registered callbacks.
|
|
*/
|
|
class AgentManager {
|
|
constructor() {
|
|
this.websocket = null;
|
|
this.progressCallbacks = [];
|
|
this.completeCallbacks = [];
|
|
this.errorCallbacks = [];
|
|
this.connected = false;
|
|
}
|
|
|
|
/**
|
|
* Connect to the WebSocket endpoint for agent progress events.
|
|
* Safe to call multiple times — won't reconnect if already connected.
|
|
*/
|
|
connect() {
|
|
if (this.connected && this.websocket?.readyState === WebSocket.OPEN) {
|
|
return;
|
|
}
|
|
|
|
const wsProtocol = window.location.protocol === 'https:' ? 'wss://' : 'ws://';
|
|
try {
|
|
this.websocket = new WebSocket(
|
|
`${wsProtocol}${window.location.host}/ws/fetch-progress`
|
|
);
|
|
} catch (e) {
|
|
console.error('AgentManager: Failed to create WebSocket:', e);
|
|
return;
|
|
}
|
|
|
|
this.websocket.onopen = () => {
|
|
this.connected = true;
|
|
console.debug('AgentManager: WebSocket connected');
|
|
};
|
|
|
|
this.websocket.onmessage = (event) => {
|
|
try {
|
|
const data = JSON.parse(event.data);
|
|
if (data.type !== 'agent_progress') return;
|
|
this._dispatch(data);
|
|
} catch (e) {
|
|
// Not JSON or wrong format — ignore
|
|
}
|
|
};
|
|
|
|
this.websocket.onerror = (error) => {
|
|
console.error('AgentManager: WebSocket error:', error);
|
|
this.connected = false;
|
|
};
|
|
|
|
this.websocket.onclose = () => {
|
|
this.connected = false;
|
|
console.debug('AgentManager: WebSocket closed');
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Dispatch a parsed agent event to the appropriate callbacks.
|
|
* @param {Object} data - The parsed WebSocket message
|
|
*/
|
|
_dispatch(data) {
|
|
const { status, skill } = data;
|
|
|
|
if (status === 'error') {
|
|
this.errorCallbacks.forEach((cb) => {
|
|
try {
|
|
cb(data);
|
|
} catch (e) {
|
|
console.error('AgentManager error callback failed:', e);
|
|
}
|
|
});
|
|
return;
|
|
}
|
|
|
|
if (status === 'completed') {
|
|
this.completeCallbacks.forEach((cb) => {
|
|
try {
|
|
cb(data);
|
|
} catch (e) {
|
|
console.error('AgentManager complete callback failed:', e);
|
|
}
|
|
});
|
|
return;
|
|
}
|
|
|
|
// started, processing — general progress
|
|
this.progressCallbacks.forEach((cb) => {
|
|
try {
|
|
cb(data);
|
|
} catch (e) {
|
|
console.error('AgentManager progress callback failed:', e);
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Register a callback for progress events (started, processing).
|
|
* @param {Function} callback - Receives the event data
|
|
*/
|
|
onProgress(callback) {
|
|
this.progressCallbacks.push(callback);
|
|
}
|
|
|
|
/**
|
|
* Register a callback for completion events.
|
|
* @param {Function} callback - Receives the event data
|
|
*/
|
|
onComplete(callback) {
|
|
this.completeCallbacks.push(callback);
|
|
}
|
|
|
|
/**
|
|
* Register a callback for error events.
|
|
* @param {Function} callback - Receives the event data
|
|
*/
|
|
onError(callback) {
|
|
this.errorCallbacks.push(callback);
|
|
}
|
|
|
|
/**
|
|
* Clear all registered callbacks.
|
|
*/
|
|
clearCallbacks() {
|
|
this.progressCallbacks = [];
|
|
this.completeCallbacks = [];
|
|
this.errorCallbacks = [];
|
|
}
|
|
|
|
/**
|
|
* Execute an agent skill on the provided model paths.
|
|
*
|
|
* @param {string} skillName - The skill to execute
|
|
* @param {string[]} modelPaths - Model file paths to process
|
|
* @returns {Promise<Object>} The response JSON
|
|
*/
|
|
async executeSkill(skillName, modelPaths) {
|
|
const response = await fetch(
|
|
`/api/lm/agent/execute/${encodeURIComponent(skillName)}`,
|
|
{
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ model_paths: modelPaths }),
|
|
}
|
|
);
|
|
|
|
if (!response.ok) {
|
|
const errorData = await response.json().catch(() => ({}));
|
|
throw new Error(
|
|
errorData.error || `HTTP ${response.status}: ${response.statusText}`
|
|
);
|
|
}
|
|
|
|
return response.json();
|
|
}
|
|
|
|
/**
|
|
* Check if the LLM provider is configured.
|
|
*
|
|
* Returns true when both an API key and a model name are set.
|
|
*
|
|
* @returns {Promise<boolean>}
|
|
*/
|
|
async isLlmConfigured() {
|
|
try {
|
|
const response = await fetch('/api/lm/settings');
|
|
if (!response.ok) return false;
|
|
const data = await response.json();
|
|
const provider = data.settings?.llm_provider;
|
|
const hasModel = !!data.settings?.llm_model;
|
|
const hasKey = !!data.settings?.llm_api_key;
|
|
return hasModel && (hasKey || provider === 'ollama');
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get the list of available agent skills.
|
|
*
|
|
* @returns {Promise<Array>}
|
|
*/
|
|
async listSkills() {
|
|
const response = await fetch('/api/lm/agent/skills');
|
|
if (!response.ok) return [];
|
|
const data = await response.json();
|
|
return data.skills || [];
|
|
}
|
|
}
|
|
|
|
// Export as singleton
|
|
export const agentManager = new AgentManager();
|