Tileable is a Python 3.12+ framework for composing event-driven workflows from small, typed “tiles”. It keeps ergonomics, observability, and testability front and centre.
make install # set up the uv environment + pre-commit hooks
python -m examples.greetingExample output:
[debug] {'tile': 'greeting', 'message': 'Tileable'}
Hi, Tileable!
runs=1
Prefer a REPL? The demo tile is wired exactly like production code:
from examples.greeting import GreetingPayload, GreetingPlugin, showcase
from tileable import EventBus, TilePluginManager, TileRegistry, invoke_tile
# Discover tiles via the bundled plugin
result, debug_events, state = showcase(message="Tileable")
# Or assemble the pieces manually
registry = TileRegistry()
plugins = TilePluginManager()
plugins.register(GreetingPlugin())
bus = EventBus()
state = {"runs": 0}
with bus.record() as lifecycle:
result = invoke_tile(
"greeting",
GreetingPayload(message="Operator"),
registry=registry,
plugins=plugins,
event_bus=bus,
state=state,
)
print(result.response)
print(lifecycle.payloads("tile.debug"))
print(state["runs"])- Predictable primitives — A tile is just a tiny class with typed payload/result models.
- Observability first —
EventBus.record()captures lifecycle events without throwaway subscribers. - State you can trust — Services and per-run state live on
TileContext, keeping plugins and tiles aligned. - Plugins without pain —
TilePluginManagercontributes tiles, startup hooks, and shutdown hooks on demand.
Run tiles and capture context
from tileable import invoke_tile
result, ctx = invoke_tile(
"greeting",
GreetingPayload(message="Developer"),
return_context=True,
)
print(result.response)
print(dict(ctx.services)) # services added during execution
print(ctx.state.get("runs"))Scope runtime state for tests
from tileable import scoped_runtime, TileRegistry
with scoped_runtime(registry=TileRegistry()):
... # run tiles without touching the global defaultsListen in when you need full control
bus = EventBus()
unsubscribe = bus.subscribe("tile.failed", lambda sender, **payload: print(payload))
invoke_tile(..., event_bus=bus)
unsubscribe()make check # ruff lint + formatter, ty type-checking, deptry hygiene
make test # pytest (sync + async paths and doctests)
tox -e py312,py313 # interpreter matrix + coverage xmlCI expects these commands to pass before merging. Pre-commit hooks (uv run pre-commit run -a) keep formatting aligned.
- Full documentation: https://tileable.dev/
- Additional demos:
examples/ - Advanced recipes:
docs/advanced.md - Contributor handbook:
AGENTS.md
Repository initiated with fpgmaas/cookiecutter-uv, heavily customised for Tileable’s design philosophy.