Skip to main content

Deep Analysts

Middleware, pluggable file backends, and streaming helpers for building LangChain v1 / LangGraph agents that have memory, skills, a filesystem, a sandbox, and subagents.

deepanalysts is a library only — no CLI, no server, no domain logic. It depends on langchain, langgraph, httpx, pyyaml, tenacity, and wcmatch; model providers and storage services are the caller's choice.

Install

uv add deepanalysts            # or: pip install deepanalysts
uv add "deepanalysts[e2b]"     # + E2B microVM sandbox backend
uv add "deepanalysts[postgres]" # + LangGraph Postgres checkpointer deps

Requires Python 3.11+, langchain >= 1.3.13, and langgraph >= 1.2.9.

What's in the box

Middleware (deepanalysts.middleware)

  • ToolErrorHandlingMiddleware — turns tool exceptions into ToolMessages and trips a circuit breaker after N consecutive failures of the same tool.
  • SummarizationMiddleware — compacts what the model sees while leaving the checkpointed message log intact, so history, replay, and evals keep the source messages.
  • MemoryMiddleware — always-on context from AGENTS.md-style sources.
  • SkillsMiddleware — on-demand workflows via progressive disclosure of SKILL.md frontmatter.
  • FilesystemMiddleware — ls / read_file / write_file / edit_file / glob / grep, plus execute when the backend is a sandbox. Oversized tool results are spilled to a file and replaced with a preview.
  • SubAgentMiddleware — a task tool that delegates to named subagents.
  • PatchToolCallsMiddleware — repairs dangling tool calls before the next model turn.

Backends (deepanalysts.backends) — all implement BackendProtocol (and SandboxBackendProtocol where they can execute):

StateBackend (agent state, ephemeral) · StoreBackend (LangGraph BaseStore, persistent) · LocalFilesystemBackend (real disk, jailed) · RestrictedSubprocessBackend (hardened local subprocess) · E2BSandboxBackend (Firecracker microVM) · SupabaseStorageBackend (async object storage) · CompositeBackend (route by path prefix).

Also: BasementClient + BasementMemoryLoader / BasementSkillsLoader for API-sourced skills and memories, DeepAnalystsState for delta-channel message checkpointing, and SubagentTransformer for typed subagent handles on LangGraph v3 streams.

Minimal usage

from langchain.agents import create_agent
from langchain.chat_models import init_chat_model

from deepanalysts import DeepAnalystsState
from deepanalysts.backends import (
    CompositeBackend,
    RestrictedSubprocessBackend,
    StoreBackend,
)
from deepanalysts.middleware import (
    FilesystemMiddleware,
    MemoryMiddleware,
    PatchToolCallsMiddleware,
    SkillsMiddleware,
    ToolErrorHandlingMiddleware,
    create_summarization_middleware,
)

model = init_chat_model("<provider>:<model-id>")  # any LangChain chat model

# Sandbox for scratch work; persistent store for skills and memories.
def backend(runtime):
    return CompositeBackend(
        default=RestrictedSubprocessBackend(timeout=30),
        routes={
            "/skills/": StoreBackend(runtime),
            "/memories/": StoreBackend(runtime),
        },
    )

agent = create_agent(
    model,
    tools=[],
    state_schema=DeepAnalystsState,  # optional: delta-channel message checkpointing
    middleware=[
        ToolErrorHandlingMiddleware(),                      # first: catches every tool error
        create_summarization_middleware(model, backend),    # model-aware compaction defaults
        MemoryMiddleware(backend=backend, sources=["/memories/AGENTS.md"]),
        SkillsMiddleware(backend=backend, sources=["/skills/user/", "/skills/project/"]),
        FilesystemMiddleware(backend=backend),              # file tools + `execute`
        PatchToolCallsMiddleware(),                         # last: repair dangling tool calls
    ],
)

result = agent.invoke(
    {"messages": [{"role": "user", "content": "Write a note to /notes.md, then read it back."}]},
    config={"configurable": {"user_id": "user-123"}},
)
print(result["messages"][-1].content)

Notes on the example:

  • Every middleware's backend= accepts either a backend instance or a factory (ToolRuntime) -> BackendProtocol. Use a factory for anything runtime-scoped, as StoreBackend is.
  • Order matters: error handling first, summarization before the prompt-injecting middleware, filesystem before subagents, patching last.
  • configurable.user_id is what StoreBackend uses to namespace files per tenant.
  • create_summarization_middleware(model, backend) derives trigger/retention from the model profile; construct SummarizationMiddleware(...) directly if you want explicit trigger=("tokens", 100_000) / keep=("messages", 20).

Subagents

from deepanalysts.middleware import (
    SubAgent,
    SubAgentMiddleware,
    private_state_field_names,
)

technical_analyst: SubAgent = {
    "name": "technical_analyst",
    "description": "Analyzes charts and technical indicators.",
    "system_prompt": "You are a technical analyst...",
    "tools": [get_indicators],
}

