Skip to content

mizcausevic-dev/procurement-decision-api

Repository files navigation

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

About

FastAPI service that drafts AI Procurement Decision Cards (Kinetic Gain Protocol Suite spec #11) from a buyer rubric and vendor Suite documents. First cross-ecosystem bridge: Suite x Decision Intelligence.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors