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:
Claude
2026-02-28 06:20:08 +00:00
parent 2199174cac
commit 4b2e7bfecf
6 changed files with 847 additions and 113 deletions

View File

@@ -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

View File

@@ -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 {

View File

@@ -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