Chapter 2 · OpenAI and Gemini, one hour, two SDKs
load_dotenv(find_dotenv()) finds the course-root .envresponse = client.responses.create(
model="gpt-5.4-nano",
input="...",
)
print(response.output_text)
OpenAI() reads OPENAI_API_KEY itself.
client = genai.Client(api_key=os.environ["GEMINI_API_KEY"])
response = client.models.generate_content(
model="gemini-3.1-flash-lite",
contents="...",
)
print(response.text)responses.create vs models.generate_contentinput vs contentsresponse.output_text vs response.textSame job. Same shape.
Each one is its own docker compose up project. Browser tab in, AI answer out.
PROVIDER = os.environ.get("PROVIDER", "openai")
ai = importlib.import_module(f"ai_{PROVIDER}")
# POST /api/ai -> ai.run(req.input)
PROVIDER=gemini docker compose up --build swaps the SDK without
touching main.py. The same loader is in all three showcases, byte for byte.
cd code/showcase/<slug>
bash bootstrap-secrets.sh
docker compose up --build
# open http://localhost:3000
Three showcases. Same shape. Still one API call each.
Get the plumbing reflexive now. Key in the environment, never in the source. Load it before the client. Fail loud and early. Every later chapter stands on this.