mirror of
https://github.com/langchain-ai/langgraph.git
synced 2026-09-23 18:15:08 +02:00
Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
615c280b21 | ||
|
|
2180e0f80f | ||
|
|
4d983036a0 | ||
|
|
2923e670b9 |
+1
-1
@@ -16,7 +16,7 @@ build-prebuilt:
|
|||||||
uv run python -m _scripts.third_party_page.create_third_party_page stats.yml docs/agents/prebuilt.md
|
uv run python -m _scripts.third_party_page.create_third_party_page stats.yml docs/agents/prebuilt.md
|
||||||
|
|
||||||
build-docs: build-prebuilt
|
build-docs: build-prebuilt
|
||||||
TARGET_LANGUAGE=python uv run python -m mkdocs build --clean -f mkdocs.yml --strict
|
TARGET_LANGUAGE=js uv run python -m mkdocs build --clean -f mkdocs.yml --strict
|
||||||
|
|
||||||
llms-text:
|
llms-text:
|
||||||
uv run python -m _scripts.generate_llms_text docs/llms-full.txt
|
uv run python -m _scripts.generate_llms_text docs/llms-full.txt
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ PYTHON_LINK_MAP = {
|
|||||||
|
|
||||||
# JavaScript-specific link mappings
|
# JavaScript-specific link mappings
|
||||||
JS_LINK_MAP = {
|
JS_LINK_MAP = {
|
||||||
|
"Auth": "reference/classes/sdk_auth.Auth.html",
|
||||||
"StateGraph": "reference/classes/langgraph.StateGraph.html",
|
"StateGraph": "reference/classes/langgraph.StateGraph.html",
|
||||||
"add_conditional_edges": "reference/functions/langgraph_StateGraph.addConditionalEdges.html",
|
"add_conditional_edges": "reference/functions/langgraph_StateGraph.addConditionalEdges.html",
|
||||||
"add_edge": "reference/functions/langgraph_StateGraph.addEdge.html",
|
"add_edge": "reference/functions/langgraph_StateGraph.addEdge.html",
|
||||||
@@ -92,7 +93,7 @@ JS_LINK_MAP = {
|
|||||||
"entrypoint.final": "reference/functions/langgraph_func.entrypoint.final.html",
|
"entrypoint.final": "reference/functions/langgraph_func.entrypoint.final.html",
|
||||||
"entrypoint": "reference/functions/langgraph_func.entrypoint.html",
|
"entrypoint": "reference/functions/langgraph_func.entrypoint.html",
|
||||||
"from_pycryptodome_aes": "reference/functions/langgraph_checkpoint_serde_encrypted.EncryptedSerializer.fromPycryptodomeAes.html",
|
"from_pycryptodome_aes": "reference/functions/langgraph_checkpoint_serde_encrypted.EncryptedSerializer.fromPycryptodomeAes.html",
|
||||||
# "getContextVariable": "<insert-ref>",
|
"getContextVariable": "https://v03.api.js.langchain.com/functions/_langchain_core.context.getContextVariable.html",
|
||||||
"get_state_history": "reference/functions/langgraph_CompiledStateGraph.getStateHistory.html",
|
"get_state_history": "reference/functions/langgraph_CompiledStateGraph.getStateHistory.html",
|
||||||
"get_stream_writer": "reference/functions/langgraph_config.getStreamWriter.html",
|
"get_stream_writer": "reference/functions/langgraph_config.getStreamWriter.html",
|
||||||
"HumanInterrupt": "reference/classes/langgraph_prebuilt.HumanInterrupt.html",
|
"HumanInterrupt": "reference/classes/langgraph_prebuilt.HumanInterrupt.html",
|
||||||
@@ -103,8 +104,8 @@ JS_LINK_MAP = {
|
|||||||
"JsonPlusSerializer": "reference/classes/langgraph_checkpoint_serde_jsonplus.JsonPlusSerializer.html",
|
"JsonPlusSerializer": "reference/classes/langgraph_checkpoint_serde_jsonplus.JsonPlusSerializer.html",
|
||||||
"langgraph.json": "reference/configuration.html",
|
"langgraph.json": "reference/configuration.html",
|
||||||
"LastValue": "reference/classes/langgraph_channels.LastValue.html",
|
"LastValue": "reference/classes/langgraph_channels.LastValue.html",
|
||||||
# "MemorySaver": "<insert-ref>",
|
"MemorySaver": "reference/classes/checkpoint.MemorySaver.html",
|
||||||
# "messagesStateReducer": "<insert-ref>",
|
"messagesStateReducer": "reference/functions/langgraph.messagesStateReducer.html",
|
||||||
"PostgresSaver": "reference/classes/langgraph_checkpoint_postgres.PostgresSaver.html",
|
"PostgresSaver": "reference/classes/langgraph_checkpoint_postgres.PostgresSaver.html",
|
||||||
"Pregel": "reference/classes/langgraph.Pregel.html",
|
"Pregel": "reference/classes/langgraph.Pregel.html",
|
||||||
"Pregel.stream": "reference/functions/langgraph_Pregel.stream.html",
|
"Pregel.stream": "reference/functions/langgraph_Pregel.stream.html",
|
||||||
|
|||||||
@@ -286,6 +286,21 @@ def _highlight_code_blocks(markdown: str) -> str:
|
|||||||
return markdown
|
return markdown
|
||||||
|
|
||||||
|
|
||||||
|
def _save_page_output(markdown: str, output_path: str):
|
||||||
|
"""Save markdown content to a file, creating parent directories if needed.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
markdown: The markdown content to save
|
||||||
|
output_path: The file path to save to
|
||||||
|
"""
|
||||||
|
# Create parent directories recursively if they don't exist
|
||||||
|
os.makedirs(os.path.dirname(output_path), exist_ok=True)
|
||||||
|
|
||||||
|
# Write the markdown content to the file
|
||||||
|
with open(output_path, "w", encoding="utf-8") as f:
|
||||||
|
f.write(markdown)
|
||||||
|
|
||||||
|
|
||||||
def _on_page_markdown_with_config(
|
def _on_page_markdown_with_config(
|
||||||
markdown: str,
|
markdown: str,
|
||||||
page: Page,
|
page: Page,
|
||||||
@@ -338,6 +353,12 @@ def on_page_markdown(markdown: str, page: Page, **kwargs: Dict[str, Any]):
|
|||||||
**kwargs,
|
**kwargs,
|
||||||
)
|
)
|
||||||
page.meta["original_markdown"] = finalized_markdown
|
page.meta["original_markdown"] = finalized_markdown
|
||||||
|
|
||||||
|
output_path = os.environ.get("MD_OUTPUT_PATH")
|
||||||
|
if output_path:
|
||||||
|
file_path = os.path.join(output_path, page.file.src_path)
|
||||||
|
_save_page_output(finalized_markdown, file_path)
|
||||||
|
|
||||||
return finalized_markdown
|
return finalized_markdown
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+27
-92
@@ -51,38 +51,12 @@ graph.invoke( # (1)!
|
|||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
:::
|
|
||||||
|
|
||||||
:::js
|
|
||||||
|
|
||||||
| Type | Description | Mutable? | Lifetime |
|
|
||||||
| ---------------------------------------------------------------------------- | --------------------------------------------- | -------- | ----------------------- |
|
|
||||||
| [**Config**](#config-static-context) | data passed at the start of a run | ❌ | per run |
|
|
||||||
| [**Short-term memory (State)**](#short-term-memory-mutable-context) | dynamic data that can change during execution | ✅ | per run or conversation |
|
|
||||||
| [**Long-term memory (Store)**](#long-term-memory-cross-conversation-context) | data that can be shared between conversations | ✅ | across conversations |
|
|
||||||
|
|
||||||
Config is for immutable data like user metadata or API keys. Use this when you have values that don't change mid-run.
|
|
||||||
|
|
||||||
Specify configuration using a key called **"configurable"** which is reserved for this purpose.
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
await graph.invoke(
|
|
||||||
// (1)!
|
|
||||||
{ messages: [{ role: "user", content: "hi!" }] }, // (2)!
|
|
||||||
// highlight-next-line
|
|
||||||
{ configurable: { user_id: "user_123" } } // (3)!
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
:::
|
|
||||||
|
|
||||||
1. This is the invocation of the agent or graph. The `invoke` method runs the underlying graph with the provided input.
|
1. This is the invocation of the agent or graph. The `invoke` method runs the underlying graph with the provided input.
|
||||||
2. This example uses messages as an input, which is common, but your application may use different input structures.
|
2. This example uses messages as an input, which is common, but your application may use different input structures.
|
||||||
3. This is where you pass the runtime data. The `context` parameter allows you to provide additional dependencies that the agent can use during its execution.
|
3. This is where you pass the runtime data. The `context` parameter allows you to provide additional dependencies that the agent can use during its execution.
|
||||||
|
|
||||||
=== "Agent prompt"
|
=== "Agent prompt"
|
||||||
|
|
||||||
:::python
|
|
||||||
```python
|
```python
|
||||||
from langchain_core.messages import AnyMessage
|
from langchain_core.messages import AnyMessage
|
||||||
from langgraph.runtime import get_runtime
|
from langgraph.runtime import get_runtime
|
||||||
@@ -108,42 +82,11 @@ await graph.invoke(
|
|||||||
context={"user_name": "John Smith"}
|
context={"user_name": "John Smith"}
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
:::
|
|
||||||
|
|
||||||
:::js
|
|
||||||
```typescript
|
|
||||||
import type { BaseMessage } from "@langchain/core/messages";
|
|
||||||
import type { RunnableConfig } from "@langchain/core/runnables";
|
|
||||||
import type { AgentState } from "@langchain/langgraph/prebuilt";
|
|
||||||
import { createReactAgent } from "@langchain/langgraph/prebuilt";
|
|
||||||
|
|
||||||
// highlight-next-line
|
|
||||||
const prompt = (state: AgentState, config: RunnableConfig): BaseMessage[] => {
|
|
||||||
const userName = config.configurable?.user_name;
|
|
||||||
const systemMsg = `You are a helpful assistant. Address the user as ${userName}.`;
|
|
||||||
return [{ role: "system", content: systemMsg }, ...state.messages];
|
|
||||||
};
|
|
||||||
|
|
||||||
const agent = createReactAgent({
|
|
||||||
llm: model,
|
|
||||||
tools: [getWeather],
|
|
||||||
prompt,
|
|
||||||
});
|
|
||||||
|
|
||||||
await agent.invoke(
|
|
||||||
{ messages: [{ role: "user", content: "what is the weather in sf" }] },
|
|
||||||
// highlight-next-line
|
|
||||||
{ configurable: { user_name: "John Smith" } }
|
|
||||||
);
|
|
||||||
```
|
|
||||||
:::
|
|
||||||
|
|
||||||
|
|
||||||
* See [Agents](../agents/agents.md) for details.
|
* See [Agents](../agents/agents.md) for details.
|
||||||
|
|
||||||
=== "Workflow node"
|
=== "Workflow node"
|
||||||
|
|
||||||
:::python
|
|
||||||
```python
|
```python
|
||||||
from langgraph.runtime import Runtime
|
from langgraph.runtime import Runtime
|
||||||
|
|
||||||
@@ -152,25 +95,11 @@ await graph.invoke(
|
|||||||
user_name = runtime.context.user_name
|
user_name = runtime.context.user_name
|
||||||
...
|
...
|
||||||
```
|
```
|
||||||
:::
|
|
||||||
|
|
||||||
:::js
|
|
||||||
```typescript
|
|
||||||
import type { RunnableConfig } from "@langchain/core/runnables";
|
|
||||||
|
|
||||||
// highlight-next-line
|
|
||||||
const node = (state: State, config?: RunnableConfig) => {
|
|
||||||
const userName = config?.configurable?.user_name;
|
|
||||||
// ...
|
|
||||||
};
|
|
||||||
```
|
|
||||||
:::
|
|
||||||
|
|
||||||
* See [the Graph API](https://langchain-ai.github.io/langgraph/how-tos/graph-api/#add-runtime-configuration) for details.
|
* See [the Graph API](https://langchain-ai.github.io/langgraph/how-tos/graph-api/#add-runtime-configuration) for details.
|
||||||
|
|
||||||
=== "In a tool"
|
=== "In a tool"
|
||||||
|
|
||||||
:::python
|
|
||||||
```python
|
```python
|
||||||
from langgraph.runtime import get_runtime
|
from langgraph.runtime import get_runtime
|
||||||
|
|
||||||
@@ -183,27 +112,6 @@ await graph.invoke(
|
|||||||
email = get_user_email_from_db(runtime.context.user_name)
|
email = get_user_email_from_db(runtime.context.user_name)
|
||||||
return email
|
return email
|
||||||
```
|
```
|
||||||
:::
|
|
||||||
|
|
||||||
:::js
|
|
||||||
```typescript
|
|
||||||
import type { RunnableConfig } from "@langchain/core/runnables";
|
|
||||||
import { tool } from "@langchain/core/tools";
|
|
||||||
import { z } from "zod";
|
|
||||||
|
|
||||||
// highlight-next-line
|
|
||||||
const getUserInfo = tool(
|
|
||||||
async (_, config: RunnableConfig): Promise<string> => {
|
|
||||||
const userId = config.configurable?.user_id;
|
|
||||||
return userId === "user_123" ? "User is John Smith" : "Unknown user";
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "get_user_info",
|
|
||||||
description: "Retrieve user information based on user ID."
|
|
||||||
}
|
|
||||||
);
|
|
||||||
```
|
|
||||||
:::
|
|
||||||
|
|
||||||
See the [tool calling guide](../how-tos/tool-calling.md#configuration) for details.
|
See the [tool calling guide](../how-tos/tool-calling.md#configuration) for details.
|
||||||
|
|
||||||
@@ -212,6 +120,33 @@ await graph.invoke(
|
|||||||
The `Runtime` object can be used to access static context and other utilities like the active store and stream writer.
|
The `Runtime` object can be used to access static context and other utilities like the active store and stream writer.
|
||||||
See the [Runtime][langgraph.runtime.Runtime] documentation for details.
|
See the [Runtime][langgraph.runtime.Runtime] documentation for details.
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
:::js
|
||||||
|
|
||||||
|
| Context type | Description | Mutability | Lifetime |
|
||||||
|
| ------------------------------------------------------------------------------------------- | --------------------------------------------- | ---------- | ------------------ |
|
||||||
|
| [**Config**](#config-static-context) | data passed at the start of a run | ❌ | per run |
|
||||||
|
| [**Dynamic runtime context (state)**](#dynamic-runtime-context-state) | Mutable data that evolves during a single run | Dynamic | Single run |
|
||||||
|
| [**Dynamic cross-conversation context (store)**](#dynamic-cross-conversation-context-store) | Persistent data shared across conversations | Dynamic | Cross-conversation |
|
||||||
|
|
||||||
|
## Config (static context)
|
||||||
|
|
||||||
|
Config is for immutable data like user metadata or API keys. Use this when you have values that don't change mid-run.
|
||||||
|
|
||||||
|
Specify configuration using a key called **"configurable"** which is reserved for this purpose.
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
await graph.invoke(
|
||||||
|
// (1)!
|
||||||
|
{ messages: [{ role: "user", content: "hi!" }] }, // (2)!
|
||||||
|
// highlight-next-line
|
||||||
|
{ configurable: { user_id: "user_123" } } // (3)!
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
## Dynamic runtime context (state)
|
## Dynamic runtime context (state)
|
||||||
|
|
||||||
**Dynamic runtime context** represents mutable data that can evolve during a single run and is managed through the LangGraph state object. This includes conversation history, intermediate results, and values derived from tools or LLM outputs. In LangGraph, the state object acts as [short-term memory](../concepts/memory.md) during a run.
|
**Dynamic runtime context** represents mutable data that can evolve during a single run and is managed through the LangGraph state object. This includes conversation history, intermediate results, and values derived from tools or LLM outputs. In LangGraph, the state object acts as [short-term memory](../concepts/memory.md) during a run.
|
||||||
|
|||||||
@@ -208,12 +208,12 @@ The high-level components are organized into several packages, each with a speci
|
|||||||
|
|
||||||
## Visualize an agent graph
|
## Visualize an agent graph
|
||||||
|
|
||||||
Use the following tool to visualize the graph generated by [`createReactAgent`](/langgraphjs/reference/functions/langgraph_prebuilt.createReactAgent.html) and to view an outline of the corresponding code. It allows you to explore the infrastructure of the agent as defined by the presence of:
|
Use the following tool to visualize the graph generated by @[`createReactAgent`][create_react_agent] and to view an outline of the corresponding code. It allows you to explore the infrastructure of the agent as defined by the presence of:
|
||||||
|
|
||||||
- [`tools`](./tools.md): A list of tools (functions, APIs, or other callable objects) that the agent can use to perform tasks.
|
- [`tools`](./tools.md): A list of tools (functions, APIs, or other callable objects) that the agent can use to perform tasks.
|
||||||
- `preModelHook`: A function that is called before the model is invoked. It can be used to condense messages or perform other preprocessing tasks.
|
- `preModelHook`: A function that is called before the model is invoked. It can be used to condense messages or perform other preprocessing tasks.
|
||||||
- `postModelHook`: A function that is called after the model is invoked. It can be used to implement guardrails, human-in-the-loop flows, or other postprocessing tasks.
|
- `postModelHook`: A function that is called after the model is invoked. It can be used to implement guardrails, human-in-the-loop flows, or other postprocessing tasks.
|
||||||
- [`responseFormat`](./agents.md#structured-output): A data structure used to constrain the type of the final output (via Zod schemas).
|
- [`responseFormat`](./agents.md#6-configure-structured-output): A data structure used to constrain the type of the final output (via Zod schemas).
|
||||||
|
|
||||||
<div class="agent-layout">
|
<div class="agent-layout">
|
||||||
<div class="agent-graph-features-container">
|
<div class="agent-graph-features-container">
|
||||||
@@ -232,7 +232,7 @@ Use the following tool to visualize the graph generated by [`createReactAgent`](
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
The following code snippet shows how to create the above agent (and underlying graph) with [`createReactAgent`](/langgraphjs/reference/functions/langgraph_prebuilt.createReactAgent.html):
|
The following code snippet shows how to create the above agent (and underlying graph) with @[`createReactAgent`][create_react_agent]:
|
||||||
|
|
||||||
<div class="language-typescript">
|
<div class="language-typescript">
|
||||||
<pre><code id="agent-code" class="language-typescript"></code></pre>
|
<pre><code id="agent-code" class="language-typescript"></code></pre>
|
||||||
|
|||||||
@@ -43,7 +43,9 @@ First, as a brief refresher on the concept of runtime context, consider the foll
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
:::python
|
||||||
For more information on runtime context, [see here](../../concepts/low_level.md#runtime-context).
|
For more information on runtime context, [see here](../../concepts/low_level.md#runtime-context).
|
||||||
|
:::
|
||||||
|
|
||||||
## Create an assistant
|
## Create an assistant
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,10 @@ The LangGraph Cloud API provides several endpoints for creating and managing ass
|
|||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
|
:::python
|
||||||
Assistants build on the LangGraph open source concepts of configuration and [runtime context](low_level.md#runtime-context).
|
Assistants build on the LangGraph open source concepts of configuration and [runtime context](low_level.md#runtime-context).
|
||||||
|
:::
|
||||||
|
|
||||||
While these features are available in the open source LangGraph library, assistants are only present in [LangGraph Platform](langgraph_platform.md). This is due to the fact that assistants are tightly coupled to your deployed graph. Upon deployment, LangGraph Server will automatically create a default assistant for each graph using the graph's default context and configuration settings.
|
While these features are available in the open source LangGraph library, assistants are only present in [LangGraph Platform](langgraph_platform.md). This is due to the fact that assistants are tightly coupled to your deployed graph. Upon deployment, LangGraph Server will automatically create a default assistant for each graph using the graph's default context and configuration settings.
|
||||||
|
|
||||||
In practice, an assistant is just an _instance_ of a graph with a specific configuration. Therefore, multiple assistants can reference the same graph but can contain different configurations (e.g. prompts, models, tools). The LangGraph Server API provides several endpoints for creating and managing assistants. See the [API reference](../cloud/reference/api/api_ref.html) and [this how-to](../cloud/how-tos/configuration_cloud.md) for more details on how to create assistants.
|
In practice, an assistant is just an _instance_ of a graph with a specific configuration. Therefore, multiple assistants can reference the same graph but can contain different configurations (e.g. prompts, models, tools). The LangGraph Server API provides several endpoints for creating and managing assistants. See the [API reference](../cloud/reference/api/api_ref.html) and [this how-to](../cloud/how-tos/configuration_cloud.md) for more details on how to create assistants.
|
||||||
|
|||||||
@@ -1159,7 +1159,7 @@ Under the hood, checkpointing is powered by checkpointer objects that conform to
|
|||||||
|
|
||||||
:::js
|
:::js
|
||||||
|
|
||||||
- `@langchain/langgraph-checkpoint`: The base interface for checkpointer savers (@[BaseCheckpointSaver][BaseCheckpointSaver]) and serialization/deserialization interface (@[SerializerProtocol][SerializerProtocol]). Includes in-memory checkpointer implementation (@[InMemorySaver) for experimentation. LangGraph comes with `@langchain/langgraph-checkpoint` included.
|
- `@langchain/langgraph-checkpoint`: The base interface for checkpointer savers (@[BaseCheckpointSaver][BaseCheckpointSaver]) and serialization/deserialization interface (@[SerializerProtocol][SerializerProtocol]). Includes in-memory checkpointer implementation (@[MemorySaver]) for experimentation. LangGraph comes with `@langchain/langgraph-checkpoint` included.
|
||||||
- `@langchain/langgraph-checkpoint-sqlite`: An implementation of LangGraph checkpointer that uses SQLite database (@[SqliteSaver]). Ideal for experimentation and local workflows. Needs to be installed separately.
|
- `@langchain/langgraph-checkpoint-sqlite`: An implementation of LangGraph checkpointer that uses SQLite database (@[SqliteSaver]). Ideal for experimentation and local workflows. Needs to be installed separately.
|
||||||
- `@langchain/langgraph-checkpoint-postgres`: An advanced checkpointer that uses Postgres database (@[PostgresSaver]), used in LangGraph Platform. Ideal for using in production. Needs to be installed separately.
|
- `@langchain/langgraph-checkpoint-postgres`: An advanced checkpointer that uses Postgres database (@[PostgresSaver]), used in LangGraph Platform. Ideal for using in production. Needs to be installed separately.
|
||||||
|
|
||||||
|
|||||||
@@ -89,7 +89,9 @@ After deployment, you can update the name and description using the LangGraph SD
|
|||||||
|
|
||||||
Define clear, minimal input and output schemas to avoid exposing unnecessary internal complexity to the LLM.
|
Define clear, minimal input and output schemas to avoid exposing unnecessary internal complexity to the LLM.
|
||||||
|
|
||||||
|
:::python
|
||||||
The default [MessagesState](./low_level.md#messagesstate) uses `AnyMessage`, which supports many message types but is too general for direct LLM exposure.
|
The default [MessagesState](./low_level.md#messagesstate) uses `AnyMessage`, which supports many message types but is too general for direct LLM exposure.
|
||||||
|
:::
|
||||||
|
|
||||||
Instead, define **custom agents or workflows** that use explicitly typed input and output structures.
|
Instead, define **custom agents or workflows** that use explicitly typed input and output structures.
|
||||||
|
|
||||||
|
|||||||
@@ -137,6 +137,7 @@ def my_node(state, config):
|
|||||||
```
|
```
|
||||||
|
|
||||||
!!! note
|
!!! note
|
||||||
|
|
||||||
Fetch user credentials from a secure secret store. Storing secrets in graph state is not recommended.
|
Fetch user credentials from a secure secret store. Storing secrets in graph state is not recommended.
|
||||||
|
|
||||||
### Authorizing a Studio user
|
### Authorizing a Studio user
|
||||||
@@ -264,6 +265,25 @@ Only use this if you want to permit developer access to a graph deployed on the
|
|||||||
curl -H "Authorization: Bearer ${your-token}" http://localhost:2024/threads
|
curl -H "Authorization: Bearer ${your-token}" http://localhost:2024/threads
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Enable agent authentication
|
||||||
|
|
||||||
|
After [authentication](#add-custom-authentication-to-your-deployment), the platform creates a special configuration object (`config`) that is passed to LangGraph Platform deployment. This object contains information about the current user, including any custom fields you return from your `authenticate` handler.
|
||||||
|
|
||||||
|
To allow an agent to perform authenticated actions on behalf of the user, access this object in your graph with the `langgraph_auth_user` key:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
async function myNode(state, config) {
|
||||||
|
const userConfig = config["configurable"]["langgraph_auth_user"];
|
||||||
|
// token was resolved during the authenticate function
|
||||||
|
const token = userConfig["github_token"];
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
|
||||||
|
Fetch user credentials from a secure secret store. Storing secrets in graph state is not recommended.
|
||||||
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
## Learn more
|
## Learn more
|
||||||
|
|||||||
@@ -26,7 +26,9 @@ Here we show how to define and update [state](../concepts/low_level.md#state) in
|
|||||||
|
|
||||||
By default, graphs will have the same input and output schema, and the state determines that schema. See [this section](#define-input-and-output-schemas) for how to define distinct input and output schemas.
|
By default, graphs will have the same input and output schema, and the state determines that schema. See [this section](#define-input-and-output-schemas) for how to define distinct input and output schemas.
|
||||||
|
|
||||||
|
:::python
|
||||||
Let's consider a simple example using [messages](../concepts/low_level.md#messagesstate). This represents a versatile formulation of state for many LLM applications. See our [concepts page](../concepts/low_level.md#working-with-messages-in-graph-state) for more detail.
|
Let's consider a simple example using [messages](../concepts/low_level.md#messagesstate). This represents a versatile formulation of state for many LLM applications. See our [concepts page](../concepts/low_level.md#working-with-messages-in-graph-state) for more detail.
|
||||||
|
:::
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from langchain_core.messages import AnyMessage
|
from langchain_core.messages import AnyMessage
|
||||||
@@ -86,6 +88,7 @@ from langchain_core.messages import HumanMessage
|
|||||||
result = graph.invoke({"messages": [HumanMessage("Hi")]})
|
result = graph.invoke({"messages": [HumanMessage("Hi")]})
|
||||||
result
|
result
|
||||||
```
|
```
|
||||||
|
|
||||||
```
|
```
|
||||||
{'messages': [HumanMessage(content='Hi'), AIMessage(content='Hello!')], 'extra_field': 10}
|
{'messages': [HumanMessage(content='Hi'), AIMessage(content='Hello!')], 'extra_field': 10}
|
||||||
```
|
```
|
||||||
@@ -101,6 +104,7 @@ For convenience, we frequently inspect the content of [message objects](https://
|
|||||||
for message in result["messages"]:
|
for message in result["messages"]:
|
||||||
message.pretty_print()
|
message.pretty_print()
|
||||||
```
|
```
|
||||||
|
|
||||||
```
|
```
|
||||||
================================ Human Message ================================
|
================================ Human Message ================================
|
||||||
|
|
||||||
@@ -139,6 +143,7 @@ def node(state: State):
|
|||||||
# highlight-next-line
|
# highlight-next-line
|
||||||
return {"messages": [new_message], "extra_field": 10}
|
return {"messages": [new_message], "extra_field": 10}
|
||||||
```
|
```
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from langgraph.graph import START
|
from langgraph.graph import START
|
||||||
|
|
||||||
@@ -149,6 +154,7 @@ result = graph.invoke({"messages": [HumanMessage("Hi")]})
|
|||||||
for message in result["messages"]:
|
for message in result["messages"]:
|
||||||
message.pretty_print()
|
message.pretty_print()
|
||||||
```
|
```
|
||||||
|
|
||||||
```
|
```
|
||||||
================================ Human Message ================================
|
================================ Human Message ================================
|
||||||
|
|
||||||
@@ -191,6 +197,7 @@ result = graph.invoke({"messages": [input_message]})
|
|||||||
for message in result["messages"]:
|
for message in result["messages"]:
|
||||||
message.pretty_print()
|
message.pretty_print()
|
||||||
```
|
```
|
||||||
|
|
||||||
```
|
```
|
||||||
================================ Human Message ================================
|
================================ Human Message ================================
|
||||||
|
|
||||||
@@ -248,6 +255,7 @@ graph = builder.compile() # Compile the graph
|
|||||||
# Invoke the graph with an input and print the result
|
# Invoke the graph with an input and print the result
|
||||||
print(graph.invoke({"question": "hi"}))
|
print(graph.invoke({"question": "hi"}))
|
||||||
```
|
```
|
||||||
|
|
||||||
```
|
```
|
||||||
{'answer': 'bye'}
|
{'answer': 'bye'}
|
||||||
```
|
```
|
||||||
@@ -310,6 +318,7 @@ response = graph.invoke(
|
|||||||
print()
|
print()
|
||||||
print(f"Output of graph invocation: {response}")
|
print(f"Output of graph invocation: {response}")
|
||||||
```
|
```
|
||||||
|
|
||||||
```
|
```
|
||||||
Entered node `node_1`:
|
Entered node `node_1`:
|
||||||
Input: {'a': 'set at start'}.
|
Input: {'a': 'set at start'}.
|
||||||
@@ -332,11 +341,7 @@ In our examples, we typically use a python-native `TypedDict` or [`dataclass`](h
|
|||||||
|
|
||||||
Here, we'll see how a [Pydantic BaseModel](https://docs.pydantic.dev/latest/api/base_model/) can be used for `state_schema` to add run-time validation on **inputs**.
|
Here, we'll see how a [Pydantic BaseModel](https://docs.pydantic.dev/latest/api/base_model/) can be used for `state_schema` to add run-time validation on **inputs**.
|
||||||
|
|
||||||
!!! note "Known Limitations"
|
!!! note "Known Limitations" - Currently, the output of the graph will **NOT** be an instance of a pydantic model. - Run-time validation only occurs on inputs into nodes, not on the outputs. - The validation error trace from pydantic does not show which node the error arises in. - Pydantic's recursive validation can be slow. For performance-sensitive applications, you may want to consider using a `dataclass` instead.
|
||||||
- Currently, the output of the graph will **NOT** be an instance of a pydantic model.
|
|
||||||
- Run-time validation only occurs on inputs into nodes, not on the outputs.
|
|
||||||
- The validation error trace from pydantic does not show which node the error arises in.
|
|
||||||
- Pydantic's recursive validation can be slow. For performance-sensitive applications, you may want to consider using a `dataclass` instead.
|
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from langgraph.graph import StateGraph, START, END
|
from langgraph.graph import StateGraph, START, END
|
||||||
@@ -370,6 +375,7 @@ except Exception as e:
|
|||||||
print("An exception was raised because `a` is an integer rather than a string.")
|
print("An exception was raised because `a` is an integer rather than a string.")
|
||||||
print(e)
|
print(e)
|
||||||
```
|
```
|
||||||
|
|
||||||
```
|
```
|
||||||
An exception was raised because `a` is an integer rather than a string.
|
An exception was raised because `a` is an integer rather than a string.
|
||||||
1 validation error for OverallState
|
1 validation error for OverallState
|
||||||
@@ -503,7 +509,7 @@ See below for additional features of Pydantic model state:
|
|||||||
|
|
||||||
## Add runtime configuration
|
## Add runtime configuration
|
||||||
|
|
||||||
Sometimes you want to be able to configure your graph when calling it. For example, you might want to be able to specify what LLM or system prompt to use at runtime, *without polluting the graph state with these parameters*.
|
Sometimes you want to be able to configure your graph when calling it. For example, you might want to be able to specify what LLM or system prompt to use at runtime, _without polluting the graph state with these parameters_.
|
||||||
|
|
||||||
To add runtime configuration:
|
To add runtime configuration:
|
||||||
|
|
||||||
@@ -551,6 +557,7 @@ print(graph.invoke({}, context={"my_runtime_value": "a"}))
|
|||||||
# highlight-next-line
|
# highlight-next-line
|
||||||
print(graph.invoke({}, context={"my_runtime_value": "b"}))
|
print(graph.invoke({}, context={"my_runtime_value": "b"}))
|
||||||
```
|
```
|
||||||
|
|
||||||
```
|
```
|
||||||
{'my_state_value': 1}
|
{'my_state_value': 1}
|
||||||
{'my_state_value': 2}
|
{'my_state_value': 2}
|
||||||
@@ -673,18 +680,18 @@ builder.add_node(
|
|||||||
|
|
||||||
By default, the `retry_on` parameter uses the `default_retry_on` function, which retries on any exception except for the following:
|
By default, the `retry_on` parameter uses the `default_retry_on` function, which retries on any exception except for the following:
|
||||||
|
|
||||||
* `ValueError`
|
- `ValueError`
|
||||||
* `TypeError`
|
- `TypeError`
|
||||||
* `ArithmeticError`
|
- `ArithmeticError`
|
||||||
* `ImportError`
|
- `ImportError`
|
||||||
* `LookupError`
|
- `LookupError`
|
||||||
* `NameError`
|
- `NameError`
|
||||||
* `SyntaxError`
|
- `SyntaxError`
|
||||||
* `RuntimeError`
|
- `RuntimeError`
|
||||||
* `ReferenceError`
|
- `ReferenceError`
|
||||||
* `StopIteration`
|
- `StopIteration`
|
||||||
* `StopAsyncIteration`
|
- `StopAsyncIteration`
|
||||||
* `OSError`
|
- `OSError`
|
||||||
|
|
||||||
In addition, for exceptions from popular http request libraries such as `requests` and `httpx` it only retries on 5xx status codes.
|
In addition, for exceptions from popular http request libraries such as `requests` and `httpx` it only retries on 5xx status codes.
|
||||||
|
|
||||||
@@ -834,7 +841,9 @@ def step_3(state: State):
|
|||||||
|
|
||||||
Finally, we define the graph. We use [StateGraph](../concepts/low_level.md#stategraph) to define a graph that operates on this state.
|
Finally, we define the graph. We use [StateGraph](../concepts/low_level.md#stategraph) to define a graph that operates on this state.
|
||||||
|
|
||||||
|
:::python
|
||||||
We will then use [add_node](../concepts/low_level.md#messagesstate) and [add_edge](../concepts/low_level.md#edges) to populate our graph and define its control flow.
|
We will then use [add_node](../concepts/low_level.md#messagesstate) and [add_edge](../concepts/low_level.md#edges) to populate our graph and define its control flow.
|
||||||
|
:::
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from langgraph.graph import START, StateGraph
|
from langgraph.graph import START, StateGraph
|
||||||
@@ -886,6 +895,7 @@ Let's proceed with a simple invocation:
|
|||||||
```python
|
```python
|
||||||
graph.invoke({"value_1": "c"})
|
graph.invoke({"value_1": "c"})
|
||||||
```
|
```
|
||||||
|
|
||||||
```
|
```
|
||||||
{'value_1': 'a b', 'value_2': 10}
|
{'value_1': 'a b', 'value_2': 10}
|
||||||
```
|
```
|
||||||
@@ -971,6 +981,7 @@ With the reducer, you can see that the values added in each node are accumulated
|
|||||||
```python
|
```python
|
||||||
graph.invoke({"aggregate": []}, {"configurable": {"thread_id": "foo"}})
|
graph.invoke({"aggregate": []}, {"configurable": {"thread_id": "foo"}})
|
||||||
```
|
```
|
||||||
|
|
||||||
```
|
```
|
||||||
Adding "A" to []
|
Adding "A" to []
|
||||||
Adding "B" to ['A']
|
Adding "B" to ['A']
|
||||||
@@ -1059,6 +1070,7 @@ display(Image(graph.get_graph().draw_mermaid_png()))
|
|||||||
```python
|
```python
|
||||||
graph.invoke({"aggregate": []})
|
graph.invoke({"aggregate": []})
|
||||||
```
|
```
|
||||||
|
|
||||||
```
|
```
|
||||||
Adding "A" to []
|
Adding "A" to []
|
||||||
Adding "B" to ['A']
|
Adding "B" to ['A']
|
||||||
@@ -1129,6 +1141,7 @@ display(Image(graph.get_graph().draw_mermaid_png()))
|
|||||||
result = graph.invoke({"aggregate": []})
|
result = graph.invoke({"aggregate": []})
|
||||||
print(result)
|
print(result)
|
||||||
```
|
```
|
||||||
|
|
||||||
```
|
```
|
||||||
Adding "A" to []
|
Adding "A" to []
|
||||||
Adding "C" to ['A']
|
Adding "C" to ['A']
|
||||||
@@ -1203,6 +1216,7 @@ display(Image(graph.get_graph().draw_mermaid_png()))
|
|||||||
for step in graph.stream({"topic": "animals"}):
|
for step in graph.stream({"topic": "animals"}):
|
||||||
print(step)
|
print(step)
|
||||||
```
|
```
|
||||||
|
|
||||||
```
|
```
|
||||||
{'generate_topics': {'subjects': ['lions', 'elephants', 'penguins']}}
|
{'generate_topics': {'subjects': ['lions', 'elephants', 'penguins']}}
|
||||||
{'generate_joke': {'jokes': ["Why don't lions like fast food? Because they can't catch it!"]}}
|
{'generate_joke': {'jokes': ["Why don't lions like fast food? Because they can't catch it!"]}}
|
||||||
@@ -1307,6 +1321,7 @@ Invoking the graph, we see that we alternate between nodes `"a"` and `"b"` befor
|
|||||||
```python
|
```python
|
||||||
graph.invoke({"aggregate": []})
|
graph.invoke({"aggregate": []})
|
||||||
```
|
```
|
||||||
|
|
||||||
```
|
```
|
||||||
Node A sees []
|
Node A sees []
|
||||||
Node B sees ['A']
|
Node B sees ['A']
|
||||||
@@ -1329,6 +1344,7 @@ try:
|
|||||||
except GraphRecursionError:
|
except GraphRecursionError:
|
||||||
print("Recursion Error")
|
print("Recursion Error")
|
||||||
```
|
```
|
||||||
|
|
||||||
```
|
```
|
||||||
Node A sees []
|
Node A sees []
|
||||||
Node B sees ['A']
|
Node B sees ['A']
|
||||||
@@ -1620,6 +1636,7 @@ If we run the graph multiple times, we'd see it take different paths (A -> B or
|
|||||||
```python
|
```python
|
||||||
graph.invoke({"foo": ""})
|
graph.invoke({"foo": ""})
|
||||||
```
|
```
|
||||||
|
|
||||||
```
|
```
|
||||||
Called A
|
Called A
|
||||||
Called C
|
Called C
|
||||||
@@ -1698,6 +1715,7 @@ graph = builder.compile()
|
|||||||
```python
|
```python
|
||||||
graph.invoke({"foo": ""})
|
graph.invoke({"foo": ""})
|
||||||
```
|
```
|
||||||
|
|
||||||
```
|
```
|
||||||
Called A
|
Called A
|
||||||
Called C
|
Called C
|
||||||
@@ -1794,6 +1812,7 @@ We can also convert a graph class into Mermaid syntax.
|
|||||||
```python
|
```python
|
||||||
print(app.get_graph().draw_mermaid())
|
print(app.get_graph().draw_mermaid())
|
||||||
```
|
```
|
||||||
|
|
||||||
```
|
```
|
||||||
%%{init: {'flowchart': {'curve': 'linear'}}}%%
|
%%{init: {'flowchart': {'curve': 'linear'}}}%%
|
||||||
graph TD;
|
graph TD;
|
||||||
|
|||||||
@@ -1973,7 +1973,7 @@ def delete_messages(state):
|
|||||||
:::
|
:::
|
||||||
|
|
||||||
:::js
|
:::js
|
||||||
To delete messages from the graph state, you can use the `RemoveMessage`. For `RemoveMessage` to work, you need to use a state key with @[`messagesStateReducer`][messagesStateReducer] [reducer](../../concepts/low_level.md#reducers), like [`MessagesZodState`](../../concepts/low_level.md#messageszodstate).
|
To delete messages from the graph state, you can use the `RemoveMessage`. For `RemoveMessage` to work, you need to use a state key with @[`messagesStateReducer`][messagesStateReducer] [reducer](../../concepts/low_level.md#reducers), like `MessagesZodState`.
|
||||||
|
|
||||||
To remove specific messages:
|
To remove specific messages:
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ There could be a few reasons you're seeing this error:
|
|||||||
This interrupt could have been triggered in one of the following ways:
|
This interrupt could have been triggered in one of the following ways:
|
||||||
|
|
||||||
- You manually set `interruptBefore: ['tools']` in `createReactAgent`
|
- You manually set `interruptBefore: ['tools']` in `createReactAgent`
|
||||||
- One of the tools raised an error that wasn't handled by the [ToolNode][ToolNode] (`"tools"`)
|
- One of the tools raised an error that wasn't handled by the @[ToolNode][ToolNode] (`"tools"`)
|
||||||
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
|
|||||||
@@ -212,7 +212,7 @@ The handler receives two parameters:
|
|||||||
:::js
|
:::js
|
||||||
The handler receives an object with the following properties:
|
The handler receives an object with the following properties:
|
||||||
|
|
||||||
1. `user` ([ProxyUser](../../cloud/reference/sdk/js_ts_sdk_ref.md#langgraph_sdk.auth.types.ProxyUser)): contains info about the current `user`, the user's `permissions`, the `resource` ("threads", "crons", "assistants")
|
1. `user` contains info about the current `user`, the user's `permissions`, the `resource` ("threads", "crons", "assistants")
|
||||||
2. `action` contains information about the action being taken ("create", "read", "update", "delete", "search", "create_run")
|
2. `action` contains information about the action being taken ("create", "read", "update", "delete", "search", "create_run")
|
||||||
3. `value` (`Record<string, any>`): data that is being created or accessed. The contents of this object depend on the resource and action being accessed. See [adding scoped authorization handlers](#scoped-authorization) below for information on how to get more tightly scoped access control.
|
3. `value` (`Record<string, any>`): data that is being created or accessed. The contents of this object depend on the resource and action being accessed. See [adding scoped authorization handlers](#scoped-authorization) below for information on how to get more tightly scoped access control.
|
||||||
:::
|
:::
|
||||||
@@ -589,9 +589,13 @@ Now that you can control access to resources, you might want to:
|
|||||||
2. Read more about [authorization patterns](../../concepts/auth.md#authorization).
|
2. Read more about [authorization patterns](../../concepts/auth.md#authorization).
|
||||||
|
|
||||||
:::python
|
:::python
|
||||||
|
|
||||||
3. Check out the [API reference](../../cloud/reference/sdk/python_sdk_ref.md#langgraph_sdk.auth.Auth) for details about the interfaces and methods used in this tutorial.
|
3. Check out the [API reference](../../cloud/reference/sdk/python_sdk_ref.md#langgraph_sdk.auth.Auth) for details about the interfaces and methods used in this tutorial.
|
||||||
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
:::js
|
:::js
|
||||||
|
|
||||||
3. Check out the [API reference](../../cloud/reference/sdk/js_sdk_ref.md#langgraph_sdk.auth.Auth) for details about the interfaces and methods used in this tutorial.
|
3. Check out the [API reference](../../cloud/reference/sdk/js_sdk_ref.md#langgraph_sdk.auth.Auth) for details about the interfaces and methods used in this tutorial.
|
||||||
|
|
||||||
:::
|
:::
|
||||||
|
|||||||
@@ -109,7 +109,15 @@ Now that we have our split documents, we can index them into a vector store that
|
|||||||
|
|
||||||
## 3. Generate query
|
## 3. Generate query
|
||||||
|
|
||||||
Now we will start building components ([nodes](../../concepts/low_level.md#nodes) and [edges](../../concepts/low_level.md#edges)) for our agentic RAG graph. Note that the components will operate on the [`MessagesState`](../../concepts/low_level.md#messagesstate) — graph state that contains a `messages` key with a list of [chat messages](https://python.langchain.com/docs/concepts/messages/).
|
Now we will start building components ([nodes](../../concepts/low_level.md#nodes) and [edges](../../concepts/low_level.md#edges)) for our agentic RAG graph.
|
||||||
|
|
||||||
|
:::python
|
||||||
|
Note that the components will operate on the [`MessagesState`](../../concepts/low_level.md#messagesstate) — graph state that contains a `messages` key with a list of [chat messages](https://python.langchain.com/docs/concepts/messages/).
|
||||||
|
:::
|
||||||
|
|
||||||
|
:::js
|
||||||
|
Note that the components will operate on the `MessagesZodState` — graph state that contains a `messages` key with a list of [chat messages](https://js.langchain.com/docs/concepts/messages/).
|
||||||
|
:::
|
||||||
|
|
||||||
1. Build a `generate_query_or_respond` node. It will call an LLM to generate a response based on the current graph state (list of messages). Given the input messages, it will decide to retrieve using the retriever tool, or respond directly to the user. Note that we're giving the chat model access to the `retriever_tool` we created earlier via `.bind_tools`:
|
1. Build a `generate_query_or_respond` node. It will call an LLM to generate a response based on the current graph state (list of messages). Given the input messages, it will decide to retrieve using the retriever tool, or respond directly to the user. Note that we're giving the chat model access to the `retriever_tool` we created earlier via `.bind_tools`:
|
||||||
|
|
||||||
|
|||||||
@@ -1274,7 +1274,7 @@ With orchestrator-worker, an orchestrator breaks down a task and delegates each
|
|||||||
|
|
||||||
**Creating Workers in LangGraph**
|
**Creating Workers in LangGraph**
|
||||||
|
|
||||||
Because orchestrator-worker workflows are common, LangGraph **has the `Send` API to support this**. It lets you dynamically create worker nodes and send each one a specific input. Each worker has its own state, and all worker outputs are written to a *shared state key* that is accessible to the orchestrator graph. This gives the orchestrator access to all worker output and allows it to synthesize them into a final output. As you can see below, we iterate over a list of sections and `Send` each to a worker node. See further documentation [here](../how-tos/map-reduce/) and [here](../concepts/low_level/#send).
|
Because orchestrator-worker workflows are common, LangGraph **has the `Send` API to support this**. It lets you dynamically create worker nodes and send each one a specific input. Each worker has its own state, and all worker outputs are written to a *shared state key* that is accessible to the orchestrator graph. This gives the orchestrator access to all worker output and allows it to synthesize them into a final output. As you can see below, we iterate over a list of sections and `Send` each to a worker node. See further documentation [here](https://langchain-ai.github.io/langgraph/how-tos/map-reduce/) and [here](https://langchain-ai.github.io/langgraph/concepts/low_level/#send).
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
import { withLangGraph } from "@langchain/langgraph/zod";
|
import { withLangGraph } from "@langchain/langgraph/zod";
|
||||||
|
|||||||
Generated
+2
-2
@@ -2337,7 +2337,7 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "langgraph"
|
name = "langgraph"
|
||||||
version = "0.6.0"
|
version = "0.6.1"
|
||||||
source = { editable = "../libs/langgraph" }
|
source = { editable = "../libs/langgraph" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "langchain-core" },
|
{ name = "langchain-core" },
|
||||||
@@ -2641,7 +2641,7 @@ test = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "langgraph-prebuilt"
|
name = "langgraph-prebuilt"
|
||||||
version = "0.6.0"
|
version = "0.6.1"
|
||||||
source = { editable = "../libs/prebuilt" }
|
source = { editable = "../libs/prebuilt" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "langchain-core" },
|
{ name = "langchain-core" },
|
||||||
|
|||||||
Reference in New Issue
Block a user