Python: Add spend preflight receipt middleware sample#7116
Conversation
There was a problem hiding this comment.
Pull request overview
Adds a new Python middleware sample demonstrating a “paid tool” execution boundary pattern (preflight authorization + post-execution receipt) for agents that call tools with external spend/quota side effects, aligning with the scenario requested in #7078.
Changes:
- Introduces
SpendPreflightReceiptMiddleware, which builds a spend envelope, authorizes beforecall_next(), blocks non-approved calls, and records receipts. - Demonstrates a guarded execution-claim approach intended to prevent duplicate spend for the same tool call.
- Updates the middleware samples README to list the new sample.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| python/samples/02-agents/middleware/spend_preflight_receipt_middleware.py | New sample implementing spend envelope preflight + receipt recording around a paid tool. |
| python/samples/02-agents/middleware/README.md | Adds the new sample to the middleware samples index. |
| def build_spend_envelope(context: FunctionInvocationContext) -> tuple[SpendEnvelope, str]: | ||
| """Build the envelope and return it with its stable hash.""" | ||
| arguments = arguments_to_dict(context.arguments) | ||
| args_hash = sha256(canonical_json(arguments)) | ||
| call_id = context.metadata.get("call_id") | ||
| issued_at = datetime.now(timezone.utc) | ||
| envelope = SpendEnvelope( | ||
| function_name=context.function.name, | ||
| call_id=call_id if isinstance(call_id, str) and call_id else f"direct:{args_hash[:12]}", | ||
| args_hash=args_hash, | ||
| amount_usd=str(arguments.get("amount_usd", "0")), | ||
| payee=str(arguments.get("payee", "unknown")), | ||
| resource=str(arguments.get("dataset_name", "unknown")), | ||
| policy_version=POLICY_VERSION, | ||
| expires_at=(issued_at + timedelta(minutes=5)).isoformat(), | ||
| ) | ||
| return envelope, sha256(canonical_json(envelope.to_dict())) | ||
|
|
There was a problem hiding this comment.
Fixed in 3b384bb. The authorization hash now uses only stable envelope fields via authorization_dict(), while expires_at remains in the full envelope and receipts for audit context.
| amount = float(envelope.amount_usd) | ||
| if amount > 100: | ||
| verdict = "denied" | ||
| reason = "amount exceeds the sample hard limit" | ||
| elif amount > 50: | ||
| verdict = "requires_approval" | ||
| reason = "amount exceeds the sample auto-approval threshold" | ||
| else: | ||
| verdict = "approved" | ||
| reason = "amount is inside the sample auto-approval threshold" |
There was a problem hiding this comment.
Fixed in 3b384bb. authorize_spend now denies non-finite and negative amounts before applying the sample thresholds.
| def buy_dataset_access( | ||
| dataset_name: Annotated[str, Field(description="The dataset to purchase access for.")], | ||
| payee: Annotated[str, Field(description="The provider receiving payment.")], | ||
| amount_usd: Annotated[float, Field(description="The spend amount in USD.")], |
There was a problem hiding this comment.
Fixed in 3b384bb. amount_usd now has a non-negative finite Field constraint so invalid values are rejected at validation before middleware logic.
|
@microsoft-github-policy-service agree company="Ond Holdings Inc." |
Motivation & Context
This adds a Python middleware sample for the paid-tool preflight and receipt scenario described in #7078.
The scenario is useful for agents that may call tools with external cost or quota impact. In those cases, the application needs a checkpoint that can evaluate a canonical spend envelope before the tool body runs, deny or require approval before side effects occur, and record an execution receipt afterward.
Description & Review Guide
What are the major changes?
spend_preflight_receipt_middleware.pyunder the Python middleware samples.call_next().What is the impact of these changes?
What do you want reviewers to focus on?
python/samples/02-agents/middleware/.Related Issue
Fixes #7078
Contribution Checklist
breaking changelabel (or add "[BREAKING]" to the title prefix, before or after any language prefix) — a workflow keeps the label and title prefix in sync automatically.Validation
python3 -m py_compile python/samples/02-agents/middleware/spend_preflight_receipt_middleware.pygit diff --check HEAD~1..HEADNote: I also tried
uv run poe pyright -S, butuvis not installed in this local environment.