A ByteBot app is a Hub and some satellites. The Hub executes tools and inference and arbitrates input. A satellite announces what it is, then renders events and sends input. In-proc, over stdio, or over HTTP, the events are the same lines of JSON.
A ByteBot app is a Hub and some satellites. The Hub executes tools and inference and arbitrates input. A satellite announces what it is, then renders events and sends input. In-proc, over stdio, or over HTTP, the events are the same lines of JSON.
{
"schema_version": 1,
"kind": "ui",
"language": "python",
"entry": { "type": "http",
"stream": "http://site:8081/stream",
"inbox": "http://site:8081/inbox" }
}
The first event on any connection is bb_sync: kind, whether it can take input, whether it's primary, language, protocol version, capabilities. The Hub assigns a component id and, from then on, both sides send freely. A manifest tells the Hub how to reach you.
import bytebot as bb
async def bb_main():
ctrl = await bb.sync(kind="ui", input_capable=True, primary=True)
@ctrl.on_infer_chunk
async def render(chunk): # kind: block_start | block_delta | block_stop | loop_done
await browser.push(chunk.scope, html_for(chunk))
ctrl.on("input_strict", ask_the_human)
await ctrl.start()
async def on_send(user_id, text):
with scoped(user_id): # CURRENT_SCOPE is the tenant wall
await ctrl.send_user_event(None, text)
await bb.serve_http(bb_main, host="0.0.0.0", port=8081, send_acks=True)
That's a web UI. It owns its own process and event loop, serves /stream and /inbox, and turns chunks into HTML for whichever browser tab belongs to that scope.
from bytebot import ByteBot
hub = ByteBot(ui="http://site:8081", context="./context") # url, path,
hub.attach("./ingest") # github slug,
hub.attach_infer({"ref": "bb-infer", "opts": {...}}, primary=True) # or package
await hub.start() Component references are a URL, a local path, a GitHub slug, or an installed package. The Hub resolves each, reads its manifest, and wires it. Attach more later.
Spec and SDKs open with the wire freeze.