add-ecosystem
GitHub用于在Civitai平台新增模型生态和基础模型记录。核心在于判断LoRA兼容性以决定生态隔离,处理常量配置及可选触发生成支持流程。
Trigger Scenarios
Install
npx skills add civitai/civitai --skill add-ecosystem -g -y
SKILL.md
Frontmatter
{
"name": "add-ecosystem",
"description": "Add a new ecosystem and base model to basemodel.constants.ts. Use when onboarding a new model family from providers like Baidu, ByteDance, Google, etc. Handles ECO constants, BM constants, ecosystem record, family (creates new if needed), license (creates new if needed), and base model record. Optionally triggers add-generation-support at the end."
}
Add Ecosystem
Adds a new ecosystem and base model entry to basemodel.constants.ts. Everything else downstream (generation support, graph, handler, workflow wiring) is handled by the add-generation-support skill.
When to use
Use when a new model provider or variant is being added to Civitai — e.g., new provider (Baidu's Ernie), new architecture (Flux's Kontext), or a variant of an existing family whose resources aren't interchangeable with its siblings.
The test: does this need its own ecosystem?
Answer this before picking IDs. The ecosystem is the compatibility key, not a UI grouping and not a media label:
A variant needs its own ecosystem when a LoRA (or other addon) trained for it would NOT work on every model carrying the sibling's baseModel.
That is the whole test. getGenerationSupport returns 'full' unconditionally for same-ecosystem pairs — there is no media dimension and no per-model nuance in it. Putting two things in one ecosystem asserts "resources cross freely between these," so only do it when that's true.
Architecture is not weights. The most common way to get this wrong is reading a vendor's "one unified model" marketing as "one checkpoint." Providers routinely ship a shared architecture as separate weight releases, and a LoRA is trained against weights. Check what actually ships — distinct releases, distinct sizes, distinct endpoints — not what the announcement calls the family.
Do not split on output media. "Image vs video" is not the question; "do resources cross" is. If one checkpoint does both, that's one ecosystem with a type: ['image', 'video'] base model (Grok). If they're separate checkpoints that happen to differ in output media, that's two ecosystems (Wan Image 2.7 / Wan Video 2.7 — note there are deliberately no crossEcosystemRules between them).
When genuinely unsure, split. The two mistakes are not symmetric:
| Choice | If wrong | Cost to fix |
|---|---|---|
| Split, but they're compatible | Resources don't cross | Add crossEcosystemRules entries — additive, that's what the mechanism is for |
| Merged, but they're incompatible | Incompatible resources offered as compatible | Change the ecosystem key → changes the AIR URN namespace on already-published resources |
Splitting is reversible; merging is not. This is doubly true for API-only / modelLocked ecosystems, where no community resources exist yet — the split costs nothing today and preserves the option.
Media-specific labelling for creators is a BaseModelRecord concern, not an ecosystem one — several base models can share one ecosystem, each with its own name and type.
Workflow (interactive after research)
Do research first, then ask the user only for what can't be inferred.
1. Gather model info
Ask the user for the model name and a reference link (HuggingFace page, official repo, announcement). Then research before asking anything else:
- WebFetch the reference link to extract:
- Provider/company (drives family selection)
- License (match against existing
licensesarray or flag as new) - Model type (
imagevsvideo— sometimes both) - How it actually ships — one checkpoint or several separate weight releases? This decides the ecosystem split (see "The test" above), so read for distinct releases/sizes/endpoints rather than trusting the family name.
- Short description for the base model record
- Search the codebase for prior patterns:
Grepfor the provider name to see if a family already exists
2. Pick IDs
Read the current state of basemodel.constants.ts to determine the next available IDs. Use Read with offsets — don't load the whole file.
ECO.<Name>: next available ecosystem ID. Groupings inECO:- Image models: 1-50 range (first come, first served; find next gap)
- Video models: 47-66 range
- Utility: 66+
- Child ecosystems (
parentEcosystemIdset): 100+ for SDXL children, 200+ for AuraFlow children - Pick the next unused number within the appropriate block
BM.<Name>: next available base model ID. Read theBMconstant block, find the next unused number.- Family ID: try to match an existing family in
ecosystemFamilies. If none match, propose creating a new one (confirm with user). - License ID: try to match an existing license in
licensesby name/URL. If none match, create a new entry (confirm with user). sortOrder: follow the pattern of the family. Image family numbers are usually sequential starting from an offset tied to the family. If the family has existing ecosystems, use the next number in its block. If new family, start at a round number (10, 20, 30, etc. — match surrounding patterns).
3. Confirm the plan with the user
Before editing, present a summary:
Adding ecosystem: <DisplayName>
- ECO.<Name> = <id>
- BM.<Name> = <id>
- Family: <existing family name> (familyId: <id>) OR [new: <name>]
- License: <existing license name> (licenseId: <id>) OR [new: <name>, <url>]
- Type: image | video
- sortOrder: <n>
- Description: "<short description>"
Wait for user confirmation. Accept corrections.
4. Make the edits
Apply all changes in one pass:
ECOconstant: add the new key under the appropriate section comment (e.g.,// Baidufor Ernie). Keep sections grouped.BMconstant: add the new key in the matching block.ecosystemFamilies(only if creating new): append at the end. Use the next family ID.licenses(only if creating new): append at the end. Use the next license ID.ecosystems: add the newEcosystemRecordunder the right family's section comment. IncludeparentEcosystemIdonly if it's a child ecosystem (rare).baseModelRecords: add the newBaseModelRecordin alphabetical or thematic position (scan existing entries for the pattern).
5. Typecheck
pnpm run typecheck
If it fails, fix the error and re-run. Don't continue until clean.
6. Offer generation support
After the ecosystem is added and typecheck passes, ask:
Do you want to add generation support now? This wires the ecosystem into the generation form with a graph, handler, and workflow config. (Runs the
add-generation-supportskill.)
If yes, invoke the add-generation-support skill. If no, stop — the ecosystem record alone is enough for it to appear in model listings.
Note: making a new ecosystem generatable (GenerationBaseModel) and featurable in auctions (AuctionBase) are manual DB steps that the constants do not handle automatically — miss them and the feature silently half-works (this bit Anima and Krea 2). Those steps are documented in the add-generation-support skill under "Post-onboarding: generation coverage & auction featurability."
Record structures
EcosystemRecord
{
id: ECO.<Name>,
key: '<Name>', // Stable identifier (e.g., 'Ernie')
name: '<name>', // lowercase (e.g., 'ernie')
displayName: '<Display Name>', // UI (e.g., 'Ernie')
familyId: <id>,
sortOrder: <n>,
parentEcosystemId?: <id>, // Only for child ecosystems
description?: string, // Rarely needed
}
BaseModelRecord
{
id: BM.<Name>,
name: '<Name>',
description: "<Provider>'s <type> generation model",
type: 'image' | 'video' | ['image', 'video'],
ecosystemId: ECO.<Name>,
licenseId: <id>,
hidden?: boolean, // true if not user-facing yet
experimental?: boolean,
disabled?: boolean,
}
BaseModelFamilyRecord (create only if needed)
{
id: <next id>,
name: '<Provider Name>',
description: "<Provider>'s <description of product lineup>",
}
LicenseRecord (create only if needed)
{
id: <next id>,
name: '<License Name>', // Exact name from the source
url: '<canonical license URL>',
notice?: string, // Only if the license requires a copyright notice
poweredBy?: string, // Only if attribution is required
disableMature?: boolean, // Only if the license prohibits NSFW
}
Notes
- Do not add to
ecosystemSupport,ecosystemSettings, workflows, graph, or handler files in this skill — those are generation-support concerns and belong to theadd-generation-supportskill. - Do not skip the typecheck step. The base model records are validated against ecosystem IDs, and a mismatch breaks the entire constants file.
- If a new ecosystem is a child of an existing one (e.g., fine-tunes of SDXL), set
parentEcosystemIdand use a sort order from the child range (100+, 200+). name(lowercase) is used for matching against orchestrator responses — keep it consistent with what the orchestrator returns.
Examples of past additions
- Ernie (Baidu, image): new family, new license; ECO.Ernie = 67, BM.Ernie = 83, familyId 17, licenseId 13 (Apache 2.0 — matched existing).
- Seedance (ByteDance, video): family 12 (ByteDance — existed), licenseId 23 (Seedream — shared with Seedream since ByteDance uses the same agreement).
- Flux 3 Video (BFL, video): ECO.Flux3Video = 79, BM.Flux3Video = 98, family 1, new licenseId 39. A worked example of the split test — BFL announced FLUX-3 as one multimodal model, which reads like a single ecosystem, but it ships as separate weight releases (Video, Image, the open-weight Dev backbone). Shared architecture, different checkpoints ⇒ separate ecosystems, named for the modality so the siblings land without a rename. Same reasoning as the Flux.2 Klein variants, which share
parentEcosystemId: ECO.Flux2purely for AIR identity while their LoRAs do not cross.
Version History
- 4214ecb Current 2026-08-20 18:48