sub = SubAgentMiddleware(default_model=model, default_tools=[], subagents=[technical_analyst])
middleware = [ToolErrorHandlingMiddleware(), FilesystemMiddleware(backend=backend), sub, PatchToolCallsMiddleware()]

# Keep middleware-private state out of spawned subagents (assign AFTER the stack exists —
# the setter rebuilds the `task` tool).
sub.private_state_keys = private_state_field_names(
    *(m.state_schema for m in middleware if getattr(m, "state_schema", None) is not None)
)

agent = create_agent(model, tools=[], middleware=middleware)

The middleware exposes one task(description, subagent_type) tool. It injects a session-context header (symbol / exchange / interval from config.configurable, plus the current UTC time) into the subagent's prompt, retries transient failures, and returns the last non-empty assistant message to the caller.

API-backed skills and memories

from deepanalysts.backends import BasementMemoryLoader, BasementSkillsLoader
from deepanalysts.middleware import MemoryMiddleware, SkillsMiddleware

memory = MemoryMiddleware(loader=BasementMemoryLoader(token_provider=get_jwt))
skills = SkillsMiddleware(
    loader=BasementSkillsLoader(token_provider=get_jwt, built_in_dirs=["./skills"]),
    agent_name="technical_analyst",  # filters by the skill's target_agents
)

Loader mode takes precedence over backend mode when both are configured. Point the client at another host with BasementClient(base_url=...) or the BASEMENT_API env var.

Sandboxed execution

FilesystemMiddleware adds an execute tool only when the backend can run commands, and hides it otherwise.

from deepanalysts.backends import E2BSandboxBackend, RestrictedSubprocessBackend

local = RestrictedSubprocessBackend(timeout=30)     # same host: process-group kill, rlimits, path jail
micro = E2BSandboxBackend(template="my-template")   # real isolation; egress denied by default
try:
    print(micro.execute("python3 -c 'print(2**10)'").output)
finally:
    micro.close()

RestrictedSubprocessBackend is hardened but not container isolation — it runs as the same OS user with open network egress. Use a microVM backend for untrusted code.

Development

uv sync --all-extras
uv run pytest
uv run ruff check . && uv run ruff format .
uv build

Async tests use anyio (@pytest.mark.anyio) with the asyncio backend. Tests are offline by default; the E2B integration tests skip unless E2B_API_KEY is set.

Releasing

Bump version in pyproject.toml, merge to main, and publish a GitHub release — CI runs the tests, builds, and pushes to PyPI. A workflow_dispatch run with test_pypi=true publishes to TestPyPI instead.

License

MIT

Metadata

Release files for deepanalysts 0.9.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for deepanalysts 0.9.0
File Size Uploaded
deepanalysts-0.9.0.tar.gz 168.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for deepanalysts 0.9.0
File Interpreter ABI Platform
deepanalysts-0.9.0-py3-none-any.whl Python 3 none any Details

Total release size: 292.9 kB

Release files / deepanalysts-0.9.0.tar.gz

Download URL deepanalysts-0.9.0.tar.gz
Size 168.9 kB
Tags Source
SHA-256 checksum
How to use checksums
6c0dec1d03b168cd1bdd10bf523fe02bc145b74d366a5aac4552d7f11d077a06
BLAKE2b-256 checksum
How to use checksums
2896e3fdfc94bfbf769dba8dcf5673bffd98e9b433b8d5260f8f2e08b96ff60a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.13 {"installer":{"name":"uv","version":"0.12.13","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / deepanalysts-0.9.0-py3-none-any.whl

Download URL deepanalysts-0.9.0-py3-none-any.whl
Size 124.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a5b0e5d9bec2cca0c028476188860d49153a27a45debc0a7c96c3f89657108d3
BLAKE2b-256 checksum
How to use checksums
554a3a7962c2fc1d1d2f1171b26a70c3c8440a6a5a59b42855e0cbe4f47835c8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.13 {"installer":{"name":"uv","version":"0.12.13","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

0.9.2

2 release files

0.9.1

2 release files

This release

0.9.0 This release

2 release files

0.8.0

2 release files

0.7.7

2 release files

0.7.6

2 release files

0.7.5

2 release files

0.7.4

2 release files

0.7.3

2 release files

0.7.2

2 release files

0.7.1

2 release files

0.7.0

2 release files

0.6.0

2 release files

0.5.1

2 release files

0.5.0

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.5

2 release files

0.2.4

2 release files

0.2.3

2 release files

0.2.2

2 release files

0.2.1

2 release files

0.2.0

2 release files

0.1.14

2 release files

0.1.13

2 release files

0.1.12

2 release files

0.1.10

2 release files

0.1.9

2 release files

0.1.8

2 release files

0.1.7

2 release files

0.1.6

2 release files

0.1.5

2 release files

0.1.4

2 release files

0.1.3

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page