mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-09-11 12:17:45 +02:00
Multi-provider support (WIP - not fully tested): - Owned agent loop replacing claude_agent_sdk (agent_loop.py, mcp_client.py) - Provider adapters: Anthropic (native), OpenAI-compat (any endpoint), Gemini (native + schema cleaning) - 19 models across 9 providers (Anthropic, OpenAI, Google, xAI, Meta, DeepSeek, Mistral, Qwen, Cohere) - OpenRouter integration for 300+ models via single API key - Builtin tool reimplementations (Read, Write, Edit, Glob, Grep, Bash, WebSearch, WebFetch, AskUserQuestion) - Standalone MCP client manager (stdio/sse/http) - Frontend: grouped model dropdown, provider selection, dynamic context windows Analytics (tested): - PostHog integration as single analytics source - Tracks: app.opened, session.started/completed, tool.called, tool.approval_resolved, error.occurred - Rich session data: user messages, assistant messages, session titles, tools used, MCP servers, task categories - PostHog dashboard with 14 insights created via API - Usage stats in Settings (Usage tab) with pixel-art bars Settings (tested): - 4 tabs: General, Models, Usage, Commands - Model Providers tab with OpenRouter (recommended), Anthropic, OpenAI, Google key fields - "Get key" links for each provider - Usage tab with session/cost/tool stats + analytics opt-in toggle - analytics_opt_in defaults to true, installation_id auto-generated Merged haik/updates-v1 (tested): - Sub-agent spawning, chat branching, browser control improvements - Settings: auto_select_mode, expand_new_chats, auto_reveal_sub_agents, dev_mode Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
124 lines
3.2 KiB
Python
124 lines
3.2 KiB
Python
"""PostHog-only analytics collector.
|
|
|
|
All events go directly to PostHog. No local SQLite storage.
|
|
|
|
Usage from any module:
|
|
from backend.apps.analytics.collector import record
|
|
record("session.started", {"model": "opus"}, session_id="abc123")
|
|
"""
|
|
|
|
import logging
|
|
import platform
|
|
from uuid import uuid4
|
|
|
|
from posthog import Posthog
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
POSTHOG_API_KEY = "phc_KdVLvAdjCuHeacFoDm1CM1Gb23XikewRqlX67Mj6TNB"
|
|
POSTHOG_HOST = "https://us.i.posthog.com"
|
|
|
|
_posthog: Posthog | None = None
|
|
_installation_id: str | None = None
|
|
|
|
|
|
def init():
|
|
"""Initialise PostHog. Called once at app startup."""
|
|
global _posthog
|
|
if _posthog is None:
|
|
_posthog = Posthog(
|
|
project_api_key=POSTHOG_API_KEY,
|
|
host=POSTHOG_HOST,
|
|
)
|
|
return _posthog
|
|
|
|
|
|
def shutdown():
|
|
"""Flush and close. Called at app shutdown."""
|
|
global _posthog
|
|
if _posthog:
|
|
try:
|
|
_posthog.shutdown()
|
|
except Exception:
|
|
pass
|
|
_posthog = None
|
|
|
|
|
|
def _get_installation_id() -> str:
|
|
"""Get or create a stable anonymous installation ID."""
|
|
global _installation_id
|
|
if _installation_id:
|
|
return _installation_id
|
|
try:
|
|
from backend.apps.settings.settings import load_settings, _save_settings
|
|
settings = load_settings()
|
|
iid = getattr(settings, "installation_id", None)
|
|
if not iid:
|
|
iid = uuid4().hex
|
|
settings.installation_id = iid
|
|
_save_settings(settings)
|
|
_installation_id = iid
|
|
except Exception:
|
|
_installation_id = uuid4().hex
|
|
return _installation_id
|
|
|
|
|
|
def _is_opted_in() -> bool:
|
|
"""Check if user has opted in to analytics."""
|
|
try:
|
|
from backend.apps.settings.settings import load_settings
|
|
return getattr(load_settings(), "analytics_opt_in", True)
|
|
except Exception:
|
|
return True
|
|
|
|
|
|
def record(
|
|
event_type: str,
|
|
properties: dict | None = None,
|
|
session_id: str | None = None,
|
|
dashboard_id: str | None = None,
|
|
):
|
|
"""Record an analytics event to PostHog."""
|
|
if not _posthog or not _is_opted_in():
|
|
return
|
|
|
|
props = {**(properties or {})}
|
|
if session_id:
|
|
props["session_id"] = session_id
|
|
if dashboard_id:
|
|
props["dashboard_id"] = dashboard_id
|
|
props["os"] = platform.system()
|
|
props["platform"] = platform.platform()
|
|
|
|
try:
|
|
_posthog.capture(
|
|
event_type,
|
|
distinct_id=_get_installation_id(),
|
|
properties=props,
|
|
)
|
|
except Exception as e:
|
|
logger.debug(f"PostHog capture failed (non-critical): {e}")
|
|
|
|
|
|
def identify(extra_properties: dict | None = None):
|
|
"""Identify the current installation with properties."""
|
|
if not _posthog or not _is_opted_in():
|
|
return
|
|
|
|
try:
|
|
_posthog.identify(
|
|
_get_installation_id(),
|
|
properties={
|
|
"os": platform.system(),
|
|
"platform": platform.platform(),
|
|
**(extra_properties or {}),
|
|
},
|
|
)
|
|
except Exception as e:
|
|
logger.debug(f"PostHog identify failed (non-critical): {e}")
|
|
|
|
|
|
def get_collector():
|
|
"""Backward compat — returns None since we no longer have a local collector."""
|
|
return None
|