From e6c4571191764596686c6bfd72b5881b092f3627 Mon Sep 17 00:00:00 2001 From: Zander Chase <130414180+vowelparrot@users.noreply.github.com> Date: Wed, 24 May 2023 14:43:16 -0700 Subject: [PATCH] Add 'status' command to get server status (#5197) Example: ``` $ langchain plus start --expose ... $ langchain plus status The LangChainPlus server is currently running. Service Status Published Ports langchain-backend Up 40 seconds 1984 langchain-db Up 41 seconds 5433 langchain-frontend Up 40 seconds 80 ngrok Up 41 seconds 4040 To connect, set the following environment variables in your LangChain application: LANGCHAIN_TRACING_V2=true LANGCHAIN_ENDPOINT=https://5cef-70-23-89-158.ngrok.io $ langchain plus stop $ langchain plus status The LangChainPlus server is not running. $ langchain plus start The LangChainPlus server is currently running. Service Status Published Ports langchain-backend Up 5 seconds 1984 langchain-db Up 6 seconds 5433 langchain-frontend Up 5 seconds 80 To connect, set the following environment variables in your LangChain application: LANGCHAIN_TRACING_V2=true LANGCHAIN_ENDPOINT=http://localhost:1984 ``` --- langchain/cli/main.py | 82 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 80 insertions(+), 2 deletions(-) diff --git a/langchain/cli/main.py b/langchain/cli/main.py index 3f59ec16..4920b2a9 100644 --- a/langchain/cli/main.py +++ b/langchain/cli/main.py @@ -1,4 +1,5 @@ import argparse +import json import logging import os import shutil @@ -6,7 +7,7 @@ import subprocess from contextlib import contextmanager from pathlib import Path from subprocess import CalledProcessError -from typing import Generator, List, Optional +from typing import Dict, Generator, List, Mapping, Optional, Union, cast import requests import yaml @@ -19,6 +20,50 @@ logger = logging.getLogger(__name__) _DIR = Path(__file__).parent +def pprint_services(services_status: List[Mapping[str, Union[str, List[str]]]]) -> None: + # Loop through and collect Service, State, and Publishers["PublishedPorts"] + # for each service + services = [] + for service in services_status: + service_status: Dict[str, str] = { + "Service": str(service["Service"]), + "Status": str(service["Status"]), + } + publishers = cast(List[Dict], service.get("Publishers", [])) + if publishers: + service_status["PublishedPorts"] = ", ".join( + [str(publisher["PublishedPort"]) for publisher in publishers] + ) + services.append(service_status) + + max_service_len = max(len(service["Service"]) for service in services) + max_state_len = max(len(service["Status"]) for service in services) + service_message = [ + "\n" + + "Service".ljust(max_service_len + 2) + + "Status".ljust(max_state_len + 2) + + "Published Ports" + ] + for service in services: + service_str = service["Service"].ljust(max_service_len + 2) + state_str = service["Status"].ljust(max_state_len + 2) + ports_str = service.get("PublishedPorts", "") + service_message.append(service_str + state_str + ports_str) + + langchain_endpoint: str = "http://localhost:1984" + used_ngrok = any(["ngrok" in service["Service"] for service in services]) + if used_ngrok: + langchain_endpoint = get_ngrok_url(auth_token=None) + + service_message.append( + "\nTo connect, set the following environment variables" + " in your LangChain application:" + "\nLANGCHAIN_TRACING_V2=true" + f"\nLANGCHAIN_ENDPOINT={langchain_endpoint}" + ) + logger.info("\n".join(service_message)) + + def get_docker_compose_command() -> List[str]: """Get the correct docker compose command for this system.""" try: @@ -222,6 +267,36 @@ class PlusCommand: ] ) + def status(self) -> None: + """Provide information about the status LangChainPlus server.""" + + command = [ + *self.docker_compose_command, + "-f", + str(self.docker_compose_file), + "ps", + "--format", + "json", + ] + + result = subprocess.run( + command, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + try: + command_stdout = result.stdout.decode("utf-8") + services_status = json.loads(command_stdout) + except json.JSONDecodeError: + logger.error("Error checking LangChainPlus server status.") + return + if services_status: + logger.info("The LangChainPlus server is currently running.") + pprint_services(services_status) + else: + logger.info("The LangChainPlus server is not running.") + return + def env() -> None: """Print the runtime environment information.""" @@ -284,7 +359,10 @@ def main() -> None: "logs", description="Show the LangChainPlus server logs." ) server_logs_parser.set_defaults(func=lambda args: server_command.logs()) - + server_status_parser = server_subparsers.add_parser( + "status", description="Show the LangChainPlus server status." + ) + server_status_parser.set_defaults(func=lambda args: server_command.status()) env_parser = subparsers.add_parser("env") env_parser.set_defaults(func=lambda args: env())