Skip to content

Commit e40f0dc

Browse files
feat: update to use new backend APIs (#3417)
1 parent 65c43ad commit e40f0dc

17 files changed

Lines changed: 287 additions & 205 deletions

src/oss/deepagents/acp.mdx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -368,7 +368,7 @@ await server.start();
368368
| `tools` | `StructuredTool[]` | Custom LangChain tools |
369369
| `systemPrompt` | `string` | Custom system prompt |
370370
| `middleware` | `AgentMiddleware[]` | Custom middleware |
371-
| `backend` | `AnyBackendProtocol \| BackendFactory` | Filesystem backend |
371+
| `backend` | `AnyBackendProtocol` | Filesystem backend |
372372
| `skills` | `string[]` | Skill source paths |
373373
| `memory` | `string[]` | Memory source paths (AGENTS.md) |
374374
| `interruptOn` | `Record<string, boolean \| InterruptOnConfig>` | Tools requiring user approval (HITL) |
@@ -479,7 +479,7 @@ const server = new DeepAgentsServer({
479479
prefix: "/workspace",
480480
backend: new FilesystemBackend({ rootDir: "./workspace" }),
481481
},
482-
{ prefix: "/", backend: (config) => new StateBackend(config) },
482+
{ prefix: "/", backend: new StateBackend() },
483483
],
484484
}),
485485
},

src/oss/deepagents/backends.mdx

