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:
Will Miao
2026-07-05 18:00:58 +08:00
parent 7b19bbb14e
commit 51c0135250
12 changed files with 113 additions and 126 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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")

View File

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