LLMOpsThe LLMOps lifecycle · MLflow 3 · FastAPI · Docker · AWS/Azure
Dashboard
0%
1
Curious builder0 XP earned · 300 to level 2
0 daysFinish a lesson to begin
Badge collection0 of 6 unlocked
41 small wins to finish your pathNext lesson →

The app entry point and its API

The entry point is the one module that exposes the assistant over HTTP: a FastAPI app with a /chat endpoint that takes a message, calls the assistant and returns its reply as JSON.

Last updated: 15 Oct, 2026 · LLMOps

The A prompt library in one place lesson was the last of the app's internal pieces. Something has to tie them together and let the outside world reach them, and in the repo that is main.py, a FastAPI app with a single POST endpoint. This lesson builds the same shape for the shop assistant and tests it with no server and no key, standing the model in with a deterministic call_api.

A deterministic stand-in for the model

A production assistant would send the message to a model, which needs a key and gives a different answer each run. The stand-in keeps the same shape, a prompt in and a string out, but answers from a small handbook, the same pattern the Agent Governance Toolkit course's shop bot uses, so the endpoint can be checked every run.

python
from fastapi import FastAPI
from pydantic import BaseModel

HANDBOOK = {
    "refund": "Refunds are issued within 30 days of delivery.",
    "shipping": "Standard shipping takes three to five working days.",
}

def call_api(prompt):                      # the deterministic stand-in for a model
    for keyword, answer in HANDBOOK.items():
        if keyword in prompt.lower():
            return answer
    return "Please contact the shop for help with that."

The FastAPI app and its endpoint

The app declares the request body with a Pydantic model and exposes one POST route. The route hands the message to the stand-in and wraps the reply in JSON, the same contract a real model-backed endpoint would keep.

python
app = FastAPI()

class ChatRequest(BaseModel):
    message: str

@app.post("/chat")
def chat(request: ChatRequest):
    # one endpoint: take a message, return the assistant's reply
    return {"reply": call_api(request.message)}

Testing the endpoint without a server

FastAPI's TestClient calls the app in the same process, so a request runs the real routing and validation with no server to start and no port to open. It posts a message and reads the status and the JSON back.

ExampleThe /chat endpoint answering through the test client
from fastapi.testclient import TestClient
from app import app

client = TestClient(app)

# a POST to /chat, with no server running and no model key
response = client.post("/chat", json={"message": "How do I get a refund?"})
print("status:", response.status_code)
print("json:", response.json())

What the test client exercised

  • The request was validated, because the JSON matched the ChatRequest model; a body without a message would have come back as a 422 before the route ran.
  • The route returned a 200 with JSON, the same response a deployed server would send, so the endpoint's contract is tested without deploying it.
  • The stand-in answered deterministically, so the test asserts a known reply. Swapping in a real model changes only call_api, not the endpoint around it.
One request through the entry point, left to right: a POST to /chat carrying a message, a ChatRequest model that validates the body, call_api where the assistant answers, and a 200 response carrying JSON with the reply. Below, a green dashed TestClient box points up at the same route, noting it needs no server and no key and exercises the same validation and the same JSON contract on every change.

A script against an entry point with an API

A script you run by handA FastAPI entry point
Reached byA person at a terminalAn HTTP request from anywhere
InputEdited in the codeA validated request body
OutputPrintedJSON with a status code
TestedBy rerunning itWith a test client, no server

Where the entry point fits

  • Serving the assistant, where a web or mobile client posts a message and reads the reply.
  • A contract to test against, where the test client checks the endpoint on every change with no deployment.
  • The seam a container runs, where a server imports this module and the next part deploys it.
Watch out. The stand-in is only the model's job. The endpoint, its validation and its JSON contract are the real FastAPI running, so the test proves the app and not the model. When a key and a real model go in behind call_api, the test stops being deterministic, which is why the stand-in stays in the test suite and the real model runs in production.
Try it yourself
  • Post a message about shipping and confirm the handbook's shipping answer comes back.
  • Send a body with no message field and read the 422 the validation returns.
  • Add a GET /health route that returns {"status": "ok"} and call it with the test client.

Slow is fine. Stopping is the only problem.