Files
simbarag/metrics.py
ryan 5809ad3adb
CI / backend (pull_request) Successful in 42s
CI / frontend (pull_request) Failing after 7s
CI / backend (push) Successful in 41s
CI / frontend (push) Failing after 7s
Add durable chat runs, shared agent runtime, reliable jobs, history tools and citations
Shared runtime
- One agent entry point (runtime.invoke_agent_for_user / prepare_agent_turn) for
  web, email, iMessage, WhatsApp, scheduled and cron, so identity, permissions,
  memories, summaries and metrics match across channels.
- Serialize turns per checkpoint thread; /query validates input and returns 409
  while a durable run owns the conversation.

Reconnectable chat (durable runs)
- POST /runs creates an idempotent, owner-scoped run; GET .../events replays an
  ordered SSE log by cursor, so generation survives refresh and device switches.
  Stop cancels; disconnect only detaches. /stream-query is a compatibility adapter.
- Coalesce streamed deltas (150 ms) and wake same-process subscribers instead of
  polling; keep buffered text when a run is stopped.
- Drain runs on shutdown; serve via Hypercorn with a shutdown timeout covering
  the drain (and no longer in debug mode). CHAT_RUN_* settings are forwarded.

Scheduled messages and cron
- Atomic delivery claims, attempt history and explicit retries; ambiguous sends
  become "uncertain" and are never resent automatically.
- Cron triggers need a run_id/Idempotency-Key; legacy jobs need an explicit owner.
- Cancelling never rewrites a started delivery; series stop via tombstones.
  Legacy sent rows are not forked into new series. Malformed bodies return 400.

History, integrations and retrieval
- Message-content search, pinned conversations, JSON/Markdown export and a
  memory editor.
- Integration status panel and YNAB data-freshness provenance.
- Date-filtered document/note search with stable citations and a source viewer.

Migration 20 is generated by aerich.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 23:11:04 -04:00

70 lines
1.8 KiB
Python

import time
from contextlib import contextmanager
from prometheus_client import (
CollectorRegistry,
Counter,
Histogram,
generate_latest,
)
REGISTRY = CollectorRegistry(auto_describe=True)
AGENT_REQUESTS_TOTAL = Counter(
"raggr_agent_requests_total",
"Agent generation requests by channel and outcome.",
labelnames=("channel", "result"),
registry=REGISTRY,
)
AGENT_RESPONSE_SECONDS = Histogram(
"raggr_agent_response_seconds",
"Agent generation time by channel, excluding delivery.",
labelnames=("channel",),
buckets=(0.5, 1, 2, 5, 10, 30, 60, 120, 300),
registry=REGISTRY,
)
CHAT_RESPONSE_SECONDS = Histogram(
"raggr_chat_response_seconds",
"End-to-end chat response time in seconds.",
labelnames=("stream",),
buckets=(0.5, 1, 2, 5, 10, 30, 60, 120, 300),
registry=REGISTRY,
)
CHAT_REQUESTS_TOTAL = Counter(
"raggr_chat_requests_total",
"Total chat requests by stream mode and result.",
labelnames=("stream", "result"),
registry=REGISTRY,
)
def _s(stream: bool) -> str:
return "true" if stream else "false"
@contextmanager
def time_response(stream: bool):
t0 = time.monotonic()
result = "success"
try:
yield
except Exception:
result = "error"
raise
finally:
CHAT_RESPONSE_SECONDS.labels(stream=_s(stream)).observe(time.monotonic() - t0)
CHAT_REQUESTS_TOTAL.labels(stream=_s(stream), result=result).inc()
def observe_response(stream: bool, seconds: float) -> None:
CHAT_RESPONSE_SECONDS.labels(stream=_s(stream)).observe(seconds)
def count_result(stream: bool, result: str) -> None:
CHAT_REQUESTS_TOTAL.labels(stream=_s(stream), result=result).inc()
def render_metrics() -> bytes:
return generate_latest(REGISTRY)