💬 Commit message: Update 2026-02-15 18:38:39, 2 files, 121 lines

📁 Files changed: 2
📝 Lines changed: 121

  • README.md
  • civitai_routes.py
This commit is contained in:
Adam Ladachowski
2026-02-15 18:38:39 +01:00
parent ab3660a175
commit f2df2bbefa
2 changed files with 105 additions and 16 deletions
+29 -5
View File
@@ -46,6 +46,15 @@ tsr search -t lora -b sdxl
# Sort by newest, limit results
tsr search -t checkpoint -s newest -n 10
# Filter by tag and period
tsr search --tag anime -p week -b illustrious
# By creator
tsr search -u "username"
# SFW only with commercial use filter
tsr search --sfw --commercial sell
```
### Get Model Info
@@ -240,9 +249,10 @@ local = "http://localhost:51200"
default_remote = "junkpile"
```
Or set API key via environment variable:
Or set API keys via environment variables:
```bash
export CIVITAI_API_KEY="your-api-key"
export CIVITAI_API_KEY="your-api-key" # For CivitAI API access
export TENSORS_API_KEY="your-server-key" # For server authentication
```
## Default Paths
@@ -263,9 +273,16 @@ Data is stored in XDG-compliant paths:
| Option | Values |
|--------|--------|
| `-t, --type` | checkpoint, lora, embedding, vae, controlnet, locon |
| `-b, --base` | sd15, sdxl, pony, flux, illustrious |
| `-b, --base` | sd14, sd15, sd2, sdxl, pony, flux, illustrious, noobai, auraflow |
| `-s, --sort` | downloads, rating, newest |
| `-n, --limit` | Number of results (default: 20) |
| `-n, --limit` | Number of results (default: 25) |
| `-p, --period` | all, year, month, week, day |
| `--tag` | Filter by tag (e.g., "anime") |
| `-u, --user` | Filter by creator username |
| `--nsfw` | none, soft, mature, x |
| `--sfw` | Exclude NSFW content |
| `--commercial` | none, image, rent, sell |
| `--page` | Page number for pagination |
## Generate Options
@@ -288,9 +305,16 @@ Data is stored in XDG-compliant paths:
When running `tsr serve`, the following endpoints are available:
**OpenAPI Documentation:** Visit `/docs` for interactive Scalar API documentation.
**Authentication:** If `TENSORS_API_KEY` is set, all endpoints except `/status` and `/docs` require authentication via:
- Header: `X-API-Key: your-key`
- Query param: `?api_key=your-key`
| Endpoint | Method | Description |
|----------|--------|-------------|
| `/status` | GET | Server status and active model |
| `/status` | GET | Server status (public) |
| `/docs` | GET | OpenAPI documentation (public) |
| `/reload` | POST | Hot-reload with new model |
| `/api/images` | GET | List gallery images |
| `/api/images/{id}` | GET | Get image file |
+76 -11
View File
@@ -3,7 +3,8 @@
from __future__ import annotations
import logging
from typing import Any
from enum import Enum
from typing import Annotated, Any
import httpx
from fastapi import APIRouter, Query, Response
@@ -17,6 +18,42 @@ logger = logging.getLogger(__name__)
router = APIRouter(prefix="/api/civitai", tags=["CivitAI"])
class SortOrder(str, Enum):
"""Sort order options for CivitAI search."""
most_downloaded = "Most Downloaded"
highest_rated = "Highest Rated"
newest = "Newest"
class Period(str, Enum):
"""Time period filter options."""
all = "AllTime"
year = "Year"
month = "Month"
week = "Week"
day = "Day"
class NsfwLevel(str, Enum):
"""NSFW content filter levels."""
none = "None"
soft = "Soft"
mature = "Mature"
x = "X"
class CommercialUse(str, Enum):
"""Commercial use filter options."""
none = "None"
image = "Image"
rent = "Rent"
sell = "Sell"
def _get_headers(api_key: str | None) -> dict[str, str]:
"""Get headers for CivitAI API requests."""
headers: dict[str, str] = {}
@@ -27,28 +64,56 @@ def _get_headers(api_key: str | None) -> dict[str, str]:
@router.get("/search", response_model=None)
async def search_models(
query: str | None = Query(default=None, description="Search query"),
types: str | None = Query(default=None, description="Model type (Checkpoint, LORA, LoCon, etc.)"),
base_models: str | None = Query(default=None, alias="baseModels", description="Base model (SD 1.5, SDXL 1.0, Pony, etc.)"),
sort: str = Query(default="Most Downloaded", description="Sort order"),
limit: int = Query(default=20, le=100, description="Max results"),
nsfw: bool = Query(default=True, description="Include NSFW models"),
query: Annotated[str | None, Query(description="Search query")] = None,
types: Annotated[str | None, Query(description="Model type (Checkpoint, LORA, etc.)")] = None,
base_models: Annotated[str | None, Query(alias="baseModels", description="Base model")] = None,
sort: Annotated[SortOrder, Query(description="Sort order")] = SortOrder.most_downloaded,
limit: Annotated[int | None, Query(le=100, description="Max results (default: 25)", example=5)] = None,
period: Annotated[Period | None, Query(description="Time period filter")] = None,
tag: Annotated[str | None, Query(description="Filter by tag")] = None,
username: Annotated[str | None, Query(description="Filter by creator username")] = None,
page: Annotated[int | None, Query(ge=1, description="Page number")] = None,
nsfw: Annotated[NsfwLevel | None, Query(description="NSFW filter level")] = None,
sfw: Annotated[bool, Query(description="Exclude NSFW content")] = False,
commercial: Annotated[CommercialUse | None, Query(description="Commercial use filter")] = None,
) -> dict[str, Any] | Response:
"""Search CivitAI models."""
"""Search CivitAI models.
Supports all CivitAI search parameters including filters for type, base model,
time period, tags, creator, NSFW level, and commercial use.
"""
api_key = load_api_key()
actual_limit = limit if limit is not None else 25
params: dict[str, Any] = {
"limit": min(limit, 100),
"nsfw": str(nsfw).lower(),
"sort": sort,
"limit": min(actual_limit, 100),
"sort": sort.value,
}
# Handle NSFW filtering
if sfw:
params["nsfw"] = "false"
elif nsfw:
params["nsfwLevel"] = nsfw.value
else:
params["nsfw"] = "true" # Default: include all
if query:
params["query"] = query
if types:
params["types"] = types
if base_models:
params["baseModels"] = base_models
if period:
params["period"] = period.value
if tag:
params["tag"] = tag
if username:
params["username"] = username
if page:
params["page"] = page
if commercial:
params["allowCommercialUse"] = commercial.value
url = f"{CIVITAI_API_BASE}/models"