mirror of
https://github.com/x1xhlol/system-prompts-and-models-of-ai-tools.git
synced 2026-06-17 23:09:35 +00:00
286 lines
10 KiB
Python
286 lines
10 KiB
Python
"""
|
|
Hermes API -- Dealix AI Revenue OS -- واجهة هيرمس البرمجية
|
|
Orchestrator endpoints: execute tasks, manage profiles, view costs,
|
|
run security scans, approve improvements, and generate executive summaries.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
from typing import Any, Dict, List, Optional
|
|
|
|
from fastapi import APIRouter, HTTPException
|
|
from fastapi.responses import JSONResponse
|
|
from pydantic import BaseModel, Field
|
|
|
|
from app.services.hermes_orchestrator import hermes_orchestrator, HermesProfile
|
|
from app.services.execution_router import execution_router, TaskClass
|
|
from app.services.shannon_security import shannon_security, ShannonScope
|
|
from app.services.self_improvement import self_improvement_engine
|
|
from app.services.observability import observability_service
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
router = APIRouter(prefix="/hermes", tags=["Hermes Orchestrator"])
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Request / response schemas
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class ExecuteRequest(BaseModel):
|
|
profile_id: str = "founder"
|
|
task: str
|
|
params: Dict[str, Any] = Field(default_factory=dict)
|
|
user_context: Dict[str, Any] = Field(default_factory=lambda: {
|
|
"user_id": "api_user", "tenant_id": "default", "role": "owner",
|
|
})
|
|
|
|
|
|
class ExecuteResponse(BaseModel):
|
|
run_id: str
|
|
profile_id: str
|
|
task: str
|
|
status: str
|
|
backend: str = ""
|
|
data: Dict[str, Any] = {}
|
|
evidence: List[str] = []
|
|
receipt_id: Optional[str] = None
|
|
cost_usd: float = 0.0
|
|
duration_ms: int = 0
|
|
error: Optional[str] = None
|
|
message_ar: str = ""
|
|
|
|
|
|
class ScanRequest(BaseModel):
|
|
environment: str = "staging"
|
|
scopes: List[str] = Field(
|
|
default_factory=lambda: ["auth", "injection", "pdpl"],
|
|
)
|
|
base_url: str = "https://staging.dealix.sa"
|
|
|
|
|
|
class ApproveRequest(BaseModel):
|
|
user_id: str = "founder"
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Execute
|
|
# ---------------------------------------------------------------------------
|
|
|
|
@router.post("/execute", response_model=ExecuteResponse)
|
|
async def execute_task(req: ExecuteRequest) -> ExecuteResponse:
|
|
"""Execute a task via the Hermes orchestrator."""
|
|
result = await hermes_orchestrator.execute(
|
|
profile_id=req.profile_id,
|
|
task=req.task,
|
|
params=req.params,
|
|
user_context=req.user_context,
|
|
)
|
|
return ExecuteResponse(**result.model_dump())
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Profiles
|
|
# ---------------------------------------------------------------------------
|
|
|
|
@router.get("/profiles")
|
|
async def list_profiles() -> JSONResponse:
|
|
"""List all available Hermes profiles."""
|
|
profiles = await hermes_orchestrator.list_profiles()
|
|
return JSONResponse(content={
|
|
"profiles": [p.model_dump() for p in profiles],
|
|
"count": len(profiles),
|
|
"message_ar": f"{len(profiles)} ملفات شخصية متاحة",
|
|
})
|
|
|
|
|
|
@router.get("/profiles/{profile_id}")
|
|
async def get_profile(profile_id: str) -> JSONResponse:
|
|
"""Get details for a specific profile."""
|
|
profile = await hermes_orchestrator.get_profile(profile_id)
|
|
if not profile:
|
|
raise HTTPException(status_code=404, detail=f"الملف الشخصي غير موجود: {profile_id}")
|
|
return JSONResponse(content=profile.model_dump())
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Cost
|
|
# ---------------------------------------------------------------------------
|
|
|
|
@router.get("/cost")
|
|
async def cost_report(period: str = "daily") -> JSONResponse:
|
|
"""Get cost report from the orchestrator and observability service."""
|
|
hermes_cost = await hermes_orchestrator.get_cost_report(period)
|
|
obs_cost = await observability_service.get_cost_report(period)
|
|
return JSONResponse(content={
|
|
"hermes": hermes_cost,
|
|
"observability": obs_cost,
|
|
})
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Health
|
|
# ---------------------------------------------------------------------------
|
|
|
|
@router.get("/health")
|
|
async def health_report() -> JSONResponse:
|
|
"""System health report across all backends and workflows."""
|
|
obs_health = await observability_service.get_health_report()
|
|
backend_health = await execution_router.get_backend_health()
|
|
anomalies = await observability_service.detect_anomalies()
|
|
return JSONResponse(content={
|
|
"system": obs_health,
|
|
"backends": {k: v.model_dump() for k, v in backend_health.items()},
|
|
"anomalies": [a.model_dump(mode="json") for a in anomalies],
|
|
"anomaly_count": len(anomalies),
|
|
})
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Runs
|
|
# ---------------------------------------------------------------------------
|
|
|
|
@router.get("/runs")
|
|
async def list_active_runs() -> JSONResponse:
|
|
"""List currently active runs."""
|
|
runs = await hermes_orchestrator.get_active_runs()
|
|
return JSONResponse(content={
|
|
"active_runs": [r.model_dump(mode="json") for r in runs],
|
|
"count": len(runs),
|
|
"message_ar": f"{len(runs)} عمليات نشطة حالياً",
|
|
})
|
|
|
|
|
|
@router.post("/runs/{run_id}/abort")
|
|
async def abort_run(run_id: str) -> JSONResponse:
|
|
"""Abort an active run."""
|
|
success = await hermes_orchestrator.abort_run(run_id)
|
|
if not success:
|
|
raise HTTPException(status_code=404, detail=f"التشغيل غير موجود أو اكتمل بالفعل: {run_id}")
|
|
return JSONResponse(content={
|
|
"run_id": run_id,
|
|
"status": "aborted",
|
|
"message_ar": f"تم إلغاء التشغيل: {run_id}",
|
|
})
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Self-improvement
|
|
# ---------------------------------------------------------------------------
|
|
|
|
@router.get("/improvements")
|
|
async def list_improvements(status: Optional[str] = None) -> JSONResponse:
|
|
"""List self-improvement proposals."""
|
|
proposals = self_improvement_engine.list_proposals(status)
|
|
report = await self_improvement_engine.report()
|
|
return JSONResponse(content={
|
|
"proposals": [p.model_dump(mode="json") for p in proposals],
|
|
"count": len(proposals),
|
|
"summary": report,
|
|
})
|
|
|
|
|
|
@router.post("/improvements/{proposal_id}/approve")
|
|
async def approve_improvement(proposal_id: str, req: ApproveRequest) -> JSONResponse:
|
|
"""Approve a self-improvement proposal."""
|
|
proposal = await self_improvement_engine.approve(proposal_id, req.user_id)
|
|
if not proposal:
|
|
raise HTTPException(
|
|
status_code=404,
|
|
detail=f"المقترح غير موجود أو ليس في حالة 'مقترح': {proposal_id}",
|
|
)
|
|
return JSONResponse(content={
|
|
"proposal_id": proposal.id,
|
|
"status": proposal.status,
|
|
"approved_by": proposal.approved_by,
|
|
"message_ar": f"تمت الموافقة على المقترح: {proposal.title_ar}",
|
|
})
|
|
|
|
|
|
@router.post("/improvements/cycle")
|
|
async def run_improvement_cycle(tenant_id: Optional[str] = None) -> JSONResponse:
|
|
"""Trigger a self-improvement cycle."""
|
|
cycle = await self_improvement_engine.run_cycle(tenant_id)
|
|
return JSONResponse(content=cycle.model_dump(mode="json"))
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Shannon security
|
|
# ---------------------------------------------------------------------------
|
|
|
|
@router.get("/security/report")
|
|
async def latest_security_report() -> JSONResponse:
|
|
"""Get the latest Shannon security scan report."""
|
|
report = shannon_security.get_latest_report()
|
|
if not report:
|
|
return JSONResponse(content={
|
|
"report": None,
|
|
"message_ar": "لا يوجد تقرير أمني بعد. قم بتشغيل فحص أولاً.",
|
|
})
|
|
return JSONResponse(content=report.model_dump(mode="json"))
|
|
|
|
|
|
@router.post("/security/scan")
|
|
async def trigger_security_scan(req: ScanRequest) -> JSONResponse:
|
|
"""Trigger a Shannon security scan (staging only)."""
|
|
scopes = []
|
|
for s in req.scopes:
|
|
try:
|
|
scopes.append(ShannonScope(s))
|
|
except ValueError:
|
|
raise HTTPException(
|
|
status_code=400,
|
|
detail=f"نطاق غير صالح: {s}. المتاحة: {[x.value for x in ShannonScope]}",
|
|
)
|
|
|
|
report = await shannon_security.run_scan(
|
|
environment=req.environment,
|
|
scopes=scopes,
|
|
base_url=req.base_url,
|
|
)
|
|
return JSONResponse(content=report.model_dump(mode="json"))
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Executive summary
|
|
# ---------------------------------------------------------------------------
|
|
|
|
@router.get("/executive-summary")
|
|
async def executive_summary(period: str = "weekly") -> JSONResponse:
|
|
"""Arabic executive summary for leadership."""
|
|
summary = await observability_service.get_executive_summary(period)
|
|
improvement_report = await self_improvement_engine.report()
|
|
security_report = shannon_security.get_latest_report()
|
|
|
|
full_summary = summary
|
|
if improvement_report.get("total_proposals", 0) > 0:
|
|
applied = improvement_report.get("by_status", {}).get("applied", 0)
|
|
pending = improvement_report.get("by_status", {}).get("proposed", 0)
|
|
full_summary += f"، {applied} تحسين مطبق، {pending} تحسين معلق"
|
|
|
|
if security_report:
|
|
full_summary += (
|
|
f"، آخر فحص أمني: {security_report.critical_count} حرجة، "
|
|
f"{security_report.high_count} عالية"
|
|
)
|
|
else:
|
|
full_summary += "، لا يوجد فحص أمني بعد"
|
|
|
|
return JSONResponse(content={
|
|
"summary_ar": full_summary,
|
|
"period": period,
|
|
"improvements": improvement_report,
|
|
"security_status": security_report.model_dump(mode="json") if security_report else None,
|
|
})
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Routing stats
|
|
# ---------------------------------------------------------------------------
|
|
|
|
@router.get("/routing-stats")
|
|
async def routing_stats() -> JSONResponse:
|
|
"""Execution routing statistics."""
|
|
stats = await execution_router.get_routing_stats()
|
|
return JSONResponse(content=stats)
|