103 lines
2.8 KiB
Python
103 lines
2.8 KiB
Python
"""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)
|