Coverage for .venv/lib/python3.13/site-packages/litellm/proxy/client/cli/main.py: 0%

91 statements  

« prev     ^ index     » next       coverage.py v7.15.2, created at 2026-10-10 12:01 +0000

1# stdlib imports 

2 

3# third party imports 

4from typing import Final 

5 

6import click 

7 

8from litellm._version import version as litellm_version 

9from litellm.proxy.client.health import HealthManagementClient 

10 

11from .commands.agents import agent_commands 

12from .commands.auth import ( 

13 CliContextObj, 

14 auth_group, 

15 context_secret_vault, 

16 get_stored_api_key, 

17 login, 

18 logout, 

19 whoami, 

20) 

21from .commands.autoroute.commands import autoroute_group 

22from .commands.chat import chat 

23from .commands.config import config_commands, get_config_value, hidden_command_names 

24from .commands.configure import configure_group, unconfigure_group 

25from .commands.credentials import credentials 

26from .commands.debug import debug 

27from .commands.encryption import encryption 

28from .commands.http import http 

29from .commands.keys import keys 

30from .commands.model_groups import model_groups 

31 

32# local imports 

33from .commands.models import models 

34from .commands.teams import teams 

35from .commands.up import down, up 

36from .commands.users import users 

37from .interface import interactive_shell 

38 

39 

40def print_version(base_url: str, api_key: str | None): 

41 """Print CLI and server version info.""" 

42 click.echo(f"LiteLLM Proxy CLI Version: {litellm_version}") 

43 if base_url: 

44 click.echo(f"LiteLLM Proxy Server URL: {base_url}") 

45 try: 

46 health_client: Final = HealthManagementClient(base_url=base_url, api_key=api_key) 

47 server_version: Final = health_client.get_server_version() 

48 if server_version: 

49 click.echo(f"LiteLLM Proxy Server Version: {server_version}") 

50 else: 

51 click.echo("LiteLLM Proxy Server Version: (unavailable)") 

52 except Exception as e: 

53 click.echo(f"Could not retrieve server version: {e}") 

54 

55 

56class HideConfiguredCommandsGroup(click.Group): 

57 """Group that omits operator-hidden commands from listings, still running them. 

58 

59 Deployments hand `lite` to users who should only see a curated subset of 

60 commands (`lite config set hidden_commands codex,opencode`). Filtering the 

61 listing rather than dropping the commands keeps anyone's existing scripts 

62 working. 

63 """ 

64 

65 def list_commands(self, ctx: click.Context) -> list[str]: 

66 hidden: Final = hidden_command_names() 

67 return [name for name in super().list_commands(ctx) if name not in hidden] 

68 

69 

70@click.group(cls=HideConfiguredCommandsGroup, invoke_without_command=True) 

71@click.option( 

72 "--version", 

73 "-v", 

74 "show_version", 

75 is_flag=True, 

76 help="Show the LiteLLM Proxy CLI and server version and exit.", 

77) 

78@click.option( 

79 "--base-url", 

80 envvar="LITELLM_PROXY_URL", 

81 show_envvar=True, 

82 default=None, 

83 show_default="base_url from `lite config`, else http://localhost:4000", 

84 help="Base URL of the LiteLLM proxy server", 

85) 

86@click.option( 

87 "--api-key", 

88 envvar="LITELLM_PROXY_API_KEY", 

89 show_envvar=True, 

90 help="API key for authentication", 

91) 

92@click.pass_context 

93def cli(ctx: click.Context, show_version: bool, base_url: str | None, api_key: str | None) -> None: 

94 """LiteLLM Proxy CLI - Manage your LiteLLM proxy server""" 

95 ctx.ensure_object(dict) 

96 

97 stored_base_url: Final = get_config_value("base_url") 

98 base_url_provided: Final = base_url is not None 

99 

100 # Normalize once here so every downstream command (login, agents, http, ...) can safely 

101 # do f"{base_url}/some/path" without producing a double slash. 

