Skip to content

Commit

Permalink
cli: Add back up (#854)
Browse files Browse the repository at this point in the history
* Revert "cli: Reduce to test and build commands (#838)"

This reverts commit 5697f07.

* Undo

* Undo

* Undo

* Fix

* Fix

* Add license key admonition

* Quote env vars

* Test
  • Loading branch information
nfcampos authored Jun 27, 2024
1 parent 7973df6 commit 4768bdb
Show file tree
Hide file tree
Showing 7 changed files with 703 additions and 20 deletions.
200 changes: 200 additions & 0 deletions libs/cli/langgraph_cli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,24 @@
import langgraph_cli.config
import langgraph_cli.docker
from langgraph_cli.analytics import log_command
from langgraph_cli.config import Config
from langgraph_cli.constants import DEFAULT_CONFIG, DEFAULT_PORT
from langgraph_cli.docker import DockerCapabilities
from langgraph_cli.exec import Runner, subp_exec
from langgraph_cli.progress import Progress

OPT_DOCKER_COMPOSE = click.option(
"--docker-compose",
"-d",
help="Advanced: Path to docker-compose.yml file with additional services to launch.",
type=click.Path(
exists=True,
file_okay=True,
dir_okay=False,
resolve_path=True,
path_type=pathlib.Path,
),
)
OPT_CONFIG = click.option(
"--config",
"-c",
Expand Down Expand Up @@ -89,6 +103,12 @@
\b
""",
)
OPT_RECREATE = click.option(
"--recreate/--no-recreate",
default=False,
show_default=True,
help="Recreate containers even if their configuration and image haven't changed",
)
OPT_PULL = click.option(
"--pull/--no-pull",
default=True,
Expand All @@ -108,13 +128,118 @@
default=False,
help="Show more output from the server logs",
)
OPT_WATCH = click.option("--watch", is_flag=True, help="Restart on file changes")
OPT_DEBUGGER_PORT = click.option(
"--debugger-port",
type=int,
help="Pull the debugger image locally and serve the UI on specified port",
)
OPT_POSTGRES_URI = click.option(
"--postgres-uri",
help="Postgres URI to use for the database. Defaults to launching a local database",
)


@click.group()
def cli():
pass


@OPT_RECREATE
@OPT_PULL
@OPT_PORT
@OPT_DOCKER_COMPOSE
@OPT_CONFIG
@OPT_VERBOSE
@OPT_DEBUGGER_PORT
@OPT_WATCH
@OPT_POSTGRES_URI
@click.option(
"--wait",
is_flag=True,
help="Wait for services to start before returning. Implies --detach",
)
@cli.command(help="Start langgraph API server. Requires a license key.")
@log_command
def up(
config: pathlib.Path,
docker_compose: Optional[pathlib.Path],
port: int,
recreate: bool,
pull: bool,
watch: bool,
wait: bool,
verbose: bool,
debugger_port: Optional[int],
postgres_uri: Optional[str],
):
with Runner() as runner, Progress(message="Pulling...") as set:
capabilities = langgraph_cli.docker.check_capabilities(runner)
args, stdin = prepare(
runner,
capabilities=capabilities,
config_path=config,
docker_compose=docker_compose,
port=port,
pull=pull,
watch=watch,
verbose=verbose,
debugger_port=debugger_port,
postgres_uri=postgres_uri,
)
# add up + options
args.extend(["up", "--remove-orphans"])
if recreate:
args.extend(["--force-recreate", "--renew-anon-volumes"])
try:
runner.run(subp_exec("docker", "volume", "rm", "langgraph-data"))
except click.exceptions.Exit:
pass
if watch:
args.append("--watch")
if wait:
args.append("--wait")
else:
args.append("--abort-on-container-exit")
# run docker compose
set("Building...")

def on_stdout(line: str):
if "unpacking to docker.io" in line:
set("Starting...")
elif "GET /ok" in line:
debugger_origin = (
f"http://localhost:{debugger_port}"
if debugger_port
else "https://smith.langchain.com"
)
set("")
sys.stdout.write(
f"""Ready!
- API: http://localhost:{port}
- Docs: http://localhost:{port}/docs
- Debugger: {debugger_origin}/studio/?baseUrl=http://127.0.0.1:{port}
"""
)
sys.stdout.flush()
return True

if capabilities.compose_type == "plugin":
compose_cmd = ["docker", "compose"]
elif capabilities.compose_type == "standalone":
compose_cmd = ["docker-compose"]

runner.run(
subp_exec(
*compose_cmd,
*args,
input=stdin,
verbose=verbose,
on_stdout=on_stdout,
)
)


@OPT_PULL
@OPT_PORT
@OPT_CONFIG
Expand Down Expand Up @@ -304,3 +429,78 @@ def build(
config_json = langgraph_cli.config.validate_config(json.load(f))
# build
_build(runner, set, config, config_json, platform, base_image, pull, tag)


def prepare_args_and_stdin(
*,
capabilities: DockerCapabilities,
config_path: pathlib.Path,
config: Config,
docker_compose: Optional[pathlib.Path],
port: int,
watch: bool,
debugger_port: Optional[int] = None,
postgres_uri: Optional[str] = None,
):
# prepare args
stdin = langgraph_cli.docker.compose(
capabilities,
port=port,
debugger_port=debugger_port,
postgres_uri=postgres_uri,
)
args = [
"--project-directory",
str(config_path.parent),
]
# apply options
if docker_compose:
args.extend(["-f", str(docker_compose)])
args.extend(["-f", "-"]) # stdin
# apply config
stdin += langgraph_cli.config.config_to_compose(
config_path,
config,
watch=watch,
base_image="langchain/langgraph-api",
)
return args, stdin


def prepare(
runner,
*,
capabilities: DockerCapabilities,
config_path: pathlib.Path,
docker_compose: Optional[pathlib.Path],
port: int,
pull: bool,
watch: bool,
verbose: bool,
debugger_port: Optional[int] = None,
postgres_uri: Optional[str] = None,
):
with open(config_path) as f:
config = langgraph_cli.config.validate_config(json.load(f))
# pull latest images
if pull:
runner.run(
subp_exec(
"docker",
"pull",
f"langchain/langgraph-api:{config['python_version']}",
verbose=verbose,
)
)

args, stdin = prepare_args_and_stdin(
capabilities=capabilities,
config_path=config_path,
config=config,
docker_compose=docker_compose,
port=port,
watch=watch,
debugger_port=debugger_port,
postgres_uri=postgres_uri,
)
return args, stdin
17 changes: 4 additions & 13 deletions libs/cli/langgraph_cli/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -265,30 +265,21 @@ def config_to_compose(
config: Config,
base_image: str,
watch: bool = False,
langgraph_api_path: Optional[pathlib.Path] = None,
):
env_vars = config["env"].items() if isinstance(config["env"], dict) else {}
env_vars_str = "\n".join(f" {k}: {v}" for k, v in env_vars)
env_vars_str = "\n".join(f' {k}: "{v}"' for k, v in env_vars)
env_file_str = (
f"env_file: {config['env']}" if isinstance(config["env"], str) else ""
)
if watch:
watch_paths = [config_path] + [
config_path.parent / dep
for dep in config["dependencies"]
if dep.startswith(".")
watch_paths = [config_path.name] + [
dep for dep in config["dependencies"] if dep.startswith(".")
]
watch_actions = "\n".join(
f"""- path: {path}
action: rebuild
ignore:
- .langgraph-data"""
action: rebuild"""
for path in watch_paths
)
if langgraph_api_path:
watch_actions += f"""\n- path: {langgraph_api_path}
action: sync+restart
target: /api/langgraph_api"""
watch_str = f"""
develop:
watch:
Expand Down
Loading

0 comments on commit 4768bdb

Please sign in to comment.