refactor(metadata): remove vestigial top-level trainedWords field

The field dates back to a development-stage bug in the enrich-metadata
(agent) pipeline, which briefly wrote trigger words at the top level of
model metadata instead of the established civitai.trainedWords location.
The write path was fixed before the feature merged to main (PR #1013)
and never shipped in any release, so no writer has existed since.

Remove the leftover pieces:

- BaseModelMetadata.trainedWords field (py/utils/models.py); sidecars
  from that dev window now pass the key through _unknown_fields instead
- HF download handler's strip-empty-trainedWords special case, reverting
  to saving the metadata object directly (py/routes/handlers/hf_handlers.py)
- trainedWords in the LLM enrichment context (agent_service.py)
- matching fallbacks/fixtures in the enrich_hf_validation harness and
  post-processor test

Trigger words continue to live in civitai.trainedWords for all model
sources, which is what the UI, agent post-processor, and metadata sync
all read and write.
This commit is contained in:
Will Miao
2026-09-07 16:24:15 +08:00
parent a7995db009
commit 82b34097fb
6 changed files with 3 additions and 15 deletions
+1 -5
View File
@@ -122,12 +122,8 @@ async def _save_hf_metadata(dest_path: str, repo: str, model_root: str) -> None:
metadata._unknown_fields["hf_url"] = hf_url
metadata.from_civitai = False # HF models are not from CivitAI
metadata_dict = metadata.to_dict()
if "trainedWords" in metadata_dict and not metadata_dict["trainedWords"]:
del metadata_dict["trainedWords"]
# 3. Save metadata atomically
await MetadataManager.save_metadata(dest_path, metadata_dict)
await MetadataManager.save_metadata(dest_path, metadata)
logger.info("Saved HF metadata (with hf_url) for %s", dest_path)
# 4. Determine relative folder path for cache
-1
View File
@@ -407,7 +407,6 @@ class AgentService:
"base_model": metadata.get("base_model", ""),
"tags": metadata.get("tags", []),
"modelDescription": metadata.get("modelDescription", ""),
"trainedWords": metadata.get("trainedWords", []),
"sha256": (metadata.get("sha256") or "")[:16] + "..." if metadata.get("sha256") else "",
"size": metadata.get("size", 0),
}
-6
View File
@@ -77,9 +77,6 @@ class BaseModelMetadata:
last_checked_at: float = 0 # Last checked timestamp
hash_status: str = "completed" # Hash calculation status: pending | calculating | completed | failed
autov3: Optional[str] = None # CivitAI AutoV3 hash (12-char lowercase hex); "" = checked but unavailable, None = not checked
trainedWords: List[str] = field(
default_factory=list
) # Trigger words / activation prompts (source-agnostic)
_unknown_fields: Dict[str, Any] = field(
default_factory=dict, repr=False, compare=False
) # Store unknown fields
@@ -92,9 +89,6 @@ class BaseModelMetadata:
if self.tags is None:
self.tags = []
if self.trainedWords is None:
self.trainedWords = []
@classmethod
def from_dict(cls, data: Dict[str, Any]) -> "BaseModelMetadata":
"""Create instance from dictionary"""
@@ -100,7 +100,7 @@ def evaluate_model(
flagged issues.
"""
civitai = metadata.get("civitai") or {}
trained_words: List[str] = civitai.get("trainedWords") or metadata.get("trainedWords") or []
trained_words: List[str] = civitai.get("trainedWords") or []
short_desc: str = civitai.get("description") or ""
tags: List[str] = metadata.get("tags") or []
notes: str = metadata.get("notes") or ""
@@ -149,7 +149,6 @@ def create_initial_metadata(
"metadata_source": "",
"last_checked_at": 0,
"hash_status": "completed",
"trainedWords": [],
"hf_url": hf_url,
"usage_tips": "{}",
}
+1 -1
View File
@@ -164,7 +164,7 @@ class TestEnrichHfMetadata:
skill_name="enrich_hf_metadata",
model_path="/p.safetensors",
llm_output=llm,
metadata={"trainedWords": []},
metadata={},
)
applied = mock_apply.call_args[0][1]
assert applied["civitai"]["trainedWords"] == ["trigger1", "trigger2"]