Skip to content

Commit 98d46e9

Browse files
authored
feat: support --env (#1167)
Signed-off-by: Aaron Pham <contact@aarnphm.xyz>
1 parent 2729cfe commit 98d46e9

File tree

3 files changed

+41
-16
lines changed

3 files changed

+41
-16
lines changed

README.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,6 @@ OpenLLM supports a wide range of state-of-the-art open-source LLMs. You can also
122122
</tr>
123123
</table>
124124

125-
126125
For the full model list, see the [OpenLLM models repository](https://github.com/bentoml/openllm-models).
127126

128127
## Start an LLM server
@@ -252,7 +251,7 @@ OpenLLM supports LLM cloud deployment via BentoML, the unified model serving fra
252251
[Sign up for BentoCloud](https://www.bentoml.com/) for free and [log in](https://docs.bentoml.com/en/latest/bentocloud/how-tos/manage-access-token.html). Then, run `openllm deploy` to deploy a model to BentoCloud:
253252

254253
```bash
255-
openllm deploy llama3.2:1b
254+
openllm deploy llama3.2:1b --env HF_TOKEN
256255
```
257256

258257
> [!NOTE]

src/openllm/__main__.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -240,12 +240,13 @@ def deploy(
240240
instance_type: typing.Optional[str] = None,
241241
repo: typing.Optional[str] = None,
242242
verbose: bool = False,
243+
env: typing.Optional[list[str]] = typer.Option(None, "--env", help="Environment variables to pass to the deployment command. Format: NAME or NAME=value. Can be specified multiple times.")
243244
) -> None:
244245
if verbose:
245246
VERBOSE_LEVEL.set(20)
246247
bento = ensure_bento(model, repo_name=repo)
247248
if instance_type is not None:
248-
return cloud_deploy(bento, DeploymentTarget(accelerators=[], name=instance_type))
249+
return cloud_deploy(bento, DeploymentTarget(accelerators=[], name=instance_type), cli_envs=env)
249250
targets = sorted(
250251
filter(lambda x: can_run(bento, x) > 0, get_cloud_machine_spec()),
251252
key=lambda x: can_run(bento, x),
@@ -256,7 +257,7 @@ def deploy(
256257
raise typer.Exit(1)
257258
target = targets[0]
258259
output(f'Recommended instance type: {target.name}', style='green')
259-
cloud_deploy(bento, target)
260+
cloud_deploy(bento, target, cli_envs=env)
260261

261262

262263
@app.callback(invoke_without_command=True)

src/openllm/cloud.py

Lines changed: 37 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,22 +17,43 @@ def resolve_cloud_config() -> pathlib.Path:
1717
return pathlib.Path.home() / 'bentoml' / '.yatai.yaml'
1818

1919

20-
def _get_deploy_cmd(bento: BentoInfo, target: typing.Optional[DeploymentTarget] = None) -> tuple[list[str], EnvVars]:
20+
def _get_deploy_cmd(
21+
bento: BentoInfo, target: typing.Optional[DeploymentTarget] = None, cli_envs: typing.Optional[list[str]] = None
22+
) -> tuple[list[str], EnvVars]:
2123
cmd = ['bentoml', 'deploy', bento.bentoml_tag]
2224
env = EnvVars({'BENTOML_HOME': f'{bento.repo.path}/bentoml'})
2325

26+
# Process CLI env vars first to determine overrides
27+
explicit_envs: dict[str, str] = {}
28+
if cli_envs:
29+
for env_var in cli_envs:
30+
if '=' in env_var:
31+
name, value = env_var.split('=', 1)
32+
explicit_envs[name] = value
33+
else:
34+
name = env_var
35+
value = typing.cast(str, os.environ.get(name))
36+
if value is None:
37+
output(f'Environment variable \'{name}\' specified via --env but not found in the current environment.', style='red')
38+
raise typer.Exit(1)
39+
explicit_envs[name] = value
40+
41+
# Process envs defined in bento.yaml, skipping those overridden by CLI
2442
required_envs = bento.bento_yaml.get('envs', [])
25-
required_env_names = [env['name'] for env in required_envs if 'name' in env]
43+
required_env_names = [env['name'] for env in required_envs if 'name' in env and env['name'] not in explicit_envs]
2644
if required_env_names:
2745
output(
28-
f'This model requires the following environment variables to run: {required_env_names!r}', style='yellow'
46+
f'This model requires the following environment variables to run (unless overridden via --env): {required_env_names!r}',
47+
style='yellow',
2948
)
3049

31-
for env_info in bento.bento_yaml.get('envs', []):
32-
if 'name' not in env_info:
50+
for env_info in required_envs:
51+
name = typing.cast(str, env_info.get('name'))
52+
if not name or name in explicit_envs:
3353
continue
34-
if os.environ.get(env_info['name']):
35-
default = os.environ[env_info['name']]
54+
55+
if os.environ.get(name):
56+
default = os.environ[name]
3657
elif 'value' in env_info:
3758
default = env_info['value']
3859
else:
@@ -41,17 +62,21 @@ def _get_deploy_cmd(bento: BentoInfo, target: typing.Optional[DeploymentTarget]
4162
if INTERACTIVE.get():
4263
import questionary
4364

44-
value = questionary.text(f'{env_info["name"]}:', default=default).ask()
65+
value = questionary.text(f'{name}: (from bento.yaml)', default=default).ask()
4566
else:
4667
if default == '':
47-
output(f'Environment variable {env_info["name"]} is required but not provided', style='red')
68+
output(f'Environment variable {name} (from bento.yaml) is required but not provided', style='red')
4869
raise typer.Exit(1)
4970
else:
5071
value = default
5172

5273
if value is None:
5374
raise typer.Exit(1)
54-
cmd += ['--env', f'{env_info["name"]}={value}']
75+
cmd += ['--env', f'{name}={value}']
76+
77+
# Add explicitly provided env vars from CLI
78+
for name, value in explicit_envs.items():
79+
cmd += ['--env', f'{name}={value}']
5580

5681
if target:
5782
cmd += ['--instance-type', target.name]
@@ -134,7 +159,7 @@ def get_cloud_machine_spec() -> list[DeploymentTarget]:
134159
return []
135160

136161

137-
def deploy(bento: BentoInfo, target: DeploymentTarget) -> None:
162+
def deploy(bento: BentoInfo, target: DeploymentTarget, cli_envs: typing.Optional[list[str]] = None) -> None:
138163
ensure_cloud_context()
139-
cmd, env = _get_deploy_cmd(bento, target)
164+
cmd, env = _get_deploy_cmd(bento, target, cli_envs=cli_envs)
140165
run_command(cmd, env=env, cwd=None)

0 commit comments

Comments
 (0)