mirror of
https://github.com/langchain-ai/langgraph.git
synced 2026-09-08 10:47:52 +02:00
cli: add analytics (#754)
This commit is contained in:
@@ -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
|
||||
@@ -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,
|
||||
|
||||
@@ -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"
|
||||
@@ -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__)
|
||||
Reference in New Issue
Block a user