|
| 1 | +"""Headless PostHog per-metric report via the posthog-summary skill. |
| 2 | +
|
| 3 | +Mirrors :mod:`integrations.sentry.morning_digest_runner`: run one headless |
| 4 | +agent turn driven by the ``posthog-summary`` skill and return the assistant |
| 5 | +report text. Used by the ``opensre posthog report`` command and the scheduled |
| 6 | +delivery path (issue #3824). |
| 7 | +""" |
| 8 | + |
| 9 | +from __future__ import annotations |
| 10 | + |
| 11 | +import logging |
| 12 | +from io import StringIO |
| 13 | + |
| 14 | +from rich.console import Console |
| 15 | + |
| 16 | +from core.agent_harness.accounting.run_record import DefaultRunRecordFactory |
| 17 | +from core.agent_harness.accounting.turn_accounting import DefaultTurnAccounting |
| 18 | +from core.agent_harness.error_reporting import DefaultErrorReporter |
| 19 | +from core.agent_harness.harness import AgentHarness, HarnessConfig |
| 20 | +from core.agent_harness.prompts.prompt_context import DefaultPromptContextProvider |
| 21 | +from core.agent_harness.tools.tool_provider import DefaultToolProvider |
| 22 | +from core.agent_harness.turns.default_reasoning_client import DefaultReasoningClientProvider |
| 23 | +from core.agent_harness.turns.headless_adapters import BufferOutputSink |
| 24 | +from core.agent_harness.turns.headless_dispatch import HeadlessAgent |
| 25 | +from core.agent_harness.turns.turn_results import ShellTurnResult |
| 26 | +from integrations.posthog.report_prerequisites import ( |
| 27 | + posthog_not_configured_hint, |
| 28 | + posthog_report_available, |
| 29 | +) |
| 30 | +from platform.scheduler.agent_runner import AgentPayload |
| 31 | + |
| 32 | +logger = logging.getLogger(__name__) |
| 33 | + |
| 34 | +_REPORT_BASE_PROMPT = ( |
| 35 | + "PostHog analytics report: produce a per-metric summary report of PostHog " |
| 36 | + "product analytics. Follow the posthog-summary skill workflow." |
| 37 | +) |
| 38 | + |
| 39 | +_DEFAULT_STATS_PERIOD = "7d" |
| 40 | + |
| 41 | + |
| 42 | +def _payload_stats_period(payload: AgentPayload) -> str: |
| 43 | + period = str(payload.get("stats_period") or "").strip() |
| 44 | + return period or _DEFAULT_STATS_PERIOD |
| 45 | + |
| 46 | + |
| 47 | +def _payload_metrics(payload: AgentPayload) -> str: |
| 48 | + return str(payload.get("metrics") or "").strip() |
| 49 | + |
| 50 | + |
| 51 | +def build_report_prompt(payload: AgentPayload) -> str: |
| 52 | + """Build the fixed headless prompt for scheduled/on-demand metric reports.""" |
| 53 | + prompt = f"{_REPORT_BASE_PROMPT} Use a {_payload_stats_period(payload)} window." |
| 54 | + metrics = _payload_metrics(payload) |
| 55 | + if metrics: |
| 56 | + prompt = f"{prompt} Focus on these metrics: {metrics}." |
| 57 | + return prompt |
| 58 | + |
| 59 | + |
| 60 | +def _require_posthog_configured() -> None: |
| 61 | + if posthog_report_available(): |
| 62 | + return |
| 63 | + raise RuntimeError(posthog_not_configured_hint()) |
| 64 | + |
| 65 | + |
| 66 | +def _dispatch_headless_turn(message: str) -> ShellTurnResult: |
| 67 | + _require_posthog_configured() |
| 68 | + |
| 69 | + harness = AgentHarness( |
| 70 | + HarnessConfig( |
| 71 | + load_env=True, |
| 72 | + hydrate_integrations=True, |
| 73 | + warm_integrations=True, |
| 74 | + persistent_tasks=False, |
| 75 | + open_storage=False, |
| 76 | + ) |
| 77 | + ) |
| 78 | + startup = harness.startup() |
| 79 | + session = startup.session |
| 80 | + output = BufferOutputSink() |
| 81 | + error_reporter = DefaultErrorReporter(logger) |
| 82 | + console = Console(force_terminal=False, file=StringIO()) |
| 83 | + |
| 84 | + agent = HeadlessAgent( |
| 85 | + session=session, |
| 86 | + output=output, |
| 87 | + tools=DefaultToolProvider(session, console, tool_action_logger=logger), |
| 88 | + prompts=DefaultPromptContextProvider(session), |
| 89 | + reasoning=DefaultReasoningClientProvider( |
| 90 | + output=output, |
| 91 | + error_reporter=error_reporter, |
| 92 | + session=session, |
| 93 | + ), |
| 94 | + run_factory=DefaultRunRecordFactory(session), |
| 95 | + accounting=DefaultTurnAccounting(session, message), |
| 96 | + error_reporter=error_reporter, |
| 97 | + gather_enabled=True, |
| 98 | + is_tty=False, |
| 99 | + ) |
| 100 | + return agent.dispatch(message) |
| 101 | + |
| 102 | + |
| 103 | +def run_posthog_report(payload: AgentPayload) -> str: |
| 104 | + """Run one headless posthog-summary turn and return the assistant report.""" |
| 105 | + message = build_report_prompt(payload) |
| 106 | + result = _dispatch_headless_turn(message) |
| 107 | + report = (result.assistant_response_text or result.action_result.response_text).strip() |
| 108 | + if not result.answered or not report: |
| 109 | + raise RuntimeError( |
| 110 | + "PostHog report failed: the reasoning client did not produce a response." |
| 111 | + ) |
| 112 | + return report |
| 113 | + |
| 114 | + |
| 115 | +__all__ = ["build_report_prompt", "run_posthog_report"] |
0 commit comments