feat: Dynamic base model fetching from Civitai API (#854)

Implement automatic fetching of base models from Civitai API to keep
data up-to-date without manual updates.

Backend:
- Add CivitaiBaseModelService with 7-day TTL caching
- Add /api/lm/base-models endpoints for fetching and refreshing
- Merge hardcoded and remote models for backward compatibility
- Smart abbreviation generation for unknown models

Frontend:
- Add civitaiBaseModelApi client for API communication
- Dynamic base model loading on app initialization
- Update SettingsManager to use merged model lists
- Add support for 8 new models: Anima, CogVideoX, LTXV 2.3, Mochi,
  Pony V7, Wan Video 2.5 T2V/I2V

API Endpoints:
- GET /api/lm/base-models - Get merged models
- POST /api/lm/base-models/refresh - Force refresh
- GET /api/lm/base-models/categories - Get categories
- GET /api/lm/base-models/cache-status - Check cache status

Closes #854
This commit is contained in:
Will Miao
2026-03-29 00:18:15 +08:00
parent 89b1675ec7
commit 00f5c1e887
12 changed files with 1227 additions and 9 deletions

View File

@@ -2,7 +2,14 @@ import { modalManager } from './ModalManager.js';
import { showToast } from '../utils/uiHelpers.js';
import { state, createDefaultSettings } from '../state/index.js';
import { resetAndReload } from '../api/modelApiFactory.js';
import { DOWNLOAD_PATH_TEMPLATES, MAPPABLE_BASE_MODELS, PATH_TEMPLATE_PLACEHOLDERS, DEFAULT_PATH_TEMPLATES, DEFAULT_PRIORITY_TAG_CONFIG } from '../utils/constants.js';
import {
DOWNLOAD_PATH_TEMPLATES,
MAPPABLE_BASE_MODELS,
PATH_TEMPLATE_PLACEHOLDERS,
DEFAULT_PATH_TEMPLATES,
DEFAULT_PRIORITY_TAG_CONFIG,
getMappableBaseModelsDynamic
} from '../utils/constants.js';
import { translate } from '../utils/i18nHelpers.js';
import { i18n } from '../i18n/index.js';
import { configureModelCardVideo } from '../components/shared/ModelCard.js';
@@ -184,7 +191,9 @@ export class SettingsManager {
}
getAvailableDownloadSkipBaseModels() {
return MAPPABLE_BASE_MODELS.filter(model => model !== 'Other');
// Use dynamic base models if available, fallback to hardcoded
const models = getMappableBaseModelsDynamic();
return models.filter(model => model !== 'Other');
}
normalizeDownloadSkipBaseModels(value) {
@@ -1517,7 +1526,7 @@ export class SettingsManager {
const row = document.createElement('div');
row.className = 'mapping-row';
const availableModels = MAPPABLE_BASE_MODELS.filter(model => {
const availableModels = getMappableBaseModelsDynamic().filter(model => {
const existingMappings = state.global.settings.base_model_path_mappings || {};
return !existingMappings.hasOwnProperty(model) || model === baseModel;
});
@@ -1619,7 +1628,7 @@ export class SettingsManager {
const currentValue = select.value;
// Get available models (not already mapped, except current)
const availableModels = MAPPABLE_BASE_MODELS.filter(model =>
const availableModels = getMappableBaseModelsDynamic().filter(model =>
!existingMappings.hasOwnProperty(model) || model === currentValue
);