Spaces:
Configuration error
Configuration error
Implementation Plan: Content Personalization API
Feature Branch: 005-content-personalize
Created: 2025-12-14
Status: Ready for Implementation
Technical Context
| Aspect | Status | Details |
|---|---|---|
| Framework | Resolved | FastAPI (existing) |
| Database | Resolved | Neon PostgreSQL via SQLAlchemy (existing) |
| AI Service | Resolved | OpenAI GPT-4 (existing SDK) |
| User Model | Resolved | Existing with software_level, hardware_level, learning_goals |
| Authentication | N/A | Out of scope per spec |
Constitution Compliance
| Principle | Status | Implementation |
|---|---|---|
| Technical accuracy | Pass | OpenAI GPT-4 for intelligent personalization |
| Python code examples | Pass | FastAPI/Python implementation |
| Clear documentation | Pass | OpenAPI spec, quickstart guide |
| Smallest viable change | Pass | Reuses existing User model, no new tables |
Architecture Overview
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Client β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
β POST /api/personalize
β {content, user_id}
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β FastAPI Router β
β (app/routes/personalize.py) β
β β
β 1. Validate request (content non-empty, β€50K chars) β
β 2. Validate user_id (positive integer) β
β 3. Query database for user profile β
β 4. If user not found β return 404 β
β 5. Build personalization prompt with user context β
β 6. Call OpenAI for personalization β
β 7. Return {personalized_content, adjustments_made} β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β
β User lookup β Personalization
βΌ βΌ
ββββββββββββββββββββββββ ββββββββββββββββββββββββββββββββ
β Neon PostgreSQL β β OpenAI GPT-4 β
β β β β
β users table β β System: Personalization β
β - id β β rules + user profileβ
β - software_level β β User: Original content β
β - hardware_level β β β
β - learning_goals β β Returns: Adapted content β
ββββββββββββββββββββββββ β + adjustments β
ββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββ
β PersonalizeResponse β
β β
β personalized_content: str β
β adjustments_made: str β
βββββββββββββββββββββββββββββββββββ
Implementation Components
1. Request/Response Schemas
File: app/schemas/personalize.py
from pydantic import BaseModel, field_validator
class PersonalizeRequest(BaseModel):
content: str
user_id: int
@field_validator('content')
@classmethod
def content_not_empty(cls, v):
if not v or not v.strip():
raise ValueError('Content cannot be empty')
v = v.strip()
if len(v) > 50000:
raise ValueError('Content exceeds maximum length of 50000 characters')
return v
@field_validator('user_id')
@classmethod
def user_id_positive(cls, v):
if v <= 0:
raise ValueError('User ID must be a positive integer')
return v
class PersonalizeResponse(BaseModel):
personalized_content: str
adjustments_made: str
2. OpenAI Service Extension
File: app/services/openai_service.py (add method)
async def personalize_content(
self,
content: str,
software_level: str,
hardware_level: str,
learning_goals: str
) -> dict:
"""Personalize content based on user's background."""
system_prompt = f"""You are an expert educational content adapter. Your task is to personalize the following content based on the user's background.
USER PROFILE:
- Software/Programming Level: {software_level}
- Hardware/Electronics Level: {hardware_level}
- Learning Goals: {learning_goals if learning_goals else 'Not specified'}
PERSONALIZATION RULES:
For Software Level:
- beginner: Add detailed explanations, use simpler terminology, break down complex concepts, provide examples
- intermediate: Maintain moderate complexity, brief explanations for advanced concepts only
- advanced: Add technical depth, skip basic explanations, use precise technical terminology
For Hardware Level:
- none: Explain all hardware concepts from scratch, use analogies
- basic: Brief hardware explanations, define technical terms
- experienced: Use technical hardware terminology without explanation
If learning goals are specified, emphasize and connect content to those objectives.
OUTPUT FORMAT:
Respond with a JSON object containing exactly two fields:
1. "personalized_content": The adapted content
2. "adjustments_made": A brief description of what changes were made
Example response format:
{{"personalized_content": "...", "adjustments_made": "..."}}"""
messages = [
{"role": "system", "content": system_prompt},
{"role": "user", "content": content}
]
response = await asyncio.to_thread(
self.client.chat.completions.create,
model="gpt-4",
messages=messages,
response_format={"type": "json_object"}
)
import json
result = json.loads(response.choices[0].message.content)
return result
3. API Route
File: app/routes/personalize.py
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
from app.database import get_db
from app.models.user import User
from app.schemas.personalize import PersonalizeRequest, PersonalizeResponse
from app.services.openai_service import OpenAIService
import logging
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/api", tags=["personalization"])
@router.post("/personalize", response_model=PersonalizeResponse)
async def personalize_content(
request: PersonalizeRequest,
db: Session = Depends(get_db)
):
"""
Personalize content based on user's background.
- Fetches user profile from database
- Uses OpenAI to adapt content complexity based on:
* software_level (beginner/intermediate/advanced)
* hardware_level (none/basic/experienced)
* learning_goals (free text)
- Returns personalized content with description of adjustments
"""
# Fetch user profile
user = db.query(User).filter(User.id == request.user_id).first()
if not user:
raise HTTPException(status_code=404, detail="User not found")
# Personalize via OpenAI
try:
openai_service = OpenAIService()
result = await openai_service.personalize_content(
content=request.content,
software_level=user.software_level,
hardware_level=user.hardware_level,
learning_goals=user.learning_goals or ""
)
except Exception as e:
logger.error(f"OpenAI personalization error: {e}")
raise HTTPException(
status_code=503,
detail="Personalization service temporarily unavailable"
)
return PersonalizeResponse(
personalized_content=result.get("personalized_content", ""),
adjustments_made=result.get("adjustments_made", "")
)
4. Router Registration
File: app/main.py (update)
from app.routes import chat, auth, translate, personalize
# Add to existing router includes
app.include_router(personalize.router)
Implementation Order
| Step | Task | Dependencies | Files |
|---|---|---|---|
| 1 | Create Pydantic schemas | None | app/schemas/personalize.py |
| 2 | Add personalize method to OpenAIService | None | app/services/openai_service.py |
| 3 | Create personalization route | Steps 1-2 | app/routes/personalize.py |
| 4 | Register router in main.py | Step 3 | app/main.py |
Error Handling Strategy
| Scenario | HTTP Code | Handling |
|---|---|---|
| Empty content | 400 | Pydantic validation |
| Content too long (>50K) | 400 | Pydantic validation |
| Invalid user_id (β€0) | 400 | Pydantic validation |
| User not found | 404 | Database lookup check |
| OpenAI API failure | 503 | Catch exception, log, return error |
| OpenAI timeout | 503 | Caught by general exception handler |
| Invalid JSON from OpenAI | 500 | Catch JSON parse error |
Personalization Logic Mapping
| User Profile | Personalization Applied |
|---|---|
| software_level=beginner | Detailed explanations, simpler terminology, examples |
| software_level=intermediate | Moderate complexity, brief advanced explanations |
| software_level=advanced | Technical depth, skip basics, precise terminology |
| hardware_level=none | Explain hardware from scratch, use analogies |
| hardware_level=basic | Brief hardware explanations, define terms |
| hardware_level=experienced | Technical hardware terminology |
| learning_goals set | Emphasize and connect to stated objectives |
Testing Strategy
Unit Tests
- Schema validation (empty content, content too long, invalid user_id)
- Personalization prompt construction
Integration Tests
- Full flow: POST β User lookup β OpenAI β Response
- User not found scenario
- OpenAI error handling
- Different user profile combinations
Manual Testing
- curl commands (see quickstart.md)
- Verify personalization quality for different user levels
Related Artifacts
| Artifact | Path |
|---|---|
| Specification | specs/005-content-personalize/spec.md |
| Research | specs/005-content-personalize/research.md |
| Data Model | specs/005-content-personalize/data-model.md |
| API Contract | specs/005-content-personalize/contracts/openapi.yaml |
| Quickstart | specs/005-content-personalize/quickstart.md |
Next Steps
Run /sp.tasks to generate implementation tasks from this plan.