Skip to main content

Modelgov API client. `feature` and `userType` are mandatory on every request.

Project description

Modelgov Python SDK

Package: modelgov (module modelgov). The Python counterpart to @modelgov/sdk.

The SDK is a thin HTTP client to the Modelgov API. Policy enforcement is always server-side. Every request declares a user, user type, and feature; policy is checked before the model call.

Install

pip install modelgov

Note: modelgov is not yet published to PyPI. Until then, install from source with the editable install below (see also self-host.md).

From the monorepo (editable, with test deps):

pip install -e "packages/sdk-python[dev]"

Requires Python >= 3.9. Depends on httpx.

Create a client

import os
from modelgov import ModelgovClient

ai = ModelgovClient(
    base_url=os.environ.get("MODELGOV_URL", "http://localhost:3000"),
    api_key=os.environ["MODELGOV_API_KEY"],
)

ModelgovClient is a context manager and closes its connection pool on exit:

with ModelgovClient(base_url=..., api_key=...) as ai:
    ...

Chat

res = ai.chat(
    user_id="user_123",        # your end-user id
    user_type="logged_in",     # must match modelgov.yaml budgets
    feature="support_chat",    # required — registered feature
    model_class="cheap",
    messages=[{"role": "user", "content": "Help me reset my password"}],
    # optional:
    # input_tokens_estimate=120,
    # temperature=0.7,
    # project_id="checkout",
    # environment="production",
    # metadata={"trace_id": "abc"},
)

print(res["message"]["content"])
print(res["model"], res["decision"], res["requestId"])

Snake_case keyword args are converted to the camelCase JSON the API expects (user_iduserId, model_classmodelClass, etc.). None-valued optional args are omitted from the request body.

Response

chat() returns a ChatResponse (a TypedDict), so it is a plain dict with typed keys:

{
  "message": {"role": "assistant", "content": "..."},
  "model": "openai/gpt-4o-mini",
  "decision": "allow",             # "allow" | "degrade" | "fallback"
  "usage": {"inputTokens": 12, "outputTokens": 8},
  "cost": {"estimatedUsd": 0.0001, "actualUsd": 0.00008},
  "budgetRemaining": {"userDailyUsd": 0.24, "featureMonthlyUsd": None, "globalMonthlyUsd": 499.5},
  "safety": {"piiMasked": False, "injectionBlocked": False},
  "requestId": "req_42",           # audit id — log with your domain ids
}

Vision (multimodal)

Pass content parts instead of a string to send images to a vision model. The gateway governs budget/audit and still runs safety on the text parts:

res = ai.chat(
    user_id="user_123",
    user_type="logged_in",
    feature="document_extraction",
    messages=[{
        "role": "user",
        "content": [
            {"type": "text", "text": "Extract the total from this receipt."},
            {"type": "image_url", "image_url": {"url": "data:image/png;base64,..."}},
        ],
    }],
)

Grounding

For a feature with safety grounding: strict, pass retrieved passages as context. The gateway answers only from them, forces verbatim citations, and verifies them — unverifiable answers become a safe refusal, and res["safety"]["grounded"] reports whether the citations checked out:

res = ai.chat(
    user_id="user_123",
    user_type="logged_in",
    feature="grounded_support",
    messages=[{"role": "user", "content": "How long do refunds take?"}],
    context=["Refunds are issued within 5 business days of approval."],
)

Streaming

chat_stream() yields incremental text chunks over Server-Sent Events. It sends "stream": true and iterates data: lines until the [DONE] sentinel.

for chunk in ai.chat_stream(
    user_id="user_123",
    user_type="logged_in",
    feature="support_chat",
    messages=[{"role": "user", "content": "Write a haiku about budgets"}],
):
    print(chunk, end="", flush=True)

SSE framing assumption: OpenAI-style events — one JSON payload per data: line, terminated by data: [DONE]. Text is read from choices[0].delta.content (or a simpler delta / content / text field). Non-JSON data: payloads are yielded verbatim. See the chat_stream docstring if the server's framing differs.

