Skip to content

PsiACE/tileable

Repository files navigation

tileable

Release Build status Commit activity License

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.

Quickstart

make install          # set up the uv environment + pre-commit hooks
python -m examples.greeting

Example 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"])

Why tiles feel good

  • Predictable primitives — A tile is just a tiny class with typed payload/result models.
  • Observability firstEventBus.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 painTilePluginManager contributes tiles, startup hooks, and shutdown hooks on demand.

Build, observe, extend

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 defaults

Listen in when you need full control

bus = EventBus()

unsubscribe = bus.subscribe("tile.failed", lambda sender, **payload: print(payload))
invoke_tile(..., event_bus=bus)
unsubscribe()

Quality gates

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 xml

CI expects these commands to pass before merging. Pre-commit hooks (uv run pre-commit run -a) keep formatting aligned.

Learn more

  • 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.

About

The modular framework for your ideas. Create. Combine. Repeat.

Resources

License

Contributing

Stars

Watchers

Forks

Packages

 
 
 

Contributors