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.

  • images — tool-tier image perception, no kernel changes. image_info: stdlib-only header probe (PNG/JPEG/GIF/BMP/WEBP — dimensions, dpi, color mode) answering deterministic questions with zero model calls. analyze_image: ONE vision-model call (OpenAI image_url data-URL block + the question), registered only when a LLMConfig is passed — the host's main config reuses the main model, a dedicated one routes vision elsewhere. Images never enter the main conversation (answers are memoized per file hash + question), so context budget / trimming / replay stay untouched.

  • 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.7.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.7.0
File Size Uploaded
betaloop-0.7.0.tar.gz 129.4 kB Details

Built distribution (wheel)

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

Total release size: 225.2 kB

Release files / betaloop-0.7.0.tar.gz

Download URL betaloop-0.7.0.tar.gz
Size 129.4 kB
Tags Source
SHA-256 checksum
How to use checksums
a7d0252aaa87595f3ce4525406a7d20367fd77d636124893d0053aab4bd1d7ff
BLAKE2b-256 checksum
How to use checksums
4276ce045cb82cb9c59c3620866c040f951618659b3fb8360f7c185cd2b1f2a9
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.7.0-py3-none-any.whl

Download URL betaloop-0.7.0-py3-none-any.whl
Size 95.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
531f16b55fee0ea8d07ec04c67dc52ca4e5a74cf17b3ca39ea1a99b30fab6099
BLAKE2b-256 checksum
How to use checksums
7c907c2ce3b8ba90a0dec5c07cb2f9441bd30a3c542c2ebfb9199bf31f053f51
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

This release

0.7.0 This release

2 release files

0.6.0

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