ProvenPin Install

OpenAI Python SDK structured outputs, tested with respx: working versions (tested 2026-10-15)

Call the OpenAI Python SDK and validate JSON output with Pydantic, testing success, malformed output and API errors offline by mocking HTTP with respx.

passed3 passed · 0 failed2026-10-15 11:55 UTC
Status
passed
Last passed
2026-10-15 11:55 UTC
Tests
3 / 3
Language
Python
Runtime
Python 3.13.16
pinned versions5
openai@3.31.0pydantic@2.14.1respx@0.23.1httpx@0.28.1pytest@9.1.1
run it
uv venv -q .venv && uv pip install -q --python .venv/bin/python -r requirements.txt
.venv/bin/python -m pytest -q
test environment
Python 3.13.16
linux 6.17.0-1022-azure
x64
runner: github-actions 38730348195

The code

Exactly the files that passed. Your agent can pull them with get_recipe("openai-python-respx-mock") (install), or see them on GitHub.

requirements.txt
openai==3.31.0
pydantic==2.14.1
respx==0.23.1
httpx==0.28.1
pytest==9.1.1
extract.py
from openai import OpenAI
from pydantic import BaseModel, ValidationError

class Invoice(BaseModel):
    vendor: str
    total: float
    currency: str

class ExtractionError(Exception):
    pass

def extract_invoice(client: OpenAI, text: str) -> Invoice:
    res = client.chat.completions.create(
        model="gpt-4o-mini",
        response_format={"type": "json_object"},
        messages=[
            {"role": "system", "content": "Extract vendor, total and currency as JSON."},
            {"role": "user", "content": text},
        ],
    )
    content = res.choices[0].message.content or ""
    try:
        return Invoice.model_validate_json(content)
    except ValidationError as e:
        raise ExtractionError(f"model returned invalid JSON: {e.error_count()} errors") from e
tests/test_extract.py
import json
import httpx
import openai
import pytest
import respx
from openai import OpenAI
from extract import ExtractionError, extract_invoice

BASE = "https://api.openai.test/v1"

def completion(content: str):
    return {"id": "c1", "object": "chat.completion", "created": 0, "model": "gpt-4o-mini",
            "choices": [{"index": 0, "finish_reason": "stop", "message": {"role": "assistant", "content": content}}]}

@pytest.fixture
def client():
    # pass an httpx client explicitly so respx can intercept it
    return OpenAI(api_key="test", base_url=BASE, max_retries=0, http_client=httpx.Client())

@respx.mock
def test_parses_valid_json(client):
    route = respx.post(f"{BASE}/chat/completions").mock(return_value=httpx.Response(200, json=completion('{"vendor":"Acme","total":12.5,"currency":"USD"}')))
    inv = extract_invoice(client, "Acme invoice $12.50")
    assert inv.vendor == "Acme" and inv.total == 12.5
    sent = json.loads(route.calls.last.request.content)
    assert sent["response_format"] == {"type": "json_object"}

@respx.mock
def test_invalid_output_raises(client):
    respx.post(f"{BASE}/chat/completions").mock(return_value=httpx.Response(200, json=completion('{"vendor":"Acme"}')))
    with pytest.raises(ExtractionError):
        extract_invoice(client, "x")

@respx.mock
def test_api_errors_surface_as_sdk_exceptions(client):
    respx.post(f"{BASE}/chat/completions").mock(return_value=httpx.Response(429, json={"error": {"message": "slow down", "type": "rate_limit"}}))
    with pytest.raises(openai.RateLimitError):
        extract_invoice(client, "x")
pytest.ini
[pytest]
pythonpath = .

Connect to ProvenPin

Add the free MCP server so your agent can pull this recipe and others like it. Same setup as the docs.

terminal
claude mcp add --transport http provenpin https://mcp.provenpin.com/mcp

Any other MCP client: Streamable HTTP at https://mcp.provenpin.com/mcp. No API key, no signup.