- Router Core with rule-based routing (1530 lines) - DevTools Backend (file ops, test execution) (393 lines) - CrewAI Orchestrator (4 workflows, 12 agents) (358 lines) - Bot Gateway (Telegram/Discord) (321 lines) - RBAC Service (role resolution) (272 lines) - Structured logging (utils/logger.py) - Docker deployment (docker-compose.yml) - Comprehensive documentation (57KB) - Test suites (41 tests, 95% coverage) - Phase 4 roadmap & ecosystem integration plans Production-ready infrastructure for DAARION microDAOs.
118 lines
3.7 KiB
Python
118 lines
3.7 KiB
Python
"""
|
|
CrewAI Provider
|
|
Orchestrates multi-agent workflows via CrewAI backend.
|
|
"""
|
|
import logging
|
|
from typing import Dict, Any, Optional
|
|
import httpx
|
|
|
|
from providers.base import Provider
|
|
from router_models import RouterRequest, RouterResponse
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class CrewAIProvider(Provider):
|
|
"""
|
|
Provider that routes requests to a CrewAI orchestrator backend.
|
|
|
|
The backend manages multi-agent workflows using CrewAI framework.
|
|
"""
|
|
|
|
def __init__(
|
|
self,
|
|
provider_id: str,
|
|
base_url: str,
|
|
timeout: int = 120,
|
|
**kwargs
|
|
):
|
|
super().__init__(provider_id)
|
|
self.base_url = base_url.rstrip("/")
|
|
self.timeout = timeout
|
|
logger.info(f"CrewAIProvider initialized: {provider_id} → {base_url}")
|
|
|
|
async def call(self, request: RouterRequest) -> RouterResponse:
|
|
"""
|
|
Route request to CrewAI orchestrator backend.
|
|
|
|
Expected request.payload format:
|
|
{
|
|
"workflow": "microdao_onboarding" | "code_review" | etc.,
|
|
"input": {
|
|
"user_id": "...",
|
|
"channel": "...",
|
|
...
|
|
}
|
|
}
|
|
"""
|
|
try:
|
|
# Extract workflow and input from payload
|
|
workflow = request.payload.get("workflow") if request.payload else None
|
|
if not workflow:
|
|
return RouterResponse(
|
|
ok=False,
|
|
provider_id=self.id,
|
|
error="Missing 'workflow' in request payload"
|
|
)
|
|
|
|
workflow_input = request.payload.get("input", {}) if request.payload else {}
|
|
|
|
# Build request body with metadata
|
|
body = {
|
|
"workflow": workflow,
|
|
"input": workflow_input,
|
|
"meta": {
|
|
"mode": request.mode,
|
|
"agent": request.agent,
|
|
"dao_id": request.dao_id,
|
|
"user_id": request.user_id,
|
|
"source": request.source,
|
|
"session_id": request.session_id,
|
|
}
|
|
}
|
|
|
|
# Call CrewAI backend
|
|
url = f"{self.base_url}/workflow/run"
|
|
logger.info(f"CrewAI workflow call: {workflow} → {url}")
|
|
|
|
async with httpx.AsyncClient(timeout=self.timeout) as client:
|
|
response = await client.post(url, json=body)
|
|
response.raise_for_status()
|
|
|
|
data = response.json()
|
|
|
|
return RouterResponse(
|
|
ok=True,
|
|
provider_id=self.id,
|
|
data=data,
|
|
metadata={
|
|
"provider_type": "orchestrator",
|
|
"workflow": workflow,
|
|
"status_code": response.status_code
|
|
}
|
|
)
|
|
|
|
except httpx.HTTPStatusError as e:
|
|
logger.error(f"CrewAI HTTP error: {e}")
|
|
return RouterResponse(
|
|
ok=False,
|
|
provider_id=self.id,
|
|
error=f"HTTP {e.response.status_code}: {e.response.text}"
|
|
)
|
|
|
|
except httpx.RequestError as e:
|
|
logger.error(f"CrewAI request error: {e}")
|
|
return RouterResponse(
|
|
ok=False,
|
|
provider_id=self.id,
|
|
error=f"Request failed: {str(e)}"
|
|
)
|
|
|
|
except Exception as e:
|
|
logger.error(f"CrewAI error: {e}")
|
|
return RouterResponse(
|
|
ok=False,
|
|
provider_id=self.id,
|
|
error=str(e)
|
|
)
|