Add json schema to CLI (#3684)

So you have cute IDE autocomplete / language server checking.
This commit is contained in:
William FH
2025-03-04 23:48:27 +00:00
committed by GitHub
parent d333f4438f
commit de85e7c246
7 changed files with 1379 additions and 58 deletions
+241 -57
View File
@@ -3,7 +3,7 @@ import os
import pathlib
import textwrap
from collections import Counter
from typing import NamedTuple, Optional, TypedDict, Union
from typing import Any, NamedTuple, Optional, TypedDict, Union
import click
@@ -12,12 +12,18 @@ MIN_PYTHON_VERSION = "3.11"
class IndexConfig(TypedDict, total=False):
"""Configuration for indexing documents for semantic search in the store."""
"""Configuration for indexing documents for semantic search in the store.
This governs how text is converted into embeddings and stored for vector-based lookups.
"""
dims: int
"""Number of dimensions in the embedding vectors.
"""Required. Dimensionality of the embedding vectors you will store.
Common embedding models have the following dimensions:
Must match the output dimension of your selected embedding model or custom embed function.
If mismatched, you will likely encounter shape/size errors when inserting or querying vectors.
Common embedding model output dimensions:
- openai:text-embedding-3-large: 3072
- openai:text-embedding-3-small: 1536
- openai:text-embedding-ada-002: 1536
@@ -28,42 +34,123 @@ class IndexConfig(TypedDict, total=False):
"""
embed: str
"""Optional model (string) to generate embeddings from text or path to model or function.
"""Required. Identifier or reference to the embedding model or a custom embedding function.
Examples:
The format can vary:
- "<provider>:<model_name>" for recognized providers (e.g., "openai:text-embedding-3-large")
- "path/to/module.py:function_name" for your own local embedding function
- "my_custom_embed" if it's a known alias in your system
Examples:
- "openai:text-embedding-3-large"
- "cohere:embed-multilingual-v3.0"
- "src/app.py:embeddings
- "src/app.py:embeddings"
Note: Must return embeddings of dimension `dims`.
"""
fields: Optional[list[str]]
"""Fields to extract text from for embedding generation.
"""Optional. List of JSON fields to extract before generating embeddings.
Defaults to the root ["$"], which embeds the json object as a whole.
Defaults to ["$"], which means the entire JSON object is embedded as one piece of text.
If you provide multiple fields (e.g. ["title", "content"]), each is extracted and embedded separately,
often saving token usage if you only care about certain parts of the data.
Example:
fields=["title", "abstract", "author.biography"]
"""
class StoreConfig(TypedDict, total=False):
embed: Optional[IndexConfig]
"""Configuration for vector embeddings in store."""
"""Configuration for the built-in long-term memory store.
This store can optionally perform semantic search. If you omit `index`,
the store will just handle traditional (non-embedded) data without vector lookups.
"""
index: Optional[IndexConfig]
"""Optional. Defines the vector-based semantic search configuration.
If provided, the store will:
- Generate embeddings according to `index.embed`
- Enforce the embedding dimension given by `index.dims`
- Embed only specified JSON fields (if any) from `index.fields`
If omitted, no vector index is initialized.
"""
class SecurityConfig(TypedDict, total=False):
securitySchemes: dict
security: list
"""Configuration for OpenAPI security definitions and requirements.
Useful for specifying global or path-level authentication and authorization flows
(e.g., OAuth2, API key headers, etc.).
"""
securitySchemes: dict[str, dict[str, Any]]
"""Required. Dict describing each security scheme recognized by your OpenAPI spec.
Keys are scheme names (e.g. "OAuth2", "ApiKeyAuth") and values are their definitions.
Example:
{
"OAuth2": {
"type": "oauth2",
"flows": {
"password": {
"tokenUrl": "/token",
"scopes": {"read": "Read data", "write": "Write data"}
}
}
}
}
"""
security: list[dict[str, list[str]]]
"""Optional. Global security requirements across all endpoints.
Each element in the list maps a security scheme (e.g. "OAuth2") to a list of scopes (e.g. ["read", "write"]).
Example:
[
{"OAuth2": ["read", "write"]},
{"ApiKeyAuth": []}
]
"""
# path => {method => security}
paths: dict[str, dict[str, list]]
paths: dict[str, dict[str, list[dict[str, list[str]]]]]
"""Optional. Path-specific security overrides.
Keys are path templates (e.g., "/items/{item_id}"), mapping to:
- Keys that are HTTP methods (e.g., "GET", "POST"),
- Values are lists of security definitions (just like `security`) for that method.
Example:
{
"/private_data": {
"GET": [{"OAuth2": ["read"]}],
"POST": [{"OAuth2": ["write"]}]
}
}
"""
class AuthConfig(TypedDict, total=False):
path: str
"""Path to the authentication function in a Python file."""
disable_studio_auth: bool
"""Whether to disable auth when connecting from the LangSmith Studio."""
openapi: SecurityConfig
"""The schema to use for updating the openapi spec.
"""Configuration for custom authentication logic and how it integrates into the OpenAPI spec."""
Example:
path: str
"""Required. Path to an instance of the Auth() class that implements custom authentication.
Format: "path/to/file.py:my_auth"
"""
disable_studio_auth: bool
"""Optional. Whether to disable LangSmith API-key authentication for requests originating the Studio.
Defaults to False, meaning that if a particular header is set, the server will verify the `x-api-key` header
value is a valid API key for the deployment's workspace. If True, all requests will go through your custom
authentication logic, regardless of origin of the request.
"""
openapi: SecurityConfig
"""Required. Detailed security configuration that merges into your deployment's OpenAPI spec.
Example (OAuth2):
{
"securitySchemes": {
"OAuth2": {
@@ -71,88 +158,181 @@ class AuthConfig(TypedDict, total=False):
"flows": {
"password": {
"tokenUrl": "/token",
"scopes": {
"me": "Read information about the current user",
"items": "Access to create and manage items"
}
"scopes": {"me": "Read user info", "items": "Manage items"}
}
}
}
},
"security": [
{"OAuth2": ["me"]} # Default security requirement for all endpoints
{"OAuth2": ["me"]}
]
}
"""
class CorsConfig(TypedDict, total=False):
"""Specifies Cross-Origin Resource Sharing (CORS) rules for your server.
If omitted, defaults are typically very restrictive (often no cross-origin requests).
Configure carefully if you want to allow usage from browsers hosted on other domains.
"""
allow_origins: list[str]
"""Optional. List of allowed origins (e.g., "https://example.com").
Default is often an empty list (no external origins).
Use "*" only if you trust all origins, as that bypasses most restrictions.
"""
allow_methods: list[str]
"""Optional. HTTP methods permitted for cross-origin requests (e.g. ["GET", "POST"]).
Default might be ["GET", "POST", "OPTIONS"] depending on your server framework.
"""
allow_headers: list[str]
"""Optional. HTTP headers that can be used in cross-origin requests (e.g. ["Content-Type", "Authorization"])."""
allow_credentials: bool
"""Optional. If True, cross-origin requests can include credentials (cookies, auth headers).
Default False to avoid accidentally exposing secured endpoints to untrusted sites.
"""
allow_origin_regex: str
"""Optional. A regex pattern for matching allowed origins, used if you have dynamic subdomains.
Example: "^https://.*\.mycompany\.com$"
"""
expose_headers: list[str]
"""Optional. List of headers that browsers are allowed to read from the response in cross-origin contexts."""
max_age: int
"""Optional. How many seconds the browser may cache preflight responses.
Default might be 600 (10 minutes). Larger values reduce preflight requests but can cause stale configurations.
"""
class HttpConfig(TypedDict, total=False):
"""Configuration for the built-in HTTP server that powers your deployment's routes and endpoints."""
app: str
"""Import path for a custom Starlette/FastAPI app to mount"""
"""Optional. Import path to a custom Starlette/FastAPI application to mount.
Format: "path/to/module.py:app_var"
If provided, it can override or extend the default routes.
"""
disable_assistants: bool
"""Disable /assistants routes"""
"""Optional. If True, /assistants routes are removed from the server.
Default is False (meaning /assistants is enabled).
"""
disable_threads: bool
"""Disable /threads routes"""
"""Optional. If True, /threads routes are removed.
Default is False.
"""
disable_runs: bool
"""Disable /runs routes"""
"""Optional. If True, /runs routes are removed.
Default is False.
"""
disable_store: bool
"""Disable /store routes"""
"""Optional. If True, /store routes are removed, disabling direct store interactions via HTTP.
Default is False.
"""
disable_meta: bool
"""Disable /ok, /info, /metrics, and /docs routes"""
"""Optional. If True, all meta endpoints (/ok, /info, /metrics, /docs) are disabled.
Default is False.
"""
cors: Optional[CorsConfig]
"""Cross-Origin Resource Sharing (CORS) configuration"""
"""Optional. Defines CORS restrictions. If omitted, no special rules are set and
cross-origin behavior depends on default server settings.
"""
class Config(TypedDict, total=False):
"""Configuration for langgraph-cli."""
"""Top-level config for langgraph-cli or similar deployment tooling."""
python_version: str
"""Python version to use."""
"""Optional. Python version in 'major.minor' format (e.g. '3.11').
Must be at least 3.11 or greater for this deployment to function properly.
"""
node_version: Optional[str]
"""Node.js version to use."""
"""Optional. Node.js version as a major version (e.g. '20'), if your deployment needs Node.
Must be >= 20 if provided.
"""
pip_config_file: Optional[str]
"""Path to a pip configuration file."""
"""Optional. Path to a pip config file (e.g., "/etc/pip.conf" or "pip.ini") for controlling
package installation (custom indices, credentials, etc.).
Only relevant if Python dependencies are installed via pip. If omitted, default pip settings are used.
"""
dockerfile_lines: list[str]
"""Additional lines to add to the Dockerfile."""
"""Optional. Additional Docker instructions that will be appended to your base Dockerfile.
Useful for installing OS packages, setting environment variables, etc.
Example:
dockerfile_lines=[
"RUN apt-get update && apt-get install -y libmagic-dev",
"ENV MY_CUSTOM_VAR=hello_world"
]
"""
dependencies: list[str]
"""Additional Python dependencies to install."""
"""List of Python dependencies to install, either from PyPI or local paths.
Examples:
- "." or "./src" if you have a local Python package
- str (aka "anthropic") for a PyPI package
- "git+https://github.com/org/repo.git@main" for a Git-based package
Defaults to an empty list, meaning no additional packages installed beyond your base environment.
"""
graphs: dict[str, str]
"""Mapping of graph names to their definitions."""
"""Optional. Named definitions of graphs, each pointing to a Python object.
Graphs can be StateGraph, @entrypoint, or any other Pregel object OR they can point to (async) context
managers that accept a single configuration argument (of type RunnableConfig) and return a pregel object
(instance of Stategraph, etc.).
Keys are graph names, values are "path/to/file.py:object_name".
Example:
{
"mygraph": "graphs/my_graph.py:graph_definition",
"anothergraph": "graphs/another.py:get_graph"
}
"""
env: Union[dict[str, str], str]
"""Environment variables to set.
If a dictionary is provided, the keys are environment variable names
and the values are the corresponding environment variable values.
If a string is provided, it is interpreted as a path to a file containing
environment variables in the format KEY=VALUE, with one environment variable
per line.
"""Optional. Environment variables to set for your deployment.
- If given as a dict, keys are variable names and values are their values.
- If given as a string, it must be a path to a file containing lines in KEY=VALUE format.
Example as a dict:
env={"API_TOKEN": "abc123", "DEBUG": "true"}
Example as a file path:
env=".env"
"""
store: Optional[StoreConfig]
"""Configuration for vector embeddings in store."""
"""Optional. Configuration for the built-in long-term memory store, including semantic search indexing.
If omitted, no vector index is set up (the object store will still be present, however).
"""
auth: Optional[AuthConfig]
"""Configuration for authentication."""
"""Optional. Custom authentication config, including the path to your Python auth logic and
the OpenAPI security definitions it uses.
"""
http: Optional[HttpConfig]
"""Configuration for HTTP server."""
"""Optional. Configuration for the built-in HTTP server, controlling which custom routes are exposed
and how cross-origin requests are handled.
"""
def _parse_version(version_str: str) -> tuple[int, int]:
@@ -687,9 +867,11 @@ def python_config_to_docker(
pip_pkgs_str = f"RUN {pip_install} {' '.join(pypi_deps)}" if pypi_deps else ""
if local_deps.pip_reqs:
pip_reqs_str = os.linesep.join(
f"COPY --from=__outer_{reqpath.name} requirements.txt {destpath}"
if reqpath.parent in local_deps.additional_contexts
else f"ADD {reqpath.relative_to(config_path.parent)} {destpath}"
(
f"COPY --from=__outer_{reqpath.name} requirements.txt {destpath}"
if reqpath.parent in local_deps.additional_contexts
else f"ADD {reqpath.relative_to(config_path.parent)} {destpath}"
)
for reqpath, destpath in local_deps.pip_reqs
)
pip_reqs_str += f'{os.linesep}RUN {pip_install} {" ".join("-r " + r for _,r in local_deps.pip_reqs)}'
@@ -724,13 +906,15 @@ RUN set -ex && \\
)
local_pkgs_str = os.linesep.join(
f"""# -- Adding local package {relpath} --
(
f"""# -- Adding local package {relpath} --
COPY --from={name} . /deps/{name}
# -- End of local package {relpath} --"""
if fullpath in local_deps.additional_contexts
else f"""# -- Adding local package {relpath} --
if fullpath in local_deps.additional_contexts
else f"""# -- Adding local package {relpath} --
ADD {relpath} /deps/{name}
# -- End of local package {relpath} --"""
)
for fullpath, (relpath, name) in local_deps.real_pkgs.items()
)