"""Holon management endpoints.""" import uuid from fastapi import APIRouter, HTTPException from pydantic import BaseModel from spore_node.db.connection import get_pool from spore_node.rid_types import SporeHolon router = APIRouter(prefix="/holons", tags=["holons"]) class HolonCreate(BaseModel): slug: str name: str holon_type: str = "agent" description: str = "" membrane_config: dict = {} metadata: dict = {} class HolonResponse(BaseModel): id: str rid: str slug: str name: str holon_type: str description: str membrane_config: dict metadata: dict @router.post("", response_model=HolonResponse, status_code=201) async def create_holon(data: HolonCreate): """Register a new holon in the commons.""" pool = get_pool() rid = str(SporeHolon(data.slug)) try: row = await pool.fetchrow( """ INSERT INTO holons (rid, slug, name, holon_type, description, membrane_config, metadata) VALUES ($1, $2, $3, $4, $5, $6::jsonb, $7::jsonb) RETURNING id, rid, slug, name, holon_type, description, membrane_config, metadata """, rid, data.slug, data.name, data.holon_type, data.description, _json(data.membrane_config), _json(data.metadata), ) except Exception as e: if "unique" in str(e).lower(): raise HTTPException(409, f"Holon '{data.slug}' already exists") raise # Log event await pool.execute( """ INSERT INTO events (entity_rid, event_kind, payload) VALUES ($1, 'holon.created', $2::jsonb) """, rid, _json({"slug": data.slug, "name": data.name}), ) return _row_to_dict(row) @router.get("", response_model=list[HolonResponse]) async def list_holons(holon_type: str | None = None): """List all holons, optionally filtered by type.""" pool = get_pool() if holon_type: rows = await pool.fetch( "SELECT * FROM holons WHERE holon_type = $1 ORDER BY created_at", holon_type, ) else: rows = await pool.fetch("SELECT * FROM holons ORDER BY created_at") return [_row_to_dict(r) for r in rows] @router.get("/{slug}", response_model=HolonResponse) async def get_holon(slug: str): """Get a single holon by slug.""" pool = get_pool() row = await pool.fetchrow("SELECT * FROM holons WHERE slug = $1", slug) if not row: raise HTTPException(404, f"Holon '{slug}' not found") return _row_to_dict(row) def _row_to_dict(row) -> dict: import json d = dict(row) d["id"] = str(d["id"]) for k in ("membrane_config", "metadata"): if isinstance(d[k], str): d[k] = json.loads(d[k]) return d def _json(obj: dict) -> str: import json return json.dumps(obj)