feat: Implement model description retrieval and update related API endpoints

This commit is contained in:
Will Miao
2025-08-27 18:22:56 +08:00
parent 9817bac2fe
commit 5b0becaaf2
8 changed files with 110 additions and 89 deletions

View File

@@ -89,6 +89,7 @@ export function getApiEndpoints(modelType) {
conflicts: `/api/${modelType}/find-filename-conflicts`,
verify: `/api/${modelType}/verify-duplicates`,
metadata: `/api/${modelType}/metadata`,
modelDescription: `/api/${modelType}/model-description`,
// Model-specific endpoints (will be merged with specific configs)
specific: {}
@@ -106,7 +107,6 @@ export const MODEL_SPECIFIC_ENDPOINTS = {
previewUrl: `/api/${MODEL_TYPES.LORA}/preview-url`,
civitaiUrl: `/api/${MODEL_TYPES.LORA}/civitai-url`,
metadata: `/api/${MODEL_TYPES.LORA}/metadata`,
modelDescription: `/api/${MODEL_TYPES.LORA}/model-description`,
getTriggerWordsPost: `/api/${MODEL_TYPES.LORA}/get_trigger_words`,
civitaiModelByVersion: `/api/${MODEL_TYPES.LORA}/civitai/model/version`,
civitaiModelByHash: `/api/${MODEL_TYPES.LORA}/civitai/model/hash`,

View File

@@ -970,4 +970,26 @@ export class BaseModelApiClient {
throw error;
}
}
async fetchModelDescription(filePath) {
try {
const params = new URLSearchParams({ file_path: filePath });
const response = await fetch(`${this.apiConfig.endpoints.modelDescription}?${params}`);
if (!response.ok) {
throw new Error(`Failed to fetch ${this.apiConfig.config.singularName} description: ${response.statusText}`);
}
const data = await response.json();
if (data.success) {
return data.description;
} else {
throw new Error(data.error || `No description found for ${this.apiConfig.config.singularName}`);
}
} catch (error) {
console.error(`Error fetching ${this.apiConfig.config.singularName} description:`, error);
throw error;
}
}
}

View File

@@ -1,5 +1,4 @@
import { BaseModelApiClient } from './baseModelApi.js';
import { showToast } from '../utils/uiHelpers.js';
import { getSessionItem } from '../utils/storageHelpers.js';
/**

View File

@@ -12,7 +12,7 @@ export function setupTabSwitching() {
const tabButtons = document.querySelectorAll('.showcase-tabs .tab-btn');
tabButtons.forEach(button => {
button.addEventListener('click', () => {
button.addEventListener('click', async () => {
// Remove active class from all tabs
document.querySelectorAll('.showcase-tabs .tab-btn').forEach(btn =>
btn.classList.remove('active')
@@ -26,24 +26,58 @@ export function setupTabSwitching() {
const tabId = `${button.dataset.tab}-tab`;
document.getElementById(tabId).classList.add('active');
// If switching to description tab, make sure content is properly sized
// If switching to description tab, load content lazily
if (button.dataset.tab === 'description') {
const descriptionContent = document.querySelector('.model-description-content');
if (descriptionContent) {
const hasContent = descriptionContent.innerHTML.trim() !== '';
document.querySelector('.model-description-loading')?.classList.add('hidden');
// If no content, show a message
if (!hasContent) {
descriptionContent.innerHTML = '<div class="no-description">No model description available</div>';
descriptionContent.classList.remove('hidden');
}
}
await loadModelDescription();
}
});
});
}
/**
* Load model description lazily
*/
async function loadModelDescription() {
const descriptionContent = document.querySelector('.model-description-content');
const descriptionLoading = document.querySelector('.model-description-loading');
const showcaseSection = document.querySelector('.showcase-section');
if (!descriptionContent || !showcaseSection) return;
// Check if already loaded
if (descriptionContent.dataset.loaded === 'true') {
return;
}
const filePath = showcaseSection.dataset.filepath;
if (!filePath) return;
try {
// Show loading state
descriptionLoading?.classList.remove('hidden');
descriptionContent.classList.add('hidden');
// Fetch description from API
const { getModelApiClient } = await import('../../api/modelApiFactory.js');
const description = await getModelApiClient().fetchModelDescription(filePath);
// Update content
descriptionContent.innerHTML = description || '<div class="no-description">No model description available</div>';
descriptionContent.dataset.loaded = 'true';
// Set up editing functionality
setupModelDescriptionEditing(filePath);
} catch (error) {
console.error('Error loading model description:', error);
descriptionContent.innerHTML = '<div class="no-description">Failed to load model description</div>';
} finally {
// Hide loading state
descriptionLoading?.classList.add('hidden');
descriptionContent.classList.remove('hidden');
}
}
/**
* Set up model description editing functionality
* @param {string} filePath - File path

View File

@@ -62,8 +62,7 @@ export function showModelModal(model, modelType) {
<div class="model-description-loading">
<i class="fas fa-spinner fa-spin"></i> Loading model description...
</div>
<div class="model-description-content">
${model.modelDescription || ''}
<div class="model-description-content hidden">
</div>
</div>
</div>
@@ -84,8 +83,7 @@ export function showModelModal(model, modelType) {
<div class="model-description-loading">
<i class="fas fa-spinner fa-spin"></i> Loading model description...
</div>
<div class="model-description-content">
${model.modelDescription || ''}
<div class="model-description-content hidden">
</div>
</div>
</div>`;
@@ -210,7 +208,7 @@ export function showModelModal(model, modelType) {
setupModelNameEditing(model.file_path);
setupBaseModelEditing(model.file_path);
setupFileNameEditing(model.file_path);
setupModelDescriptionEditing(model.file_path, model.modelDescription || '');
// Remove setupModelDescriptionEditing from here - it will be called lazily
setupEventHandlers(model.file_path);
// LoRA specific setup