Rewrite tone
Paste any message (email, Slack, commit message), pick a tone, get it
Showcase — Rewrite tone
Paste any message (email, Slack, commit message), pick a tone, get it rewritten with the same facts and a different voice. Still one API call.
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— the OpenAI callbackend/ai_gemini.py— the Gemini callbackend/main.py— identical FastAPI loader; reads PROVIDER and dispatchesfrontend/app/page.tsx— textarea + tone<select>+ resultdocker-compose.yml— two services, secrets mounted from./secrets/
The frontend encodes <tone>\n---\n<message> into the single-string input
the backend expects, which is how the def run(input: str) -> str contract
stays the same across all three showcases in this week.
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 rewrite-tone.zip
cd rewrite-tone
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 1 - Showcase 2 (OpenAI): rewrite a message in a chosen tone.
The frontend encodes the input as "<tone>\\n---\\n<message>" so the
def run(input: str) -> str contract stays the same as the other showcases.
"""
from openai import OpenAI
_client = OpenAI()
def run(payload: str) -> str:
tone, _, message = payload.partition("\n---\n")
tone = (tone or "neutral").strip().lower()
message = (message or payload).strip()
prompt = (
f"Rewrite the following message in a {tone} tone. Preserve every fact, "
"name, date, and number. Do not add or remove information. Return only "
"the rewritten message, no preamble."
f"\n\n---\n{message}\n---"
)
# Cap the response so the model can't run away on us. 2048 tokens
# is roughly 6k characters — comfortably more than any reasonable
# rewrite of an 8k-char input.
response = _client.responses.create(
model="gpt-5.4-nano", input=prompt, max_output_tokens=2048,
)
return response.output_text
backend/ai_gemini.py
"""Week 1 - Showcase 2 (Gemini): rewrite a message in a chosen tone."""
import os
from google import genai
from google.genai import types
_client = genai.Client(api_key=os.environ["GEMINI_API_KEY"])
def run(payload: str) -> str:
tone, _, message = payload.partition("\n---\n")
tone = (tone or "neutral").strip().lower()
message = (message or payload).strip()
prompt = (
f"Rewrite the following message in a {tone} tone. Preserve every fact, "
"name, date, and number. Do not add or remove information. Return only "
"the rewritten message, no preamble."
f"\n\n---\n{message}\n---"
)
# Cap the response so the model can't run away on us. 2048 tokens
# is roughly 6k characters — comfortably more than any reasonable
# rewrite of an 8k-char input.
response = _client.models.generate_content(
model="gemini-3.1-flash-lite", contents=prompt,
config=types.GenerateContentConfig(max_output_tokens=2048),
)
return response.text
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