mirror of
https://github.com/langchain-ai/langgraph.git
synced 2026-09-06 17:57:49 +02:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ce6d8800d8 |
+12
-120
@@ -127,79 +127,6 @@ REDIRECT_MAP = {
|
||||
"how-tos/human_in_the_loop/breakpoints.md": "how-tos/human_in_the_loop/add-human-in-the-loop.md",
|
||||
"cloud/how-tos/human_in_the_loop_breakpoint.md": "cloud/how-tos/add-human-in-the-loop.md",
|
||||
"how-tos/human_in_the_loop/edit-graph-state.ipynb": "how-tos/human_in_the_loop/time-travel.md",
|
||||
|
||||
# mintlify
|
||||
# "tutorials/auth/getting_started.md": "https://docs.langchain.com/langgraph-platform/",
|
||||
# "tutorials/auth/resource_auth.md": "https://docs.langchain.com/langgraph-platform/",
|
||||
# "tutorials/auth/add_auth_server.md": "https://docs.langchain.com/langgraph-platform/",
|
||||
# "how-tos/use-remote-graph.md": "https://docs.langchain.com/langgraph-platform/",
|
||||
# "how-tos/autogen-integration.md": "https://docs.langchain.com/langgraph-platform/",
|
||||
# "cloud/how-tos/use_stream_react.md": "https://docs.langchain.com/langgraph-platform/",
|
||||
# "cloud/how-tos/generative_ui_react.md": "https://docs.langchain.com/langgraph-platform/",
|
||||
# "concepts/langgraph_platform.md": "https://docs.langchain.com/langgraph-platform/",
|
||||
# "concepts/langgraph_components.md": "https://docs.langchain.com/langgraph-platform/",
|
||||
# "concepts/langgraph_server.md": "https://docs.langchain.com/langgraph-platform/",
|
||||
# "concepts/langgraph_data_plane.md": "https://docs.langchain.com/langgraph-platform/",
|
||||
# "concepts/langgraph_control_plane.md": "https://docs.langchain.com/langgraph-platform/",
|
||||
# "concepts/langgraph_cli.md": "https://docs.langchain.com/langgraph-platform/",
|
||||
# "concepts/langgraph_studio.md": "https://docs.langchain.com/langgraph-platform/",
|
||||
# "cloud/how-tos/studio/quick_start.md": "https://docs.langchain.com/langgraph-platform/",
|
||||
# "cloud/how-tos/invoke_studio.md": "https://docs.langchain.com/langgraph-platform/",
|
||||
# "cloud/how-tos/studio/manage_assistants.md": "https://docs.langchain.com/langgraph-platform/",
|
||||
# "cloud/how-tos/threads_studio.md": "https://docs.langchain.com/langgraph-platform/",
|
||||
# "cloud/how-tos/iterate_graph_studio.md": "https://docs.langchain.com/langgraph-platform/",
|
||||
# "cloud/how-tos/studio/run_evals.md": "https://docs.langchain.com/langgraph-platform/",
|
||||
# "cloud/how-tos/clone_traces_studio.md": "https://docs.langchain.com/langgraph-platform/",
|
||||
# "cloud/how-tos/datasets_studio.md": "https://docs.langchain.com/langgraph-platform/",
|
||||
# "concepts/sdk.md": "https://docs.langchain.com/langgraph-platform/",
|
||||
# "concepts/plans.md": "https://docs.langchain.com/langgraph-platform/",
|
||||
# "concepts/application_structure.md": "https://docs.langchain.com/langgraph-platform/",
|
||||
# "concepts/scalability_and_resilience.md": "https://docs.langchain.com/langgraph-platform/",
|
||||
# "concepts/auth.md": "https://docs.langchain.com/langgraph-platform/",
|
||||
# "how-tos/auth/custom_auth.md": "https://docs.langchain.com/langgraph-platform/",
|
||||
# "how-tos/auth/openapi_security.md": "https://docs.langchain.com/langgraph-platform/",
|
||||
# "concepts/assistants.md": "https://docs.langchain.com/langgraph-platform/",
|
||||
# "cloud/how-tos/configuration_cloud.md": "https://docs.langchain.com/langgraph-platform/",
|
||||
# "cloud/how-tos/use_threads.md": "https://docs.langchain.com/langgraph-platform/",
|
||||
# "cloud/how-tos/background_run.md": "https://docs.langchain.com/langgraph-platform/",
|
||||
# "cloud/how-tos/same-thread.md": "https://docs.langchain.com/langgraph-platform/",
|
||||
# "cloud/how-tos/cron_jobs.md": "https://docs.langchain.com/langgraph-platform/",
|
||||
# "cloud/how-tos/stateless_runs.md": "https://docs.langchain.com/langgraph-platform/",
|
||||
# "cloud/how-tos/configurable_headers.md": "https://docs.langchain.com/langgraph-platform/",
|
||||
# "concepts/double_texting.md": "https://docs.langchain.com/langgraph-platform/",
|
||||
# "cloud/how-tos/interrupt_concurrent.md": "https://docs.langchain.com/langgraph-platform/",
|
||||
# "cloud/how-tos/rollback_concurrent.md": "https://docs.langchain.com/langgraph-platform/",
|
||||
# "cloud/how-tos/reject_concurrent.md": "https://docs.langchain.com/langgraph-platform/",
|
||||
# "cloud/how-tos/enqueue_concurrent.md": "https://docs.langchain.com/langgraph-platform/",
|
||||
# "cloud/concepts/webhooks.md": "https://docs.langchain.com/langgraph-platform/",
|
||||
# "cloud/how-tos/webhooks.md": "https://docs.langchain.com/langgraph-platform/",
|
||||
# "cloud/concepts/cron_jobs.md": "https://docs.langchain.com/langgraph-platform/",
|
||||
# "cloud/how-tos/cron_jobs.md": "https://docs.langchain.com/langgraph-platform/",
|
||||
# "how-tos/http/custom_lifespan.md": "https://docs.langchain.com/langgraph-platform/",
|
||||
# "how-tos/http/custom_middleware.md": "https://docs.langchain.com/langgraph-platform/",
|
||||
# "how-tos/http/custom_routes.md": "https://docs.langchain.com/langgraph-platform/",
|
||||
# "cloud/concepts/data_storage_and_privacy.md": "https://docs.langchain.com/langgraph-platform/",
|
||||
# "cloud/deployment/semantic_search.md": "https://docs.langchain.com/langgraph-platform/",
|
||||
# "how-tos/ttl/configure_ttl.md": "https://docs.langchain.com/langgraph-platform/",
|
||||
# "concepts/deployment_options.md": "https://docs.langchain.com/langgraph-platform/",
|
||||
# "cloud/quick_start.md": "https://docs.langchain.com/langgraph-platform/",
|
||||
# "cloud/deployment/setup.md": "https://docs.langchain.com/langgraph-platform/",
|
||||
# "cloud/deployment/setup_pyproject.md": "https://docs.langchain.com/langgraph-platform/",
|
||||
# "cloud/deployment/setup_javascript.md": "https://docs.langchain.com/langgraph-platform/",
|
||||
# "cloud/deployment/custom_docker.md": "https://docs.langchain.com/langgraph-platform/",
|
||||
# "cloud/deployment/graph_rebuild.md": "https://docs.langchain.com/langgraph-platform/",
|
||||
# "concepts/langgraph_cloud.md": "https://docs.langchain.com/langgraph-platform/",
|
||||
# "concepts/langgraph_self_hosted_data_plane.md": "https://docs.langchain.com/langgraph-platform/",
|
||||
# "concepts/langgraph_self_hosted_control_plane.md": "https://docs.langchain.com/langgraph-platform/",
|
||||
# "concepts/langgraph_standalone_container.md": "https://docs.langchain.com/langgraph-platform/",
|
||||
# "cloud/deployment/cloud.md": "https://docs.langchain.com/langgraph-platform/",
|
||||
# "cloud/deployment/self_hosted_data_plane.md": "https://docs.langchain.com/langgraph-platform/",
|
||||
# "cloud/deployment/self_hosted_control_plane.md": "https://docs.langchain.com/langgraph-platform/",
|
||||
# "cloud/deployment/standalone_container.md": "https://docs.langchain.com/langgraph-platform/",
|
||||
# "concepts/server-mcp.md": "https://docs.langchain.com/langgraph-platform/",
|
||||
# "cloud/how-tos/human_in_the_loop_time_travel.md": "https://docs.langchain.com/langgraph-platform/",
|
||||
# "cloud/how-tos/add-human-in-the-loop.md": "https://docs.langchain.com/langgraph-platform/",
|
||||
# "cloud/how-tos/streaming.md": "https://docs.langchain.com/langgraph-platform/",
|
||||
}
|
||||
|
||||
|
||||
@@ -555,51 +482,16 @@ def on_post_page(html: str, page: Page, config: MkDocsConfig) -> str:
|
||||
def on_post_build(config):
|
||||
use_directory_urls = config.get("use_directory_urls")
|
||||
for page_old, page_new in REDIRECT_MAP.items():
|
||||
# Convert .ipynb to .md for path calculation
|
||||
page_old = page_old.replace(".ipynb", ".md")
|
||||
|
||||
# Calculate the HTML path for the old page (whether it exists or not)
|
||||
if use_directory_urls:
|
||||
# With directory URLs: /path/to/page/ becomes /path/to/page/index.html
|
||||
if page_old.endswith(".md"):
|
||||
old_html_path = page_old[:-3] + "/index.html"
|
||||
else:
|
||||
old_html_path = page_old + "/index.html"
|
||||
else:
|
||||
# Without directory URLs: /path/to/page.md becomes /path/to/page.html
|
||||
if page_old.endswith(".md"):
|
||||
old_html_path = page_old[:-3] + ".html"
|
||||
else:
|
||||
old_html_path = page_old + ".html"
|
||||
|
||||
if isinstance(page_new, str) and page_new.startswith("http"):
|
||||
# Handle external redirects
|
||||
_write_html(config["site_dir"], old_html_path, page_new)
|
||||
else:
|
||||
# Handle internal redirects
|
||||
page_new = page_new.replace(".ipynb", ".md")
|
||||
page_new_before_hash, hash, suffix = page_new.partition("#")
|
||||
|
||||
# Try to get the new path using File class, but fallback to manual calculation
|
||||
try:
|
||||
new_html_path = File(page_new_before_hash, "", "", True).url
|
||||
new_html_path = (
|
||||
posixpath.relpath(new_html_path, start=posixpath.dirname(old_html_path))
|
||||
+ hash
|
||||
+ suffix
|
||||
)
|
||||
except:
|
||||
# Fallback: calculate relative path manually
|
||||
if use_directory_urls:
|
||||
if page_new_before_hash.endswith(".md"):
|
||||
new_html_path = page_new_before_hash[:-3] + "/"
|
||||
else:
|
||||
new_html_path = page_new_before_hash + "/"
|
||||
else:
|
||||
if page_new_before_hash.endswith(".md"):
|
||||
new_html_path = page_new_before_hash[:-3] + ".html"
|
||||
else:
|
||||
new_html_path = page_new_before_hash + ".html"
|
||||
new_html_path += hash + suffix
|
||||
|
||||
_write_html(config["site_dir"], old_html_path, new_html_path)
|
||||
page_new = page_new.replace(".ipynb", ".md")
|
||||
page_new_before_hash, hash, suffix = page_new.partition("#")
|
||||
old_html_path = File(page_old, "", "", use_directory_urls).dest_path.replace(
|
||||
os.sep, "/"
|
||||
)
|
||||
new_html_path = File(page_new_before_hash, "", "", True).url
|
||||
new_html_path = (
|
||||
posixpath.relpath(new_html_path, start=posixpath.dirname(old_html_path))
|
||||
+ hash
|
||||
+ suffix
|
||||
)
|
||||
_write_html(config["site_dir"], old_html_path, new_html_path)
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
**Context engineering** is the practice of building dynamic systems that provide the right information and tools, in the right format, so that an AI application can accomplish a task. Context can be characterized along two key dimensions:
|
||||
|
||||
1. By **mutability**:
|
||||
- **Static context**: Immutable data that doesn't change during execution (e.g., user metadata, database connections, tools)
|
||||
- **Dynamic context**: Mutable data that evolves as the application runs (e.g., conversation history, intermediate results, tool call observations)
|
||||
- **Static context**: Immutable data that doesn't change during execution (e.g., user metadata, database connections, tools)
|
||||
- **Dynamic context**: Mutable data that evolves as the application runs (e.g., conversation history, intermediate results, tool call observations)
|
||||
2. By **lifetime**:
|
||||
- **Runtime context**: Data scoped to a single run or invocation
|
||||
- **Cross-conversation context**: Data that persists across multiple conversations or sessions
|
||||
- **Runtime context**: Data scoped to a single run or invocation
|
||||
- **Cross-conversation context**: Data that persists across multiple conversations or sessions
|
||||
|
||||
!!! tip "Runtime context vs LLM context"
|
||||
|
||||
|
||||
@@ -4,16 +4,6 @@
|
||||
|
||||
---
|
||||
|
||||
## v0.2.113 (2025-07-30)
|
||||
- Improved thread search pagination by refining response headers for better navigation and accuracy.
|
||||
|
||||
## v0.2.112 (2025-07-30)
|
||||
- Ensured asynchronous handling for sync logging methods and added a linter to prevent future issues.
|
||||
- Fixed an issue where JavaScript tasks were not populating correctly in graphs.
|
||||
|
||||
## v0.2.111 (2025-07-29)
|
||||
- Started the heartbeat immediately upon connection to prevent JS graph streaming errors during long startups.
|
||||
|
||||
## v0.2.110 (2025-07-29)
|
||||
- Added interrupts as default values for all operations except streams to maintain consistent behavior.
|
||||
|
||||
|
||||
+2
-1
@@ -383,4 +383,5 @@ extra_css:
|
||||
- stylesheets/version_admonitions.css
|
||||
- stylesheets/logos.css
|
||||
- stylesheets/sticky_navigation.css
|
||||
- stylesheets/agent_graph_widget.css
|
||||
- stylesheets/agent_graph_widget.css
|
||||
|
||||
|
||||
@@ -360,5 +360,5 @@ j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
|
||||
{% endblock %}
|
||||
|
||||
{% block announce %}
|
||||
LangGraph Platform docs are moving! Find the LangGraph Platform docs at the new <a href="https://docs.langchain.com/langgraph-platform" target="_blank">LangChain Docs</a> site!
|
||||
Our <a href="https://academy.langchain.com/courses/ambient-agents/?utm_medium=internal&utm_source=docs&utm_campaign=q2-2025_ambient-agents_co" target="_blank">Building Ambient Agents with LangGraph</a> course is now available on LangChain Academy!
|
||||
{% endblock %}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "b3cec425",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This file has been moved to https://github.com/langchain-ai/langgraph/blob/main/docs/docs/how-tos/human_in_the_loop/dynamic_breakpoints.ipynb"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.9"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "4876215f",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This file has been moved to https://github.com/langchain-ai/langgraph/blob/main/docs/docs/how-tos/human_in_the_loop/edit-graph-state.ipynb"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.8"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "b162f1bd",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This file has been moved to https://github.com/langchain-ai/langgraph/blob/main/docs/docs/how-tos/human_in_the_loop/review-tool-calls.ipynb"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.9"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "84c5f6f1",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This file has been moved to https://github.com/langchain-ai/langgraph/blob/main/docs/docs/how-tos/human_in_the_loop/time-travel.ipynb"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.9"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "5eb637a4",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This file has been moved to https://github.com/langchain-ai/langgraph/blob/main/docs/docs/tutorials/multi_agent/agent_supervisor.ipynb"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.9"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
@@ -72,7 +72,7 @@ from langgraph._internal._runnable import (
|
||||
RunnableSeq,
|
||||
coerce_to_runnable,
|
||||
)
|
||||
from langgraph._internal._typing import MISSING, DeprecatedKwargs
|
||||
from langgraph._internal._typing import DeprecatedKwargs
|
||||
from langgraph.cache.base import BaseCache
|
||||
from langgraph.channels.base import BaseChannel
|
||||
from langgraph.channels.topic import Topic
|
||||
@@ -636,10 +636,7 @@ class Pregel(
|
||||
name: str = "LangGraph",
|
||||
**deprecated_kwargs: Unpack[DeprecatedKwargs],
|
||||
) -> None:
|
||||
if (
|
||||
config_type := deprecated_kwargs.get("config_type"),
|
||||
MISSING,
|
||||
) is not MISSING:
|
||||
if config_type := deprecated_kwargs.get("config_type"):
|
||||
warnings.warn(
|
||||
"`config_type` is deprecated and will be removed. Please use `context_schema` instead.",
|
||||
category=LangGraphDeprecatedSinceV10,
|
||||
@@ -3230,30 +3227,45 @@ def _output(
|
||||
|
||||
|
||||
def _coerce_context(
|
||||
context_schema: type[ContextT] | None, context: Any
|
||||
context_schema: type[ContextT] | None, context: ContextT | dict[str, Any] | None
|
||||
) -> ContextT | None:
|
||||
"""Coerce context input to the appropriate schema type.
|
||||
|
||||
If context is a dict and context_schema is a dataclass or pydantic model, we coerce.
|
||||
Else, we return the context as-is.
|
||||
|
||||
Args:
|
||||
context_schema: The schema type to coerce to (BaseModel, dataclass, or TypedDict)
|
||||
context: The context value to coerce
|
||||
|
||||
Returns:
|
||||
The coerced context value or None if context is None
|
||||
"""
|
||||
if context is None:
|
||||
return None
|
||||
|
||||
if context_schema is None:
|
||||
"""Coerce dict context to typed context schema (dataclass, pydantic model, etc.)"""
|
||||
if context is None or context_schema is None:
|
||||
return context
|
||||
|
||||
schema_is_class = issubclass(context_schema, BaseModel) or is_dataclass(
|
||||
context_schema
|
||||
)
|
||||
if isinstance(context, dict) and schema_is_class:
|
||||
return context_schema(**context) # type: ignore[misc]
|
||||
|
||||
return cast(ContextT, context)
|
||||
|
||||
# If context is a dict and schema is not a dict type, coerce it
|
||||
if isinstance(context, dict):
|
||||
from inspect import isclass
|
||||
from typing_extensions import is_typeddict
|
||||
from dataclasses import is_dataclass
|
||||
from pydantic import BaseModel
|
||||
|
||||
# Check if the schema is a typed dict, dataclass, or pydantic model
|
||||
if (
|
||||
(isclass(context_schema) and issubclass(context_schema, BaseModel)) or
|
||||
is_typeddict(context_schema) or
|
||||
is_dataclass(context_schema)
|
||||
):
|
||||
try:
|
||||
return context_schema(**context)
|
||||
except Exception as e:
|
||||
raise ValueError(
|
||||
f"Failed to coerce context dict to {context_schema}: {e}"
|
||||
) from e
|
||||
|
||||
# For non-dict contexts, check type compatibility (but avoid isinstance with TypedDict)
|
||||
# If it's not a dict and the schema is a TypedDict, we can't do much validation
|
||||
# For dataclasses and pydantic models, we can check type
|
||||
if not isinstance(context, dict):
|
||||
from inspect import isclass
|
||||
from typing_extensions import is_typeddict
|
||||
from dataclasses import is_dataclass
|
||||
from pydantic import BaseModel
|
||||
|
||||
# Only check isinstance for non-TypedDict schemas
|
||||
if not is_typeddict(context_schema):
|
||||
if isclass(context_schema) and isinstance(context, context_schema):
|
||||
return context
|
||||
|
||||
# Return as-is if no coercion is needed/possible
|
||||
return context
|
||||
|
||||
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
||||
|
||||
[project]
|
||||
name = "langgraph"
|
||||
version = "0.6.2"
|
||||
version = "0.6.1"
|
||||
description = "Building stateful, multi-actor applications with LLMs"
|
||||
authors = []
|
||||
requires-python = ">=3.9"
|
||||
|
||||
@@ -102,7 +102,6 @@ def test_config_schema_deprecation() -> None:
|
||||
match="`config_schema` is deprecated and will be removed. Please use `context_schema` instead.",
|
||||
):
|
||||
builder = StateGraph(PlainState, config_schema=PlainState)
|
||||
assert builder.context_schema == PlainState
|
||||
|
||||
builder.add_node("test_node", lambda state: state)
|
||||
builder.set_entry_point("test_node")
|
||||
@@ -112,7 +111,7 @@ def test_config_schema_deprecation() -> None:
|
||||
LangGraphDeprecatedSinceV10,
|
||||
match="`config_schema` is deprecated. Use `get_context_jsonschema` for the relevant schema instead.",
|
||||
):
|
||||
assert graph.config_schema() is not None
|
||||
graph.config_schema()
|
||||
|
||||
with pytest.warns(
|
||||
LangGraphDeprecatedSinceV10,
|
||||
@@ -121,21 +120,6 @@ def test_config_schema_deprecation() -> None:
|
||||
graph.get_config_jsonschema()
|
||||
|
||||
|
||||
@pytest.mark.filterwarnings("ignore:`config_schema` is deprecated")
|
||||
def test_config_schema_deprecation_on_entrypoint() -> None:
|
||||
with pytest.warns(
|
||||
LangGraphDeprecatedSinceV10,
|
||||
match="`config_schema` is deprecated and will be removed. Please use `context_schema` instead.",
|
||||
):
|
||||
|
||||
@entrypoint(config_schema=PlainState) # type: ignore[arg-type]
|
||||
def my_entrypoint(state: PlainState) -> PlainState:
|
||||
return state
|
||||
|
||||
assert my_entrypoint.context_schema == PlainState
|
||||
assert my_entrypoint.config_schema() is not None
|
||||
|
||||
|
||||
def test_config_type_deprecation_pregel(mocker: MockerFixture) -> None:
|
||||
add_one = mocker.Mock(side_effect=lambda x: x + 1)
|
||||
chain = NodeBuilder().subscribe_only("input").do(add_one).write_to("output")
|
||||
@@ -144,7 +128,7 @@ def test_config_type_deprecation_pregel(mocker: MockerFixture) -> None:
|
||||
LangGraphDeprecatedSinceV10,
|
||||
match="`config_type` is deprecated and will be removed. Please use `context_schema` instead.",
|
||||
):
|
||||
instance = Pregel(
|
||||
Pregel(
|
||||
nodes={
|
||||
"one": chain,
|
||||
},
|
||||
@@ -156,7 +140,6 @@ def test_config_type_deprecation_pregel(mocker: MockerFixture) -> None:
|
||||
output_channels="output",
|
||||
config_type=PlainState,
|
||||
)
|
||||
assert instance.context_schema == PlainState
|
||||
|
||||
|
||||
@pytest.mark.filterwarnings("ignore:`interrupt_id` is deprecated. Use `id` instead.")
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
from dataclasses import dataclass
|
||||
from typing import Any
|
||||
|
||||
import pytest
|
||||
from pydantic import BaseModel, ValidationError
|
||||
from typing_extensions import TypedDict
|
||||
|
||||
from langgraph.graph import END, START, StateGraph
|
||||
@@ -108,284 +106,3 @@ def test_runtime_propogated_to_subgraph() -> None:
|
||||
context = Context(username="Alice")
|
||||
result = graph.invoke({}, context=context)
|
||||
assert result == {"subgraph": "Alice!", "main": "Alice!"}
|
||||
|
||||
|
||||
def test_context_coercion_dataclass() -> None:
|
||||
"""Test that dict context is coerced to dataclass."""
|
||||
|
||||
@dataclass
|
||||
class Context:
|
||||
api_key: str
|
||||
timeout: int = 30
|
||||
|
||||
class State(TypedDict):
|
||||
message: str
|
||||
|
||||
def node_with_context(state: State, runtime: Runtime[Context]) -> dict[str, Any]:
|
||||
return {
|
||||
"message": f"api_key: {runtime.context.api_key}, timeout: {runtime.context.timeout}"
|
||||
}
|
||||
|
||||
graph = StateGraph(state_schema=State, context_schema=Context)
|
||||
graph.add_node("node", node_with_context)
|
||||
graph.add_edge(START, "node")
|
||||
graph.add_edge("node", END)
|
||||
compiled = graph.compile()
|
||||
|
||||
# Test dict coercion with all fields
|
||||
result = compiled.invoke(
|
||||
{"message": "test"}, context={"api_key": "sk_test", "timeout": 60}
|
||||
)
|
||||
assert result == {"message": "api_key: sk_test, timeout: 60"}
|
||||
|
||||
# Test dict coercion with default field
|
||||
result = compiled.invoke({"message": "test"}, context={"api_key": "sk_test2"})
|
||||
assert result == {"message": "api_key: sk_test2, timeout: 30"}
|
||||
|
||||
# Test with actual dataclass instance (should still work)
|
||||
result = compiled.invoke(
|
||||
{"message": "test"}, context=Context(api_key="sk_test3", timeout=90)
|
||||
)
|
||||
assert result == {"message": "api_key: sk_test3, timeout: 90"}
|
||||
|
||||
|
||||
def test_context_coercion_pydantic() -> None:
|
||||
"""Test that dict context is coerced to Pydantic model."""
|
||||
|
||||
class Context(BaseModel):
|
||||
api_key: str
|
||||
timeout: int = 30
|
||||
tags: list[str] = []
|
||||
|
||||
class State(TypedDict):
|
||||
message: str
|
||||
|
||||
def node_with_context(state: State, runtime: Runtime[Context]) -> dict[str, Any]:
|
||||
return {
|
||||
"message": f"api_key: {runtime.context.api_key}, timeout: {runtime.context.timeout}, tags: {runtime.context.tags}"
|
||||
}
|
||||
|
||||
graph = StateGraph(state_schema=State, context_schema=Context)
|
||||
graph.add_node("node", node_with_context)
|
||||
graph.add_edge(START, "node")
|
||||
graph.add_edge("node", END)
|
||||
compiled = graph.compile()
|
||||
|
||||
# Test dict coercion with all fields
|
||||
result = compiled.invoke(
|
||||
{"message": "test"},
|
||||
context={"api_key": "sk_test", "timeout": 60, "tags": ["prod", "v2"]},
|
||||
)
|
||||
assert result == {"message": "api_key: sk_test, timeout: 60, tags: ['prod', 'v2']"}
|
||||
|
||||
# Test dict coercion with defaults
|
||||
result = compiled.invoke({"message": "test"}, context={"api_key": "sk_test2"})
|
||||
assert result == {"message": "api_key: sk_test2, timeout: 30, tags: []"}
|
||||
|
||||
# Test with actual Pydantic instance (should still work)
|
||||
result = compiled.invoke(
|
||||
{"message": "test"},
|
||||
context=Context(api_key="sk_test3", timeout=90, tags=["test"]),
|
||||
)
|
||||
assert result == {"message": "api_key: sk_test3, timeout: 90, tags: ['test']"}
|
||||
|
||||
|
||||
def test_context_coercion_typeddict() -> None:
|
||||
"""Test that dict context with TypedDict schema passes through as-is."""
|
||||
|
||||
class Context(TypedDict):
|
||||
api_key: str
|
||||
timeout: int
|
||||
|
||||
class State(TypedDict):
|
||||
message: str
|
||||
|
||||
def node_with_context(state: State, runtime: Runtime[Context]) -> dict[str, Any]:
|
||||
# TypedDict context is just a dict at runtime
|
||||
return {
|
||||
"message": f"api_key: {runtime.context['api_key']}, timeout: {runtime.context['timeout']}"
|
||||
}
|
||||
|
||||
graph = StateGraph(state_schema=State, context_schema=Context)
|
||||
graph.add_node("node", node_with_context)
|
||||
graph.add_edge(START, "node")
|
||||
graph.add_edge("node", END)
|
||||
compiled = graph.compile()
|
||||
|
||||
# Test dict passes through for TypedDict
|
||||
result = compiled.invoke(
|
||||
{"message": "test"}, context={"api_key": "sk_test", "timeout": 60}
|
||||
)
|
||||
assert result == {"message": "api_key: sk_test, timeout: 60"}
|
||||
|
||||
|
||||
def test_context_coercion_none() -> None:
|
||||
"""Test that None context is handled properly."""
|
||||
|
||||
@dataclass
|
||||
class Context:
|
||||
api_key: str
|
||||
|
||||
class State(TypedDict):
|
||||
message: str
|
||||
|
||||
def node_without_context(state: State, runtime: Runtime[Context]) -> dict[str, Any]:
|
||||
# Should be None when no context provided
|
||||
return {"message": f"context is None: {runtime.context is None}"}
|
||||
|
||||
graph = StateGraph(state_schema=State, context_schema=Context)
|
||||
graph.add_node("node", node_without_context)
|
||||
graph.add_edge(START, "node")
|
||||
graph.add_edge("node", END)
|
||||
compiled = graph.compile()
|
||||
|
||||
# Test with None context
|
||||
result = compiled.invoke({"message": "test"}, context=None)
|
||||
assert result == {"message": "context is None: True"}
|
||||
|
||||
# Test without context parameter (defaults to None)
|
||||
result = compiled.invoke({"message": "test"})
|
||||
assert result == {"message": "context is None: True"}
|
||||
|
||||
|
||||
def test_context_coercion_errors() -> None:
|
||||
"""Test error handling for invalid context."""
|
||||
|
||||
@dataclass
|
||||
class Context:
|
||||
api_key: str # Required field
|
||||
|
||||
class State(TypedDict):
|
||||
message: str
|
||||
|
||||
def node_with_context(state: State, runtime: Runtime[Context]) -> dict[str, Any]:
|
||||
return {"message": "should not reach here"}
|
||||
|
||||
graph = StateGraph(state_schema=State, context_schema=Context)
|
||||
graph.add_node("node", node_with_context)
|
||||
graph.add_edge(START, "node")
|
||||
graph.add_edge("node", END)
|
||||
compiled = graph.compile()
|
||||
|
||||
# Test missing required field
|
||||
with pytest.raises(TypeError):
|
||||
compiled.invoke({"message": "test"}, context={"timeout": 60})
|
||||
|
||||
# Test invalid dict keys
|
||||
with pytest.raises(TypeError):
|
||||
compiled.invoke(
|
||||
{"message": "test"}, context={"api_key": "test", "invalid_field": "value"}
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_context_coercion_async() -> None:
|
||||
"""Test context coercion with async methods."""
|
||||
|
||||
@dataclass
|
||||
class Context:
|
||||
api_key: str
|
||||
async_mode: bool = True
|
||||
|
||||
class State(TypedDict):
|
||||
message: str
|
||||
|
||||
async def async_node(state: State, runtime: Runtime[Context]) -> dict[str, Any]:
|
||||
return {
|
||||
"message": f"async api_key: {runtime.context.api_key}, async_mode: {runtime.context.async_mode}"
|
||||
}
|
||||
|
||||
graph = StateGraph(state_schema=State, context_schema=Context)
|
||||
graph.add_node("node", async_node)
|
||||
graph.add_edge(START, "node")
|
||||
graph.add_edge("node", END)
|
||||
compiled = graph.compile()
|
||||
|
||||
# Test dict coercion with ainvoke
|
||||
result = await compiled.ainvoke(
|
||||
{"message": "test"}, context={"api_key": "sk_async", "async_mode": False}
|
||||
)
|
||||
assert result == {"message": "async api_key: sk_async, async_mode: False"}
|
||||
|
||||
# Test dict coercion with astream
|
||||
chunks = []
|
||||
async for chunk in compiled.astream(
|
||||
{"message": "test"}, context={"api_key": "sk_stream"}
|
||||
):
|
||||
chunks.append(chunk)
|
||||
|
||||
# Find the chunk with our node output
|
||||
node_output = None
|
||||
for chunk in chunks:
|
||||
if "node" in chunk:
|
||||
node_output = chunk["node"]
|
||||
break
|
||||
|
||||
assert node_output == {"message": "async api_key: sk_stream, async_mode: True"}
|
||||
|
||||
|
||||
def test_context_coercion_stream() -> None:
|
||||
"""Test context coercion with sync stream method."""
|
||||
|
||||
@dataclass
|
||||
class Context:
|
||||
api_key: str
|
||||
stream_mode: str = "default"
|
||||
|
||||
class State(TypedDict):
|
||||
message: str
|
||||
|
||||
def node_with_context(state: State, runtime: Runtime[Context]) -> dict[str, Any]:
|
||||
return {
|
||||
"message": f"stream api_key: {runtime.context.api_key}, mode: {runtime.context.stream_mode}"
|
||||
}
|
||||
|
||||
graph = StateGraph(state_schema=State, context_schema=Context)
|
||||
graph.add_node("node", node_with_context)
|
||||
graph.add_edge(START, "node")
|
||||
graph.add_edge("node", END)
|
||||
compiled = graph.compile()
|
||||
|
||||
# Test dict coercion with stream
|
||||
chunks = []
|
||||
for chunk in compiled.stream(
|
||||
{"message": "test"}, context={"api_key": "sk_stream", "stream_mode": "fast"}
|
||||
):
|
||||
chunks.append(chunk)
|
||||
|
||||
# Find the chunk with our node output
|
||||
node_output = None
|
||||
for chunk in chunks:
|
||||
if "node" in chunk:
|
||||
node_output = chunk["node"]
|
||||
break
|
||||
|
||||
assert node_output == {"message": "stream api_key: sk_stream, mode: fast"}
|
||||
|
||||
|
||||
def test_context_coercion_pydantic_validation_errors() -> None:
|
||||
"""Test that Pydantic validation errors are raised."""
|
||||
|
||||
class Context(BaseModel):
|
||||
api_key: str
|
||||
timeout: int
|
||||
|
||||
class State(TypedDict):
|
||||
message: str
|
||||
|
||||
def node_with_context(state: State, runtime: Runtime[Context]) -> dict[str, Any]:
|
||||
return {
|
||||
"message": f"api_key: {runtime.context.api_key}, timeout: {runtime.context.timeout}"
|
||||
}
|
||||
|
||||
graph = StateGraph(state_schema=State, context_schema=Context)
|
||||
graph.add_node("node", node_with_context)
|
||||
graph.add_edge(START, "node")
|
||||
graph.add_edge("node", END)
|
||||
|
||||
compiled = graph.compile()
|
||||
|
||||
with pytest.raises(ValidationError):
|
||||
compiled.invoke(
|
||||
{"message": "test"}, context={"api_key": "sk_test", "timeout": "not_an_int"}
|
||||
)
|
||||
|
||||
Generated
+2
-2
@@ -1192,7 +1192,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "langgraph"
|
||||
version = "0.6.2"
|
||||
version = "0.6.1"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "langchain-core" },
|
||||
@@ -1433,7 +1433,7 @@ dev = [
|
||||
|
||||
[[package]]
|
||||
name = "langgraph-prebuilt"
|
||||
version = "0.6.2"
|
||||
version = "0.6.1"
|
||||
source = { editable = "../prebuilt" }
|
||||
dependencies = [
|
||||
{ name = "langchain-core" },
|
||||
|
||||
@@ -459,11 +459,11 @@ def create_react_agent(
|
||||
config_schema := deprecated_kwargs.pop("config_schema", MISSING)
|
||||
) is not MISSING:
|
||||
warn(
|
||||
"`config_schema` is deprecated and will be removed. Please use `context_schema` instead.",
|
||||
"`config_schema` is no longer supported. Use `context_schema` instead.",
|
||||
category=LangGraphDeprecatedSinceV10,
|
||||
)
|
||||
|
||||
if context_schema is None:
|
||||
if context_schema is not None:
|
||||
context_schema = config_schema
|
||||
|
||||
if version not in ("v1", "v2"):
|
||||
|
||||
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
||||
|
||||
[project]
|
||||
name = "langgraph-prebuilt"
|
||||
version = "0.6.2"
|
||||
version = "0.6.1"
|
||||
description = "Library with high-level APIs for creating and executing LangGraph agents and tools."
|
||||
authors = []
|
||||
requires-python = ">=3.9"
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
import pytest
|
||||
from typing_extensions import TypedDict
|
||||
|
||||
from langgraph.prebuilt import create_react_agent
|
||||
from langgraph.warnings import LangGraphDeprecatedSinceV10
|
||||
from tests.model import FakeToolCallingModel
|
||||
|
||||
|
||||
class Config(TypedDict):
|
||||
model: str
|
||||
|
||||
|
||||
@pytest.mark.filterwarnings("ignore:`config_schema` is deprecated")
|
||||
@pytest.mark.filterwarnings("ignore:`get_config_jsonschema` is deprecated")
|
||||
def test_config_schema_deprecation() -> None:
|
||||
with pytest.warns(
|
||||
LangGraphDeprecatedSinceV10,
|
||||
match="`config_schema` is deprecated and will be removed. Please use `context_schema` instead.",
|
||||
):
|
||||
agent = create_react_agent(FakeToolCallingModel(), [], config_schema=Config)
|
||||
assert agent.context_schema == Config
|
||||
|
||||
with pytest.warns(
|
||||
LangGraphDeprecatedSinceV10,
|
||||
match="`config_schema` is deprecated. Use `get_context_jsonschema` for the relevant schema instead.",
|
||||
):
|
||||
assert agent.config_schema() is not None
|
||||
|
||||
with pytest.warns(
|
||||
LangGraphDeprecatedSinceV10,
|
||||
match="`get_config_jsonschema` is deprecated. Use `get_context_jsonschema` instead.",
|
||||
):
|
||||
assert agent.get_config_jsonschema() is not None
|
||||
Generated
+2
-2
@@ -316,7 +316,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "langgraph"
|
||||
version = "0.6.2"
|
||||
version = "0.6.1"
|
||||
source = { editable = "../langgraph" }
|
||||
dependencies = [
|
||||
{ name = "langchain-core" },
|
||||
@@ -460,7 +460,7 @@ dev = [
|
||||
|
||||
[[package]]
|
||||
name = "langgraph-prebuilt"
|
||||
version = "0.6.2"
|
||||
version = "0.6.1"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "langchain-core" },
|
||||
|
||||
Reference in New Issue
Block a user