feat: CI pipeline verification, 3D body model, auth fixes, requirements lock
- .github/workflows/verify-pipeline.yml: CI that verifies pipeline determinism and checks for np.random in production code - ui/components/body-model.js: Three.js 3D human body model with 24 DensePose body parts mapped to 3D geometry - v1/requirements-lock.txt: Minimal pinned dependencies for verification - v1/src/api/dependencies.py: Fix mock auth returns with proper errors - v1/src/core/router_interface.py: Additional mock mode cleanup - v1/src/services/pose_service.py: Further mock elimination in service https://claude.ai/code/session_01Ki7pvEZtJDvqJkmyn6B714
This commit is contained in:
@@ -78,21 +78,33 @@ async def get_current_user(
|
||||
if not credentials:
|
||||
return None
|
||||
|
||||
# This would normally validate the JWT token
|
||||
# For now, return a mock user for development
|
||||
# Validate the JWT token
|
||||
# JWT validation must be configured via settings (e.g. JWT_SECRET, JWT_ALGORITHM)
|
||||
if settings.is_development:
|
||||
return {
|
||||
"id": "dev-user",
|
||||
"username": "developer",
|
||||
"email": "dev@example.com",
|
||||
"is_admin": True,
|
||||
"permissions": ["read", "write", "admin"]
|
||||
}
|
||||
|
||||
logger.warning(
|
||||
"Authentication credentials provided in development mode but JWT "
|
||||
"validation is not configured. Set up JWT authentication via "
|
||||
"environment variables (JWT_SECRET, JWT_ALGORITHM) or disable "
|
||||
"authentication. Rejecting request."
|
||||
)
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail=(
|
||||
"JWT authentication is not configured. In development mode, either "
|
||||
"disable authentication (enable_authentication=False) or configure "
|
||||
"JWT validation. Returning mock users is not permitted in any environment."
|
||||
),
|
||||
headers={"WWW-Authenticate": "Bearer"},
|
||||
)
|
||||
|
||||
# In production, implement proper JWT validation
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Authentication not implemented",
|
||||
detail=(
|
||||
"JWT authentication is not configured. Configure JWT_SECRET and "
|
||||
"JWT_ALGORITHM environment variables, or integrate an external "
|
||||
"identity provider. See docs/authentication.md for setup instructions."
|
||||
),
|
||||
headers={"WWW-Authenticate": "Bearer"},
|
||||
)
|
||||
|
||||
@@ -404,17 +416,22 @@ async def get_websocket_user(
|
||||
# Skip authentication if disabled
|
||||
if not settings.enable_authentication:
|
||||
return None
|
||||
|
||||
# For development, return mock user
|
||||
|
||||
# Validate the WebSocket token
|
||||
if not websocket_token:
|
||||
return None
|
||||
|
||||
if settings.is_development:
|
||||
return {
|
||||
"id": "ws-user",
|
||||
"username": "websocket_user",
|
||||
"is_admin": False,
|
||||
"permissions": ["read"]
|
||||
}
|
||||
|
||||
logger.warning(
|
||||
"WebSocket token provided in development mode but token validation "
|
||||
"is not configured. Rejecting. Disable authentication or configure "
|
||||
"JWT validation to allow WebSocket connections."
|
||||
)
|
||||
return None
|
||||
|
||||
# In production, implement proper token validation
|
||||
# TODO: Implement JWT/token validation for WebSocket connections
|
||||
logger.warning("WebSocket token validation is not implemented. Rejecting token.")
|
||||
return None
|
||||
|
||||
|
||||
|
||||
@@ -57,19 +57,16 @@ class RouterInterface:
|
||||
self.error_count = 0
|
||||
self.sample_count = 0
|
||||
|
||||
# Mock data generation
|
||||
self.mock_data_generator = None
|
||||
# Mock data generation (delegated to testing module)
|
||||
self._mock_csi_generator = None
|
||||
if mock_mode:
|
||||
self._initialize_mock_generator()
|
||||
|
||||
|
||||
def _initialize_mock_generator(self):
|
||||
"""Initialize mock data generator."""
|
||||
self.mock_data_generator = {
|
||||
'phase': 0,
|
||||
'amplitude_base': 1.0,
|
||||
'frequency': 0.1,
|
||||
'noise_level': 0.1
|
||||
}
|
||||
"""Initialize mock data generator from the testing module."""
|
||||
from src.testing.mock_csi_generator import MockCSIGenerator
|
||||
self._mock_csi_generator = MockCSIGenerator()
|
||||
self._mock_csi_generator.show_banner()
|
||||
|
||||
async def connect(self):
|
||||
"""Connect to the router."""
|
||||
@@ -143,56 +140,14 @@ class RouterInterface:
|
||||
return None
|
||||
|
||||
def _generate_mock_csi_data(self) -> np.ndarray:
|
||||
"""Generate mock CSI data for testing."""
|
||||
# Simulate CSI data with realistic characteristics
|
||||
num_subcarriers = 64
|
||||
num_antennas = 4
|
||||
num_samples = 100
|
||||
|
||||
# Update mock generator state
|
||||
self.mock_data_generator['phase'] += self.mock_data_generator['frequency']
|
||||
|
||||
# Generate amplitude and phase data
|
||||
time_axis = np.linspace(0, 1, num_samples)
|
||||
|
||||
# Create realistic CSI patterns
|
||||
csi_data = np.zeros((num_antennas, num_subcarriers, num_samples), dtype=complex)
|
||||
|
||||
for antenna in range(num_antennas):
|
||||
for subcarrier in range(num_subcarriers):
|
||||
# Base signal with some variation per antenna/subcarrier
|
||||
amplitude = (
|
||||
self.mock_data_generator['amplitude_base'] *
|
||||
(1 + 0.2 * np.sin(2 * np.pi * subcarrier / num_subcarriers)) *
|
||||
(1 + 0.1 * antenna)
|
||||
)
|
||||
|
||||
# Phase with spatial and frequency variation
|
||||
phase_offset = (
|
||||
self.mock_data_generator['phase'] +
|
||||
2 * np.pi * subcarrier / num_subcarriers +
|
||||
np.pi * antenna / num_antennas
|
||||
)
|
||||
|
||||
# Add some movement simulation
|
||||
movement_freq = 0.5 # Hz
|
||||
movement_amplitude = 0.3
|
||||
movement = movement_amplitude * np.sin(2 * np.pi * movement_freq * time_axis)
|
||||
|
||||
# Generate complex signal
|
||||
signal_amplitude = amplitude * (1 + movement)
|
||||
signal_phase = phase_offset + movement * 0.5
|
||||
|
||||
# Add noise
|
||||
noise_real = np.random.normal(0, self.mock_data_generator['noise_level'], num_samples)
|
||||
noise_imag = np.random.normal(0, self.mock_data_generator['noise_level'], num_samples)
|
||||
noise = noise_real + 1j * noise_imag
|
||||
|
||||
# Create complex signal
|
||||
signal = signal_amplitude * np.exp(1j * signal_phase) + noise
|
||||
csi_data[antenna, subcarrier, :] = signal
|
||||
|
||||
return csi_data
|
||||
"""Generate mock CSI data for testing.
|
||||
|
||||
Delegates to the MockCSIGenerator in the testing module.
|
||||
This method is only callable when mock_mode is True.
|
||||
"""
|
||||
if self._mock_csi_generator is None:
|
||||
self._initialize_mock_generator()
|
||||
return self._mock_csi_generator.generate()
|
||||
|
||||
async def _collect_real_csi_data(self) -> Optional[np.ndarray]:
|
||||
"""Collect real CSI data from the router.
|
||||
@@ -264,18 +219,9 @@ class RouterInterface:
|
||||
Dictionary containing router information
|
||||
"""
|
||||
if self.mock_mode:
|
||||
return {
|
||||
"model": "Mock Router",
|
||||
"firmware": "1.0.0-mock",
|
||||
"wifi_standard": "802.11ac",
|
||||
"antennas": 4,
|
||||
"supported_bands": ["2.4GHz", "5GHz"],
|
||||
"csi_capabilities": {
|
||||
"max_subcarriers": 64,
|
||||
"max_antennas": 4,
|
||||
"sampling_rate": 1000
|
||||
}
|
||||
}
|
||||
if self._mock_csi_generator is None:
|
||||
self._initialize_mock_generator()
|
||||
return self._mock_csi_generator.get_router_info()
|
||||
|
||||
# For real routers, this would query the actual hardware
|
||||
return {
|
||||
|
||||
@@ -714,31 +714,39 @@ class PoseService:
|
||||
}
|
||||
|
||||
async def get_statistics(self, start_time, end_time):
|
||||
"""Get pose estimation statistics."""
|
||||
"""Get pose estimation statistics.
|
||||
|
||||
In mock mode, delegates to testing module. In production, returns
|
||||
actual accumulated statistics from self.stats, or indicates no data.
|
||||
"""
|
||||
try:
|
||||
import random
|
||||
|
||||
# Mock statistics
|
||||
total_detections = random.randint(100, 1000)
|
||||
successful_detections = int(total_detections * random.uniform(0.8, 0.95))
|
||||
|
||||
if self.settings.mock_pose_data:
|
||||
from src.testing.mock_pose_generator import generate_mock_statistics
|
||||
return generate_mock_statistics(start_time=start_time, end_time=end_time)
|
||||
|
||||
# Production: return actual accumulated statistics
|
||||
total = self.stats["total_processed"]
|
||||
successful = self.stats["successful_detections"]
|
||||
failed = self.stats["failed_detections"]
|
||||
|
||||
return {
|
||||
"total_detections": total_detections,
|
||||
"successful_detections": successful_detections,
|
||||
"failed_detections": total_detections - successful_detections,
|
||||
"success_rate": successful_detections / total_detections,
|
||||
"average_confidence": random.uniform(0.75, 0.90),
|
||||
"average_processing_time_ms": random.uniform(50, 200),
|
||||
"unique_persons": random.randint(5, 20),
|
||||
"most_active_zone": random.choice(["zone_1", "zone_2", "zone_3"]),
|
||||
"total_detections": total,
|
||||
"successful_detections": successful,
|
||||
"failed_detections": failed,
|
||||
"success_rate": successful / max(1, total),
|
||||
"average_confidence": self.stats["average_confidence"],
|
||||
"average_processing_time_ms": self.stats["processing_time_ms"],
|
||||
"unique_persons": 0,
|
||||
"most_active_zone": "N/A",
|
||||
"activity_distribution": {
|
||||
"standing": random.uniform(0.3, 0.5),
|
||||
"sitting": random.uniform(0.2, 0.4),
|
||||
"walking": random.uniform(0.1, 0.3),
|
||||
"lying": random.uniform(0.0, 0.1)
|
||||
}
|
||||
"standing": 0.0,
|
||||
"sitting": 0.0,
|
||||
"walking": 0.0,
|
||||
"lying": 0.0,
|
||||
},
|
||||
"note": "Statistics reflect actual processed data. Activity distribution and unique persons require a persistence backend." if total == 0 else None,
|
||||
}
|
||||
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"Error getting statistics: {e}")
|
||||
raise
|
||||
|
||||
Reference in New Issue
Block a user