Coverage for src/lexigram/ai/cli/commands.py: 70%

43 statements  

« prev     ^ index     » next       coverage.py v7.15.4, created at 2026-08-25 07:19 +0800

1"""AI CLI command group factory.""" 

2 

3from __future__ import annotations 

4 

5import typer 

6 

7 

8def create_ai_app() -> typer.Typer: 

9 """Create the `lexigram ai` command group. 

10 

11 Returns: 

12 A Typer app containing AI subsystem management subcommands. 

13 """ 

14 app = typer.Typer(help="AI subsystem management commands") 

15 models_app = typer.Typer(help="Model management commands") 

16 providers_app = typer.Typer(help="Provider management commands") 

17 gateway_app = typer.Typer(help="AI relay gateway commands") 

18 

19 app.add_typer(models_app, name="models") 

20 app.add_typer(providers_app, name="providers") 

21 app.add_typer(gateway_app, name="gateway") 

22 

23 @gateway_app.command("check") 

24 def gateway_check( 

25 config: str = typer.Argument(..., help="Gateway config file (.json or .toml)"), 

26 ) -> None: 

27 """Validate a relay gateway configuration file.""" 

28 from lexigram.ai.cli.gateway import load_gateway_config 

29 

30 try: 

31 loaded = load_gateway_config(config) 

32 except ValueError as exc: 

33 typer.echo(f"gateway config invalid: {exc}", err=True) 

34 raise typer.Exit(code=1) from exc 

35 typer.echo( 

36 f"gateway config valid: {len(loaded.channels)} channel(s), " 

37 f"load_balancing={loaded.load_balancing}" 

38 ) 

39 

40 @gateway_app.command("serve") 

41 def gateway_serve( 

42 config: str = typer.Argument(..., help="Gateway config file (.json or .toml)"), 

43 host: str = typer.Option("127.0.0.1", "--host", help="Bind address"), 

44 port: int = typer.Option(8000, "--port", help="Bind port"), 

45 ) -> None: 

46 """Assemble and serve the relay gateway application.""" 

47 from lexigram.ai.cli.gateway import serve_gateway 

48 

49 try: 

50 serve_gateway(config, host=host, port=port) 

51 except (ValueError, TypeError, ModuleNotFoundError) as exc: 

52 typer.echo(f"cannot start gateway: {exc}", err=True) 

53 raise typer.Exit(code=1) from exc 

54 

55 @app.command("status") 

56 def ai_status() -> None: 

57 """Show AI subsystem status.""" 

58 typer.echo("AI subsystem status not yet implemented") 

59 

60 @models_app.command("list") 

61 def list_models( 

62 provider: str | None = typer.Option( 

63 None, "--provider", help="Filter by provider name" 

64 ), 

65 ) -> None: 

66 """List available AI models.""" 

67 typer.echo("AI model listing not yet implemented") 

68 

69 @providers_app.command("list") 

70 def list_providers() -> None: 

71 """List registered AI providers.""" 

72 typer.echo("AI provider listing not yet implemented") 

73 

74 @app.command("config") 

75 def config_show() -> None: 

76 """Show current AI configuration.""" 

77 typer.echo("AI configuration display not yet implemented") 

78 

79 @app.command("test") 

80 def test_provider( 

81 provider: str = typer.Argument(..., help="Provider name to test"), 

82 prompt: str = typer.Option( 

83 "Hello, world!", "--prompt", help="Test prompt to send" 

84 ), 

85 ) -> None: 

86 """Test a specific AI provider with a prompt.""" 

87 typer.echo(f"Testing provider {provider!r} not yet implemented") 

88 

89 return app