langgraph-cli 0.3.4__tar.gz → 0.3.6__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/PKG-INFO +2 -2
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/examples/graphs/agent.py +2 -2
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/langgraph_cli/cli.py +32 -4
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/langgraph_cli/config.py +29 -8
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/langgraph_cli/docker.py +4 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/pyproject.toml +2 -2
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/schemas/schema.json +1 -1
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/schemas/schema.v0.json +1 -1
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/tests/unit_tests/cli/test_cli.py +245 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/tests/unit_tests/test_config.py +192 -0
- langgraph_cli-0.3.6/tests/unit_tests/test_docker.py +365 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/uv.lock +175 -165
- langgraph_cli-0.3.4/tests/unit_tests/test_docker.py +0 -148
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/.gitignore +0 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/LICENSE +0 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/Makefile +0 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/README.md +0 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/examples/.env.example +0 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/examples/.gitignore +0 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/examples/Makefile +0 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/examples/graphs/langgraph.json +0 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/examples/graphs/storm.py +0 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/examples/graphs_reqs_a/__init__.py +0 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/examples/graphs_reqs_a/graphs_submod/__init__.py +0 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/examples/graphs_reqs_a/graphs_submod/agent.py +0 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/examples/graphs_reqs_a/graphs_submod/subprompt.txt +0 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/examples/graphs_reqs_a/hello.py +0 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/examples/graphs_reqs_a/langgraph.json +0 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/examples/graphs_reqs_a/prompt.txt +0 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/examples/graphs_reqs_a/requirements.txt +0 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/examples/graphs_reqs_b/graphs_submod/agent.py +0 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/examples/graphs_reqs_b/graphs_submod/subprompt.txt +0 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/examples/graphs_reqs_b/hello.py +0 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/examples/graphs_reqs_b/langgraph.json +0 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/examples/graphs_reqs_b/prompt.txt +0 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/examples/graphs_reqs_b/requirements.txt +0 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/examples/graphs_reqs_b/utils/__init__.py +0 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/examples/graphs_reqs_b/utils/greeter.py +0 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/examples/langgraph.json +0 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/examples/my_app.py +0 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/examples/pipconf.txt +0 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/examples/poetry.lock +0 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/examples/pyproject.toml +0 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/generate_schema.py +0 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/js-examples/.dockerignore +0 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/js-examples/.env.example +0 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/js-examples/.eslintrc.cjs +0 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/js-examples/.gitignore +0 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/js-examples/LICENSE +0 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/js-examples/README.md +0 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/js-examples/jest.config.js +0 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/js-examples/langgraph.json +0 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/js-examples/package.json +0 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/js-examples/src/agent/graph.ts +0 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/js-examples/src/agent/state.ts +0 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/js-examples/static/studio.png +0 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/js-examples/tests/agent.test.ts +0 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/js-examples/tests/graph.int.test.ts +0 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/js-examples/tsconfig.json +0 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/js-examples/yarn.lock +0 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/langgraph_cli/__init__.py +0 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/langgraph_cli/__main__.py +0 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/langgraph_cli/analytics.py +0 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/langgraph_cli/constants.py +0 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/langgraph_cli/exec.py +0 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/langgraph_cli/progress.py +0 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/langgraph_cli/py.typed +0 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/langgraph_cli/templates.py +0 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/langgraph_cli/util.py +0 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/langgraph_cli/version.py +0 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/schemas/version.schema.json +0 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/tests/__init__.py +0 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/tests/integration_tests/__init__.py +0 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/tests/integration_tests/test_cli.py +0 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/tests/unit_tests/__init__.py +0 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/tests/unit_tests/agent.py +0 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/tests/unit_tests/cli/__init__.py +0 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/tests/unit_tests/cli/langgraph.json +0 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/tests/unit_tests/cli/pyproject.toml +0 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/tests/unit_tests/cli/test_templates.py +0 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/tests/unit_tests/conftest.py +0 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/tests/unit_tests/graphs/agent.py +0 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/tests/unit_tests/helpers.py +0 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/tests/unit_tests/multiplatform/js.mts +0 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/tests/unit_tests/multiplatform/python.py +0 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/tests/unit_tests/pipconfig.txt +0 -0
- {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/tests/unit_tests/test_config.json +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: langgraph-cli
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.6
|
|
4
4
|
Summary: CLI for interacting with LangGraph API
|
|
5
5
|
Project-URL: Repository, https://www.github.com/langchain-ai/langgraph
|
|
6
6
|
License-Expression: MIT
|
|
@@ -10,7 +10,7 @@ Requires-Dist: click>=8.1.7
|
|
|
10
10
|
Requires-Dist: langgraph-sdk>=0.1.0; python_version >= '3.11'
|
|
11
11
|
Provides-Extra: inmem
|
|
12
12
|
Requires-Dist: langgraph-api<0.3.0,>=0.2.67; (python_version >= '3.11') and extra == 'inmem'
|
|
13
|
-
Requires-Dist: langgraph-runtime-inmem
|
|
13
|
+
Requires-Dist: langgraph-runtime-inmem>=0.6.0; (python_version >= '3.11') and extra == 'inmem'
|
|
14
14
|
Requires-Dist: python-dotenv>=0.8.0; extra == 'inmem'
|
|
15
15
|
Description-Content-Type: text/markdown
|
|
16
16
|
|
|
@@ -49,12 +49,12 @@ def call_model(state, config):
|
|
|
49
49
|
tool_node = ToolNode(tools)
|
|
50
50
|
|
|
51
51
|
|
|
52
|
-
class
|
|
52
|
+
class ContextSchema(TypedDict):
|
|
53
53
|
model: Literal["anthropic", "openai"]
|
|
54
54
|
|
|
55
55
|
|
|
56
56
|
# Define a new graph
|
|
57
|
-
workflow = StateGraph(AgentState,
|
|
57
|
+
workflow = StateGraph(AgentState, context_schema=ContextSchema)
|
|
58
58
|
|
|
59
59
|
# Define the two nodes we will cycle between
|
|
60
60
|
workflow.add_node("agent", call_model)
|
|
@@ -153,6 +153,12 @@ OPT_POSTGRES_URI = click.option(
|
|
|
153
153
|
help="Postgres URI to use for the database. Defaults to launching a local database",
|
|
154
154
|
)
|
|
155
155
|
|
|
156
|
+
OPT_API_VERSION = click.option(
|
|
157
|
+
"--api-version",
|
|
158
|
+
type=str,
|
|
159
|
+
help="API server version to use for the base image. If unspecified, the latest version will be used.",
|
|
160
|
+
)
|
|
161
|
+
|
|
156
162
|
|
|
157
163
|
@click.group()
|
|
158
164
|
@click.version_option(version=__version__, prog_name="LangGraph CLI")
|
|
@@ -170,6 +176,7 @@ def cli():
|
|
|
170
176
|
@OPT_DEBUGGER_BASE_URL
|
|
171
177
|
@OPT_WATCH
|
|
172
178
|
@OPT_POSTGRES_URI
|
|
179
|
+
@OPT_API_VERSION
|
|
173
180
|
@click.option(
|
|
174
181
|
"--image",
|
|
175
182
|
type=str,
|
|
@@ -203,6 +210,7 @@ def up(
|
|
|
203
210
|
debugger_port: Optional[int],
|
|
204
211
|
debugger_base_url: Optional[str],
|
|
205
212
|
postgres_uri: Optional[str],
|
|
213
|
+
api_version: Optional[str],
|
|
206
214
|
image: Optional[str],
|
|
207
215
|
base_image: Optional[str],
|
|
208
216
|
):
|
|
@@ -225,6 +233,7 @@ For production use, requires a license key in env var LANGGRAPH_CLOUD_LICENSE_KE
|
|
|
225
233
|
debugger_port=debugger_port,
|
|
226
234
|
debugger_base_url=debugger_base_url,
|
|
227
235
|
postgres_uri=postgres_uri,
|
|
236
|
+
api_version=api_version,
|
|
228
237
|
image=image,
|
|
229
238
|
base_image=base_image,
|
|
230
239
|
)
|
|
@@ -290,6 +299,7 @@ def _build(
|
|
|
290
299
|
config: pathlib.Path,
|
|
291
300
|
config_json: dict,
|
|
292
301
|
base_image: Optional[str],
|
|
302
|
+
api_version: Optional[str],
|
|
293
303
|
pull: bool,
|
|
294
304
|
tag: str,
|
|
295
305
|
passthrough: Sequence[str] = (),
|
|
@@ -300,7 +310,7 @@ def _build(
|
|
|
300
310
|
subp_exec(
|
|
301
311
|
"docker",
|
|
302
312
|
"pull",
|
|
303
|
-
langgraph_cli.config.docker_tag(config_json, base_image),
|
|
313
|
+
langgraph_cli.config.docker_tag(config_json, base_image, api_version),
|
|
304
314
|
verbose=True,
|
|
305
315
|
)
|
|
306
316
|
)
|
|
@@ -314,7 +324,7 @@ def _build(
|
|
|
314
324
|
]
|
|
315
325
|
# apply config
|
|
316
326
|
stdin, additional_contexts = langgraph_cli.config.config_to_docker(
|
|
317
|
-
config, config_json, base_image
|
|
327
|
+
config, config_json, base_image, api_version
|
|
318
328
|
)
|
|
319
329
|
# add additional_contexts
|
|
320
330
|
if additional_contexts:
|
|
@@ -355,6 +365,7 @@ def _build(
|
|
|
355
365
|
"\n\n \b\nExamples:\n --base-image langchain/langgraph-server:0.2.18 # Pin to a specific patch version"
|
|
356
366
|
"\n --base-image langchain/langgraph-server:0.2 # Pin to a minor version (Python)",
|
|
357
367
|
)
|
|
368
|
+
@OPT_API_VERSION
|
|
358
369
|
@click.argument("docker_build_args", nargs=-1, type=click.UNPROCESSED)
|
|
359
370
|
@cli.command(
|
|
360
371
|
help="📦 Build LangGraph API server Docker image.",
|
|
@@ -367,6 +378,7 @@ def build(
|
|
|
367
378
|
config: pathlib.Path,
|
|
368
379
|
docker_build_args: Sequence[str],
|
|
369
380
|
base_image: Optional[str],
|
|
381
|
+
api_version: Optional[str],
|
|
370
382
|
pull: bool,
|
|
371
383
|
tag: str,
|
|
372
384
|
):
|
|
@@ -376,7 +388,15 @@ def build(
|
|
|
376
388
|
config_json = langgraph_cli.config.validate_config_file(config)
|
|
377
389
|
warn_non_wolfi_distro(config_json)
|
|
378
390
|
_build(
|
|
379
|
-
runner,
|
|
391
|
+
runner,
|
|
392
|
+
set,
|
|
393
|
+
config,
|
|
394
|
+
config_json,
|
|
395
|
+
base_image,
|
|
396
|
+
api_version,
|
|
397
|
+
pull,
|
|
398
|
+
tag,
|
|
399
|
+
docker_build_args,
|
|
380
400
|
)
|
|
381
401
|
|
|
382
402
|
|
|
@@ -456,12 +476,14 @@ tests
|
|
|
456
476
|
"\n\n \b\nExamples:\n --base-image langchain/langgraph-server:0.2.18 # Pin to a specific patch version"
|
|
457
477
|
"\n --base-image langchain/langgraph-server:0.2 # Pin to a minor version (Python)",
|
|
458
478
|
)
|
|
479
|
+
@OPT_API_VERSION
|
|
459
480
|
@log_command
|
|
460
481
|
def dockerfile(
|
|
461
482
|
save_path: str,
|
|
462
483
|
config: pathlib.Path,
|
|
463
484
|
add_docker_compose: bool,
|
|
464
485
|
base_image: Optional[str] = None,
|
|
486
|
+
api_version: Optional[str] = None,
|
|
465
487
|
) -> None:
|
|
466
488
|
save_path = pathlib.Path(save_path).absolute()
|
|
467
489
|
secho(f"🔍 Validating configuration at path: {config}", fg="yellow")
|
|
@@ -474,6 +496,7 @@ def dockerfile(
|
|
|
474
496
|
config,
|
|
475
497
|
config_json,
|
|
476
498
|
base_image=base_image,
|
|
499
|
+
api_version=api_version,
|
|
477
500
|
)
|
|
478
501
|
with open(str(save_path), "w", encoding="utf-8") as f:
|
|
479
502
|
f.write(dockerfile)
|
|
@@ -739,6 +762,7 @@ def prepare_args_and_stdin(
|
|
|
739
762
|
debugger_port: Optional[int] = None,
|
|
740
763
|
debugger_base_url: Optional[str] = None,
|
|
741
764
|
postgres_uri: Optional[str] = None,
|
|
765
|
+
api_version: Optional[str] = None,
|
|
742
766
|
# Like "my-tag" (if you already built it locally)
|
|
743
767
|
image: Optional[str] = None,
|
|
744
768
|
# Like "langchain/langgraphjs-api" or "langchain/langgraph-api
|
|
@@ -754,6 +778,7 @@ def prepare_args_and_stdin(
|
|
|
754
778
|
postgres_uri=postgres_uri,
|
|
755
779
|
image=image, # Pass image to compose YAML generator
|
|
756
780
|
base_image=base_image,
|
|
781
|
+
api_version=api_version,
|
|
757
782
|
)
|
|
758
783
|
args = [
|
|
759
784
|
"--project-directory",
|
|
@@ -769,6 +794,7 @@ def prepare_args_and_stdin(
|
|
|
769
794
|
config,
|
|
770
795
|
watch=watch,
|
|
771
796
|
base_image=langgraph_cli.config.default_base_image(config),
|
|
797
|
+
api_version=api_version,
|
|
772
798
|
image=image,
|
|
773
799
|
)
|
|
774
800
|
return args, stdin
|
|
@@ -787,6 +813,7 @@ def prepare(
|
|
|
787
813
|
debugger_port: Optional[int] = None,
|
|
788
814
|
debugger_base_url: Optional[str] = None,
|
|
789
815
|
postgres_uri: Optional[str] = None,
|
|
816
|
+
api_version: Optional[str] = None,
|
|
790
817
|
image: Optional[str] = None,
|
|
791
818
|
base_image: Optional[str] = None,
|
|
792
819
|
) -> tuple[list[str], str]:
|
|
@@ -799,7 +826,7 @@ def prepare(
|
|
|
799
826
|
subp_exec(
|
|
800
827
|
"docker",
|
|
801
828
|
"pull",
|
|
802
|
-
langgraph_cli.config.docker_tag(config_json, base_image),
|
|
829
|
+
langgraph_cli.config.docker_tag(config_json, base_image, api_version),
|
|
803
830
|
verbose=verbose,
|
|
804
831
|
)
|
|
805
832
|
)
|
|
@@ -814,6 +841,7 @@ def prepare(
|
|
|
814
841
|
debugger_port=debugger_port,
|
|
815
842
|
debugger_base_url=debugger_base_url or f"http://127.0.0.1:{port}",
|
|
816
843
|
postgres_uri=postgres_uri,
|
|
844
|
+
api_version=api_version,
|
|
817
845
|
image=image,
|
|
818
846
|
base_image=base_image,
|
|
819
847
|
)
|
|
@@ -338,7 +338,10 @@ class HttpConfig(TypedDict, total=False):
|
|
|
338
338
|
Default is False.
|
|
339
339
|
"""
|
|
340
340
|
disable_meta: bool
|
|
341
|
-
"""Optional.
|
|
341
|
+
"""Optional. Remove meta endpoints.
|
|
342
|
+
|
|
343
|
+
Set to True to disable the following endpoints: /openapi.json, /info, /metrics, /docs.
|
|
344
|
+
This will also make the /ok endpoint skip any DB or other checks, always returning {"ok": True}.
|
|
342
345
|
|
|
343
346
|
Default is False.
|
|
344
347
|
"""
|
|
@@ -1210,6 +1213,7 @@ def python_config_to_docker(
|
|
|
1210
1213
|
config_path: pathlib.Path,
|
|
1211
1214
|
config: Config,
|
|
1212
1215
|
base_image: str,
|
|
1216
|
+
api_version: Optional[str] = None,
|
|
1213
1217
|
) -> tuple[str, dict[str, str]]:
|
|
1214
1218
|
"""Generate a Dockerfile from the configuration."""
|
|
1215
1219
|
pip_installer = config.get("pip_installer", "auto")
|
|
@@ -1357,7 +1361,7 @@ ADD {relpath} /deps/{name}
|
|
|
1357
1361
|
"# -- End of JS dependencies install --",
|
|
1358
1362
|
]
|
|
1359
1363
|
)
|
|
1360
|
-
image_str = docker_tag(config, base_image)
|
|
1364
|
+
image_str = docker_tag(config, base_image, api_version)
|
|
1361
1365
|
docker_file_contents = [
|
|
1362
1366
|
f"FROM {image_str}",
|
|
1363
1367
|
"",
|
|
@@ -1399,10 +1403,11 @@ def node_config_to_docker(
|
|
|
1399
1403
|
config_path: pathlib.Path,
|
|
1400
1404
|
config: Config,
|
|
1401
1405
|
base_image: str,
|
|
1406
|
+
api_version: Optional[str] = None,
|
|
1402
1407
|
) -> tuple[str, dict[str, str]]:
|
|
1403
1408
|
faux_path = f"/deps/{config_path.parent.name}"
|
|
1404
1409
|
install_cmd = _get_node_pm_install_cmd(config_path, config)
|
|
1405
|
-
image_str = docker_tag(config, base_image)
|
|
1410
|
+
image_str = docker_tag(config, base_image, api_version)
|
|
1406
1411
|
|
|
1407
1412
|
env_vars: list[str] = []
|
|
1408
1413
|
|
|
@@ -1458,6 +1463,7 @@ def default_base_image(config: Config) -> str:
|
|
|
1458
1463
|
def docker_tag(
|
|
1459
1464
|
config: Config,
|
|
1460
1465
|
base_image: Optional[str] = None,
|
|
1466
|
+
api_version: Optional[str] = None,
|
|
1461
1467
|
) -> str:
|
|
1462
1468
|
base_image = base_image or default_base_image(config)
|
|
1463
1469
|
|
|
@@ -1470,28 +1476,43 @@ def docker_tag(
|
|
|
1470
1476
|
if "/langgraph-server" in base_image:
|
|
1471
1477
|
return f"{base_image}-py{config['python_version']}"
|
|
1472
1478
|
|
|
1479
|
+
# Build the standard tag format
|
|
1480
|
+
language, version = None, None
|
|
1473
1481
|
if config.get("node_version") and not config.get("python_version"):
|
|
1474
|
-
|
|
1475
|
-
|
|
1482
|
+
language, version = "node", config["node_version"]
|
|
1483
|
+
else:
|
|
1484
|
+
language, version = "py", config["python_version"]
|
|
1485
|
+
|
|
1486
|
+
version_distro_tag = f"{version}{distro_tag}"
|
|
1487
|
+
|
|
1488
|
+
# Prepend API version if provided
|
|
1489
|
+
if api_version:
|
|
1490
|
+
full_tag = f"{api_version}-{language}{version_distro_tag}"
|
|
1491
|
+
else:
|
|
1492
|
+
full_tag = version_distro_tag
|
|
1493
|
+
|
|
1494
|
+
return f"{base_image}:{full_tag}"
|
|
1476
1495
|
|
|
1477
1496
|
|
|
1478
1497
|
def config_to_docker(
|
|
1479
1498
|
config_path: pathlib.Path,
|
|
1480
1499
|
config: Config,
|
|
1481
1500
|
base_image: Optional[str] = None,
|
|
1501
|
+
api_version: Optional[str] = None,
|
|
1482
1502
|
) -> tuple[str, dict[str, str]]:
|
|
1483
1503
|
base_image = base_image or default_base_image(config)
|
|
1484
1504
|
|
|
1485
1505
|
if config.get("node_version") and not config.get("python_version"):
|
|
1486
|
-
return node_config_to_docker(config_path, config, base_image)
|
|
1506
|
+
return node_config_to_docker(config_path, config, base_image, api_version)
|
|
1487
1507
|
|
|
1488
|
-
return python_config_to_docker(config_path, config, base_image)
|
|
1508
|
+
return python_config_to_docker(config_path, config, base_image, api_version)
|
|
1489
1509
|
|
|
1490
1510
|
|
|
1491
1511
|
def config_to_compose(
|
|
1492
1512
|
config_path: pathlib.Path,
|
|
1493
1513
|
config: Config,
|
|
1494
1514
|
base_image: Optional[str] = None,
|
|
1515
|
+
api_version: Optional[str] = None,
|
|
1495
1516
|
image: Optional[str] = None,
|
|
1496
1517
|
watch: bool = False,
|
|
1497
1518
|
) -> str:
|
|
@@ -1528,7 +1549,7 @@ def config_to_compose(
|
|
|
1528
1549
|
|
|
1529
1550
|
else:
|
|
1530
1551
|
dockerfile, additional_contexts = config_to_docker(
|
|
1531
|
-
config_path, config, base_image
|
|
1552
|
+
config_path, config, base_image, api_version
|
|
1532
1553
|
)
|
|
1533
1554
|
|
|
1534
1555
|
additional_contexts_str = "\n".join(
|
|
@@ -147,6 +147,8 @@ def compose_as_dict(
|
|
|
147
147
|
image: Optional[str] = None,
|
|
148
148
|
# Base image to use for the LangGraph API server
|
|
149
149
|
base_image: Optional[str] = None,
|
|
150
|
+
# API version of the base image
|
|
151
|
+
api_version: Optional[str] = None,
|
|
150
152
|
) -> dict:
|
|
151
153
|
"""Create a docker compose file as a dictionary in YML style."""
|
|
152
154
|
if postgres_uri is None:
|
|
@@ -252,6 +254,7 @@ def compose(
|
|
|
252
254
|
postgres_uri: Optional[str] = None,
|
|
253
255
|
image: Optional[str] = None,
|
|
254
256
|
base_image: Optional[str] = None,
|
|
257
|
+
api_version: Optional[str] = None,
|
|
255
258
|
) -> str:
|
|
256
259
|
"""Create a docker compose file as a string."""
|
|
257
260
|
compose_content = compose_as_dict(
|
|
@@ -262,6 +265,7 @@ def compose(
|
|
|
262
265
|
postgres_uri=postgres_uri,
|
|
263
266
|
image=image,
|
|
264
267
|
base_image=base_image,
|
|
268
|
+
api_version=api_version,
|
|
265
269
|
)
|
|
266
270
|
compose_str = dict_to_yaml(compose_content)
|
|
267
271
|
return compose_str
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "langgraph-cli"
|
|
7
|
-
version = "0.3.
|
|
7
|
+
version = "0.3.6"
|
|
8
8
|
description = "CLI for interacting with LangGraph API"
|
|
9
9
|
authors = []
|
|
10
10
|
requires-python = ">=3.9"
|
|
@@ -19,7 +19,7 @@ dependencies = [
|
|
|
19
19
|
[project.optional-dependencies]
|
|
20
20
|
inmem = [
|
|
21
21
|
"langgraph-api>=0.2.67,<0.3.0 ; python_version >= '3.11'",
|
|
22
|
-
"langgraph-runtime-inmem>=0.
|
|
22
|
+
"langgraph-runtime-inmem>=0.6.0 ; python_version >= '3.11'",
|
|
23
23
|
"python-dotenv>=0.8.0",
|
|
24
24
|
]
|
|
25
25
|
|
|
@@ -539,7 +539,7 @@
|
|
|
539
539
|
},
|
|
540
540
|
"disable_meta": {
|
|
541
541
|
"type": "boolean",
|
|
542
|
-
"description": "Optional.
|
|
542
|
+
"description": "Optional. Remove meta endpoints.\n\n\nDefault is False.\n"
|
|
543
543
|
},
|
|
544
544
|
"disable_runs": {
|
|
545
545
|
"type": "boolean",
|
|
@@ -539,7 +539,7 @@
|
|
|
539
539
|
},
|
|
540
540
|
"disable_meta": {
|
|
541
541
|
"type": "boolean",
|
|
542
|
-
"description": "Optional.
|
|
542
|
+
"description": "Optional. Remove meta endpoints.\n\n\nDefault is False.\n"
|
|
543
543
|
},
|
|
544
544
|
"disable_runs": {
|
|
545
545
|
"type": "boolean",
|
|
@@ -574,3 +574,248 @@ def test_build_generate_proper_build_context():
|
|
|
574
574
|
assert len(build_contexts) == 2, (
|
|
575
575
|
f"Expected 2 build contexts, but found {len(build_contexts)}"
|
|
576
576
|
)
|
|
577
|
+
|
|
578
|
+
|
|
579
|
+
def test_dockerfile_command_with_api_version() -> None:
|
|
580
|
+
"""Test the 'dockerfile' command with --api-version flag."""
|
|
581
|
+
runner = CliRunner()
|
|
582
|
+
config_content = {
|
|
583
|
+
"python_version": "3.11",
|
|
584
|
+
"graphs": {"agent": "agent.py:graph"},
|
|
585
|
+
"dependencies": ["."],
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
with temporary_config_folder(config_content) as temp_dir:
|
|
589
|
+
save_path = temp_dir / "Dockerfile"
|
|
590
|
+
agent_path = temp_dir / "agent.py"
|
|
591
|
+
agent_path.touch()
|
|
592
|
+
|
|
593
|
+
result = runner.invoke(
|
|
594
|
+
cli,
|
|
595
|
+
[
|
|
596
|
+
"dockerfile",
|
|
597
|
+
str(save_path),
|
|
598
|
+
"--config",
|
|
599
|
+
str(temp_dir / "config.json"),
|
|
600
|
+
"--api-version",
|
|
601
|
+
"0.2.74",
|
|
602
|
+
],
|
|
603
|
+
)
|
|
604
|
+
|
|
605
|
+
# Assert command was successful
|
|
606
|
+
assert result.exit_code == 0, result.output
|
|
607
|
+
assert "✅ Created: Dockerfile" in result.output
|
|
608
|
+
|
|
609
|
+
# Check if Dockerfile was created and contains correct FROM line
|
|
610
|
+
assert save_path.exists()
|
|
611
|
+
with open(save_path) as f:
|
|
612
|
+
dockerfile = f.read()
|
|
613
|
+
assert "FROM langchain/langgraph-api:0.2.74-py3.11" in dockerfile
|
|
614
|
+
|
|
615
|
+
|
|
616
|
+
def test_dockerfile_command_with_api_version_and_base_image() -> None:
|
|
617
|
+
"""Test the 'dockerfile' command with both --api-version and --base-image flags."""
|
|
618
|
+
runner = CliRunner()
|
|
619
|
+
config_content = {
|
|
620
|
+
"python_version": "3.12",
|
|
621
|
+
"graphs": {"agent": "agent.py:graph"},
|
|
622
|
+
"dependencies": ["."],
|
|
623
|
+
"image_distro": "wolfi",
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
with temporary_config_folder(config_content) as temp_dir:
|
|
627
|
+
save_path = temp_dir / "Dockerfile"
|
|
628
|
+
agent_path = temp_dir / "agent.py"
|
|
629
|
+
agent_path.touch()
|
|
630
|
+
|
|
631
|
+
result = runner.invoke(
|
|
632
|
+
cli,
|
|
633
|
+
[
|
|
634
|
+
"dockerfile",
|
|
635
|
+
str(save_path),
|
|
636
|
+
"--config",
|
|
637
|
+
str(temp_dir / "config.json"),
|
|
638
|
+
"--api-version",
|
|
639
|
+
"1.0.0",
|
|
640
|
+
"--base-image",
|
|
641
|
+
"my-registry/custom-api",
|
|
642
|
+
],
|
|
643
|
+
)
|
|
644
|
+
|
|
645
|
+
# Assert command was successful
|
|
646
|
+
assert result.exit_code == 0, result.output
|
|
647
|
+
assert "✅ Created: Dockerfile" in result.output
|
|
648
|
+
|
|
649
|
+
# Check if Dockerfile was created and contains correct FROM line
|
|
650
|
+
assert save_path.exists()
|
|
651
|
+
with open(save_path) as f:
|
|
652
|
+
dockerfile = f.read()
|
|
653
|
+
assert "FROM my-registry/custom-api:1.0.0-py3.12-wolfi" in dockerfile
|
|
654
|
+
|
|
655
|
+
|
|
656
|
+
def test_dockerfile_command_with_api_version_nodejs() -> None:
|
|
657
|
+
"""Test the 'dockerfile' command with --api-version flag for Node.js config."""
|
|
658
|
+
runner = CliRunner()
|
|
659
|
+
config_content = {
|
|
660
|
+
"node_version": "20",
|
|
661
|
+
"graphs": {"agent": "agent.js:graph"},
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
with temporary_config_folder(config_content) as temp_dir:
|
|
665
|
+
save_path = temp_dir / "Dockerfile"
|
|
666
|
+
agent_path = temp_dir / "agent.js"
|
|
667
|
+
agent_path.touch()
|
|
668
|
+
|
|
669
|
+
result = runner.invoke(
|
|
670
|
+
cli,
|
|
671
|
+
[
|
|
672
|
+
"dockerfile",
|
|
673
|
+
str(save_path),
|
|
674
|
+
"--config",
|
|
675
|
+
str(temp_dir / "config.json"),
|
|
676
|
+
"--api-version",
|
|
677
|
+
"0.2.74",
|
|
678
|
+
],
|
|
679
|
+
)
|
|
680
|
+
|
|
681
|
+
# Assert command was successful
|
|
682
|
+
assert result.exit_code == 0, result.output
|
|
683
|
+
assert "✅ Created: Dockerfile" in result.output
|
|
684
|
+
|
|
685
|
+
# Check if Dockerfile was created and contains correct FROM line
|
|
686
|
+
assert save_path.exists()
|
|
687
|
+
with open(save_path) as f:
|
|
688
|
+
dockerfile = f.read()
|
|
689
|
+
assert "FROM langchain/langgraphjs-api:0.2.74-node20" in dockerfile
|
|
690
|
+
|
|
691
|
+
|
|
692
|
+
def test_build_command_with_api_version() -> None:
|
|
693
|
+
"""Test the 'build' command with --api-version flag."""
|
|
694
|
+
runner = CliRunner()
|
|
695
|
+
config_content = {
|
|
696
|
+
"python_version": "3.11",
|
|
697
|
+
"graphs": {"agent": "agent.py:graph"},
|
|
698
|
+
"dependencies": ["."],
|
|
699
|
+
"image_distro": "wolfi", # Use wolfi to avoid warning messages
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
with temporary_config_folder(config_content) as temp_dir:
|
|
703
|
+
agent_path = temp_dir / "agent.py"
|
|
704
|
+
agent_path.touch()
|
|
705
|
+
|
|
706
|
+
# Mock docker command since we don't want to actually build
|
|
707
|
+
with runner.isolated_filesystem():
|
|
708
|
+
result = runner.invoke(
|
|
709
|
+
cli,
|
|
710
|
+
[
|
|
711
|
+
"build",
|
|
712
|
+
"--tag",
|
|
713
|
+
"test-image",
|
|
714
|
+
"--config",
|
|
715
|
+
str(temp_dir / "config.json"),
|
|
716
|
+
"--api-version",
|
|
717
|
+
"0.2.74",
|
|
718
|
+
"--no-pull", # Avoid pulling non-existent images
|
|
719
|
+
],
|
|
720
|
+
catch_exceptions=True,
|
|
721
|
+
)
|
|
722
|
+
|
|
723
|
+
# Check that the build command is called with the correct tag
|
|
724
|
+
# The output should contain the docker build command with the api_version tag
|
|
725
|
+
assert "langchain/langgraph-api:0.2.74-py3.11-wolfi" in result.output
|
|
726
|
+
|
|
727
|
+
|
|
728
|
+
def test_build_command_with_api_version_and_base_image() -> None:
|
|
729
|
+
"""Test the 'build' command with both --api-version and --base-image flags."""
|
|
730
|
+
runner = CliRunner()
|
|
731
|
+
config_content = {
|
|
732
|
+
"python_version": "3.12",
|
|
733
|
+
"graphs": {"agent": "agent.py:graph"},
|
|
734
|
+
"dependencies": ["."],
|
|
735
|
+
"image_distro": "wolfi", # Use wolfi to avoid warning messages
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
with temporary_config_folder(config_content) as temp_dir:
|
|
739
|
+
agent_path = temp_dir / "agent.py"
|
|
740
|
+
agent_path.touch()
|
|
741
|
+
|
|
742
|
+
# Mock docker command since we don't want to actually build
|
|
743
|
+
with runner.isolated_filesystem():
|
|
744
|
+
result = runner.invoke(
|
|
745
|
+
cli,
|
|
746
|
+
[
|
|
747
|
+
"build",
|
|
748
|
+
"--tag",
|
|
749
|
+
"test-image",
|
|
750
|
+
"--config",
|
|
751
|
+
str(temp_dir / "config.json"),
|
|
752
|
+
"--api-version",
|
|
753
|
+
"1.0.0",
|
|
754
|
+
"--base-image",
|
|
755
|
+
"my-registry/custom-api",
|
|
756
|
+
"--no-pull", # Avoid pulling non-existent images
|
|
757
|
+
],
|
|
758
|
+
catch_exceptions=True,
|
|
759
|
+
)
|
|
760
|
+
|
|
761
|
+
# Check that the build command includes the api_version
|
|
762
|
+
assert "my-registry/custom-api:1.0.0-py3.12-wolfi" in result.output
|
|
763
|
+
|
|
764
|
+
|
|
765
|
+
def test_prepare_args_and_stdin_with_api_version() -> None:
|
|
766
|
+
"""Test prepare_args_and_stdin function with api_version parameter."""
|
|
767
|
+
config_path = pathlib.Path(__file__).parent / "langgraph.json"
|
|
768
|
+
config = validate_config(
|
|
769
|
+
Config(dependencies=["."], graphs={"agent": "agent.py:graph"})
|
|
770
|
+
)
|
|
771
|
+
port = 8000
|
|
772
|
+
api_version = "0.2.74"
|
|
773
|
+
|
|
774
|
+
actual_args, actual_stdin = prepare_args_and_stdin(
|
|
775
|
+
capabilities=DEFAULT_DOCKER_CAPABILITIES,
|
|
776
|
+
config_path=config_path,
|
|
777
|
+
config=config,
|
|
778
|
+
docker_compose=None,
|
|
779
|
+
port=port,
|
|
780
|
+
watch=False,
|
|
781
|
+
api_version=api_version,
|
|
782
|
+
)
|
|
783
|
+
|
|
784
|
+
expected_args = [
|
|
785
|
+
"--project-directory",
|
|
786
|
+
str(pathlib.Path(__file__).parent.absolute()),
|
|
787
|
+
"-f",
|
|
788
|
+
"-",
|
|
789
|
+
]
|
|
790
|
+
|
|
791
|
+
# Check that the args are correct
|
|
792
|
+
assert actual_args == expected_args
|
|
793
|
+
|
|
794
|
+
# Check that the stdin contains the correct FROM line with api_version
|
|
795
|
+
assert "FROM langchain/langgraph-api:0.2.74-py3.11" in actual_stdin
|
|
796
|
+
|
|
797
|
+
|
|
798
|
+
def test_prepare_args_and_stdin_with_api_version_and_image() -> None:
|
|
799
|
+
"""Test prepare_args_and_stdin function with both api_version and image parameters."""
|
|
800
|
+
config_path = pathlib.Path(__file__).parent / "langgraph.json"
|
|
801
|
+
config = validate_config(
|
|
802
|
+
Config(dependencies=["."], graphs={"agent": "agent.py:graph"})
|
|
803
|
+
)
|
|
804
|
+
port = 8000
|
|
805
|
+
api_version = "0.2.74"
|
|
806
|
+
image = "my-custom-image:latest"
|
|
807
|
+
|
|
808
|
+
actual_args, actual_stdin = prepare_args_and_stdin(
|
|
809
|
+
capabilities=DEFAULT_DOCKER_CAPABILITIES,
|
|
810
|
+
config_path=config_path,
|
|
811
|
+
config=config,
|
|
812
|
+
docker_compose=None,
|
|
813
|
+
port=port,
|
|
814
|
+
watch=False,
|
|
815
|
+
api_version=api_version,
|
|
816
|
+
image=image,
|
|
817
|
+
)
|
|
818
|
+
|
|
819
|
+
# When image is provided, api_version should be ignored for the image
|
|
820
|
+
# but the stdin should not contain a build section (since image is provided)
|
|
821
|
+
assert "pull_policy: build" not in actual_stdin
|