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.
Files changed (87) hide show
  1. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/PKG-INFO +2 -2
  2. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/examples/graphs/agent.py +2 -2
  3. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/langgraph_cli/cli.py +32 -4
  4. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/langgraph_cli/config.py +29 -8
  5. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/langgraph_cli/docker.py +4 -0
  6. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/pyproject.toml +2 -2
  7. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/schemas/schema.json +1 -1
  8. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/schemas/schema.v0.json +1 -1
  9. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/tests/unit_tests/cli/test_cli.py +245 -0
  10. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/tests/unit_tests/test_config.py +192 -0
  11. langgraph_cli-0.3.6/tests/unit_tests/test_docker.py +365 -0
  12. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/uv.lock +175 -165
  13. langgraph_cli-0.3.4/tests/unit_tests/test_docker.py +0 -148
  14. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/.gitignore +0 -0
  15. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/LICENSE +0 -0
  16. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/Makefile +0 -0
  17. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/README.md +0 -0
  18. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/examples/.env.example +0 -0
  19. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/examples/.gitignore +0 -0
  20. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/examples/Makefile +0 -0
  21. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/examples/graphs/langgraph.json +0 -0
  22. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/examples/graphs/storm.py +0 -0
  23. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/examples/graphs_reqs_a/__init__.py +0 -0
  24. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/examples/graphs_reqs_a/graphs_submod/__init__.py +0 -0
  25. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/examples/graphs_reqs_a/graphs_submod/agent.py +0 -0
  26. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/examples/graphs_reqs_a/graphs_submod/subprompt.txt +0 -0
  27. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/examples/graphs_reqs_a/hello.py +0 -0
  28. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/examples/graphs_reqs_a/langgraph.json +0 -0
  29. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/examples/graphs_reqs_a/prompt.txt +0 -0
  30. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/examples/graphs_reqs_a/requirements.txt +0 -0
  31. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/examples/graphs_reqs_b/graphs_submod/agent.py +0 -0
  32. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/examples/graphs_reqs_b/graphs_submod/subprompt.txt +0 -0
  33. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/examples/graphs_reqs_b/hello.py +0 -0
  34. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/examples/graphs_reqs_b/langgraph.json +0 -0
  35. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/examples/graphs_reqs_b/prompt.txt +0 -0
  36. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/examples/graphs_reqs_b/requirements.txt +0 -0
  37. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/examples/graphs_reqs_b/utils/__init__.py +0 -0
  38. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/examples/graphs_reqs_b/utils/greeter.py +0 -0
  39. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/examples/langgraph.json +0 -0
  40. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/examples/my_app.py +0 -0
  41. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/examples/pipconf.txt +0 -0
  42. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/examples/poetry.lock +0 -0
  43. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/examples/pyproject.toml +0 -0
  44. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/generate_schema.py +0 -0
  45. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/js-examples/.dockerignore +0 -0
  46. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/js-examples/.env.example +0 -0
  47. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/js-examples/.eslintrc.cjs +0 -0
  48. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/js-examples/.gitignore +0 -0
  49. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/js-examples/LICENSE +0 -0
  50. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/js-examples/README.md +0 -0
  51. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/js-examples/jest.config.js +0 -0
  52. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/js-examples/langgraph.json +0 -0
  53. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/js-examples/package.json +0 -0
  54. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/js-examples/src/agent/graph.ts +0 -0
  55. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/js-examples/src/agent/state.ts +0 -0
  56. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/js-examples/static/studio.png +0 -0
  57. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/js-examples/tests/agent.test.ts +0 -0
  58. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/js-examples/tests/graph.int.test.ts +0 -0
  59. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/js-examples/tsconfig.json +0 -0
  60. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/js-examples/yarn.lock +0 -0
  61. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/langgraph_cli/__init__.py +0 -0
  62. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/langgraph_cli/__main__.py +0 -0
  63. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/langgraph_cli/analytics.py +0 -0
  64. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/langgraph_cli/constants.py +0 -0
  65. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/langgraph_cli/exec.py +0 -0
  66. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/langgraph_cli/progress.py +0 -0
  67. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/langgraph_cli/py.typed +0 -0
  68. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/langgraph_cli/templates.py +0 -0
  69. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/langgraph_cli/util.py +0 -0
  70. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/langgraph_cli/version.py +0 -0
  71. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/schemas/version.schema.json +0 -0
  72. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/tests/__init__.py +0 -0
  73. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/tests/integration_tests/__init__.py +0 -0
  74. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/tests/integration_tests/test_cli.py +0 -0
  75. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/tests/unit_tests/__init__.py +0 -0
  76. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/tests/unit_tests/agent.py +0 -0
  77. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/tests/unit_tests/cli/__init__.py +0 -0
  78. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/tests/unit_tests/cli/langgraph.json +0 -0
  79. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/tests/unit_tests/cli/pyproject.toml +0 -0
  80. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/tests/unit_tests/cli/test_templates.py +0 -0
  81. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/tests/unit_tests/conftest.py +0 -0
  82. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/tests/unit_tests/graphs/agent.py +0 -0
  83. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/tests/unit_tests/helpers.py +0 -0
  84. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/tests/unit_tests/multiplatform/js.mts +0 -0
  85. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/tests/unit_tests/multiplatform/python.py +0 -0
  86. {langgraph_cli-0.3.4 → langgraph_cli-0.3.6}/tests/unit_tests/pipconfig.txt +0 -0
  87. {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.4
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<0.4.0,>=0.3.0; (python_version >= '3.11') and extra == '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 ConfigSchema(TypedDict):
52
+ class ContextSchema(TypedDict):
53
53
  model: Literal["anthropic", "openai"]
54
54
 
55
55
 
56
56
  # Define a new graph
57
- workflow = StateGraph(AgentState, config_schema=ConfigSchema)
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, set, config, config_json, base_image, pull, tag, docker_build_args
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. If True, all meta endpoints (/openapi.json, /info, /metrics, /docs) are disabled.
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
- return f"{base_image}:{config['node_version']}{distro_tag}"
1475
- return f"{base_image}:{config['python_version']}{distro_tag}"
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.4"
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.3.0,<0.4.0 ; python_version >= '3.11'",
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. If True, all meta endpoints (/openapi.json, /info, /metrics, /docs) are disabled.\n\nDefault is False.\n"
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. If True, all meta endpoints (/openapi.json, /info, /metrics, /docs) are disabled.\n\nDefault is False.\n"
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