feat(analytics): per-campaign fan-out + audit_creative + analyze_budget_efficiency (#120)#139
Merged
Merged
Conversation
467f2df to
895bacb
Compare
e253f00 to
a67ab0a
Compare
895bacb to
d128bb2
Compare
a67ab0a to
6561635
Compare
Confirmed end-to-end during validation: the built-in google_ads / meta_ads analytics adapters silently returned `cost=0` and `conversions=0` in BYOD mode because the aggregators assumed the live row shape (metrics nested under `row["metrics"]` for Google, conversion counts inside `row["actions"]` for Meta), while `ByodGoogleAdsClient.get_performance_report` returns metrics flat at the top level and `ByodMetaAdsClient` returns conversions as a top-level field with no `actions` list. Both shapes are valid outputs of `mureo.mcp._client_factory.get_*_client`, so the aggregator has to accept either. Introduce helpers `_google_row_metrics` and `_meta_row_conversions` that try the live shape first and fall back to the flat shape, and route both the anomaly aggregators and the diagnose_performance summarisers through them. Validated post-fix against the local BYOD bundle: - google_ads: 4 campaigns, spend = 4,256,000, CV 478.4, CPA 8,895 - meta_ads: 4 campaigns, spend = 854,000, CV 306.6, CPA 2,785 Regression tests added for both shapes; full analytics suite 96 → 98 passing, zero regressions in the full repo suite. Refs #120
…et_efficiency (#120) Three additions on top of the prior live-wiring PR, all on the same adapter contract: 1. **Per-campaign fan-out for `detect_anomalies`**. The live fetcher now returns `{campaign_id: (current, baseline)}` and the adapter runs the pure detector once per campaign, surfacing anomalies the previous account-level aggregation masked when one campaign moved opposite to another. Day-grain rows summed, new campaigns get `baseline=None`, zero-spend baselines dropped. Legacy `MetricsFetcher` aggregate-path injection still works (back-compat with the existing suite). 2. **`audit_creative`** for both adapters. Google flags RSAs with <3 headlines or <2 descriptions (UI policy), RSAs with fewer than the recommended 15 headlines (Ad Strength), and RDAs missing long-headline / descriptions / marketing images. Meta flags ads without primary text, title, image, or call_to_action. Pure scorer (`_creative_audit.py`) consumes whatever shape `list_ads` returns — Live nested-creative and BYOD flat both accepted. 3. **`analyze_budget_efficiency`** for both adapters. Pure scorer (`_budget_efficiency.py`) normalises `conversions/cost` to [0,1] relative to the top performer, flags campaigns below the 0.3 threshold, and emits a reallocation suggestion when at least one campaign clears 0.7. Zero-conversion accounts get a tracking-warning sentinel instead of a misleading score. Both adapters now advertise all four `AnalyticsCapability` values. The MCP tool `mureo_analytics_modules_list` picks this up dynamically — no schema change. Shared BYOD-tolerance helpers (`google_row_metrics`, `meta_row_conversions`) promoted from `_live_clients` to `_common.py` so the three callers — aggregator, summariser, budget scorer — share one definition. `_resolve_per_campaign_metrics` return type tightened to `dict[str, tuple[CampaignMetrics, CampaignMetrics | None]] | None` so mypy sees `baseline.cost` is a real attribute. Validated end-to-end against the local BYOD bundle: - analyze_budget_efficiency: identified 2 inefficient vs 2 efficient campaigns with concrete reallocation suggestion text. - detect_anomalies + per-campaign fan-out: empty (in-window data is steady) — fan-out behaviour pinned by adapter-level tests. Coverage: 93.6% across `mureo/analytics/*` (49 new tests, 74 → 123). Lint + mypy clean for the analytics package. Zero regressions vs the prior branch. Code-review findings addressed: - HIGH: `_resolve_per_campaign_metrics` return-type tightening - HIGH: removed inline per-loop import in `_summarise_performance` - MEDIUM: dropped unreachable branch in `score_budget_efficiency` - MEDIUM: promoted private BYOD-tolerance helpers to public `_common` Refs #120
6561635 to
e6b4e45
Compare
hyoshi
added a commit
that referenced
this pull request
May 23, 2026
…atforms (#120) (#142) Bump version across pyproject.toml / mureo/__init__.py / .claude-plugin/plugin.json and add the 0.9.9 CHANGELOG entry summarising the five PRs that shipped the analytics-module work (#137, #138, #139, #140, #141). Headline: - opt-in `AnalyticsModule` Protocol + registry + entry-point group `mureo.analytics`, so external-integration platforms (official MCPs and third-party plugins) can plug into mureo's deep analytics on the same terms as the built-in adapters - built-in google_ads / meta_ads adapters wired against live + BYOD clients with sentinel handling for missing credentials - per-campaign fan-out for `detect_anomalies`, full implementations of `audit_creative` and `analyze_budget_efficiency`, and DEEP scope drilldown for `diagnose_performance` - 10 row-shape TypedDicts re-exported from `mureo.analytics` for plugin-side typing - skill prompts (daily-check, rescue, _mureo-shared) consult the new `mureo_analytics_modules_list` MCP tool and report `analytics_not_available_for_<platform>` honestly when a module is absent Refs #120
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Three additions on top of the live-wiring PR:
detect_anomalies— replaces the account-level aggregation that masked single-campaign anomalies when an offsetting campaign moved opposite. Newfetch_*_per_campaign_metricsreturns{campaign_id: (current, baseline)}and the adapter runs the pure detector once per campaign.audit_creative— Google flags RSAs with <3 headlines / <2 descriptions / below-recommended-15 headlines, RDAs missing copy / image. Meta flags ads without primary text / title / image / call_to_action.analyze_budget_efficiency— normalisesconversions/costacross campaigns to [0,1] relative to the top performer, flags campaigns below 0.3, emits a reallocation suggestion when a clear split exists.Both adapters now advertise all four
AnalyticsCapabilityvalues; the MCPmureo_analytics_modules_listpicks this up dynamically — no schema change.Shared BYOD-tolerance helpers (
google_row_metrics,meta_row_conversions) promoted from_live_clientsto public_common.pyso the three callers (aggregator, summariser, budget scorer) share one definition.Test plan
_resolve_per_campaign_metricsreturn type tightened fromobjecttoCampaignMetrics; inline per-loop import removed) + MEDIUM (dead branch inscore_budget_efficiencydropped; private helpers promoted) addressedanalyze_budget_efficiencycorrectly identified 2 inefficient vs 2 efficient campaigns and produced a real reallocation suggestionmainStacked on #PR2 (
feat/analytics-builtin-live-wiring).Refs #120