Lines changed: 131 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ Here are a few prebuilt filesystem backends that you can quickly use with your d
6363
|---|---|
6464
| [Default](#statebackend-ephemeral) | `agent = create_deep_agent()` <br></br> Ephemeral in state. The default filesystem backend for an agent is stored in `langgraph` state. Note that this filesystem only persists _for a single thread_. |
6565
| [Local filesystem persistence](#filesystembackend-local-disk) | `agent = create_deep_agent(backend=FilesystemBackend(root_dir="/Users/nh/Desktop/"))` <br></br>This gives the deep agent access to your local machine's filesystem. You can specify the root directory that the agent has access to. Note that any provided `root_dir` must be an absolute path. |
66-
| [Durable store (LangGraph store)](#storebackend-langgraph-store) | `agent = create_deep_agent(backend=lambda rt: StoreBackend(rt))` <br></br>This gives the agent access to long-term storage that is _persisted across threads_. This is great for storing longer term memories or instructions that are applicable to the agent over multiple executions. |
66+
| [Durable store (LangGraph store)](#storebackend-langgraph-store) | `agent = create_deep_agent(backend=StoreBackend())` <br></br>This gives the agent access to long-term storage that is _persisted across threads_. This is great for storing longer term memories or instructions that are applicable to the agent over multiple executions. |
6767
| [Sandbox](/oss/deepagents/sandboxes) | `agent = create_deep_agent(backend=sandbox)` <br></br>Execute code in isolated environments. Sandboxes provide filesystem tools plus the `execute` tool for running shell commands. Choose from Modal, Daytona, Deno, or local VFS. |
6868
| [Local shell](#localshellbackend-local-shell) | `agent = create_deep_agent(backend=LocalShellBackend(root_dir=".", env={"PATH": "/usr/bin:/bin"}))` <br></br>Filesystem and shell execution directly on the host. No isolation—use only in controlled development environments. See [security considerations](#localshellbackend-local-shell) below. |
6969
| [Composite](#compositebackend-router) | Ephemeral by default, `/memories/` persisted. The Composite backend is maximally flexible. You can specify different routes in the filesystem to point towards different backends. See Composite routing below for a ready-to-paste example. |
@@ -212,6 +212,8 @@ Use with extreme caution and only in appropriate environments.
212212

213213
A namespace factory controls where `StoreBackend` reads and writes data. It receives a `BackendContext` and returns a tuple of strings used as the store namespace. Use namespace factories to isolate data between users, tenants, or assistants.
214214

215+
Pass the namespace factory to the `namespace` parameter when constructing a `StoreBackend`:
216+
215217
```python
216218
NamespaceFactory = Callable[[BackendContext], tuple[str, ...]]
217219
```
@@ -230,22 +232,19 @@ from langgraph.config import get_config
230232
from deepagents.backends import StoreBackend
231233

232234
# Per-user: each user gets their own isolated storage
233-
backend = lambda rt: StoreBackend(
234-
rt,
235+
backend = StoreBackend(
235236
namespace=lambda ctx: (ctx.runtime.context.user_id,),
236237
)
237238

238239
# Per-assistant: all users of the same assistant share storage
239-
backend = lambda rt: StoreBackend(
240-
rt,
240+
backend = StoreBackend(
241241
namespace=lambda ctx: (
242242
get_config()["metadata"]["assistant_id"],
243243
),
244244
)
245245

246246
# Per-thread: storage scoped to a single conversation
247-
backend = lambda rt: StoreBackend(
248-
rt,
247+
backend = StoreBackend(
249248
namespace=lambda ctx: (
250249
get_config()["configurable"]["thread_id"],
251250
),
@@ -259,20 +258,20 @@ import { getConfig } from "@langchain/langgraph";
259258
import { StoreBackend } from "deepagents";
260259

261260
// Per-user: each user gets their own isolated storage
262-
const backend = (rt) => new StoreBackend(rt, {
261+
const backend = new StoreBackend({
263262
namespace: (ctx) => [ctx.runtime.context.userId],
264263
});
265264

266265
// Per-assistant: all users of the same assistant share storage
267-
const backend = (rt) => new StoreBackend(rt, {
266+
const backend = new StoreBackend({
268267
namespace: (ctx) => {
269268
const config = getConfig();
270269
return [config.metadata.assistantId];
271270
},
272271
});
273272

274273
// Per-thread: storage scoped to a single conversation
275-
const backend = (rt) => new StoreBackend(rt, {
274+
const backend = new StoreBackend({
276275
namespace: (ctx) => {
277276
const config = getConfig();
278277
return [config.configurable.threadId];
@@ -322,19 +321,15 @@ Namespace components must contain only alphanumeric characters, hyphens, undersc
322321
## Specify a backend
323322

324323
:::python
325-
- Pass a backend to `create_deep_agent(backend=...)`. The filesystem middleware uses it for all tooling.
326-
- You can pass either:
327-
- An instance implementing `BackendProtocol` (for example, `FilesystemBackend(root_dir=".")`), or
328-
- A factory `BackendFactory = Callable[[ToolRuntime], BackendProtocol]` (for backends that need runtime like `StateBackend` or `StoreBackend`).
329-
- If omitted, the default is `lambda rt: StateBackend(rt)`.
324+
- Pass a backend instance to `create_deep_agent(backend=...)`. The filesystem middleware uses it for all tooling.
325+
- The backend must implement `BackendProtocol` (for example, `StateBackend()`, `FilesystemBackend(root_dir=".")`, `StoreBackend()`).
326+
- If omitted, the default is `StateBackend()`.
330327
:::
331328

332329
:::js
333-
- Pass a backend to `createDeepAgent({ backend: ... })`. The filesystem middleware uses it for all tooling.
334-
- You can pass either:
335-
- An instance implementing `AnyBackendProtocol` (`BackendProtocolV1` or `BackendProtocolV2`) — for example, `new FilesystemBackend({ rootDir: "." })`, or
336-
- A `BackendFactory` function `(stateAndStore: StateAndStore) => AnyBackendProtocol` — for backends that need runtime state or a store, like `StateBackend` or `StoreBackend`.
337-
- If omitted, the default is `(config) => new StateBackend(config)`.
330+
- Pass a backend instance to `createDeepAgent({ backend: ... })`. The filesystem middleware uses it for all tooling.
331+
- The backend must implement `AnyBackendProtocol` (`BackendProtocolV1` or `BackendProtocolV2`) — for example, `new StateBackend()`, `new FilesystemBackend({ rootDir: "." })`, `new StoreBackend()`.
332+
- If omitted, the default is `new StateBackend()`.
338333

339334
<Note>
340335
Before version 1.9.0, only `BackendProtocol` was supported which is now `BackendProtocolV1`. V1 backends are automatically adapted to V2 at runtime via `adaptBackendProtocol()`. No code changes are required to keep using existing V1 backends. To update to v2, see [update existing backends to v2](#update-existing-backends-to-v2).
@@ -351,29 +346,29 @@ Route parts of the namespace to different backends. Commonly used to persist `/m
351346
from deepagents import create_deep_agent
352347
from deepagents.backends import CompositeBackend, StateBackend, FilesystemBackend
353348

354-
composite_backend = lambda rt: CompositeBackend(
355-
default=StateBackend(rt),
356-
routes={
357-
"/memories/": FilesystemBackend(root_dir="/deepagents/myagent", virtual_mode=True),
358-
},
349+
agent = create_deep_agent(
350+
backend=CompositeBackend(
351+
default=StateBackend(),
352+
routes={
353+
"/memories/": FilesystemBackend(root_dir="/deepagents/myagent", virtual_mode=True),
354+
},
355+
)
359356
)
360-
361-
agent = create_deep_agent(backend=composite_backend)
362357
```
363358
:::
364359

365360
:::js
366361
```typescript
367362
import { createDeepAgent, CompositeBackend, FilesystemBackend, StateBackend } from "deepagents";
368363

369-
const compositeBackend = (rt) => new CompositeBackend(
370-
new StateBackend(rt),
371-
{
372-
"/memories/": new FilesystemBackend({ rootDir: "/deepagents/myagent", virtualMode: true }),
373-
},
374-
);
375-
376-
const agent = createDeepAgent({ backend: compositeBackend });
364+
const agent = createDeepAgent({
365+
backend: new CompositeBackend(
366+
new StateBackend(),
367+
{
368+
"/memories/": new FilesystemBackend({ rootDir: "/deepagents/myagent", virtualMode: true }),
369+
},
370+
),
371+
});
377372
```
378373
:::
379374

@@ -384,7 +379,7 @@ Behavior:
384379

385380
Notes:
386381
- Longer prefixes win (for example, route `"/memories/projects/"` can override `"/memories/"`).
387-
- For StoreBackend routing, ensure the agent runtime provides a store (`runtime.store`).
382+
- For StoreBackend routing, ensure a store is provided via `create_deep_agent(store=...)` or provisioned by the platform.
388383

389384
## Use a virtual filesystem
390385

@@ -716,7 +711,106 @@ type FileData =
716711
};
717712
```
718713

719-
Backends may encounter either format when reading from state or store. The framework handles both transparently. New writes default to the v2 format. During rolling deployments where older readers need the legacy format, pass `fileFormat: "v1"` to the backend constructor (e.g., `new StoreBackend(rt, { fileFormat: "v1" })`).
714+
Backends may encounter either format when reading from state or store. The framework handles both transparently. New writes default to the v2 format. During rolling deployments where older readers need the legacy format, pass `fileFormat: "v1"` to the backend constructor (e.g., `new StoreBackend({ fileFormat: "v1" })`).
715+
:::
716+
717+
## Migrate from backend factories
718+
719+
<Warning>
720+
The backend factory pattern is **deprecated**. Pass pre-constructed backend instances directly instead of factory functions.
721+
</Warning>
722+
723+
Previously, backends like `StateBackend` and `StoreBackend` required a factory function that received a runtime object, because they needed runtime context (state, store) to operate. Backends now resolve this context internally via LangGraph's `get_config()`, `get_store()`, and `get_runtime()` helpers, so you can pass instances directly.
724+
725+
### What changed
726+
727+
| Before (deprecated) | After |
728+
|---|---|
729+
| `backend=lambda rt: StateBackend(rt)` | `backend=StateBackend()` |
730+
| `backend=lambda rt: StoreBackend(rt)` | `backend=StoreBackend()` |
731+
| `backend=lambda rt: CompositeBackend(default=StateBackend(rt), ...)` | `backend=CompositeBackend(default=StateBackend(), ...)` |
732+
| `backend: (config) => new StateBackend(config)` | `backend: new StateBackend()` |
733+
| `backend: (config) => new StoreBackend(config)` | `backend: new StoreBackend()` |
734+
735+
### Deprecated APIs
736+
737+
:::python
738+
739+
| Deprecated | Replacement |
740+
|---|---|
741+
| Passing a callable to `backend=` in `create_deep_agent` | Pass a backend instance directly |
742+
| `runtime` constructor argument on `StateBackend(runtime)` | `StateBackend()` (no arguments needed) |
743+
| `runtime` constructor argument on `StoreBackend(runtime)` | `StoreBackend()` or `StoreBackend(namespace=..., store=...)` |
744+
| `files_update` field on `WriteResult` and `EditResult` | State writes are now handled internally by the backend |
745+
| `Command` wrapping in middleware write/edit tools | Tools return plain strings; no `Command(update=...)` needed |
746+
747+
:::
748+
749+
:::js
750+
751+
| Deprecated | Replacement |
752+
|---|---|
753+
| `BackendFactory` type | Pass a backend instance directly |
754+
| `BackendRuntime` interface | Backends resolve context internally |
755+
| `StateBackend(runtime, options?)` constructor overload | `new StateBackend(options?)` |
756+
| `StoreBackend(stateAndStore, options?)` constructor overload | `new StoreBackend(options?)` |
757+
| `filesUpdate` field on `WriteResult` and `EditResult` | State writes are now handled internally by the backend |
758+
759+
:::
760+
761+
<Note>
762+
The factory pattern still works at runtime and emits a deprecation warning. Update your code to use direct instances before the next major version.
763+
</Note>
764+
765+
### Migration example
766+
767+
:::python
768+
```python
769+
# Before (deprecated)
770+
from deepagents import create_deep_agent
771+
from deepagents.backends import CompositeBackend, StateBackend, StoreBackend
772+
773+
agent = create_deep_agent(
774+
backend=lambda rt: CompositeBackend(
775+
default=StateBackend(rt),
776+
routes={"/memories/": StoreBackend(rt, namespace=lambda ctx: (ctx.runtime.context.user_id,))},
777+
),
778+
)
779+
780+
# After
781+
agent = create_deep_agent(
782+
backend=CompositeBackend(
783+
default=StateBackend(),
784+
routes={"/memories/": StoreBackend(namespace=lambda ctx: (ctx.runtime.context.user_id,))},
785+
),
786+
)
787+
```
788+
:::
789+
790+
:::js
791+
```typescript
792+
// Before (deprecated)
793+
import { createDeepAgent, CompositeBackend, StateBackend, StoreBackend } from "deepagents";
794+
795+
const agent = createDeepAgent({
796+
backend: (config) => new CompositeBackend(
797+
new StateBackend(config),
798+
{ "/memories/": new StoreBackend(config, {
799+
namespace: (ctx) => [ctx.runtime.context.userId],
800+
}) },
801+
),
802+
});
803+
804+
// After
805+
const agent = createDeepAgent({
806+
backend: new CompositeBackend(
807+
new StateBackend(),
808+
{ "/memories/": new StoreBackend({
809+
namespace: (ctx) => [ctx.runtime.context.userId],
810+
}) },
811+
),
812+
});
813+
```
720814
:::
721815

722816
## Protocol reference

src/oss/deepagents/context-engineering.mdx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -466,9 +466,9 @@ import { InMemoryStore } from "@langchain/langgraph-checkpoint";
466466
const agent = await createDeepAgent({
467467
model: "claude-sonnet-4-6",
468468
store: new InMemoryStore(),
469-
backend: (config) => new CompositeBackend(
470-
new StateBackend(config),
471-
{ "/memories/": new StoreBackend(config) },
469+
backend: new CompositeBackend(
470+
new StateBackend(),
471+
{ "/memories/": new StoreBackend() },
472472
),
473473
systemPrompt: `When users tell you their preferences, save them to /memories/user_preferences.txt so you remember them in future conversations.`,
474474
});

src/oss/deepagents/customization.mdx

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -665,7 +665,7 @@ You can pass one or more file paths to the `memory` parameter when creating your
665665
)
666666

667667
agent = create_deep_agent(
668-
backend=(lambda rt: StoreBackend(rt)),
668+
backend=StoreBackend(),
669669
store=store,
670670
memory=[
671671
"/AGENTS.md"
@@ -771,7 +771,6 @@ You can pass one or more file paths to the `memory` parameter when creating your
771771
import {
772772
InMemoryStore,
773773
MemorySaver,
774-
type BaseStore,
775774
} from "@langchain/langgraph";
776775

777776
const AGENTS_MD_URL =
@@ -803,15 +802,8 @@ You can pass one or more file paths to the `memory` parameter when creating your
803802

804803
const checkpointer = new MemorySaver();
805804

806-
const backendFactory = (config: { state: unknown; store?: BaseStore }) => {
807-
return new StoreBackend({
808-
state: config.state,
809-
store: config.store ?? store,
810-
});
811-
};
812-
813805
const agent = await createDeepAgent({
814-
backend: backendFactory,
806+
backend: new StoreBackend(),
815807
store: store,
816808
checkpointer: checkpointer,
817809
memory: ["/AGENTS.md"],
@@ -839,8 +831,7 @@ You can pass one or more file paths to the `memory` parameter when creating your
839831
const checkpointer = new MemorySaver();
840832

841833
const agent = await createDeepAgent({
842-
backend: (config) =>
843-
new FilesystemBackend({ rootDir: "/Users/user/{project}" }),
834+
backend: new FilesystemBackend({ rootDir: "/Users/user/{project}" }),
844835
memory: ["./AGENTS.md", "./.deepagents/AGENTS.md"],
845836
interruptOn: {
846837
read_file: true,

0 commit comments

Comments
 (0)