Implement Phase 3: dynamic graph builder, blueprint persistence, and CRUD API
- Add dynamic_graph_builder.py that constructs LangGraph graphs at runtime
from frontend CouncilBlueprint JSON (no more hardcoded graphs in production)
- Add PostgreSQL persistence via SQLAlchemy async with Blueprint model
- Add blueprint CRUD endpoints (POST/GET/PUT/DELETE /api/councils/)
- Add POST /api/councils/{id}/run to execute blueprints dynamically
- Add Alembic migration infrastructure with initial blueprints table
- Add database.py with async engine and SQLite fallback for dev/test
- Fix missing typing-extensions and add aiosqlite dependency
- Add 42 new tests (80/80 total passing) covering dynamic graph building,
blueprint service CRUD, and API integration
https://claude.ai/code/session_014yZUxrPsgZbvkebXbCXR4U
This commit is contained in:
parent
89ba3aacd4
commit
437db4ca68
16 changed files with 1698 additions and 11 deletions
62
backend/models/blueprint.py
Normal file
62
backend/models/blueprint.py
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
"""
|
||||
Blueprint model — stores council blueprints as JSON in PostgreSQL.
|
||||
|
||||
Each blueprint represents a complete council graph configuration created
|
||||
by the user in the "Rat-Architekt" (Setup Mode) frontend tab.
|
||||
"""
|
||||
|
||||
import uuid
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from sqlalchemy import Column, DateTime, Integer, JSON, String
|
||||
from sqlalchemy.orm import DeclarativeBase
|
||||
|
||||
|
||||
class Base(DeclarativeBase):
|
||||
"""SQLAlchemy declarative base for all models."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class Blueprint(Base):
|
||||
"""
|
||||
A council blueprint stored in PostgreSQL.
|
||||
|
||||
The nodes and edges are stored as JSON columns matching the
|
||||
CouncilBlueprint TypeScript interface from the frontend.
|
||||
"""
|
||||
|
||||
__tablename__ = "blueprints"
|
||||
|
||||
id = Column(
|
||||
String(36),
|
||||
primary_key=True,
|
||||
default=lambda: str(uuid.uuid4()),
|
||||
)
|
||||
version = Column(Integer, nullable=False, default=1)
|
||||
name = Column(String(255), nullable=False)
|
||||
nodes = Column(JSON, nullable=False, default=list)
|
||||
edges = Column(JSON, nullable=False, default=list)
|
||||
created_at = Column(
|
||||
DateTime(timezone=True),
|
||||
nullable=False,
|
||||
default=lambda: datetime.now(timezone.utc),
|
||||
)
|
||||
updated_at = Column(
|
||||
DateTime(timezone=True),
|
||||
nullable=False,
|
||||
default=lambda: datetime.now(timezone.utc),
|
||||
onupdate=lambda: datetime.now(timezone.utc),
|
||||
)
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
"""Serialize to the CouncilBlueprint JSON format expected by the frontend."""
|
||||
return {
|
||||
"id": self.id,
|
||||
"version": self.version,
|
||||
"name": self.name,
|
||||
"nodes": self.nodes,
|
||||
"edges": self.edges,
|
||||
"createdAt": self.created_at.isoformat() if self.created_at else None,
|
||||
"updatedAt": self.updated_at.isoformat() if self.updated_at else None,
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue