|
9 | 9 | from typing import Dict, List, Optional, Tuple |
10 | 10 |
|
11 | 11 | import typer |
12 | | -from langserve.packages import get_langserve_export |
13 | 12 | from typing_extensions import Annotated |
14 | 13 |
|
15 | 14 | from langchain_cli.utils.events import create_events |
|
19 | 18 | parse_dependencies, |
20 | 19 | update_repo, |
21 | 20 | ) |
22 | | -from langchain_cli.utils.packages import get_package_root |
| 21 | +from langchain_cli.utils.packages import ( |
| 22 | + LangServeExport, |
| 23 | + get_langserve_export, |
| 24 | + get_package_root, |
| 25 | +) |
| 26 | +from langchain_cli.utils.pyproject import ( |
| 27 | + add_dependencies_to_pyproject_toml, |
| 28 | + remove_dependencies_from_pyproject_toml, |
| 29 | +) |
23 | 30 |
|
24 | 31 | REPO_DIR = Path(typer.get_app_dir("langchain")) / "git_repos" |
25 | 32 |
|
@@ -98,7 +105,8 @@ def add( |
98 | 105 | grouped[key_tup] = lst |
99 | 106 |
|
100 | 107 | installed_destination_paths: List[Path] = [] |
101 | | - installed_exports: List[Dict] = [] |
| 108 | + installed_destination_names: List[str] = [] |
| 109 | + installed_exports: List[LangServeExport] = [] |
102 | 110 |
|
103 | 111 | for (git, ref), group_deps in grouped.items(): |
104 | 112 | if len(group_deps) == 1: |
@@ -131,23 +139,38 @@ def add( |
131 | 139 | copy_repo(source_path, destination_path) |
132 | 140 | typer.echo(f" - Downloaded {dep['subdirectory']} to {inner_api_path}") |
133 | 141 | installed_destination_paths.append(destination_path) |
| 142 | + installed_destination_names.append(inner_api_path) |
134 | 143 | installed_exports.append(langserve_export) |
135 | 144 |
|
136 | 145 | if len(installed_destination_paths) == 0: |
137 | 146 | typer.echo("No packages installed. Exiting.") |
138 | 147 | return |
139 | 148 |
|
140 | | - cwd = Path.cwd() |
141 | | - installed_desination_strs = [ |
142 | | - str(p.relative_to(cwd)) for p in installed_destination_paths |
143 | | - ] |
144 | | - cmd = ["pip", "install", "-e"] + installed_desination_strs |
145 | | - cmd_str = " \\\n ".join(installed_desination_strs) |
146 | | - install_str = f"To install:\n\npip install -e \\\n {cmd_str}" |
147 | | - typer.echo(install_str) |
148 | | - |
149 | | - if typer.confirm("Run it?"): |
150 | | - subprocess.run(cmd, cwd=cwd) |
| 149 | + try: |
| 150 | + add_dependencies_to_pyproject_toml( |
| 151 | + project_root / "pyproject.toml", |
| 152 | + zip(installed_destination_names, installed_destination_paths), |
| 153 | + ) |
| 154 | + except Exception: |
| 155 | + # Can fail if user modified/removed pyproject.toml |
| 156 | + typer.echo("Failed to add dependencies to pyproject.toml, continuing...") |
| 157 | + |
| 158 | + try: |
| 159 | + cwd = Path.cwd() |
| 160 | + installed_destination_strs = [ |
| 161 | + str(p.relative_to(cwd)) for p in installed_destination_paths |
| 162 | + ] |
| 163 | + except ValueError: |
| 164 | + # Can fail if the cwd is not a parent of the package |
| 165 | + typer.echo("Failed to print install command, continuing...") |
| 166 | + else: |
| 167 | + cmd = ["pip", "install", "-e"] + installed_destination_strs |
| 168 | + cmd_str = " \\\n ".join(installed_destination_strs) |
| 169 | + install_str = f"To install:\n\npip install -e \\\n {cmd_str}" |
| 170 | + typer.echo(install_str) |
| 171 | + |
| 172 | + if typer.confirm("Run it?"): |
| 173 | + subprocess.run(cmd, cwd=cwd) |
151 | 174 |
|
152 | 175 | if typer.confirm("\nGenerate route code for these packages?", default=True): |
153 | 176 | chain_names = [] |
@@ -187,20 +210,43 @@ def add( |
187 | 210 | @app_cli.command() |
188 | 211 | def remove( |
189 | 212 | api_paths: Annotated[List[str], typer.Argument(help="The API paths to remove")], |
| 213 | + *, |
| 214 | + project_dir: Annotated[ |
| 215 | + Optional[Path], typer.Option(help="The project directory") |
| 216 | + ] = None, |
190 | 217 | ): |
191 | 218 | """ |
192 | 219 | Removes the specified package from the current LangServe app. |
193 | 220 | """ |
| 221 | + |
| 222 | + project_root = get_package_root(project_dir) |
| 223 | + |
| 224 | + project_pyproject = project_root / "pyproject.toml" |
| 225 | + |
| 226 | + package_root = project_root / "packages" |
| 227 | + |
| 228 | + remove_deps: List[str] = [] |
| 229 | + |
194 | 230 | for api_path in api_paths: |
195 | | - package_dir = Path.cwd() / "packages" / api_path |
| 231 | + package_dir = package_root / api_path |
196 | 232 | if not package_dir.exists(): |
197 | | - typer.echo(f"Endpoint {api_path} does not exist. Skipping...") |
| 233 | + typer.echo(f"Package {api_path} does not exist. Skipping...") |
198 | 234 | continue |
199 | | - pyproject = package_dir / "pyproject.toml" |
200 | | - langserve_export = get_langserve_export(pyproject) |
201 | | - typer.echo(f"Removing {langserve_export['package_name']}...") |
202 | | - |
203 | | - shutil.rmtree(package_dir) |
| 235 | + try: |
| 236 | + pyproject = package_dir / "pyproject.toml" |
| 237 | + langserve_export = get_langserve_export(pyproject) |
| 238 | + typer.echo(f"Removing {langserve_export['package_name']}...") |
| 239 | + |
| 240 | + shutil.rmtree(package_dir) |
| 241 | + remove_deps.append(api_path) |
| 242 | + except Exception: |
| 243 | + pass |
| 244 | + |
| 245 | + try: |
| 246 | + remove_dependencies_from_pyproject_toml(project_pyproject, remove_deps) |
| 247 | + except Exception: |
| 248 | + # Can fail if user modified/removed pyproject.toml |
| 249 | + typer.echo("Failed to remove dependencies from pyproject.toml.") |
204 | 250 |
|
205 | 251 |
|
206 | 252 | @app_cli.command() |
|
0 commit comments