102 base_url = ((stored_base_url or "http://localhost:4000") if base_url is None else base_url).rstrip("/") 

103 

104 # If no API key provided via flag or environment variable, try to load from saved token. 

105 # Pass base_url so we only use the stored key when it was issued for this server. 

106 api_key_from_token_file: Final = api_key is None and ctx.invoked_subcommand not in ("configure", "unconfigure") 

107 resolved_api_key: Final = ( 

108 get_stored_api_key(expected_base_url=base_url, vault=context_secret_vault(ctx)) 

109 if api_key_from_token_file 

110 else api_key 

111 ) 

112 

113 ctx.obj["base_url"] = base_url 

114 ctx.obj["api_key"] = resolved_api_key 

115 ctx.obj["api_key_from_token_file"] = api_key_from_token_file 

116 # `--base-url` defaults to localhost:4000 for local dev convenience, but 

117 # apiKeyHelper is invoked bare (no flags) -- commands that must work 

118 # unattended (print-token) need to tell "user didn't say" apart from 

119 # "user said localhost:4000 on purpose" so they can fall back to 

120 # whatever server the stored token was actually issued for. A base_url 

121 # saved via `lite config set` counts as the user saying it. 

122 ctx.obj["base_url_explicit"] = base_url_provided or bool(stored_base_url) 

123 

124 if show_version: 

125 print_version(base_url, resolved_api_key) 

126 ctx.exit() 

127 

128 # If no subcommand was invoked, start interactive mode 

129 if ctx.invoked_subcommand is None: 

130 interactive_shell(ctx) 

131 

132 

133@cli.command() 

134@click.pass_context 

135def version(ctx: click.Context): 

136 """Show the LiteLLM Proxy CLI and server version.""" 

137 ctx_obj: Final[CliContextObj] = ctx.obj 

138 print_version(ctx_obj.get("base_url"), ctx_obj.get("api_key")) 

139 

140 

141# Add authentication commands as top-level commands 

142cli.add_command(login) 

143cli.add_command(logout) 

144cli.add_command(whoami) 

145# Add the auth command group (e.g. `lite auth print-token`, used as Claude Code's apiKeyHelper) 

146cli.add_command(auth_group, name="auth") 

147# Add the models command group 

148cli.add_command(models) 

149# Add the credentials command group 

150cli.add_command(credentials) 

151# Add the encryption migration command group 

152cli.add_command(encryption) 

153# Add the chat command group 

154cli.add_command(chat) 

155# Add the http command group 

156cli.add_command(http) 

157cli.add_command(debug) 

158# Add the keys command group 

159cli.add_command(keys) 

160# Add the teams command group 

161cli.add_command(teams) 

162# Add the users command group 

163cli.add_command(users) 

164# Add a top-level command per coding agent (claude, codex, opencode, ...) 

165for agent_command in agent_commands(): 

166 cli.add_command(agent_command) 

167# Add the up/down commands (route Claude Code through the local LiteLLM proxy) 

168cli.add_command(up) 

169cli.add_command(down) 

170# Add the model-groups command group (discover models your key can access) 

171cli.add_command(model_groups) 

172# Add the autoroute command group (QA auto-routing against your real proxy) 

173cli.add_command(autoroute_group, name="autoroute") 

174cli.add_command(config_commands) 

175# Add configure/unconfigure (persistently wire a coding agent to the proxy with a virtual key) 

176cli.add_command(configure_group) 

177cli.add_command(unconfigure_group) 

178 

179 

180LITELLM_PROXY_DEPRECATION_NOTICE: Final = ( 

181 "The `litellm-proxy` command is deprecated and will be removed in a future release; " 

182 "run `lite` instead, it takes the same commands and options." 

183) 

184 

185 

186def litellm_proxy_cli() -> None: 

187 click.secho(LITELLM_PROXY_DEPRECATION_NOTICE, err=True, fg="yellow") 

188 cli() 

189 

190 

191if __name__ == "__main__": 

192 cli()