feat: align agent/microdao model - add is_orchestrator, is_platform, hierarchy
This commit is contained in:
139
docs/internal/architecture/DAARION_AGENT_MICRODAO_MODEL_v1.md
Normal file
139
docs/internal/architecture/DAARION_AGENT_MICRODAO_MODEL_v1.md
Normal file
@@ -0,0 +1,139 @@
|
||||
# DAARION Agent & MicroDAO Model v1
|
||||
|
||||
## Overview
|
||||
|
||||
This document describes the unified data model for Agents and MicroDAOs in the DAARION ecosystem.
|
||||
|
||||
## Core Hierarchy
|
||||
|
||||
```
|
||||
Node → Agent → MicroDAO
|
||||
↓
|
||||
Platform (District)
|
||||
```
|
||||
|
||||
- **Node**: Physical/virtual infrastructure where agents run (NODE1, NODE2)
|
||||
- **Agent**: AI entity with identity, capabilities, and affiliations
|
||||
- **MicroDAO**: Organization/community of agents with shared goals
|
||||
- **Platform/District**: Top-level MicroDAO that acts as a category/district
|
||||
|
||||
## Agent Model
|
||||
|
||||
### Key Fields
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `id` | text | Unique identifier |
|
||||
| `slug` | text | URL-friendly identifier |
|
||||
| `display_name` | text | Human-readable name |
|
||||
| `kind` | text | Type: orchestrator, security, marketing, etc. |
|
||||
| `node_id` | text | Home node where agent runs |
|
||||
| `is_public` | boolean | Visible in public Citizens catalog |
|
||||
| `visibility_scope` | text | Access level: global, microdao, private |
|
||||
| `is_orchestrator` | boolean | Can create/manage microDAOs |
|
||||
| `primary_microdao_id` | text | Primary organization affiliation |
|
||||
|
||||
### Visibility Scope Values
|
||||
|
||||
- **global**: Visible to everyone in the city
|
||||
- **microdao**: Visible only to MicroDAO members
|
||||
- **private**: Visible only to owner/admin
|
||||
|
||||
### Agent Types by Kind
|
||||
|
||||
- `orchestrator`: MicroDAO leaders, can manage organizations
|
||||
- `security`: Security and audit agents
|
||||
- `marketing`: Marketing and communication agents
|
||||
- `developer`: Development and technical agents
|
||||
- `research`: Research and analysis agents
|
||||
- `finance`: Financial management agents
|
||||
- `system`: Infrastructure and monitoring agents
|
||||
|
||||
## MicroDAO Model
|
||||
|
||||
### Key Fields
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `id` | text | Unique identifier |
|
||||
| `slug` | text | URL-friendly identifier |
|
||||
| `name` | text | Display name |
|
||||
| `is_public` | boolean | Visible in public services |
|
||||
| `is_platform` | boolean | Is a platform/district (top-level) |
|
||||
| `orchestrator_agent_id` | text | Main orchestrator agent |
|
||||
| `parent_microdao_id` | text | Parent for hierarchy |
|
||||
| `district` | text | District/category name |
|
||||
|
||||
### MicroDAO Types
|
||||
|
||||
- **Platform** (`is_platform = true`): Top-level organizational unit (district)
|
||||
- **Regular** (`is_platform = false`): Standard MicroDAO under a platform
|
||||
|
||||
### Hierarchy
|
||||
|
||||
```
|
||||
Platform (District)
|
||||
├── MicroDAO 1
|
||||
│ ├── Agent A (orchestrator)
|
||||
│ ├── Agent B (member)
|
||||
│ └── Agent C (member)
|
||||
└── MicroDAO 2
|
||||
├── Agent D (orchestrator)
|
||||
└── Agent E (member)
|
||||
```
|
||||
|
||||
## UI Mapping
|
||||
|
||||
### Agent Console (`/agents`)
|
||||
- Technical view of all agents
|
||||
- Shows: node_id, visibility_scope, is_orchestrator
|
||||
- Filters: kind, node_id, microdao_id, is_public
|
||||
|
||||
### Citizens (`/citizens`)
|
||||
- Public view of agents (`is_public = true`)
|
||||
- Shows: display_name, title, tagline, skills
|
||||
- Filters: district, kind, search
|
||||
|
||||
### MicroDAO Dashboard (`/microdao`)
|
||||
- Organization management
|
||||
- Shows: member_count, orchestrator, channels
|
||||
- Filters: district, is_platform, is_public
|
||||
|
||||
## API Endpoints
|
||||
|
||||
### Agents
|
||||
```
|
||||
GET /city/agents
|
||||
?kind=orchestrator
|
||||
&node_id=node-1-hetzner-gex44
|
||||
µdao_id=dao_daarion
|
||||
&is_public=true
|
||||
&visibility_scope=global
|
||||
&include_system=false
|
||||
```
|
||||
|
||||
### MicroDAOs
|
||||
```
|
||||
GET /city/microdao
|
||||
?district=Core
|
||||
&is_public=true
|
||||
&is_platform=false
|
||||
&q=search
|
||||
|
||||
GET /city/microdao/{slug}
|
||||
```
|
||||
|
||||
## Important Notes
|
||||
|
||||
1. **Citizen ≠ separate entity**: Citizens are just public agents (`is_public = true`)
|
||||
2. **Every agent needs MicroDAO**: Active agents must belong to at least one MicroDAO
|
||||
3. **Orchestrators**: Agents with `is_orchestrator = true` can manage MicroDAOs
|
||||
4. **Soft delete**: Use `is_archived`, `is_test`, `deleted_at` instead of hard delete
|
||||
|
||||
## Related Files
|
||||
|
||||
- Models: `services/city-service/models_city.py`
|
||||
- Repository: `services/city-service/repo_city.py`
|
||||
- Routes: `services/city-service/routes_city.py`
|
||||
- Migrations: `migrations/026_align_agent_microdao_model.sql`
|
||||
|
||||
91
migrations/026_align_agent_microdao_model.sql
Normal file
91
migrations/026_align_agent_microdao_model.sql
Normal file
@@ -0,0 +1,91 @@
|
||||
-- Migration: Align Agent/MicroDAO model
|
||||
-- Purpose: Standardize fields for Agent Console, Citizens, MicroDAO Dashboard
|
||||
-- Date: 2025-11-28
|
||||
|
||||
-- ============================================================================
|
||||
-- AGENTS TABLE
|
||||
-- ============================================================================
|
||||
|
||||
-- Add is_orchestrator flag (agent can create/manage microDAOs)
|
||||
ALTER TABLE agents
|
||||
ADD COLUMN IF NOT EXISTS is_orchestrator boolean NOT NULL DEFAULT false;
|
||||
|
||||
-- Create index for orchestrator lookup
|
||||
CREATE INDEX IF NOT EXISTS idx_agents_is_orchestrator ON agents(is_orchestrator) WHERE is_orchestrator = true;
|
||||
|
||||
-- Update existing orchestrators based on kind
|
||||
UPDATE agents
|
||||
SET is_orchestrator = true
|
||||
WHERE kind = 'orchestrator'
|
||||
AND is_orchestrator = false;
|
||||
|
||||
-- ============================================================================
|
||||
-- MICRODAOS TABLE
|
||||
-- ============================================================================
|
||||
|
||||
-- Add is_platform flag (microDAO is a platform/district)
|
||||
ALTER TABLE microdaos
|
||||
ADD COLUMN IF NOT EXISTS is_platform boolean NOT NULL DEFAULT false;
|
||||
|
||||
-- Add orchestrator_agent_id as alias/copy of owner_agent_id for clarity
|
||||
-- (keeping owner_agent_id for backward compatibility)
|
||||
ALTER TABLE microdaos
|
||||
ADD COLUMN IF NOT EXISTS orchestrator_agent_id text;
|
||||
|
||||
-- Copy owner_agent_id to orchestrator_agent_id where not set
|
||||
UPDATE microdaos
|
||||
SET orchestrator_agent_id = owner_agent_id
|
||||
WHERE orchestrator_agent_id IS NULL
|
||||
AND owner_agent_id IS NOT NULL;
|
||||
|
||||
-- Add parent_microdao_id for hierarchy (platform -> child microDAOs)
|
||||
ALTER TABLE microdaos
|
||||
ADD COLUMN IF NOT EXISTS parent_microdao_id text;
|
||||
|
||||
-- Create indexes
|
||||
CREATE INDEX IF NOT EXISTS idx_microdaos_is_platform ON microdaos(is_platform) WHERE is_platform = true;
|
||||
CREATE INDEX IF NOT EXISTS idx_microdaos_orchestrator ON microdaos(orchestrator_agent_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_microdaos_parent ON microdaos(parent_microdao_id);
|
||||
|
||||
-- Add foreign key for parent_microdao_id (self-reference)
|
||||
-- Note: Using DO block to handle if constraint already exists
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM pg_constraint WHERE conname = 'microdaos_parent_fk'
|
||||
) THEN
|
||||
ALTER TABLE microdaos
|
||||
ADD CONSTRAINT microdaos_parent_fk
|
||||
FOREIGN KEY (parent_microdao_id)
|
||||
REFERENCES microdaos(id)
|
||||
ON DELETE SET NULL;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- Add foreign key for orchestrator_agent_id
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM pg_constraint WHERE conname = 'microdaos_orchestrator_agent_fk'
|
||||
) THEN
|
||||
ALTER TABLE microdaos
|
||||
ADD CONSTRAINT microdaos_orchestrator_agent_fk
|
||||
FOREIGN KEY (orchestrator_agent_id)
|
||||
REFERENCES agents(id)
|
||||
ON DELETE SET NULL;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- ============================================================================
|
||||
-- COMMENTS
|
||||
-- ============================================================================
|
||||
|
||||
COMMENT ON COLUMN agents.is_orchestrator IS 'Agent can create and manage microDAOs';
|
||||
COMMENT ON COLUMN agents.visibility_scope IS 'Visibility: global (everyone), microdao (members only), private (owner only)';
|
||||
COMMENT ON COLUMN agents.is_public IS 'Agent visible in public Citizens catalog';
|
||||
COMMENT ON COLUMN agents.primary_microdao_id IS 'Primary microDAO affiliation';
|
||||
|
||||
COMMENT ON COLUMN microdaos.is_platform IS 'MicroDAO is a platform/district (top-level organization)';
|
||||
COMMENT ON COLUMN microdaos.orchestrator_agent_id IS 'Main orchestrator agent for this microDAO';
|
||||
COMMENT ON COLUMN microdaos.parent_microdao_id IS 'Parent microDAO for hierarchy (platform -> child)';
|
||||
|
||||
@@ -233,6 +233,8 @@ class MicrodaoBadge(BaseModel):
|
||||
name: str
|
||||
slug: Optional[str] = None
|
||||
role: Optional[str] = None # orchestrator, member, etc.
|
||||
is_public: bool = True
|
||||
is_platform: bool = False
|
||||
|
||||
|
||||
class AgentSummary(BaseModel):
|
||||
@@ -251,11 +253,12 @@ class AgentSummary(BaseModel):
|
||||
node_label: Optional[str] = None # "НОДА1" / "НОДА2"
|
||||
home_node: Optional[HomeNodeView] = None
|
||||
|
||||
# Visibility
|
||||
visibility_scope: str = "city" # city, microdao, owner_only
|
||||
# Visibility & roles
|
||||
visibility_scope: str = "city" # global, microdao, private
|
||||
is_listed_in_directory: bool = True
|
||||
is_system: bool = False
|
||||
is_public: bool = False # backward compatibility
|
||||
is_public: bool = False
|
||||
is_orchestrator: bool = False # Can create/manage microDAOs
|
||||
|
||||
# MicroDAO
|
||||
primary_microdao_id: Optional[str] = None
|
||||
@@ -354,12 +357,27 @@ class MicrodaoSummary(BaseModel):
|
||||
name: str
|
||||
description: Optional[str] = None
|
||||
district: Optional[str] = None
|
||||
|
||||
# Visibility & type
|
||||
is_public: bool = True
|
||||
is_platform: bool = False # Is a platform/district
|
||||
is_active: bool = True
|
||||
|
||||
# Orchestrator
|
||||
orchestrator_agent_id: Optional[str] = None
|
||||
is_active: bool
|
||||
orchestrator_agent_name: Optional[str] = None
|
||||
|
||||
# Hierarchy
|
||||
parent_microdao_id: Optional[str] = None
|
||||
parent_microdao_slug: Optional[str] = None
|
||||
|
||||
# Stats
|
||||
logo_url: Optional[str] = None
|
||||
agents_count: int
|
||||
rooms_count: int
|
||||
channels_count: int
|
||||
member_count: int = 0 # alias for agents_count
|
||||
agents_count: int = 0 # backward compatibility
|
||||
room_count: int = 0 # alias for rooms_count
|
||||
rooms_count: int = 0 # backward compatibility
|
||||
channels_count: int = 0
|
||||
|
||||
|
||||
class MicrodaoChannelView(BaseModel):
|
||||
@@ -385,13 +403,25 @@ class MicrodaoDetail(BaseModel):
|
||||
name: str
|
||||
description: Optional[str] = None
|
||||
district: Optional[str] = None
|
||||
|
||||
# Visibility & type
|
||||
is_public: bool = True
|
||||
is_platform: bool = False
|
||||
is_active: bool = True
|
||||
|
||||
# Orchestrator
|
||||
orchestrator_agent_id: Optional[str] = None
|
||||
orchestrator_display_name: Optional[str] = None
|
||||
is_active: bool
|
||||
is_public: bool
|
||||
|
||||
# Hierarchy
|
||||
parent_microdao_id: Optional[str] = None
|
||||
parent_microdao_slug: Optional[str] = None
|
||||
child_microdaos: List["MicrodaoSummary"] = []
|
||||
|
||||
# Content
|
||||
logo_url: Optional[str] = None
|
||||
agents: List[MicrodaoAgentView]
|
||||
channels: List[MicrodaoChannelView]
|
||||
agents: List[MicrodaoAgentView] = []
|
||||
channels: List[MicrodaoChannelView] = []
|
||||
public_citizens: List[MicrodaoCitizenView] = []
|
||||
|
||||
|
||||
|
||||
@@ -295,6 +295,8 @@ async def get_rooms_for_map() -> List[dict]:
|
||||
async def list_agent_summaries(
|
||||
*,
|
||||
node_id: Optional[str] = None,
|
||||
microdao_id: Optional[str] = None,
|
||||
is_public: Optional[bool] = None,
|
||||
visibility_scope: Optional[str] = None,
|
||||
listed_only: Optional[bool] = None,
|
||||
kinds: Optional[List[str]] = None,
|
||||
@@ -322,6 +324,14 @@ async def list_agent_summaries(
|
||||
params.append(node_id)
|
||||
where_clauses.append(f"a.node_id = ${len(params)}")
|
||||
|
||||
if microdao_id:
|
||||
params.append(microdao_id)
|
||||
where_clauses.append(f"EXISTS (SELECT 1 FROM microdao_agents ma WHERE ma.agent_id = a.id AND ma.microdao_id = ${len(params)})")
|
||||
|
||||
if is_public is not None:
|
||||
params.append(is_public)
|
||||
where_clauses.append(f"COALESCE(a.is_public, false) = ${len(params)}")
|
||||
|
||||
if visibility_scope:
|
||||
params.append(visibility_scope)
|
||||
where_clauses.append(f"COALESCE(a.visibility_scope, 'city') = ${len(params)}")
|
||||
@@ -359,6 +369,7 @@ async def list_agent_summaries(
|
||||
COALESCE(a.is_listed_in_directory, true) AS is_listed_in_directory,
|
||||
COALESCE(a.is_system, false) AS is_system,
|
||||
COALESCE(a.is_public, false) AS is_public,
|
||||
COALESCE(a.is_orchestrator, false) AS is_orchestrator,
|
||||
a.primary_microdao_id,
|
||||
pm.name AS primary_microdao_name,
|
||||
pm.slug AS primary_microdao_slug,
|
||||
@@ -1246,17 +1257,26 @@ async def get_microdaos(district: Optional[str] = None, q: Optional[str] = None,
|
||||
m.name,
|
||||
m.description,
|
||||
m.district,
|
||||
m.owner_agent_id as orchestrator_agent_id,
|
||||
COALESCE(m.orchestrator_agent_id, m.owner_agent_id) as orchestrator_agent_id,
|
||||
oa.display_name as orchestrator_agent_name,
|
||||
m.is_active,
|
||||
COALESCE(m.is_public, true) as is_public,
|
||||
COALESCE(m.is_platform, false) as is_platform,
|
||||
m.parent_microdao_id,
|
||||
pm.slug as parent_microdao_slug,
|
||||
m.logo_url,
|
||||
COUNT(DISTINCT ma.agent_id) AS agents_count,
|
||||
COUNT(DISTINCT ma.agent_id) AS member_count,
|
||||
COUNT(DISTINCT mc.id) AS channels_count,
|
||||
COUNT(DISTINCT CASE WHEN mc.kind = 'city_room' THEN mc.id END) AS rooms_count
|
||||
COUNT(DISTINCT CASE WHEN mc.kind = 'city_room' THEN mc.id END) AS rooms_count,
|
||||
COUNT(DISTINCT CASE WHEN mc.kind = 'city_room' THEN mc.id END) AS room_count
|
||||
FROM microdaos m
|
||||
LEFT JOIN microdao_agents ma ON ma.microdao_id = m.id
|
||||
LEFT JOIN microdao_channels mc ON mc.microdao_id = m.id
|
||||
LEFT JOIN agents oa ON COALESCE(m.orchestrator_agent_id, m.owner_agent_id) = oa.id
|
||||
LEFT JOIN microdaos pm ON m.parent_microdao_id = pm.id
|
||||
WHERE {where_sql}
|
||||
GROUP BY m.id
|
||||
GROUP BY m.id, oa.display_name, pm.slug
|
||||
ORDER BY m.name
|
||||
LIMIT ${len(params) + 1} OFFSET ${len(params) + 2}
|
||||
"""
|
||||
@@ -1269,6 +1289,93 @@ async def get_microdaos(district: Optional[str] = None, q: Optional[str] = None,
|
||||
return [dict(row) for row in rows]
|
||||
|
||||
|
||||
async def list_microdao_summaries(
|
||||
*,
|
||||
is_public: Optional[bool] = None,
|
||||
is_platform: Optional[bool] = None,
|
||||
district: Optional[str] = None,
|
||||
q: Optional[str] = None,
|
||||
limit: int = 50,
|
||||
offset: int = 0
|
||||
) -> List[dict]:
|
||||
"""
|
||||
Unified method to list microDAOs.
|
||||
Wraps get_microdaos with additional filtering.
|
||||
"""
|
||||
pool = await get_pool()
|
||||
|
||||
params = []
|
||||
where_clauses = [
|
||||
"COALESCE(m.is_archived, false) = false",
|
||||
"COALESCE(m.is_test, false) = false",
|
||||
"m.deleted_at IS NULL",
|
||||
"m.is_active = true"
|
||||
]
|
||||
|
||||
if is_public is not None:
|
||||
params.append(is_public)
|
||||
where_clauses.append(f"COALESCE(m.is_public, true) = ${len(params)}")
|
||||
|
||||
if is_platform is not None:
|
||||
params.append(is_platform)
|
||||
where_clauses.append(f"COALESCE(m.is_platform, false) = ${len(params)}")
|
||||
|
||||
if district:
|
||||
params.append(district)
|
||||
where_clauses.append(f"m.district = ${len(params)}")
|
||||
|
||||
if q:
|
||||
params.append(f"%{q}%")
|
||||
where_clauses.append(f"(m.name ILIKE ${len(params)} OR m.description ILIKE ${len(params)})")
|
||||
|
||||
where_sql = " AND ".join(where_clauses)
|
||||
|
||||
query = f"""
|
||||
SELECT
|
||||
m.id,
|
||||
m.slug,
|
||||
m.name,
|
||||
m.description,
|
||||
m.district,
|
||||
COALESCE(m.orchestrator_agent_id, m.owner_agent_id) as orchestrator_agent_id,
|
||||
oa.display_name as orchestrator_agent_name,
|
||||
m.is_active,
|
||||
COALESCE(m.is_public, true) as is_public,
|
||||
COALESCE(m.is_platform, false) as is_platform,
|
||||
m.parent_microdao_id,
|
||||
pm.slug as parent_microdao_slug,
|
||||
m.logo_url,
|
||||
COUNT(DISTINCT ma.agent_id) AS agents_count,
|
||||
COUNT(DISTINCT ma.agent_id) AS member_count,
|
||||
COUNT(DISTINCT mc.id) AS channels_count,
|
||||
COUNT(DISTINCT CASE WHEN mc.kind = 'city_room' THEN mc.id END) AS rooms_count,
|
||||
COUNT(DISTINCT CASE WHEN mc.kind = 'city_room' THEN mc.id END) AS room_count
|
||||
FROM microdaos m
|
||||
LEFT JOIN microdao_agents ma ON ma.microdao_id = m.id
|
||||
LEFT JOIN microdao_channels mc ON mc.microdao_id = m.id
|
||||
LEFT JOIN agents oa ON COALESCE(m.orchestrator_agent_id, m.owner_agent_id) = oa.id
|
||||
LEFT JOIN microdaos pm ON m.parent_microdao_id = pm.id
|
||||
WHERE {where_sql}
|
||||
GROUP BY m.id, oa.display_name, pm.slug
|
||||
ORDER BY m.name
|
||||
LIMIT ${len(params) + 1} OFFSET ${len(params) + 2}
|
||||
"""
|
||||
|
||||
params.append(limit)
|
||||
params.append(offset)
|
||||
|
||||
rows = await pool.fetch(query, *params)
|
||||
return [dict(row) for row in rows]
|
||||
|
||||
|
||||
async def get_microdao_detail(slug: str) -> Optional[dict]:
|
||||
"""
|
||||
Get detailed microDAO info including agents, channels, children.
|
||||
Alias for get_microdao_by_slug with clearer naming.
|
||||
"""
|
||||
return await get_microdao_by_slug(slug)
|
||||
|
||||
|
||||
async def get_microdao_by_slug(slug: str) -> Optional[dict]:
|
||||
"""Отримати детальну інформацію про MicroDAO"""
|
||||
pool = await get_pool()
|
||||
@@ -1281,14 +1388,21 @@ async def get_microdao_by_slug(slug: str) -> Optional[dict]:
|
||||
m.name,
|
||||
m.description,
|
||||
m.district,
|
||||
m.owner_agent_id as orchestrator_agent_id,
|
||||
COALESCE(m.orchestrator_agent_id, m.owner_agent_id) as orchestrator_agent_id,
|
||||
a.display_name as orchestrator_display_name,
|
||||
m.is_active,
|
||||
m.is_public,
|
||||
m.logo_url,
|
||||
a.display_name as orchestrator_display_name
|
||||
COALESCE(m.is_public, true) as is_public,
|
||||
COALESCE(m.is_platform, false) as is_platform,
|
||||
m.parent_microdao_id,
|
||||
pm.slug as parent_microdao_slug,
|
||||
m.logo_url
|
||||
FROM microdaos m
|
||||
LEFT JOIN agents a ON m.owner_agent_id = a.id
|
||||
WHERE m.slug = $1 AND COALESCE(m.is_archived, false) = false
|
||||
LEFT JOIN agents a ON COALESCE(m.orchestrator_agent_id, m.owner_agent_id) = a.id
|
||||
LEFT JOIN microdaos pm ON m.parent_microdao_id = pm.id
|
||||
WHERE m.slug = $1
|
||||
AND COALESCE(m.is_archived, false) = false
|
||||
AND COALESCE(m.is_test, false) = false
|
||||
AND m.deleted_at IS NULL
|
||||
"""
|
||||
|
||||
dao_row = await pool.fetchrow(query_dao, slug)
|
||||
@@ -1308,6 +1422,9 @@ async def get_microdao_by_slug(slug: str) -> Optional[dict]:
|
||||
FROM microdao_agents ma
|
||||
JOIN agents a ON ma.agent_id = a.id
|
||||
WHERE ma.microdao_id = $1
|
||||
AND COALESCE(a.is_archived, false) = false
|
||||
AND COALESCE(a.is_test, false) = false
|
||||
AND a.deleted_at IS NULL
|
||||
ORDER BY ma.is_core DESC, ma.role
|
||||
"""
|
||||
agents_rows = await pool.fetch(query_agents, dao_id)
|
||||
@@ -1327,6 +1444,20 @@ async def get_microdao_by_slug(slug: str) -> Optional[dict]:
|
||||
channels_rows = await pool.fetch(query_channels, dao_id)
|
||||
result["channels"] = [dict(row) for row in channels_rows]
|
||||
|
||||
# 4. Get child microDAOs
|
||||
query_children = """
|
||||
SELECT id, slug, name, COALESCE(is_public, true) as is_public,
|
||||
COALESCE(is_platform, false) as is_platform
|
||||
FROM microdaos
|
||||
WHERE parent_microdao_id = $1
|
||||
AND COALESCE(is_archived, false) = false
|
||||
AND COALESCE(is_test, false) = false
|
||||
AND deleted_at IS NULL
|
||||
ORDER BY name
|
||||
"""
|
||||
children_rows = await pool.fetch(query_children, dao_id)
|
||||
result["child_microdaos"] = [dict(row) for row in children_rows]
|
||||
|
||||
public_citizens = await get_microdao_public_citizens(dao_id)
|
||||
result["public_citizens"] = public_citizens
|
||||
|
||||
|
||||
@@ -68,7 +68,9 @@ class MicrodaoMembershipPayload(BaseModel):
|
||||
async def list_agents(
|
||||
kind: Optional[str] = Query(None, description="Filter by agent kind"),
|
||||
node_id: Optional[str] = Query(None, description="Filter by node_id"),
|
||||
visibility_scope: Optional[str] = Query(None, description="Filter by visibility: city, microdao, owner_only"),
|
||||
microdao_id: Optional[str] = Query(None, description="Filter by microDAO id"),
|
||||
is_public: Optional[bool] = Query(None, description="Filter by public status"),
|
||||
visibility_scope: Optional[str] = Query(None, description="Filter by visibility: global, microdao, private"),
|
||||
include_system: bool = Query(True, description="Include system agents"),
|
||||
limit: int = Query(100, le=200),
|
||||
offset: int = Query(0, ge=0)
|
||||
@@ -78,6 +80,8 @@ async def list_agents(
|
||||
kinds_list = [kind] if kind else None
|
||||
agents, total = await repo_city.list_agent_summaries(
|
||||
node_id=node_id,
|
||||
microdao_id=microdao_id,
|
||||
is_public=is_public,
|
||||
visibility_scope=visibility_scope,
|
||||
kinds=kinds_list,
|
||||
include_system=include_system,
|
||||
@@ -126,6 +130,7 @@ async def list_agents(
|
||||
is_listed_in_directory=agent.get("is_listed_in_directory", True),
|
||||
is_system=agent.get("is_system", False),
|
||||
is_public=agent.get("is_public", False),
|
||||
is_orchestrator=agent.get("is_orchestrator", False),
|
||||
primary_microdao_id=agent.get("primary_microdao_id"),
|
||||
primary_microdao_name=agent.get("primary_microdao_name"),
|
||||
primary_microdao_slug=agent.get("primary_microdao_slug"),
|
||||
@@ -1320,6 +1325,8 @@ async def get_agents_presence_snapshot():
|
||||
@router.get("/microdao", response_model=List[MicrodaoSummary])
|
||||
async def get_microdaos(
|
||||
district: Optional[str] = Query(None, description="Filter by district"),
|
||||
is_public: Optional[bool] = Query(None, description="Filter by public status"),
|
||||
is_platform: Optional[bool] = Query(None, description="Filter by platform status"),
|
||||
q: Optional[str] = Query(None, description="Search by name/description"),
|
||||
limit: int = Query(50, le=100),
|
||||
offset: int = Query(0, ge=0)
|
||||
@@ -1328,10 +1335,19 @@ async def get_microdaos(
|
||||
Отримати список MicroDAOs.
|
||||
|
||||
- **district**: фільтр по дістрікту (Core, Energy, Green, Labs, etc.)
|
||||
- **is_public**: фільтр по публічності
|
||||
- **is_platform**: фільтр по типу (платформа/дістрікт)
|
||||
- **q**: пошук по назві або опису
|
||||
"""
|
||||
try:
|
||||
daos = await repo_city.get_microdaos(district=district, q=q, limit=limit, offset=offset)
|
||||
daos = await repo_city.list_microdao_summaries(
|
||||
district=district,
|
||||
is_public=is_public,
|
||||
is_platform=is_platform,
|
||||
q=q,
|
||||
limit=limit,
|
||||
offset=offset
|
||||
)
|
||||
|
||||
result = []
|
||||
for dao in daos:
|
||||
@@ -1341,10 +1357,17 @@ async def get_microdaos(
|
||||
name=dao["name"],
|
||||
description=dao.get("description"),
|
||||
district=dao.get("district"),
|
||||
orchestrator_agent_id=dao.get("orchestrator_agent_id"),
|
||||
is_public=dao.get("is_public", True),
|
||||
is_platform=dao.get("is_platform", False),
|
||||
is_active=dao.get("is_active", True),
|
||||
orchestrator_agent_id=dao.get("orchestrator_agent_id"),
|
||||
orchestrator_agent_name=dao.get("orchestrator_agent_name"),
|
||||
parent_microdao_id=dao.get("parent_microdao_id"),
|
||||
parent_microdao_slug=dao.get("parent_microdao_slug"),
|
||||
logo_url=dao.get("logo_url"),
|
||||
member_count=dao.get("member_count", 0),
|
||||
agents_count=dao.get("agents_count", 0),
|
||||
room_count=dao.get("room_count", 0),
|
||||
rooms_count=dao.get("rooms_count", 0),
|
||||
channels_count=dao.get("channels_count", 0)
|
||||
))
|
||||
@@ -1405,16 +1428,31 @@ async def get_microdao_by_slug(slug: str):
|
||||
primary_room_slug=citizen.get("public_primary_room_slug")
|
||||
))
|
||||
|
||||
# Build child microDAOs list
|
||||
child_microdaos = []
|
||||
for child in dao.get("child_microdaos", []):
|
||||
child_microdaos.append(MicrodaoSummary(
|
||||
id=child["id"],
|
||||
slug=child["slug"],
|
||||
name=child["name"],
|
||||
is_public=child.get("is_public", True),
|
||||
is_platform=child.get("is_platform", False)
|
||||
))
|
||||
|
||||
return MicrodaoDetail(
|
||||
id=dao["id"],
|
||||
slug=dao["slug"],
|
||||
name=dao["name"],
|
||||
description=dao.get("description"),
|
||||
district=dao.get("district"),
|
||||
is_public=dao.get("is_public", True),
|
||||
is_platform=dao.get("is_platform", False),
|
||||
is_active=dao.get("is_active", True),
|
||||
orchestrator_agent_id=dao.get("orchestrator_agent_id"),
|
||||
orchestrator_display_name=dao.get("orchestrator_display_name"),
|
||||
is_active=dao.get("is_active", True),
|
||||
is_public=dao.get("is_public", True),
|
||||
parent_microdao_id=dao.get("parent_microdao_id"),
|
||||
parent_microdao_slug=dao.get("parent_microdao_slug"),
|
||||
child_microdaos=child_microdaos,
|
||||
logo_url=dao.get("logo_url"),
|
||||
agents=agents,
|
||||
channels=channels,
|
||||
|
||||
Reference in New Issue
Block a user