Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 44 additions & 13 deletions tensors/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -2600,27 +2600,57 @@ def db_cache(


@db_app.command("list")
def db_list(
def db_list( # noqa: PLR0915
model_type: Annotated[
str | None, typer.Option("-t", "--type", help="Filter by model type (Checkpoint, LORA, VAE, etc.)")
] = None,
base: Annotated[
str | None, typer.Option("-b", "--base", help="Filter by base model (Pony, Illustrious, SDXL 1.0, SD 1.5, etc.)")
] = None,
remote: Annotated[
str | None, typer.Option("-r", "--remote", help="Remote server name or URL (overrides default_remote)")
] = None,
local: Annotated[
bool, typer.Option("--local", help="Force read from local DB, ignoring default_remote")
] = False,
json_output: Annotated[bool, typer.Option("--json", "-j", help="Output as JSON")] = False,
) -> None:
"""List local files with CivitAI info.
"""List files indexed in the tensors DB.

Defaults to the configured remote (config.toml default_remote) so the
table reflects what's actually on the generation host. Pass --local to
inspect the local SQLite DB on this machine instead. If no remote is
configured and --local is not passed, falls back to local silently.

Examples:
tsr db list # All local files
tsr db list -t Checkpoint # Only checkpoints
tsr db list -t LORA # Only LoRAs
tsr db list -t Checkpoint -b Pony # Pony checkpoints only
tsr db list -b "SDXL 1.0" # All SDXL 1.0 models
tsr db list # Remote (or local if no default_remote)
tsr db list --local # Force local DB
tsr db list -r junkpile # Explicit remote
tsr db list -t Checkpoint -b Pony # Filter, default source
tsr db list -b "SDXL 1.0" # All SDXL 1.0 models
"""
with Database() as db:
db.init_schema()
files = db.list_local_files()
from tensors.config import resolve_remote as do_resolve_remote # noqa: PLC0415

# Resolution precedence: --local wins; else explicit -r; else default_remote; else local.
remote_url: str | None = None
if not local:
remote_url = do_resolve_remote(remote) if remote else do_resolve_remote(None)

files: list[dict[str, Any]] | None
if remote_url:
from tensors.remote import remote_db_files # noqa: PLC0415

if not json_output:
console.print(f"[dim]Remote: {remote_url}[/dim]")
files = remote_db_files(remote or remote_url, console=console)
if files is None:
raise typer.Exit(1)
source_label = "Remote Files"
else:
with Database() as db:
db.init_schema()
files = db.list_local_files()
source_label = "Local Files"

# Apply filters (case-insensitive substring match)
if model_type:
Expand All @@ -2635,17 +2665,18 @@ def db_list(
return

if not files:
console.print("[yellow]No files found. Try 'tsr db scan' or adjust filters.[/yellow]")
hint = "Remote DB is empty" if remote_url else "Try 'tsr db scan'"
console.print(f"[yellow]No files found. {hint} or adjust filters.[/yellow]")
return

title = "Local Files"
title = source_label
if model_type or base:
parts = []
if model_type:
parts.append(model_type)
if base:
parts.append(base)
title = f"Local Files ({', '.join(parts)})"
title = f"{source_label} ({', '.join(parts)})"

table = Table(title=title, show_header=True, header_style="bold magenta")
table.add_column("Path", style="cyan", max_width=50)
Expand Down
45 changes: 45 additions & 0 deletions tensors/remote.py
Original file line number Diff line number Diff line change
Expand Up @@ -302,3 +302,48 @@ def remote_download_status(
return result
except (httpx.HTTPStatusError, httpx.RequestError):
return None


def remote_db_files(
remote: str,
*,
console: Console | None = None,
) -> list[dict[str, Any]] | None:
"""Fetch the local-files index from a remote tensors server's DB.

Returns the same shape as Database.list_local_files() but reflecting the
remote host's SQLite DB rather than ours.

Args:
remote: Remote name or URL (resolved via config)
console: Rich console for error output

Returns:
List of file dicts, or None on connection / API error
"""
base_url = resolve_remote(remote)
if not base_url:
if console:
console.print("[red]Error: Could not resolve remote server[/red]")
return None

try:
with _build_client(base_url) as client:
response = client.get("/api/db/files")
response.raise_for_status()
result: list[dict[str, Any]] = response.json()
return result
except httpx.HTTPStatusError as e:
if console:
console.print(f"[red]Remote API error: {e.response.status_code}[/red]")
try:
detail = e.response.json().get("detail", "")
if detail:
console.print(f" [yellow]{detail}[/yellow]")
except Exception:
pass
return None
except httpx.RequestError as e:
if console:
console.print(f"[red]Remote connection error: {e}[/red]")
return None
Loading