mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-07-06 01:11:17 -03:00
refactor(agent): rename agent_cli to metadata_ops, strip temp debug logs
- Rename py/agent_cli/ -> py/metadata_ops/ (module was never agent-related) - Rename tests/agent_cli/ -> tests/metadata_ops/ - Remove 9 low-value/debug INFO log points across agent_handlers.py, agent_service.py, llm_service.py, and metadata_ops/__init__.py - Keep LLM raw response at DEBUG level for diagnostics - Consolidate per-model progress + LLM result into single concise log line with basename instead of full path - Update package/class/method docstrings to clarify this is a pipeline infrastructure, not a true agent loop
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
"""Agent CLI — thin in-process wrappers around LoRA Manager internal services.
|
"""Metadata operations — thin in-process wrappers around LoRA Manager internal services.
|
||||||
|
|
||||||
All functions are simple Python async functions that delegate to the
|
All functions are simple Python async functions that delegate to the
|
||||||
appropriate internal service. They use **relative imports** within the
|
appropriate internal service. They use **relative imports** within the
|
||||||
@@ -7,15 +7,15 @@ risk of double import or circular dependencies.
|
|||||||
|
|
||||||
Usage (in-process, primary)::
|
Usage (in-process, primary)::
|
||||||
|
|
||||||
from py.agent_cli import list_base_models, read_metadata
|
from py.metadata_ops import list_base_models, read_metadata
|
||||||
|
|
||||||
models = await list_base_models()
|
models = await list_base_models()
|
||||||
meta = await read_metadata("/path/to/model.safetensors")
|
meta = await read_metadata("/path/to/model.safetensors")
|
||||||
|
|
||||||
Usage (subprocess, debugging / external)::
|
Usage (subprocess, debugging / external)::
|
||||||
|
|
||||||
python -m py.agent_cli base-models list
|
python -m py.metadata_ops base-models list
|
||||||
python -m py.agent_cli metadata read /path/to/model.safetensors
|
python -m py.metadata_ops metadata read /path/to/model.safetensors
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
@@ -214,7 +214,6 @@ async def download_preview(
|
|||||||
)
|
)
|
||||||
with open(output_path, "wb") as f:
|
with open(output_path, "wb") as f:
|
||||||
f.write(optimized_data)
|
f.write(optimized_data)
|
||||||
logger.info("Preview downloaded and optimised for %s", model_path)
|
|
||||||
return output_path
|
return output_path
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logger.warning("Preview optimisation failed, saving raw: %s", exc)
|
logger.warning("Preview optimisation failed, saving raw: %s", exc)
|
||||||
@@ -224,7 +223,6 @@ async def download_preview(
|
|||||||
try:
|
try:
|
||||||
ok, _ = await downloader.download_file(url, output_path, use_auth=False)
|
ok, _ = await downloader.download_file(url, output_path, use_auth=False)
|
||||||
if ok:
|
if ok:
|
||||||
logger.info("Preview downloaded (fallback) for %s", model_path)
|
|
||||||
return output_path
|
return output_path
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logger.warning("Preview fallback download failed for %s: %s", model_path, exc)
|
logger.warning("Preview fallback download failed for %s: %s", model_path, exc)
|
||||||
@@ -1,17 +1,12 @@
|
|||||||
"""Subprocess entry point for AgentCLI (debugging / external use).
|
"""Subprocess entry point for ``metadata_ops`` (debugging / external use).
|
||||||
|
|
||||||
Usage::
|
Usage::
|
||||||
|
|
||||||
python -m py.agent_cli base-models list [--limit N]
|
python -m py.metadata_ops base-models list [--limit N]
|
||||||
python -m py.agent_cli metadata read <path>
|
python -m py.metadata_ops metadata read <path>
|
||||||
python -m py.agent_cli metadata update <path> --json '{...}'
|
python -m py.metadata_ops metadata update <path> --json '{...}'
|
||||||
python -m py.agent_cli preview download <path> --url <url>
|
python -m py.metadata_ops preview download <path> --url <url>
|
||||||
python -m py.agent_cli cache refresh <path>
|
python -m py.metadata_ops cache refresh <path>
|
||||||
|
|
||||||
NOTE: This is an **optional** convenience wrapper. The primary consumer of
|
|
||||||
AgentCLI is the :mod:`AgentService` (in-process). This entry point exists
|
|
||||||
for manual debugging and future integration with subprocess-based agent
|
|
||||||
frameworks.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
@@ -99,12 +99,11 @@ class AgentHandler:
|
|||||||
# Launch execution in the background
|
# Launch execution in the background
|
||||||
progress_reporter = AgentProgressReporter()
|
progress_reporter = AgentProgressReporter()
|
||||||
logger.info(
|
logger.info(
|
||||||
"LLM enrichment '%s' starting for %d model(s) in background task",
|
"LLM enrichment '%s' starting for %d model(s)",
|
||||||
skill_name, len(model_paths),
|
skill_name, len(model_paths),
|
||||||
)
|
)
|
||||||
|
|
||||||
async def _run() -> None:
|
async def _run() -> None:
|
||||||
logger.info("Background task started for enrichment '%s'", skill_name)
|
|
||||||
try:
|
try:
|
||||||
result = await service.execute_skill(
|
result = await service.execute_skill(
|
||||||
skill_name=skill_name,
|
skill_name=skill_name,
|
||||||
@@ -137,8 +136,7 @@ class AgentHandler:
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Fire and forget — progress comes via WebSocket
|
# Fire and forget — progress comes via WebSocket
|
||||||
task = asyncio.create_task(_run())
|
asyncio.create_task(_run())
|
||||||
logger.info("LLM enrichment '%s' background task created (id=%s)", skill_name, task)
|
|
||||||
|
|
||||||
return web.json_response(
|
return web.json_response(
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,8 +1,12 @@
|
|||||||
"""Agent-powered skill system for LoRA Manager.
|
"""LLM-powered metadata enrichment pipeline infrastructure.
|
||||||
|
|
||||||
This package provides the orchestration layer for LLM/agent-powered features.
|
This package provides the orchestration layer for LLM-powered features.
|
||||||
Skills define *what* to do (prompt template). The :class:`AgentService`
|
Skills define *what* to do (prompt template). The :class:`AgentService`
|
||||||
handles *how* (LLM calls, context gathering, validation, progress).
|
handles *how* (LLM calls, context gathering, validation, progress).
|
||||||
|
|
||||||
|
NOTE: The current implementation is a code-driven pipeline, not a true
|
||||||
|
agent loop. Future agent orchestration (LLM-driven tool selection) will
|
||||||
|
live alongside this package with its own namespace.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|||||||
@@ -1,16 +1,17 @@
|
|||||||
"""Agent orchestration service.
|
"""Pipeline orchestration service.
|
||||||
|
|
||||||
The :class:`AgentService` coordinates skill execution:
|
The :class:`AgentService` coordinates LLM-powered pipeline execution:
|
||||||
|
|
||||||
1. Look up the skill in :class:`SkillRegistry`
|
1. Look up the pipeline definition in :class:`SkillRegistry`
|
||||||
2. Validate input against the skill's ``input_schema``
|
2. Validate input against its ``input_schema``
|
||||||
3. Prepare context via :mod:`~py.agent_cli` (read metadata, list base models, fetch HF README)
|
3. Prepare context via :mod:`~py.metadata_ops` (read metadata, list base models, fetch HF README)
|
||||||
4. If ``llm_required``: call :class:`LLMService` with the rendered prompt
|
4. If ``llm_required``: call :class:`LLMService` with the rendered prompt
|
||||||
5. Post-process via :class:`PostProcessor` (delegates I/O to :mod:`~py.agent_cli`)
|
5. Post-process via :class:`PostProcessor` (delegates I/O to :mod:`~py.metadata_ops`)
|
||||||
6. Broadcast progress and completion via :class:`WebSocketManager`
|
6. Broadcast progress and completion via :class:`WebSocketManager`
|
||||||
|
|
||||||
Skills define *what* to do (prompt template). The AgentService handles *how*
|
Pipeline definitions (*skills*) describe *what* to do (prompt template).
|
||||||
(LLM calls, context gathering, validation, progress).
|
The AgentService handles *how* (LLM calls, context gathering, validation,
|
||||||
|
progress).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
@@ -202,11 +203,11 @@ class AgentService:
|
|||||||
input_data: Dict[str, Any],
|
input_data: Dict[str, Any],
|
||||||
progress_callback: Optional[AgentProgressReporter] = None,
|
progress_callback: Optional[AgentProgressReporter] = None,
|
||||||
) -> SkillResult:
|
) -> SkillResult:
|
||||||
"""Execute an agent skill.
|
"""Execute a pipeline (skill) on the given models.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
skill_name: Name of the skill to execute
|
skill_name: Name of the pipeline to execute
|
||||||
input_data: Input validated against the skill's ``input_schema``
|
input_data: Input validated against the pipeline's ``input_schema``
|
||||||
progress_callback: Optional WebSocket progress reporter
|
progress_callback: Optional WebSocket progress reporter
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
@@ -214,7 +215,6 @@ class AgentService:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
registry = await self._ensure_registry()
|
registry = await self._ensure_registry()
|
||||||
logger.info("execute_skill '%s': looking up skill", skill_name)
|
|
||||||
skill = registry.get_skill(skill_name)
|
skill = registry.get_skill(skill_name)
|
||||||
if skill is None:
|
if skill is None:
|
||||||
return SkillResult(
|
return SkillResult(
|
||||||
@@ -246,7 +246,6 @@ class AgentService:
|
|||||||
errors: List[str] = []
|
errors: List[str] = []
|
||||||
post_processor = PostProcessor()
|
post_processor = PostProcessor()
|
||||||
|
|
||||||
logger.info("execute_skill '%s': starting with %d model(s)", skill_name, total)
|
|
||||||
await self._emit_progress(
|
await self._emit_progress(
|
||||||
progress_callback, skill_name, status="started",
|
progress_callback, skill_name, status="started",
|
||||||
total=total, processed=0, success=0,
|
total=total, processed=0, success=0,
|
||||||
@@ -256,13 +255,14 @@ class AgentService:
|
|||||||
llm_configured = llm.is_configured() if skill.llm_required else True
|
llm_configured = llm.is_configured() if skill.llm_required else True
|
||||||
|
|
||||||
for model_path in model_paths:
|
for model_path in model_paths:
|
||||||
|
model_filename = os.path.basename(model_path)
|
||||||
logger.info(
|
logger.info(
|
||||||
"execute_skill '%s': processing model %d/%d: %s",
|
"[%s] [%d/%d] %s",
|
||||||
skill_name, processed + 1, total, model_path,
|
skill_name, processed + 1, total, model_filename,
|
||||||
)
|
)
|
||||||
updated_data: Dict[str, Any] = {}
|
updated_data: Dict[str, Any] = {}
|
||||||
try:
|
try:
|
||||||
from ...agent_cli import read_metadata
|
from ...metadata_ops import read_metadata
|
||||||
metadata = await read_metadata(model_path)
|
metadata = await read_metadata(model_path)
|
||||||
|
|
||||||
prompt_vars: Dict[str, Any] = {"model_path": model_path}
|
prompt_vars: Dict[str, Any] = {"model_path": model_path}
|
||||||
@@ -275,10 +275,6 @@ class AgentService:
|
|||||||
if skill.llm_required and llm_configured:
|
if skill.llm_required and llm_configured:
|
||||||
prompt_template = registry.load_prompt(skill_name)
|
prompt_template = registry.load_prompt(skill_name)
|
||||||
rendered = _render_prompt(prompt_template, prompt_vars)
|
rendered = _render_prompt(prompt_template, prompt_vars)
|
||||||
logger.info(
|
|
||||||
"execute_skill '%s': LLM call for %s (prompt=%d chars)",
|
|
||||||
skill_name, model_path, len(rendered),
|
|
||||||
)
|
|
||||||
llm_response = await llm.chat_completion_json(
|
llm_response = await llm.chat_completion_json(
|
||||||
system_prompt=prompt_vars.get(
|
system_prompt=prompt_vars.get(
|
||||||
"system_prompt",
|
"system_prompt",
|
||||||
@@ -286,6 +282,13 @@ class AgentService:
|
|||||||
),
|
),
|
||||||
user_prompt=rendered,
|
user_prompt=rendered,
|
||||||
)
|
)
|
||||||
|
if llm_response:
|
||||||
|
logger.info(
|
||||||
|
"[%s] [%d/%d] %s → base_model=%s confidence=%s",
|
||||||
|
skill_name, processed + 1, total, model_filename,
|
||||||
|
(llm_response.get("base_model") or "?")[:50],
|
||||||
|
llm_response.get("confidence", "?"),
|
||||||
|
)
|
||||||
|
|
||||||
model_result = await post_processor.process(
|
model_result = await post_processor.process(
|
||||||
skill_name=skill_name,
|
skill_name=skill_name,
|
||||||
@@ -329,7 +332,6 @@ class AgentService:
|
|||||||
summary=f"Processed {processed}/{total} models, {success_count} succeeded",
|
summary=f"Processed {processed}/{total} models, {success_count} succeeded",
|
||||||
)
|
)
|
||||||
|
|
||||||
logger.info("execute_skill '%s': done — %s", skill_name, result.summary)
|
|
||||||
await self._emit_progress(
|
await self._emit_progress(
|
||||||
progress_callback, skill_name, status="completed",
|
progress_callback, skill_name, status="completed",
|
||||||
total=total, processed=processed, success=success_count,
|
total=total, processed=processed, success=success_count,
|
||||||
@@ -366,7 +368,7 @@ class AgentService:
|
|||||||
base models, loads user priority tags, and returns a dict that maps to
|
base models, loads user priority tags, and returns a dict that maps to
|
||||||
``{{variable}}`` placeholders in ``prompt.md``.
|
``{{variable}}`` placeholders in ``prompt.md``.
|
||||||
"""
|
"""
|
||||||
from ...agent_cli import identify_model_type, list_base_models
|
from ...metadata_ops import identify_model_type, list_base_models
|
||||||
from ..settings_manager import SettingsManager
|
from ..settings_manager import SettingsManager
|
||||||
|
|
||||||
context: Dict[str, Any] = {
|
context: Dict[str, Any] = {
|
||||||
@@ -411,10 +413,6 @@ class AgentService:
|
|||||||
cleaned = clean_readme_for_llm(readme) if readme else ""
|
cleaned = clean_readme_for_llm(readme) if readme else ""
|
||||||
context["readme_content"] = cleaned if cleaned else "(README not available)"
|
context["readme_content"] = cleaned if cleaned else "(README not available)"
|
||||||
context["readme_content_full"] = readme or ""
|
context["readme_content_full"] = readme or ""
|
||||||
logger.info(
|
|
||||||
"Cleaned README for %s (%d chars): ---BEGIN---\n%s\n---END---",
|
|
||||||
repo, len(cleaned), cleaned[:800] if cleaned else "(empty)",
|
|
||||||
)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
raw_models = await list_base_models()
|
raw_models = await list_base_models()
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
"""Post-processing engine for skill pipeline outputs.
|
"""Post-processing engine for skill pipeline outputs.
|
||||||
|
|
||||||
The :class:`PostProcessor` takes the LLM's structured JSON output and applies
|
The :class:`PostProcessor` takes the LLM's structured JSON output and applies
|
||||||
it to a model's on-disk metadata via the :mod:`~py.agent_cli` functions.
|
it to a model's on-disk metadata via the :mod:`~py.metadata_ops` functions.
|
||||||
|
|
||||||
It handles all the skill-specific business logic — conditions, transformations,
|
It handles all the skill-specific business logic — conditions, transformations,
|
||||||
and orchestration of multiple side-effects (write metadata, download preview,
|
and orchestration of multiple side-effects (write metadata, download preview,
|
||||||
refresh cache). All actual I/O is delegated to :mod:`~py.agent_cli`.
|
refresh cache). All actual I/O is delegated to :mod:`~py.metadata_ops`.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
@@ -30,7 +30,7 @@ class PostProcessor:
|
|||||||
skill_name="enrich_hf_metadata",
|
skill_name="enrich_hf_metadata",
|
||||||
model_path="/path/to/model.safetensors",
|
model_path="/path/to/model.safetensors",
|
||||||
llm_output={...},
|
llm_output={...},
|
||||||
metadata={...}, # from agent_cli.read_metadata()
|
metadata={...}, # from metadata_ops.read_metadata()
|
||||||
)
|
)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -73,7 +73,7 @@ class PostProcessor:
|
|||||||
metadata: Dict[str, Any],
|
metadata: Dict[str, Any],
|
||||||
readme_content: str = "",
|
readme_content: str = "",
|
||||||
) -> Dict[str, Any]:
|
) -> Dict[str, Any]:
|
||||||
from ...agent_cli import (
|
from ...metadata_ops import (
|
||||||
apply_metadata_updates,
|
apply_metadata_updates,
|
||||||
download_preview,
|
download_preview,
|
||||||
refresh_cache,
|
refresh_cache,
|
||||||
|
|||||||
@@ -521,15 +521,9 @@ class LLMService:
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
parsed = json.loads(result["content"])
|
parsed = json.loads(result["content"])
|
||||||
logger.info(
|
logger.debug(
|
||||||
"LLM response base_model=%s tags=%s confidence=%s",
|
|
||||||
parsed.get("base_model", "?")[:50],
|
|
||||||
parsed.get("tags", []),
|
|
||||||
parsed.get("confidence", "?"),
|
|
||||||
)
|
|
||||||
logger.info(
|
|
||||||
"LLM raw content: %s",
|
"LLM raw content: %s",
|
||||||
(result.get("content") or "")[:1200],
|
json.dumps(parsed, ensure_ascii=False)[:2000],
|
||||||
)
|
)
|
||||||
return parsed
|
return parsed
|
||||||
except (json.JSONDecodeError, TypeError) as exc:
|
except (json.JSONDecodeError, TypeError) as exc:
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ _FALLBACK_BASE_MODELS: List[str] = [
|
|||||||
async def init_supported_base_models() -> None:
|
async def init_supported_base_models() -> None:
|
||||||
"""Populate ``SUPPORTED_BASE_MODELS`` from the production codebase.
|
"""Populate ``SUPPORTED_BASE_MODELS`` from the production codebase.
|
||||||
|
|
||||||
Calls ``py.agent_cli.list_base_models()`` which merges a hardcoded
|
Calls ``py.metadata_ops.list_base_models()`` which merges a hardcoded
|
||||||
fallback with models fetched from the CivitAI API. When the call
|
fallback with models fetched from the CivitAI API. When the call
|
||||||
fails (e.g. offline, API error), falls back to ``_FALLBACK_BASE_MODELS``.
|
fails (e.g. offline, API error), falls back to ``_FALLBACK_BASE_MODELS``.
|
||||||
|
|
||||||
@@ -80,7 +80,7 @@ async def init_supported_base_models() -> None:
|
|||||||
``run_validation.main()``, not at module level).
|
``run_validation.main()``, not at module level).
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
from py.agent_cli import list_base_models
|
from py.metadata_ops import list_base_models
|
||||||
|
|
||||||
models = await list_base_models()
|
models = await list_base_models()
|
||||||
if models:
|
if models:
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
"""Tests for the AgentCLI module (py/agent_cli/).
|
"""Tests for the metadata_ops module (py/metadata_ops/).
|
||||||
|
|
||||||
All tests mock the underlying services (scanner, MetadataManager, downloader)
|
All tests mock the underlying services (scanner, MetadataManager, downloader)
|
||||||
since the AgentCLI is a thin delegation layer.
|
since it is a thin delegation layer.
|
||||||
|
|
||||||
Mock targets must match where imports are resolved inside each function
|
Mock targets must match where imports are resolved inside each function
|
||||||
(lazy imports via ``from X import Y`` inside function body).
|
(lazy imports via ``from X import Y`` inside function body).
|
||||||
@@ -13,7 +13,7 @@ from unittest import mock
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from py.agent_cli import (
|
from py.metadata_ops import (
|
||||||
list_base_models,
|
list_base_models,
|
||||||
read_metadata,
|
read_metadata,
|
||||||
apply_metadata_updates,
|
apply_metadata_updates,
|
||||||
@@ -160,7 +160,7 @@ class TestApplyMetadataUpdates:
|
|||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_updates_field(self):
|
async def test_updates_field(self):
|
||||||
with (
|
with (
|
||||||
mock.patch("py.agent_cli.read_metadata") as mock_read,
|
mock.patch("py.metadata_ops.read_metadata") as mock_read,
|
||||||
mock.patch("py.utils.metadata_manager.MetadataManager") as mm,
|
mock.patch("py.utils.metadata_manager.MetadataManager") as mm,
|
||||||
):
|
):
|
||||||
mock_read.return_value = {"base_model": "", "tags": []}
|
mock_read.return_value = {"base_model": "", "tags": []}
|
||||||
@@ -176,7 +176,7 @@ class TestApplyMetadataUpdates:
|
|||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_noop_when_value_unchanged(self):
|
async def test_noop_when_value_unchanged(self):
|
||||||
with (
|
with (
|
||||||
mock.patch("py.agent_cli.read_metadata") as mock_read,
|
mock.patch("py.metadata_ops.read_metadata") as mock_read,
|
||||||
mock.patch("py.utils.metadata_manager.MetadataManager") as mm,
|
mock.patch("py.utils.metadata_manager.MetadataManager") as mm,
|
||||||
):
|
):
|
||||||
mock_read.return_value = {"base_model": "Flux.1 D"}
|
mock_read.return_value = {"base_model": "Flux.1 D"}
|
||||||
@@ -189,7 +189,7 @@ class TestApplyMetadataUpdates:
|
|||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_multiple_fields(self):
|
async def test_multiple_fields(self):
|
||||||
with (
|
with (
|
||||||
mock.patch("py.agent_cli.read_metadata") as mock_read,
|
mock.patch("py.metadata_ops.read_metadata") as mock_read,
|
||||||
mock.patch("py.utils.metadata_manager.MetadataManager") as mm,
|
mock.patch("py.utils.metadata_manager.MetadataManager") as mm,
|
||||||
):
|
):
|
||||||
mm.save_metadata = mock.AsyncMock(return_value=True)
|
mm.save_metadata = mock.AsyncMock(return_value=True)
|
||||||
@@ -207,7 +207,7 @@ class TestApplyMetadataUpdates:
|
|||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_empty_updates_noop(self):
|
async def test_empty_updates_noop(self):
|
||||||
with (
|
with (
|
||||||
mock.patch("py.agent_cli.read_metadata"),
|
mock.patch("py.metadata_ops.read_metadata"),
|
||||||
mock.patch("py.utils.metadata_manager.MetadataManager") as mm,
|
mock.patch("py.utils.metadata_manager.MetadataManager") as mm,
|
||||||
):
|
):
|
||||||
updated = await apply_metadata_updates("/p.safetensors", {})
|
updated = await apply_metadata_updates("/p.safetensors", {})
|
||||||
@@ -277,7 +277,7 @@ class TestRefreshCache:
|
|||||||
get_checkpoint_scanner=mock.AsyncMock(return_value=None),
|
get_checkpoint_scanner=mock.AsyncMock(return_value=None),
|
||||||
get_embedding_scanner=mock.AsyncMock(return_value=None),
|
get_embedding_scanner=mock.AsyncMock(return_value=None),
|
||||||
),
|
),
|
||||||
mock.patch("py.agent_cli.read_metadata") as mock_read,
|
mock.patch("py.metadata_ops.read_metadata") as mock_read,
|
||||||
):
|
):
|
||||||
mock_read.return_value = {"base_model": "SDXL 1.0"}
|
mock_read.return_value = {"base_model": "SDXL 1.0"}
|
||||||
result = await refresh_cache("/some/path.safetensors")
|
result = await refresh_cache("/some/path.safetensors")
|
||||||
@@ -306,7 +306,7 @@ class TestRefreshCache:
|
|||||||
get_checkpoint_scanner=mock.AsyncMock(return_value=None),
|
get_checkpoint_scanner=mock.AsyncMock(return_value=None),
|
||||||
get_embedding_scanner=mock.AsyncMock(return_value=None),
|
get_embedding_scanner=mock.AsyncMock(return_value=None),
|
||||||
),
|
),
|
||||||
mock.patch("py.agent_cli.read_metadata") as mock_read,
|
mock.patch("py.metadata_ops.read_metadata") as mock_read,
|
||||||
):
|
):
|
||||||
mock_read.return_value = {}
|
mock_read.return_value = {}
|
||||||
result = await refresh_cache("/some/path.safetensors")
|
result = await refresh_cache("/some/path.safetensors")
|
||||||
@@ -39,9 +39,9 @@ class TestProcessDispatch:
|
|||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_enrich_hf_metadata_routes_correctly(self, processor):
|
async def test_enrich_hf_metadata_routes_correctly(self, processor):
|
||||||
with (
|
with (
|
||||||
mock.patch("py.agent_cli.apply_metadata_updates") as mock_apply,
|
mock.patch("py.metadata_ops.apply_metadata_updates") as mock_apply,
|
||||||
mock.patch("py.agent_cli.download_preview") as mock_dl,
|
mock.patch("py.metadata_ops.download_preview") as mock_dl,
|
||||||
mock.patch("py.agent_cli.refresh_cache") as mock_ref,
|
mock.patch("py.metadata_ops.refresh_cache") as mock_ref,
|
||||||
):
|
):
|
||||||
mock_apply.return_value = ["metadata_source"]
|
mock_apply.return_value = ["metadata_source"]
|
||||||
mock_dl.return_value = None
|
mock_dl.return_value = None
|
||||||
@@ -82,9 +82,9 @@ class TestEnrichHfMetadata:
|
|||||||
"""Empty current base_model → new value is applied."""
|
"""Empty current base_model → new value is applied."""
|
||||||
llm = {**self.MIN_LLM_OUTPUT, "base_model": "Flux.1 D"}
|
llm = {**self.MIN_LLM_OUTPUT, "base_model": "Flux.1 D"}
|
||||||
with (
|
with (
|
||||||
mock.patch("py.agent_cli.apply_metadata_updates") as mock_apply,
|
mock.patch("py.metadata_ops.apply_metadata_updates") as mock_apply,
|
||||||
mock.patch("py.agent_cli.download_preview", return_value=False),
|
mock.patch("py.metadata_ops.download_preview", return_value=False),
|
||||||
mock.patch("py.agent_cli.refresh_cache"),
|
mock.patch("py.metadata_ops.refresh_cache"),
|
||||||
):
|
):
|
||||||
await processor.process(
|
await processor.process(
|
||||||
skill_name="enrich_hf_metadata",
|
skill_name="enrich_hf_metadata",
|
||||||
@@ -100,9 +100,9 @@ class TestEnrichHfMetadata:
|
|||||||
"""Existing base_model from CivitAI → not overwritten."""
|
"""Existing base_model from CivitAI → not overwritten."""
|
||||||
llm = {**self.MIN_LLM_OUTPUT, "base_model": "Flux.1 D"}
|
llm = {**self.MIN_LLM_OUTPUT, "base_model": "Flux.1 D"}
|
||||||
with (
|
with (
|
||||||
mock.patch("py.agent_cli.apply_metadata_updates") as mock_apply,
|
mock.patch("py.metadata_ops.apply_metadata_updates") as mock_apply,
|
||||||
mock.patch("py.agent_cli.download_preview", return_value=False),
|
mock.patch("py.metadata_ops.download_preview", return_value=False),
|
||||||
mock.patch("py.agent_cli.refresh_cache"),
|
mock.patch("py.metadata_ops.refresh_cache"),
|
||||||
):
|
):
|
||||||
await processor.process(
|
await processor.process(
|
||||||
skill_name="enrich_hf_metadata",
|
skill_name="enrich_hf_metadata",
|
||||||
@@ -119,9 +119,9 @@ class TestEnrichHfMetadata:
|
|||||||
"""Existing base_model from HF → overwritten (LLM is more reliable)."""
|
"""Existing base_model from HF → overwritten (LLM is more reliable)."""
|
||||||
llm = {**self.MIN_LLM_OUTPUT, "base_model": "Flux.1 D"}
|
llm = {**self.MIN_LLM_OUTPUT, "base_model": "Flux.1 D"}
|
||||||
with (
|
with (
|
||||||
mock.patch("py.agent_cli.apply_metadata_updates") as mock_apply,
|
mock.patch("py.metadata_ops.apply_metadata_updates") as mock_apply,
|
||||||
mock.patch("py.agent_cli.download_preview", return_value=False),
|
mock.patch("py.metadata_ops.download_preview", return_value=False),
|
||||||
mock.patch("py.agent_cli.refresh_cache"),
|
mock.patch("py.metadata_ops.refresh_cache"),
|
||||||
):
|
):
|
||||||
await processor.process(
|
await processor.process(
|
||||||
skill_name="enrich_hf_metadata",
|
skill_name="enrich_hf_metadata",
|
||||||
@@ -136,9 +136,9 @@ class TestEnrichHfMetadata:
|
|||||||
async def test_base_model_skipped_when_llm_empty(self, processor):
|
async def test_base_model_skipped_when_llm_empty(self, processor):
|
||||||
"""LLM returns empty base_model → nothing written."""
|
"""LLM returns empty base_model → nothing written."""
|
||||||
with (
|
with (
|
||||||
mock.patch("py.agent_cli.apply_metadata_updates") as mock_apply,
|
mock.patch("py.metadata_ops.apply_metadata_updates") as mock_apply,
|
||||||
mock.patch("py.agent_cli.download_preview", return_value=False),
|
mock.patch("py.metadata_ops.download_preview", return_value=False),
|
||||||
mock.patch("py.agent_cli.refresh_cache"),
|
mock.patch("py.metadata_ops.refresh_cache"),
|
||||||
):
|
):
|
||||||
await processor.process(
|
await processor.process(
|
||||||
skill_name="enrich_hf_metadata",
|
skill_name="enrich_hf_metadata",
|
||||||
@@ -156,9 +156,9 @@ class TestEnrichHfMetadata:
|
|||||||
"""New trigger words written when current list is empty."""
|
"""New trigger words written when current list is empty."""
|
||||||
llm = {**self.MIN_LLM_OUTPUT, "trigger_words": ["trigger1", "trigger2"]}
|
llm = {**self.MIN_LLM_OUTPUT, "trigger_words": ["trigger1", "trigger2"]}
|
||||||
with (
|
with (
|
||||||
mock.patch("py.agent_cli.apply_metadata_updates") as mock_apply,
|
mock.patch("py.metadata_ops.apply_metadata_updates") as mock_apply,
|
||||||
mock.patch("py.agent_cli.download_preview", return_value=None),
|
mock.patch("py.metadata_ops.download_preview", return_value=None),
|
||||||
mock.patch("py.agent_cli.refresh_cache"),
|
mock.patch("py.metadata_ops.refresh_cache"),
|
||||||
):
|
):
|
||||||
await processor.process(
|
await processor.process(
|
||||||
skill_name="enrich_hf_metadata",
|
skill_name="enrich_hf_metadata",
|
||||||
@@ -176,9 +176,9 @@ class TestEnrichHfMetadata:
|
|||||||
"""short_description written to civitai.description for HF models."""
|
"""short_description written to civitai.description for HF models."""
|
||||||
llm = {**self.MIN_LLM_OUTPUT, "short_description": "A short summary"}
|
llm = {**self.MIN_LLM_OUTPUT, "short_description": "A short summary"}
|
||||||
with (
|
with (
|
||||||
mock.patch("py.agent_cli.apply_metadata_updates") as mock_apply,
|
mock.patch("py.metadata_ops.apply_metadata_updates") as mock_apply,
|
||||||
mock.patch("py.agent_cli.download_preview", return_value=None),
|
mock.patch("py.metadata_ops.download_preview", return_value=None),
|
||||||
mock.patch("py.agent_cli.refresh_cache"),
|
mock.patch("py.metadata_ops.refresh_cache"),
|
||||||
):
|
):
|
||||||
await processor.process(
|
await processor.process(
|
||||||
skill_name="enrich_hf_metadata",
|
skill_name="enrich_hf_metadata",
|
||||||
@@ -194,9 +194,9 @@ class TestEnrichHfMetadata:
|
|||||||
"""short_description NOT written for CivitAI models (has own description)."""
|
"""short_description NOT written for CivitAI models (has own description)."""
|
||||||
llm = {**self.MIN_LLM_OUTPUT, "short_description": "A short summary"}
|
llm = {**self.MIN_LLM_OUTPUT, "short_description": "A short summary"}
|
||||||
with (
|
with (
|
||||||
mock.patch("py.agent_cli.apply_metadata_updates") as mock_apply,
|
mock.patch("py.metadata_ops.apply_metadata_updates") as mock_apply,
|
||||||
mock.patch("py.agent_cli.download_preview", return_value=None),
|
mock.patch("py.metadata_ops.download_preview", return_value=None),
|
||||||
mock.patch("py.agent_cli.refresh_cache"),
|
mock.patch("py.metadata_ops.refresh_cache"),
|
||||||
):
|
):
|
||||||
await processor.process(
|
await processor.process(
|
||||||
skill_name="enrich_hf_metadata",
|
skill_name="enrich_hf_metadata",
|
||||||
@@ -213,9 +213,9 @@ class TestEnrichHfMetadata:
|
|||||||
async def test_readme_content_converted_to_model_description(self, processor):
|
async def test_readme_content_converted_to_model_description(self, processor):
|
||||||
"""Raw README converted to HTML and stored as modelDescription."""
|
"""Raw README converted to HTML and stored as modelDescription."""
|
||||||
with (
|
with (
|
||||||
mock.patch("py.agent_cli.apply_metadata_updates") as mock_apply,
|
mock.patch("py.metadata_ops.apply_metadata_updates") as mock_apply,
|
||||||
mock.patch("py.agent_cli.download_preview", return_value=None),
|
mock.patch("py.metadata_ops.download_preview", return_value=None),
|
||||||
mock.patch("py.agent_cli.refresh_cache"),
|
mock.patch("py.metadata_ops.refresh_cache"),
|
||||||
):
|
):
|
||||||
await processor.process(
|
await processor.process(
|
||||||
skill_name="enrich_hf_metadata",
|
skill_name="enrich_hf_metadata",
|
||||||
@@ -232,9 +232,9 @@ class TestEnrichHfMetadata:
|
|||||||
async def test_readme_content_skipped_for_civitai_model(self, processor):
|
async def test_readme_content_skipped_for_civitai_model(self, processor):
|
||||||
"""README content NOT converted for CivitAI models."""
|
"""README content NOT converted for CivitAI models."""
|
||||||
with (
|
with (
|
||||||
mock.patch("py.agent_cli.apply_metadata_updates") as mock_apply,
|
mock.patch("py.metadata_ops.apply_metadata_updates") as mock_apply,
|
||||||
mock.patch("py.agent_cli.download_preview", return_value=None),
|
mock.patch("py.metadata_ops.download_preview", return_value=None),
|
||||||
mock.patch("py.agent_cli.refresh_cache"),
|
mock.patch("py.metadata_ops.refresh_cache"),
|
||||||
):
|
):
|
||||||
await processor.process(
|
await processor.process(
|
||||||
skill_name="enrich_hf_metadata",
|
skill_name="enrich_hf_metadata",
|
||||||
@@ -260,9 +260,9 @@ widget:
|
|||||||
Content
|
Content
|
||||||
"""
|
"""
|
||||||
with (
|
with (
|
||||||
mock.patch("py.agent_cli.apply_metadata_updates") as mock_apply,
|
mock.patch("py.metadata_ops.apply_metadata_updates") as mock_apply,
|
||||||
mock.patch("py.agent_cli.download_preview", return_value=None),
|
mock.patch("py.metadata_ops.download_preview", return_value=None),
|
||||||
mock.patch("py.agent_cli.refresh_cache"),
|
mock.patch("py.metadata_ops.refresh_cache"),
|
||||||
):
|
):
|
||||||
await processor.process(
|
await processor.process(
|
||||||
skill_name="enrich_hf_metadata",
|
skill_name="enrich_hf_metadata",
|
||||||
@@ -286,9 +286,9 @@ Content
|
|||||||
async def test_gallery_images_skipped_for_civitai_model(self, processor):
|
async def test_gallery_images_skipped_for_civitai_model(self, processor):
|
||||||
"""Gallery images NOT extracted for CivitAI models."""
|
"""Gallery images NOT extracted for CivitAI models."""
|
||||||
with (
|
with (
|
||||||
mock.patch("py.agent_cli.apply_metadata_updates") as mock_apply,
|
mock.patch("py.metadata_ops.apply_metadata_updates") as mock_apply,
|
||||||
mock.patch("py.agent_cli.download_preview", return_value=None),
|
mock.patch("py.metadata_ops.download_preview", return_value=None),
|
||||||
mock.patch("py.agent_cli.refresh_cache"),
|
mock.patch("py.metadata_ops.refresh_cache"),
|
||||||
):
|
):
|
||||||
await processor.process(
|
await processor.process(
|
||||||
skill_name="enrich_hf_metadata",
|
skill_name="enrich_hf_metadata",
|
||||||
@@ -310,9 +310,9 @@ Content
|
|||||||
async def test_tags_merged_and_deduplicated(self, processor):
|
async def test_tags_merged_and_deduplicated(self, processor):
|
||||||
llm = {**self.MIN_LLM_OUTPUT, "tags": ["flux", "lora", "STYLE"]}
|
llm = {**self.MIN_LLM_OUTPUT, "tags": ["flux", "lora", "STYLE"]}
|
||||||
with (
|
with (
|
||||||
mock.patch("py.agent_cli.apply_metadata_updates") as mock_apply,
|
mock.patch("py.metadata_ops.apply_metadata_updates") as mock_apply,
|
||||||
mock.patch("py.agent_cli.download_preview", return_value=False),
|
mock.patch("py.metadata_ops.download_preview", return_value=False),
|
||||||
mock.patch("py.agent_cli.refresh_cache"),
|
mock.patch("py.metadata_ops.refresh_cache"),
|
||||||
):
|
):
|
||||||
await processor.process(
|
await processor.process(
|
||||||
skill_name="enrich_hf_metadata",
|
skill_name="enrich_hf_metadata",
|
||||||
@@ -332,9 +332,9 @@ Content
|
|||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_audit_fields_always_set(self, processor):
|
async def test_audit_fields_always_set(self, processor):
|
||||||
with (
|
with (
|
||||||
mock.patch("py.agent_cli.apply_metadata_updates") as mock_apply,
|
mock.patch("py.metadata_ops.apply_metadata_updates") as mock_apply,
|
||||||
mock.patch("py.agent_cli.download_preview", return_value=False),
|
mock.patch("py.metadata_ops.download_preview", return_value=False),
|
||||||
mock.patch("py.agent_cli.refresh_cache"),
|
mock.patch("py.metadata_ops.refresh_cache"),
|
||||||
):
|
):
|
||||||
await processor.process(
|
await processor.process(
|
||||||
skill_name="enrich_hf_metadata",
|
skill_name="enrich_hf_metadata",
|
||||||
@@ -352,9 +352,9 @@ Content
|
|||||||
async def test_preview_downloaded_when_url_provided(self, processor):
|
async def test_preview_downloaded_when_url_provided(self, processor):
|
||||||
llm = {**self.MIN_LLM_OUTPUT, "preview_url": "https://ex.com/img.png"}
|
llm = {**self.MIN_LLM_OUTPUT, "preview_url": "https://ex.com/img.png"}
|
||||||
with (
|
with (
|
||||||
mock.patch("py.agent_cli.apply_metadata_updates") as mock_apply,
|
mock.patch("py.metadata_ops.apply_metadata_updates") as mock_apply,
|
||||||
mock.patch("py.agent_cli.download_preview") as mock_dl,
|
mock.patch("py.metadata_ops.download_preview") as mock_dl,
|
||||||
mock.patch("py.agent_cli.refresh_cache"),
|
mock.patch("py.metadata_ops.refresh_cache"),
|
||||||
):
|
):
|
||||||
mock_dl.return_value = "/p.webp"
|
mock_dl.return_value = "/p.webp"
|
||||||
result = await processor.process(
|
result = await processor.process(
|
||||||
@@ -373,9 +373,9 @@ Content
|
|||||||
"""If current_preview file exists on disk, skip download."""
|
"""If current_preview file exists on disk, skip download."""
|
||||||
llm = {**self.MIN_LLM_OUTPUT, "preview_url": "https://ex.com/img.png"}
|
llm = {**self.MIN_LLM_OUTPUT, "preview_url": "https://ex.com/img.png"}
|
||||||
with (
|
with (
|
||||||
mock.patch("py.agent_cli.apply_metadata_updates"),
|
mock.patch("py.metadata_ops.apply_metadata_updates"),
|
||||||
mock.patch("py.agent_cli.download_preview") as mock_dl,
|
mock.patch("py.metadata_ops.download_preview") as mock_dl,
|
||||||
mock.patch("py.agent_cli.refresh_cache"),
|
mock.patch("py.metadata_ops.refresh_cache"),
|
||||||
mock.patch("os.path.exists", return_value=True),
|
mock.patch("os.path.exists", return_value=True),
|
||||||
):
|
):
|
||||||
await processor.process(
|
await processor.process(
|
||||||
@@ -392,9 +392,9 @@ Content
|
|||||||
async def test_cache_refreshed_when_updates_applied(self, processor):
|
async def test_cache_refreshed_when_updates_applied(self, processor):
|
||||||
llm = {**self.MIN_LLM_OUTPUT, "base_model": "Flux.1 D"}
|
llm = {**self.MIN_LLM_OUTPUT, "base_model": "Flux.1 D"}
|
||||||
with (
|
with (
|
||||||
mock.patch("py.agent_cli.apply_metadata_updates", return_value=["base_model"]),
|
mock.patch("py.metadata_ops.apply_metadata_updates", return_value=["base_model"]),
|
||||||
mock.patch("py.agent_cli.download_preview", return_value=False),
|
mock.patch("py.metadata_ops.download_preview", return_value=False),
|
||||||
mock.patch("py.agent_cli.refresh_cache") as mock_ref,
|
mock.patch("py.metadata_ops.refresh_cache") as mock_ref,
|
||||||
):
|
):
|
||||||
await processor.process(
|
await processor.process(
|
||||||
skill_name="enrich_hf_metadata",
|
skill_name="enrich_hf_metadata",
|
||||||
@@ -407,9 +407,9 @@ Content
|
|||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_cache_not_refreshed_when_nothing_changed(self, processor):
|
async def test_cache_not_refreshed_when_nothing_changed(self, processor):
|
||||||
with (
|
with (
|
||||||
mock.patch("py.agent_cli.apply_metadata_updates", return_value=[]),
|
mock.patch("py.metadata_ops.apply_metadata_updates", return_value=[]),
|
||||||
mock.patch("py.agent_cli.download_preview", return_value=False),
|
mock.patch("py.metadata_ops.download_preview", return_value=False),
|
||||||
mock.patch("py.agent_cli.refresh_cache") as mock_ref,
|
mock.patch("py.metadata_ops.refresh_cache") as mock_ref,
|
||||||
):
|
):
|
||||||
await processor.process(
|
await processor.process(
|
||||||
skill_name="enrich_hf_metadata",
|
skill_name="enrich_hf_metadata",
|
||||||
|
|||||||
Reference in New Issue
Block a user