From 7c3066f9ec15d74b9b1b6d97e041027e2f4e6fc2 Mon Sep 17 00:00:00 2001 From: Erick Friis Date: Tue, 14 Nov 2023 14:49:43 -0800 Subject: [PATCH] more cli interactivity, bugfix (#13360) --- libs/cli/langchain_cli/namespaces/app.py | 56 +++++++++++++++---- libs/cli/langchain_cli/namespaces/template.py | 12 ++++ libs/cli/langchain_cli/utils/github.py | 29 ++++++++++ 3 files changed, 86 insertions(+), 11 deletions(-) create mode 100644 libs/cli/langchain_cli/utils/github.py diff --git a/libs/cli/langchain_cli/namespaces/app.py b/libs/cli/langchain_cli/namespaces/app.py index ae3c38cabe3..5d60f672dff 100644 --- a/libs/cli/langchain_cli/namespaces/app.py +++ b/libs/cli/langchain_cli/namespaces/app.py @@ -36,12 +36,11 @@ app_cli = typer.Typer(no_args_is_help=True, add_completion=False) @app_cli.command() def new( name: Annotated[ - str, - typer.Option( + Optional[str], + typer.Argument( help="The name of the folder to create", - prompt="What folder would you like to create?", ), - ], + ] = None, *, package: Annotated[ Optional[List[str]], @@ -55,21 +54,56 @@ def new( is_flag=True, ), ] = None, + noninteractive: Annotated[ + bool, + typer.Option( + "--non-interactive/--interactive", + help="Don't prompt for any input", + is_flag=True, + ), + ] = False, ): """ Create a new LangServe application. """ has_packages = package is not None and len(package) > 0 - pip_bool = False - if pip is None and has_packages: - pip_bool = typer.confirm( - "Would you like to `pip install -e` the template(s)?", - default=False, + + if noninteractive: + if name is None: + raise typer.BadParameter("name is required when --non-interactive is set") + name_str = name + pip_bool = bool(pip) # None should be false + else: + name_str = ( + name if name else typer.prompt("What folder would you like to create?") ) + if not has_packages: + package = [] + package_prompt = "What package would you like to add? (leave blank to skip)" + while True: + package_str = typer.prompt( + package_prompt, default="", show_default=False + ) + if not package_str: + break + package.append(package_str) + package_prompt = ( + f"{len(package)} added. Any more packages (leave blank to end)?" + ) + + has_packages = len(package) > 0 + + pip_bool = False + if pip is None and has_packages: + pip_bool = typer.confirm( + "Would you like to install these templates into your environment " + "with pip?", + default=False, + ) # copy over template from ../project_template project_template_dir = Path(__file__).parents[1] / "project_template" - destination_dir = Path.cwd() / name if name != "." else Path.cwd() - app_name = name if name != "." else Path.cwd().name + destination_dir = Path.cwd() / name_str if name_str != "." else Path.cwd() + app_name = name_str if name_str != "." else Path.cwd().name shutil.copytree(project_template_dir, destination_dir, dirs_exist_ok=name == ".") readme = destination_dir / "README.md" diff --git a/libs/cli/langchain_cli/namespaces/template.py b/libs/cli/langchain_cli/namespaces/template.py index 507e9f88d2c..a7ce0a0545d 100644 --- a/libs/cli/langchain_cli/namespaces/template.py +++ b/libs/cli/langchain_cli/namespaces/template.py @@ -129,3 +129,15 @@ def serve( port=port if port is not None else 8000, host=host_str, ) + + +@package_cli.command() +def list(contains: Annotated[Optional[str], typer.Argument()] = None) -> None: + """ + List all or search for available templates. + """ + from langchain_cli.utils.github import list_packages + + packages = list_packages(contains=contains) + for package in packages: + typer.echo(package) diff --git a/libs/cli/langchain_cli/utils/github.py b/libs/cli/langchain_cli/utils/github.py new file mode 100644 index 00000000000..edaea5525d7 --- /dev/null +++ b/libs/cli/langchain_cli/utils/github.py @@ -0,0 +1,29 @@ +import http.client +import json +from typing import Optional + + +def list_packages(*, contains: Optional[str] = None): + conn = http.client.HTTPSConnection("api.github.com") + + headers = { + "Accept": "application/vnd.github+json", + "X-GitHub-Api-Version": "2022-11-28", + "User-Agent": "langchain-cli", + } + + conn.request( + "GET", "/repos/langchain-ai/langchain/contents/templates", headers=headers + ) + res = conn.getresponse() + + res_str = res.read() + + data = json.loads(res_str) + package_names = [ + p["name"] for p in data if p["type"] == "dir" and p["name"] != "docs" + ] + package_names_filtered = ( + [p for p in package_names if contains in p] if contains else package_names + ) + return package_names_filtered