Classify JSON
Drop in a support message, get back an object you can branch on without ever
Showcase — Classify JSON
Drop in a support message, get back an object you can branch on without ever parsing text: a label from a fixed set, a confidence score, and a short list of reasons. Week 2 coaxed a label out as a string and hoped the format held. Here the schema makes it a guarantee.
Run
bash bootstrap-secrets.sh # reads ../../../../.env, writes secrets/
docker compose up --build # default: PROVIDER=openai
Open http://localhost:3000.
To run against Gemini instead:
PROVIDER=gemini docker compose up --build
What's where
backend/ai_openai.py— theClassificationschema (Literal label + float + list) and the OpenAI callbackend/ai_gemini.py— same schema, Gemini callbackend/main.py— identical FastAPI loader; reads PROVIDER and dispatchesfrontend/app/page.tsx— textarea + result (rendered as JSON)docker-compose.yml— two services, secrets mounted from./secrets/
run(input: str) -> str returns the validated object as pretty JSON, keeping
the contract identical across the week's showcases.
Stop
docker compose down
Ejecútalo en tu máquina
Descarga el proyecto como ZIP y córrelo con Docker. Levanta un backend FastAPI y un frontend Next.js en localhost:3000.
unzip classify-json.zip
cd classify-json
bash bootstrap-secrets.sh # one-time: pulls API keys into ./secrets
docker compose up --build # default provider: openai
# or: PROVIDER=gemini docker compose up --build
Escribe algo, elige un proveedor y ejecuta el mismo código de Código contra la API real. Requiere iniciar sesión.
Los mismos módulos que ejecuta el botón Run. El proyecto completo (frontend, Dockerfile, compose) está en el ZIP, pestaña README.
backend/ai_openai.py
"""Week 4 - Showcase 2 (OpenAI): classify into a guaranteed-parseable object.
Week 2's few-shot showcase coaxed a label out as text and hoped it stayed
on format. This does the same job, but the schema makes the format a
guarantee: a label from a fixed set, a confidence float, and the reasons.
No parsing, no regex, no praying.
"""
import json
from typing import Literal
from openai import OpenAI
from pydantic import BaseModel
_client = OpenAI()
class Classification(BaseModel):
label: Literal["billing", "bug", "feature_request", "praise", "other"]
confidence: float
reasons: list[str]
def run(text: str) -> str:
response = _client.responses.parse(
model="gpt-5.4-nano",
input="Classify this support message. Give the label, your confidence "
f"from 0 to 1, and a short reason or two.\n\n{text.strip()}",
text_format=Classification,
)
return json.dumps(response.output_parsed.model_dump(), indent=2)
backend/ai_gemini.py
"""Week 4 - Showcase 2 (Gemini): classify into a guaranteed-parseable object."""
import json
import os
from typing import Literal
from google import genai
from google.genai import types
from pydantic import BaseModel
_client = genai.Client(api_key=os.environ["GEMINI_API_KEY"])
class Classification(BaseModel):
label: Literal["billing", "bug", "feature_request", "praise", "other"]
confidence: float
reasons: list[str]
def run(text: str) -> str:
response = _client.models.generate_content(
model="gemini-3.1-flash-lite",
contents="Classify this support message. Give the label, your confidence "
f"from 0 to 1, and a short reason or two.\n\n{text.strip()}",
config=types.GenerateContentConfig(
response_mime_type="application/json",
response_schema=Classification,
),
)
return json.dumps(response.parsed.model_dump(), indent=2)
Archivos del proyecto
.gitignoreREADME.mdbackend/Dockerfilebackend/ai_gemini.pybackend/ai_openai.pybackend/main.pybackend/requirements.txtbootstrap-secrets.shdocker-compose.ymlfrontend/Dockerfilefrontend/app/layout.tsxfrontend/app/page.tsxfrontend/next.config.tsfrontend/package.jsonfrontend/tsconfig.json