- 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
179 lines
5.4 KiB
Python
179 lines
5.4 KiB
Python
"""
|
||
Second Me Service Logic
|
||
"""
|
||
|
||
import os
|
||
import httpx
|
||
import time
|
||
import logging
|
||
from typing import List, Dict
|
||
|
||
import repository
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
# Config
|
||
SECONDME_AGENT_ID = os.getenv("SECONDME_AGENT_ID", "ag_secondme_global")
|
||
AGENTS_SERVICE_URL = os.getenv("AGENTS_SERVICE_URL", "http://agents-service:7002")
|
||
|
||
|
||
async def invoke_second_me(user_id: str, prompt: str) -> Dict:
|
||
"""
|
||
Викликати Second Me agent для користувача
|
||
|
||
1. Отримати або створити сесію
|
||
2. Зберегти user prompt
|
||
3. Зібрати контекст (останні N повідомлень)
|
||
4. Викликати Agents Core
|
||
5. Зберегти assistant відповідь
|
||
6. Повернути результат
|
||
"""
|
||
start_time = time.monotonic()
|
||
|
||
# 1. Отримати/створити сесію
|
||
session = await repository.get_or_create_session(user_id, agent_id=SECONDME_AGENT_ID)
|
||
session_id = session["id"]
|
||
|
||
# 2. Зберегти user prompt
|
||
await repository.create_message(
|
||
session_id=session_id,
|
||
user_id=user_id,
|
||
role="user",
|
||
content=prompt
|
||
)
|
||
|
||
# 3. Зібрати контекст
|
||
messages = await repository.get_session_messages(session_id, limit=10)
|
||
|
||
# Сформувати контекст для LLM
|
||
context_messages = []
|
||
for msg in messages:
|
||
context_messages.append({
|
||
"role": msg["role"],
|
||
"content": msg["content"]
|
||
})
|
||
|
||
# 4. Викликати Agents Core
|
||
try:
|
||
response_text, tokens_used = await call_agents_core(
|
||
agent_id=SECONDME_AGENT_ID,
|
||
user_id=user_id,
|
||
prompt=prompt,
|
||
context=context_messages
|
||
)
|
||
except Exception as e:
|
||
logger.error(f"Failed to call Agents Core: {e}")
|
||
# Fallback до mock відповіді
|
||
response_text = f"Я — твій Second Me. Ти запитав: '{prompt}'. На жаль, зараз я не можу підключитися до LLM, але я тут для тебе! 🤖"
|
||
tokens_used = 50
|
||
|
||
latency_ms = int((time.monotonic() - start_time) * 1000)
|
||
|
||
# 5. Зберегти assistant відповідь
|
||
await repository.create_message(
|
||
session_id=session_id,
|
||
user_id=user_id,
|
||
role="assistant",
|
||
content=response_text,
|
||
tokens_used=tokens_used,
|
||
latency_ms=latency_ms
|
||
)
|
||
|
||
# Оновити час останньої взаємодії
|
||
await repository.update_session_interaction(session_id)
|
||
|
||
# 6. Повернути результат
|
||
return {
|
||
"response": response_text,
|
||
"tokens_used": tokens_used,
|
||
"latency_ms": latency_ms
|
||
}
|
||
|
||
|
||
async def call_agents_core(
|
||
agent_id: str,
|
||
user_id: str,
|
||
prompt: str,
|
||
context: List[Dict]
|
||
) -> tuple[str, int]:
|
||
"""
|
||
Викликати Agents Core service
|
||
|
||
Returns: (response_text, tokens_used)
|
||
"""
|
||
|
||
# Формуємо input з контекстом
|
||
context_str = ""
|
||
if context:
|
||
for msg in context[-5:]: # Останні 5 повідомлень
|
||
role = msg["role"]
|
||
content = msg["content"]
|
||
context_str += f"{role.capitalize()}: {content}\n"
|
||
|
||
input_text = f"""You are Second Me — персональний цифровий двійник користувача в DAARION City.
|
||
|
||
Контекст попередніх розмов:
|
||
{context_str}
|
||
|
||
Поточне запитання користувача:
|
||
{prompt}
|
||
|
||
Твоя відповідь (українською мовою):"""
|
||
|
||
payload = {
|
||
"input": input_text,
|
||
"context": {
|
||
"user_id": user_id,
|
||
"kind": "secondme",
|
||
"agent_id": agent_id
|
||
}
|
||
}
|
||
|
||
url = f"{AGENTS_SERVICE_URL}/agents/invoke"
|
||
|
||
async with httpx.AsyncClient(timeout=30.0) as client:
|
||
response = await client.post(url, json={
|
||
"agent_id": agent_id,
|
||
"payload": payload
|
||
})
|
||
|
||
response.raise_for_status()
|
||
data = response.json()
|
||
|
||
# TODO: адаптувати під реальний формат відповіді Agents Core
|
||
response_text = data.get("response", data.get("reply", "Немає відповіді"))
|
||
tokens_used = data.get("tokens_used", 100)
|
||
|
||
return response_text, tokens_used
|
||
|
||
|
||
async def get_user_history(user_id: str, limit: int = 5) -> List[Dict]:
|
||
"""Отримати історію користувача"""
|
||
messages = await repository.get_user_messages(user_id, limit=limit)
|
||
return [
|
||
{
|
||
"role": msg["role"],
|
||
"content": msg["content"],
|
||
"created_at": msg["created_at"].isoformat()
|
||
}
|
||
for msg in messages
|
||
]
|
||
|
||
|
||
async def get_user_profile(user_id: str) -> Dict:
|
||
"""Отримати профіль Second Me для користувача"""
|
||
stats = await repository.get_user_stats(user_id)
|
||
|
||
return {
|
||
"user_id": user_id,
|
||
"agent_id": SECONDME_AGENT_ID,
|
||
"total_interactions": stats.get("total_messages", 0),
|
||
"last_interaction": stats.get("last_interaction").isoformat() if stats.get("last_interaction") else None
|
||
}
|
||
|
||
|
||
async def clear_user_history(user_id: str):
|
||
"""Очистити історію користувача"""
|
||
await repository.clear_user_history(user_id)
|
||
|