Skip to content

Commit 5a3d777

Browse files
authored
feat: add llama stack rm command (#2127)
# What does this PR do? [Provide a short summary of what this PR does and why. Link to relevant issues if applicable.] ``` llama stack rm llamastack-test ``` [//]: # (If resolving an issue, uncomment and update the line below) [//]: # (Closes #[issue-number]) #225 ## Test Plan [Describe the tests you ran to verify your changes with result summaries. *Provide clear instructions so the plan can be easily re-executed.*] [//]: # (## Documentation)
1 parent 091d8c4 commit 5a3d777

File tree

4 files changed

+218
-1
lines changed

4 files changed

+218
-1
lines changed

docs/source/distributions/building_distro.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,48 @@ INFO: Application startup complete.
338338
INFO: Uvicorn running on http://['::', '0.0.0.0']:8321 (Press CTRL+C to quit)
339339
INFO: 2401:db00:35c:2d2b:face:0:c9:0:54678 - "GET /models/list HTTP/1.1" 200 OK
340340
```
341+
### Listing Distributions
342+
Using the list command, you can view all existing Llama Stack distributions, including stacks built from templates, from scratch, or using custom configuration files.
343+
344+
```
345+
llama stack list -h
346+
usage: llama stack list [-h]
347+
348+
list the build stacks
349+
350+
options:
351+
-h, --help show this help message and exit
352+
```
353+
354+
Example Usage
355+
356+
```
357+
llama stack list
358+
```
359+
360+
### Removing a Distribution
361+
Use the remove command to delete a distribution you've previously built.
362+
363+
```
364+
llama stack rm -h
365+
usage: llama stack rm [-h] [--all] [name]
366+
367+
Remove the build stack
368+
369+
positional arguments:
370+
name Name of the stack to delete (default: None)
371+
372+
options:
373+
-h, --help show this help message and exit
374+
--all, -a Delete all stacks (use with caution) (default: False)
375+
```
376+
377+
Example
378+
```
379+
llama stack rm llamastack-test
380+
```
381+
382+
To keep your environment organized and avoid clutter, consider using `llama stack list` to review old or unused distributions and `llama stack rm <name>` to delete them when they’re no longer needed.
341383

342384
### Troubleshooting
343385

llama_stack/cli/stack/list_stacks.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# Copyright (c) Meta Platforms, Inc. and affiliates.
2+
# All rights reserved.
3+
#
4+
# This source code is licensed under the terms described in the LICENSE file in
5+
# the root directory of this source tree.
6+
7+
import argparse
8+
from pathlib import Path
9+
10+
from llama_stack.cli.subcommand import Subcommand
11+
from llama_stack.cli.table import print_table
12+
13+
14+
class StackListBuilds(Subcommand):
15+
"""List built stacks in .llama/distributions directory"""
16+
17+
def __init__(self, subparsers: argparse._SubParsersAction):
18+
super().__init__()
19+
self.parser = subparsers.add_parser(
20+
"list",
21+
prog="llama stack list",
22+
description="list the build stacks",
23+
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
24+
)
25+
self._add_arguments()
26+
self.parser.set_defaults(func=self._list_stack_command)
27+
28+
def _get_distribution_dirs(self) -> dict[str, Path]:
29+
"""Return a dictionary of distribution names and their paths"""
30+
distributions = {}
31+
dist_dir = Path.home() / ".llama" / "distributions"
32+
33+
if dist_dir.exists():
34+
for stack_dir in dist_dir.iterdir():
35+
if stack_dir.is_dir():
36+
distributions[stack_dir.name] = stack_dir
37+
return distributions
38+
39+
def _list_stack_command(self, args: argparse.Namespace) -> None:
40+
distributions = self._get_distribution_dirs()
41+
42+
if not distributions:
43+
print("No stacks found in ~/.llama/distributions")
44+
return
45+
46+
headers = ["Stack Name", "Path"]
47+
headers.extend(["Build Config", "Run Config"])
48+
rows = []
49+
for name, path in distributions.items():
50+
row = [name, str(path)]
51+
# Check for build and run config files
52+
build_config = "Yes" if (path / f"{name}-build.yaml").exists() else "No"
53+
run_config = "Yes" if (path / f"{name}-run.yaml").exists() else "No"
54+
row.extend([build_config, run_config])
55+
rows.append(row)
56+
print_table(rows, headers, separate_rows=True)

llama_stack/cli/stack/remove.py

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
# Copyright (c) Meta Platforms, Inc. and affiliates.
2+
# All rights reserved.
3+
#
4+
# This source code is licensed under the terms described in the LICENSE file in
5+
# the root directory of this source tree.
6+
7+
import argparse
8+
import shutil
9+
import sys
10+
from pathlib import Path
11+
12+
from termcolor import cprint
13+
14+
from llama_stack.cli.subcommand import Subcommand
15+
from llama_stack.cli.table import print_table
16+
17+
18+
class StackRemove(Subcommand):
19+
"""Remove the build stack"""
20+
21+
def __init__(self, subparsers: argparse._SubParsersAction):
22+
super().__init__()
23+
self.parser = subparsers.add_parser(
24+
"rm",
25+
prog="llama stack rm",
26+
description="Remove the build stack",
27+
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
28+
)
29+
self._add_arguments()
30+
self.parser.set_defaults(func=self._remove_stack_build_command)
31+
32+
def _add_arguments(self) -> None:
33+
self.parser.add_argument(
34+
"name",
35+
type=str,
36+
nargs="?",
37+
help="Name of the stack to delete",
38+
)
39+
self.parser.add_argument(
40+
"--all",
41+
"-a",
42+
action="store_true",
43+
help="Delete all stacks (use with caution)",
44+
)
45+
46+
def _get_distribution_dirs(self) -> dict[str, Path]:
47+
"""Return a dictionary of distribution names and their paths"""
48+
distributions = {}
49+
dist_dir = Path.home() / ".llama" / "distributions"
50+
51+
if dist_dir.exists():
52+
for stack_dir in dist_dir.iterdir():
53+
if stack_dir.is_dir():
54+
distributions[stack_dir.name] = stack_dir
55+
return distributions
56+
57+
def _list_stacks(self) -> None:
58+
"""Display available stacks in a table"""
59+
distributions = self._get_distribution_dirs()
60+
if not distributions:
61+
print("No stacks found in ~/.llama/distributions")
62+
return
63+
64+
headers = ["Stack Name", "Path"]
65+
rows = [[name, str(path)] for name, path in distributions.items()]
66+
print_table(rows, headers, separate_rows=True)
67+
68+
def _remove_stack_build_command(self, args: argparse.Namespace) -> None:
69+
distributions = self._get_distribution_dirs()
70+
71+
if args.all:
72+
confirm = input("Are you sure you want to delete ALL stacks? [yes-i-really-want/N] ").lower()
73+
if confirm != "yes-i-really-want":
74+
print("Deletion cancelled.")
75+
return
76+
77+
for name, path in distributions.items():
78+
try:
79+
shutil.rmtree(path)
80+
print(f"Deleted stack: {name}")
81+
except Exception as e:
82+
cprint(
83+
f"Failed to delete stack {name}: {e}",
84+
color="red",
85+
)
86+
sys.exit(2)
87+
88+
if not args.name:
89+
self._list_stacks()
90+
if not args.name:
91+
return
92+
93+
if args.name not in distributions:
94+
self._list_stacks()
95+
cprint(
96+
f"Stack not found: {args.name}",
97+
color="red",
98+
)
99+
return
100+
101+
stack_path = distributions[args.name]
102+
103+
confirm = input(f"Are you sure you want to delete stack '{args.name}'? [y/N] ").lower()
104+
if confirm != "y":
105+
print("Deletion cancelled.")
106+
return
107+
108+
try:
109+
shutil.rmtree(stack_path)
110+
print(f"Successfully deleted stack: {args.name}")
111+
except Exception as e:
112+
cprint(
113+
f"Failed to delete stack {args.name}: {e}",
114+
color="red",
115+
)
116+
sys.exit(2)

llama_stack/cli/stack/stack.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,14 @@
77
import argparse
88
from importlib.metadata import version
99

10+
from llama_stack.cli.stack.list_stacks import StackListBuilds
1011
from llama_stack.cli.stack.utils import print_subcommand_description
1112
from llama_stack.cli.subcommand import Subcommand
1213

1314
from .build import StackBuild
1415
from .list_apis import StackListApis
1516
from .list_providers import StackListProviders
17+
from .remove import StackRemove
1618
from .run import StackRun
1719

1820

@@ -41,5 +43,6 @@ def __init__(self, subparsers: argparse._SubParsersAction):
4143
StackListApis.create(subparsers)
4244
StackListProviders.create(subparsers)
4345
StackRun.create(subparsers)
44-
46+
StackRemove.create(subparsers)
47+
StackListBuilds.create(subparsers)
4548
print_subcommand_description(self.parser, subparsers)

0 commit comments

Comments
 (0)