Skip to main content

betaloop

A reusable, storage-free ReAct agent kernel: the engine, framework and protocols that drive a tool-calling agent, plus optional capability bundles. It knows nothing about how runs are stored (or even whether they are) — persistence is an optional EventSink a host plugs in. Any application (a thesis-writing platform, a coding agent, ...) implements its own tools + prompt + storage and reuses this kernel.

Core (zero I/O, zero business)

  • runtime — AgentRuntime ReAct loop + event stream. Supports cancellation (run(..., stop=Event|callable) → cancelled event, status="cancelled") and streaming (LLMConfig(stream=True) → assistant_delta events while the model generates; a final full assistant event always follows). Every model call emits a usage event — prompt/completion/total tokens, that call's cost, and context fullness (context_tokens, context_chars, context_window, context_percent when LLMConfig(context_window=...) is set) — so a frontend can show live token/context gauges; RunStats and the host done event carry the cumulative breakdown. Malformed tool results instead of executing with empty/wrong args. A run that exhausts its step budget gets a forced toolless wrap-up call (tool_choice="none") so it ends with the model's summary, reporting status="max_steps" (and never executing the stubborn model's further tool calls). LLMConfig(temperature=..., max_tokens=...) are forwarded on every call. Tool calls execute in model order with consecutive READ tools parallel and every WRITE/META tool alone (no write races); ToolSpec(timeout=...) cancels a hung call. A mid-run context budget (context_budget, default 400k chars) shrinks old tool results head+tail so long runs don't blow the context window. Sinks are error-isolated (a broken display/record sink logs instead of killing the run; strict_records=True opts record failures back into fatal).
  • llm — OpenAI-compatible chat client (retry / jittered backoff / response-shape validation / Retry-After-aware 429 handling / fatal-4xx fail-fast) + SSE streaming helpers; transports normalize usage to prompt_tokens/completion_tokens/total_tokens across chat-completions and Responses shapes
  • tools — ToolRegistry (register / unregister / dispatch / mode filtering / argument validation / per-tool timeout) + pre-dispatch middleware via add_middleware (audit / quota / human-in-the-loop confirmation of write tools)
  • actions — Action + UndoEngine (pure, storage-free undo; reverters may be sync or async)
  • memory — replay_messages / recap_text + MemoryProvider
  • events — EventSink (display + record channels) + SSE serialization
  • context / modes — AgentContext + AgentMode / ToolCategory; host-defined modes via register_mode(name, categories) (unknown modes raise instead of silently degrading to read-only)

Optional bundles (betaloop.bundles)

  • host — AgentHost host-adapter framework: message assembly, run envelope (run_start/done), error funneling, StoreSink (persist via a store), undo_run (reverters see the host's extra context), DictToolAdapter (wrap a dict-based tool system). Eliminates the per-host boilerplate round 1 left behind. host.run(..., stop=...) forwards cancellation to the runtime.

  • store — RunStore/ConversationStore/BlobStore Protocols + JsonlRunStore (default, zero-database JSONL + content-addressed blob spillover). Hosts wanting a DB implement the Protocols; the default needs none. Action values larger than spill_threshold (default 8KB) externalize to blobs and rehydrate transparently on read; id counters are in-memory so appends don't rescan the stream.

  • subagents — SubagentEngine + SubagentRoster/SubagentSpec + delegate tool: isolated worker agents the orchestrator hands subtasks to, tagged so undo still reverts them while the orchestrator's context stays lean. delegate_parallel fans independent tasks out concurrently (bounded by max_parallel, failures isolated per agent); SubagentEngine(on_subagent_event=...) streams live subagent_progress heartbeats to a host push channel; SubagentSpec(transport=...) routes a subagent to a different endpoint.

  • admin — tool_categories / list_tools_admin / list_tool_packages_admin / check_packages over a registry + display packages (admin-panel source; packages are grouping only, tools stay per-name togglable).

  • workspace — sandboxed file I/O + read/write/edit/list/search/glob tools + file undo reverters. edit_file refuses ambiguous old_text (multi-match) unless replace_all is set and returns a diff; search_files greps content by regex (dir / glob filters), glob_files matches paths by pattern; read_file supports offset/limit line-window reads.

  • sandbox — Python code execution (bubblewrap or passthrough backend); output truncation keeps head+tail so tracebacks at the end stay visible

  • skills — markdown skill libraries (SkillLibrary flat dir; package-aware SkillPackages with RemoteSkillSource registry mirrors) + load_skill tool. Skills do file/network I/O, hence a bundle — import betaloop stays zero-I/O (deprecated betaloop.skills alias kept).

  • mcp — MCPManager + MCPServerConfig + parse_servers: bridge external MCP servers (stdio transport, e.g. npx -y @z_ai/mcp-server) into the registry. Sessions outlive registry rebuilds and lazily self-heal: a dead session is restarted on the next tool call, no re-attach needed. readOnlyHint annotations map to the READ category; MCP tools carry no undo reverters. Stdlib-only (newline-delimited JSON-RPC), secrets stay host-side:

    from betaloop.bundles import MCPManager, MCPServerConfig
    
    manager = MCPManager([MCPServerConfig(
        name="zai", command=["npx", "-y", "@z_ai/mcp-server"],
        env={"Z_AI_API_KEY": "...", "Z_AI_MODE": "ZHIPU"},
        default_category="read")])
    await manager.attach(registry)   # registry gains zai__* tools
    

Install

pip install betaloop
# local dev (editable + test/lint deps):
pip install -e ".[dev]"

Storage model

The kernel stores nothing. A host provides:

  • an EventSink (write side) — persists message records however it likes (DB / file / nowhere);
  • a MemoryProvider (read side, optional) — replays prior turns;
  • an UndoEngine fed from wherever the host kept actions.

Minimal host sketch

from betaloop import LLMConfig, ToolRegistry
from betaloop.bundles import AgentHost, JsonlRunStore
from betaloop.bundles.workspace import register_file_tools

registry = ToolRegistry()
register_file_tools(registry, lambda ctx: f"/data/{ctx.user_id}")  # your files
store = JsonlRunStore("/var/lib/myapp/agent")                       # zero-DB default
host = AgentHost(registry,
                 LLMConfig(model=..., base_url=..., api_key=...),
                 store, build_system_prompt=my_prompt_builder)

ctx = AgentContext(run_id=rid, user_id=uid)
async for event in host.run(ctx, task, history=prior_turns):
    ...  # forward run_start / step / tool_call / tool_result / done to your frontend

A host supplies only its tools, system prompt, and (optionally) a store backend — the engine, persistence, run envelope, undo, and (via subagents) delegation are all reused.

More

  • examples/minimal_host.py — a runnable, offline minimal host (tools → run → events → undo).
  • CHANGELOG.md — what changed and when.

License

MIT

Release files for betaloop 0.6.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 betaloop 0.6.0
File Size Uploaded
betaloop-0.6.0.tar.gz 118.2 kB Details

Built distribution (wheel)

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

Total release size: 206.0 kB

Release files / betaloop-0.6.0.tar.gz

Download URL betaloop-0.6.0.tar.gz
Size 118.2 kB
Tags Source
SHA-256 checksum
How to use checksums
b4080e07bef4724abf35818a6e10dae697b7efd78f46ebf16102efce3f3afd22
BLAKE2b-256 checksum
How to use checksums
f90d4726bdf46493b0496f3ef26cd06f68c992e379d324d80ef7ab2372b93466
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.3

Release files / betaloop-0.6.0-py3-none-any.whl

Download URL betaloop-0.6.0-py3-none-any.whl
Size 87.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
7cb44bde43d8dfae363f3772bf4136b891fabd67d5640a557df91fab131438b7
BLAKE2b-256 checksum
How to use checksums
20b4fd5320484152bfa4cbe5aacb7a0b87ae0732d3d42d5ffb48491ce4254b7c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.3

Release history Release notifications | RSS feed

0.9.4

2 release files

0.9.3

2 release files

0.9.2

2 release files

0.9.1

2 release files

0.9.0

2 release files

0.8.0

2 release files

0.7.0

2 release files

This release

0.6.0 This release

2 release files

0.4.6

2 release files

0.4.5

2 release files

0.4.4

2 release files

0.4.3

2 release files

0.4.2

2 release files

0.4.1

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.0

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