cli: add analytics (#754)

This commit is contained in:
Vadym Barda
2024-06-24 18:14:56 -04:00
committed by GitHub
parent 5e8d4e584b
commit e80ddab3a5
4 changed files with 124 additions and 2 deletions
+98
View File
@@ -0,0 +1,98 @@
import functools
import json
import os
import pathlib
import platform
import threading
import urllib.error
import urllib.request
from typing import Any, TypedDict
from langgraph_cli.constants import (
DEFAULT_CONFIG,
DEFAULT_PORT,
SUPABASE_PUBLIC_API_KEY,
SUPABASE_URL,
)
from langgraph_cli.version import __version__
class LogData(TypedDict):
os: str
os_version: str
python_version: str
cli_version: str
cli_command: str
params: dict[str, Any]
def get_anonymized_params(kwargs: dict[str, Any]) -> dict[str, bool]:
params = {}
# anonymize params with values
if config := kwargs.get("config"):
if config != pathlib.Path(DEFAULT_CONFIG).resolve():
params["config"] = True
if port := kwargs.get("port"):
if port != DEFAULT_PORT:
params["port"] = True
if kwargs.get("docker_compose"):
params["docker_compose"] = True
if kwargs.get("debugger_port"):
params["debugger_port"] = True
if kwargs.get("postgres_uri"):
params["postgres_uri"] = True
# pick up exact values for boolean flags
for boolean_param in ["recreate", "pull", "watch", "wait", "verbose"]:
if kwargs.get(boolean_param):
params[boolean_param] = kwargs[boolean_param]
return params
def log_data(data: LogData) -> None:
headers = {
"Content-Type": "application/json",
"apikey": SUPABASE_PUBLIC_API_KEY,
"User-Agent": "Mozilla/5.0",
}
supabase_url = SUPABASE_URL
req = urllib.request.Request(
f"{supabase_url}/rest/v1/logs",
data=json.dumps(data).encode("utf-8"),
headers=headers,
method="POST",
)
try:
urllib.request.urlopen(req)
except urllib.error.URLError:
pass
def log_command(func):
@functools.wraps(func)
def decorator(*args, **kwargs):
if os.getenv("LANGGRAPH_CLI_NO_ANALYTICS") == "1":
return func(*args, **kwargs)
data = {
"os": platform.system(),
"os_version": platform.version(),
"python_version": platform.python_version(),
"cli_version": __version__,
"cli_command": func.__name__,
"params": get_anonymized_params(kwargs),
}
background_thread = threading.Thread(target=log_data, args=(data,))
background_thread.start()
return func(*args, **kwargs)
return decorator
+10 -2
View File
@@ -9,7 +9,9 @@ import click.exceptions
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
@@ -79,7 +81,7 @@ OPT_CONFIG = click.option(
}
Defaults to looking for langgraph.json in the current directory.""",
default="langgraph.json",
default=DEFAULT_CONFIG,
type=click.Path(
exists=True,
file_okay=True,
@@ -92,7 +94,7 @@ OPT_PORT = click.option(
"--port",
"-p",
type=int,
default=8123,
default=DEFAULT_PORT,
show_default=True,
help="""
Port to expose.
@@ -166,6 +168,7 @@ def cli():
help="Wait for services to start before returning. Implies --detach",
)
@cli.command(help="Start langgraph API server")
@log_command
def up(
config: pathlib.Path,
docker_compose: Optional[pathlib.Path],
@@ -254,6 +257,7 @@ def up(
@OPT_VERBOSE
@OPT_DEBUGGER_PORT
@cli.command(help="Stop langgraph API server")
@log_command
def down(
config: pathlib.Path,
docker_compose: Optional[pathlib.Path],
@@ -290,6 +294,7 @@ def down(
@OPT_CONFIG
@click.option("--follow", "-f", is_flag=True, help="Follow logs")
@cli.command(help="Show langgraph API server logs")
@log_command
def logs(
config: pathlib.Path,
docker_compose: Optional[pathlib.Path],
@@ -347,6 +352,7 @@ def logs(
""",
)
@cli.command(help="Build langgraph API server docker image")
@log_command
def build(
config: pathlib.Path,
platform: Optional[str],
@@ -409,6 +415,7 @@ def export():
@OPT_WATCH
@OPT_LANGGRAPH_API_PATH
@export.command(name="compose", help="Export docker compose file")
@log_command
def export_compose(
output: pathlib.Path,
config: pathlib.Path,
@@ -455,6 +462,7 @@ def export_compose(
help="Build and export a helm chart to deploy to a Kubernetes cluster",
hidden=True,
)
@log_command
def export_helm(
output: pathlib.Path,
config: pathlib.Path,
+6
View File
@@ -0,0 +1,6 @@
DEFAULT_CONFIG = "langgraph.json"
DEFAULT_PORT = 8123
# analytics
SUPABASE_PUBLIC_API_KEY = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Imt6cmxwcG9qaW5wY3l5YWlweG5iIiwicm9sZSI6ImFub24iLCJpYXQiOjE3MTkyNTc1NzksImV4cCI6MjAzNDgzMzU3OX0.kkVOlLz3BxemA5nP-vat3K4qRtrDuO4SwZSR_htcX9c"
SUPABASE_URL = "https://kzrlppojinpcyyaipxnb.supabase.co"
+10
View File
@@ -0,0 +1,10 @@
"""Main entrypoint into package."""
from importlib import metadata
try:
__version__ = metadata.version(__package__)
except metadata.PackageNotFoundError:
# Case where package metadata is not available.
__version__ = ""
del metadata # optional, avoids polluting the results of dir(__package__)