Add SafeGuard Privacy vendor approval gate for deal requests#82
Open
tdanielcox wants to merge 2 commits intoIABTechLab:mainfrom
Open
Add SafeGuard Privacy vendor approval gate for deal requests#82tdanielcox wants to merge 2 commits intoIABTechLab:mainfrom
tdanielcox wants to merge 2 commits intoIABTechLab:mainfrom
Conversation
added 2 commits
April 22, 2026 16:01
Introduces an optional integration with the SafeGuard Privacy (SGP) platform that lets buyers gate Deal ID generation on the `iabBuyerAgentApproval` flag they maintain for each vendor in their SGP tenant. The integration is off by default — when SGP_API_KEY is empty the buyer agent behaves exactly as before. New components - SGPClient: async httpx client for the SGP buyer-agent-approval endpoint. Domain normalization, dedupe, batching to 10 per request, TTL cache, and full HTTP status handling. Transport errors are wrapped as SGPClientError so the deal-request gate fails closed. - ApprovalRecord pydantic model mirroring IabBuyerAgentResource. - SGPVendorApprovalTool: CrewAI tool wired into the Buyer Deal Specialist so the agent can consult approval status during product selection, not just at Deal ID generation. Class is SGP-prefixed to leave room for future vendor-approval sources. Integration points - DiscoverInventoryTool annotates each product row with APPROVED / NOT APPROVED / UNKNOWN when an SGPClient is provided. Discovery fails open on SGP transport errors so outages do not break browsing. - RequestDealTool adds a pre-flight gate. When SGP_ENFORCE_ON_DEAL_REQUEST is true and an SGPClient is configured, Deal IDs are not issued for unapproved vendors. Unknown-vendor behavior is governed by SGP_UNKNOWN_VENDOR_POLICY (block | warn | allow; default block). - BuyerDealFlow auto-instantiates the client from settings and logs a warning if enforcement is configured without an API key. Configuration - SGP_API_KEY, SGP_BASE_URL, SGP_ENFORCE_ON_DEAL_REQUEST, SGP_UNKNOWN_VENDOR_POLICY, SGP_CACHE_TTL_SECONDS. Defaults point at the production SGP endpoint; staging is noted inline. Docs - docs/integration/safeguard-privacy.md covers endpoint contract, config, behavior matrix, and troubleshooting. Added to mkdocs nav, the configuration guide, and the README Key Features section. Tests - 31 new unit tests covering the client (normalization, batching, all HTTP statuses, transport errors, caching), the gate (approved, denied, unknown policies, fail-closed on errors, missing seller domain), and flow-level tool wiring.
Scope is limited to lines introduced by this PR: - Trailing newlines added to new files (W292) - Optional[X] → X | None on our new type annotations (UP045) - Split an over-long logger.warning line in discover_inventory.py (E501) - Minor whitespace/formatting adjustments from `ruff format` on our five new files only Pre-existing lint issues in files we also modified (unused imports, datetime.timezone.utc → datetime.UTC, E501 on the pre-existing RequestDealInput description, I001 import-group order caused by upstream `events.*` placement) are intentionally left alone — they are out of scope for this PR. Tests: 2704 passed, 41 skipped.
| The client calls a single endpoint on the SafeGuard Privacy platform: | ||
|
|
||
| ``` | ||
| GET /api/v1/integrations/iab/buyer-agent-approval?domain=a.com,b.com |
There was a problem hiding this comment.
Why does the buyer agent need to make the choices and status of their vendor approvals public? is this a public endpoint available to anyone that can query the buyer agent or is it private?
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
Adds an optional integration with the SafeGuard Privacy (SGP) platform that lets buyers gate Deal ID generation on the
iabBuyerAgentApprovalflag they maintain for each vendor in their SGP tenant. The integration is off by default. WhenSGP_API_KEYis empty, the buyer agent behaves exactly as before — no calls, no warnings, no behavior change.The approval is the buyer's own mark on their vendors; SGP is the system of record. This PR wires the buyer agent to consult that record before committing to a programmatic deal.
Why
Privacy-conscious buyers want programmatic tooling to refuse deals against sellers they haven't explicitly cleared. Today that check happens out-of-band (SGP dashboard, email approvals, spreadsheets) and breaks down when agents choose inventory autonomously. This PR brings the check inline — both as a hard gate at Deal ID time and as an agent-callable capability during inventory reasoning.
What's new
Client and model
src/ad_buyer/clients/sgp_client.pySGPClient— async httpx client forGET /api/v1/integrations/iab/buyer-agent-approval. Domain normalization, dedupe, batching to 10/request, per-domain TTL cache, full HTTP status handling. Transport errors wrap asSGPClientErrorso callers catch one type.src/ad_buyer/models/sgp.pyApprovalRecordpydantic model mirroringIabBuyerAgentResource.Tools
src/ad_buyer/tools/research/sgp_vendor_approval.pySGPVendorApprovalTool— CrewAI tool (namecheck_sgp_vendor_approval) wired into the Buyer Deal Specialist so the agent can consult approval status during product selection, not only at Deal ID time. Class isSGP-prefixed so future vendor-approval integrations can coexist.Integration points (existing tools extended)
DiscoverInventoryTool— accepts an optionalSGPClient. When provided, each returned product is annotated withAPPROVED/NOT APPROVED/UNKNOWN. Discovery fails open on SGP transport errors so outages never break browsing.RequestDealTool— optional pre-flight gate after product lookup and before Deal ID generation. Fails closed on transport errors when enforcement is on. Unknown-vendor behavior is governed bySGP_UNKNOWN_VENDOR_POLICY(block/warn/allow).BuyerDealFlow— auto-instantiatesSGPClientfrom settings whenSGP_API_KEYis set and threads it through to the tools above. Logs a warning if enforcement is configured without an API key (catches the silent-bypass misconfiguration).Configuration
Five new env vars in
.env.exampleandsrc/ad_buyer/config/settings.py:SGP_API_KEYstr""iab:buyerAgentscope. Empty = integration disabled.SGP_BASE_URLstrhttps://api.safeguardprivacy.comhttps://api.safeguardprivacy-demo.com.SGP_ENFORCE_ON_DEAL_REQUESTboolfalsetrue,RequestDealToolblocks Deal ID generation for unapproved vendors.SGP_UNKNOWN_VENDOR_POLICYstrblockSGP_CACHE_TTL_SECONDSint900Behavior matrix
With enforcement on (
SGP_ENFORCE_ON_DEAL_REQUEST=trueandSGP_API_KEYset):block(default)warnallowiabBuyerAgentApproval: trueiabBuyerAgentApproval: falseAn explicit
iabBuyerAgentApproval: falseis always fatal — the policy only governs the unknown-vendor case.Zero-config compatibility
A default install (no
SGP_API_KEY, no env overrides) produces exactly the same behavior as before this PR:RequestDealToolskips the gate entirelySGPVendorApprovalToolis not instantiated or added to any agent's toolboxAll new constructor parameters are optional with backward-compatible defaults. No existing call sites require changes.
Documentation
docs/integration/safeguard-privacy.md— endpoint contract, config, behavior matrix, troubleshootingdocs/guides/configuration.md(SGP env var table),mkdocs.yml(nav entry),README.md(Key Features)Test plan
ruff checkandruff format --checkpass for all SGP-authored codeSGP_API_KEYagainst a live SGP tenant, run a deal request against an approved vs. unapproved seller domainTest coverage breakdown
Client (
tests/unit/test_sgp_client.py)SGPAuthError, 400 raisesSGPClientError, 503 raisesSGPClientErrorwith status codehttpx.ConnectErrorwrapped viaMockTransportGate (
tests/unit/test_sgp_gate.py)sgp_enforce=Falseboth bypass cleanlyblock/warn/alloweach honoredValueErrorat constructionBuyerDealFlowwiresSGPVendorApprovalToolinto the agent when SGP is configured, omits when notCI status
ruff check src/ tests/main.ruff format --check src/ tests/main.pytest tests/ --cov=src/ad_buyerdocker build -f infra/docker/Dockerfile .main. Introduced inab66e6f feat: add IaC deployment infrastructure. The runtime stage copiessrc/andpyproject.tomlbut notREADME.md; hatchling's editable install then fails withOSError: Readme file does not exist: README.md. Reproducible from a clean checkout ofmainwithout these changes. Out of scope for this PR.