diff --git a/systems/x86_64-linux/mx/nextcloud-claude-bot/bot.py b/systems/x86_64-linux/mx/nextcloud-claude-bot/bot.py index dd1049e..2f1f162 100644 --- a/systems/x86_64-linux/mx/nextcloud-claude-bot/bot.py +++ b/systems/x86_64-linux/mx/nextcloud-claude-bot/bot.py @@ -49,8 +49,10 @@ log = logging.getLogger(__name__) app = FastAPI(title="Nextcloud Claude Bot") -# Number of recent messages to fetch for context -CONTEXT_MESSAGES = int(os.environ.get("CONTEXT_MESSAGES", "6")) +# In-memory conversation history per conversation token +# Format: {token: [(user, message), ...]} +conversations: dict[str, list[tuple[str, str]]] = {} +MAX_HISTORY = int(os.environ.get("CONTEXT_MESSAGES", "6")) def generate_bot_auth_headers(body: str = "") -> dict: @@ -68,35 +70,6 @@ def generate_bot_auth_headers(body: str = "") -> dict: } -async def fetch_chat_history(conversation_token: str, limit: int = CONTEXT_MESSAGES) -> list[dict]: - """Fetch recent messages from Nextcloud Talk conversation.""" - if not NEXTCLOUD_URL: - log.warning("NEXTCLOUD_URL not configured, cannot fetch history") - return [] - - url = f"{NEXTCLOUD_URL}/ocs/v2.php/apps/spreed/api/v1/chat/{conversation_token}" - params = { - "limit": limit, - "lookIntoFuture": 0, - } - - headers = generate_bot_auth_headers() - headers["Accept"] = "application/json" - - async with httpx.AsyncClient() as client: - try: - resp = await client.get(url, params=params, headers=headers) - if resp.status_code == 200: - data = resp.json() - messages = data.get("ocs", {}).get("data", []) - # Messages come newest first, reverse for chronological order - return list(reversed(messages)) - else: - log.warning(f"Failed to fetch chat history: {resp.status_code} {resp.text[:200]}") - return [] - except Exception as e: - log.exception("Error fetching chat history") - return [] def verify_signature(body: bytes, signature: str, random: Optional[str] = None) -> bool: @@ -128,32 +101,17 @@ def verify_signature(body: bytes, signature: str, random: Optional[str] = None) return False -async def build_prompt(conversation_token: str, current_message: str, current_user: str) -> str: - """Build prompt with conversation history from Nextcloud.""" +def build_prompt(conversation_token: str, current_message: str, current_user: str) -> str: + """Build prompt with in-memory conversation history.""" parts = [] if SYSTEM_PROMPT: parts.append(f"System: {SYSTEM_PROMPT}\n") - # Fetch recent history from Nextcloud - history = await fetch_chat_history(conversation_token) - - # Add recent history (excluding the current message which triggered this) - for msg in history: - actor_type = msg.get("actorType", "") - actor_id = msg.get("actorId", "") - message_text = msg.get("message", "") - msg_type = msg.get("messageType", "") - - # Skip system messages - if msg_type == "system": - continue - - # Determine if this is a user or the bot - if actor_type == "bots": - parts.append(f"Assistant: {message_text}") - elif actor_type == "users": - parts.append(f"User ({actor_id}): {message_text}") + # Add recent history from memory + history = conversations.get(conversation_token, []) + for role, msg in history[-MAX_HISTORY:]: + parts.append(f"{role}: {msg}") # Add current message parts.append(f"User ({current_user}): {current_message}") @@ -331,17 +289,27 @@ Schreib mir einfach eine Nachricht und ich antworte dir. **Befehle:** • `/help` oder `/hilfe` – Diese Hilfe anzeigen -Der Bot nutzt die letzten Nachrichten aus dem Chat als Kontext.""" +Der Bot merkt sich die letzten Nachrichten pro Raum (bis zum Neustart).""" await send_reply(conversation_token, help_text, reply_to=message_id) return JSONResponse({"status": "ok", "action": "help"}) # Build prompt with chat history and call Claude - prompt = await build_prompt(conversation_token, message_text, actor_id) + prompt = build_prompt(conversation_token, message_text, actor_id) response = await call_claude(prompt) + # Store in history + if conversation_token not in conversations: + conversations[conversation_token] = [] + conversations[conversation_token].append((f"User ({actor_id})", message_text)) + conversations[conversation_token].append(("Assistant", response)) + + # Trim history + if len(conversations[conversation_token]) > MAX_HISTORY * 2: + conversations[conversation_token] = conversations[conversation_token][-MAX_HISTORY * 2:] + # Send response await send_reply(conversation_token, response, reply_to=message_id) - + return JSONResponse({"status": "ok"}) @@ -353,7 +321,7 @@ async def health(): "nextcloud_url": NEXTCLOUD_URL, "claude_path": CLAUDE_PATH, "allowed_users": ALLOWED_USERS if ALLOWED_USERS else "all", - "context_messages": CONTEXT_MESSAGES, + "max_history": MAX_HISTORY, }