Compare commits

..
Author SHA1 Message Date
Caspar Broekhuizen a0d8d88b28 fix(sdk-py): expose ls_user_id on StudioUser 2026-09-08 16:55:23 -07:00
Parker J. RuleandGitHub 0199b519e1 fix(cli): clarify missing deploy config (#8854)
Shows an actionable, docs-linked error when `langgraph deploy` is run
without a `langgraph.json` instead of exposing a traceback.
2026-09-08 17:03:14 +00:00
12 changed files with 5963 additions and 9416 deletions
+597 -1044
View File
File diff suppressed because it is too large Load Diff
+587 -1092
View File
File diff suppressed because it is too large Load Diff
+600 -1096
View File
File diff suppressed because it is too large Load Diff
+696 -1384
View File
File diff suppressed because it is too large Load Diff
+10
View File
@@ -1603,6 +1603,16 @@ def _deploy_cmd(
# -- 1. Preflight --
validate_deploy_commands(install_command, build_command)
if not config.exists():
message = (
"We couldn't find a langgraph.json file. Run `langgraph deploy` from "
"the root of a LangSmith Deployment project. To get started, visit "
"https://docs.langchain.com/langsmith/deployment-quickstart."
)
if json_output:
em.error(message)
raise click.exceptions.Exit(1)
raise click.ClickException(message)
config_json = langgraph_cli.config.validate_config_file(config)
warn_non_wolfi_distro(config_json, emit=em.note)
+25
View File
@@ -320,6 +320,31 @@ def test_top_level_help_truncates_command_descriptions_to_single_line() -> None:
assert "[Beta] List LangSmith Deployments." in deploy_list_line
def test_deploy_missing_config_shows_actionable_error(tmp_path, monkeypatch) -> None:
runner = CliRunner()
monkeypatch.chdir(tmp_path)
result = runner.invoke(cli, ["deploy"])
assert result.exit_code == 1
assert "We couldn't find a langgraph.json file." in result.output
assert "Run `langgraph deploy` from the root" in result.output
assert "https://docs.langchain.com/langsmith/deployment-quickstart" in result.output
assert "Traceback" not in result.output
def test_deploy_missing_config_emits_json_error(tmp_path, monkeypatch) -> None:
runner = CliRunner()
monkeypatch.chdir(tmp_path)
result = runner.invoke(cli, ["deploy", "--json"])
assert result.exit_code == 1
events = [json.loads(line) for line in result.output.splitlines()]
assert events[-1]["event"] == "error"
assert "We couldn't find a langgraph.json file." in events[-1]["message"]
def test_dev_command_requires_ssl_certfile_and_keyfile_together(tmp_path) -> None:
config_path = tmp_path / "langgraph.json"
config_path.write_text(
+1120 -1460
View File
File diff suppressed because it is too large Load Diff
+1241 -1728
View File
File diff suppressed because it is too large Load Diff
+689 -992
View File
File diff suppressed because it is too large Load Diff
+4 -1
View File
@@ -218,6 +218,8 @@ class BaseUser(typing.Protocol):
class StudioUser:
"""A user object that's populated from authenticated requests from the LangGraph studio.
`ls_user_id` is the canonical LangSmith user ID when supplied by Agent Server.
Note: Studio auth can be disabled in your `langgraph.json` config.
```json
@@ -250,10 +252,11 @@ class StudioUser:
```
"""
__slots__ = ("_is_authenticated", "_permissions", "username")
__slots__ = ("_is_authenticated", "_permissions", "ls_user_id", "username")
def __init__(self, username: str, is_authenticated: bool = False) -> None:
self.username = username
self.ls_user_id: str | None = None
self._is_authenticated = is_authenticated
self._permissions = ["authenticated"] if is_authenticated else []
+14
View File
@@ -1,8 +1,22 @@
import pytest
from typing_extensions import assert_type
from langgraph_sdk import Auth
def test_studio_user_langsmith_id() -> None:
def langsmith_id(user: object) -> str | None:
if isinstance(user, Auth.types.StudioUser):
return assert_type(user.ls_user_id, str | None)
return None
user = Auth.types.StudioUser("login-user", is_authenticated=True)
assert langsmith_id(user) is None
user.ls_user_id = "langsmith-user"
assert langsmith_id(user) == "langsmith-user"
assert user.identity == "login-user"
def test_handler_multiple_resources_and_actions() -> None:
auth = Auth()
+380 -619
View File
File diff suppressed because it is too large Load Diff