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.1

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.1
File Size Uploaded
deepanalysts-0.9.1.tar.gz 171.7 kB Details

Built distribution (wheel)

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

Total release size: 296.2 kB

Release files / deepanalysts-0.9.1.tar.gz

Download URL deepanalysts-0.9.1.tar.gz
Size 171.7 kB
Tags Source
SHA-256 checksum
How to use checksums
1c2a9d018a4f2a293cccf4bdb18f1eb65ce381ee7ff63cbb59abd0da7462b053
BLAKE2b-256 checksum
How to use checksums
1e86438dfe8d937b71c444bdc920dc729fb8a216a56f2ade55177248e1ee08ba
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.17 {"installer":{"name":"uv","version":"0.12.17","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.1-py3-none-any.whl

Download URL deepanalysts-0.9.1-py3-none-any.whl
Size 124.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
65d00f68a936b7d5dd1754387ca60d9e4a222fead7ad506c04b1c66ba8b5a0a2
BLAKE2b-256 checksum
How to use checksums
8137b180562c08f9b1db77e461f0521df08aa864fc74b318c7e260e62fb66f11
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.17 {"installer":{"name":"uv","version":"0.12.17","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

This release

0.9.1 This release

2 release files

0.9.0

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