Imported from JoelMdO/AI-Translation-API (
.github/AGENTS.md). Install upstream withnpx skills add JoelMdO/AI-Translation-API --skill .github. Copyright stays with the author.
AGENTS.md — Python AI API
Overview
FastAPI Python application that provides AI-powered translation and article summarisation for the Blog Editor CMS. It validates Google OAuth Access Tokens, sanitises HTML input, and proxies requests to a locally-running Ollama LLM container to perform HTML-structure-preserving translation and content summarisation.
Tech Stack
| Concern | Library / Tool |
|---|---|
| Framework | FastAPI 0.115 |
| Language | Python 3.11+ |
| ASGI server | uvicorn (with standard extras) |
| Schema validation | Pydantic v2 |
| Async HTTP client | httpx |
| HTML parsing | beautifulsoup4 |
| Auth | Google OAuth Access Tokens (via httpx) |
| Environment config | python-dotenv |
| LLM backend | Ollama (llama3.2 default model) |
| Package manager | pip / requirements.txt |
| Container runtime | Docker + docker-compose |
Directory Layout
app/
├── main.py # FastAPI app factory, lifespan, CORS, router registration
├── config.py # Env variable loading (ALLOWED_ORIGINS, OLLAMA_BASE_URL …)
├── requirements.txt # Python dependencies
├── models/
│ └── models.py # Internal Pydantic models (GenerateRequest/Response …)
├── routers/
│ ├── translate_router.py # POST /api/translate
│ └── resume_router.py # POST /api/resume
├── schemas/
│ ├── translation.py # TranslationRequest/Response, ResumeRequest/Response, HealthResponse
│ └── testUser.py # GoogleUser schema returned by auth dependency
├── services/
│ ├── translation.py # TranslationService — HTML/text translation via Ollama
│ └── resume.py # ResumeService — article summarisation via Ollama
└── utils/
├── auth.py # verify_google_access_token FastAPI dependency
├── ollama_services.py # OllamaService — all Ollama HTTP communication
├── sanitize_html.py # Strip dangerous tags/attributes (regex-based)
├── sanitize_text.py # Strip unwanted characters from plain text output
└── create_prompt_translation.py # Prompt builder for translation requests
API Endpoints
| Method | Path | Auth required | Description |
|---|---|---|---|
| GET | /health |
No | Returns API + Ollama health status |
| POST | /api/translate |
Yes (Google) | Translate title/body/section HTML to target lang |
| POST | /api/resume |
Yes (Google) | Summarise article title + body via Ollama |
Request / Response Examples
POST /api/translate
{
"title": "My Article",
"body": "<p>Hello <strong>world</strong></p>",
"section": "Technology",
"target_language": "Spanish",
"model": "llama3.2"
}
Response:
{
"translated_text": {
"title": "Mi Artículo",
"body": "<p>Hola <strong>mundo</strong></p>",
"section": "Tecnología"
},
"success": true,
"model_used": "llama3.2"
}
POST /api/resume
{ "title": "My Article", "body": "<p>...</p>", "language": "en" }
Response:
{ "resume": "A concise one-paragraph summary.", "success": true }
Auth Flow
- The Next.js editor retrieves a Google OAuth Access Token from the active
next-authsession. - It passes it as
Authorization: Bearer <token>on every request to this API. utils/auth.py—verify_google_access_tokendependency validates the token by callingGET https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=<token>.- It checks that
email_verifiedistrueand returns aGoogleUserinjected into the route handler. DEV_MODE=truebypasses token validation (returns a static dev user — never use in production).TESTING_MODE=trueaccepts fake signed JWTs in place of real Google tokens (tests only).
Configuration (Environment Variables)
| Variable | Required | Description |
|---|---|---|
ALLOWED_ORIGINS |
Yes | CORS origins string (e.g. http://localhost:8000) |
CORS_METHODS |
No | JSON array of allowed HTTP methods |
CORS_ALLOW_HEADERS |
No | JSON array of allowed headers |
GOOGLE_CLIENT_ID |
Yes | Google OAuth client ID for token introspection |
OLLAMA_BASE_URL |
Yes | Ollama server URL (e.g. http://ollama:11434) |
OLLAMA_DEFAULT_MODEL |
No | Default LLM model name (default: llama3.2) |
DEV_MODE |
No | true bypasses Google token validation — never use in prod |
TESTING_MODE |
No | true accepts fake JWTs — tests only |
All variables are loaded via python-dotenv in config.py. Never commit .env files.
Adding a New Endpoint
- Define request and response
BaseModelschemas withField(...)inschemas/translation.py. - Add service logic in
services/(new file or existing class). - Create (or extend) a router in
routers/:
from fastapi import APIRouter, Depends
from schemas.translation import MyRequest, MyResponse
from services.my_service import my_service
from utils.auth import verify_user_access
from schemas.testUser import GoogleUser
router = APIRouter()
@router.post("/my-route", response_model=MyResponse)
async def my_handler(
request: MyRequest,
current_user: GoogleUser = Depends(verify_user_access),
) -> MyResponse:
return await my_service.process(request)
- Register in
main.py:app.include_router(my_router.router, prefix="/api") - Add tests under
tests/(see.github/skills/test.md).
Development
# Install dependencies
pip install -r app/requirements.txt
# Run locally (from app/ directory)
uvicorn main:app --reload --port 8001
Or via Docker (from the project root):
docker compose -f docker-compose.dev.yml up --build
The API runs on port 8001 inside Docker (proxied via Nginx on 443).
Testing
Use pytest + pytest-asyncio + httpx.AsyncClient for full async coverage.
External services (Ollama, Google OAuth) must always be mocked.
pip install pytest pytest-asyncio httpx pytest-mock
pytest -v --cov=app --cov-report=term-missing
See .github/skills/test.md for the full testing skill and fixture conventions.
Security Notes
- All HTML input is sanitised by
utils/sanitize_html.pybefore being forwarded to Ollama. - Google Access Tokens are validated server-side — never trusted client-side.
ALLOWED_ORIGINSmust be set or the app refuses to start.- All secrets must come from environment variables. Never hardcode or commit them.
- Pydantic v2 validates and coerces all request data at the API boundary.
- Never log full token values; truncate to the first 50 characters.
Code Editing Rules
- Follow
.github/skills/codeEdit.mdand.github/skills/test.md. - Use Python type hints on all function signatures and return types.
- Prefer
async deffor all route handlers and service methods. - Use
logging(notprint) for production logging. - PEP 8:
snake_casefor functions/variables,PascalCasefor classes,UPPER_CASEfor constants.