MuhammadSaad16's picture
Upload 112 files
971b4ea verified

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.