Extract fields
Paste a messy blob — an email signature, a forwarded intro, a chat message —
Showcase — Extract fields
Paste a messy blob — an email signature, a forwarded intro, a chat message — and get back a strict JSON object: name, email, phone, company. The model fills what it finds and returns null for the rest instead of guessing, because the schema says those fields are optional, not absent.
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— theContactschema +responses.parsecallbackend/ai_gemini.py— same schema,response_schemaon the configbackend/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 serialized to 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 extract-fields.zip
cd extract-fields
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 1 (OpenAI): pull contact fields out of free text.
A schema in, a clean object out. The model fills what it finds and leaves
the rest null instead of guessing. The run() contract still returns a
string, so we serialize the validated object to pretty JSON for display.
"""
import json
from typing import Optional
from openai import OpenAI
from pydantic import BaseModel
_client = OpenAI()
class Contact(BaseModel):
name: Optional[str] = None
email: Optional[str] = None
phone: Optional[str] = None
company: Optional[str] = None
def run(text: str) -> str:
response = _client.responses.parse(
model="gpt-5.4-nano",
input="Extract the contact details from this text. Use null for "
f"anything not present.\n\n{text.strip()}",
text_format=Contact,
)
return json.dumps(response.output_parsed.model_dump(), indent=2)
backend/ai_gemini.py
"""Week 4 - Showcase 1 (Gemini): pull contact fields out of free text."""
import json
import os
from typing import Optional
from google import genai
from google.genai import types
from pydantic import BaseModel
_client = genai.Client(api_key=os.environ["GEMINI_API_KEY"])
class Contact(BaseModel):
name: Optional[str] = None
email: Optional[str] = None
phone: Optional[str] = None
company: Optional[str] = None
def run(text: str) -> str:
response = _client.models.generate_content(
model="gemini-3.1-flash-lite",
contents="Extract the contact details from this text. Use null for "
f"anything not present.\n\n{text.strip()}",
config=types.GenerateContentConfig(
response_mime_type="application/json",
response_schema=Contact,
),
)
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