feat: Add presence heartbeat for Matrix online status

- matrix-gateway: POST /internal/matrix/presence/online endpoint
- usePresenceHeartbeat hook with activity tracking
- Auto away after 5 min inactivity
- Offline on page close/visibility change
- Integrated in MatrixChatRoom component
This commit is contained in:
Apple
2025-11-27 00:19:40 -08:00
parent 5bed515852
commit 3de3c8cb36
6371 changed files with 1317450 additions and 932 deletions

76
main.py
View File

@@ -22,6 +22,7 @@ from dotenv import load_dotenv
load_dotenv()
import os
from openai import OpenAI
# xAI uses OpenAI-compatible API, no need for separate import
import httpx
# ============================================================================
@@ -31,6 +32,10 @@ DEEPSEEK_API_KEY = os.getenv("DEEPSEEK_API_KEY", "")
DEEPSEEK_BASE_URL = os.getenv("DEEPSEEK_BASE_URL", "https://api.deepseek.com")
DEEPSEEK_MODEL = os.getenv("DEEPSEEK_MODEL", "deepseek-chat")
XAI_API_KEY = os.getenv("XAI_API_KEY", "")
XAI_BASE_URL = os.getenv("XAI_BASE_URL", "https://api.x.ai/v1")
XAI_MODEL = os.getenv("XAI_MODEL", "grok-beta")
OLLAMA_BASE_URL = os.getenv("OLLAMA_BASE_URL", "http://host.docker.internal:11434")
OLLAMA_MODEL = os.getenv("OLLAMA_MODEL", "qwen2.5:3b")
@@ -107,12 +112,13 @@ def simple_routing_strategy(req: RouteRequest) -> str:
logger.info(f"Provider override via metadata: {provider}")
return provider
# Default: use DeepSeek
# Allow explicit provider selection via metadata
# Default: use DeepSeek if available, otherwise fallback
if DEEPSEEK_API_KEY:
logger.info(f"Routing to DeepSeek for user={req.context.user_id}")
return "cloud_deepseek"
else:
logger.warning("No DeepSeek API key, falling back to echo")
logger.warning("No cloud API keys, falling back to echo")
return "echo"
@@ -149,6 +155,56 @@ def call_backend(provider: str, req: RouteRequest) -> RouteResponse:
route_debug=debug,
)
elif provider == "cloud_xai":
if not XAI_API_KEY:
raise HTTPException(status_code=500, detail="xAI API key not configured")
try:
logger.info(f"Calling xAI (Grok) API for user={req.context.user_id}")
# xAI SDK is OpenAI-compatible, use OpenAI client with xAI endpoint
client = OpenAI(api_key=XAI_API_KEY, base_url=XAI_BASE_URL)
messages = [
{"role": "system", "content": "Ти - DAGI (Decentralized Agent Gateway Interface), асистент для DAARION.city та microDAO екосистеми. Відповідай українською мовою, будь корисним та дружнім."},
{"role": "user", "content": req.message}
]
response = client.chat.completions.create(
model=XAI_MODEL,
messages=messages,
temperature=0.7,
max_tokens=2000
)
reply = response.choices[0].message.content
debug = {
"model": XAI_MODEL,
"provider": "xAI (Grok)",
"finish_reason": response.choices[0].finish_reason,
"usage": {
"prompt_tokens": response.usage.prompt_tokens,
"completion_tokens": response.usage.completion_tokens,
"total_tokens": response.usage.total_tokens
},
"base_url": XAI_BASE_URL
}
logger.info(f"xAI (Grok) response: {response.usage.total_tokens} tokens")
return RouteResponse(
text=reply,
provider=provider,
model=XAI_MODEL,
routed_at=routed_at,
route_debug=debug
)
except Exception as e:
logger.error(f"xAI API error: {e}")
raise HTTPException(status_code=500, detail=f"xAI error: {str(e)}")
elif provider == "cloud_deepseek":
if not DEEPSEEK_API_KEY:
raise HTTPException(status_code=500, detail="DeepSeek API key not configured")
@@ -258,6 +314,9 @@ def health():
"""Health check endpoint"""
available_providers = ["echo"]
if XAI_API_KEY:
available_providers.append("cloud_xai")
if DEEPSEEK_API_KEY:
available_providers.append("cloud_deepseek")
@@ -275,7 +334,7 @@ def health():
"service": "dagi-router",
"version": "0.3.0",
"providers": available_providers,
"capabilities": ["multi_provider_routing", "deepseek_integration", "ollama_integration"]
"capabilities": ["multi_provider_routing", "xai_integration", "deepseek_integration", "ollama_integration"]
}
@@ -301,6 +360,15 @@ def list_providers():
{"name": "echo", "status": "active", "description": "Echo provider (no LLM)"}
]
if XAI_API_KEY:
providers.append({
"name": "cloud_xai",
"status": "active",
"description": f"xAI (Grok) - {XAI_MODEL}",
"model": XAI_MODEL,
"base_url": XAI_BASE_URL
})
if DEEPSEEK_API_KEY:
providers.append({
"name": "cloud_deepseek",
@@ -340,4 +408,4 @@ def list_providers():
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=9100)
uvicorn.run(app, host="0.0.0.0", port=9102)