Skip to main content

FastAPI service that drafts AI Procurement Decision Cards (Kinetic Gain Protocol Suite spec #11) from a buyer rubric and vendor Suite documents. Optional audit-stream-py integration via AUDIT_STREAM_URL env var.

Project description

procurement-decision-api

CI License: MIT Python Framework: FastAPI

The machine that produces buyer-side AI procurement decisions, schema-conformant and ready to publish.

A FastAPI service that ingests a buyer's evaluation rubric plus a set of vendor Kinetic Gain Protocol Suite declarations and returns a draft AI Procurement Decision Card (spec #11 of the Suite). The Decision Card is the canonical machine-readable carrier for NIST AI RMF-aligned procurement outcomes under OMB M-24-10 — see the crosswalk doc.

The cross-ecosystem bridge

This is the first repo that composes the Kinetic Gain Protocol Suite with the Decision Intelligence Engines portfolio:

Vendor publishes:                Buyer publishes (this service produces):
─────────────────────────        ────────────────────────────────────────
AEO Protocol Card           ┐
Tool Disclosure             │
Clinical AI Card            ├──> AI Procurement Decision Card
Student AI Disclosure       │       (status / rubric / conditions /
Agent Card                  │        documents reviewed / rationale)
…the other six specs…       ┘

Quick start

pip install procurement-decision-api
procurement-decision-api  # listens on http://0.0.0.0:8088

Or via Docker:

docker run -p 8088:8088 ghcr.io/mizcausevic-dev/procurement-decision-api:latest

Then draft a decision:

curl -s http://localhost:8088/decisions/draft \
  -H 'content-type: application/json' \
  -d '{
    "decision_id": "SPRINGFIELD-DEC-2026-001",
    "buyer": {
      "name": "Springfield Unified School District",
      "type": "school-district",
      "jurisdiction": "US-CA"
    },
    "decision_maker": {
      "role": "Director of Educational Technology",
      "name": "Dr. Jane Doe",
      "authority": "Board Resolution 2026-04"
    },
    "vendor_name": "AcmeTutor Inc.",
    "product_name": "AcmeTutor 3.0",
    "vendor_id": "https://acmetutor.example/.well-known/aeo.json",
    "fetch_targets": [
      { "type": "aeo",                    "url": "https://acmetutor.example/.well-known/aeo.json" },
      { "type": "tutor-card",             "url": "https://acmetutor.example/.well-known/tutor-card.json" },
      { "type": "student-ai-disclosure",  "url": "https://acmetutor.example/.well-known/student-ai-disclosure.json" }
    ],
    "policy_uris": [
      "https://springfield.edu/.well-known/aup.json"
    ],
    "rubric": [
      { "id": "ferpa-compliance",         "result": "pass", "weight": 1.0 },
      { "id": "coppa-compliance",         "result": "pass", "weight": 1.0 },
      { "id": "no-training-on-student-data", "result": "pass-with-condition", "weight": 1.0,
        "notes": "Disclosure asserts no-training; require contractual confirmation." },
      { "id": "bias-audit-completed",     "result": "partial", "weight": 0.8,
        "notes": "Audit current but due for refresh by 2026-09." }
    ],
    "conditions": [
      { "id": "no-training-restriction",
        "description": "Vendor SHALL NOT use Springfield USD student-provided content for model training.",
        "enforcement": "contractual" },
      { "id": "bias-audit-refresh",
        "description": "Vendor SHALL deliver a refreshed third-party bias audit by 2026-12-01.",
        "enforcement": "audit" }
    ]
  }' | jq

