Course EN
← back to chapter

Reescribe el tono

Pega cualquier mensaje (correo, Slack, mensaje de commit), elige un tono, y

Showcase — Reescribe el tono

Pega cualquier mensaje (correo, Slack, mensaje de commit), elige un tono, y recíbelo reescrito con los mismos datos y otra voz. Sigue siendo una sola llamada a la API.

Córrelo

bash bootstrap-secrets.sh              # reads ../../../../.env, writes secrets/
docker compose up --build              # default: PROVIDER=openai

Abre http://localhost:3000.

Para correrlo contra Gemini en su lugar:

PROVIDER=gemini docker compose up --build

Qué hay aquí

  • backend/ai_openai.py — la llamada a OpenAI
  • backend/ai_gemini.py — la llamada a Gemini
  • backend/main.py — loader de FastAPI idéntico; lee PROVIDER y despacha
  • frontend/app/page.tsx — textarea + <select> de tono + resultado
  • docker-compose.yml — dos servicios, secrets montados desde ./secrets/

El frontend codifica <tone>\n---\n<message> en el input de un solo string que el backend espera, que es como el contrato def run(input: str) -> str se mantiene igual en los tres showcases de esta semana.

Detenlo

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.

Descargar rewrite-tone.zip

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

  • .gitignore
  • README.es.md
  • README.md
  • backend/Dockerfile
  • backend/ai_gemini.py
  • backend/ai_openai.py
  • backend/main.py
  • backend/requirements.txt
  • bootstrap-secrets.sh
  • docker-compose.yml
  • frontend/Dockerfile
  • frontend/app/layout.tsx
  • frontend/app/page.tsx
  • frontend/next.config.ts
  • frontend/package.json
  • frontend/tsconfig.json