adloop 0.18.0__tar.gz → 0.18.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {adloop-0.18.0 → adloop-0.18.1}/PKG-INFO +1 -1
- {adloop-0.18.0 → adloop-0.18.1}/pyproject.toml +1 -1
- {adloop-0.18.0 → adloop-0.18.1}/pyproject.toml.orig +1 -1
- {adloop-0.18.0 → adloop-0.18.1}/src/adloop/cli.py +9 -0
- {adloop-0.18.0 → adloop-0.18.1}/src/adloop/config.py +10 -0
- {adloop-0.18.0 → adloop-0.18.1}/src/adloop/merchant/read.py +13 -1
- {adloop-0.18.0 → adloop-0.18.1}/src/adloop/rules/adloop.md +1 -1
- {adloop-0.18.0 → adloop-0.18.1}/src/adloop/server.py +2 -1
- {adloop-0.18.0 → adloop-0.18.1}/README.md +0 -0
- {adloop-0.18.0 → adloop-0.18.1}/src/adloop/__init__.py +0 -0
- {adloop-0.18.0 → adloop-0.18.1}/src/adloop/__main__.py +0 -0
- {adloop-0.18.0 → adloop-0.18.1}/src/adloop/_mcp_patches.py +0 -0
- {adloop-0.18.0 → adloop-0.18.1}/src/adloop/ads/__init__.py +0 -0
- {adloop-0.18.0 → adloop-0.18.1}/src/adloop/ads/ai_max.py +0 -0
- {adloop-0.18.0 → adloop-0.18.1}/src/adloop/ads/brands.py +0 -0
- {adloop-0.18.0 → adloop-0.18.1}/src/adloop/ads/client.py +0 -0
- {adloop-0.18.0 → adloop-0.18.1}/src/adloop/ads/conversion_actions.py +0 -0
- {adloop-0.18.0 → adloop-0.18.1}/src/adloop/ads/currency.py +0 -0
- {adloop-0.18.0 → adloop-0.18.1}/src/adloop/ads/custom_conversion_goals.py +0 -0
- {adloop-0.18.0 → adloop-0.18.1}/src/adloop/ads/enums.py +0 -0
- {adloop-0.18.0 → adloop-0.18.1}/src/adloop/ads/forecast.py +0 -0
- {adloop-0.18.0 → adloop-0.18.1}/src/adloop/ads/gaql.py +0 -0
- {adloop-0.18.0 → adloop-0.18.1}/src/adloop/ads/pmax.py +0 -0
- {adloop-0.18.0 → adloop-0.18.1}/src/adloop/ads/read.py +0 -0
- {adloop-0.18.0 → adloop-0.18.1}/src/adloop/ads/validate_only.py +0 -0
- {adloop-0.18.0 → adloop-0.18.1}/src/adloop/ads/write.py +0 -0
- {adloop-0.18.0 → adloop-0.18.1}/src/adloop/auth.py +0 -0
- {adloop-0.18.0 → adloop-0.18.1}/src/adloop/crossref.py +0 -0
- {adloop-0.18.0 → adloop-0.18.1}/src/adloop/diagnostics.py +0 -0
- {adloop-0.18.0 → adloop-0.18.1}/src/adloop/ga4/__init__.py +0 -0
- {adloop-0.18.0 → adloop-0.18.1}/src/adloop/ga4/client.py +0 -0
- {adloop-0.18.0 → adloop-0.18.1}/src/adloop/ga4/reports.py +0 -0
- {adloop-0.18.0 → adloop-0.18.1}/src/adloop/ga4/tracking.py +0 -0
- {adloop-0.18.0 → adloop-0.18.1}/src/adloop/ga4/write.py +0 -0
- {adloop-0.18.0 → adloop-0.18.1}/src/adloop/gsc/__init__.py +0 -0
- {adloop-0.18.0 → adloop-0.18.1}/src/adloop/gsc/client.py +0 -0
- {adloop-0.18.0 → adloop-0.18.1}/src/adloop/gsc/reports.py +0 -0
- {adloop-0.18.0 → adloop-0.18.1}/src/adloop/gtm/__init__.py +0 -0
- {adloop-0.18.0 → adloop-0.18.1}/src/adloop/gtm/client.py +0 -0
- {adloop-0.18.0 → adloop-0.18.1}/src/adloop/gtm/read.py +0 -0
- {adloop-0.18.0 → adloop-0.18.1}/src/adloop/merchant/__init__.py +0 -0
- {adloop-0.18.0 → adloop-0.18.1}/src/adloop/merchant/client.py +0 -0
- {adloop-0.18.0 → adloop-0.18.1}/src/adloop/pagespeed.py +0 -0
- {adloop-0.18.0 → adloop-0.18.1}/src/adloop/reddit/__init__.py +0 -0
- {adloop-0.18.0 → adloop-0.18.1}/src/adloop/reddit/auth.py +0 -0
- {adloop-0.18.0 → adloop-0.18.1}/src/adloop/reddit/client.py +0 -0
- {adloop-0.18.0 → adloop-0.18.1}/src/adloop/reddit/read.py +0 -0
- {adloop-0.18.0 → adloop-0.18.1}/src/adloop/reddit/schedule.py +0 -0
- {adloop-0.18.0 → adloop-0.18.1}/src/adloop/reddit/write.py +0 -0
- {adloop-0.18.0 → adloop-0.18.1}/src/adloop/rules/__init__.py +0 -0
- {adloop-0.18.0 → adloop-0.18.1}/src/adloop/rules/commands/analyze-performance.md +0 -0
- {adloop-0.18.0 → adloop-0.18.1}/src/adloop/rules/commands/budget-plan.md +0 -0
- {adloop-0.18.0 → adloop-0.18.1}/src/adloop/rules/commands/create-ad.md +0 -0
- {adloop-0.18.0 → adloop-0.18.1}/src/adloop/rules/commands/create-campaign.md +0 -0
- {adloop-0.18.0 → adloop-0.18.1}/src/adloop/rules/commands/diagnose-tracking.md +0 -0
- {adloop-0.18.0 → adloop-0.18.1}/src/adloop/rules/commands/optimize-campaign.md +0 -0
- {adloop-0.18.0 → adloop-0.18.1}/src/adloop/rules_install.py +0 -0
- {adloop-0.18.0 → adloop-0.18.1}/src/adloop/runtime.py +0 -0
- {adloop-0.18.0 → adloop-0.18.1}/src/adloop/safety/__init__.py +0 -0
- {adloop-0.18.0 → adloop-0.18.1}/src/adloop/safety/audit.py +0 -0
- {adloop-0.18.0 → adloop-0.18.1}/src/adloop/safety/guards.py +0 -0
- {adloop-0.18.0 → adloop-0.18.1}/src/adloop/safety/preview.py +0 -0
- {adloop-0.18.0 → adloop-0.18.1}/src/adloop/tracking.py +0 -0
|
@@ -156,6 +156,7 @@ def _generate_config_yaml(
|
|
|
156
156
|
gsc_site_url: str = "",
|
|
157
157
|
gtm_account_id: str = "",
|
|
158
158
|
gtm_container_id: str = "",
|
|
159
|
+
merchant_account_id: str = "",
|
|
159
160
|
pagespeed_api_key: str = "",
|
|
160
161
|
reddit_client_id: str = "",
|
|
161
162
|
reddit_client_secret: str = "",
|
|
@@ -206,6 +207,11 @@ def _generate_config_yaml(
|
|
|
206
207
|
f' account_id: "{gtm_account_id}"',
|
|
207
208
|
f' container_id: "{gtm_container_id}"',
|
|
208
209
|
"",
|
|
210
|
+
"merchant:",
|
|
211
|
+
" # Default Merchant Center ID for get_merchant_feed_health",
|
|
212
|
+
" # (see list_merchant_accounts). Empty = pass account_id per call.",
|
|
213
|
+
f' account_id: "{merchant_account_id}"',
|
|
214
|
+
"",
|
|
209
215
|
"pagespeed:",
|
|
210
216
|
" # Optional API key for analyze_page_speed (keyless works, low quota).",
|
|
211
217
|
f' api_key: "{pagespeed_api_key}"',
|
|
@@ -844,6 +850,9 @@ def _run_wizard_post_config(
|
|
|
844
850
|
gsc_site_url=gsc_site_url,
|
|
845
851
|
gtm_account_id=gtm_account_id,
|
|
846
852
|
gtm_container_id=gtm_container_id,
|
|
853
|
+
# No Merchant discovery in the wizard; keep a hand-set default
|
|
854
|
+
# across re-runs instead of wiping it.
|
|
855
|
+
merchant_account_id=_existing("merchant", "account_id"), # type: ignore[operator]
|
|
847
856
|
pagespeed_api_key=pagespeed_api_key,
|
|
848
857
|
reddit_client_id=reddit_values["client_id"],
|
|
849
858
|
reddit_client_secret=reddit_values["client_secret"],
|
|
@@ -43,6 +43,11 @@ class GtmConfig:
|
|
|
43
43
|
container_id: str = ""
|
|
44
44
|
|
|
45
45
|
|
|
46
|
+
@dataclass
|
|
47
|
+
class MerchantConfig:
|
|
48
|
+
account_id: str = "" # numeric Merchant Center ID (see list_merchant_accounts)
|
|
49
|
+
|
|
50
|
+
|
|
46
51
|
@dataclass
|
|
47
52
|
class PageSpeedConfig:
|
|
48
53
|
api_key: str = "" # optional — keyless PSI calls work but are rate-limited
|
|
@@ -85,6 +90,7 @@ class AdLoopConfig:
|
|
|
85
90
|
ads: AdsConfig = field(default_factory=AdsConfig)
|
|
86
91
|
gsc: GscConfig = field(default_factory=GscConfig)
|
|
87
92
|
gtm: GtmConfig = field(default_factory=GtmConfig)
|
|
93
|
+
merchant: MerchantConfig = field(default_factory=MerchantConfig)
|
|
88
94
|
pagespeed: PageSpeedConfig = field(default_factory=PageSpeedConfig)
|
|
89
95
|
reddit: RedditConfig = field(default_factory=RedditConfig)
|
|
90
96
|
safety: SafetyConfig = field(default_factory=SafetyConfig)
|
|
@@ -144,6 +150,7 @@ def load_config(config_path: str | None = None) -> AdLoopConfig:
|
|
|
144
150
|
ads_raw = raw.get("ads", {})
|
|
145
151
|
gsc_raw = raw.get("gsc", {})
|
|
146
152
|
gtm_raw = raw.get("gtm", {})
|
|
153
|
+
merchant_raw = raw.get("merchant", {}) or {}
|
|
147
154
|
pagespeed_raw = raw.get("pagespeed", {})
|
|
148
155
|
reddit_raw = raw.get("reddit", {}) or {}
|
|
149
156
|
safety_raw = raw.get("safety", {})
|
|
@@ -169,6 +176,9 @@ def load_config(config_path: str | None = None) -> AdLoopConfig:
|
|
|
169
176
|
account_id=_text(gtm_raw, "account_id"),
|
|
170
177
|
container_id=_text(gtm_raw, "container_id"),
|
|
171
178
|
),
|
|
179
|
+
merchant=MerchantConfig(
|
|
180
|
+
account_id=_text(merchant_raw, "account_id"),
|
|
181
|
+
),
|
|
172
182
|
pagespeed=PageSpeedConfig(
|
|
173
183
|
api_key=_text(pagespeed_raw, "api_key"),
|
|
174
184
|
),
|
|
@@ -124,7 +124,19 @@ def get_merchant_feed_health(
|
|
|
124
124
|
register_gcp,
|
|
125
125
|
)
|
|
126
126
|
|
|
127
|
-
account_id = str(account_id or "").strip()
|
|
127
|
+
account_id = str(account_id or "").strip() or config.merchant.account_id.strip()
|
|
128
|
+
if not account_id:
|
|
129
|
+
return {
|
|
130
|
+
"error": (
|
|
131
|
+
"account_id is required: no Merchant Center account was "
|
|
132
|
+
"passed and no default is configured."
|
|
133
|
+
),
|
|
134
|
+
"hint": (
|
|
135
|
+
"Call list_merchant_accounts to find the numeric Merchant "
|
|
136
|
+
"Center ID and pass it as account_id, or set "
|
|
137
|
+
"merchant.account_id in the config to make it the default."
|
|
138
|
+
),
|
|
139
|
+
}
|
|
128
140
|
if not account_id.isdigit():
|
|
129
141
|
return {
|
|
130
142
|
"error": "account_id must be a numeric Merchant Center ID — "
|
|
@@ -125,7 +125,7 @@ These tools call both APIs internally and return unified results with computed `
|
|
|
125
125
|
| Tool | When to Use | Key Parameters |
|
|
126
126
|
|------|-------------|----------------|
|
|
127
127
|
| `list_merchant_accounts` | Discovery — which Merchant Center accounts the connected user can reach | (none) |
|
|
128
|
-
| `get_merchant_feed_health` | Shopping/PMax underperforming or dead — check feed disapprovals FIRST, before touching bids/budgets | `account_id` (numeric; from list_merchant_accounts) |
|
|
128
|
+
| `get_merchant_feed_health` | Shopping/PMax underperforming or dead — check feed disapprovals FIRST, before touching bids/budgets | `account_id` (numeric; from list_merchant_accounts — optional when `merchant.account_id` is configured) |
|
|
129
129
|
|
|
130
130
|
- **Disapproved feed items are the #1 silent PMax/Shopping killer.** When a Shopping or PMax campaign has impressions collapsing or zero delivery, check `get_merchant_feed_health` before any campaign-side diagnosis.
|
|
131
131
|
- The Merchant API has no read-only scope; AdLoop's merchant tools are read-only by design — never propose feed edits through these tools. Product-status data lags ~30 minutes — say so when the user just changed the feed.
|
|
@@ -718,7 +718,7 @@ def list_merchant_accounts() -> dict:
|
|
|
718
718
|
|
|
719
719
|
@mcp.tool(title="Merchant Center feed health", annotations=_READONLY, tags={"merchant"})
|
|
720
720
|
@_safe
|
|
721
|
-
def get_merchant_feed_health(account_id: str) -> dict:
|
|
721
|
+
def get_merchant_feed_health(account_id: str = "") -> dict:
|
|
722
722
|
"""Merchant Center feed health — disapproved products + account issues.
|
|
723
723
|
|
|
724
724
|
Disapproved feed items silently starve Shopping and Performance Max
|
|
@@ -728,6 +728,7 @@ def get_merchant_feed_health(account_id: str) -> dict:
|
|
|
728
728
|
account-level issues — CRITICAL ones stop offers serving entirely.
|
|
729
729
|
|
|
730
730
|
account_id: numeric Merchant Center ID from list_merchant_accounts.
|
|
731
|
+
Defaults to merchant.account_id in the config.
|
|
731
732
|
Product-status data lags reality by ~30 minutes.
|
|
732
733
|
"""
|
|
733
734
|
from adloop.merchant.read import get_merchant_feed_health as _impl
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|