Data Model
Database schema and configuration for the PraisonAI Platform
Every workspace, issue, agent, and comment you create is stored in these tables.
Every workspace, issue, agent, and comment you create is stored in these tables.
from praisonaiagents import Agent
agent = Agent(name="architect", instructions="Explain platform entities and relationships.")
agent.start("How do workspaces, projects, and issues relate?")The user maps product concepts to tables; workspaces contain projects, issues, agents, and comments.
Quick Start
- Zero Configuration (Default)
python from praisonai_platform.client import PlatformClient # In-memory SQLite - data lost on restart client = PlatformClient(base_url="http://localhost:8000") client.login(email="me@example.com", password="...") workspace = client.workspaces.create(name="Research") issue = client.issues.create(workspace_id=workspace.id, title="Setup database") - Persistent SQLite
bash # Set database URL for persistence export DATABASE_URL="sqlite+aiosqlite:///./platform.db" # Initialize database tables python -c "import asyncio; from praisonai_platform.db import init_db; asyncio.run(init_db())" - PostgreSQL Production
bash # Switch to PostgreSQL export DATABASE_URL="postgresql+asyncpg://user:pass@host/db" # Initialize tables python -c "import asyncio; from praisonai_platform.db import init_db; asyncio.run(init_db())"
How It Works
| Component | Purpose | Lifecycle |
|---|---|---|
| Engine | Database connection pool | Cached globally, first call wins |
| Session | Per-request transaction | Created/disposed per API call |
| get_db | FastAPI dependency | Yields session, auto-commits |
| init_db | Table creation | Call once for persistent databases |
Entities
User (users)
Authentication and identity for platform access.
| Column | Type | Default | Description |
|---|---|---|---|
id | str | UUID | Primary key |
name | str | — | Username (unique) |
email | str | — | Email address (unique) |
password_hash | str | — | Hashed password |
created_at | datetime | UTC now | Creation timestamp |
updated_at | datetime | UTC now | Last update timestamp |
Workspace (workspaces)
Top-level container for organizing projects and issues.
| Column | Type | Default | Description |
|---|---|---|---|
id | str | UUID | Primary key |
name | str | — | Workspace name |
description | str | None | Optional description |
settings | dict | {} | JSON configuration |
issue_prefix | str | "ISSUE" | Issue numbering prefix |
created_at | datetime | UTC now | Creation timestamp |
updated_at | datetime | UTC now | Last update timestamp |
Member (members)
User membership and roles within workspaces.
| Column | Type | Default | Description |
|---|---|---|---|
id | str | UUID | Primary key |
user_id | str | — | Foreign key to users |
workspace_id | str | — | Foreign key to workspaces |
role | str | "member" | Role: owner, admin, member |
created_at | datetime | UTC now | Creation timestamp |
updated_at | datetime | UTC now | Last update timestamp |
Constraints: Unique (user_id, workspace_id)
Project (projects)
Optional grouping for issues within a workspace.
| Column | Type | Default | Description |
|---|---|---|---|
id | str | UUID | Primary key |
workspace_id | str | — | Foreign key to workspaces |
name | str | — | Project name |
description | str | None | Optional description |
settings | dict | {} | JSON configuration |
created_at | datetime | UTC now | Creation timestamp |
updated_at | datetime | UTC now | Last update timestamp |
Issue (issues)
Work items and tasks within a workspace.
| Column | Type | Default | Description |
|---|---|---|---|
id | str | UUID | Primary key |
workspace_id | str | — | Foreign key to workspaces |
project_id | str | None | Optional foreign key to projects |
title | str | — | Issue title |
description | str | None | Optional description |
status | str | "backlog" | Status: backlog, in-progress, done |
priority | str | "medium" | Priority: low, medium, high, urgent |
assignee_id | str | None | Optional foreign key to users |
created_by_id | str | None | Optional foreign key to users |
issue_number | int | None | Auto-incrementing number |
created_at | datetime | UTC now | Creation timestamp |
updated_at | datetime | UTC now | Last update timestamp |
Comment (comments)
Discussion threads on issues.
| Column | Type | Default | Description |
|---|---|---|---|
id | str | UUID | Primary key |
issue_id | str | — | Foreign key to issues |
author_id | str | None | Optional foreign key to users |
content | str | — | Comment text |
created_at | datetime | UTC now | Creation timestamp |
updated_at | datetime | UTC now | Last update timestamp |
Agent (agents)
AI agents assigned to workspaces.
| Column | Type | Default | Description |
|---|---|---|---|
id | str | UUID | Primary key |
workspace_id | str | — | Foreign key to workspaces |
name | str | — | Agent name |
description | str | None | Optional description |
status | str | "idle" | Status: idle, active, error |
config | dict | {} | JSON configuration |
created_at | datetime | UTC now | Creation timestamp |
updated_at | datetime | UTC now | Last update timestamp |
IssueLabel (issue_labels)
Label definitions for categorizing issues.
| Column | Type | Default | Description |
|---|---|---|---|
id | str | UUID | Primary key |
workspace_id | str | — | Foreign key to workspaces |
name | str | — | Label name |
color | str | "#000000" | Hex color code |
description | str | None | Optional description |
created_at | datetime | UTC now | Creation timestamp |
updated_at | datetime | UTC now | Last update timestamp |
IssueLabelLink (issue_label_links)
Many-to-many relationship between issues and labels.
| Column | Type | Default | Description |
|---|---|---|---|
id | str | UUID | Primary key |
issue_id | str | — | Foreign key to issues |
label_id | str | — | Foreign key to issue_labels |
created_at | datetime | UTC now | Creation timestamp |
Constraints: Unique (issue_id, label_id)
IssueDependency (issue_dependencies)
Dependency relationships between issues.
| Column | Type | Default | Description |
|---|---|---|---|
id | str | UUID | Primary key |
issue_id | str | — | Foreign key to issues |
depends_on_id | str | — | Foreign key to issues |
dependency_type | str | — | Type: blocks, blocked_by, related |
created_at | datetime | UTC now | Creation timestamp |
Constraints: Unique (issue_id, depends_on_id, dependency_type)
ActivityLog (activity_logs)
Audit trail for workspace activities.
| Column | Type | Default | Description |
|---|---|---|---|
id | str | UUID | Primary key |
workspace_id | str | — | Foreign key to workspaces |
entity_type | str | — | Entity type: issue, project, workspace |
entity_id | str | — | ID of the affected entity |
action | str | — | Action: created, updated, deleted |
actor_id | str | None | Optional foreign key to users |
details | dict | {} | JSON details |
created_at | datetime | UTC now | Creation timestamp |
Configuration Options
| Name | Where | Default | Description |
|---|---|---|---|
DATABASE_URL | env var | sqlite+aiosqlite:///:memory: | SQLAlchemy async URL used by get_engine() |
get_engine(database_url=None) | praisonai_platform.db | — | Returns the cached AsyncEngine; first call wins, pass database_url to override env |
get_session() | praisonai_platform.db | — | Async generator yielding an AsyncSession per request |
init_db() | praisonai_platform.db | — | Creates all tables via Base.metadata.create_all |
reset_engine() | praisonai_platform.db | — | Disposes and clears the cached engine (tests / URL switch) |
Common Patterns
Running One-Off Queries
import asyncio
from praisonai_platform.db import get_session, init_db
from praisonai_platform.db.models import Workspace
from sqlalchemy import select
async def count_workspaces():
await init_db() # Ensure tables exist
async for session in get_session():
result = await session.execute(select(Workspace))
workspaces = result.scalars().all()
print(f"Found {len(workspaces)} workspaces")
break
asyncio.run(count_workspaces())Switching to PostgreSQL in Production
# 1. Install driver
pip install asyncpg
# 2. Set database URL
export DATABASE_URL="postgresql+asyncpg://user:password@localhost/platform"
# 3. Initialize tables
python -c "import asyncio; from praisonai_platform.db import init_db; asyncio.run(init_db())"
# 4. Start platform server
python -m praisonai_platformResetting Engine in Tests
import pytest
from praisonai_platform.db import reset_engine, init_db
@pytest.fixture(autouse=True)
async def clean_db():
await reset_engine() # Clear cached engine
await init_db() # Create fresh tables
yield
await reset_engine() # Clean upBest Practices
Initialize Database Once
For non-memory databases, call init_db() once at application startup, not per request. The default in-memory SQLite creates tables automatically.
# In your app startup
import asyncio
from praisonai_platform.db import init_db
if __name__ == "__main__":
asyncio.run(init_db()) # Only for persistent databases
# Start your server...Don't Mix Database URLs
The engine is cached globally – if you need to switch DATABASE_URL values, call reset_engine() first to avoid connection pool conflicts.
from praisonai_platform.db import reset_engine, get_engine
# Switch from SQLite to Postgres
await reset_engine()
engine = get_engine("postgresql+asyncpg://user:pass@host/db")Keep SQLite for Development Only
Use SQLite for development and testing, but switch to PostgreSQL for production workloads. SQLite doesn’t handle concurrent writes well in server environments.
# Development
export DATABASE_URL="sqlite+aiosqlite:///./dev.db"
# Production
export DATABASE_URL="postgresql+asyncpg://user:pass@prod-host/platform"Use Sessions Properly
Always use the get_db dependency in FastAPI routes – it handles session lifecycle, commits, and rollbacks automatically. Don’t create sessions manually.
from fastapi import Depends
from praisonai_platform.api.deps import get_db
@app.get("/workspaces")
async def list_workspaces(session: AsyncSession = Depends(get_db)):
# Session is auto-managed
return await workspace_service.list_all(session)