The response includes:

  • draft — the full, schema-conformant Decision Card (ready to sign + publish at /.well-known/decisions/<id>.json)
  • documents_fetched[] — each vendor URL with its retrieval timestamp + sha256 content hash
  • fetch_errors[] — per-target retrieval errors (the draft doesn't fail wholesale on one missing URL)
  • inferred_statustrue if the service inferred the decision status from the rubric

What the service does

  1. Fetches every URL in fetch_targets concurrently with httpx, capped at 2 MB / 10 s per document, and computes a canonical sha256 hash over each (sorted keys, no whitespace).
  2. Infers decision.status from the rubric if you didn't supply proposed_status. The inference rules:
    • Any failrejected-with-remediation
    • Any partial or pass-with-conditionapproved-with-conditions
    • All passapproved
    • Empty / all n/apending
  3. Composes a default rationale from the rubric results if you didn't supply rationale_template.
  4. Validates the Decision Card against the same conditional rules the upstream zod schema enforces:
    • status ∈ {approved-with-conditions, rejected-with-remediation} → conditions must be non-empty
    • status = withdrawnwithdrawal block required
    • publication.is_public = truepublication_uri required
  5. Returns the Draft Decision Card. Review, edit, sign, publish.

Endpoints

Method Path Purpose
GET / Service info + relevant links
GET /healthz Liveness probe (always 200 if the process is running)
POST /decisions/draft Produce a Draft Decision Card
POST /decisions/validate Validate an existing Decision Card against the v0.1 schema
GET /docs Interactive OpenAPI documentation (Swagger UI)
GET /openapi.json Machine-readable API schema

Why this matters

AI procurement under OMB M-24-10 and NIST AI RMF requires agencies to publish reviewable decisions about vendor AI systems. Today, those decisions sit in PDFs and procurement databases — invisible to vendors trying to win future RFPs and invisible to citizens whose data is being processed.

The AI Procurement Decision Card spec defines a machine-readable carrier for those decisions. This service is the tool that produces them at scale: a reviewer fills in the rubric, points at the vendor's published declarations, and gets back a schema-valid card ready to publish at /.well-known/decisions/<decision_id>.json.

For procurement teams, this means a decision becomes a queryable, searchable, audit-friendly artifact — and the vendor's published declarations are cited by URL and content hash, so any drift after the decision is detectable.

Architecture

┌────────────────────────────────────────────────────────────┐
│                  FastAPI app (lifespan-managed)            │
│                                                            │
│   POST /decisions/draft                                    │
│       │                                                    │
│       ▼                                                    │
│   ┌────────────────────────────────────────────────┐       │
│   │ fetcher.fetch_documents (async, httpx)         │       │
│   │   - timeout 10s per doc                        │       │
│   │   - 2 MB size cap                              │       │
│   │   - canonical sha256 hash                      │       │
│   │   - per-target error collection                │       │
│   └────────────────────────────────────────────────┘       │
│       │                                                    │
│       ▼                                                    │
│   ┌────────────────────────────────────────────────┐       │
│   │ rubric.infer_status                            │       │
│   │ rubric.compose_rationale                       │       │
│   │ rubric.weighted_score                          │       │
│   └────────────────────────────────────────────────┘       │
│       │                                                    │
│       ▼                                                    │
│   ┌────────────────────────────────────────────────┐       │
│   │ drafter.draft_decision_card                    │       │
│   │   - validates conditional rules                │       │
│   │   - assembles history events                   │       │
│   └────────────────────────────────────────────────┘       │
│       │                                                    │
│       ▼                                                    │
│   DraftResponse                                            │
└────────────────────────────────────────────────────────────┘

Pydantic v2 models mirror the JSON Schema 2020-12 spec exactly, including the conditional rules (which run as @model_validator(mode="after") hooks).

Development

git clone https://github.com/mizcausevic-dev/procurement-decision-api
cd procurement-decision-api
pip install -e ".[dev]"

# Run the test suite (mocks the vendor HTTP layer; no internet required)
pytest -q

# Lint, format, typecheck
ruff check src tests
ruff format src tests
mypy src

# Run the service
python -m procurement_decision_api
# or
uvicorn procurement_decision_api.app:app --reload --port 8088

Composability

This service composes naturally with the rest of the Kinetic Gain ecosystem:

  • Input documents can be fetched directly from any vendor's /.well-known/ paths, or validated first via kg-validate-action in your CI.
  • Output Decision Cards can be inspected by mcp-kinetic-gain (tools: decision_card_inspect, decision_card_validate).
  • Inline validation in the browser is available at validator.kineticgain.com — paste the produced draft, get inline error markers.

License

MIT. The Kinetic Gain Protocol Suite specifications this service produces are also MIT; reference implementations like mcp-kinetic-gain are AGPL-3.0.

Related

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

procurement_decision_api-0.1.1.tar.gz (20.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

procurement_decision_api-0.1.1-py3-none-any.whl (18.9 kB view details)

Uploaded Python 3

File details

Details for the file procurement_decision_api-0.1.1.tar.gz.

File metadata

  • Download URL: procurement_decision_api-0.1.1.tar.gz
  • Upload date:
  • Size: 20.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for procurement_decision_api-0.1.1.tar.gz
Algorithm Hash digest
SHA256 23ba3e358a31b2a4c045f16e5076597a9bd028f0b44c85247b645688b99dcf75
MD5 50f99e7a762f24279ca277fa2957863a
BLAKE2b-256 b06f0af95cc6d69d891024206a5950c8fb46ebdee8d44ecc813a9b60beea4a2b

See more details on using hashes here.

Provenance

The following attestation bundles were made for procurement_decision_api-0.1.1.tar.gz:

Publisher: publish.yml on mizcausevic-dev/procurement-decision-api

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file procurement_decision_api-0.1.1-py3-none-any.whl.

File metadata

File hashes

Hashes for procurement_decision_api-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 8c66d3106c9ae64f1e2ed6d1bb4ad91334dd67dea7e0060e4478440a49196510
MD5 a5bd1e44d09c0c11938265236023ef30
BLAKE2b-256 002dc9d74a6f3924a3174612ec224bbfac796f62fdaa3be3d174eb81e5f928b2

See more details on using hashes here.

Provenance

The following attestation bundles were made for procurement_decision_api-0.1.1-py3-none-any.whl:

Publisher: publish.yml on mizcausevic-dev/procurement-decision-api

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page