Files
library-desk/src/models/volatile.py
T
jpmschweitzerandClaude Opus 4.5 46b9bcd7a0 feat: separate current weather from forecast into distinct namespaces
- Add FORECAST namespace for multi-day outlook (12hr TTL)
- WEATHER namespace now stores only current conditions (1hr TTL)
- Split fetch_weather into fetch_current_weather + fetch_forecast
- Add POST /volatile/fetch/forecast/{city} endpoint
- Different update frequencies for efficient caching

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-30 12:44:16 +01:00

135 lines
6.6 KiB
Python

"""
Volatile memory models for Library Desk.
Provides models for ephemeral cached data with TTL - weather, news, financial data,
transit schedules, and other time-sensitive external information.
"""
from pydantic import BaseModel, Field
from typing import Dict, Any, Optional, List
from datetime import datetime
from enum import Enum
class VolatileNamespace(str, Enum):
"""
Predefined namespaces for volatile data.
Each namespace can have different default TTLs and refresh schedules.
"""
# Real-time external data
WEATHER = "weather" # Current conditions (temperature, humidity, wind)
FORECAST = "forecast" # Multi-day weather outlook
SUN = "sun" # Sunrise, sunset, daylight duration
NEWS = "news" # Headlines, breaking news
FINANCIAL = "financial" # Stock prices, exchange rates, crypto
TRANSIT = "transit" # Train/bus schedules, delays, disruptions
TRAFFIC = "traffic" # Commute times, road conditions
AIR_QUALITY = "air_quality" # Pollution levels, pollen counts
SPORTS = "sports" # Live scores, upcoming matches
# System/integration data
SOCIAL = "social" # Social media mentions, notifications
SYSTEM = "system" # Service health, infrastructure status
# Ephemeral context
CONTEXT = "context" # Conversation context, session state
CUSTOM = "custom" # User-defined volatile data
# Default TTLs per namespace (in seconds)
NAMESPACE_DEFAULT_TTL: Dict[str, int] = {
VolatileNamespace.WEATHER: 3600, # 1 hour - current conditions
VolatileNamespace.FORECAST: 43200, # 12 hours - forecast stable longer
VolatileNamespace.SUN: 86400, # 24 hours - sun times change daily
VolatileNamespace.NEWS: 3600, # 1 hour - news cycles
VolatileNamespace.FINANCIAL: 300, # 5 min - markets move fast
VolatileNamespace.TRANSIT: 300, # 5 min - schedules update frequently
VolatileNamespace.TRAFFIC: 600, # 10 min - traffic patterns
VolatileNamespace.AIR_QUALITY: 3600, # 1 hour - air quality stable
VolatileNamespace.SPORTS: 60, # 1 min - live scores
VolatileNamespace.SOCIAL: 600, # 10 min - social notifications
VolatileNamespace.SYSTEM: 60, # 1 min - system health
VolatileNamespace.CONTEXT: 3600, # 1 hour - session context
VolatileNamespace.CUSTOM: 3600, # 1 hour - default for custom
}
class VolatileRecord(BaseModel):
"""
A volatile cache record with TTL.
Volatile records are ephemeral data stored in Redis with automatic expiration.
Used for weather, news, financial data, and other time-sensitive information.
"""
key: str = Field(..., description="Record key (e.g., 'rotterdam', 'nos-headlines')")
namespace: str = Field(..., description="Namespace (e.g., 'weather', 'news', 'financial')")
data: Dict[str, Any] = Field(..., description="Actual content/payload")
source: Optional[str] = Field(None, description="Origin API/service (e.g., 'openweathermap', 'nos.nl')")
created_at: datetime = Field(default_factory=datetime.utcnow, description="When record was created")
updated_at: datetime = Field(default_factory=datetime.utcnow, description="When record was last updated")
ttl: int = Field(..., ge=60, le=604800, description="Time-to-live in seconds (max 7 days)")
refresh_schedule: Optional[str] = Field(None, description="Cron expression for scheduled refresh")
user: str = Field(..., description="User identifier for multi-tenancy")
class VolatileRecordCreate(BaseModel):
"""Request model for creating/updating a volatile record."""
data: Dict[str, Any] = Field(..., description="Content to store")
source: Optional[str] = Field(None, description="Origin API/service")
ttl: Optional[int] = Field(None, ge=60, le=604800, description="TTL in seconds (uses namespace default if not set)")
refresh_schedule: Optional[str] = Field(None, description="Cron expression for scheduled refresh")
class VolatileRecordResponse(BaseModel):
"""Response model for a volatile record."""
key: str = Field(..., description="Record key")
namespace: str = Field(..., description="Namespace")
data: Dict[str, Any] = Field(..., description="Stored content")
source: Optional[str] = Field(None, description="Origin API/service")
created_at: datetime = Field(..., description="Creation timestamp")
updated_at: datetime = Field(..., description="Last update timestamp")
ttl: int = Field(..., description="TTL in seconds")
ttl_remaining: int = Field(..., description="Seconds until expiration")
refresh_schedule: Optional[str] = Field(None, description="Cron expression if scheduled")
user: str = Field(..., description="User identifier")
class VolatileListResponse(BaseModel):
"""Response model for listing volatile records."""
namespace: str = Field(..., description="Namespace queried")
keys: List[str] = Field(..., description="List of keys in namespace")
count: int = Field(..., description="Number of keys")
user: str = Field(..., description="User identifier")
class VolatileScheduledResponse(BaseModel):
"""Response model for records needing refresh."""
records: List[VolatileRecordResponse] = Field(..., description="Records with refresh schedules")
count: int = Field(..., description="Number of scheduled records")
user: str = Field(..., description="User identifier")
class VolatileStatsResponse(BaseModel):
"""Response model for volatile cache statistics."""
total_records: int = Field(..., description="Total volatile records for user")
by_namespace: Dict[str, int] = Field(..., description="Record count per namespace")
scheduled_count: int = Field(..., description="Records with refresh schedules")
total_memory_bytes: Optional[int] = Field(None, description="Approximate memory usage")
user: str = Field(..., description="User identifier")
class VolatileDeleteResponse(BaseModel):
"""Response model for delete operation."""
key: str = Field(..., description="Deleted key")
namespace: str = Field(..., description="Namespace")
deleted: bool = Field(..., description="Whether record was found and deleted")
user: str = Field(..., description="User identifier")
class VolatileBulkDeleteResponse(BaseModel):
"""Response model for bulk delete operations."""
namespace: Optional[str] = Field(None, description="Namespace if namespace-wide delete")
deleted_count: int = Field(..., description="Number of records deleted")
user: str = Field(..., description="User identifier")