This document covers model discovery, downloads, versioning, and lifecycle management for Civitai models in the Oshun platform.
Overview#
The model management system provides:
- Model Discovery: Search, browse, and filter models
- Download Management: Queue-based downloading with progress tracking
- Version Control: Handle model versions and compatibility
- Storage Optimization: Intelligent caching and cleanup
- Hot-Swap: Live model switching with memory management
CivitAI Service#
The CivitAIService provides a production-grade API client with resilience
patterns built in.
Initialization#
typescript
import { CivitAIService, createCivitAIService } from '@oshun/civitai-service';
const service = createCivitAIService({
apiKey: process.env.CIVITAI_API_KEY,
rateLimitBuffer: 60, // requests per minute
cacheTtl: 300, // cache TTL in seconds
maxConcurrentDownloads: 3,
enableCache: true,
nsfwEnabled: true,
contentLevel: 'R', // PG, PG13, R, X, XXX
});
Configuration Options#
typescript
interface CivitAIConfig {
apiKey: string;
baseUrl?: string; // default: 'https://civitai.com/api/v1'
rateLimitBuffer?: number; // default: 60
cacheTtl?: number; // default: 300
retryAttempts?: number; // default: 3
retryBaseDelay?: number; // default: 1000
enableCache?: boolean; // default: true
maxConcurrentDownloads?: number; // default: 3
nsfwEnabled?: boolean; // default: true
contentLevel?: ContentLevel; // default: 'R'
}
Model Discovery#
Listing Models#
typescript
const response = await service.listModels({
query: 'portrait', // search query
types: ['Checkpoint'], // model types
sort: 'Highest Rated', // sort order
page: 1,
limit: 20,
nsfw: false,
});
for (const model of response.items) {
console.log(`${model.name} (${model.type})`);
console.log(`Creator: ${model.creator.username}`);
console.log(`Downloads: ${model.stats.downloadCount}`);
console.log(`Rating: ${model.stats.rating}/5`);
}
Search Parameters#
| Parameter | Type | Description |
|---|---|---|
query |
string | Search query string |
types |
ModelType[] | Filter by model types |
sort |
string | Sort order |
page |
number | Page number (1-indexed) |
limit |
number | Results per page (max 100) |
nsfw |
boolean | Include NSFW content |
tag |
string | Filter by tag |
username |
string | Filter by creator |
Model Types#
typescript
type ModelType =
| 'Checkpoint'
| 'TextualInversion'
| 'Hypernetwork'
| 'AestheticGradient'
| 'LORA'
| 'Controlnet'
| 'Poses';
Sort Options#
| Value | Description |
|---|---|
Highest Rated |
Sort by rating |
Most Downloaded |
Sort by download count |
Newest |
Sort by creation date |
Getting Model Details#
typescript
// Get model by ID
const model = await service.getModel(133005);
console.log('Model:', model.name);
console.log('Description:', model.description);
console.log('Tags:', model.tags);
console.log('NSFW:', model.nsfw);
console.log('Commercial Use:', model.allowCommercialUse);
// List versions
for (const version of model.modelVersions) {
console.log(`Version: ${version.name}`);
console.log(`Base Model: ${version.baseModel}`);
console.log(`Trained Words: ${version.trainedWords.join(', ')}`);
}
Getting Version by Hash#
typescript
// Useful for identifying models by file hash
const version = await service.getModelVersionByHash(
'abc123def456...' // SHA256 hash
);
console.log('Model:', version.model.name);
console.log('Version:', version.name);
Listing Creators#
typescript
const creators = await service.listCreators({
query: 'artist',
limit: 20,
});
for (const creator of creators.items) {
console.log(`Username: ${creator.username}`);
}
Listing Tags#
typescript
const tags = await service.listTags({
query: 'style',
limit: 50,
});
for (const tag of tags.items) {
console.log(`${tag.name}: ${tag.modelCount} models`);
}
Download Management#
Download Queue#
The service includes a priority-based download queue with concurrent download support.
typescript
// Queue a download
const downloadId = service.queueDownload({
modelVersionId: 348913,
destinationPath: '/models/checkpoints/',
customFilename: 'juggernaut-xl.safetensors',
priority: 'normal',
verifyHash: true,
hashAlgorithm: 'SHA256',
onProgress: (progress) => {
console.log(`${progress.percentage}% - ${progress.speedBps / 1024} KB/s`);
},
});
console.log('Download queued:', downloadId);
Download Priorities#
| Priority | Description | Use Case |
|---|---|---|
urgent |
Immediate processing | User-requested downloads |
high |
Next in queue | Auto-triggered downloads |
normal |
Standard priority | Batch downloads |
low |
Background processing | Pre-caching |
Monitoring Progress#
typescript
// Get single download progress
const progress = service.getDownloadProgress(downloadId);
if (progress) {
console.log(`Status: ${progress.status}`);
console.log(`Progress: ${progress.percentage}%`);
console.log(
`Downloaded: ${progress.downloadedBytes} / ${progress.totalBytes}`
);
console.log(`Speed: ${progress.speedBps / 1024} KB/s`);
console.log(`ETA: ${progress.etaSeconds} seconds`);
}
// Get all downloads
const allDownloads = service.getAllDownloads();
for (const download of allDownloads) {
console.log(
`${download.id}: ${download.status} - ${download.progress.percentage}%`
);
}
// Get queue statistics
const stats = service.getDownloadStats();
console.log(`Total: ${stats.totalItems}`);
console.log(`Queued: ${stats.queued}`);
console.log(`Downloading: ${stats.downloading}`);
console.log(`Completed: ${stats.completed}`);
console.log(`Failed: ${stats.failed}`);
Download Control#
typescript
// Pause download
service.pauseDownload(downloadId);
// Resume download
service.resumeDownload(downloadId);
// Cancel download
service.cancelDownload(downloadId);
Download Status Flow#
text
queued → downloading → completed
→ failed
paused ↔ queued
cancelled
Model Type Manager#
The Model Type Manager provides metadata and compatibility information for different model types.
Supported Model Types#
| Type | Subtypes | Description |
|---|---|---|
| Checkpoints | base, refiner, inpaint, merged | Base models for generation |
| LoRA/LyCORIS | LoRA, LoHa, LoKr, LoCon, DyLoRA, DoRA | Fine-tuning adapters |
| VAE | standard, MSE, EMA, TAESD | Image encoding/decoding |
| Embeddings | positive, negative, style, character | Text embeddings |
| ControlNet | canny, depth, openpose, lineart | Conditional generation |
| IP-Adapters | standard, plus, faceid | Image-conditioned generation |
| Text Encoders | CLIP-L, CLIP-G, T5, SigLIP | Text encoding models |
| Upscalers | ESRGAN, RealESRGAN, SwinIR | Image upscaling |
| Motion Modules | AnimateDiff v1-3, LCM | Video generation |
| Inpainting | BrushNet, PowerPaint, MAT, LaMa | Image inpainting |
Architecture Support#
| Architecture | Versions |
|---|---|
| Stable Diffusion | 1.4, 1.5, 2.0, 2.1 |
| SDXL | 0.9, 1.0, Turbo, Lightning |
| Flux | 1.0 S, D, D Hyper |
| Pony | V6 |
| Video | LTXV, CogVideoX, SVD |
Rate Limiting#
Sliding Window Rate Limiter#
The service implements a sliding window rate limiter to respect API limits.
typescript
// Check rate limit state
const rateLimitState = service.getRateLimitState();
console.log(`Remaining: ${rateLimitState.remaining}/${rateLimitState.limit}`);
console.log(`Reset at: ${rateLimitState.resetAt}`);
console.log(`Is limited: ${rateLimitState.isLimited}`);
if (rateLimitState.isLimited) {
console.log(`Retry after: ${rateLimitState.retryAfter} seconds`);
}
Rate Limit Configuration#
| Setting | Default | Description |
|---|---|---|
| Window | 60 seconds | Sliding window duration |
| Max Requests | 60 | Requests per window |
| Retry Delay | Exponential | Backoff on 429 response |
Circuit Breaker#
Circuit States#
The circuit breaker protects against cascading failures:
text
closed → (failures exceed threshold) → open
↑ ↓
└──────── (success threshold met) ← half_open
↑
(timeout elapsed) ┘
Checking Circuit State#
typescript
const circuitState = service.getCircuitBreakerState();
console.log(`State: ${circuitState.state}`);
console.log(`Failures: ${circuitState.failures}`);
console.log(`Successes: ${circuitState.successes}`);
if (circuitState.state === 'open') {
console.log(`Opened at: ${circuitState.openedAt}`);
}
Circuit Breaker Configuration#
| Setting | Default | Description |
|---|---|---|
| Failure Threshold | 5 | Failures to open circuit |
| Success Threshold | 3 | Successes to close circuit |
| Timeout | 30 seconds | Time before half-open |
| Monitor Window | 60 seconds | Failure tracking window |
Request Caching#
Cache Configuration#
typescript
// Cache is enabled by default
const service = createCivitAIService({
apiKey: process.env.CIVITAI_API_KEY,
enableCache: true,
cacheTtl: 300, // 5 minutes
});
// Check cache statistics
const cacheStats = service.getCacheStats();
console.log(`Size: ${cacheStats.size}`);
console.log(`Hits: ${cacheStats.hits}`);
console.log(`Misses: ${cacheStats.misses}`);
console.log(
`Hit Rate: ${(cacheStats.hits / (cacheStats.hits + cacheStats.misses)) * 100}%`
);
// Clear cache
service.clearCache();
Cache Behavior#
- GET requests are cached by default
- Cache key is generated from endpoint + params
- LRU eviction when cache reaches max size
- Automatic cleanup of expired entries
Bypassing Cache#
typescript
// Force fresh request
const model = await service.request('/models/133005', {
useCache: false,
});
Content Filtering#
Content Levels#
| Level | Description | Includes |
|---|---|---|
PG |
Safe for all ages | PG only |
PG13 |
Teen appropriate | PG, PG13 |
R |
Adult themes | PG, PG13, R |
X |
Explicit content | PG, PG13, R, X |
XXX |
All content | Everything |
Configuring Content Filters#
typescript
const service = createCivitAIService({
apiKey: process.env.CIVITAI_API_KEY,
nsfwEnabled: true,
contentLevel: 'R',
});
// Check if content is allowed
const isAllowed = service.isContentLevelAllowed('PG13');
console.log('PG13 allowed:', isAllowed);
Event System#
Event Types#
| Event | Description | Data |
|---|---|---|
request_start |
Request initiated | endpoint, method, url |
request_complete |
Request succeeded | endpoint, method, duration |
request_error |
Request failed | endpoint, method, error |
rate_limited |
Rate limit hit | endpoint, waitTime |
circuit_open |
Circuit opened | endpoint |
cache_hit |
Cache hit | endpoint, cacheKey |
cache_miss |
Cache miss | endpoint, cacheKey |
download_start |
Download queued | downloadId, request |
Subscribing to Events#
typescript
// Subscribe to events
service.on('request_complete', (event) => {
console.log(
`${event.data.method} ${event.data.endpoint} - ${event.data.duration}ms`
);
});
service.on('rate_limited', (event) => {
console.warn(`Rate limited! Wait ${event.data.waitTime}ms`);
});
service.on('circuit_open', (event) => {
console.error(`Circuit breaker opened for ${event.data.endpoint}`);
});
// Unsubscribe
const listener = (event) => console.log(event);
service.on('cache_hit', listener);
service.off('cache_hit', listener);
Analytics#
Request Analytics#
typescript
const analytics = service.getAnalytics();
console.log(`Total Requests: ${analytics.totalRequests}`);
console.log(`Successful: ${analytics.successfulRequests}`);
console.log(`Failed: ${analytics.failedRequests}`);
console.log(`Rate Limited: ${analytics.rateLimitedRequests}`);
console.log(`Avg Response Time: ${analytics.averageResponseTimeMs}ms`);
// Per-endpoint counts
for (const [endpoint, count] of Object.entries(analytics.endpointCounts)) {
console.log(`${endpoint}: ${count} requests`);
}
// Error distribution
for (const [code, count] of Object.entries(analytics.errorCounts)) {
console.log(`${code}: ${count} errors`);
}
// Reset analytics
service.resetAnalytics();
Civitai Link (Hot-Swap)#
The Civitai Link provider enables real-time model synchronization via WebSocket.
Initialization#
typescript
import { CivitaiLinkProvider } from '@oshun/civitai-link';
const link = new CivitaiLinkProvider({
endpoint: process.env.CIVITAI_LINK_ENDPOINT,
apiKey: process.env.CIVITAI_LINK_KEY,
autoReconnect: true,
reconnectDelay: 5000,
heartbeatInterval: 30000,
});
await link.connect();
Hot-Swap Models#
typescript
// Swap to a different model
await link.swapModel({
modelVersionId: 348913,
slot: 'checkpoint',
priority: 'high',
});
// Check memory stats
const memory = await link.getMemoryStats();
console.log(`VRAM Used: ${memory.vramUsed / 1024 / 1024} MB`);
console.log(`VRAM Free: ${memory.vramFree / 1024 / 1024} MB`);
console.log(`RAM Used: ${memory.ramUsed / 1024 / 1024} MB`);
Download Queue#
typescript
// Queue download with priority
link.queueDownload({
modelVersionId: 348913,
priority: 'high',
onProgress: (progress) => {
console.log(`${progress.filename}: ${progress.percentage}%`);
},
});
// Get queue status
const queue = link.getDownloadQueue();
for (const item of queue) {
console.log(`${item.id}: ${item.status}`);
}
Predictive Loading#
typescript
// Enable predictive loading based on usage patterns
link.enablePredictiveLoading({
analyzeUsagePatterns: true,
preloadThreshold: 0.7, // 70% confidence
});
// Get preload recommendations
const recommendations = await link.getPreloadRecommendations();
for (const rec of recommendations) {
console.log(`Recommend preloading: ${rec.modelName}`);
console.log(`Usage frequency: ${rec.usageFrequency}`);
console.log(`Confidence: ${rec.confidence}`);
}
Storage Optimization#
typescript
// Analyze storage usage
const analysis = await link.analyzeStorage();
console.log(`Total models: ${analysis.totalModels}`);
console.log(`Total size: ${analysis.totalSize / 1024 / 1024 / 1024} GB`);
console.log(`Duplicates: ${analysis.duplicates.length}`);
// Get cleanup suggestions
const suggestions = await link.getCleanupSuggestions();
for (const suggestion of suggestions) {
console.log(`Suggest removing: ${suggestion.modelName}`);
console.log(`Reason: ${suggestion.reason}`);
console.log(`Space savings: ${suggestion.sizeSaved / 1024 / 1024} MB`);
}
// Execute cleanup
await link.executeCleanup(suggestions.map((s) => s.modelVersionId));
Best Practices#
Efficient API Usage#
- Use caching: Enable caching for repeated queries
- Batch requests: Combine multiple model lookups when possible
- Respect rate limits: Monitor rate limit state proactively
- Handle errors gracefully: Implement retry logic with backoff
Download Management#
- Use priorities wisely: Reserve
urgentfor user-initiated downloads - Monitor progress: Provide feedback to users on download status
- Verify hashes: Always verify downloaded files
- Clean up failures: Remove failed downloads from queue
Model Organization#
- Use consistent naming: Follow a naming convention for downloaded models
- Track versions: Maintain metadata about model versions
- Regular cleanup: Remove unused models periodically
- Monitor disk space: Set alerts for low disk space
Related Documentation#
- API Documentation - Generation API reference
- Training Guide - Training custom LoRAs
- Best Practices - Optimization and cost management