openwebui-cli/openwebui_cli/commands/config_cmd.py
Danny Stocker 8530f74687 Initial CLI scaffolding with v1.0 MVP structure
Complete Python CLI for OpenWebUI with:
- Auth commands (login, logout, whoami, token, refresh)
- Chat send with streaming support
- RAG files/collections management
- Models list/info
- Admin stats (minimal v1.0)
- Config management with profiles and keyring

Stack: typer, httpx, rich, pydantic, keyring

Based on RFC v1.2 with 22-step implementation checklist

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-30 20:09:19 +01:00

173 lines
5.2 KiB
Python

"""CLI configuration commands."""
import typer
from rich.console import Console
from rich.prompt import Prompt
from rich.table import Table
from ..config import (
Config,
ProfileConfig,
get_config_path,
load_config,
save_config,
)
app = typer.Typer(no_args_is_help=True)
console = Console()
@app.command()
def init(
ctx: typer.Context,
force: bool = typer.Option(False, "--force", "-f", help="Overwrite existing config"),
) -> None:
"""Initialize configuration file interactively."""
config_path = get_config_path()
if config_path.exists() and not force:
console.print(f"[yellow]Config already exists at: {config_path}[/yellow]")
console.print("Use --force to overwrite")
raise typer.Exit(1)
console.print("[bold]OpenWebUI CLI Configuration Setup[/bold]\n")
# Get server URI
uri = Prompt.ask(
"Server URI",
default="http://localhost:8080",
)
# Get default model
default_model = Prompt.ask(
"Default model (optional)",
default="",
)
# Get output format
default_format = Prompt.ask(
"Default output format",
choices=["text", "json", "yaml"],
default="text",
)
# Build config
config = Config(
default_profile="default",
profiles={
"default": ProfileConfig(uri=uri),
},
)
if default_model:
config.defaults.model = default_model
config.defaults.format = default_format
# Save config
save_config(config)
console.print(f"\n[green]Configuration saved to: {config_path}[/green]")
console.print("\nNext steps:")
console.print(" 1. Run 'openwebui auth login' to authenticate")
console.print(" 2. Run 'openwebui models list' to see available models")
console.print(" 3. Run 'openwebui chat send -m <model> -p \"Hello\"' to chat")
@app.command()
def show(ctx: typer.Context) -> None:
"""Show current configuration."""
config_path = get_config_path()
if not config_path.exists():
console.print("[yellow]No config file found. Run 'openwebui config init' first.[/yellow]")
raise typer.Exit(1)
config = load_config()
console.print(f"[bold]Config file:[/bold] {config_path}\n")
# Show profiles
table = Table(title="Profiles")
table.add_column("Name", style="cyan")
table.add_column("URI", style="green")
table.add_column("Default", style="yellow")
for name, profile in config.profiles.items():
is_default = "" if name == config.default_profile else ""
table.add_row(name, profile.uri, is_default)
console.print(table)
# Show defaults
console.print("\n[bold]Defaults:[/bold]")
console.print(f" Model: {config.defaults.model or '(not set)'}")
console.print(f" Format: {config.defaults.format}")
console.print(f" Stream: {config.defaults.stream}")
console.print(f" Timeout: {config.defaults.timeout}s")
@app.command("set")
def set_value(
ctx: typer.Context,
key: str = typer.Argument(..., help="Config key (e.g., 'defaults.model')"),
value: str = typer.Argument(..., help="Value to set"),
) -> None:
"""Set a configuration value."""
config = load_config()
parts = key.split(".")
if len(parts) == 2:
section, field = parts
if section == "defaults":
if field == "model":
config.defaults.model = value
elif field == "format":
config.defaults.format = value
elif field == "stream":
config.defaults.stream = value.lower() in ("true", "1", "yes")
elif field == "timeout":
config.defaults.timeout = int(value)
else:
console.print(f"[red]Unknown defaults field: {field}[/red]")
raise typer.Exit(1)
else:
console.print(f"[red]Unknown section: {section}[/red]")
raise typer.Exit(1)
else:
console.print("[red]Key format: section.field (e.g., 'defaults.model')[/red]")
raise typer.Exit(1)
save_config(config)
console.print(f"[green]Set {key} = {value}[/green]")
@app.command("get")
def get_value(
ctx: typer.Context,
key: str = typer.Argument(..., help="Config key to get"),
) -> None:
"""Get a configuration value."""
config = load_config()
parts = key.split(".")
if len(parts) == 2:
section, field = parts
if section == "defaults":
value = getattr(config.defaults, field, None)
if value is not None:
console.print(str(value))
else:
console.print(f"[red]Unknown field: {field}[/red]")
raise typer.Exit(1)
elif section == "profiles":
profile = config.profiles.get(field)
if profile:
console.print(f"uri: {profile.uri}")
else:
console.print(f"[red]Unknown profile: {field}[/red]")
raise typer.Exit(1)
else:
console.print(f"[red]Unknown section: {section}[/red]")
raise typer.Exit(1)
else:
console.print("[red]Key format: section.field (e.g., 'defaults.model')[/red]")
raise typer.Exit(1)