Files
ComfyUI-Lora-Manager/py/services/agent/skills/enrich_hf_metadata/prompt.md
T
Will Miao 5ab0e88abc feat(links): support ModelScope and TensorArt as model sources
A model file could only ever be linked to huggingface.co: `set_hf_url`
validated the URL with a huggingface-only regex, the agent fetched the card
from a hardcoded HF URL, and the readme processor built every relative image
path off `https://huggingface.co/{repo}/resolve/main`. ModelScope publishes the
same model-card convention (README.md + YAML frontmatter, often carrying
`base_model:` and `trigger_words:`) behind a public, key-less API, so the
enrichment pipeline could already serve it - it was the plumbing that was
HF-shaped, not the idea.

Make the external source a first-class, provider-driven concept:

- New `py/services/model_sources/` registry. A `ModelSource` owns URL
  recognition (lenient for stored values, strict for user input), the
  canonical page URL, model-card fetching, the asset base URL and the
  capability flags. `HuggingFaceSource` is the previous logic relocated;
  `ModelScopeSource` reads `/models/{o}/{n}/resolve/{master|main}/README.md`
  and falls back to `/api/v1/models/{o}/{n}/repo`. `TensorArtSource` is
  link-only on purpose: tensor.art answers plain HTTP clients with a
  Cloudflare challenge and its internal API (ap-east-1.tensorart.cloud /
  cn.tensorart.net) rejects every /v1/model/* route with "invalid
  authorization header", so it declares supports_enrichment=False rather than
  failing silently later.
- Metadata gains `source_platform` + `source_url`; `hf_url` stays as a
  read/write alias, written only for Hugging Face, so existing sidecars,
  cached rows and third-party consumers keep working. Normalisation runs at
  the scanner, the persistent cache (both directions, plus two new columns
  behind an ALTER migration) and the linking handler - which is what stops a
  user who switches sources from leaving a stale `hf_url` on a ModelScope
  model.
- The agent pipeline keys off the provider instead of `hf_url`: the fast-fail
  gate now explains *why* a model is skipped (no source / unknown source /
  source without a reachable card), the prompt context exposes
  source_url/source_id/source_label/asset_base_url while still filling the
  legacy hf_url/repo aliases, and the four README image extractors take a
  base_url (defaulting to HF) so relative paths resolve against the right
  site. Version grouping generalises to hf: / ms: / ta: keys.
- `POST /api/lm/set-hf-url` keeps its path and its legacy payload keys but
  accepts `source_url`, validates against every provider and returns the
  platform. `GET /api/lm/model-sources` lets the UI render the supported-site
  list from the server.
- Frontend: a `modelSourceHelpers` mirror of the registry drives the link
  dialog, the card/modal globe (branded "View on ModelScope/TensorArt"), the
  version-group key and the enrichment gate; the versions tab no longer sends
  ms:/ta: keys to the CivitAI API.

TensorArt stays in the list because provenance is worth keeping even when the
card is unreadable - the dialog says so plainly ("Sites that don't expose one
(currently TensorArt) can only be linked") and the context menu disables
enrichment with a matching tooltip, instead of the user getting
"Unsupported URL".

Verified against the real ModelScope API: jj3550945163/Krea-2-LORA returns a
1882-byte card whose frontmatter carries base_model/tags/trigger_words, and
relative images resolve to .../resolve/master/....

Tests: backend 2815 passed; frontend 1130 JS + 91 Vue passed; pytest
tests/i18n and a Jinja compile pass over templates/. The nine locales carry
[TODO: Translate] for the new strings, completed in the next commit.
2026-09-14 07:24:08 +08:00

8.2 KiB

name, title, description, llm_required
name title description llm_required
enrich_hf_metadata Enrich Metadata from Model Card Parse the model card (README) from HuggingFace, ModelScope, or any other supported model site via LLM to extract description, trigger words, base model, tags, and preview image URL. true

You are an expert assistant for AI image generation models. Your task is to extract structured metadata from a model card (README).

Model Information

  • Source site: {{source_label}} ({{source_platform}})
  • Model page: {{source_url}}
  • Model file path: {{model_path}}
  • Model filename: {{model_basename}}
  • Repository ID: {{source_id}}
  • Repository raw-file base URL: {{asset_base_url}}

Current Metadata (may be incomplete)

{{current_metadata}}

User Priority Tags Reference

The user has configured the following list of meaningful tag categories for this model type ({{model_type}}):

{{priority_tags}}

These are the subjects, styles, and concepts the user considers useful for categorization. Use this list as a reference when evaluating tags (see the tags section below).

Available Base Models

The following base models are currently valid in this system. Use the EXACT name listed — do not invent aliases or modify variant suffixes.

{{base_models}}

Model Card Content

{{readme_content}}

Extraction Instructions

Extract the following information from the README content above:

base_model

The base model this model was trained on. Use EXACTLY one of the names from the Available Base Models list above. Do not invent new names or use aliases.

Check the YAML frontmatter for base_model: first. If the frontmatter has no base_model:, look at the model filename ({{model_basename}}), YAML tags:, README title and first paragraph for clues — the base model family is often embedded in the name

trigger_words

The trigger words or activation prompts needed to use this LoRA. Look for:

  • instance_prompt: in the YAML frontmatter
  • Phrases like "trigger word:", "trigger:", "use this prompt:", "activation prompt:"
  • In collection repos: the trigger section specific to this model file (look near matching download links or anchor IDs)
  • Example prompts at the start (usually the first word or phrase before any description) Return as an array of strings. If none found, return an empty array []. Never return ["None"] or any placeholder value — a truly empty list means no trigger words exist.

short_description

A concise 1-2 sentence summary of what this model does. Extract from the "Model description" section or the first paragraph. For collection repos, focus on the specific model version matching {{model_basename}}, not the repo as a whole. Return empty string if the README is too minimal.

tags

3-8 relevant tags for categorizing this model. Quality over quantity.

Sources to consider:

  • The YAML frontmatter tags: list (filter out technical ones — see below)
  • The subject, style, character, or concept the model represents
  • The model filename itself may give clues (e.g. "pokemon", "anime", "pixelart")

Critical filtering rules — apply them strictly:

  1. Exclude technical/generic tags. Reject any tag that describes the model's training methodology, framework, architecture, or modality rather than its content. Examples to exclude: text-to-image, diffusers, lora, dreambooth, diffusers-training, flux, sdxl, checkpoint, pytorch, safetensors, fine-tuning, stable-diffusion, and any variant of these.

  2. Cross-reference against the priority_tags reference. Only include a tag if it meaningfully describes what the model actually creates (subject, style, character type) and is semantically close to one of the priority_tags. If none of the README's tags match meaningful categories, prefer returning a smaller set or an empty array over including low-value tags.

  3. All lowercase, no spaces, no hyphens (use single words like "photorealistic", "anime", "character").

Return empty array if no meaningful content tags remain after filtering.

The recommended image generation resolution for this model, in pixels. Look for sections like "Best Dimensions", "Recommended size", "Suggested resolution", or similar phrasing in the README. Prefer the explicitly marked "Best" or default resolution. If the table/list has multiple entries (e.g. "768 x 1024 (Best)" and "1024 x 1024 (Default)"), use the one marked "Best". Return integers. If no resolution can be determined, return 0 for both.

preview_url

The URL of the most suitable preview image from the README. Look for:

  • Image tags near the section matching the model filename ({{model_basename}})
  • The YAML frontmatter widget: section (which often has output.url fields)
  • In collection repos: the sample images listed under the section for this specific model version
  • Generic ![alt](url) in the body Choose the first image that appears to be a generation example (not a logo or diagram). Construct the absolute URL from the repository raw-file base URL ({{asset_base_url}}) plus the relative path. If no suitable image is found, return an empty string.

notes

A plain-text summary of the model card's key practical usage information. Combine trigger words, style modifiers, recommended parameters (steps, CFG, resolution, sampler), and any setup tips into a readable paragraph. For collection repos, focus on the specific model version matching {{model_basename}}. Return empty string if the README has no useful usage info.

usage_tips

A JSON string with structured usage recommendations. Extract from the README any explicit ranges or recommended values (e.g. "Set LoRA strength: 0.85 - 1.4", "CLIP strength: 0.5"). Possible fields (include only those you can determine):

{
  "strength_min": 0.85,
  "strength_max": 1.4,
  "strength_range": "0.85-1.4",
  "strength": 0.6,
  "clip_strength": 0.5,
  "clip_skip": 2
}

Return the JSON string (e.g. '{"strength_min":0.85,"strength_max":1.4}'). Return "{}" if nothing useful is found.

confidence

Your confidence level in the extracted data:

  • "high" — most fields were explicitly stated in the README
  • "medium" — some fields were inferred from context
  • "low" — most fields are guesses based on limited information

Important: Handling Collection Repos (multiple model files)

Many model repositories contain multiple model files in a single repository (e.g. a "LoRA collection" with different styles/characters in separate files).

The model file currently being enriched is: {{model_basename}}

To find the correct section in the README:

  1. Search for download links containing the filename — the surrounding paragraph is your section.
  2. Search for anchor IDs (<a id="...">) or section headings whose text matches words from the filename.
  3. Search for HTML headings (<h1>, <h2>, <span>) containing parts of the filename.
  4. If no match is found, use the full README as usual — the model may be the only one in the repo.

When a matching section IS found, prefer metadata from that section. When no section matches (e.g. single-model repos or repos without per-file sections), extract metadata from the full README normally. Do not return empty data just because the filename doesn't appear in the README.

Output Format

Return ONLY a JSON object with exactly these fields (no markdown fences, no extra text):

{
  "model_path": "{{model_path}}",
  "base_model": "<canonical name or empty string>",
  "trigger_words": ["<word1>", "<word2>"],
  "short_description": "<1-2 sentence summary>",
  "tags": ["<tag1>", "<tag2>"],
  "recommended_width": 768,
  "recommended_height": 1024,
  "preview_url": "<image URL or empty string>",
  "notes": "<plain-text usage summary or empty string>",
  "usage_tips": "<JSON string like '{\"strength_min\":0.85,\"strength_max\":1.4}' or '{}'>",
  "confidence": "<high|medium|low>"
}

Important:

  • Only include the JSON object, no other text
  • If a field cannot be determined, use an empty string or empty array
  • Do not fabricate information not supported by the README
  • Never use placeholder values like "None" or "unknown" for missing data — use empty string or empty array