agentleFS
dbtinoy-dbtinoy-/lexigram-framework-skills/real-time-web/SKILL.md

real-time-web

Skill1 starsChanged 3 months agoMITView on GitHub

Use when implementing real-time features with SSE, WebSocket, or HTMX integration in Lexigram

The file

Files
SKILL.md

Real-Time Web

Overview

Lexigram supports two real-time primitives: SSE (one-way server→client) and WebSocket (bidirectional). Both are class-based handlers registered by decorators. HTMX can consume SSE streams for declarative live updates.

SSE with HTMX

Subclass AbstractSSEHandler, decorate the class with @sse_endpoint(path), and override stream(request) as an async generator yielding event dicts:

from lexigram.web.sse import sse_endpoint
from lexigram.web.sse.handler import AbstractSSEHandler
from starlette.requests import Request

@sse_endpoint("/events/{channel}", heartbeat_interval=15)
class LiveFeedHandler(AbstractSSEHandler):
    event_types = ["message"]

    async def stream(self, request: Request):
        channel = request.path_params["channel"]
        async for update in subscribe(channel):        # your pub/sub source
            yield {"event": "message", "data": update}

There is no push() — each client runs its own generator. To fan out to many clients, read from a shared async queue or pub/sub channel inside stream() (the built-in broadcast() raises NotImplementedError). Override on_connect/on_disconnect for lifecycle; max_connections caps concurrency.

HTMX client:

<div hx-sse="connect:/events/dashboard swap:message">
  <div hx-get="/partials/dashboard" hx-trigger="load">Loading…</div>
</div>

WebSocket Handler

@websocket_handler(path) decorates the handler class (not methods). Subclass AbstractWebSocketHandler and implement on_connect / on_message / on_disconnect; on_message receives an already-parsed JSON dict:

from lexigram.web.websocket.decorators import websocket_handler
from lexigram.web.websocket.handler import AbstractWebSocketHandler

@websocket_handler("/ws/chat/{room_id}")
class ChatHandler(AbstractWebSocketHandler):
    async def on_connect(self, websocket):
        await websocket.accept()                       # required

    async def on_message(self, websocket, message: dict):
        await self.broadcast(message)                  # or send per-connection

    async def on_disconnect(self, websocket):
        pass

For raw receive loops use while True: message = await websocket.receive_text() — there is no iter_text() async iterator on the connection.

Common Mistakes

  • Decorating methods instead of classes — both @sse_endpoint and @websocket_handler wrap the class
  • Calling push(...) or broadcast(...) on SSE handlers — SSE is unidirectional; share a queue/pub-sub in stream()
  • Forgetting await websocket.accept() in WebSocket on_connect — connection never opens
  • Blocking the SSE generator with sync I/O — keep it async throughout

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.