Phase 7: serve the vet sample from the template tools and docs
- build.py: _TEMPLATE_COMPONENTS rewritten around the vet sample (53 kinds —
per-resource slices, jobs, worked appointment_reminder family, tests);
seed tokens dropped from the manifest ('sample: vet' instead)
- registry.py: get_app_component(kind) no longer renames item/example seeds;
prompts and tool docs updated to 'copy a slice and adapt'
- templates/README.md, 01_build_recipe, 40_ui_admin, wiki/03 updated off the
old seed scheme; repo inventory/config now exclude vet (docs move under
standards; react/vet itself is deleted in a later phase)
- regenerated MCP bundle, vendored templates, and site output
This commit is contained in:
88
standards/mcp/templates/app/infra/adapters/actions_stream.py
Normal file
88
standards/mcp/templates/app/infra/adapters/actions_stream.py
Normal file
@@ -0,0 +1,88 @@
|
||||
"""# Summary
|
||||
|
||||
Species-scoped action stream — the SSE event source behind ``stream_actions``.
|
||||
|
||||
---
|
||||
|
||||
Every pet gets a background asyncio task that emits random, species-appropriate
|
||||
behaviors (a wagging dog, a kneading cat, a chirping bird) onto per-subscriber
|
||||
queues. The HTTP handler subscribes one queue per connected client and
|
||||
unsubscribes when the response closes.
|
||||
|
||||
Notes:
|
||||
Process-local and best-effort — the stream is generated, not persisted,
|
||||
and is intentionally independent of the persistence backend.
|
||||
"""
|
||||
import asyncio
|
||||
import json
|
||||
import random
|
||||
|
||||
_BEHAVIORS_BY_SPECIES: dict[str, list[str]] = {
|
||||
"dog": ["wagging_tail", "stretching", "showing_belly", "barking", "panting"],
|
||||
"cat": ["blep", "stretching", "showing_belly", "purring", "kneading"],
|
||||
"bird": ["head_tilt", "stretching", "chirping", "wing_flap"],
|
||||
}
|
||||
|
||||
_subscribers: dict[int, list[asyncio.Queue]] = {}
|
||||
_worker_tasks: dict[int, asyncio.Task] = {}
|
||||
|
||||
|
||||
async def _behavior_worker(pet_id: int, species: str) -> None:
|
||||
"""Emit random behaviors for one pet until cancelled.
|
||||
|
||||
Args:
|
||||
pet_id (int): Pet whose subscribers receive the events.
|
||||
species (str): Animal species selecting the behavior pool.
|
||||
"""
|
||||
behaviors = _BEHAVIORS_BY_SPECIES.get(species, ["wagging_tail"])
|
||||
while True:
|
||||
action = random.choice(behaviors)
|
||||
data = json.dumps({"action": action})
|
||||
for q in _subscribers.get(pet_id, []):
|
||||
await q.put(data)
|
||||
await asyncio.sleep(random.uniform(1, 5))
|
||||
|
||||
|
||||
def _ensure_worker(pet_id: int, species: str) -> None:
|
||||
"""Start the behavior worker for ``pet_id`` if it is not running.
|
||||
|
||||
Args:
|
||||
pet_id (int): Pet identifier.
|
||||
species (str): Animal species selecting the behavior pool.
|
||||
"""
|
||||
if pet_id not in _worker_tasks or _worker_tasks[pet_id].done():
|
||||
_subscribers.setdefault(pet_id, [])
|
||||
_worker_tasks[pet_id] = asyncio.create_task(_behavior_worker(pet_id, species))
|
||||
|
||||
|
||||
async def subscribe(pet_id: int, species: str) -> asyncio.Queue:
|
||||
"""Subscribe a new listener queue for a pet's action stream.
|
||||
|
||||
Args:
|
||||
pet_id (int): Pet identifier.
|
||||
species (str): Animal species selecting the behavior pool.
|
||||
|
||||
Returns:
|
||||
asyncio.Queue: Queue that receives JSON-encoded action payloads.
|
||||
"""
|
||||
q: asyncio.Queue = asyncio.Queue()
|
||||
_subscribers.setdefault(pet_id, []).append(q)
|
||||
_ensure_worker(pet_id, species)
|
||||
return q
|
||||
|
||||
|
||||
def unsubscribe(pet_id: int, q: asyncio.Queue) -> None:
|
||||
"""Remove a listener queue and stop the worker when the last one leaves.
|
||||
|
||||
Args:
|
||||
pet_id (int): Pet identifier.
|
||||
q (asyncio.Queue): Queue returned by :func:`subscribe`.
|
||||
"""
|
||||
queues = _subscribers.get(pet_id, [])
|
||||
if q in queues:
|
||||
queues.remove(q)
|
||||
if not queues:
|
||||
task = _worker_tasks.pop(pet_id, None)
|
||||
if task and not task.done():
|
||||
task.cancel()
|
||||
_subscribers.pop(pet_id, None)
|
||||
Reference in New Issue
Block a user