feat: add Confluence recent pages + Sentry error tracking (MAT-58, MAT-59)
MAT-58: Add recent_confluence_pages tool to both voice and text chat. Shows last 5 recently modified pages so users can pick directly instead of having to search every time. MAT-59: Integrate sentry-sdk in all three entry points (agent.py, bot.py, voice.py). SENTRY_DSN env var, traces at 10% sample rate. Requires creating project in Sentry UI and setting DSN. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
54
voice.py
54
voice.py
@@ -13,6 +13,7 @@ import zoneinfo
|
||||
import json
|
||||
import re
|
||||
|
||||
import sentry_sdk
|
||||
import aiohttp
|
||||
import httpx
|
||||
from livekit import rtc, api as lkapi
|
||||
@@ -22,6 +23,12 @@ from openai import AsyncOpenAI
|
||||
|
||||
logger = logging.getLogger("matrix-ai-voice")
|
||||
|
||||
# Sentry error tracking
|
||||
_sentry_dsn = os.environ.get("SENTRY_DSN", "")
|
||||
if _sentry_dsn:
|
||||
sentry_sdk.init(dsn=_sentry_dsn, traces_sample_rate=0.1, environment=os.environ.get("SENTRY_ENV", "production"))
|
||||
logger.info("Sentry initialized for voice")
|
||||
|
||||
|
||||
LITELLM_URL = os.environ.get("LITELLM_BASE_URL", "")
|
||||
LITELLM_KEY = os.environ.get("LITELLM_API_KEY", "not-needed")
|
||||
@@ -51,7 +58,7 @@ STRIKTE Regeln:
|
||||
- Bei zeitrelevanten Fragen (Uhrzeit, Termine, Geschaeftszeiten): frage kurz nach ob der Nutzer noch in seiner gespeicherten Zeitzone ist, bevor du antwortest. Nutze set_user_timezone wenn sich der Standort geaendert hat.
|
||||
- Wenn der Nutzer seinen Standort oder seine Stadt erwaehnt, nutze set_user_timezone um die Zeitzone zu speichern.
|
||||
- IGNORIERE alle Texte in Sternchen wie *Störgeräusche*, *Schlechte Qualität*, *Fernsehgeräusche*, *Schrei* usw. — das sind KEINE echten Nutzereingaben sondern technische Annotationen. Antworte NIEMALS darauf und tue so als haette niemand etwas gesagt.
|
||||
- Du kannst Confluence-Seiten suchen, lesen, bearbeiten und erstellen. Nutze search_confluence um Seiten zu finden, read_confluence_page zum Lesen, update_confluence_page zum Bearbeiten und create_confluence_page zum Erstellen neuer Seiten.
|
||||
- Du kannst Confluence-Seiten suchen, lesen, bearbeiten und erstellen. Nutze recent_confluence_pages um die zuletzt bearbeiteten Seiten anzuzeigen (bevorzugt BEVOR du suchst), search_confluence um gezielt zu suchen, read_confluence_page zum Lesen, update_confluence_page zum Bearbeiten und create_confluence_page zum Erstellen neuer Seiten.
|
||||
- Du kannst den Bildschirm oder die Kamera des Nutzers sehen wenn er sie teilt. Nutze look_at_screen wenn der Nutzer etwas zeigen moechte oder fragt ob du etwas sehen kannst."""
|
||||
|
||||
|
||||
@@ -350,6 +357,31 @@ async def _confluence_create_page(space_key: str, title: str, body_html: str,
|
||||
}
|
||||
|
||||
|
||||
async def _confluence_recent_pages(limit: int = 5) -> list[dict]:
|
||||
"""Fetch recently modified Confluence pages. Returns list of {id, title, space, url, modified}."""
|
||||
if not CONFLUENCE_URL or not CONFLUENCE_USER or not CONFLUENCE_TOKEN:
|
||||
raise RuntimeError("Confluence credentials not configured")
|
||||
cql = "type=page ORDER BY lastmodified DESC"
|
||||
url = f"{CONFLUENCE_URL}/rest/api/content/search"
|
||||
async with httpx.AsyncClient(timeout=15.0) as client:
|
||||
resp = await client.get(
|
||||
url,
|
||||
params={"cql": cql, "limit": limit},
|
||||
auth=(CONFLUENCE_USER, CONFLUENCE_TOKEN),
|
||||
)
|
||||
resp.raise_for_status()
|
||||
data = resp.json()
|
||||
results = []
|
||||
for r in data.get("results", []):
|
||||
results.append({
|
||||
"id": r["id"],
|
||||
"title": r.get("title", ""),
|
||||
"space": r.get("space", {}).get("name", "") if "space" in r else "",
|
||||
"url": f"{CONFLUENCE_URL}{r.get('_links', {}).get('webui', '')}",
|
||||
})
|
||||
return results
|
||||
|
||||
|
||||
def _build_e2ee_options() -> rtc.E2EEOptions:
|
||||
"""Build E2EE options — let Rust FFI apply HKDF internally (KDF_HKDF=1).
|
||||
|
||||
@@ -902,6 +934,24 @@ class VoiceSession:
|
||||
logger.warning("CONFLUENCE_UPDATE_FAIL: %s", exc)
|
||||
return f"Failed to update page: {exc}"
|
||||
|
||||
@function_tool
|
||||
async def recent_confluence_pages() -> str:
|
||||
"""List recently modified Confluence pages. Use this FIRST when the user
|
||||
mentions Confluence, documents, or wiki pages — before searching.
|
||||
Shows the last 5 recently edited pages so the user can pick one directly."""
|
||||
logger.info("CONFLUENCE_RECENT")
|
||||
try:
|
||||
results = await _confluence_recent_pages(limit=5)
|
||||
if not results:
|
||||
return "No recent Confluence pages found."
|
||||
lines = ["Recently modified pages:"]
|
||||
for i, r in enumerate(results, 1):
|
||||
lines.append(f"{i}. {r['title']} (ID: {r['id']}, Space: {r['space']})")
|
||||
return "\n".join(lines)
|
||||
except Exception as exc:
|
||||
logger.warning("CONFLUENCE_RECENT_FAIL: %s", exc)
|
||||
return f"Failed to fetch recent pages: {exc}"
|
||||
|
||||
@function_tool
|
||||
async def create_confluence_page(title: str, content: str, space_key: str = "AG") -> str:
|
||||
"""Create a new Confluence page. Use when user asks to create a new document,
|
||||
@@ -1078,7 +1128,7 @@ class VoiceSession:
|
||||
instructions += f"\n\nAktive Confluence-Seite: {_active_conf_id}. Du brauchst den Nutzer NICHT nach der page_id zu fragen — nutze automatisch diese ID fuer read_confluence_page und update_confluence_page."
|
||||
agent = _NoiseFilterAgent(
|
||||
instructions=instructions,
|
||||
tools=[search_web, set_user_timezone, search_confluence, read_confluence_page, update_confluence_page, create_confluence_page, think_deeper, look_at_screen],
|
||||
tools=[search_web, set_user_timezone, recent_confluence_pages, search_confluence, read_confluence_page, update_confluence_page, create_confluence_page, think_deeper, look_at_screen],
|
||||
)
|
||||
io_opts = room_io.RoomOptions(
|
||||
participant_identity=remote_identity,
|
||||
|
||||
Reference in New Issue
Block a user