The generator holds the connection open until fully consumed. Policy/safety blocks that occur before the stream begins raise the usual typed errors.

Embeddings

embed() runs governed embeddings (POST /v1/embeddings) — policy-checked, budget-reserved, and audited like chat(). Pass one string or a batch:

res = ai.embed(
    user_id="user_123",
    user_type="logged_in",
    feature="rag_ingest",
    input=["first passage", "second passage"],   # or a single string
)
vectors = res["embeddings"]   # one vector per input, in request order

Idempotency

Pass a stable key to retry safely without double-charging budget or re-calling the model:

ai.chat(
    user_id="user_123",
    user_type="logged_in",
    feature="support_chat",
    messages=[{"role": "user", "content": "..."}],
    idempotency_key=f"chat-{user_id}-{session_id}",
)

The API returns x-idempotent-replay: true on cache hits; a same-key request with a different body returns 422 idempotency_key_reuse.

Explain (dry run)

Evaluate policy without calling the model or reserving budget:

plan = ai.explain(
    user_id="user_123",
    user_type="logged_in",
    feature="support_chat",
    model_class="premium",
)
print(plan["decision"], plan["summary"])

Usage

Requires an API key with usage:read.

usage = ai.get_usage(user_id="user_123")
summary = ai.get_usage_summary(feature="support_chat", since="7d")

Errors

Class When
PolicyBlockedError 403 policy_blocked or budget_exceeded
SafetyBlockedError 403 safety_blocked (PII or prompt injection)
ModelgovError Other 4xx / 5xx

PolicyBlockedError and SafetyBlockedError subclass ModelgovError. Each error carries the API's structured envelope:

from modelgov import ModelgovError, PolicyBlockedError, SafetyBlockedError

try:
    ai.chat(
        user_id="user_123",
        user_type="logged_in",
        feature="support_chat",
        messages=[{"role": "user", "content": "..."}],
    )
except PolicyBlockedError as err:
    print(err.status)            # 403
    print(err.code)              # "policy_blocked" | "budget_exceeded"
    print(err.message)           # human-readable
    print(err.details)           # error.details object
    print(err.audit_request_id)  # "req_<n>" — modelgov requests show
    print(err.request_id)        # HTTP trace id (UUID)
    print(err.body)              # full parsed envelope
except ModelgovError as err:
    ...

Integration pattern

1. Authenticate user (your app)
2. Authorize product action (your app)
3. ai.chat(user_id=..., user_type=..., feature=..., messages=...)
4. Return res["message"]["content"] to the user

Never call Modelgov before your app has decided the user may use this feature.

Development

pip install -e "packages/sdk-python[dev]"
cd packages/sdk-python
pytest

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

modelgov-1.0.0.tar.gz (14.5 kB view details)

Uploaded Source

Built Distribution

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

modelgov-1.0.0-py3-none-any.whl (14.2 kB view details)

Uploaded Python 3

File details

Details for the file modelgov-1.0.0.tar.gz.

File metadata

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

File hashes

Hashes for modelgov-1.0.0.tar.gz
Algorithm Hash digest
SHA256 18644080e51d189fa4460a35033c9e41ea8867a7c173d19f66f9b462ed187afd
MD5 98ede7763b046568c93c4799c4e5bd9a
BLAKE2b-256 2abc95d7dc64fc3e5f170080aeb3f49659b64efa7f94d849acbf23ea93385c46

See more details on using hashes here.

Provenance

The following attestation bundles were made for modelgov-1.0.0.tar.gz:

Publisher: release.yml on mml555/modelgov

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

File details

Details for the file modelgov-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: modelgov-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 14.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for modelgov-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 3e2c871b80726af2522cbcb8ca871cad0524b847ff897add9a2e2e11d4278267
MD5 43097ac57555d742628f78658d6955b8
BLAKE2b-256 03a8e0d23a12269f0e0342b6cb4204f20e05d3259231d91348907f51cdeef19e

See more details on using hashes here.

Provenance

The following attestation bundles were made for modelgov-1.0.0-py3-none-any.whl:

Publisher: release.yml on mml555/modelgov

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