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—AgentRuntimeReAct loop + event stream. Supports cancellation (run(..., stop=Event|callable)→cancelledevent,status="cancelled") and streaming (LLMConfig(stream=True)→assistant_deltaevents while the model generates; a final fullassistantevent always follows). Every model call emits ausageevent — prompt/completion/total tokens, that call's cost, and context fullness (context_tokens,context_chars,context_window,context_percentwhenLLMConfig(context_window=...)is set) — so a frontend can show live token/context gauges;RunStatsand the hostdoneevent 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, reportingstatus="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=Trueopts 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 toprompt_tokens/completion_tokens/total_tokensacross chat-completions and Responses shapestools—ToolRegistry(register / unregister / dispatch / mode filtering / argument validation / per-tool timeout) + pre-dispatch middleware viaadd_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+MemoryProviderevents—EventSink(display + record channels) + SSE serializationcontext/modes—AgentContext+AgentMode/ToolCategory; host-defined modes viaregister_mode(name, categories)(unknown modes raise instead of silently degrading to read-only)
Optional bundles (betaloop.bundles)
-
host—AgentHosthost-adapter framework: message assembly, run envelope (run_start/done), error funneling,StoreSink(persist via a store),undo_run(reverters see the host'sextracontext),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/BlobStoreProtocols +JsonlRunStore(default, zero-database JSONL + content-addressed blob spillover). Hosts wanting a DB implement the Protocols; the default needs none. Action values larger thanspill_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+delegatetool: isolated worker agents the orchestrator hands subtasks to, tagged so undo still reverts them while the orchestrator's context stays lean.delegate_parallelfans independent tasks out concurrently (bounded bymax_parallel, failures isolated per agent);SubagentEngine(on_subagent_event=...)streams livesubagent_progressheartbeats 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_packagesover 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_filerefuses ambiguousold_text(multi-match) unlessreplace_allis set and returns a diff;search_filesgreps content by regex (dir / glob filters),glob_filesmatches paths by pattern;read_filesupportsoffset/limitline-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 (SkillLibraryflat dir; package-awareSkillPackageswithRemoteSkillSourceregistry mirrors) +load_skilltool. Skills do file/network I/O, hence a bundle —import betaloopstays zero-I/O (deprecatedbetaloop.skillsalias 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.readOnlyHintannotations 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
UndoEnginefed 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)
| File | Size | Uploaded | |
|---|---|---|---|
| betaloop-0.6.0.tar.gz | 118.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| 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
|