mirror of
https://github.com/langchain-ai/langgraph.git
synced 2026-08-18 05:35:43 +02:00
Compare commits
73
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
993ba33d76 | ||
|
|
ea20432b9b | ||
|
|
e2efab8061 | ||
|
|
9babffa054 | ||
|
|
73cebea3c2 | ||
|
|
b73b2d19eb | ||
|
|
ca26805b5f | ||
|
|
5ac837d7cd | ||
|
|
c4f5861166 | ||
|
|
172238b2d5 | ||
|
|
095da17833 | ||
|
|
e931c68669 | ||
|
|
666c224c2d | ||
|
|
21a6f41e0a | ||
|
|
acdf85aba6 | ||
|
|
9b18243fa6 | ||
|
|
0225b998af | ||
|
|
bec122d4a2 | ||
|
|
2b416f6f47 | ||
|
|
9b9de5bd16 | ||
|
|
3b04ee4677 | ||
|
|
e80b3136ad | ||
|
|
4eb1766b58 | ||
|
|
a76cf88232 | ||
|
|
bd6da75a85 | ||
|
|
7889a907e5 | ||
|
|
762b8f8579 | ||
|
|
83fcca8687 | ||
|
|
5da9a1d844 | ||
|
|
17b3285907 | ||
|
|
20570cf700 | ||
|
|
df94475d3a | ||
|
|
270621db66 | ||
|
|
a181e0bb91 | ||
|
|
b233201308 | ||
|
|
443cee2fb3 | ||
|
|
d280bca8da | ||
|
|
3701fa4806 | ||
|
|
72be9b23ee | ||
|
|
52bbd34673 | ||
|
|
7216504ce2 | ||
|
|
fe4daa1c7c | ||
|
|
34769f31bc | ||
|
|
eac6abb8ee | ||
|
|
9f0ae94f27 | ||
|
|
f5e56e200d | ||
|
|
f9870bc9ae | ||
|
|
a734f5e6ce | ||
|
|
84446f5ad8 | ||
|
|
f6d95abbe3 | ||
|
|
a7a27dd43a | ||
|
|
50238be239 | ||
|
|
114978b612 | ||
|
|
0c0a159539 | ||
|
|
f688b068e7 | ||
|
|
1fb405bd55 | ||
|
|
86b65beb8f | ||
|
|
63bd852da9 | ||
|
|
82f9c09b95 | ||
|
|
193e128c20 | ||
|
|
c94e7b96ac | ||
|
|
2dd39432a3 | ||
|
|
3ff6340379 | ||
|
|
0a6145fd72 | ||
|
|
fbcb8a911b | ||
|
|
2c6f99cbf0 | ||
|
|
7b9ff6129b | ||
|
|
c1b3598ca8 | ||
|
|
30355a7a5d | ||
|
|
b0c6126f2a | ||
|
|
0cab88c7dd | ||
|
|
8cb87eaf76 | ||
|
|
089cdd0ffb |
@@ -0,0 +1,177 @@
|
||||
---
|
||||
name: build-checkpointer
|
||||
description: Build a LangGraph checkpoint saver implementation that passes all conformance tests. Use when creating a new checkpointer for any storage backend (Redis, DynamoDB, MongoDB, etc.) or wrapping an existing storage client.
|
||||
disable-model-invocation: true
|
||||
user-invocable: true
|
||||
argument-hint: [storage-backend]
|
||||
---
|
||||
|
||||
# Build a Conformant LangGraph Checkpointer
|
||||
|
||||
You are building a LangGraph checkpoint saver for the **$ARGUMENTS** storage backend. Your goal is FULL conformance: all 82 tests across 8 capabilities must pass.
|
||||
|
||||
Read [interface-reference.md](interface-reference.md) for method signatures, data structures, and the conformance test harness template.
|
||||
Read [critical-contracts.md](critical-contracts.md) for the 8 most common failure points.
|
||||
Read [sqlite-reference.md](sqlite-reference.md) for patterns from a working implementation.
|
||||
|
||||
## Ground Rules
|
||||
|
||||
**You are not done until ALL conformance tests pass.** Do not stop after writing code — you must run the tests, read failures, fix, and re-run in a loop until you see FULL conformance. If you hit a wall, try a different approach rather than giving up.
|
||||
|
||||
**No hacks or shortcuts.** Specifically:
|
||||
- Do NOT skip or xfail tests to make the suite "pass"
|
||||
- Do NOT weaken assertions or modify the conformance test suite itself
|
||||
- Do NOT use `# type: ignore` to paper over real type mismatches
|
||||
- Do NOT store data in global/module-level dicts to fake persistence — use the actual storage backend
|
||||
- Do NOT disable serialization or store raw Python objects — use `self.serde.dumps_typed` / `loads_typed`
|
||||
- Do NOT catch and swallow exceptions to hide failures
|
||||
|
||||
**Flag security concerns.** As you implement:
|
||||
- Ensure all queries use parameterized statements — never interpolate user-provided values (thread_id, checkpoint_id, etc.) into SQL or query strings
|
||||
- Check for injection risks in metadata filtering (JSON path queries, NoSQL operators, etc.)
|
||||
- Ensure connection credentials are not hardcoded in the implementation — accept them as constructor args
|
||||
- Flag any backend client library that has known CVEs or security advisories
|
||||
- If the backend requires TLS/auth, note it prominently in the constructor docstring
|
||||
|
||||
**Ask the user for help when you need it.** Don't guess or assume — ask when:
|
||||
- You need database connection details, credentials, or access
|
||||
- You're unsure which client library or driver to use
|
||||
- You need the user to start/stop a database service
|
||||
- You're stuck on a test failure after multiple attempts
|
||||
- You're unsure about a design decision (e.g., schema layout, indexing strategy)
|
||||
|
||||
**Safety checks — ask the user to confirm:**
|
||||
- "Is this database safe to use for testing? Please confirm it is NOT a production database." (before running any tests that create/delete tables)
|
||||
- "I'm about to create tables and run destructive test operations (INSERT, DELETE, DROP). Is this OK?" (before first test run)
|
||||
- "What connection string / credentials should I use?" (never assume defaults for non-local databases)
|
||||
|
||||
## Step 1: Understand the target
|
||||
|
||||
Determine the storage backend from the arguments. If no arguments were provided, ask the user:
|
||||
- What storage backend? (Redis, DynamoDB, MongoDB, Cassandra, etc.)
|
||||
- From scratch, or wrapping an existing client/library?
|
||||
- Any connection/authentication requirements?
|
||||
- How do I connect to a test instance? (Docker compose, local install, cloud sandbox, etc.)
|
||||
|
||||
Install the backend's Python client library if needed.
|
||||
|
||||
## Step 2: Scaffold the package
|
||||
|
||||
Create `libs/checkpoint-<backend>/` with this structure:
|
||||
|
||||
```
|
||||
libs/checkpoint-<backend>/
|
||||
pyproject.toml
|
||||
Makefile
|
||||
langgraph/
|
||||
checkpoint/
|
||||
<backend>/
|
||||
__init__.py # Main implementation
|
||||
tests/
|
||||
test_conformance.py # Conformance harness
|
||||
```
|
||||
|
||||
The `pyproject.toml` should depend on:
|
||||
- `langgraph-checkpoint` (the base interfaces)
|
||||
- The backend's client library
|
||||
- `langgraph-checkpoint-conformance` as a test dependency
|
||||
|
||||
Model the `Makefile` after `libs/checkpoint-sqlite/Makefile`.
|
||||
|
||||
## Step 3: Implement the checkpointer
|
||||
|
||||
Subclass `BaseCheckpointSaver` and implement ALL 8 async methods:
|
||||
|
||||
**Required (5):** `aput`, `aget_tuple`, `alist`, `aput_writes`, `adelete_thread`
|
||||
**Extended (3):** `adelete_for_runs`, `acopy_thread`, `aprune`
|
||||
|
||||
Key implementation guidance:
|
||||
|
||||
1. **Storage layout depends on your backend.** Choose the layout that fits your backend's strengths:
|
||||
- **SQL databases (Postgres, MySQL, SQLite):** Use 3 tables — checkpoints, checkpoint_blobs (channel values keyed by version), checkpoint_writes. The blobs table avoids re-serializing unchanged large values on every checkpoint write. Inline primitive channel values (str, int, float, bool, None) in the checkpoint JSON; store non-primitives as blobs keyed by `(thread_id, checkpoint_ns, channel, version)`.
|
||||
- **Document stores (MongoDB, DynamoDB, Firestore):** Use 2 collections — checkpoints (with channel values embedded) and writes. Serialize the full checkpoint including all channel values. The blob optimization adds complexity without much benefit in document stores.
|
||||
- **Key-value stores (Redis, etcd):** Use composite keys to namespace checkpoints and writes. Store serialized checkpoint + writes as values.
|
||||
|
||||
See `critical-contracts.md` for composite key design.
|
||||
|
||||
3. **Serialize blobs and writes with `self.serde`** — use `self.serde.dumps_typed(value)` which returns `(type_str, bytes)` and `self.serde.loads_typed((type_str, bytes))` for deserialization. For CPU-bound serialization, use `asyncio.to_thread()` to avoid blocking the event loop.
|
||||
|
||||
4. **Serialize metadata as JSON** — use `get_checkpoint_metadata(config, metadata)` to merge config metadata before storing, then `json.dumps()`. Deserialize with `json.loads()`. Metadata is small enough to store inline (no blob table needed).
|
||||
|
||||
5. **Handle `new_versions` correctly** — this is the #1 source of failures. The checkpoint's `channel_values` contains ALL channels, but `new_versions` only lists CHANGED channels. If using a blob table (SQL pattern), only write blobs for channels in `new_versions` and reference all versions in the checkpoint JSON. If storing the full checkpoint (document/KV pattern), just serialize all of `checkpoint["channel_values"]` — simpler and correct.
|
||||
|
||||
6. **Handle `WRITES_IDX_MAP`** — special channels (ERROR, INTERRUPT, SCHEDULED, RESUME) use fixed negative indices. Regular writes use their positional index. Special channel writes should UPSERT (replace on conflict); regular writes should be idempotent (ignore on conflict).
|
||||
|
||||
7. **Return correct `parent_config`** — the `checkpoint_id` in the incoming config to `aput` is the parent. When returning `CheckpointTuple`, set `parent_config` to a config with that parent checkpoint_id, or None if there was no parent.
|
||||
|
||||
### Production-quality patterns
|
||||
|
||||
Go beyond "just passing tests" — build something that performs well at scale:
|
||||
|
||||
- **Connection pooling.** Accept both a single connection and a connection pool in the constructor. Use a pool for production workloads. For Postgres, use `psycopg_pool.AsyncConnectionPool`. For Redis, use the client's built-in pool. Document which to use.
|
||||
- **Use native backend features.** Don't treat the backend as a dumb key-value store. Examples:
|
||||
- Postgres: use JSONB containment (`@>`) for metadata filtering, `COPY FROM STDIN` for bulk inserts, `DISTINCT ON` for pruning, pipeline mode for batching
|
||||
- Redis: use Lua scripts for atomic operations, sorted sets for ordering, hash fields for channel blobs
|
||||
- DynamoDB: use query vs scan appropriately, batch write items, GSIs for metadata filtering
|
||||
- MongoDB: use `$match` aggregation stages, bulk write operations, compound indexes
|
||||
- **Batch writes where possible.** In `aput`, group the checkpoint insert and blob upserts into a single round-trip (pipeline, transaction, or batch write). Don't make N separate calls for N blobs.
|
||||
- **Fetch writes alongside checkpoints in a single query.** Use subqueries, JOINs, or array aggregation to avoid N+1 patterns where you fetch N checkpoints then query writes for each one separately.
|
||||
- **Use `asyncio.to_thread()` for CPU-bound serialization** — `serde.dumps_typed` and `serde.loads_typed` can be expensive for large values. Offload to a thread to keep the event loop responsive.
|
||||
- **Add appropriate indexes.** At minimum: primary/unique keys on all collections, and an index on `thread_id` for `adelete_thread`. For `adelete_for_runs`, consider an index on the metadata `run_id` field if the backend supports it.
|
||||
|
||||
## Step 4: Run conformance and iterate — DO NOT STOP UNTIL GREEN
|
||||
|
||||
```bash
|
||||
cd libs/checkpoint-<backend>
|
||||
pip install -e ".[test]"
|
||||
python -m pytest tests/test_conformance.py -x -v
|
||||
```
|
||||
|
||||
Or run via `make test` if your Makefile is set up.
|
||||
|
||||
**This is the core of the task.** You MUST loop:
|
||||
|
||||
1. Run the conformance tests
|
||||
2. Read the failure output carefully — it tells you exactly which contract was violated
|
||||
3. Understand WHY it failed — read the test source in `libs/checkpoint-conformance/langgraph/checkpoint/conformance/spec/` if the error message isn't clear
|
||||
4. Fix the implementation with a proper solution (not a hack — see Ground Rules)
|
||||
5. Re-run. Go back to step 1.
|
||||
|
||||
**Do not stop until `report.passed_all()` returns True.** If you've been through 5+ iterations and are still failing, step back and re-read the critical-contracts.md and the failing test source code. The answer is always in the test — it specifies exactly what the contract requires.
|
||||
|
||||
**If you're blocked, ask the user.** Common things to ask about:
|
||||
- "The database isn't reachable — can you check the connection / start the service?"
|
||||
- "I'm stuck on this test failure after N attempts — here's what I've tried, can you help?"
|
||||
- "I need to install this package / run this command — is that OK?"
|
||||
|
||||
Common failure patterns:
|
||||
- `test_put_incremental_channel_update` fails → you're not storing all channel values, only the ones in `new_versions`
|
||||
- `test_put_writes_idempotent` fails → your write upsert logic is wrong, check `WRITES_IDX_MAP` handling
|
||||
- `test_list_global_search` fails → you're requiring a thread_id when config is None
|
||||
- `test_get_tuple_pending_writes` fails → writes not ordered by `(task_id, idx)` or missing `task_id` in tuple
|
||||
- `test_list_metadata_filter_*` fails → metadata filtering not checking all keys, or not handling custom keys
|
||||
|
||||
## Step 5: Final verification and review
|
||||
|
||||
Run the full suite one more time with verbose output:
|
||||
|
||||
```bash
|
||||
python -m pytest tests/test_conformance.py -v
|
||||
```
|
||||
|
||||
Confirm the output shows FULL conformance (all 82 tests pass). The report should show:
|
||||
- PUT: all pass
|
||||
- PUT_WRITES: all pass
|
||||
- GET_TUPLE: all pass
|
||||
- LIST: all pass
|
||||
- DELETE_THREAD: all pass
|
||||
- DELETE_FOR_RUNS: all pass
|
||||
- COPY_THREAD: all pass
|
||||
- PRUNE: all pass
|
||||
|
||||
Then do a final review of your implementation:
|
||||
|
||||
1. **Run `make lint` and `make format`** to clean up the code
|
||||
2. **Security review** — check for SQL/query injection, hardcoded credentials, unvalidated inputs
|
||||
3. **Performance review** — check for N+1 query patterns (fetching writes per checkpoint in a loop), missing indexes on frequently-filtered columns, unnecessary full-table scans
|
||||
4. **Report findings** — tell the user about any security concerns, performance considerations, or caveats about the implementation
|
||||
@@ -0,0 +1,86 @@
|
||||
# Critical Contracts — Common Failure Points
|
||||
|
||||
These are the 8 requirements most likely to cause test failures. Get these right and you'll pass.
|
||||
|
||||
## 1. Store the FULL checkpoint, not just the diff
|
||||
|
||||
`aput` receives `new_versions` which lists only CHANGED channels. But `checkpoint["channel_values"]` contains ALL channels. You must store all of them. The `new_versions` parameter is informational — some implementations use it to optimize blob storage by only writing changed blobs, but the simplest correct approach is to serialize and store the entire checkpoint.
|
||||
|
||||
**Failing test:** `test_put_incremental_channel_update`, `test_put_new_channel_added`, `test_put_channel_removed`
|
||||
|
||||
## 2. Write idempotency with WRITES_IDX_MAP
|
||||
|
||||
The unique key for a write is `(thread_id, checkpoint_ns, checkpoint_id, task_id, idx)`.
|
||||
|
||||
The `idx` comes from `WRITES_IDX_MAP.get(channel, positional_index)`:
|
||||
- Special channels: `__error__` → -1, `__interrupt__` → -3, `__scheduled__` → -2, `__resume__` → -4
|
||||
- Regular channels: use their positional index in the writes list (0, 1, 2, ...)
|
||||
|
||||
For **special channels** (all writes are in WRITES_IDX_MAP): use UPSERT (replace on conflict) because these channels get updated in place.
|
||||
|
||||
For **regular channels**: use INSERT-ignore-on-conflict to be idempotent — calling `aput_writes` twice with the same `(task_id, idx)` must not create duplicates.
|
||||
|
||||
```python
|
||||
if all(w[0] in WRITES_IDX_MAP for w in writes):
|
||||
# UPSERT — replace existing
|
||||
else:
|
||||
# INSERT OR IGNORE — idempotent
|
||||
```
|
||||
|
||||
**Failing test:** `test_put_writes_idempotent`, `test_put_writes_special_channels`
|
||||
|
||||
## 3. Namespace isolation
|
||||
|
||||
`checkpoint_ns` (from `config["configurable"].get("checkpoint_ns", "")`) is part of the composite key for BOTH checkpoints and writes. Default to empty string `""` if not present.
|
||||
|
||||
Two checkpoints with the same `thread_id` and `checkpoint_id` but different `checkpoint_ns` are DIFFERENT checkpoints.
|
||||
|
||||
**Failing test:** `test_put_child_namespace`, `test_put_writes_across_namespaces`, `test_get_tuple_respects_namespace`
|
||||
|
||||
## 4. Metadata round-trip
|
||||
|
||||
Before storing metadata, call `get_checkpoint_metadata(config, metadata)` which merges additional keys from config. Store the result as JSON. When loading, deserialize back to dict.
|
||||
|
||||
ALL keys must survive — standard ones (`source`, `step`, `parents`, `run_id`) AND custom keys the caller added.
|
||||
|
||||
**Failing test:** `test_put_preserves_metadata`, `test_list_metadata_custom_keys`
|
||||
|
||||
## 5. Global search: `alist(None, filter=...)`
|
||||
|
||||
When `config` is `None`, `alist` must search across ALL threads. Don't require `thread_id`. Filter by metadata keys if `filter` is provided.
|
||||
|
||||
**Failing test:** `test_list_global_search`
|
||||
|
||||
## 6. parent_config in CheckpointTuple
|
||||
|
||||
When `aput(config, checkpoint, ...)` is called, `config["configurable"].get("checkpoint_id")` is the PARENT checkpoint ID. Store this as `parent_checkpoint_id`.
|
||||
|
||||
When returning `CheckpointTuple`:
|
||||
- If `parent_checkpoint_id` exists: set `parent_config = {"configurable": {"thread_id": ..., "checkpoint_ns": ..., "checkpoint_id": parent_checkpoint_id}}`
|
||||
- If no parent: set `parent_config = None`
|
||||
|
||||
**Failing test:** `test_put_parent_config`, `test_get_tuple_parent_config`
|
||||
|
||||
## 7. Pending writes in CheckpointTuple
|
||||
|
||||
`pending_writes` must be a list of `(task_id, channel, deserialized_value)` tuples, ordered by `(task_id, idx)`.
|
||||
|
||||
Every `aget_tuple` and every tuple yielded by `alist` must include pending writes. Don't forget to query the writes table/collection.
|
||||
|
||||
**Failing test:** `test_get_tuple_pending_writes`, `test_list_includes_pending_writes`
|
||||
|
||||
## 8. Checkpoint ordering in alist
|
||||
|
||||
`alist` must return checkpoints in descending order by `checkpoint_id` (newest first). Checkpoint IDs are UUID-like strings that sort chronologically. Use `ORDER BY checkpoint_id DESC` or equivalent.
|
||||
|
||||
The `before` parameter means: only return checkpoints with `checkpoint_id < before_checkpoint_id`.
|
||||
|
||||
**Failing test:** `test_list_ordering`, `test_list_before`, `test_list_limit_plus_before`
|
||||
|
||||
## 9. Storage design principles
|
||||
|
||||
All backends must key checkpoints by `(thread_id, checkpoint_ns, checkpoint_id)` and writes by `(thread_id, checkpoint_ns, checkpoint_id, task_id, idx)`.
|
||||
|
||||
- **SQL backends:** Consider a 3rd blobs table keyed by `(thread_id, checkpoint_ns, channel, version)` to avoid re-serializing unchanged large channel values. Only write blobs for channels in `new_versions`; reconstruct all values on read via `channel_versions`.
|
||||
- **Document/KV backends:** Embed all channel values directly in the checkpoint document/value. Serialize the full checkpoint on every `aput` — simpler and correct.
|
||||
- **All backends need:** descending `checkpoint_id` ordering for `alist`, metadata field filtering for `alist(filter=...)`, delete by `thread_id` for `adelete_thread`, delete by `metadata.run_id` for `adelete_for_runs`.
|
||||
@@ -0,0 +1,228 @@
|
||||
# Checkpointer Interface Reference
|
||||
|
||||
## Imports
|
||||
|
||||
```python
|
||||
from langgraph.checkpoint.base import (
|
||||
WRITES_IDX_MAP,
|
||||
BaseCheckpointSaver,
|
||||
ChannelVersions,
|
||||
Checkpoint,
|
||||
CheckpointMetadata,
|
||||
CheckpointTuple,
|
||||
SerializerProtocol,
|
||||
get_checkpoint_id,
|
||||
get_checkpoint_metadata,
|
||||
)
|
||||
from langgraph.checkpoint.serde.jsonplus import JsonPlusSerializer
|
||||
from langchain_core.runnables import RunnableConfig
|
||||
```
|
||||
|
||||
## Data Structures
|
||||
|
||||
```python
|
||||
# RunnableConfig["configurable"] keys:
|
||||
# thread_id: str — identifies the conversation thread
|
||||
# checkpoint_ns: str — namespace (empty string for root, dotted path for subgraphs)
|
||||
# checkpoint_id: str — unique monotonically-increasing ID (UUID-like, sortable)
|
||||
|
||||
# Checkpoint (TypedDict):
|
||||
# v: int — format version (currently 1)
|
||||
# id: str — unique checkpoint ID
|
||||
# ts: str — ISO 8601 timestamp
|
||||
# channel_values: dict[str, Any] — serialized state per channel
|
||||
# channel_versions: ChannelVersions — version number per channel
|
||||
# versions_seen: dict[str, ChannelVersions] — per-node version tracking
|
||||
|
||||
# CheckpointMetadata (TypedDict):
|
||||
# source: str — "input" | "loop" | "update" | "fork"
|
||||
# step: int — -1 for input, 0+ for loop steps
|
||||
# parents: dict[str, str] — parent checkpoint IDs
|
||||
# (plus any custom keys the caller adds)
|
||||
|
||||
# CheckpointTuple (NamedTuple):
|
||||
# config: RunnableConfig
|
||||
# checkpoint: Checkpoint
|
||||
# metadata: CheckpointMetadata
|
||||
# parent_config: RunnableConfig | None
|
||||
# pending_writes: list[tuple[str, str, Any]] | None
|
||||
# Each write is (task_id, channel, value)
|
||||
|
||||
# ChannelVersions = dict[str, Any] (typically str or int version numbers)
|
||||
|
||||
# WRITES_IDX_MAP = {"__error__": -1, "__scheduled__": -2, "__interrupt__": -3, "__resume__": -4}
|
||||
```
|
||||
|
||||
## Method Signatures
|
||||
|
||||
### Required Methods
|
||||
|
||||
```python
|
||||
async def aput(
|
||||
self,
|
||||
config: RunnableConfig,
|
||||
checkpoint: Checkpoint,
|
||||
metadata: CheckpointMetadata,
|
||||
new_versions: ChannelVersions,
|
||||
) -> RunnableConfig:
|
||||
"""Store a checkpoint. Return config with checkpoint_id set to checkpoint["id"].
|
||||
|
||||
The incoming config["configurable"]["checkpoint_id"] is the PARENT checkpoint ID.
|
||||
new_versions contains only the channels that changed — but checkpoint["channel_values"]
|
||||
has ALL channels. Store the full checkpoint.
|
||||
"""
|
||||
|
||||
async def aget_tuple(self, config: RunnableConfig) -> CheckpointTuple | None:
|
||||
"""Retrieve a checkpoint.
|
||||
|
||||
If config has checkpoint_id: return that exact checkpoint.
|
||||
If no checkpoint_id: return the LATEST checkpoint for the thread+namespace.
|
||||
Return None if not found.
|
||||
Include pending_writes as list of (task_id, channel, value) ordered by (task_id, idx).
|
||||
"""
|
||||
|
||||
async def alist(
|
||||
self,
|
||||
config: RunnableConfig | None,
|
||||
*,
|
||||
filter: dict[str, Any] | None = None,
|
||||
before: RunnableConfig | None = None,
|
||||
limit: int | None = None,
|
||||
) -> AsyncIterator[CheckpointTuple]:
|
||||
"""List checkpoints, newest first (descending checkpoint_id).
|
||||
|
||||
If config is None: search ALL threads (global search).
|
||||
If config has thread_id: filter to that thread.
|
||||
filter: dict of metadata key-value pairs (AND logic).
|
||||
before: only return checkpoints before this checkpoint_id.
|
||||
limit: max number to return.
|
||||
Each yielded tuple must include pending_writes.
|
||||
"""
|
||||
|
||||
async def aput_writes(
|
||||
self,
|
||||
config: RunnableConfig,
|
||||
writes: Sequence[tuple[str, Any]],
|
||||
task_id: str,
|
||||
task_path: str = "",
|
||||
) -> None:
|
||||
"""Store pending writes for a checkpoint.
|
||||
|
||||
Each write is (channel, value). Use WRITES_IDX_MAP.get(channel, idx) for the index.
|
||||
Special channels (in WRITES_IDX_MAP) should UPSERT (replace on conflict).
|
||||
Regular channels should be idempotent (ignore on conflict).
|
||||
"""
|
||||
|
||||
async def adelete_thread(self, thread_id: str) -> None:
|
||||
"""Delete ALL checkpoints and writes for a thread (all namespaces)."""
|
||||
```
|
||||
|
||||
### Extended Methods
|
||||
|
||||
```python
|
||||
async def adelete_for_runs(self, run_ids: Sequence[str]) -> None:
|
||||
"""Delete checkpoints+writes where metadata.run_id is in run_ids."""
|
||||
|
||||
async def acopy_thread(self, source_thread_id: str, target_thread_id: str) -> None:
|
||||
"""Copy all checkpoints+writes from source thread to target thread."""
|
||||
|
||||
async def aprune(
|
||||
self,
|
||||
thread_ids: Sequence[str],
|
||||
*,
|
||||
strategy: str = "keep_latest",
|
||||
) -> None:
|
||||
"""Prune checkpoints for given threads.
|
||||
strategy="keep_latest": keep only the latest checkpoint per thread+namespace.
|
||||
strategy="delete_all": delete everything for those threads.
|
||||
"""
|
||||
```
|
||||
|
||||
## Conformance Test Harness Template
|
||||
|
||||
Create `tests/test_conformance.py`:
|
||||
|
||||
```python
|
||||
"""Conformance tests for <Backend>Saver."""
|
||||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
from langgraph.checkpoint.conformance import checkpointer_test, validate
|
||||
from langgraph.checkpoint.conformance.report import ProgressCallbacks
|
||||
|
||||
# Import your checkpointer
|
||||
from langgraph.checkpoint.<backend> import <Backend>Saver
|
||||
|
||||
|
||||
# Optional: lifespan for one-time setup/teardown (database creation, etc.)
|
||||
# async def backend_lifespan():
|
||||
# # setup
|
||||
# yield
|
||||
# # teardown
|
||||
|
||||
|
||||
@checkpointer_test(name="<Backend>Saver") # add lifespan=backend_lifespan if needed
|
||||
async def backend_checkpointer():
|
||||
# Create and yield a fresh checkpointer instance.
|
||||
# Use async with if your saver needs connection management.
|
||||
saver = <Backend>Saver(...)
|
||||
yield saver
|
||||
# cleanup (close connections, etc.)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_full_conformance():
|
||||
"""<Backend>Saver passes ALL conformance tests."""
|
||||
report = await validate(
|
||||
backend_checkpointer,
|
||||
progress=ProgressCallbacks.verbose(),
|
||||
)
|
||||
report.print_report()
|
||||
assert report.passed_all(), f"Conformance failed: {report.to_dict()}"
|
||||
```
|
||||
|
||||
## Serialization Pattern
|
||||
|
||||
```python
|
||||
# In __init__:
|
||||
super().__init__(serde=serde)
|
||||
|
||||
# Storing metadata (use JSON, not serde):
|
||||
merged = get_checkpoint_metadata(config, metadata)
|
||||
serialized_md = json.dumps(merged).encode("utf-8")
|
||||
|
||||
# Loading metadata:
|
||||
metadata = json.loads(serialized_md_bytes)
|
||||
|
||||
# Storing/loading blob values and write values (use serde):
|
||||
type_, serialized = self.serde.dumps_typed(value)
|
||||
value = self.serde.loads_typed((type_, serialized_bytes))
|
||||
|
||||
# For CPU-bound serde in async context, offload to thread:
|
||||
type_, serialized = await asyncio.to_thread(self.serde.dumps_typed, value)
|
||||
value = await asyncio.to_thread(self.serde.loads_typed, (type_, serialized_bytes))
|
||||
```
|
||||
|
||||
## Schema Design by Backend Type
|
||||
|
||||
All backends must store checkpoints keyed by `(thread_id, checkpoint_ns, checkpoint_id)` and writes keyed by `(thread_id, checkpoint_ns, checkpoint_id, task_id, idx)`.
|
||||
|
||||
### SQL backends (Postgres, MySQL, SQLite)
|
||||
|
||||
Use 3 tables: **checkpoints** (checkpoint JSON with primitive channel_values inlined + channel_versions for blob lookup, metadata JSON), **checkpoint_blobs** (non-primitive channel values keyed by `(thread_id, checkpoint_ns, channel, version)`), and **checkpoint_writes** (pending writes). The blobs table avoids re-serializing unchanged large values — only write blobs for channels in `new_versions`. On read, JOIN blobs via `channel_versions` to reconstruct all channel values. PKs on all three tables handle most access patterns; add an index on the metadata `run_id` field for `adelete_for_runs`. Use subqueries/JOINs to fetch writes alongside checkpoints in a single round-trip.
|
||||
|
||||
### Document stores (MongoDB, Firestore, DynamoDB)
|
||||
|
||||
Use 2 collections: **checkpoints** (full checkpoint with all channel_values embedded, metadata as top-level fields) and **writes**. Serialize the full checkpoint including all channel values on every `aput`. Use composite `_id` or PK/SK from the key parts. Required indexes:
|
||||
- `(thread_id, checkpoint_ns, checkpoint_id DESC)` — for `alist` ordering and `aget_tuple` latest-lookup
|
||||
- `(thread_id)` — for `adelete_thread`
|
||||
- `(metadata.run_id)` — for `adelete_for_runs`
|
||||
- Use native query operators (e.g. MongoDB `$match`, DynamoDB filter expressions) for metadata filtering in `alist(filter=...)`
|
||||
|
||||
### Key-value stores (Redis, etcd)
|
||||
|
||||
Use composite keys like `cp:{thread_id}:{ns}:{id}`. Use sorted sets or equivalent for descending-order listing. **Requires manual secondary indexes** maintained on every write:
|
||||
- Thread index (`thread:{thread_id}` → set of `{ns}:{checkpoint_id}`) — for `adelete_thread` and `alist`
|
||||
- Run ID index (`run:{run_id}` → set of checkpoint keys) — for `adelete_for_runs`
|
||||
- Write index (`writes:{thread_id}:{ns}:{checkpoint_id}` → set of `{task_id}:{idx}`) — for pending writes lookup
|
||||
- Metadata filtering for `alist(filter=...)` is the hardest: either scan+deserialize, or maintain per-field indexes. For small datasets scanning is acceptable; for large ones consider a search module.
|
||||
@@ -0,0 +1,174 @@
|
||||
# SQLite Implementation Reference
|
||||
|
||||
Working patterns from `libs/checkpoint-sqlite/langgraph/checkpoint/sqlite/aio.py`.
|
||||
|
||||
## Schema
|
||||
|
||||
```sql
|
||||
CREATE TABLE IF NOT EXISTS checkpoints (
|
||||
thread_id TEXT NOT NULL,
|
||||
checkpoint_ns TEXT NOT NULL DEFAULT '',
|
||||
checkpoint_id TEXT NOT NULL,
|
||||
parent_checkpoint_id TEXT,
|
||||
type TEXT,
|
||||
checkpoint BLOB,
|
||||
metadata BLOB,
|
||||
PRIMARY KEY (thread_id, checkpoint_ns, checkpoint_id)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS writes (
|
||||
thread_id TEXT NOT NULL,
|
||||
checkpoint_ns TEXT NOT NULL DEFAULT '',
|
||||
checkpoint_id TEXT NOT NULL,
|
||||
task_id TEXT NOT NULL,
|
||||
idx INTEGER NOT NULL,
|
||||
channel TEXT NOT NULL,
|
||||
type TEXT,
|
||||
value BLOB,
|
||||
PRIMARY KEY (thread_id, checkpoint_ns, checkpoint_id, task_id, idx)
|
||||
);
|
||||
```
|
||||
|
||||
## aput pattern
|
||||
|
||||
```python
|
||||
async def aput(self, config, checkpoint, metadata, new_versions):
|
||||
thread_id = config["configurable"]["thread_id"]
|
||||
checkpoint_ns = config["configurable"].get("checkpoint_ns", "")
|
||||
parent_checkpoint_id = config["configurable"].get("checkpoint_id")
|
||||
|
||||
type_, serialized_checkpoint = self.serde.dumps_typed(checkpoint)
|
||||
serialized_metadata = json.dumps(
|
||||
get_checkpoint_metadata(config, metadata), ensure_ascii=False
|
||||
).encode("utf-8", "ignore")
|
||||
|
||||
# UPSERT checkpoint row
|
||||
await db.execute(
|
||||
"INSERT OR REPLACE INTO checkpoints (...) VALUES (...)",
|
||||
(thread_id, checkpoint_ns, checkpoint["id"], parent_checkpoint_id,
|
||||
type_, serialized_checkpoint, serialized_metadata),
|
||||
)
|
||||
|
||||
return {
|
||||
"configurable": {
|
||||
"thread_id": thread_id,
|
||||
"checkpoint_ns": checkpoint_ns,
|
||||
"checkpoint_id": checkpoint["id"],
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## aget_tuple pattern
|
||||
|
||||
```python
|
||||
async def aget_tuple(self, config):
|
||||
checkpoint_ns = config["configurable"].get("checkpoint_ns", "")
|
||||
|
||||
if checkpoint_id := get_checkpoint_id(config):
|
||||
# Fetch specific checkpoint
|
||||
query = "... WHERE thread_id = ? AND checkpoint_ns = ? AND checkpoint_id = ?"
|
||||
else:
|
||||
# Fetch latest
|
||||
query = "... WHERE thread_id = ? AND checkpoint_ns = ? ORDER BY checkpoint_id DESC LIMIT 1"
|
||||
|
||||
row = await fetch_one(query, ...)
|
||||
if not row:
|
||||
return None
|
||||
|
||||
# Fetch pending writes for this checkpoint
|
||||
writes = await fetch_all(
|
||||
"SELECT task_id, channel, type, value FROM writes "
|
||||
"WHERE thread_id = ? AND checkpoint_ns = ? AND checkpoint_id = ? "
|
||||
"ORDER BY task_id, idx",
|
||||
...
|
||||
)
|
||||
|
||||
return CheckpointTuple(
|
||||
config={"configurable": {"thread_id": ..., "checkpoint_ns": ..., "checkpoint_id": ...}},
|
||||
checkpoint=self.serde.loads_typed((type_, blob)),
|
||||
metadata=json.loads(metadata_blob),
|
||||
parent_config=(
|
||||
{"configurable": {"thread_id": ..., "checkpoint_ns": ..., "checkpoint_id": parent_id}}
|
||||
if parent_id else None
|
||||
),
|
||||
pending_writes=[
|
||||
(task_id, channel, self.serde.loads_typed((type_, value)))
|
||||
for task_id, channel, type_, value in writes
|
||||
],
|
||||
)
|
||||
```
|
||||
|
||||
## alist pattern
|
||||
|
||||
```python
|
||||
async def alist(self, config, *, filter=None, before=None, limit=None):
|
||||
# Build WHERE clause dynamically
|
||||
where_clauses = []
|
||||
params = []
|
||||
|
||||
if config is not None:
|
||||
where_clauses.append("thread_id = ?")
|
||||
params.append(config["configurable"]["thread_id"])
|
||||
if checkpoint_ns := config["configurable"].get("checkpoint_ns"):
|
||||
where_clauses.append("checkpoint_ns = ?")
|
||||
params.append(checkpoint_ns)
|
||||
|
||||
if filter:
|
||||
# Filter on metadata JSON — for each key-value pair:
|
||||
for key, value in filter.items():
|
||||
where_clauses.append(f"json_extract(metadata, '$.{key}') = ?")
|
||||
params.append(json.dumps(value) if not isinstance(value, (str, int, float)) else value)
|
||||
|
||||
if before:
|
||||
before_id = before["configurable"]["checkpoint_id"]
|
||||
where_clauses.append("checkpoint_id < ?")
|
||||
params.append(before_id)
|
||||
|
||||
where = "WHERE " + " AND ".join(where_clauses) if where_clauses else ""
|
||||
query = f"SELECT ... FROM checkpoints {where} ORDER BY checkpoint_id DESC"
|
||||
if limit:
|
||||
query += " LIMIT ?"
|
||||
params.append(limit)
|
||||
|
||||
# For each checkpoint row, also fetch its writes (same as aget_tuple)
|
||||
```
|
||||
|
||||
## aput_writes pattern
|
||||
|
||||
```python
|
||||
async def aput_writes(self, config, writes, task_id, task_path=""):
|
||||
thread_id = config["configurable"]["thread_id"]
|
||||
checkpoint_ns = config["configurable"].get("checkpoint_ns", "")
|
||||
checkpoint_id = config["configurable"]["checkpoint_id"]
|
||||
|
||||
# Choose UPSERT vs INSERT-ignore based on channel types
|
||||
if all(w[0] in WRITES_IDX_MAP for w in writes):
|
||||
query = "INSERT OR REPLACE INTO writes (...) VALUES (...)"
|
||||
else:
|
||||
query = "INSERT OR IGNORE INTO writes (...) VALUES (...)"
|
||||
|
||||
rows = [
|
||||
(thread_id, checkpoint_ns, checkpoint_id, task_id,
|
||||
WRITES_IDX_MAP.get(channel, idx), channel,
|
||||
*self.serde.dumps_typed(value))
|
||||
for idx, (channel, value) in enumerate(writes)
|
||||
]
|
||||
await executemany(query, rows)
|
||||
```
|
||||
|
||||
## adelete_thread pattern
|
||||
|
||||
```python
|
||||
async def adelete_thread(self, thread_id):
|
||||
await execute("DELETE FROM checkpoints WHERE thread_id = ?", (thread_id,))
|
||||
await execute("DELETE FROM writes WHERE thread_id = ?", (thread_id,))
|
||||
```
|
||||
|
||||
## Key takeaway
|
||||
|
||||
The SQLite implementation is ~300 lines and is the simplest correct reference. It uses 2 tables and serializes the full checkpoint as a single blob.
|
||||
|
||||
- SQLite patterns show the simplest correct implementation of every contract
|
||||
- SQL backends can add a 3rd blobs table for performance (see `interface-reference.md` Schema Design section)
|
||||
- NoSQL backends should adapt the contracts to native idioms — focus on `critical-contracts.md`
|
||||
- Don't port SQL patterns to NoSQL; use your backend's native features (document embedding, sorted sets, composite keys, etc.)
|
||||
@@ -1,6 +0,0 @@
|
||||
# Contributing to LangGraph
|
||||
|
||||
Hi there! Thank you for even being interested in contributing to LangGraph.
|
||||
As an open-source project in a rapidly developing field, we are extremely open to contributions, whether they involve new features, improved infrastructure, better documentation, or bug fixes.
|
||||
|
||||
To learn how to contribute to LangGraph, please follow the [contribution guide here](https://docs.langchain.com/oss/python/contributing).
|
||||
@@ -1,43 +1,60 @@
|
||||
name: "\U0001F41B Bug Report"
|
||||
description: Report a bug in LangGraph. To report a security issue, please instead use the security option below. For questions, please use the LangChain Forum at forum.langchain.com.
|
||||
labels: [pending, bug]
|
||||
description: Report a bug in LangGraph. To report a security issue, please instead use the security option (below). For questions, please use the LangChain forum (below).
|
||||
labels: ["bug"]
|
||||
type: bug
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thank you for taking the time to file a bug report.
|
||||
Thank you for taking the time to file a bug report.
|
||||
|
||||
Use this to report BUGS in LangGraph. For usage questions, feature requests and general design questions, please use the [LangChain Forum](https://forum.langchain.com/).
|
||||
For usage questions, feature requests and general design questions, please use the [LangChain Forum](https://forum.langchain.com/).
|
||||
|
||||
Relevant links to check before filing a bug report to see if your issue has already been reported, fixed or
|
||||
if there's another way to solve your problem:
|
||||
Check these before submitting to see if your issue has already been reported, fixed or if there's another way to solve your problem:
|
||||
|
||||
* [LangChain Forum](https://forum.langchain.com/),
|
||||
* [LangGraph Github Issues](https://github.com/langchain-ai/langgraph/issues),
|
||||
* [LangChain documentation with the integrated search](https://docs.langchain.com/),
|
||||
* [Documentation](https://docs.langchain.com/oss/python/langgraph/overview),
|
||||
* [API Reference Documentation](https://reference.langchain.com/python/),
|
||||
* [LangChain ChatBot](https://chat.langchain.com/)
|
||||
* [GitHub search](https://github.com/langchain-ai/langgraph),
|
||||
* [LangChain Forum](https://forum.langchain.com/),
|
||||
- type: checkboxes
|
||||
id: checks
|
||||
attributes:
|
||||
label: Checked other resources
|
||||
description: Before submitting this issue, please confirm that you have completed all the steps below by checking each option. These steps help ensure your issue is well-defined, relevant, and actionable.
|
||||
description: Please confirm and check all the following options.
|
||||
options:
|
||||
- label: This is a bug, not a usage question. For questions, please use the LangChain Forum (https://forum.langchain.com/).
|
||||
- label: This is a bug, not a usage question.
|
||||
required: true
|
||||
- label: I added a clear and detailed title that summarizes the issue.
|
||||
- label: I added a clear and descriptive title that summarizes this issue.
|
||||
required: true
|
||||
- label: I read what a minimal reproducible example is (https://stackoverflow.com/help/minimal-reproducible-example).
|
||||
- label: I used the GitHub search to find a similar question and didn't find it.
|
||||
required: true
|
||||
- label: I included a self-contained, minimal example that demonstrates the issue INCLUDING all the relevant imports. The code run AS IS to reproduce the issue.
|
||||
- label: I am sure that this is a bug in LangGraph rather than my code.
|
||||
required: true
|
||||
- label: The bug is not resolved by updating to the latest stable version of LangGraph (or the specific integration package).
|
||||
required: true
|
||||
- label: This is not related to the langchain-community package.
|
||||
required: true
|
||||
- label: I posted a self-contained, minimal, reproducible example. A maintainer can copy it and run it AS IS.
|
||||
required: true
|
||||
- type: textarea
|
||||
id: reproduction
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: Example Code
|
||||
label: Reproduction Steps / Example Code (Python)
|
||||
description: |
|
||||
Please add a self-contained, [minimal, reproducible, example](https://stackoverflow.com/help/minimal-reproducible-example) with your use case. Replace this code with your own!
|
||||
Please add a self-contained, [minimal, reproducible, example](https://stackoverflow.com/help/minimal-reproducible-example) with your use case.
|
||||
|
||||
If a maintainer can copy it, run it, and see it right away, there's a much higher chance that you'll be able to get help.
|
||||
|
||||
**Important!**
|
||||
|
||||
* Avoid screenshots, as they are hard to read and (more importantly) don't allow others to copy-and-paste your code.
|
||||
* Reduce your code to the minimum required to reproduce the issue if possible.
|
||||
|
||||
(This will be automatically formatted into code, so no need for backticks.)
|
||||
render: python
|
||||
placeholder: |
|
||||
from langgraph.graph import StateGraph
|
||||
|
||||
@@ -46,17 +63,13 @@ body:
|
||||
|
||||
chain = StateGraph(list)
|
||||
chain.invoke('Hello!')
|
||||
render: python
|
||||
- type: textarea
|
||||
id: error
|
||||
validations:
|
||||
required: false
|
||||
attributes:
|
||||
label: Error Message and Stack Trace (if applicable)
|
||||
description: |
|
||||
If you are reporting an error, please include the full error message and stack trace.
|
||||
placeholder: |
|
||||
Exception + full stack trace
|
||||
If you are reporting an error, please copy and paste the full error message and
|
||||
stack trace.
|
||||
(This will be automatically formatted into code, so no need for backticks.)
|
||||
render: shell
|
||||
- type: textarea
|
||||
id: description
|
||||
@@ -77,7 +90,18 @@ body:
|
||||
attributes:
|
||||
label: System Info
|
||||
description: |
|
||||
Run on your machine: `python -m langchain_core.sys_info`
|
||||
Please share your system info with us.
|
||||
|
||||
Run the following command in your terminal and paste the output here:
|
||||
|
||||
`python -m langchain_core.sys_info`
|
||||
|
||||
or if you have an existing python interpreter running:
|
||||
|
||||
```python
|
||||
from langchain_core import sys_info
|
||||
sys_info.print_sys_info()
|
||||
```
|
||||
placeholder: |
|
||||
python -m langchain_core.sys_info
|
||||
validations:
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
blank_issues_enabled: false
|
||||
version: 2.1
|
||||
contact_links:
|
||||
- name: Documentation
|
||||
url: https://github.com/langchain-ai/docs/issues/new?template=langgraph.yml
|
||||
about: Report an issue related to the LangGraph documentation
|
||||
- name: LangChain Forum
|
||||
- name: 💬 LangChain Forum
|
||||
url: https://forum.langchain.com/
|
||||
about: General community discussions and support
|
||||
- name: 📚 LangGraph Documentation
|
||||
url: https://docs.langchain.com/oss/python/langgraph/overview
|
||||
about: View the official LangGraph documentation
|
||||
- name: 📚 API Reference Documentation
|
||||
url: https://reference.langchain.com/python/
|
||||
about: View the official LangGraph API reference documentation
|
||||
- name: 📚 Documentation issue
|
||||
url: https://github.com/langchain-ai/docs/issues/new?template=02-langgraph.yml
|
||||
about: Report an issue related to the LangGraph documentation
|
||||
|
||||
@@ -21,7 +21,7 @@ Thank you for contributing to LangGraph! Follow these steps to mark your pull re
|
||||
1. A test for the integration, preferably unit tests that do not rely on network access,
|
||||
2. An example notebook showing its use. It lives in `docs/docs/integrations` directory.
|
||||
|
||||
- [ ] **Lint and test**: Run `make format`, `make lint` and `make test` from the root of the package(s) you've modified. We will not consider a PR unless these three are passing in CI. See [contribution guidelines](https://github.com/langchain-ai/langgraph/blob/main/CONTRIBUTING.md) for more.
|
||||
- [ ] **Lint and test**: Run `make format`, `make lint` and `make test` from the root of the package(s) you've modified. We will not consider a PR unless these three are passing in CI. See [contribution guidelines](https://docs.langchain.com/oss/python/contributing/overview) for more.
|
||||
|
||||
Additional guidelines:
|
||||
|
||||
|
||||
+102
-9
@@ -4,15 +4,108 @@ updates:
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
day: "monday"
|
||||
groups:
|
||||
all-dependencies:
|
||||
patterns:
|
||||
- "*"
|
||||
|
||||
- package-ecosystem: "pip"
|
||||
directories:
|
||||
- "libs/checkpoint"
|
||||
- "libs/checkpoint-postgres"
|
||||
- "libs/checkpoint-sqlite"
|
||||
- "libs/cli"
|
||||
- "libs/langgraph"
|
||||
- "libs/prebuilt"
|
||||
- "libs/sdk-py"
|
||||
- package-ecosystem: "uv"
|
||||
directory: "/libs/checkpoint"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
day: "monday"
|
||||
groups:
|
||||
all-dependencies:
|
||||
patterns:
|
||||
- "*"
|
||||
- package-ecosystem: "uv"
|
||||
directory: "/libs/checkpoint-conformance"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
day: "monday"
|
||||
groups:
|
||||
all-dependencies:
|
||||
patterns:
|
||||
- "*"
|
||||
|
||||
|
||||
- package-ecosystem: "uv"
|
||||
directory: "/libs/checkpoint-postgres"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
day: "monday"
|
||||
groups:
|
||||
all-dependencies:
|
||||
patterns:
|
||||
- "*"
|
||||
|
||||
- package-ecosystem: "uv"
|
||||
directory: "/libs/checkpoint-sqlite"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
day: "monday"
|
||||
groups:
|
||||
all-dependencies:
|
||||
patterns:
|
||||
- "*"
|
||||
|
||||
- package-ecosystem: "uv"
|
||||
directory: "/libs/cli"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
day: "monday"
|
||||
groups:
|
||||
all-dependencies:
|
||||
patterns:
|
||||
- "*"
|
||||
|
||||
- package-ecosystem: "uv"
|
||||
directory: "/libs/langgraph"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
day: "monday"
|
||||
groups:
|
||||
all-dependencies:
|
||||
patterns:
|
||||
- "*"
|
||||
|
||||
- package-ecosystem: "uv"
|
||||
directory: "/libs/prebuilt"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
day: "monday"
|
||||
groups:
|
||||
all-dependencies:
|
||||
patterns:
|
||||
- "*"
|
||||
|
||||
- package-ecosystem: "uv"
|
||||
directory: "/libs/sdk-py"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
day: "monday"
|
||||
groups:
|
||||
all-dependencies:
|
||||
patterns:
|
||||
- "*"
|
||||
|
||||
- package-ecosystem: "npm"
|
||||
directory: "/libs/cli/js-examples"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
day: "monday"
|
||||
groups:
|
||||
all-dependencies:
|
||||
patterns:
|
||||
- "*"
|
||||
|
||||
- package-ecosystem: "npm"
|
||||
directory: "/libs/cli/js-monorepo-example"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
day: "monday"
|
||||
groups:
|
||||
all-dependencies:
|
||||
patterns:
|
||||
- "*"
|
||||
|
||||
@@ -63,7 +63,7 @@ def test(config: pathlib.Path, port: int, tag: str, verbose: bool):
|
||||
try:
|
||||
sys.stderr.write("\n== docker compose ps ==\n")
|
||||
runner.run(
|
||||
subp_exec(*compose_cmd, *args, "ps", input=stdin, verbose=False)
|
||||
subp_exec(*compose_cmd, *args, "ps", input=stdin, verbose=True)
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
@@ -76,7 +76,7 @@ def test(config: pathlib.Path, port: int, tag: str, verbose: bool):
|
||||
"logs",
|
||||
"langgraph-api",
|
||||
input=stdin,
|
||||
verbose=False,
|
||||
verbose=True,
|
||||
)
|
||||
)
|
||||
except Exception:
|
||||
|
||||
@@ -2,6 +2,9 @@ name: CLI integration test
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
secrets:
|
||||
LANGSMITH_API_KEY:
|
||||
required: false
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -28,6 +31,8 @@ jobs:
|
||||
workdir: libs/cli/examples/graphs_reqs_b
|
||||
tag: langgraph-test-d
|
||||
name: "CLI integration test"
|
||||
env:
|
||||
HAS_LANGSMITH_API_KEY: ${{ secrets.LANGSMITH_API_KEY != '' }}
|
||||
defaults:
|
||||
run:
|
||||
working-directory: libs/cli
|
||||
@@ -49,19 +54,22 @@ jobs:
|
||||
- name: Install cli globally
|
||||
if: steps.changed-files.outputs.all
|
||||
run: pip install -e .
|
||||
- name: Build and test service ${{ matrix.example.name }}
|
||||
- name: Build service ${{ matrix.example.name }}
|
||||
if: steps.changed-files.outputs.all
|
||||
working-directory: ${{ matrix.example.workdir }}
|
||||
run: |
|
||||
langgraph build -t ${{ matrix.example.tag }}
|
||||
- name: Test service ${{ matrix.example.name }}
|
||||
if: ${{ steps.changed-files.outputs.all && env.HAS_LANGSMITH_API_KEY == 'true' }}
|
||||
working-directory: ${{ matrix.example.workdir }}
|
||||
env:
|
||||
LANGSMITH_API_KEY: ${{ secrets.LANGSMITH_API_KEY }}
|
||||
run: |
|
||||
# Build the image for this example
|
||||
langgraph build -t ${{ matrix.example.tag }}
|
||||
# Prepare environment file from local or parent example directory
|
||||
if [ -f .env.example ]; then cp .env.example .env; elif [ -f ../.env.example ]; then cp ../.env.example .env && cp ../.env.example ../.env; fi
|
||||
if [ -n "${{ secrets.LANGSMITH_API_KEY }}" ]; then echo "LANGSMITH_API_KEY=${{ secrets.LANGSMITH_API_KEY }}" >> .env; if [ -f ../.env ]; then echo "LANGSMITH_API_KEY=${{ secrets.LANGSMITH_API_KEY }}" >> ../.env; fi; fi
|
||||
echo "LANGSMITH_API_KEY=${{ secrets.LANGSMITH_API_KEY }}" >> .env
|
||||
if [ -f ../.env ]; then echo "LANGSMITH_API_KEY=${{ secrets.LANGSMITH_API_KEY }}" >> ../.env; fi
|
||||
# Run the integration test using the built tag
|
||||
# Compute repo root to reference the shared script robustly
|
||||
REPO_ROOT=$(git rev-parse --show-toplevel)
|
||||
timeout 60 python "$REPO_ROOT/.github/scripts/run_langgraph_cli_test.py" -t ${{ matrix.example.tag }}
|
||||
|
||||
@@ -82,22 +90,34 @@ jobs:
|
||||
working-directory: libs/cli/python-monorepo-example
|
||||
run: |
|
||||
langgraph build -t langgraph-test-g -c apps/agent/langgraph.json
|
||||
- name: Test Python monorepo service
|
||||
if: ${{ steps.changed-files.outputs.all && matrix.example.name == 'A' && env.HAS_LANGSMITH_API_KEY == 'true' }}
|
||||
working-directory: libs/cli/python-monorepo-example
|
||||
env:
|
||||
LANGSMITH_API_KEY: ${{ secrets.LANGSMITH_API_KEY }}
|
||||
run: |
|
||||
cp apps/agent/.env.example apps/agent/.env
|
||||
if [ -n "${{ secrets.LANGSMITH_API_KEY }}" ]; then echo "LANGSMITH_API_KEY=${{ secrets.LANGSMITH_API_KEY }}" >> apps/agent/.env; fi
|
||||
echo "LANGSMITH_API_KEY=${{ secrets.LANGSMITH_API_KEY }}" >> apps/agent/.env
|
||||
timeout 60 python ../../../.github/scripts/run_langgraph_cli_test.py -t langgraph-test-g -c apps/agent/langgraph.json
|
||||
|
||||
- name: Build and test prerelease reqs service
|
||||
- name: Build prerelease reqs service
|
||||
if: ${{ steps.changed-files.outputs.all && matrix.example.name == 'A' }}
|
||||
working-directory: libs/cli/examples/graph_prerelease_reqs
|
||||
run: |
|
||||
langgraph build -t langgraph-test-h
|
||||
- name: Test prerelease reqs service
|
||||
if: ${{ steps.changed-files.outputs.all && matrix.example.name == 'A' && env.HAS_LANGSMITH_API_KEY == 'true' }}
|
||||
working-directory: libs/cli/examples/graph_prerelease_reqs
|
||||
env:
|
||||
LANGSMITH_API_KEY: ${{ secrets.LANGSMITH_API_KEY }}
|
||||
run: |
|
||||
cp ../.env.example .env
|
||||
if [ -n "${{ secrets.LANGSMITH_API_KEY }}" ]; then echo "LANGSMITH_API_KEY=${{ secrets.LANGSMITH_API_KEY }}" >> .env; fi
|
||||
echo "LANGSMITH_API_KEY=${{ secrets.LANGSMITH_API_KEY }}" >> .env
|
||||
timeout 60 python ../../../../.github/scripts/run_langgraph_cli_test.py -t langgraph-test-h
|
||||
echo "Finished starting up langgraph-test-h"
|
||||
LANGGRAPH_VERSION=$(docker run --rm --entrypoint "" langgraph-test-h python -c "import sys; from importlib.metadata import version; v = version('langgraph'); print(v);")
|
||||
if [ "$LANGGRAPH_VERSION" != "1.0.2" ]; then
|
||||
echo "LANGGRAPH_VERSION != 1.0.2; $LANGGRAPH_VERSION"
|
||||
if [ "$LANGGRAPH_VERSION" != "1.0.8" ]; then
|
||||
echo "LANGGRAPH_VERSION != 1.0.8; $LANGGRAPH_VERSION"
|
||||
exit 1
|
||||
fi
|
||||
LANGCHAIN_OPENAI_VERSION=$(docker run --rm --entrypoint "" langgraph-test-h python -c "import sys; from importlib.metadata import version; v = version('langchain-openai'); print(v);")
|
||||
|
||||
@@ -39,6 +39,7 @@ jobs:
|
||||
- 'libs/checkpoint/**'
|
||||
- 'libs/checkpoint-sqlite/**'
|
||||
- 'libs/checkpoint-postgres/**'
|
||||
- 'libs/checkpoint-conformance/**'
|
||||
- 'libs/prebuilt/**'
|
||||
deps:
|
||||
- '**/pyproject.toml'
|
||||
@@ -57,7 +58,7 @@ jobs:
|
||||
"libs/checkpoint",
|
||||
"libs/checkpoint-sqlite",
|
||||
"libs/checkpoint-postgres",
|
||||
|
||||
"libs/checkpoint-conformance",
|
||||
"libs/prebuilt",
|
||||
]
|
||||
if: needs.changes.outputs.python == 'true' || needs.changes.outputs.deps == 'true'
|
||||
@@ -77,6 +78,7 @@ jobs:
|
||||
"libs/checkpoint",
|
||||
"libs/checkpoint-sqlite",
|
||||
"libs/checkpoint-postgres",
|
||||
"libs/checkpoint-conformance",
|
||||
"libs/prebuilt",
|
||||
"libs/sdk-py",
|
||||
]
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
name: Deploy Redirects to GitHub Pages
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- 'docs/**'
|
||||
- '.github/workflows/deploy-redirects.yml'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pages: write
|
||||
id-token: write
|
||||
|
||||
concurrency:
|
||||
group: "pages"
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.11'
|
||||
|
||||
- name: Generate redirect files
|
||||
run: python docs/generate_redirects.py
|
||||
|
||||
- name: Setup Pages
|
||||
uses: actions/configure-pages@v4
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-pages-artifact@v3
|
||||
with:
|
||||
path: 'docs/_site'
|
||||
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v4
|
||||
@@ -53,3 +53,5 @@ sdk-js (standalone)
|
||||
```
|
||||
|
||||
Changes to a library may impact all of its dependents shown above.
|
||||
|
||||
- Do NOT use Sphinx-style double backtick formatting (` ``code`` `). Use single backticks (`` `code` ``) for inline code references in docstrings and comments.
|
||||
|
||||
@@ -53,3 +53,5 @@ sdk-js (standalone)
|
||||
```
|
||||
|
||||
Changes to a library may impact all of its dependents shown above.
|
||||
|
||||
- Do NOT use Sphinx-style double backtick formatting (` ``code`` `). Use single backticks (`` `code` ``) for inline code references in docstrings and comments.
|
||||
|
||||
@@ -79,7 +79,7 @@ While LangGraph can be used standalone, it also integrates seamlessly with any L
|
||||
|
||||
## Additional resources
|
||||
|
||||
- [Guides](https://docs.langchain.com/oss/python/langgraph/guides): Quick, actionable code snippets for topics such as streaming, adding memory & persistence, and design patterns (e.g. branching, subgraphs, etc.).
|
||||
- [Guides](https://docs.langchain.com/oss/python/langgraph/overview): Quick, actionable code snippets for topics such as streaming, adding memory & persistence, and design patterns (e.g. branching, subgraphs, etc.).
|
||||
- [Reference](https://reference.langchain.com/python/langgraph/): Detailed reference on core classes, methods, how to use the graph and checkpointing APIs, and higher-level prebuilt components.
|
||||
- [Examples](https://docs.langchain.com/oss/python/langgraph/agentic-rag): Guided examples on getting started with LangGraph.
|
||||
- [LangChain Forum](https://forum.langchain.com/): Connect with the community and share all of your technical questions, ideas, and feedback.
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
_site/
|
||||
@@ -0,0 +1,142 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Generate HTML redirect files from redirects.json.
|
||||
|
||||
Usage:
|
||||
python generate_redirects.py
|
||||
|
||||
This script reads redirects.json and generates individual HTML files
|
||||
for each redirect path. Each HTML file uses meta refresh (0 delay)
|
||||
which is SEO-friendly and treated similarly to 301 redirects by Google.
|
||||
|
||||
To add new redirects, simply edit redirects.json and re-run this script.
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
# Default fallback URL for any path not in the redirect map
|
||||
DEFAULT_REDIRECT = "https://docs.langchain.com/oss/python/langgraph/overview"
|
||||
|
||||
HTML_TEMPLATE = """<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Redirecting...</title>
|
||||
<link rel="canonical" href="{url}">
|
||||
<meta name="robots" content="noindex">
|
||||
<script>var anchor=window.location.hash.substr(1);location.href="{url}"+(anchor?"#"+anchor:"")</script>
|
||||
<meta http-equiv="refresh" content="0; url={url}">
|
||||
</head>
|
||||
<body>
|
||||
Redirecting...
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
ROOT_HTML_TEMPLATE = """<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Redirecting to LangGraph Documentation</title>
|
||||
<link rel="canonical" href="{url}">
|
||||
<meta name="robots" content="noindex">
|
||||
<script>var anchor=window.location.hash.substr(1);location.href="{url}"+(anchor?"#"+anchor:"")</script>
|
||||
<meta http-equiv="refresh" content="0; url={url}">
|
||||
</head>
|
||||
<body>
|
||||
<h1>Documentation has moved</h1>
|
||||
<p>The LangGraph documentation has moved to <a href="{url}">docs.langchain.com</a>.</p>
|
||||
<p>Redirecting you now...</p>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
CATCHALL_404_TEMPLATE = """<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Redirecting to LangGraph Documentation</title>
|
||||
<link rel="canonical" href="{default_url}">
|
||||
<meta name="robots" content="noindex">
|
||||
<script>
|
||||
// Catchall redirect for any unmapped paths
|
||||
window.location.replace("{default_url}");
|
||||
</script>
|
||||
<meta http-equiv="refresh" content="0; url={default_url}">
|
||||
</head>
|
||||
<body>
|
||||
<h1>Documentation has moved</h1>
|
||||
<p>The LangGraph documentation has moved to <a href="{default_url}">docs.langchain.com</a>.</p>
|
||||
<p>Redirecting you now...</p>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
|
||||
def generate_redirects():
|
||||
script_dir = Path(__file__).parent
|
||||
output_dir = script_dir / "_site"
|
||||
|
||||
# Load redirects
|
||||
with open(script_dir / "redirects.json") as f:
|
||||
redirects = json.load(f)
|
||||
|
||||
# Clean output directory
|
||||
if output_dir.exists():
|
||||
import shutil
|
||||
shutil.rmtree(output_dir)
|
||||
output_dir.mkdir(parents=True)
|
||||
|
||||
# Generate individual HTML files for each redirect
|
||||
for old_path, new_url in redirects.items():
|
||||
# Remove leading slash and create directory structure
|
||||
path = old_path.lstrip("/")
|
||||
|
||||
# Check if path has a file extension (e.g., .txt, .xml)
|
||||
# If so, create the file directly instead of a directory with index.html
|
||||
path_obj = Path(path)
|
||||
has_extension = path_obj.suffix and len(path_obj.suffix) <= 5
|
||||
|
||||
if not path:
|
||||
html_path = output_dir / "index.html"
|
||||
elif has_extension:
|
||||
# For files with extensions, create the file directly
|
||||
html_path = output_dir / path
|
||||
else:
|
||||
# For directory-style URLs, create index.html inside
|
||||
html_path = output_dir / path / "index.html"
|
||||
|
||||
# Create parent directories
|
||||
html_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Write the redirect HTML
|
||||
html_path.write_text(HTML_TEMPLATE.format(url=new_url))
|
||||
print(f"Created: {html_path}")
|
||||
|
||||
# Create root index.html
|
||||
root_index = output_dir / "index.html"
|
||||
if not root_index.exists():
|
||||
root_index.write_text(ROOT_HTML_TEMPLATE.format(url=DEFAULT_REDIRECT))
|
||||
print(f"Created: {root_index}")
|
||||
|
||||
# Create 404.html for catchall
|
||||
catchall_404 = output_dir / "404.html"
|
||||
catchall_404.write_text(CATCHALL_404_TEMPLATE.format(default_url=DEFAULT_REDIRECT))
|
||||
print(f"Created: {catchall_404}")
|
||||
|
||||
# Copy static files (like llms.txt) that can't be redirected via HTML
|
||||
static_files = ["llms.txt"]
|
||||
for static_file in static_files:
|
||||
src = script_dir / static_file
|
||||
if src.exists():
|
||||
dst = output_dir / static_file
|
||||
dst.write_text(src.read_text())
|
||||
print(f"Copied: {dst}")
|
||||
|
||||
print(f"\nGenerated {len(redirects)} redirect files in {output_dir}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
generate_redirects()
|
||||
@@ -0,0 +1,35 @@
|
||||
# LangGraph
|
||||
|
||||
LangGraph documentation has moved to docs.langchain.com.
|
||||
|
||||
## Overview
|
||||
|
||||
- [LangGraph Overview](https://docs.langchain.com/oss/python/langgraph/overview): Introduction to LangGraph, a library for building stateful, multi-actor applications with LLMs.
|
||||
- [Why LangGraph?](https://docs.langchain.com/oss/python/langgraph/why-langgraph): Motivation for LangGraph and its key features.
|
||||
|
||||
## Core Concepts
|
||||
|
||||
- [Graph API](https://docs.langchain.com/oss/python/langgraph/graph-api): Learn how to define state, create nodes, and connect them with edges.
|
||||
- [Streaming](https://docs.langchain.com/oss/python/langgraph/streaming): Stream outputs from your graph for better UX.
|
||||
- [Persistence](https://docs.langchain.com/oss/python/langgraph/persistence): Add memory and checkpointing to your graphs.
|
||||
- [Add Memory](https://docs.langchain.com/oss/python/langgraph/add-memory): Implement short-term and long-term memory.
|
||||
- [Workflows & Agents](https://docs.langchain.com/oss/python/langgraph/workflows-agents): Build agents and workflows with LangGraph.
|
||||
|
||||
## How-To Guides
|
||||
|
||||
- [Use Subgraphs](https://docs.langchain.com/oss/python/langgraph/use-subgraphs): Compose graphs using subgraphs.
|
||||
- [Observability](https://docs.langchain.com/oss/python/langgraph/observability): Add tracing and debugging to your graphs.
|
||||
- [Common Errors](https://docs.langchain.com/oss/python/langgraph/common-errors): Troubleshoot common LangGraph errors.
|
||||
|
||||
## Tutorials
|
||||
|
||||
- [Agentic RAG](https://docs.langchain.com/oss/python/langgraph/agentic-rag): Build an agentic RAG system with LangGraph.
|
||||
- [SQL Agent](https://docs.langchain.com/oss/python/langgraph/sql-agent): Create a SQL agent with LangGraph.
|
||||
|
||||
## Reference
|
||||
|
||||
- [API Reference](https://reference.langchain.com/python/langgraph/): Complete API documentation for LangGraph.
|
||||
|
||||
## LangGraph Platform
|
||||
|
||||
For deploying LangGraph applications in production, see the [LangSmith documentation](https://docs.langchain.com/langsmith/agent-server).
|
||||
@@ -0,0 +1,296 @@
|
||||
{
|
||||
"/how-tos/stream-values": "https://docs.langchain.com/oss/python/langgraph/streaming",
|
||||
"/how-tos/stream-updates": "https://docs.langchain.com/oss/python/langgraph/streaming",
|
||||
"/how-tos/streaming-content": "https://docs.langchain.com/oss/python/langgraph/streaming",
|
||||
"/how-tos/stream-multiple": "https://docs.langchain.com/oss/python/langgraph/streaming",
|
||||
"/how-tos/streaming-tokens-without-langchain": "https://docs.langchain.com/oss/python/langgraph/streaming",
|
||||
"/how-tos/streaming-from-final-node": "https://docs.langchain.com/oss/python/langgraph/streaming",
|
||||
"/how-tos/streaming-events-from-within-tools-without-langchain": "https://docs.langchain.com/oss/python/langgraph/streaming",
|
||||
"/how-tos/state-reducers": "https://docs.langchain.com/oss/python/langgraph/graph-api#define-and-update-state",
|
||||
"/how-tos/sequence": "https://docs.langchain.com/oss/python/langgraph/graph-api#create-a-sequence-of-steps",
|
||||
"/how-tos/branching": "https://docs.langchain.com/oss/python/langgraph/graph-api#create-branches",
|
||||
"/how-tos/recursion-limit": "https://docs.langchain.com/oss/python/langgraph/graph-api#create-and-control-loops",
|
||||
"/how-tos/visualization": "https://docs.langchain.com/oss/python/langgraph/graph-api#visualize-your-graph",
|
||||
"/how-tos/input_output_schema": "https://docs.langchain.com/oss/python/langgraph/graph-api#define-input-and-output-schemas",
|
||||
"/how-tos/pass_private_state": "https://docs.langchain.com/oss/python/langgraph/graph-api#pass-private-state-between-nodes",
|
||||
"/how-tos/state-model": "https://docs.langchain.com/oss/python/langgraph/graph-api#use-pydantic-models-for-graph-state",
|
||||
"/how-tos/map-reduce": "https://docs.langchain.com/oss/python/langgraph/graph-api#map-reduce-and-the-send-api",
|
||||
"/how-tos/command": "https://docs.langchain.com/oss/python/langgraph/graph-api#combine-control-flow-and-state-updates-with-command",
|
||||
"/how-tos/configuration": "https://docs.langchain.com/oss/python/langgraph/graph-api#add-runtime-configuration",
|
||||
"/how-tos/node-retries": "https://docs.langchain.com/oss/python/langgraph/graph-api#add-retry-policies",
|
||||
"/how-tos/return-when-recursion-limit-hits": "https://docs.langchain.com/oss/python/langgraph/graph-api#impose-a-recursion-limit",
|
||||
"/how-tos/async": "https://docs.langchain.com/oss/python/langgraph/graph-api#async",
|
||||
"/how-tos/memory/manage-conversation-history": "https://docs.langchain.com/oss/python/langgraph/add-memory",
|
||||
"/how-tos/memory/delete-messages": "https://docs.langchain.com/oss/python/langgraph/add-memory#delete-messages",
|
||||
"/how-tos/memory/add-summary-conversation-history": "https://docs.langchain.com/oss/python/langgraph/add-memory#summarize-messages",
|
||||
"/how-tos/memory": "https://docs.langchain.com/oss/python/langgraph/add-memory",
|
||||
"/agents/memory": "https://docs.langchain.com/oss/python/langgraph/add-memory",
|
||||
"/how-tos/subgraph-transform-state": "https://docs.langchain.com/oss/python/langgraph/use-subgraphs#different-state-schemas",
|
||||
"/how-tos/subgraphs-manage-state": "https://docs.langchain.com/oss/python/langgraph/use-subgraphs#add-persistence",
|
||||
"/how-tos/persistence_postgres": "https://docs.langchain.com/oss/python/langgraph/add-memory#use-in-production",
|
||||
"/how-tos/persistence_mongodb": "https://docs.langchain.com/oss/python/langgraph/add-memory#use-in-production",
|
||||
"/how-tos/persistence_redis": "https://docs.langchain.com/oss/python/langgraph/add-memory#use-in-production",
|
||||
"/how-tos/subgraph-persistence": "https://docs.langchain.com/oss/python/langgraph/add-memory#use-with-subgraphs",
|
||||
"/how-tos/cross-thread-persistence": "https://docs.langchain.com/oss/python/langgraph/add-memory#add-long-term-memory",
|
||||
"/cloud/how-tos/copy_threads": "https://docs.langchain.com/langsmith/use-threads",
|
||||
"/cloud/how-tos/check-thread-status": "https://docs.langchain.com/langsmith/use-threads",
|
||||
"/cloud/concepts/threads": "https://docs.langchain.com/oss/python/langgraph/persistence#threads",
|
||||
"/how-tos/persistence": "https://docs.langchain.com/oss/python/langgraph/add-memory",
|
||||
"/how-tos/tool-calling-errors": "https://docs.langchain.com/oss/python/langgraph/workflows-agents",
|
||||
"/how-tos/pass-config-to-tools": "https://docs.langchain.com/oss/python/langgraph/workflows-agents",
|
||||
"/how-tos/pass-run-time-values-to-tools": "https://docs.langchain.com/oss/python/langgraph/workflows-agents",
|
||||
"/how-tos/update-state-from-tools": "https://docs.langchain.com/oss/python/langgraph/workflows-agents",
|
||||
"/agents/tools": "https://docs.langchain.com/oss/python/langgraph/workflows-agents",
|
||||
"/how-tos/agent-handoffs": "https://docs.langchain.com/oss/python/langgraph/graph-api",
|
||||
"/how-tos/multi-agent-network": "https://docs.langchain.com/oss/python/langgraph/graph-api",
|
||||
"/how-tos/multi-agent-multi-turn-convo": "https://docs.langchain.com/oss/python/langgraph/graph-api",
|
||||
"/cloud/index": "https://docs.langchain.com/oss/python/langgraph/overview",
|
||||
"/cloud/how-tos/index": "https://docs.langchain.com/langsmith/home",
|
||||
"/cloud/concepts/api": "https://docs.langchain.com/langsmith/agent-server",
|
||||
"/cloud/concepts/cloud": "https://docs.langchain.com/langsmith/cloud",
|
||||
"/cloud/faq/studio": "https://docs.langchain.com/langsmith/studio",
|
||||
"/cloud/how-tos/human_in_the_loop_edit_state": "https://docs.langchain.com/langsmith/add-human-in-the-loop",
|
||||
"/cloud/how-tos/human_in_the_loop_user_input": "https://docs.langchain.com/langsmith/add-human-in-the-loop",
|
||||
"/concepts/platform_architecture": "https://docs.langchain.com/langsmith/cloud#architecture",
|
||||
"/cloud/how-tos/stream_values": "https://docs.langchain.com/langsmith/streaming",
|
||||
"/cloud/how-tos/stream_updates": "https://docs.langchain.com/langsmith/streaming",
|
||||
"/cloud/how-tos/stream_messages": "https://docs.langchain.com/langsmith/streaming",
|
||||
"/cloud/how-tos/stream_events": "https://docs.langchain.com/langsmith/streaming",
|
||||
"/cloud/how-tos/stream_debug": "https://docs.langchain.com/langsmith/streaming",
|
||||
"/cloud/how-tos/stream_multiple": "https://docs.langchain.com/langsmith/streaming",
|
||||
"/cloud/concepts/streaming": "https://docs.langchain.com/oss/python/langgraph/streaming",
|
||||
"/agents/streaming": "https://docs.langchain.com/oss/python/langgraph/streaming",
|
||||
"/how-tos/create-react-agent": "https://docs.langchain.com/oss/python/langchain/agents#basic-configuration",
|
||||
"/how-tos/create-react-agent-memory": "https://docs.langchain.com/oss/python/langgraph/add-memory",
|
||||
"/how-tos/create-react-agent-system-prompt": "https://docs.langchain.com/oss/python/langgraph/add-memory",
|
||||
"/how-tos/create-react-agent-structured-output": "https://docs.langchain.com/oss/python/langchain/agents#structured-output",
|
||||
"/prebuilt": "https://docs.langchain.com/oss/python/langchain/agents",
|
||||
"/reference/prebuilt": "https://reference.langchain.com/python/langgraph/agents/",
|
||||
"/concepts/high_level": "https://docs.langchain.com/oss/python/langgraph/overview",
|
||||
"/concepts/index": "https://docs.langchain.com/oss/python/langgraph/overview",
|
||||
"/concepts/v0-human-in-the-loop": "https://docs.langchain.com/oss/python/langgraph/interrupts",
|
||||
"/how-tos/index": "https://docs.langchain.com/oss/python/langgraph/overview",
|
||||
"/tutorials/introduction": "https://docs.langchain.com/oss/python/langgraph/overview",
|
||||
"/agents/deployment": "https://docs.langchain.com/oss/python/langgraph/local-server",
|
||||
"/how-tos/deploy-self-hosted": "https://docs.langchain.com/langsmith/platform-setup",
|
||||
"/concepts/self_hosted": "https://docs.langchain.com/langsmith/platform-setup",
|
||||
"/tutorials/deployment": "https://docs.langchain.com/langsmith/deployments",
|
||||
"/cloud/how-tos/assistant_versioning": "https://docs.langchain.com/langsmith/configuration-cloud",
|
||||
"/cloud/concepts/runs": "https://docs.langchain.com/langsmith/assistants#execution",
|
||||
"/how-tos/wait-user-input-functional": "https://docs.langchain.com/oss/python/langgraph/functional-api",
|
||||
"/how-tos/review-tool-calls-functional": "https://docs.langchain.com/oss/python/langgraph/functional-api",
|
||||
"/how-tos/create-react-agent-hitl": "https://docs.langchain.com/oss/python/langgraph/interrupts",
|
||||
"/agents/human-in-the-loop": "https://docs.langchain.com/oss/python/langgraph/interrupts",
|
||||
"/how-tos/human_in_the_loop/dynamic_breakpoints": "https://docs.langchain.com/oss/python/langgraph/interrupts",
|
||||
"/concepts/breakpoints": "https://docs.langchain.com/oss/python/langgraph/interrupts",
|
||||
"/how-tos/human_in_the_loop/breakpoints": "https://docs.langchain.com/oss/python/langgraph/interrupts",
|
||||
"/cloud/how-tos/human_in_the_loop_breakpoint": "https://docs.langchain.com/langsmith/add-human-in-the-loop",
|
||||
"/how-tos/human_in_the_loop/edit-graph-state": "https://docs.langchain.com/oss/python/langgraph/use-time-travel",
|
||||
"/examples/index": "https://docs.langchain.com/oss/python/langgraph/case-studies",
|
||||
"/guides/index": "https://docs.langchain.com/oss/python/langchain/overview",
|
||||
"/tutorials/index": "https://docs.langchain.com/oss/python/learn",
|
||||
"/llms-txt-overview": "https://docs.langchain.com/llms.txt",
|
||||
"/tutorials/rag/langgraph_adaptive_rag": "https://docs.langchain.com/oss/python/langgraph/agentic-rag",
|
||||
"/tutorials/multi_agent/multi-agent-collaboration": "https://docs.langchain.com/oss/python/langchain/multi-agent",
|
||||
"/how-tos/create-react-agent-manage-message-history": "https://docs.langchain.com/oss/python/langgraph/add-memory",
|
||||
"/how-tos/many-tools": "https://docs.langchain.com/oss/python/langchain/tools",
|
||||
"/tutorials/customer-support/customer-support": "https://docs.langchain.com/oss/python/langgraph/agentic-rag",
|
||||
"/how-tos/react-agent-structured-output": "https://docs.langchain.com/oss/python/langchain/agents#structured-output",
|
||||
"/tutorials/code_assistant/langgraph_code_assistant": "https://docs.langchain.com/oss/python/langgraph/agentic-rag",
|
||||
"/tutorials/multi_agent/hierarchical_agent_teams": "https://docs.langchain.com/oss/python/langchain/supervisor",
|
||||
"/tutorials/auth/getting_started": "https://docs.langchain.com/langsmith/auth",
|
||||
"/tutorials/auth/resource_auth": "https://docs.langchain.com/langsmith/resource-auth",
|
||||
"/tutorials/auth/add_auth_server": "https://docs.langchain.com/langsmith/add-auth-server",
|
||||
"/how-tos/use-remote-graph": "https://docs.langchain.com/langsmith/use-remote-graph",
|
||||
"/how-tos/autogen-integration": "https://docs.langchain.com/langsmith/autogen-integration",
|
||||
"/how-tos/human_in_the_loop/wait-user-input": "https://docs.langchain.com/oss/python/langgraph/interrupts",
|
||||
"/cloud/how-tos/use_stream_react": "https://docs.langchain.com/langsmith/use-stream-react",
|
||||
"/cloud/how-tos/generative_ui_react": "https://docs.langchain.com/langsmith/generative-ui-react",
|
||||
"/concepts/langgraph_platform": "https://docs.langchain.com/langsmith/home",
|
||||
"/concepts/langgraph_components": "https://docs.langchain.com/langsmith/components",
|
||||
"/concepts/langgraph_server": "https://docs.langchain.com/langsmith/agent-server",
|
||||
"/concepts/langgraph_data_plane": "https://docs.langchain.com/langsmith/data-plane",
|
||||
"/concepts/langgraph_control_plane": "https://docs.langchain.com/langsmith/control-plane",
|
||||
"/concepts/langgraph_cli": "https://docs.langchain.com/langsmith/cli",
|
||||
"/concepts/langgraph_studio": "https://docs.langchain.com/langsmith/studio",
|
||||
"/cloud/how-tos/studio/quick_start": "https://docs.langchain.com/langsmith/quick-start-studio",
|
||||
"/cloud/how-tos/invoke_studio": "https://docs.langchain.com/langsmith/use-studio",
|
||||
"/cloud/how-tos/studio/manage_assistants": "https://docs.langchain.com/langsmith/use-studio",
|
||||
"/cloud/how-tos/threads_studio": "https://docs.langchain.com/langsmith/use-threads",
|
||||
"/cloud/how-tos/iterate_graph_studio": "https://docs.langchain.com/langsmith/use-studio",
|
||||
"/cloud/how-tos/studio/run_evals": "https://docs.langchain.com/langsmith/observability",
|
||||
"/cloud/how-tos/clone_traces_studio": "https://docs.langchain.com/langsmith/observability",
|
||||
"/cloud/how-tos/datasets_studio": "https://docs.langchain.com/langsmith/use-studio",
|
||||
"/concepts/sdk": "https://docs.langchain.com/langsmith/sdk",
|
||||
"/concepts/plans": "https://docs.langchain.com/langsmith/home",
|
||||
"/concepts/application_structure": "https://docs.langchain.com/langsmith/application-structure",
|
||||
"/concepts/scalability_and_resilience": "https://docs.langchain.com/langsmith/scalability-and-resilience",
|
||||
"/concepts/auth": "https://docs.langchain.com/langsmith/auth",
|
||||
"/how-tos/auth/custom_auth": "https://docs.langchain.com/langsmith/custom-auth",
|
||||
"/how-tos/auth/openapi_security": "https://docs.langchain.com/langsmith/openapi-security",
|
||||
"/concepts/assistants": "https://docs.langchain.com/langsmith/assistants",
|
||||
"/cloud/how-tos/configuration_cloud": "https://docs.langchain.com/langsmith/configuration-cloud",
|
||||
"/cloud/how-tos/use_threads": "https://docs.langchain.com/langsmith/use-threads",
|
||||
"/cloud/how-tos/background_run": "https://docs.langchain.com/langsmith/background-run",
|
||||
"/cloud/how-tos/same-thread": "https://docs.langchain.com/langsmith/same-thread",
|
||||
"/cloud/how-tos/stateless_runs": "https://docs.langchain.com/langsmith/stateless-runs",
|
||||
"/cloud/how-tos/configurable_headers": "https://docs.langchain.com/langsmith/configurable-headers",
|
||||
"/concepts/double_texting": "https://docs.langchain.com/langsmith/double-texting",
|
||||
"/cloud/how-tos/interrupt_concurrent": "https://docs.langchain.com/langsmith/interrupt-concurrent",
|
||||
"/cloud/how-tos/rollback_concurrent": "https://docs.langchain.com/langsmith/rollback-concurrent",
|
||||
"/cloud/how-tos/reject_concurrent": "https://docs.langchain.com/langsmith/reject-concurrent",
|
||||
"/cloud/how-tos/enqueue_concurrent": "https://docs.langchain.com/langsmith/enqueue-concurrent",
|
||||
"/cloud/concepts/webhooks": "https://docs.langchain.com/langsmith/use-webhooks",
|
||||
"/cloud/how-tos/webhooks": "https://docs.langchain.com/langsmith/use-webhooks",
|
||||
"/cloud/concepts/cron_jobs": "https://docs.langchain.com/langsmith/cron-jobs",
|
||||
"/cloud/how-tos/cron_jobs": "https://docs.langchain.com/langsmith/cron-jobs",
|
||||
"/how-tos/http/custom_lifespan": "https://docs.langchain.com/langsmith/custom-lifespan",
|
||||
"/how-tos/http/custom_middleware": "https://docs.langchain.com/langsmith/custom-middleware",
|
||||
"/how-tos/http/custom_routes": "https://docs.langchain.com/langsmith/custom-routes",
|
||||
"/cloud/concepts/data_storage_and_privacy": "https://docs.langchain.com/langsmith/data-storage-and-privacy",
|
||||
"/cloud/deployment/semantic_search": "https://docs.langchain.com/langsmith/semantic-search",
|
||||
"/how-tos/ttl/configure_ttl": "https://docs.langchain.com/langsmith/configure-ttl",
|
||||
"/concepts/deployment_options": "https://docs.langchain.com/langsmith/deployments",
|
||||
"/cloud/quick_start": "https://docs.langchain.com/langsmith/deployment-quickstart",
|
||||
"/cloud/deployment/setup": "https://docs.langchain.com/langsmith/setup-app-requirements-txt",
|
||||
"/cloud/deployment/setup_pyproject": "https://docs.langchain.com/langsmith/setup-pyproject",
|
||||
"/cloud/deployment/setup_javascript": "https://docs.langchain.com/langsmith/setup-javascript",
|
||||
"/cloud/deployment/custom_docker": "https://docs.langchain.com/langsmith/custom-docker",
|
||||
"/cloud/deployment/graph_rebuild": "https://docs.langchain.com/langsmith/graph-rebuild",
|
||||
"/concepts/langgraph_cloud": "https://docs.langchain.com/langsmith/cloud",
|
||||
"/concepts/langgraph_self_hosted_data_plane": "https://docs.langchain.com/langsmith/platform-setup",
|
||||
"/concepts/langgraph_self_hosted_control_plane": "https://docs.langchain.com/langsmith/platform-setup",
|
||||
"/concepts/langgraph_standalone_container": "https://docs.langchain.com/langsmith/docker",
|
||||
"/cloud/deployment/cloud": "https://docs.langchain.com/langsmith/cloud",
|
||||
"/cloud/deployment/self_hosted_data_plane": "https://docs.langchain.com/langsmith/platform-setup",
|
||||
"/cloud/deployment/self_hosted_control_plane": "https://docs.langchain.com/langsmith/platform-setup",
|
||||
"/cloud/deployment/standalone_container": "https://docs.langchain.com/langsmith/docker",
|
||||
"/concepts/server-mcp": "https://docs.langchain.com/langsmith/server-mcp",
|
||||
"/cloud/how-tos/human_in_the_loop_time_travel": "https://docs.langchain.com/langsmith/human-in-the-loop-time-travel",
|
||||
"/cloud/how-tos/add-human-in-the-loop": "https://docs.langchain.com/langsmith/add-human-in-the-loop",
|
||||
"/cloud/deployment/egress": "https://docs.langchain.com/langsmith/env-var",
|
||||
"/cloud/how-tos/streaming": "https://docs.langchain.com/langsmith/streaming",
|
||||
"/cloud/reference/api/api_ref": "https://docs.langchain.com/langsmith/server-api-ref",
|
||||
"/cloud/reference/langgraph_server_changelog": "https://docs.langchain.com/langsmith/agent-server-changelog",
|
||||
"/cloud/reference/api/api_ref_control_plane": "https://docs.langchain.com/langsmith/api-ref-control-plane",
|
||||
"/cloud/reference/cli": "https://docs.langchain.com/langsmith/cli",
|
||||
"/cloud/reference/env_var": "https://docs.langchain.com/langsmith/env-var",
|
||||
"/troubleshooting/studio": "https://docs.langchain.com/langsmith/troubleshooting-studio",
|
||||
"/index": "https://docs.langchain.com/oss/python/langgraph/overview",
|
||||
"/agents/agents": "https://docs.langchain.com/oss/python/langchain/agents",
|
||||
"/concepts/why-langgraph": "https://docs.langchain.com/oss/python/langgraph/overview",
|
||||
"/tutorials/get-started/1-build-basic-chatbot": "https://docs.langchain.com/oss/python/langgraph/quickstart",
|
||||
"/tutorials/get-started/2-add-tools": "https://docs.langchain.com/oss/python/langgraph/quickstart",
|
||||
"/tutorials/get-started/3-add-memory": "https://docs.langchain.com/oss/python/langgraph/quickstart",
|
||||
"/tutorials/get-started/4-human-in-the-loop": "https://docs.langchain.com/oss/python/langgraph/quickstart",
|
||||
"/tutorials/get-started/5-customize-state": "https://docs.langchain.com/oss/python/langgraph/quickstart",
|
||||
"/tutorials/get-started/6-time-travel": "https://docs.langchain.com/oss/python/langgraph/quickstart",
|
||||
"/tutorials/langsmith/local-server": "https://docs.langchain.com/oss/python/langgraph/local-server",
|
||||
"/tutorials/workflows": "https://docs.langchain.com/oss/python/langgraph/workflows-agents",
|
||||
"/tutorials/plan-and-execute/plan-and-execute": "https://docs.langchain.com/oss/python/langchain/middleware/built-in#to-do-list",
|
||||
"/tutorials/langgraph-platform/local-server/local-server": "https://docs.langchain.com/langsmith/local-server",
|
||||
"/concepts/agentic_concepts": "https://docs.langchain.com/oss/python/langgraph/workflows-agents",
|
||||
"/agents/overview": "https://docs.langchain.com/oss/python/langchain/agents",
|
||||
"/agents/run_agents": "https://docs.langchain.com/oss/python/langgraph/quickstart",
|
||||
"/concepts/low_level": "https://docs.langchain.com/oss/python/langgraph/graph-api",
|
||||
"/how-tos/graph-api": "https://docs.langchain.com/oss/python/langgraph/graph-api",
|
||||
"/how-tos/react-agent-from-scratch": "https://docs.langchain.com/oss/python/langchain/quickstart",
|
||||
"/concepts/functional_api": "https://docs.langchain.com/oss/python/langgraph/functional-api",
|
||||
"/how-tos/use-functional-api": "https://docs.langchain.com/oss/python/langgraph/functional-api",
|
||||
"/concepts/pregel": "https://docs.langchain.com/oss/python/langgraph/pregel",
|
||||
"/concepts/streaming": "https://docs.langchain.com/oss/python/langgraph/streaming",
|
||||
"/how-tos/streaming": "https://docs.langchain.com/oss/python/langgraph/streaming",
|
||||
"/concepts/persistence": "https://docs.langchain.com/oss/python/langgraph/persistence",
|
||||
"/concepts/durable_execution": "https://docs.langchain.com/oss/python/langgraph/durable-execution",
|
||||
"/concepts/memory": "https://docs.langchain.com/oss/python/langgraph/memory",
|
||||
"/how-tos/memory/add-memory": "https://docs.langchain.com/oss/python/langgraph/add-memory",
|
||||
"/agents/context": "https://docs.langchain.com/oss/python/langgraph/add-memory",
|
||||
"/agents/models": "https://docs.langchain.com/oss/python/langgraph/overview",
|
||||
"/concepts/tools": "https://docs.langchain.com/oss/python/langgraph/workflows-agents",
|
||||
"/how-tos/tool-calling": "https://docs.langchain.com/oss/python/langgraph/workflows-agents",
|
||||
"/concepts/human_in_the_loop": "https://docs.langchain.com/oss/python/langgraph/interrupts",
|
||||
"/how-tos/human_in_the_loop/add-human-in-the-loop": "https://docs.langchain.com/oss/python/langgraph/interrupts",
|
||||
"/concepts/time-travel": "https://docs.langchain.com/oss/python/langgraph/persistence",
|
||||
"/how-tos/human_in_the_loop/time-travel": "https://docs.langchain.com/oss/python/langgraph/use-time-travel",
|
||||
"/concepts/subgraphs": "https://docs.langchain.com/oss/python/langgraph/use-subgraphs",
|
||||
"/how-tos/subgraph": "https://docs.langchain.com/oss/python/langgraph/use-subgraphs",
|
||||
"/concepts/multi_agent": "https://docs.langchain.com/oss/python/langgraph/graph-api",
|
||||
"/agents/multi-agent": "https://docs.langchain.com/oss/python/langchain/multi-agent",
|
||||
"/how-tos/multi_agent": "https://docs.langchain.com/oss/python/langgraph/graph-api",
|
||||
"/concepts/mcp": "https://docs.langchain.com/oss/python/langgraph/overview",
|
||||
"/agents/mcp": "https://docs.langchain.com/oss/python/langgraph/overview",
|
||||
"/concepts/tracing": "https://docs.langchain.com/oss/python/langgraph/observability",
|
||||
"/how-tos/enable-tracing": "https://docs.langchain.com/oss/python/langgraph/observability",
|
||||
"/agents/evals": "https://docs.langchain.com/oss/python/langgraph/overview",
|
||||
"/concepts/template_applications": "https://docs.langchain.com/oss/python/langgraph/overview",
|
||||
"/tutorials/rag/langgraph_agentic_rag": "https://docs.langchain.com/oss/python/langgraph/agentic-rag",
|
||||
"/tutorials/multi_agent/agent_supervisor": "https://docs.langchain.com/oss/python/langgraph/workflows-agents",
|
||||
"/tutorials/sql/sql-agent": "https://docs.langchain.com/oss/python/langgraph/sql-agent",
|
||||
"/agents/ui": "https://docs.langchain.com/oss/python/langgraph/ui",
|
||||
"/how-tos/run-id-langsmith": "https://docs.langchain.com/oss/python/langgraph/observability",
|
||||
"/troubleshooting/errors/index": "https://docs.langchain.com/oss/python/langgraph/common-errors",
|
||||
"/troubleshooting/errors/INVALID_CHAT_HISTORY": "https://docs.langchain.com/oss/python/langgraph/INVALID_CHAT_HISTORY",
|
||||
"/troubleshooting/errors/INVALID_LICENSE": "https://docs.langchain.com/oss/python/langgraph/common-errors",
|
||||
"/adopters": "https://docs.langchain.com/oss/python/langgraph/case-studies",
|
||||
"/concepts/faq": "https://docs.langchain.com/oss/python/langgraph/overview",
|
||||
"/agents/prebuilt": "https://docs.langchain.com/oss/python/langchain/agents",
|
||||
"/reference/index": "https://reference.langchain.com/python/langgraph/",
|
||||
"/reference/graphs": "https://reference.langchain.com/python/langgraph/graphs/",
|
||||
"/reference/func": "https://reference.langchain.com/python/langgraph/func/",
|
||||
"/reference/pregel": "https://reference.langchain.com/python/langgraph/pregel/",
|
||||
"/reference/checkpoints": "https://reference.langchain.com/python/langgraph/checkpoints/",
|
||||
"/reference/store": "https://reference.langchain.com/python/langgraph/store/",
|
||||
"/reference/cache": "https://reference.langchain.com/python/langgraph/cache/",
|
||||
"/reference/types": "https://reference.langchain.com/python/langgraph/types/",
|
||||
"/reference/runtime": "https://reference.langchain.com/python/langgraph/runtime/",
|
||||
"/reference/config": "https://reference.langchain.com/python/langgraph/config/",
|
||||
"/reference/errors": "https://reference.langchain.com/python/langgraph/errors/",
|
||||
"/reference/constants": "https://reference.langchain.com/python/langgraph/constants/",
|
||||
"/reference/channels": "https://reference.langchain.com/python/langgraph/channels/",
|
||||
"/reference/agents": "https://reference.langchain.com/python/langgraph/agents/",
|
||||
"/reference/supervisor": "https://reference.langchain.com/python/langgraph/supervisor/",
|
||||
"/reference/swarm": "https://reference.langchain.com/python/langgraph/swarm/",
|
||||
"/reference/mcp": "https://reference.langchain.com/python/langgraph/mcp/",
|
||||
"/cloud/reference/sdk/python_sdk_ref": "https://reference.langchain.com/python/langsmith/deployment/sdk/",
|
||||
"/reference/remote_graph": "https://reference.langchain.com/python/langsmith/deployment/remote_graph/",
|
||||
"/additional-resources/index": "https://docs.langchain.com/oss/python/langchain/overview",
|
||||
"/cloud/reference/sdk/js_ts_sdk_ref": "https://reference.langchain.com/javascript/modules/langsmith.html",
|
||||
"/snippets/chat_model_tabs": "https://docs.langchain.com/oss/python/langchain/overview",
|
||||
"/troubleshooting/errors/GRAPH_RECURSION_LIMIT": "https://docs.langchain.com/oss/python/langgraph/GRAPH_RECURSION_LIMIT",
|
||||
"/troubleshooting/errors/INVALID_CONCURRENT_GRAPH_UPDATE": "https://docs.langchain.com/oss/python/langgraph/INVALID_CONCURRENT_GRAPH_UPDATE",
|
||||
"/troubleshooting/errors/INVALID_GRAPH_NODE_RETURN_VALUE": "https://docs.langchain.com/oss/python/langgraph/INVALID_GRAPH_NODE_RETURN_VALUE",
|
||||
"/troubleshooting/errors/MULTIPLE_SUBGRAPHS": "https://docs.langchain.com/oss/python/langgraph/MULTIPLE_SUBGRAPHS",
|
||||
"/tutorials/rag/langgraph_self_rag": "https://docs.langchain.com/oss/python/langgraph/agentic-rag",
|
||||
"/additional-resources": "https://docs.langchain.com/oss/python/langgraph/overview",
|
||||
"/examples": "https://docs.langchain.com/oss/python/langgraph/overview",
|
||||
"/guides": "https://docs.langchain.com/oss/python/langgraph/overview",
|
||||
"/how-tos/autogen-integration-functional": "https://docs.langchain.com/oss/python/langgraph/overview",
|
||||
"/how-tos/cross-thread-persistence-functional": "https://docs.langchain.com/oss/python/langgraph/add-memory#add-long-term-memory",
|
||||
"/how-tos/disable-streaming": "https://docs.langchain.com/oss/python/langgraph/streaming",
|
||||
"/how-tos/memory/semantic-search": "https://docs.langchain.com/oss/python/langgraph/add-memory",
|
||||
"/how-tos/multi-agent-multi-turn-convo-functional": "https://docs.langchain.com/oss/python/langgraph/graph-api",
|
||||
"/how-tos/multi-agent-network-functional": "https://docs.langchain.com/oss/python/langgraph/graph-api",
|
||||
"/how-tos/persistence-functional": "https://docs.langchain.com/oss/python/langgraph/add-memory",
|
||||
"/how-tos/react-agent-from-scratch-functional": "https://docs.langchain.com/oss/python/langgraph/workflows-agents",
|
||||
"/reference": "https://reference.langchain.com/python/langgraph/",
|
||||
"/troubleshooting/errors": "https://docs.langchain.com/oss/python/langgraph/common-errors",
|
||||
"/tutorials/chatbot-simulation-evaluation/agent-simulation-evaluation": "https://docs.langchain.com/oss/python/langgraph/overview",
|
||||
"/tutorials/chatbot-simulation-evaluation/langsmith-agent-simulation-evaluation": "https://docs.langchain.com/oss/python/langgraph/overview",
|
||||
"/tutorials/chatbots/information-gather-prompting": "https://docs.langchain.com/oss/python/langgraph/overview",
|
||||
"/tutorials/extraction/retries": "https://docs.langchain.com/oss/python/langgraph/overview",
|
||||
"/tutorials/langgraph-platform/local-server": "https://docs.langchain.com/langsmith/agent-server",
|
||||
"/tutorials/lats/lats": "https://docs.langchain.com/oss/python/langgraph/overview",
|
||||
"/tutorials/llm-compiler/LLMCompiler": "https://docs.langchain.com/oss/python/langgraph/overview",
|
||||
"/tutorials/rag/langgraph_adaptive_rag_local": "https://docs.langchain.com/oss/python/langgraph/agentic-rag",
|
||||
"/tutorials/rag/langgraph_crag": "https://docs.langchain.com/oss/python/langgraph/agentic-rag",
|
||||
"/tutorials/rag/langgraph_crag_local": "https://docs.langchain.com/oss/python/langgraph/agentic-rag",
|
||||
"/tutorials/rag/langgraph_self_rag_local": "https://docs.langchain.com/oss/python/langgraph/agentic-rag",
|
||||
"/tutorials/reflection/reflection": "https://docs.langchain.com/oss/python/langgraph/overview",
|
||||
"/tutorials/reflexion/reflexion": "https://docs.langchain.com/oss/python/langgraph/overview",
|
||||
"/tutorials/rewoo/rewoo": "https://docs.langchain.com/oss/python/langgraph/overview",
|
||||
"/tutorials/self-discover/self-discover": "https://docs.langchain.com/oss/python/langgraph/overview",
|
||||
"/tutorials/tnt-llm/tnt-llm": "https://docs.langchain.com/oss/python/langgraph/overview",
|
||||
"/tutorials/tot/tot": "https://docs.langchain.com/oss/python/langgraph/overview",
|
||||
"/tutorials/usaco/usaco": "https://docs.langchain.com/oss/python/langgraph/overview",
|
||||
"/tutorials/web-navigation/web_voyager": "https://docs.langchain.com/oss/python/langgraph/overview"
|
||||
}
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
# LangGraph examples
|
||||
|
||||
This directory should NOT be used for documentation. All new documentation must be added to `docs/docs/` directory.
|
||||
This directory is retained purely for archival purposes and is no longer updated. The examples previously found here have been moved to the newly [consolidated LangChain documentation](https://docs.langchain.com/oss/python/langgraph/overview). Please refer to the LangChain docs for the most up-to-date examples and usage guidelines for LangGraph.
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "23544406",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This file has been moved to https://github.com/langchain-ai/langgraph/blob/main/docs/docs/how-tos/async.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.12.2"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "14f7ca50",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This file has been moved to https://github.com/langchain-ai/langgraph/blob/main/docs/docs/how-tos/branching.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
|
||||
}
|
||||
@@ -5,7 +5,15 @@
|
||||
"id": "10251c1c",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This file has been moved to https://github.com/langchain-ai/langgraph/blob/main/docs/docs/tutorials/chatbot-simulation-evaluation/agent-simulation-evaluation.ipynb"
|
||||
"[This file has been moved](https://github.com/langchain-ai/langgraph/blob/23961cff61a42b52525f3b20b4094d8d2fba1744/docs/docs/tutorials/chatbot-simulation-evaluation/agent-simulation-evaluation.ipynb)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "c5fc63df",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This directory is retained purely for archival purposes and is no longer updated. The examples previously found here have been moved to the newly [consolidated LangChain documentation](https://docs.langchain.com/oss/python/langgraph/overview)."
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
@@ -5,7 +5,15 @@
|
||||
"id": "a4351a24",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This file has been moved to https://github.com/langchain-ai/langgraph/blob/main/docs/docs/tutorials/chatbot-simulation-evaluation/langsmith-agent-simulation-evaluation.ipynb"
|
||||
"[This file has been moved](https://github.com/langchain-ai/langgraph/blob/23961cff61a42b52525f3b20b4094d8d2fba1744/docs/docs/tutorials/chatbot-simulation-evaluation/langsmith-agent-simulation-evaluation.ipynb)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "4cc9af1e",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This directory is retained purely for archival purposes and is no longer updated. The examples previously found here have been moved to the newly [consolidated LangChain documentation](https://docs.langchain.com/oss/python/langgraph/overview)."
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
@@ -5,7 +5,15 @@
|
||||
"id": "a9014f94",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This file has been moved to https://github.com/langchain-ai/langgraph/blob/main/docs/docs/tutorials/chatbots/information-gather-prompting.ipynb"
|
||||
"[This file has been moved](https://github.com/langchain-ai/langgraph/blob/23961cff61a42b52525f3b20b4094d8d2fba1744/docs/docs/tutorials/chatbots/information-gather-prompting.ipynb)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "f47ce992",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This directory is retained purely for archival purposes and is no longer updated. The examples previously found here have been moved to the newly [consolidated LangChain documentation](https://docs.langchain.com/oss/python/langgraph/overview)."
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "2b789e16",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This file has been moved to https://github.com/langchain-ai/langgraph/blob/main/docs/docs/cloud/how-tos/langgraph_to_langgraph_cloud.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.1"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
@@ -5,7 +5,15 @@
|
||||
"id": "1f2f13ca",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This file has been moved to https://github.com/langchain-ai/langgraph/blob/main/docs/docs/tutorials/code_assistant/langgraph_code_assistant.ipynb"
|
||||
"[This file has been moved](https://github.com/langchain-ai/langgraph/blob/23961cff61a42b52525f3b20b4094d8d2fba1744/docs/docs/tutorials/code_assistant/langgraph_code_assistant.ipynb)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "5e4c9bfe",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This directory is retained purely for archival purposes and is no longer updated. The examples previously found here have been moved to the newly [consolidated LangChain documentation](https://docs.langchain.com/oss/python/langgraph/overview)."
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "1d38cbab",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This directory is retained purely for archival purposes and is no longer updated. Please see the newly [consolidated LangChain documentation](https://docs.langchain.com/oss/python/langgraph/overview) for the most current information and resources."
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {
|
||||
"15d3ac32-cdf3-4800-a30c-f26d828d69c8.png": {
|
||||
@@ -33,7 +41,9 @@
|
||||
"id": "e501686f-323f-4b87-8f9c-8ba89133078b",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": ["! pip install -U langchain_community langchain-mistralai langchain langgraph"]
|
||||
"source": [
|
||||
"! pip install -U langchain_community langchain-mistralai langchain langgraph"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
@@ -51,7 +61,12 @@
|
||||
"id": "982e4609-86e4-4934-828f-e03d89c20393",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": ["import os\n\nos.environ[\"TOKENIZERS_PARALLELISM\"] = \"true\"\nmistral_api_key = os.getenv(\"MISTRAL_API_KEY\") # Ensure this is set"]
|
||||
"source": [
|
||||
"import os\n",
|
||||
"\n",
|
||||
"os.environ[\"TOKENIZERS_PARALLELISM\"] = \"true\"\n",
|
||||
"mistral_api_key = os.getenv(\"MISTRAL_API_KEY\") # Ensure this is set"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
@@ -69,7 +84,12 @@
|
||||
"id": "37b172d2-3a9d-49a8-898c-22ed0cb45c88",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": ["os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\nos.environ[\"LANGCHAIN_ENDPOINT\"] = \"https://api.smith.langchain.com\"\nos.environ[\"LANGCHAIN_API_KEY\"] = \"<your-api-key>\"\nos.environ[\"LANGCHAIN_PROJECT\"] = \"Mistral-code-gen-testing\""]
|
||||
"source": [
|
||||
"os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n",
|
||||
"os.environ[\"LANGCHAIN_ENDPOINT\"] = \"https://api.smith.langchain.com\"\n",
|
||||
"os.environ[\"LANGCHAIN_API_KEY\"] = \"<your-api-key>\"\n",
|
||||
"os.environ[\"LANGCHAIN_PROJECT\"] = \"Mistral-code-gen-testing\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
@@ -87,7 +107,42 @@
|
||||
"id": "a188c8ca-c053-4e6d-b7af-38a3b6b371c7",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": ["# Select LLM\nfrom langchain_core.prompts import ChatPromptTemplate\nfrom langchain_core.pydantic_v1 import BaseModel, Field\nfrom langchain_mistralai import ChatMistralAI\n\nmistral_model = \"mistral-large-latest\"\nllm = ChatMistralAI(model=mistral_model, temperature=0)\n\n# Prompt\ncode_gen_prompt_claude = ChatPromptTemplate.from_messages(\n [\n (\n \"system\",\n \"\"\"You are a coding assistant. Ensure any code you provide can be executed with all required imports and variables \\n\n defined. Structure your answer: 1) a prefix describing the code solution, 2) the imports, 3) the functioning code block.\n \\n Here is the user question:\"\"\",\n ),\n (\"placeholder\", \"{messages}\"),\n ]\n)\n\n\n# Data model\nclass code(BaseModel):\n \"\"\"Code output\"\"\"\n\n prefix: str = Field(description=\"Description of the problem and approach\")\n imports: str = Field(description=\"Code block import statements\")\n code: str = Field(description=\"Code block not including import statements\")\n description = \"Schema for code solutions to questions about LCEL.\"\n\n\n# LLM\ncode_gen_chain = llm.with_structured_output(code, include_raw=False)"]
|
||||
"source": [
|
||||
"# Select LLM\n",
|
||||
"from langchain_core.prompts import ChatPromptTemplate\n",
|
||||
"from langchain_core.pydantic_v1 import BaseModel, Field\n",
|
||||
"from langchain_mistralai import ChatMistralAI\n",
|
||||
"\n",
|
||||
"mistral_model = \"mistral-large-latest\"\n",
|
||||
"llm = ChatMistralAI(model=mistral_model, temperature=0)\n",
|
||||
"\n",
|
||||
"# Prompt\n",
|
||||
"code_gen_prompt_claude = ChatPromptTemplate.from_messages(\n",
|
||||
" [\n",
|
||||
" (\n",
|
||||
" \"system\",\n",
|
||||
" \"\"\"You are a coding assistant. Ensure any code you provide can be executed with all required imports and variables \\n\n",
|
||||
" defined. Structure your answer: 1) a prefix describing the code solution, 2) the imports, 3) the functioning code block.\n",
|
||||
" \\n Here is the user question:\"\"\",\n",
|
||||
" ),\n",
|
||||
" (\"placeholder\", \"{messages}\"),\n",
|
||||
" ]\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Data model\n",
|
||||
"class code(BaseModel):\n",
|
||||
" \"\"\"Code output\"\"\"\n",
|
||||
"\n",
|
||||
" prefix: str = Field(description=\"Description of the problem and approach\")\n",
|
||||
" imports: str = Field(description=\"Code block import statements\")\n",
|
||||
" code: str = Field(description=\"Code block not including import statements\")\n",
|
||||
" description = \"Schema for code solutions to questions about LCEL.\"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# LLM\n",
|
||||
"code_gen_chain = llm.with_structured_output(code, include_raw=False)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
@@ -95,7 +150,10 @@
|
||||
"id": "9fc0290d-5a04-4514-8664-91f9dbf2da7b",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": ["question = \"Write a function for fibonacci.\"\nmessages = [(\"user\", question)]"]
|
||||
"source": [
|
||||
"question = \"Write a function for fibonacci.\"\n",
|
||||
"messages = [(\"user\", question)]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
@@ -114,7 +172,11 @@
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": ["# Test\nresult = code_gen_chain.invoke(messages)\nresult"]
|
||||
"source": [
|
||||
"# Test\n",
|
||||
"result = code_gen_chain.invoke(messages)\n",
|
||||
"result"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
@@ -130,7 +192,28 @@
|
||||
"id": "183d77b8-f180-4815-b39f-8ef507ec0534",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": ["from typing import Annotated, TypedDict\n\nfrom langgraph.graph.message import AnyMessage, add_messages\n\n\nclass GraphState(TypedDict):\n \"\"\"\n Represents the state of our graph.\n\n Attributes:\n error : Binary flag for control flow to indicate whether test error was tripped\n messages : With user question, error messages, reasoning\n generation : Code solution\n iterations : Number of tries\n \"\"\"\n\n error: str\n messages: Annotated[list[AnyMessage], add_messages]\n generation: str\n iterations: int"]
|
||||
"source": [
|
||||
"from typing import Annotated, TypedDict\n",
|
||||
"\n",
|
||||
"from langgraph.graph.message import AnyMessage, add_messages\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class GraphState(TypedDict):\n",
|
||||
" \"\"\"\n",
|
||||
" Represents the state of our graph.\n",
|
||||
"\n",
|
||||
" Attributes:\n",
|
||||
" error : Binary flag for control flow to indicate whether test error was tripped\n",
|
||||
" messages : With user question, error messages, reasoning\n",
|
||||
" generation : Code solution\n",
|
||||
" iterations : Number of tries\n",
|
||||
" \"\"\"\n",
|
||||
"\n",
|
||||
" error: str\n",
|
||||
" messages: Annotated[list[AnyMessage], add_messages]\n",
|
||||
" generation: str\n",
|
||||
" iterations: int"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
@@ -146,7 +229,163 @@
|
||||
"id": "14bc89d1-3ca6-4847-a048-1803e0e4600e",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": ["import uuid\n\nfrom langchain_core.pydantic_v1 import BaseModel, Field\n\n### Parameters\nmax_iterations = 3\n\n\n### Nodes\ndef generate(state: GraphState):\n \"\"\"\n Generate a code solution\n\n Args:\n state (dict): The current graph state\n\n Returns:\n state (dict): New key added to state, generation\n \"\"\"\n\n print(\"---GENERATING CODE SOLUTION---\")\n\n # State\n messages = state[\"messages\"]\n iterations = state[\"iterations\"]\n\n # Solution\n code_solution = code_gen_chain.invoke(messages)\n messages += [\n (\n \"assistant\",\n f\"Here is my attempt to solve the problem: {code_solution.prefix} \\n Imports: {code_solution.imports} \\n Code: {code_solution.code}\",\n )\n ]\n\n # Increment\n iterations = iterations + 1\n return {\"generation\": code_solution, \"messages\": messages, \"iterations\": iterations}\n\n\ndef code_check(state: GraphState):\n \"\"\"\n Check code\n\n Args:\n state (dict): The current graph state\n\n Returns:\n state (dict): New key added to state, error\n \"\"\"\n\n print(\"---CHECKING CODE---\")\n\n # State\n messages = state[\"messages\"]\n code_solution = state[\"generation\"]\n iterations = state[\"iterations\"]\n\n # Get solution components\n imports = code_solution.imports\n code = code_solution.code\n\n # Check imports\n try:\n exec(imports)\n except Exception as e:\n print(\"---CODE IMPORT CHECK: FAILED---\")\n error_message = [\n (\n \"user\",\n f\"Your solution failed the import test. Here is the error: {e}. Reflect on this error and your prior attempt to solve the problem. (1) State what you think went wrong with the prior solution and (2) try to solve this problem again. Return the FULL SOLUTION. Use the code tool to structure the output with a prefix, imports, and code block:\",\n )\n ]\n messages += error_message\n return {\n \"generation\": code_solution,\n \"messages\": messages,\n \"iterations\": iterations,\n \"error\": \"yes\",\n }\n\n # Check execution\n try:\n combined_code = f\"{imports}\\n{code}\"\n print(f\"CODE TO TEST: {combined_code}\")\n # Use a shared scope for exec\n global_scope = {}\n exec(combined_code, global_scope)\n except Exception as e:\n print(\"---CODE BLOCK CHECK: FAILED---\")\n error_message = [\n (\n \"user\",\n f\"Your solution failed the code execution test: {e}) Reflect on this error and your prior attempt to solve the problem. (1) State what you think went wrong with the prior solution and (2) try to solve this problem again. Return the FULL SOLUTION. Use the code tool to structure the output with a prefix, imports, and code block:\",\n )\n ]\n messages += error_message\n return {\n \"generation\": code_solution,\n \"messages\": messages,\n \"iterations\": iterations,\n \"error\": \"yes\",\n }\n\n # No errors\n print(\"---NO CODE TEST FAILURES---\")\n return {\n \"generation\": code_solution,\n \"messages\": messages,\n \"iterations\": iterations,\n \"error\": \"no\",\n }\n\n\n### Conditional edges\n\n\ndef decide_to_finish(state: GraphState):\n \"\"\"\n Determines whether to finish.\n\n Args:\n state (dict): The current graph state\n\n Returns:\n str: Next node to call\n \"\"\"\n error = state[\"error\"]\n iterations = state[\"iterations\"]\n\n if error == \"no\" or iterations == max_iterations:\n print(\"---DECISION: FINISH---\")\n return \"end\"\n else:\n print(\"---DECISION: RE-TRY SOLUTION---\")\n return \"generate\"\n\n\n### Utilities\n\n\ndef _print_event(event: dict, _printed: set, max_length=1500):\n current_state = event.get(\"dialog_state\")\n if current_state:\n print(\"Currently in: \", current_state[-1])\n message = event.get(\"messages\")\n if message:\n if isinstance(message, list):\n message = message[-1]\n if message.id not in _printed:\n msg_repr = message.pretty_repr(html=True)\n if len(msg_repr) > max_length:\n msg_repr = msg_repr[:max_length] + \" ... (truncated)\"\n print(msg_repr)\n _printed.add(message.id)"]
|
||||
"source": [
|
||||
"import uuid\n",
|
||||
"\n",
|
||||
"from langchain_core.pydantic_v1 import BaseModel, Field\n",
|
||||
"\n",
|
||||
"### Parameters\n",
|
||||
"max_iterations = 3\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"### Nodes\n",
|
||||
"def generate(state: GraphState):\n",
|
||||
" \"\"\"\n",
|
||||
" Generate a code solution\n",
|
||||
"\n",
|
||||
" Args:\n",
|
||||
" state (dict): The current graph state\n",
|
||||
"\n",
|
||||
" Returns:\n",
|
||||
" state (dict): New key added to state, generation\n",
|
||||
" \"\"\"\n",
|
||||
"\n",
|
||||
" print(\"---GENERATING CODE SOLUTION---\")\n",
|
||||
"\n",
|
||||
" # State\n",
|
||||
" messages = state[\"messages\"]\n",
|
||||
" iterations = state[\"iterations\"]\n",
|
||||
"\n",
|
||||
" # Solution\n",
|
||||
" code_solution = code_gen_chain.invoke(messages)\n",
|
||||
" messages += [\n",
|
||||
" (\n",
|
||||
" \"assistant\",\n",
|
||||
" f\"Here is my attempt to solve the problem: {code_solution.prefix} \\n Imports: {code_solution.imports} \\n Code: {code_solution.code}\",\n",
|
||||
" )\n",
|
||||
" ]\n",
|
||||
"\n",
|
||||
" # Increment\n",
|
||||
" iterations = iterations + 1\n",
|
||||
" return {\"generation\": code_solution, \"messages\": messages, \"iterations\": iterations}\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def code_check(state: GraphState):\n",
|
||||
" \"\"\"\n",
|
||||
" Check code\n",
|
||||
"\n",
|
||||
" Args:\n",
|
||||
" state (dict): The current graph state\n",
|
||||
"\n",
|
||||
" Returns:\n",
|
||||
" state (dict): New key added to state, error\n",
|
||||
" \"\"\"\n",
|
||||
"\n",
|
||||
" print(\"---CHECKING CODE---\")\n",
|
||||
"\n",
|
||||
" # State\n",
|
||||
" messages = state[\"messages\"]\n",
|
||||
" code_solution = state[\"generation\"]\n",
|
||||
" iterations = state[\"iterations\"]\n",
|
||||
"\n",
|
||||
" # Get solution components\n",
|
||||
" imports = code_solution.imports\n",
|
||||
" code = code_solution.code\n",
|
||||
"\n",
|
||||
" # Check imports\n",
|
||||
" try:\n",
|
||||
" exec(imports)\n",
|
||||
" except Exception as e:\n",
|
||||
" print(\"---CODE IMPORT CHECK: FAILED---\")\n",
|
||||
" error_message = [\n",
|
||||
" (\n",
|
||||
" \"user\",\n",
|
||||
" f\"Your solution failed the import test. Here is the error: {e}. Reflect on this error and your prior attempt to solve the problem. (1) State what you think went wrong with the prior solution and (2) try to solve this problem again. Return the FULL SOLUTION. Use the code tool to structure the output with a prefix, imports, and code block:\",\n",
|
||||
" )\n",
|
||||
" ]\n",
|
||||
" messages += error_message\n",
|
||||
" return {\n",
|
||||
" \"generation\": code_solution,\n",
|
||||
" \"messages\": messages,\n",
|
||||
" \"iterations\": iterations,\n",
|
||||
" \"error\": \"yes\",\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
" # Check execution\n",
|
||||
" try:\n",
|
||||
" combined_code = f\"{imports}\\n{code}\"\n",
|
||||
" print(f\"CODE TO TEST: {combined_code}\")\n",
|
||||
" # Use a shared scope for exec\n",
|
||||
" global_scope = {}\n",
|
||||
" exec(combined_code, global_scope)\n",
|
||||
" except Exception as e:\n",
|
||||
" print(\"---CODE BLOCK CHECK: FAILED---\")\n",
|
||||
" error_message = [\n",
|
||||
" (\n",
|
||||
" \"user\",\n",
|
||||
" f\"Your solution failed the code execution test: {e}) Reflect on this error and your prior attempt to solve the problem. (1) State what you think went wrong with the prior solution and (2) try to solve this problem again. Return the FULL SOLUTION. Use the code tool to structure the output with a prefix, imports, and code block:\",\n",
|
||||
" )\n",
|
||||
" ]\n",
|
||||
" messages += error_message\n",
|
||||
" return {\n",
|
||||
" \"generation\": code_solution,\n",
|
||||
" \"messages\": messages,\n",
|
||||
" \"iterations\": iterations,\n",
|
||||
" \"error\": \"yes\",\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
" # No errors\n",
|
||||
" print(\"---NO CODE TEST FAILURES---\")\n",
|
||||
" return {\n",
|
||||
" \"generation\": code_solution,\n",
|
||||
" \"messages\": messages,\n",
|
||||
" \"iterations\": iterations,\n",
|
||||
" \"error\": \"no\",\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"### Conditional edges\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def decide_to_finish(state: GraphState):\n",
|
||||
" \"\"\"\n",
|
||||
" Determines whether to finish.\n",
|
||||
"\n",
|
||||
" Args:\n",
|
||||
" state (dict): The current graph state\n",
|
||||
"\n",
|
||||
" Returns:\n",
|
||||
" str: Next node to call\n",
|
||||
" \"\"\"\n",
|
||||
" error = state[\"error\"]\n",
|
||||
" iterations = state[\"iterations\"]\n",
|
||||
"\n",
|
||||
" if error == \"no\" or iterations == max_iterations:\n",
|
||||
" print(\"---DECISION: FINISH---\")\n",
|
||||
" return \"end\"\n",
|
||||
" else:\n",
|
||||
" print(\"---DECISION: RE-TRY SOLUTION---\")\n",
|
||||
" return \"generate\"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"### Utilities\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def _print_event(event: dict, _printed: set, max_length=1500):\n",
|
||||
" current_state = event.get(\"dialog_state\")\n",
|
||||
" if current_state:\n",
|
||||
" print(\"Currently in: \", current_state[-1])\n",
|
||||
" message = event.get(\"messages\")\n",
|
||||
" if message:\n",
|
||||
" if isinstance(message, list):\n",
|
||||
" message = message[-1]\n",
|
||||
" if message.id not in _printed:\n",
|
||||
" msg_repr = message.pretty_repr(html=True)\n",
|
||||
" if len(msg_repr) > max_length:\n",
|
||||
" msg_repr = msg_repr[:max_length] + \" ... (truncated)\"\n",
|
||||
" print(msg_repr)\n",
|
||||
" _printed.add(message.id)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
@@ -154,7 +393,31 @@
|
||||
"id": "2dff2209-44c7-4e2c-b607-ba6675f9e45f",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": ["from langgraph.checkpoint.memory import InMemorySaver\nfrom langgraph.graph import END, StateGraph, START\n\nbuilder = StateGraph(GraphState)\n\n# Define the nodes\nbuilder.add_node(\"generate\", generate) # generation solution\nbuilder.add_node(\"check_code\", code_check) # check code\n\n# Build graph\nbuilder.add_edge(START, \"generate\")\nbuilder.add_edge(\"generate\", \"check_code\")\nbuilder.add_conditional_edges(\n \"check_code\",\n decide_to_finish,\n {\n \"end\": END,\n \"generate\": \"generate\",\n },\n)\n\nmemory = InMemorySaver()\ngraph = builder.compile(checkpointer=memory)"]
|
||||
"source": [
|
||||
"from langgraph.checkpoint.memory import InMemorySaver\n",
|
||||
"from langgraph.graph import END, StateGraph, START\n",
|
||||
"\n",
|
||||
"builder = StateGraph(GraphState)\n",
|
||||
"\n",
|
||||
"# Define the nodes\n",
|
||||
"builder.add_node(\"generate\", generate) # generation solution\n",
|
||||
"builder.add_node(\"check_code\", code_check) # check code\n",
|
||||
"\n",
|
||||
"# Build graph\n",
|
||||
"builder.add_edge(START, \"generate\")\n",
|
||||
"builder.add_edge(\"generate\", \"check_code\")\n",
|
||||
"builder.add_conditional_edges(\n",
|
||||
" \"check_code\",\n",
|
||||
" decide_to_finish,\n",
|
||||
" {\n",
|
||||
" \"end\": END,\n",
|
||||
" \"generate\": \"generate\",\n",
|
||||
" },\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"memory = InMemorySaver()\n",
|
||||
"graph = builder.compile(checkpointer=memory)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
@@ -173,7 +436,15 @@
|
||||
"output_type": "display_data"
|
||||
}
|
||||
],
|
||||
"source": ["from IPython.display import Image, display\n\ntry:\n display(Image(graph.get_graph(xray=True).draw_mermaid_png()))\nexcept Exception:\n # This requires some extra dependencies and is optional\n pass"]
|
||||
"source": [
|
||||
"from IPython.display import Image, display\n",
|
||||
"\n",
|
||||
"try:\n",
|
||||
" display(Image(graph.get_graph(xray=True).draw_mermaid_png()))\n",
|
||||
"except Exception:\n",
|
||||
" # This requires some extra dependencies and is optional\n",
|
||||
" pass"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
@@ -181,7 +452,23 @@
|
||||
"id": "242aa2f0-2c31-462f-a958-ff9ae0cf7c62",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": ["_printed = set()\nthread_id = str(uuid.uuid4())\nconfig = {\n \"configurable\": {\n # Checkpoints are accessed by thread_id\n \"thread_id\": thread_id,\n }\n}\n\nquestion = \"Write a Python program that prints 'Hello, World!' to the console.\"\nevents = graph.stream(\n {\"messages\": [(\"user\", question)], \"iterations\": 0}, config, stream_mode=\"values\"\n)\nfor event in events:\n _print_event(event, _printed)"]
|
||||
"source": [
|
||||
"_printed = set()\n",
|
||||
"thread_id = str(uuid.uuid4())\n",
|
||||
"config = {\n",
|
||||
" \"configurable\": {\n",
|
||||
" # Checkpoints are accessed by thread_id\n",
|
||||
" \"thread_id\": thread_id,\n",
|
||||
" }\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"question = \"Write a Python program that prints 'Hello, World!' to the console.\"\n",
|
||||
"events = graph.stream(\n",
|
||||
" {\"messages\": [(\"user\", question)], \"iterations\": 0}, config, stream_mode=\"values\"\n",
|
||||
")\n",
|
||||
"for event in events:\n",
|
||||
" _print_event(event, _printed)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
@@ -199,7 +486,31 @@
|
||||
"id": "390b2768-f395-4aea-8b0e-9d36212a31ac",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": ["_printed = set()\nthread_id = str(uuid.uuid4())\nconfig = {\n \"configurable\": {\n # Checkpoints are accessed by thread_id\n \"thread_id\": thread_id,\n }\n}\n\nquestion = \"\"\"Create a Python program that checks if a given string is a palindrome. A palindrome is a word, phrase, number, or other sequence of characters that reads the same forward and backward (ignoring spaces, punctuation, and capitalization).\n\nRequirements:\nThe program should define a function is_palindrome(s) that takes a string s as input.\nThe function should return True if the string is a palindrome and False otherwise.\nIgnore spaces, punctuation, and case differences when checking for palindromes.\n\nGive an example of it working on an example input word.\"\"\"\n\nevents = graph.stream(\n {\"messages\": [(\"user\", question)], \"iterations\": 0}, config, stream_mode=\"values\"\n)\nfor event in events:\n _print_event(event, _printed)"]
|
||||
"source": [
|
||||
"_printed = set()\n",
|
||||
"thread_id = str(uuid.uuid4())\n",
|
||||
"config = {\n",
|
||||
" \"configurable\": {\n",
|
||||
" # Checkpoints are accessed by thread_id\n",
|
||||
" \"thread_id\": thread_id,\n",
|
||||
" }\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"question = \"\"\"Create a Python program that checks if a given string is a palindrome. A palindrome is a word, phrase, number, or other sequence of characters that reads the same forward and backward (ignoring spaces, punctuation, and capitalization).\n",
|
||||
"\n",
|
||||
"Requirements:\n",
|
||||
"The program should define a function is_palindrome(s) that takes a string s as input.\n",
|
||||
"The function should return True if the string is a palindrome and False otherwise.\n",
|
||||
"Ignore spaces, punctuation, and case differences when checking for palindromes.\n",
|
||||
"\n",
|
||||
"Give an example of it working on an example input word.\"\"\"\n",
|
||||
"\n",
|
||||
"events = graph.stream(\n",
|
||||
" {\"messages\": [(\"user\", question)], \"iterations\": 0}, config, stream_mode=\"values\"\n",
|
||||
")\n",
|
||||
"for event in events:\n",
|
||||
" _print_event(event, _printed)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
@@ -217,7 +528,26 @@
|
||||
"id": "0a3f946b-e2f2-44d9-905b-09f36980cf9f",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": ["_printed = set()\nthread_id = str(uuid.uuid4())\nconfig = {\n \"configurable\": {\n # Checkpoints are accessed by thread_id\n \"thread_id\": thread_id,\n }\n}\n\nquestion = \"\"\"Write a program that prints the numbers from 1 to 100. \nBut for multiples of three, print \"Fizz\" instead of the number, and for the multiples of five, print \"Buzz\". \nFor numbers which are multiples of both three and five, print \"FizzBuzz\".\"\"\"\n\nevents = graph.stream(\n {\"messages\": [(\"user\", question)], \"iterations\": 0}, config, stream_mode=\"values\"\n)\nfor event in events:\n _print_event(event, _printed)"]
|
||||
"source": [
|
||||
"_printed = set()\n",
|
||||
"thread_id = str(uuid.uuid4())\n",
|
||||
"config = {\n",
|
||||
" \"configurable\": {\n",
|
||||
" # Checkpoints are accessed by thread_id\n",
|
||||
" \"thread_id\": thread_id,\n",
|
||||
" }\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"question = \"\"\"Write a program that prints the numbers from 1 to 100. \n",
|
||||
"But for multiples of three, print \"Fizz\" instead of the number, and for the multiples of five, print \"Buzz\". \n",
|
||||
"For numbers which are multiples of both three and five, print \"FizzBuzz\".\"\"\"\n",
|
||||
"\n",
|
||||
"events = graph.stream(\n",
|
||||
" {\"messages\": [(\"user\", question)], \"iterations\": 0}, config, stream_mode=\"values\"\n",
|
||||
")\n",
|
||||
"for event in events:\n",
|
||||
" _print_event(event, _printed)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
@@ -235,7 +565,37 @@
|
||||
"id": "2bb883df-540b-46ab-9415-fe27db68456f",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": ["import uuid\n\n_printed = set()\nthread_id = str(uuid.uuid4())\nconfig = {\n \"configurable\": {\n # Checkpoints are accessed by thread_id\n \"thread_id\": thread_id,\n }\n}\n\nquestion = \"\"\"I want to vectorize a function\n\n frame = np.zeros((out_h, out_w, 3), dtype=np.uint8)\n for i, val1 in enumerate(rows):\n for j, val2 in enumerate(cols):\n for j, val3 in enumerate(ch):\n # Assuming you want to store the pair as tuples in the matrix\n frame[i, j, k] = image[val1, val2, val3]\n\n out.write(np.array(frame))\n\nwith a simple numpy function that does something like this what is it called. Show me a test case with this working.\"\"\"\n\nevents = graph.stream(\n {\"messages\": [(\"user\", question)], \"iterations\": 0}, config, stream_mode=\"values\"\n)\nfor event in events:\n _print_event(event, _printed)"]
|
||||
"source": [
|
||||
"import uuid\n",
|
||||
"\n",
|
||||
"_printed = set()\n",
|
||||
"thread_id = str(uuid.uuid4())\n",
|
||||
"config = {\n",
|
||||
" \"configurable\": {\n",
|
||||
" # Checkpoints are accessed by thread_id\n",
|
||||
" \"thread_id\": thread_id,\n",
|
||||
" }\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"question = \"\"\"I want to vectorize a function\n",
|
||||
"\n",
|
||||
" frame = np.zeros((out_h, out_w, 3), dtype=np.uint8)\n",
|
||||
" for i, val1 in enumerate(rows):\n",
|
||||
" for j, val2 in enumerate(cols):\n",
|
||||
" for j, val3 in enumerate(ch):\n",
|
||||
" # Assuming you want to store the pair as tuples in the matrix\n",
|
||||
" frame[i, j, k] = image[val1, val2, val3]\n",
|
||||
"\n",
|
||||
" out.write(np.array(frame))\n",
|
||||
"\n",
|
||||
"with a simple numpy function that does something like this what is it called. Show me a test case with this working.\"\"\"\n",
|
||||
"\n",
|
||||
"events = graph.stream(\n",
|
||||
" {\"messages\": [(\"user\", question)], \"iterations\": 0}, config, stream_mode=\"values\"\n",
|
||||
")\n",
|
||||
"for event in events:\n",
|
||||
" _print_event(event, _printed)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
@@ -253,7 +613,34 @@
|
||||
"id": "ee05da1f-c272-405d-8a7b-552cfc3106e1",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": ["_printed = set()\nthread_id = str(uuid.uuid4())\nconfig = {\n \"configurable\": {\n # Checkpoints are accessed by thread_id\n \"thread_id\": thread_id,\n }\n}\n\nquestion = \"\"\"Create a Python program that allows two players to play a game of Tic-Tac-Toe. The game should be played on a 3x3 grid. The program should:\n\n- Allow players to take turns to input their moves.\n- Check for invalid moves (e.g., placing a marker on an already occupied space).\n- Determine and announce the winner or if the game ends in a draw.\n\nRequirements:\n- Use a 2D list to represent the Tic-Tac-Toe board.\n- Use functions to modularize the code.\n- Validate player input.\n- Check for win conditions and draw conditions after each move.\"\"\"\n\nevents = graph.stream(\n {\"messages\": [(\"user\", question)], \"iterations\": 0}, config, stream_mode=\"values\"\n)\nfor event in events:\n _print_event(event, _printed)"]
|
||||
"source": [
|
||||
"_printed = set()\n",
|
||||
"thread_id = str(uuid.uuid4())\n",
|
||||
"config = {\n",
|
||||
" \"configurable\": {\n",
|
||||
" # Checkpoints are accessed by thread_id\n",
|
||||
" \"thread_id\": thread_id,\n",
|
||||
" }\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"question = \"\"\"Create a Python program that allows two players to play a game of Tic-Tac-Toe. The game should be played on a 3x3 grid. The program should:\n",
|
||||
"\n",
|
||||
"- Allow players to take turns to input their moves.\n",
|
||||
"- Check for invalid moves (e.g., placing a marker on an already occupied space).\n",
|
||||
"- Determine and announce the winner or if the game ends in a draw.\n",
|
||||
"\n",
|
||||
"Requirements:\n",
|
||||
"- Use a 2D list to represent the Tic-Tac-Toe board.\n",
|
||||
"- Use functions to modularize the code.\n",
|
||||
"- Validate player input.\n",
|
||||
"- Check for win conditions and draw conditions after each move.\"\"\"\n",
|
||||
"\n",
|
||||
"events = graph.stream(\n",
|
||||
" {\"messages\": [(\"user\", question)], \"iterations\": 0}, config, stream_mode=\"values\"\n",
|
||||
")\n",
|
||||
"for event in events:\n",
|
||||
" _print_event(event, _printed)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
@@ -271,7 +658,7 @@
|
||||
"id": "814fc2a4-8e5b-4faa-8f52-3977226bd09a",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [""]
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "e9a58c69",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This file has been moved to https://github.com/langchain-ai/langgraph/blob/main/docs/docs/how-tos/configuration.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
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "a1e6efeb",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This file has been moved to https://github.com/langchain-ai/langgraph/blob/main/docs/docs/how-tos/create-react-agent-hitl.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
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "1ef41a89",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This file has been moved to https://github.com/langchain-ai/langgraph/blob/main/docs/docs/how-tos/create-react-agent-memory.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.1"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "9e2f7902",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This file has been moved to https://github.com/langchain-ai/langgraph/blob/main/docs/docs/how-tos/create-react-agent-system-prompt.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.1"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "eb07372e",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This file has been moved to https://github.com/langchain-ai/langgraph/blob/main/docs/docs/how-tos/create-react-agent.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.1"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
@@ -5,7 +5,15 @@
|
||||
"id": "a8232bc9",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This file has been moved to https://github.com/langchain-ai/langgraph/blob/main/docs/docs/tutorials/customer-support/customer-support.ipynb"
|
||||
"[This file has been moved](https://github.com/langchain-ai/langgraph/blob/23961cff61a42b52525f3b20b4094d8d2fba1744/docs/docs/tutorials/customer-support/customer-support.ipynb)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "63da8671",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This directory is retained purely for archival purposes and is no longer updated. The examples previously found here have been moved to the newly [consolidated LangChain documentation](https://docs.langchain.com/oss/python/langgraph/overview)."
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
@@ -5,7 +5,15 @@
|
||||
"id": "8dbdba5b",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This file has been moved to https://github.com/langchain-ai/langgraph/blob/main/docs/docs/tutorials/extraction/retries.ipynb"
|
||||
"[This file has been moved](https://github.com/langchain-ai/langgraph/blob/23961cff61a42b52525f3b20b4094d8d2fba1744/docs/docs/tutorials/extraction/retries.ipynb)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "1d444b7f",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This directory is retained purely for archival purposes and is no longer updated. The examples previously found here have been moved to the newly [consolidated LangChain documentation](https://docs.langchain.com/oss/python/langgraph/overview)."
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
@@ -5,7 +5,15 @@
|
||||
"id": "3ecab357",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This file has been moved to https://github.com/langchain-ai/langgraph/blob/main/docs/docs/how-tos/human_in_the_loop/wait-user-input.ipynb"
|
||||
"[This file has been moved](https://github.com/langchain-ai/langgraph/blob/23961cff61a42b52525f3b20b4094d8d2fba1744/docs/docs/how-tos/human_in_the_loop/wait-user-input.ipynb)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "3f2866bd",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This directory is retained purely for archival purposes and is no longer updated. The examples previously found here have been moved to the newly [consolidated LangChain documentation](https://docs.langchain.com/oss/python/langgraph/overview)."
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "fc0793cb",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This file has been moved to https://github.com/langchain-ai/langgraph/blob/main/docs/docs/how-tos/input_output_schema.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.1"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
@@ -5,7 +5,15 @@
|
||||
"id": "09038b53",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This file has been moved to https://github.com/langchain-ai/langgraph/blob/main/docs/docs/tutorials/lats/lats.ipynb"
|
||||
"[This file has been moved](https://github.com/langchain-ai/langgraph/blob/23961cff61a42b52525f3b20b4094d8d2fba1744/docs/docs/tutorials/lats/lats.ipynb)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "b1669748",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This directory is retained purely for archival purposes and is no longer updated. The examples previously found here have been moved to the newly [consolidated LangChain documentation](https://docs.langchain.com/oss/python/langgraph/overview)."
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
@@ -5,7 +5,15 @@
|
||||
"id": "85205e97",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This file has been moved to https://github.com/langchain-ai/langgraph/blob/main/docs/docs/tutorials/llm-compiler/LLMCompiler.ipynb"
|
||||
"[This file has been moved](https://github.com/langchain-ai/langgraph/blob/23961cff61a42b52525f3b20b4094d8d2fba1744/docs/docs/tutorials/llm-compiler/LLMCompiler.ipynb)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "2fdab366",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This directory is retained purely for archival purposes and is no longer updated. The examples previously found here have been moved to the newly [consolidated LangChain documentation](https://docs.langchain.com/oss/python/langgraph/overview)."
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "42abb708",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This file has been moved to https://github.com/langchain-ai/langgraph/blob/main/docs/docs/how-tos/map-reduce.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
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "298784f6",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This file has been moved to https://github.com/langchain-ai/langgraph/blob/main/docs/docs/how-tos/memory/add-summary-conversation-history.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.1"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "3f4370fd",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This file has been moved to https://github.com/langchain-ai/langgraph/blob/main/docs/docs/how-tos/memory/delete-messages.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
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "6ec7cb13",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This file has been moved to https://github.com/langchain-ai/langgraph/blob/main/docs/docs/how-tos/memory/manage-conversation-history.ipynb"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"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
|
||||
}
|
||||
@@ -5,7 +5,15 @@
|
||||
"id": "5cc8a2ad",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This file has been moved to https://github.com/langchain-ai/langgraph/blob/main/docs/docs/tutorials/multi_agent/hierarchical_agent_teams.ipynb"
|
||||
"[This file has been moved](https://github.com/langchain-ai/langgraph/blob/23961cff61a42b52525f3b20b4094d8d2fba1744/docs/docs/tutorials/multi_agent/hierarchical_agent_teams.ipynb)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "b9f3508a",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This directory is retained purely for archival purposes and is no longer updated. The examples previously found here have been moved to the newly [consolidated LangChain documentation](https://docs.langchain.com/oss/python/langgraph/overview)."
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
@@ -5,7 +5,15 @@
|
||||
"id": "d2b507b9",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This file has been moved to https://github.com/langchain-ai/langgraph/blob/main/docs/docs/tutorials/multi_agent/multi-agent-collaboration.ipynb"
|
||||
"[This file has been moved](https://github.com/langchain-ai/langgraph/blob/23961cff61a42b52525f3b20b4094d8d2fba1744/docs/docs/tutorials/multi_agent/multi-agent-collaboration.ipynb)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "41a8f10a",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This directory is retained purely for archival purposes and is no longer updated. The examples previously found here have been moved to the newly [consolidated LangChain documentation](https://docs.langchain.com/oss/python/langgraph/overview)."
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "017a01f4",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This file has been moved to https://github.com/langchain-ai/langgraph/blob/main/docs/docs/how-tos/node-retries.ipynb"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "env",
|
||||
"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": 2
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "05f6ad0a",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This file has been moved to https://github.com/langchain-ai/langgraph/blob/main/docs/docs/how-tos/pass-config-to-tools.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": 4
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "8f38bec5",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This file has been moved to https://github.com/langchain-ai/langgraph/blob/main/docs/docs/how-tos/pass-run-time-values-to-tools.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
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "4da17088",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This file has been moved to https://github.com/langchain-ai/langgraph/blob/main/docs/docs/how-tos/pass_private_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.1"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "d16e8b9c",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This file has been moved to https://github.com/langchain-ai/langgraph/blob/main/docs/docs/how-tos/memory/add-memory.md."
|
||||
]
|
||||
}
|
||||
],
|
||||
"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.12.2"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "78217098",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This file has been moved to https://github.com/langchain-ai/langgraph/blob/main/docs/docs/how-tos/persistence_mongodb.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
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "18526f23",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This file has been moved to https://github.com/langchain-ai/langgraph/blob/main/docs/docs/how-tos/memory/add-memory.md"
|
||||
]
|
||||
}
|
||||
],
|
||||
"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
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "eee6ecdd",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This file has been moved to https://github.com/langchain-ai/langgraph/blob/main/docs/docs/how-tos/persistence_redis.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
|
||||
}
|
||||
@@ -5,7 +5,15 @@
|
||||
"id": "9138f92e",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This file has been moved to https://github.com/langchain-ai/langgraph/blob/main/docs/docs/tutorials/plan-and-execute/plan-and-execute.ipynb"
|
||||
"[This file has been moved](https://github.com/langchain-ai/langgraph/blob/23961cff61a42b52525f3b20b4094d8d2fba1744/docs/docs/tutorials/plan-and-execute/plan-and-execute.ipynb)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "093678ba",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This directory is retained purely for archival purposes and is no longer updated. The examples previously found here have been moved to the newly [consolidated LangChain documentation](https://docs.langchain.com/oss/python/langgraph/overview)."
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "fedd6d23",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This directory is retained purely for archival purposes and is no longer updated. Please see the newly [consolidated LangChain documentation](https://docs.langchain.com/oss/python/langgraph/overview) for the most current information and resources."
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {
|
||||
"36fa621a-9d3d-4860-a17c-5d20e6987481.png": {
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,5 +1,13 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "39b26b09",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This directory is retained purely for archival purposes and is no longer updated. Please see the newly [consolidated LangChain documentation](https://docs.langchain.com/oss/python/langgraph/overview) for the most current information and resources."
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {
|
||||
"3755396d-c4a8-45bd-87d4-00cb56339fe5.png": {
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "47e3b43b",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This directory is retained purely for archival purposes and is no longer updated. Please see the newly [consolidated LangChain documentation](https://docs.langchain.com/oss/python/langgraph/overview) for the most current information and resources."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "425fb020-e864-40ce-a31f-8da40c73d14b",
|
||||
@@ -200,11 +208,11 @@
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"********************Prompt[rlm/rag-prompt]********************\n",
|
||||
"================================\u001B[1m Human Message \u001B[0m=================================\n",
|
||||
"================================\u001b[1m Human Message \u001b[0m=================================\n",
|
||||
"\n",
|
||||
"You are an assistant for question-answering tasks. Use the following pieces of retrieved context to answer the question. If you don't know the answer, just say that you don't know. Use three sentences maximum and keep the answer concise.\n",
|
||||
"Question: \u001B[33;1m\u001B[1;3m{question}\u001B[0m \n",
|
||||
"Context: \u001B[33;1m\u001B[1;3m{context}\u001B[0m \n",
|
||||
"Question: \u001b[33;1m\u001b[1;3m{question}\u001b[0m \n",
|
||||
"Context: \u001b[33;1m\u001b[1;3m{context}\u001b[0m \n",
|
||||
"Answer:\n"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "c71da2ea",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This directory is retained purely for archival purposes and is no longer updated. Please see the newly [consolidated LangChain documentation](https://docs.langchain.com/oss/python/langgraph/overview) for the most current information and resources."
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {
|
||||
"683fae34-980f-43f0-a9c2-9894bebd9157.png": {
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "ac7db067",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This directory is retained purely for archival purposes and is no longer updated. Please see the newly [consolidated LangChain documentation](https://docs.langchain.com/oss/python/langgraph/overview) for the most current information and resources."
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {
|
||||
"b77a7d3b-b28a-4dcf-9f1a-861f2f2c5f6c.png": {
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "b3d959ff",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This directory is retained purely for archival purposes and is no longer updated. Please see the newly [consolidated LangChain documentation](https://docs.langchain.com/oss/python/langgraph/overview) for the most current information and resources."
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {
|
||||
"15cba0ab-a549-4909-8373-fb761e384eff.png": {
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "345488d8",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This directory is retained purely for archival purposes and is no longer updated. Please see the newly [consolidated LangChain documentation](https://docs.langchain.com/oss/python/langgraph/overview) for the most current information and resources."
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {
|
||||
"5fca0a3e-d13d-4bfa-95ea-58203640cc7a.png": {
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "403aeb6e",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This directory is retained purely for archival purposes and is no longer updated. Please see the newly [consolidated LangChain documentation](https://docs.langchain.com/oss/python/langgraph/overview) for the most current information and resources."
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {
|
||||
"15cba0ab-a549-4909-8373-fb761e384eff.png": {
|
||||
@@ -54,7 +62,11 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import os\n\nos.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\nos.environ[\"LANGCHAIN_ENDPOINT\"] = \"https://api.smith.langchain.com\"\nos.environ[\"LANGCHAIN_API_KEY\"] = \"<your-api-key>\""
|
||||
"import os\n",
|
||||
"\n",
|
||||
"os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n",
|
||||
"os.environ[\"LANGCHAIN_ENDPOINT\"] = \"https://api.smith.langchain.com\"\n",
|
||||
"os.environ[\"LANGCHAIN_API_KEY\"] = \"<your-api-key>\""
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -64,7 +76,9 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import os\n\nos.environ[\"LANGCHAIN_PROJECT\"] = \"pinecone-devconnect\""
|
||||
"import os\n",
|
||||
"\n",
|
||||
"os.environ[\"LANGCHAIN_PROJECT\"] = \"pinecone-devconnect\""
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -84,7 +98,18 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_openai import OpenAIEmbeddings\nfrom langchain_pinecone import PineconeVectorStore\n\n# use pinecone movies database\n\n# Add to vectorDB\nvectorstore = PineconeVectorStore(\n embedding=OpenAIEmbeddings(),\n index_name=\"sample-movies\",\n text_key=\"summary\",\n)\nretriever = vectorstore.as_retriever()"
|
||||
"from langchain_openai import OpenAIEmbeddings\n",
|
||||
"from langchain_pinecone import PineconeVectorStore\n",
|
||||
"\n",
|
||||
"# use pinecone movies database\n",
|
||||
"\n",
|
||||
"# Add to vectorDB\n",
|
||||
"vectorstore = PineconeVectorStore(\n",
|
||||
" embedding=OpenAIEmbeddings(),\n",
|
||||
" index_name=\"sample-movies\",\n",
|
||||
" text_key=\"summary\",\n",
|
||||
")\n",
|
||||
"retriever = vectorstore.as_retriever()"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -113,7 +138,11 @@
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"docs = retriever.invoke(\"James Cameron\")\nfor doc in docs:\n print(\"# \" + doc.metadata[\"title\"])\n print(doc.page_content)\n print()"
|
||||
"docs = retriever.invoke(\"James Cameron\")\n",
|
||||
"for doc in docs:\n",
|
||||
" print(\"# \" + doc.metadata[\"title\"])\n",
|
||||
" print(doc.page_content)\n",
|
||||
" print()"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -173,7 +202,12 @@
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Test the retrieval grader\nquestion = \"movies starring jason momoa\"\ndocs = retriever.invoke(question)\ndoc_txt = docs[0].page_content\nprint(doc_txt)\nprint(retrieval_grader.invoke({\"question\": question, \"document\": doc_txt}))"
|
||||
"# Test the retrieval grader\n",
|
||||
"question = \"movies starring jason momoa\"\n",
|
||||
"docs = retriever.invoke(question)\n",
|
||||
"doc_txt = docs[0].page_content\n",
|
||||
"print(doc_txt)\n",
|
||||
"print(retrieval_grader.invoke({\"question\": question, \"document\": doc_txt}))"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -201,7 +235,23 @@
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"### Generate\n\nfrom langchain import hub\nfrom langchain_core.output_parsers import StrOutputParser\n\n# Prompt\nprompt = hub.pull(\"rlm/rag-prompt\")\n\n# LLM\nllm = ChatOpenAI(model_name=\"gpt-3.5-turbo\", temperature=0)\n\n# Chain\nrag_chain = prompt | llm | StrOutputParser()\n\n# Run\ngeneration = rag_chain.invoke({\"context\": docs, \"question\": question})\nprint(generation)"
|
||||
"### Generate\n",
|
||||
"\n",
|
||||
"from langchain import hub\n",
|
||||
"from langchain_core.output_parsers import StrOutputParser\n",
|
||||
"\n",
|
||||
"# Prompt\n",
|
||||
"prompt = hub.pull(\"rlm/rag-prompt\")\n",
|
||||
"\n",
|
||||
"# LLM\n",
|
||||
"llm = ChatOpenAI(model_name=\"gpt-3.5-turbo\", temperature=0)\n",
|
||||
"\n",
|
||||
"# Chain\n",
|
||||
"rag_chain = prompt | llm | StrOutputParser()\n",
|
||||
"\n",
|
||||
"# Run\n",
|
||||
"generation = rag_chain.invoke({\"context\": docs, \"question\": question})\n",
|
||||
"print(generation)"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -329,7 +379,17 @@
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"### Question Re-writer\n\n# LLM\nllm = ChatOpenAI(model=\"gpt-3.5-turbo-0125\", temperature=0)\n\n# Prompt\nre_write_prompt = hub.pull(\"efriis/self-rag-question-rewriter\")\n\nquestion_rewriter = re_write_prompt | llm | StrOutputParser()\nprint(question)\nquestion_rewriter.invoke({\"question\": question})"
|
||||
"### Question Re-writer\n",
|
||||
"\n",
|
||||
"# LLM\n",
|
||||
"llm = ChatOpenAI(model=\"gpt-3.5-turbo-0125\", temperature=0)\n",
|
||||
"\n",
|
||||
"# Prompt\n",
|
||||
"re_write_prompt = hub.pull(\"efriis/self-rag-question-rewriter\")\n",
|
||||
"\n",
|
||||
"question_rewriter = re_write_prompt | llm | StrOutputParser()\n",
|
||||
"print(question)\n",
|
||||
"question_rewriter.invoke({\"question\": question})"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -351,7 +411,24 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from typing import List\n\nfrom typing_extensions import TypedDict\n\n\nclass GraphState(TypedDict):\n \"\"\"\n Represents the state of our graph.\n\n Attributes:\n question: question\n generation: LLM generation\n documents: list of documents\n \"\"\"\n\n question: str\n generation: str\n documents: List[str]"
|
||||
"from typing import List\n",
|
||||
"\n",
|
||||
"from typing_extensions import TypedDict\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class GraphState(TypedDict):\n",
|
||||
" \"\"\"\n",
|
||||
" Represents the state of our graph.\n",
|
||||
"\n",
|
||||
" Attributes:\n",
|
||||
" question: question\n",
|
||||
" generation: LLM generation\n",
|
||||
" documents: list of documents\n",
|
||||
" \"\"\"\n",
|
||||
"\n",
|
||||
" question: str\n",
|
||||
" generation: str\n",
|
||||
" documents: List[str]"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -361,7 +438,95 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"### Nodes\n\n\ndef retrieve(state):\n \"\"\"\n Retrieve documents\n\n Args:\n state (dict): The current graph state\n\n Returns:\n state (dict): New key added to state, documents, that contains retrieved documents\n \"\"\"\n print(\"---RETRIEVE---\")\n question = state[\"question\"]\n\n # Retrieval\n documents = retriever.invoke(question)\n return {\"documents\": documents, \"question\": question}\n\n\ndef generate(state):\n \"\"\"\n Generate answer\n\n Args:\n state (dict): The current graph state\n\n Returns:\n state (dict): New key added to state, generation, that contains LLM generation\n \"\"\"\n print(\"---GENERATE---\")\n question = state[\"question\"]\n documents = state[\"documents\"]\n\n # RAG generation\n generation = rag_chain.invoke({\"context\": documents, \"question\": question})\n return {\"documents\": documents, \"question\": question, \"generation\": generation}\n\n\ndef grade_documents(state):\n \"\"\"\n Determines whether the retrieved documents are relevant to the question.\n\n Args:\n state (dict): The current graph state\n\n Returns:\n state (dict): Updates documents key with only filtered relevant documents\n \"\"\"\n\n print(\"---CHECK DOCUMENT RELEVANCE TO QUESTION---\")\n question = state[\"question\"]\n documents = state[\"documents\"]\n\n # Score each doc\n filtered_docs = []\n for d in documents:\n score = retrieval_grader.invoke(\n {\"question\": question, \"document\": d.page_content}\n )\n grade = score.binary_score\n if grade == \"yes\":\n print(\"---GRADE: DOCUMENT RELEVANT---\")\n filtered_docs.append(d)\n else:\n print(\"---GRADE: DOCUMENT NOT RELEVANT---\")\n continue\n return {\"documents\": filtered_docs, \"question\": question}\n\n\ndef transform_query(state):\n \"\"\"\n Transform the query to produce a better question.\n\n Args:\n state (dict): The current graph state\n\n Returns:\n state (dict): Updates question key with a re-phrased question\n \"\"\"\n\n print(\"---TRANSFORM QUERY---\")\n question = state[\"question\"]\n documents = state[\"documents\"]\n\n # Re-write question\n better_question = question_rewriter.invoke({\"question\": question})\n return {\"documents\": documents, \"question\": better_question}"
|
||||
"### Nodes\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def retrieve(state):\n",
|
||||
" \"\"\"\n",
|
||||
" Retrieve documents\n",
|
||||
"\n",
|
||||
" Args:\n",
|
||||
" state (dict): The current graph state\n",
|
||||
"\n",
|
||||
" Returns:\n",
|
||||
" state (dict): New key added to state, documents, that contains retrieved documents\n",
|
||||
" \"\"\"\n",
|
||||
" print(\"---RETRIEVE---\")\n",
|
||||
" question = state[\"question\"]\n",
|
||||
"\n",
|
||||
" # Retrieval\n",
|
||||
" documents = retriever.invoke(question)\n",
|
||||
" return {\"documents\": documents, \"question\": question}\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def generate(state):\n",
|
||||
" \"\"\"\n",
|
||||
" Generate answer\n",
|
||||
"\n",
|
||||
" Args:\n",
|
||||
" state (dict): The current graph state\n",
|
||||
"\n",
|
||||
" Returns:\n",
|
||||
" state (dict): New key added to state, generation, that contains LLM generation\n",
|
||||
" \"\"\"\n",
|
||||
" print(\"---GENERATE---\")\n",
|
||||
" question = state[\"question\"]\n",
|
||||
" documents = state[\"documents\"]\n",
|
||||
"\n",
|
||||
" # RAG generation\n",
|
||||
" generation = rag_chain.invoke({\"context\": documents, \"question\": question})\n",
|
||||
" return {\"documents\": documents, \"question\": question, \"generation\": generation}\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def grade_documents(state):\n",
|
||||
" \"\"\"\n",
|
||||
" Determines whether the retrieved documents are relevant to the question.\n",
|
||||
"\n",
|
||||
" Args:\n",
|
||||
" state (dict): The current graph state\n",
|
||||
"\n",
|
||||
" Returns:\n",
|
||||
" state (dict): Updates documents key with only filtered relevant documents\n",
|
||||
" \"\"\"\n",
|
||||
"\n",
|
||||
" print(\"---CHECK DOCUMENT RELEVANCE TO QUESTION---\")\n",
|
||||
" question = state[\"question\"]\n",
|
||||
" documents = state[\"documents\"]\n",
|
||||
"\n",
|
||||
" # Score each doc\n",
|
||||
" filtered_docs = []\n",
|
||||
" for d in documents:\n",
|
||||
" score = retrieval_grader.invoke(\n",
|
||||
" {\"question\": question, \"document\": d.page_content}\n",
|
||||
" )\n",
|
||||
" grade = score.binary_score\n",
|
||||
" if grade == \"yes\":\n",
|
||||
" print(\"---GRADE: DOCUMENT RELEVANT---\")\n",
|
||||
" filtered_docs.append(d)\n",
|
||||
" else:\n",
|
||||
" print(\"---GRADE: DOCUMENT NOT RELEVANT---\")\n",
|
||||
" continue\n",
|
||||
" return {\"documents\": filtered_docs, \"question\": question}\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def transform_query(state):\n",
|
||||
" \"\"\"\n",
|
||||
" Transform the query to produce a better question.\n",
|
||||
"\n",
|
||||
" Args:\n",
|
||||
" state (dict): The current graph state\n",
|
||||
"\n",
|
||||
" Returns:\n",
|
||||
" state (dict): Updates question key with a re-phrased question\n",
|
||||
" \"\"\"\n",
|
||||
"\n",
|
||||
" print(\"---TRANSFORM QUERY---\")\n",
|
||||
" question = state[\"question\"]\n",
|
||||
" documents = state[\"documents\"]\n",
|
||||
"\n",
|
||||
" # Re-write question\n",
|
||||
" better_question = question_rewriter.invoke({\"question\": question})\n",
|
||||
" return {\"documents\": documents, \"question\": better_question}"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -371,7 +536,74 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"### Edges\n\n\ndef decide_to_generate(state):\n \"\"\"\n Determines whether to generate an answer, or re-generate a question.\n\n Args:\n state (dict): The current graph state\n\n Returns:\n str: Binary decision for next node to call\n \"\"\"\n\n print(\"---ASSESS GRADED DOCUMENTS---\")\n state[\"question\"]\n filtered_documents = state[\"documents\"]\n\n if not filtered_documents:\n # All documents have been filtered check_relevance\n # We will re-generate a new query\n print(\n \"---DECISION: ALL DOCUMENTS ARE NOT RELEVANT TO QUESTION, TRANSFORM QUERY---\"\n )\n return \"transform_query\"\n else:\n # We have relevant documents, so generate answer\n print(\"---DECISION: GENERATE---\")\n return \"generate\"\n\n\ndef grade_generation_v_documents_and_question(state):\n \"\"\"\n Determines whether the generation is grounded in the document and answers question.\n\n Args:\n state (dict): The current graph state\n\n Returns:\n str: Decision for next node to call\n \"\"\"\n\n print(\"---CHECK HALLUCINATIONS---\")\n question = state[\"question\"]\n documents = state[\"documents\"]\n generation = state[\"generation\"]\n\n score = hallucination_grader.invoke(\n {\"documents\": documents, \"generation\": generation}\n )\n grade = score.binary_score\n\n # Check hallucination\n if grade == \"yes\":\n print(\"---DECISION: GENERATION IS GROUNDED IN DOCUMENTS---\")\n # Check question-answering\n print(\"---GRADE GENERATION vs QUESTION---\")\n score = answer_grader.invoke({\"question\": question, \"generation\": generation})\n grade = score.binary_score\n if grade == \"yes\":\n print(\"---DECISION: GENERATION ADDRESSES QUESTION---\")\n return \"useful\"\n else:\n print(\"---DECISION: GENERATION DOES NOT ADDRESS QUESTION---\")\n return \"not useful\"\n else:\n pprint(\"---DECISION: GENERATION IS NOT GROUNDED IN DOCUMENTS, RE-TRY---\")\n return \"not supported\""
|
||||
"### Edges\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def decide_to_generate(state):\n",
|
||||
" \"\"\"\n",
|
||||
" Determines whether to generate an answer, or re-generate a question.\n",
|
||||
"\n",
|
||||
" Args:\n",
|
||||
" state (dict): The current graph state\n",
|
||||
"\n",
|
||||
" Returns:\n",
|
||||
" str: Binary decision for next node to call\n",
|
||||
" \"\"\"\n",
|
||||
"\n",
|
||||
" print(\"---ASSESS GRADED DOCUMENTS---\")\n",
|
||||
" state[\"question\"]\n",
|
||||
" filtered_documents = state[\"documents\"]\n",
|
||||
"\n",
|
||||
" if not filtered_documents:\n",
|
||||
" # All documents have been filtered check_relevance\n",
|
||||
" # We will re-generate a new query\n",
|
||||
" print(\n",
|
||||
" \"---DECISION: ALL DOCUMENTS ARE NOT RELEVANT TO QUESTION, TRANSFORM QUERY---\"\n",
|
||||
" )\n",
|
||||
" return \"transform_query\"\n",
|
||||
" else:\n",
|
||||
" # We have relevant documents, so generate answer\n",
|
||||
" print(\"---DECISION: GENERATE---\")\n",
|
||||
" return \"generate\"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def grade_generation_v_documents_and_question(state):\n",
|
||||
" \"\"\"\n",
|
||||
" Determines whether the generation is grounded in the document and answers question.\n",
|
||||
"\n",
|
||||
" Args:\n",
|
||||
" state (dict): The current graph state\n",
|
||||
"\n",
|
||||
" Returns:\n",
|
||||
" str: Decision for next node to call\n",
|
||||
" \"\"\"\n",
|
||||
"\n",
|
||||
" print(\"---CHECK HALLUCINATIONS---\")\n",
|
||||
" question = state[\"question\"]\n",
|
||||
" documents = state[\"documents\"]\n",
|
||||
" generation = state[\"generation\"]\n",
|
||||
"\n",
|
||||
" score = hallucination_grader.invoke(\n",
|
||||
" {\"documents\": documents, \"generation\": generation}\n",
|
||||
" )\n",
|
||||
" grade = score.binary_score\n",
|
||||
"\n",
|
||||
" # Check hallucination\n",
|
||||
" if grade == \"yes\":\n",
|
||||
" print(\"---DECISION: GENERATION IS GROUNDED IN DOCUMENTS---\")\n",
|
||||
" # Check question-answering\n",
|
||||
" print(\"---GRADE GENERATION vs QUESTION---\")\n",
|
||||
" score = answer_grader.invoke({\"question\": question, \"generation\": generation})\n",
|
||||
" grade = score.binary_score\n",
|
||||
" if grade == \"yes\":\n",
|
||||
" print(\"---DECISION: GENERATION ADDRESSES QUESTION---\")\n",
|
||||
" return \"useful\"\n",
|
||||
" else:\n",
|
||||
" print(\"---DECISION: GENERATION DOES NOT ADDRESS QUESTION---\")\n",
|
||||
" return \"not useful\"\n",
|
||||
" else:\n",
|
||||
" pprint(\"---DECISION: GENERATION IS NOT GROUNDED IN DOCUMENTS, RE-TRY---\")\n",
|
||||
" return \"not supported\""
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -390,7 +622,42 @@
|
||||
"id": "0e09ca9f-e36d-4ef4-a0d5-79fdbada9fe0",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": ["from langgraph.graph import END, StateGraph, START\n\nworkflow = StateGraph(GraphState)\n\n# Define the nodes\nworkflow.add_node(\"retrieve\", retrieve) # retrieve\nworkflow.add_node(\"grade_documents\", grade_documents) # grade documents\nworkflow.add_node(\"generate\", generate) # generate\nworkflow.add_node(\"transform_query\", transform_query) # transform_query\n\n# Build graph\nworkflow.add_edge(START, \"retrieve\")\nworkflow.add_edge(\"retrieve\", \"grade_documents\")\nworkflow.add_conditional_edges(\n \"grade_documents\",\n decide_to_generate,\n {\n \"transform_query\": \"transform_query\",\n \"generate\": \"generate\",\n },\n)\nworkflow.add_edge(\"transform_query\", \"retrieve\")\nworkflow.add_conditional_edges(\n \"generate\",\n grade_generation_v_documents_and_question,\n {\n \"not supported\": \"generate\",\n \"useful\": END,\n \"not useful\": \"transform_query\",\n },\n)\n\n# Compile\napp = workflow.compile()"]
|
||||
"source": [
|
||||
"from langgraph.graph import END, StateGraph, START\n",
|
||||
"\n",
|
||||
"workflow = StateGraph(GraphState)\n",
|
||||
"\n",
|
||||
"# Define the nodes\n",
|
||||
"workflow.add_node(\"retrieve\", retrieve) # retrieve\n",
|
||||
"workflow.add_node(\"grade_documents\", grade_documents) # grade documents\n",
|
||||
"workflow.add_node(\"generate\", generate) # generate\n",
|
||||
"workflow.add_node(\"transform_query\", transform_query) # transform_query\n",
|
||||
"\n",
|
||||
"# Build graph\n",
|
||||
"workflow.add_edge(START, \"retrieve\")\n",
|
||||
"workflow.add_edge(\"retrieve\", \"grade_documents\")\n",
|
||||
"workflow.add_conditional_edges(\n",
|
||||
" \"grade_documents\",\n",
|
||||
" decide_to_generate,\n",
|
||||
" {\n",
|
||||
" \"transform_query\": \"transform_query\",\n",
|
||||
" \"generate\": \"generate\",\n",
|
||||
" },\n",
|
||||
")\n",
|
||||
"workflow.add_edge(\"transform_query\", \"retrieve\")\n",
|
||||
"workflow.add_conditional_edges(\n",
|
||||
" \"generate\",\n",
|
||||
" grade_generation_v_documents_and_question,\n",
|
||||
" {\n",
|
||||
" \"not supported\": \"generate\",\n",
|
||||
" \"useful\": END,\n",
|
||||
" \"not useful\": \"transform_query\",\n",
|
||||
" },\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# Compile\n",
|
||||
"app = workflow.compile()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
@@ -426,7 +693,18 @@
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from pprint import pprint\n\n# Run\ninputs = {\"question\": \"Movies that star Daniel Craig\"}\nfor output in app.stream(inputs):\n for key, value in output.items():\n # Node\n pprint(f\"Node '{key}':\")\n pprint(\"\\n---\\n\")\n\n# Final generation\npprint(value[\"generation\"])"
|
||||
"from pprint import pprint\n",
|
||||
"\n",
|
||||
"# Run\n",
|
||||
"inputs = {\"question\": \"Movies that star Daniel Craig\"}\n",
|
||||
"for output in app.stream(inputs):\n",
|
||||
" for key, value in output.items():\n",
|
||||
" # Node\n",
|
||||
" pprint(f\"Node '{key}':\")\n",
|
||||
" pprint(\"\\n---\\n\")\n",
|
||||
"\n",
|
||||
"# Final generation\n",
|
||||
"pprint(value[\"generation\"])"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -436,7 +714,15 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"inputs = {\"question\": \"Which movies are about aliens?\"}\nfor output in app.stream(inputs):\n for key, value in output.items():\n # Node\n pprint(f\"Node '{key}':\")\n pprint(\"\\n---\\n\")\n\n# Final generation\npprint(value[\"generation\"])"
|
||||
"inputs = {\"question\": \"Which movies are about aliens?\"}\n",
|
||||
"for output in app.stream(inputs):\n",
|
||||
" for key, value in output.items():\n",
|
||||
" # Node\n",
|
||||
" pprint(f\"Node '{key}':\")\n",
|
||||
" pprint(\"\\n---\\n\")\n",
|
||||
"\n",
|
||||
"# Final generation\n",
|
||||
"pprint(value[\"generation\"])"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -445,9 +731,7 @@
|
||||
"id": "42369ab8-322d-434a-b5dd-2266e4cb2903",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
""
|
||||
]
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
|
||||
@@ -5,7 +5,14 @@
|
||||
"id": "294995c4",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This file has been moved to https://github.com/langchain-ai/langgraph/blob/main/docs/docs/how-tos/react-agent-from-scratch.ipynb"
|
||||
"[This file has been moved](https://github.com/langchain-ai/langgraph/blob/23961cff61a42b52525f3b20b4094d8d2fba1744/docs/docs/how-tos/react-agent-from-scratch.ipynb)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This directory is retained purely for archival purposes and is no longer updated. The examples previously found here have been moved to the newly [consolidated LangChain documentation](https://docs.langchain.com/oss/python/langgraph/overview)."
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
@@ -5,7 +5,14 @@
|
||||
"id": "40f0d107",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This file has been moved to https://github.com/langchain-ai/langgraph/blob/main/docs/docs/how-tos/react-agent-structured-output.ipynb"
|
||||
"[This file has been moved](https://github.com/langchain-ai/langgraph/blob/23961cff61a42b52525f3b20b4094d8d2fba1744/docs/docs/how-tos/react-agent-structured-output.ipynb)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This directory is retained purely for archival purposes and is no longer updated. The examples previously found here have been moved to the newly [consolidated LangChain documentation](https://docs.langchain.com/oss/python/langgraph/overview)."
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "fa3f7c50",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This file has been moved to https://github.com/langchain-ai/langgraph/blob/main/docs/docs/how-tos/recursion-limit.ipynb"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"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": 2
|
||||
}
|
||||
@@ -5,7 +5,15 @@
|
||||
"id": "658773a2",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This file has been moved to https://github.com/langchain-ai/langgraph/blob/main/docs/docs/tutorials/reflection/reflection.ipynb"
|
||||
"[This file has been moved](https://github.com/langchain-ai/langgraph/blob/23961cff61a42b52525f3b20b4094d8d2fba1744/docs/docs/tutorials/reflection/reflection.ipynb)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "1cb60657",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This directory is retained purely for archival purposes and is no longer updated. The examples previously found here have been moved to the newly [consolidated LangChain documentation](https://docs.langchain.com/oss/python/langgraph/overview)."
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
@@ -5,7 +5,15 @@
|
||||
"id": "caf07859",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This file has been moved to https://github.com/langchain-ai/langgraph/blob/main/docs/docs/tutorials/reflexion/reflexion.ipynb"
|
||||
"[This file has been moved](https://github.com/langchain-ai/langgraph/blob/23961cff61a42b52525f3b20b4094d8d2fba1744/docs/docs/tutorials/reflexion/reflexion.ipynb)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "cd1df0e0",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This directory is retained purely for archival purposes and is no longer updated. The examples previously found here have been moved to the newly [consolidated LangChain documentation](https://docs.langchain.com/oss/python/langgraph/overview)."
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
@@ -5,7 +5,15 @@
|
||||
"id": "961f43ec",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This file has been moved to https://github.com/langchain-ai/langgraph/blob/main/docs/docs/tutorials/rewoo/rewoo.ipynb"
|
||||
"[This file has been moved](https://github.com/langchain-ai/langgraph/blob/23961cff61a42b52525f3b20b4094d8d2fba1744/docs/docs/tutorials/rewoo/rewoo.ipynb)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "7f00c427",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This directory is retained purely for archival purposes and is no longer updated. The examples previously found here have been moved to the newly [consolidated LangChain documentation](https://docs.langchain.com/oss/python/langgraph/overview)."
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
@@ -5,7 +5,14 @@
|
||||
"id": "bbd6e9b8",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This file has been moved to https://github.com/langchain-ai/langgraph/blob/main/docs/docs/how-tos/run-id-langsmith.ipynb"
|
||||
"[This file has been moved](https://github.com/langchain-ai/langgraph/blob/23961cff61a42b52525f3b20b4094d8d2fba1744/docs/docs/how-tos/run-id-langsmith.md)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This directory is retained purely for archival purposes and is no longer updated. The examples previously found here have been moved to the newly [consolidated LangChain documentation](https://docs.langchain.com/oss/python/langgraph/overview)."
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
@@ -5,7 +5,15 @@
|
||||
"id": "f6db1873",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This file has been moved to https://github.com/langchain-ai/langgraph/blob/main/docs/docs/tutorials/self-discover/self-discover.ipynb"
|
||||
"[This file has been moved](https://github.com/langchain-ai/langgraph/blob/23961cff61a42b52525f3b20b4094d8d2fba1744/docs/docs/tutorials/self-discover/self-discover.ipynb)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "219a78f9",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This directory is retained purely for archival purposes and is no longer updated. The examples previously found here have been moved to the newly [consolidated LangChain documentation](https://docs.langchain.com/oss/python/langgraph/overview)."
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "4149ffcc",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This file has been moved to https://github.com/langchain-ai/langgraph/blob/main/docs/docs/how-tos/state-model.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
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "3e05d7f9",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This file has been moved to https://github.com/langchain-ai/langgraph/blob/main/docs/docs/tutorials/storm/storm.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.12.2"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 4
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "e663f597",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This file has been moved to https://github.com/langchain-ai/langgraph/blob/main/docs/docs/how-tos/stream-multiple.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
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "e6829c80",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This file has been moved to https://github.com/langchain-ai/langgraph/blob/main/docs/docs/how-tos/stream-updates.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
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "5ec11895",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This file has been moved to https://github.com/langchain-ai/langgraph/blob/main/docs/docs/how-tos/stream-values.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
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "6619387c",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This file has been moved to https://github.com/langchain-ai/langgraph/blob/main/docs/docs/how-tos/streaming-content.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
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "57b7e303",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This file has been moved to https://github.com/langchain-ai/langgraph/blob/main/docs/docs/how-tos/streaming-events-from-within-tools-without-langchain.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
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "8e71a0c8",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This file has been moved to https://github.com/langchain-ai/langgraph/blob/main/docs/docs/how-tos/streaming-events-from-within-tools.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
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "756e4554",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This file has been moved to https://github.com/langchain-ai/langgraph/blob/main/docs/docs/how-tos/streaming-from-final-node.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
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "47164a72",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This file has been moved to https://github.com/langchain-ai/langgraph/blob/main/docs/docs/how-tos/streaming-subgraphs.ipynb"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"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": 2
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "218dfbcb",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This file has been moved to https://github.com/langchain-ai/langgraph/blob/main/docs/docs/how-tos/streaming-tokens-without-langchain.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
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "99eb887e",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This file has been moved to https://github.com/langchain-ai/langgraph/blob/main/docs/docs/how-tos/streaming-tokens.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
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "0de7689f",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This file has been moved to https://github.com/langchain-ai/langgraph/blob/main/docs/docs/how-tos/subgraph-transform-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.9"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 4
|
||||
}
|
||||
@@ -5,7 +5,14 @@
|
||||
"id": "f49876e1",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This file has been moved to https://github.com/langchain-ai/langgraph/blob/main/docs/docs/how-tos/subgraph.ipynb"
|
||||
"[This file has been moved](https://github.com/langchain-ai/langgraph/blob/23961cff61a42b52525f3b20b4094d8d2fba1744/docs/docs/how-tos/subgraph.md)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This directory is retained purely for archival purposes and is no longer updated. The examples previously found here have been moved to the newly [consolidated LangChain documentation](https://docs.langchain.com/oss/python/langgraph/overview)."
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "5106959e",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This file has been moved to https://github.com/langchain-ai/langgraph/blob/main/docs/docs/how-tos/subgraphs-manage-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.9"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 4
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "dc21501d",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This file has been moved to https://github.com/langchain-ai/langgraph/blob/main/docs/docs/how-tos/tool-calling-errors.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": 4
|
||||
}
|
||||
@@ -5,7 +5,14 @@
|
||||
"id": "7fd8bd65",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This file has been moved to https://github.com/langchain-ai/langgraph/blob/main/docs/docs/how-tos/tool-calling.ipynb"
|
||||
"[This file has been moved](https://github.com/langchain-ai/langgraph/blob/23961cff61a42b52525f3b20b4094d8d2fba1744/docs/docs/how-tos/tool-calling.md)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This directory is retained purely for archival purposes and is no longer updated. The examples previously found here have been moved to the newly [consolidated LangChain documentation](https://docs.langchain.com/oss/python/langgraph/overview)."
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
@@ -5,7 +5,15 @@
|
||||
"id": "83c2223f",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This file has been moved to https://github.com/langchain-ai/langgraph/blob/main/docs/docs/tutorials/sql-agent.ipynb"
|
||||
"[This file has been moved](https://github.com/langchain-ai/langgraph/blob/23961cff61a42b52525f3b20b4094d8d2fba1744/docs/docs/tutorials/sql/sql-agent.md)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "57f924b1",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This directory is retained purely for archival purposes and is no longer updated. The examples previously found here have been moved to the newly [consolidated LangChain documentation](https://docs.langchain.com/oss/python/langgraph/overview)."
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
@@ -5,7 +5,15 @@
|
||||
"id": "11140167",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This file has been moved to https://github.com/langchain-ai/langgraph/blob/main/docs/docs/tutorials/tnt-llm/tnt-llm.ipynb"
|
||||
"[This file has been moved](https://github.com/langchain-ai/langgraph/blob/23961cff61a42b52525f3b20b4094d8d2fba1744/docs/docs/tutorials/tnt-llm/tnt-llm.ipynb)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "1a2ba3e6",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This directory is retained purely for archival purposes and is no longer updated. The examples previously found here have been moved to the newly [consolidated LangChain documentation](https://docs.langchain.com/oss/python/langgraph/overview)."
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
@@ -5,7 +5,15 @@
|
||||
"id": "9dffdb54",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This file has been moved to https://github.com/langchain-ai/langgraph/blob/main/docs/docs/tutorials/usaco/usaco.ipynb"
|
||||
"[This file has been moved](https://github.com/langchain-ai/langgraph/blob/23961cff61a42b52525f3b20b4094d8d2fba1744/docs/docs/tutorials/usaco/usaco.ipynb)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "579c9959",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This directory is retained purely for archival purposes and is no longer updated. The examples previously found here have been moved to the newly [consolidated LangChain documentation](https://docs.langchain.com/oss/python/langgraph/overview)."
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "9c9cb15a",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This file has been moved to https://github.com/langchain-ai/langgraph/blob/main/docs/docs/how-tos/visualization.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
|
||||
}
|
||||
@@ -5,7 +5,15 @@
|
||||
"id": "007ea2e9",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This file has been moved to https://github.com/langchain-ai/langgraph/blob/main/docs/docs/tutorials/web-navigation/web_voyager.ipynb"
|
||||
"[This file has been moved](https://github.com/langchain-ai/langgraph/blob/23961cff61a42b52525f3b20b4094d8d2fba1744/docs/docs/tutorials/web-navigation/web_voyager.ipynb)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "f0d7b895",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This directory is retained purely for archival purposes and is no longer updated. The examples previously found here have been moved to the newly [consolidated LangChain documentation](https://docs.langchain.com/oss/python/langgraph/overview)."
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
.PHONY: format lint test
|
||||
|
||||
format:
|
||||
uv run ruff format .
|
||||
uv run ruff check --fix .
|
||||
|
||||
lint:
|
||||
uv run ruff check .
|
||||
uv run ty check
|
||||
|
||||
test:
|
||||
uv run pytest $(TEST)
|
||||
@@ -0,0 +1,111 @@
|
||||
# langgraph-checkpoint-conformance
|
||||
|
||||
Conformance test suite for [LangGraph](https://github.com/langchain-ai/langgraph) checkpointer implementations.
|
||||
|
||||
Validates that a `BaseCheckpointSaver` subclass correctly implements the checkpoint storage contract — blob round-trips, metadata preservation, namespace isolation, incremental channel updates, and more.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
pip install langgraph-checkpoint-conformance
|
||||
```
|
||||
|
||||
## Quick start
|
||||
|
||||
Register your checkpointer with `@checkpointer_test` and run `validate()`:
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
from langgraph.checkpoint.conformance import checkpointer_test, validate
|
||||
|
||||
@checkpointer_test(name="MyCheckpointer")
|
||||
async def my_checkpointer():
|
||||
saver = MyCheckpointer(...)
|
||||
yield saver
|
||||
# cleanup runs after yield
|
||||
|
||||
async def main():
|
||||
report = await validate(my_checkpointer)
|
||||
report.print_report()
|
||||
assert report.passed_all_base()
|
||||
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
Or in a pytest test:
|
||||
|
||||
```python
|
||||
import pytest
|
||||
from langgraph.checkpoint.conformance import checkpointer_test, validate
|
||||
|
||||
@checkpointer_test(name="MyCheckpointer")
|
||||
async def my_checkpointer():
|
||||
yield MyCheckpointer(...)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_conformance():
|
||||
report = await validate(my_checkpointer)
|
||||
report.print_report()
|
||||
assert report.passed_all_base()
|
||||
```
|
||||
|
||||
## Capabilities
|
||||
|
||||
The suite tests **base** capabilities (required) and **extended** capabilities (optional, auto-detected):
|
||||
|
||||
| Capability | Required | Method |
|
||||
|---|---|---|
|
||||
| `put` | yes | `aput` |
|
||||
| `put_writes` | yes | `aput_writes` |
|
||||
| `get_tuple` | yes | `aget_tuple` |
|
||||
| `list` | yes | `alist` |
|
||||
| `delete_thread` | yes | `adelete_thread` |
|
||||
| `delete_for_runs` | no | `adelete_for_runs` |
|
||||
| `copy_thread` | no | `acopy_thread` |
|
||||
| `prune` | no | `aprune` |
|
||||
|
||||
Extended capabilities are detected by checking whether the method is overridden from `BaseCheckpointSaver`. If not overridden, those tests are skipped.
|
||||
|
||||
## Options
|
||||
|
||||
### Progress output
|
||||
|
||||
```python
|
||||
from langgraph.checkpoint.conformance.report import ProgressCallbacks
|
||||
|
||||
# Dot-style progress (. per pass, F per fail)
|
||||
report = await validate(my_checkpointer, progress=ProgressCallbacks.default())
|
||||
|
||||
# Verbose (per-test names + stacktraces on failure)
|
||||
report = await validate(my_checkpointer, progress=ProgressCallbacks.verbose())
|
||||
```
|
||||
|
||||
### Skip capabilities
|
||||
|
||||
```python
|
||||
@checkpointer_test(name="MyCheckpointer", skip_capabilities={"prune"})
|
||||
async def my_checkpointer():
|
||||
yield MyCheckpointer(...)
|
||||
```
|
||||
|
||||
### Run specific capabilities
|
||||
|
||||
```python
|
||||
report = await validate(my_checkpointer, capabilities={"put", "list"})
|
||||
```
|
||||
|
||||
### Lifespan (one-time setup/teardown)
|
||||
|
||||
For expensive setup like database creation:
|
||||
|
||||
```python
|
||||
async def db_lifespan():
|
||||
await create_database()
|
||||
yield
|
||||
await drop_database()
|
||||
|
||||
@checkpointer_test(name="PostgresSaver", lifespan=db_lifespan)
|
||||
async def pg_checkpointer():
|
||||
async with PostgresSaver.from_conn_string(CONN_STRING) as saver:
|
||||
yield saver
|
||||
```
|
||||
@@ -0,0 +1,9 @@
|
||||
"""langgraph-checkpoint-conformance: conformance test suite for checkpointer implementations."""
|
||||
|
||||
from langgraph.checkpoint.conformance.initializer import checkpointer_test
|
||||
from langgraph.checkpoint.conformance.validate import validate
|
||||
|
||||
__all__ = [
|
||||
"checkpointer_test",
|
||||
"validate",
|
||||
]
|
||||
@@ -0,0 +1,93 @@
|
||||
"""Capability detection for checkpointer implementations."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from enum import Enum
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from langgraph.checkpoint.base import BaseCheckpointSaver
|
||||
|
||||
if TYPE_CHECKING:
|
||||
pass
|
||||
|
||||
|
||||
class Capability(str, Enum):
|
||||
"""Capabilities that a checkpointer may support."""
|
||||
|
||||
PUT = "put"
|
||||
PUT_WRITES = "put_writes"
|
||||
GET_TUPLE = "get_tuple"
|
||||
LIST = "list"
|
||||
DELETE_THREAD = "delete_thread"
|
||||
DELETE_FOR_RUNS = "delete_for_runs"
|
||||
COPY_THREAD = "copy_thread"
|
||||
PRUNE = "prune"
|
||||
|
||||
|
||||
# Capabilities that every checkpointer must support.
|
||||
BASE_CAPABILITIES = frozenset(
|
||||
{
|
||||
Capability.PUT,
|
||||
Capability.PUT_WRITES,
|
||||
Capability.GET_TUPLE,
|
||||
Capability.LIST,
|
||||
Capability.DELETE_THREAD,
|
||||
}
|
||||
)
|
||||
|
||||
# Capabilities that are optional extensions.
|
||||
EXTENDED_CAPABILITIES = frozenset(
|
||||
{
|
||||
Capability.DELETE_FOR_RUNS,
|
||||
Capability.COPY_THREAD,
|
||||
Capability.PRUNE,
|
||||
}
|
||||
)
|
||||
|
||||
ALL_CAPABILITIES = BASE_CAPABILITIES | EXTENDED_CAPABILITIES
|
||||
|
||||
# Maps capability to the async method name on BaseCheckpointSaver (or subclass).
|
||||
_CAPABILITY_METHOD_MAP: dict[Capability, str] = {
|
||||
Capability.PUT: "aput",
|
||||
Capability.PUT_WRITES: "aput_writes",
|
||||
Capability.GET_TUPLE: "aget_tuple",
|
||||
Capability.LIST: "alist",
|
||||
Capability.DELETE_THREAD: "adelete_thread",
|
||||
Capability.DELETE_FOR_RUNS: "adelete_for_runs",
|
||||
Capability.COPY_THREAD: "acopy_thread",
|
||||
Capability.PRUNE: "aprune",
|
||||
}
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class DetectedCapabilities:
|
||||
"""Result of capability detection for a checkpointer type."""
|
||||
|
||||
detected: frozenset[Capability]
|
||||
missing: frozenset[Capability]
|
||||
|
||||
@classmethod
|
||||
def from_instance(cls, saver: BaseCheckpointSaver) -> DetectedCapabilities:
|
||||
"""Detect capabilities from a checkpointer instance."""
|
||||
inner_type = type(saver)
|
||||
detected: set[Capability] = set()
|
||||
|
||||
for cap, method_name in _CAPABILITY_METHOD_MAP.items():
|
||||
if _is_overridden(inner_type, method_name):
|
||||
detected.add(cap)
|
||||
|
||||
detected_fs = frozenset(detected)
|
||||
return cls(
|
||||
detected=detected_fs,
|
||||
missing=ALL_CAPABILITIES - detected_fs,
|
||||
)
|
||||
|
||||
|
||||
def _is_overridden(inner_type: type, method: str) -> bool:
|
||||
"""Check if *method* on *inner_type* differs from the base class default."""
|
||||
base = getattr(BaseCheckpointSaver, method, None)
|
||||
impl = getattr(inner_type, method, None)
|
||||
if base is None or impl is None:
|
||||
return impl is not None
|
||||
return impl is not base
|
||||
@@ -0,0 +1,100 @@
|
||||
"""Checkpointer test registration and factory management."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import AsyncGenerator, Callable
|
||||
from contextlib import asynccontextmanager
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Any
|
||||
|
||||
from langgraph.checkpoint.base import BaseCheckpointSaver
|
||||
|
||||
# Type for the lifespan async context manager factory.
|
||||
LifespanFactory = Callable[[], AsyncGenerator[None, None]]
|
||||
|
||||
# Module-level registry of decorated checkpointer factories.
|
||||
_REGISTRY: dict[str, RegisteredCheckpointer] = {}
|
||||
|
||||
|
||||
async def _noop_lifespan() -> AsyncGenerator[None, None]:
|
||||
yield
|
||||
|
||||
|
||||
@dataclass
|
||||
class RegisteredCheckpointer:
|
||||
"""A registered checkpointer test factory."""
|
||||
|
||||
name: str
|
||||
factory: Callable[[], AsyncGenerator[BaseCheckpointSaver, None]]
|
||||
skip_capabilities: set[str] = field(default_factory=set)
|
||||
lifespan: LifespanFactory = _noop_lifespan
|
||||
|
||||
@asynccontextmanager
|
||||
async def create(self) -> AsyncGenerator[BaseCheckpointSaver, None]:
|
||||
"""Create a fresh checkpointer instance via the async generator."""
|
||||
gen = self.factory()
|
||||
try:
|
||||
saver = await gen.__anext__()
|
||||
yield saver
|
||||
finally:
|
||||
try:
|
||||
await gen.__anext__()
|
||||
except StopAsyncIteration:
|
||||
pass
|
||||
|
||||
@asynccontextmanager
|
||||
async def enter_lifespan(self) -> AsyncGenerator[None, None]:
|
||||
"""Enter the lifespan context (once per validation run)."""
|
||||
gen = self.lifespan()
|
||||
try:
|
||||
await gen.__anext__()
|
||||
yield
|
||||
finally:
|
||||
try:
|
||||
await gen.__anext__()
|
||||
except StopAsyncIteration:
|
||||
pass
|
||||
|
||||
|
||||
def checkpointer_test(
|
||||
name: str,
|
||||
*,
|
||||
skip_capabilities: set[str] | None = None,
|
||||
lifespan: LifespanFactory | None = None,
|
||||
) -> Callable[[Any], RegisteredCheckpointer]:
|
||||
"""Register an async generator as a checkpointer test factory.
|
||||
|
||||
The factory is called once per capability suite to create a fresh
|
||||
checkpointer. The optional `lifespan` is an async generator that
|
||||
runs once for the entire validation run (e.g. to create/destroy a
|
||||
database).
|
||||
|
||||
Example::
|
||||
|
||||
@checkpointer_test(name="InMemorySaver")
|
||||
async def memory_checkpointer():
|
||||
yield InMemorySaver()
|
||||
|
||||
With lifespan::
|
||||
|
||||
async def pg_lifespan():
|
||||
await create_database()
|
||||
yield
|
||||
await drop_database()
|
||||
|
||||
@checkpointer_test(name="PostgresSaver", lifespan=pg_lifespan)
|
||||
async def pg_checkpointer():
|
||||
yield PostgresSaver(conn_string="...")
|
||||
"""
|
||||
|
||||
def decorator(fn: Any) -> RegisteredCheckpointer:
|
||||
registered = RegisteredCheckpointer(
|
||||
name=name,
|
||||
factory=fn,
|
||||
skip_capabilities=skip_capabilities or set(),
|
||||
lifespan=lifespan or _noop_lifespan,
|
||||
)
|
||||
_REGISTRY[name] = registered
|
||||
return registered
|
||||
|
||||
return decorator
|
||||
@@ -0,0 +1,198 @@
|
||||
"""Capability report: results, progress callbacks, and pretty-printing."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Any
|
||||
|
||||
from langgraph.checkpoint.conformance.capabilities import (
|
||||
BASE_CAPABILITIES,
|
||||
EXTENDED_CAPABILITIES,
|
||||
Capability,
|
||||
)
|
||||
|
||||
# Callback type for per-test progress reporting.
|
||||
# (capability_name, test_name, passed, error_msg_or_None) -> None
|
||||
OnTestResult = Callable[[str, str, bool, str | None], None]
|
||||
|
||||
# Callback type for capability-level events.
|
||||
# (capability_name, detected) -> None
|
||||
OnCapabilityStart = Callable[[str, bool], None]
|
||||
|
||||
|
||||
class ProgressCallbacks:
|
||||
"""Grouped callbacks for progress reporting during validation."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
on_capability_start: Callable[[str, bool], None] | None = None,
|
||||
on_test_result: OnTestResult | None = None,
|
||||
on_capability_end: Callable[[str], None] | None = None,
|
||||
) -> None:
|
||||
self.on_capability_start = on_capability_start
|
||||
self.on_test_result = on_test_result
|
||||
self.on_capability_end = on_capability_end
|
||||
|
||||
@classmethod
|
||||
def default(cls) -> ProgressCallbacks:
|
||||
"""Dot-style progress: ``.`` per pass, ``F`` per fail."""
|
||||
|
||||
def _cap_start(capability: str, detected: bool) -> None:
|
||||
if detected:
|
||||
print(f" {capability}: ", end="", flush=True)
|
||||
else:
|
||||
print(f" ⊘ {capability} (not implemented)")
|
||||
|
||||
def _test_result(
|
||||
capability: str, test_name: str, passed: bool, error: str | None
|
||||
) -> None:
|
||||
print("." if passed else "F", end="", flush=True)
|
||||
|
||||
def _cap_end(capability: str) -> None:
|
||||
print() # newline after dots
|
||||
|
||||
return cls(
|
||||
on_capability_start=_cap_start,
|
||||
on_test_result=_test_result,
|
||||
on_capability_end=_cap_end,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def verbose(cls) -> ProgressCallbacks:
|
||||
"""Per-test output with names and errors."""
|
||||
|
||||
def _cap_start(capability: str, detected: bool) -> None:
|
||||
if detected:
|
||||
print(f" {capability}:")
|
||||
else:
|
||||
print(f" ⊘ {capability} (not implemented)")
|
||||
|
||||
def _test_result(
|
||||
capability: str, test_name: str, passed: bool, error: str | None
|
||||
) -> None:
|
||||
icon = "✓" if passed else "✗"
|
||||
print(f" {icon} {test_name}")
|
||||
if error:
|
||||
for line in error.rstrip().splitlines():
|
||||
print(f" {line}")
|
||||
|
||||
return cls(
|
||||
on_capability_start=_cap_start,
|
||||
on_test_result=_test_result,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def quiet(cls) -> ProgressCallbacks:
|
||||
"""No progress output."""
|
||||
return cls()
|
||||
|
||||
|
||||
@dataclass
|
||||
class CapabilityResult:
|
||||
"""Result of running a single capability's test suite."""
|
||||
|
||||
detected: bool = False
|
||||
passed: bool | None = None # None = skipped
|
||||
tests_passed: int = 0
|
||||
tests_failed: int = 0
|
||||
tests_skipped: int = 0
|
||||
failures: list[str] = field(default_factory=list)
|
||||
|
||||
|
||||
@dataclass
|
||||
class CapabilityReport:
|
||||
"""Aggregate report across all capabilities."""
|
||||
|
||||
checkpointer_name: str
|
||||
results: dict[str, CapabilityResult] = field(default_factory=dict)
|
||||
|
||||
def passed_all_base(self) -> bool:
|
||||
"""Whether all base capability tests passed."""
|
||||
for cap in BASE_CAPABILITIES:
|
||||
result = self.results.get(cap.value)
|
||||
if result is None or result.passed is not True:
|
||||
return False
|
||||
return True
|
||||
|
||||
def passed_all(self) -> bool:
|
||||
"""Whether every detected capability's tests passed."""
|
||||
for result in self.results.values():
|
||||
if result.detected and result.passed is not True:
|
||||
return False
|
||||
return True
|
||||
|
||||
def conformance_level(self) -> str:
|
||||
"""Return a human-readable conformance level string."""
|
||||
if self.passed_all():
|
||||
return "FULL"
|
||||
if self.passed_all_base():
|
||||
return "BASE+PARTIAL"
|
||||
return "BASE" if self._any_base_passed() else "NONE"
|
||||
|
||||
def _any_base_passed(self) -> bool:
|
||||
for cap in BASE_CAPABILITIES:
|
||||
result = self.results.get(cap.value)
|
||||
if result and result.passed is True:
|
||||
return True
|
||||
return False
|
||||
|
||||
def print_report(self) -> None:
|
||||
"""Pretty-print the report to stdout."""
|
||||
width = 52
|
||||
border = "=" * width
|
||||
print(f"\n{'':>2}{border}")
|
||||
print(f"{'':>2} Checkpointer Validation: {self.checkpointer_name}")
|
||||
print(f"{'':>2}{border}")
|
||||
|
||||
def _section(title: str, caps: frozenset[Capability]) -> None:
|
||||
print(f"{'':>2} {title}")
|
||||
for cap in sorted(caps, key=lambda c: c.value):
|
||||
result = self.results.get(cap.value)
|
||||
if result is None:
|
||||
icon = " "
|
||||
suffix = "(no tests)"
|
||||
elif not result.detected:
|
||||
icon = "⊘ "
|
||||
suffix = "(not implemented)"
|
||||
elif result.passed is True:
|
||||
icon = "✅"
|
||||
suffix = ""
|
||||
elif result.passed is False:
|
||||
icon = "❌"
|
||||
suffix = f"({result.tests_failed} failed)"
|
||||
else:
|
||||
icon = "⏭ "
|
||||
suffix = "(skipped)"
|
||||
print(f"{'':>2} {icon} {cap.value:20s} {suffix}")
|
||||
print()
|
||||
|
||||
_section("BASE CAPABILITIES", BASE_CAPABILITIES)
|
||||
_section("EXTENDED CAPABILITIES", EXTENDED_CAPABILITIES)
|
||||
|
||||
total = sum(1 for r in self.results.values() if r.detected)
|
||||
passed = sum(
|
||||
1 for r in self.results.values() if r.detected and r.passed is True
|
||||
)
|
||||
level = self.conformance_level()
|
||||
print(f"{'':>2} Result: {level} ({passed}/{total})")
|
||||
print(f"{'':>2}{border}\n")
|
||||
|
||||
def to_dict(self) -> dict[str, Any]:
|
||||
"""Return a JSON-serializable dict."""
|
||||
return {
|
||||
"checkpointer_name": self.checkpointer_name,
|
||||
"conformance_level": self.conformance_level(),
|
||||
"results": {
|
||||
name: {
|
||||
"detected": r.detected,
|
||||
"passed": r.passed,
|
||||
"tests_passed": r.tests_passed,
|
||||
"tests_failed": r.tests_failed,
|
||||
"tests_skipped": r.tests_skipped,
|
||||
"failures": r.failures,
|
||||
}
|
||||
for name, r in self.results.items()
|
||||
},
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user