Skip to course content
Free FastAPI backend course

FastAPI for AI Backend Development

Unit 13.00: The model call behind one interface

The service should depend on the shape of a model call, not on the vendor.

One interface, two implementations

A protocol, a real implementation and a fake, with a function that accepts either.

The code calls it with the fake.

from typing import Protocol


class Classifier(Protocol):
    def classify(self, text: str) -> dict: ...


class RealClassifier:
    def classify(self, text: str) -> dict:
        raise RuntimeError("would call a paid API")


class FakeClassifier:
    def __init__(self, category="billing"): self.category = category
    def classify(self, text: str) -> dict:
        return {"category": self.category, "confidence": 0.9}


def handle(text: str, classifier: Classifier) -> dict:
    """Knows there is a classifier; does not know which."""
    return classifier.classify(text)


print(handle("charged twice", FakeClassifier()))
print(f"real classifier used: {isinstance(FakeClassifier(), RealClassifier)}")

# One interface, two implementations. The service depends on the shape, not the
# vendor, so the suite runs offline and a provider change touches one file.

The handling function knows there is a classifier and not which one. That is what lets the suite run offline and what makes a provider change a single-file edit.

The protocol is worth declaring even though Python does not require it: it documents the shape both implementations must satisfy, and the type checker enforces it.

The mistake this prevents

The mistake is calling the vendor's SDK directly from the service. The vendor's types then appear throughout your logic, and every one of those places is a site that changes when the provider does.

Takeaway

Define the model call as an interface and depend on that. The vendor lives behind it, which makes offline testing and provider changes both cheap.