Hardware Domain¶
Hardware detection and inference scoring for the three engine backends: MLX (Apple Silicon), CUDA (NVIDIA) and CPU fallback.
Overview¶
Detection is delegated to the engine selected at startup by BaseEngine.get_engine(); the domain only persists the result and serves it. The engine that ends up selected can differ from what detection chose: user_settings.inference_backend pins the CPU engine when the user asked for it, and the CUDA pre-flight (src/engines/cuda_compatibility.py) reports — without changing anything — a GPU the bundled CUDA build cannot drive. See Hardware Detection.
Endpoints -> Service -> Repository -> HardwareProfile entity -> PostgreSQL
|
Engine (MLX / CUDA / CPU)
The profile is a singleton row. Only an inference score is computed and stored — there is no fine-tuning or training score anywhere in the codebase.
API endpoints¶
The router is mounted with the /hardware prefix under the app-wide /erudi prefix, so the full paths are /erudi/hardware/....
GET /erudi/hardware/app_startup¶
Minimal payload consumed by the renderer on boot (HardwareAppStartupInfo in schemas.py). All six fields are required.
{
"backend_type": "mlx",
"global_inference_score": 92.0,
"global_inference_label": "Excellent",
"raw_inference_score": 72.0,
"recommended_param_min": 4.0,
"recommended_param_max": 14.0
}
global_inference_scoreis the boosted score,raw_inference_scorethe engine output.recommended_param_min/recommended_param_maxare the model-size window (in billions of parameters) that drives the "Models For You" list on the model page.
GET /erudi/hardware/detailed¶
Full diagnostics (DetailedHardwareInfo): the backend-specific hardware block, the typed performance breakdown, and the boosted score.
{
"hardware": { "backend_type": "mlx", "...": "backend-specific fields" },
"performance_breakdown": {
"compute_score": 95.0,
"memory_bandwidth_score": 92.0,
"memory_capacity_score": 88.0,
"cpu_performance_score": 75.0,
"disk_score": 75.0
},
"boosted_inference_score": 92.0
}
The raw score stays available inside hardware.raw_inference_score.
POST /erudi/hardware/refresh¶
Re-runs detection through the engine and updates the stored profile. No request body.
Useful after a hardware change, or to refresh the dynamic fields (available_memory_gb, disk_available_gb) without restarting the app.
Schemas¶
hardware/schemas.py builds the response types from a discriminated union keyed on backend_type:
BaseHardwareInfo— fields every backend provides (CPU model, memory, disk,raw_inference_score,global_inference_label,cpu_score,memory_score).MLXHardwareInfo— addsmlx_chip_model,mlx_gpu_cores,mps_available,neural_engine_tops,estimated_tflops,memory_bandwidth_gbs,gpu_score,unified_memory.CUDAHardwareInfo— addsgpu_name,cuda_cores,cuda_version,compute_capability,vram_total_gb,vram_available_gb,estimated_tflops,memory_bandwidth_gbs,gpu_score.CPUHardwareInfo— addscompute_units,cpu_performance_units,accelerator_available;gpu_scoreis always0.0.PerformanceBreakdown— typed component scores (compute_score,memory_bandwidth_score,memory_capacity_score,cpu_performance_score, optionaldisk_score).
Scoring¶
Each engine implements get_performance_evaluation() and returns a single 0-100 inference score built from normalized component scores.
MLX (engines/mlx_engine.py)¶
| Component | Weight | Normalization (= 100 points) |
|---|---|---|
| GPU compute | 35 % | 20 TFLOPS |
| Memory bandwidth | 30 % | 400 GB/s |
| Memory capacity | 20 % | 64 GB |
| Neural Engine | 10 % | 20 TOPS |
| CPU | 5 % | 20 CPU units (performance cores x 2.5) |
CUDA (engines/cuda_engine.py)¶
Weights live in _WEIGHTS_INFERENCE, normalization factors in _NORM_INFERENCE.
| Component | Weight | Normalization (= 100 points) |
|---|---|---|
GPU compute (gpu_compute) |
40 % | 80 TFLOPS (BF16/FP16) |
Memory bandwidth (gpu_bw) |
30 % | 500 GB/s |
VRAM (gpu_vram) |
15 % | 12 GB |
CPU single-core (cpu_single) |
5 % | 3.6 GHz |
System RAM (sys_ram) |
5 % | 24 GB |
PCIe (pcie) |
5 % | 32 (Gen3 x16 / Gen4 x8) |
CPU (engines/cpu_engine.py)¶
Weights live in INF_WEIGHTS. Memory bandwidth is estimated, not measured: 1.5 GB/s per core.
| Component | Weight | Normalization (= 100 points) |
|---|---|---|
| CPU cores | 40 % | 64 cores |
| Memory capacity | 30 % | 128 GB |
| Memory bandwidth | 20 % | 100 GB/s (estimated) |
| Disk | 10 % | 500 GB available |
Display boost and labels¶
Hardware_Service.calculate_boosted_scores() returns both the raw score and a boosted one for the UI:
The label is derived from the boosted score by Hardware_Service._get_label():
| Boosted score | Label |
|---|---|
| >= 80 | Excellent |
| >= 60 | Good |
| >= 40 | Fair |
| >= 20 | Poor |
| < 20 | Weak |
Service layer¶
from src.domains.hardware.services import Hardware_Service
service = Hardware_Service(repository)
profile = service.get_or_create_profile() # cached singleton, detects on first call
scores = service.calculate_boosted_scores(profile)
service.warm_up(duration_seconds=5) # warm the accelerator before benchmarking
profile = service.refresh_profile() # force re-detection
db.commit()
Engine contract¶
Every engine implements the same three hardware class methods:
@classmethod
def get_hardware_info(cls) -> Dict[str, Any]: ...
@classmethod
def warm_up_accelerator(cls, duration_seconds: float) -> bool: ...
@classmethod
def get_performance_evaluation(cls) -> Dict[str, Any]: ...
get_flat_hardware_data() flattens the nested detection dict into the columns the entity stores.
Database entity¶
HardwareProfile (entities/HardwareProfile.py) is a singleton row with backend-specific columns left nullable. It stores global_inference_score and global_inference_label (no fine-tuning columns) plus the JSON performance_breakdown.
It has no compute_units column: the CPU count is stored in cpu_performance_units and cast to an int when the endpoint builds CPUHardwareInfo.
Frontend¶
frontend/src/utils/hardwareTransform.js exports a single helper, transformAppStartupInfo, used to normalize the /erudi/hardware/app_startup payload for the renderer.
Troubleshooting¶
Stale hardware data — force a re-detection:
Missing backend-specific fields — branch on the backend_type discriminator before reading mlx_*, cuda_* or CPU-only fields; each backend only populates its own.
Code reference¶
src.domains.hardware.endpoints
¶
REST API endpoints for backend-agnostic hardware information.
Provides hardware detection, scoring, and monitoring endpoints across all backends (MLX/Apple Silicon, CUDA/NVIDIA, CPU fallback). Uses discriminated unions and transparent score boosting for optimal type safety and user experience.
Endpoints
GET /hardware/app_startup: Minimal UI data with boosted scores GET /hardware/detailed: Comprehensive diagnostics with raw/boosted scores POST /hardware/refresh: Force hardware re-detection
Architecture
Endpoints → Service → Repository → Entity → Database
get_app_startup_info(db=Depends(get_db))
¶
Get minimal performance scores for application startup dashboard.
Returns boosted scores (+20 points, capped at 100) for user-friendly display alongside raw scores for transparency. This endpoint is optimized for quick loading on app startup.
Response structure
{ "backend_type": "cuda", "global_inference_score": 100.0, # Boosted, capped at 100 "global_inference_label": "Excellent", "raw_inference_score": 82.0 # Actual hardware score }
Raises:
| Type | Description |
|---|---|
HTTPException
|
500 if hardware detection fails |
Source code in backend/src/domains/hardware/endpoints.py
get_detailed_hardware_info(db=Depends(get_db))
¶
Get comprehensive hardware diagnostics with raw/boosted score comparison.
Returns complete backend-specific hardware profile plus performance breakdown and both raw/boosted scores for debugging and diagnostics.
Response structure
{ "hardware": { ...full backend-specific fields... }, "performance_breakdown": { ...typed scores... }, "boosted_inference_score": 85.0 }
Note
Raw score available in hardware.raw_inference_score
Raises:
| Type | Description |
|---|---|
HTTPException
|
500 if hardware detection fails |
Source code in backend/src/domains/hardware/endpoints.py
refresh_hardware_profile(db=Depends(get_db))
¶
Force hardware re-detection and update cached profile.
Performs fresh hardware detection through engine and updates database profile. Useful for detecting hardware changes (RAM upgrade, new GPU) or refreshing dynamic fields (available_memory_gb, disk_available_gb) without app restart.
Returns:
| Name | Type | Description |
|---|---|---|
dict |
Confirmation message with backend_type |
Example response
{ "message": "Hardware profile refreshed successfully", "backend_type": "mlx" }
Raises:
| Type | Description |
|---|---|
HTTPException
|
500 if refresh fails |
Source code in backend/src/domains/hardware/endpoints.py
src.domains.hardware.schemas
¶
Pydantic validation schemas for backend-agnostic hardware information.
Defines response schemas for hardware detection endpoints across all backends (MLX/CUDA/CPU). Uses discriminated unions and inheritance to reduce duplication while maintaining type safety for backend-specific fields.
Architecture
- BaseHardwareInfo: Common fields shared by all backends
- MLXHardwareInfo/CUDAHardwareInfo/CPUHardwareInfo: Backend-specific extensions
- PerformanceBreakdown: Typed performance metrics (replaces Dict[str, Any])
- Endpoint-specific views: Training, AppStartup, Detailed
Example
Training endpoint returns full backend-specific schema::
{
"backend_type": "mlx",
"mlx_chip_model": "M3 Max",
"mlx_gpu_cores": 40,
...
}
App startup returns minimal UI data::
{
"backend_type": "cuda",
"raw_inference_score": 82.5,
"boosted_inference_score": 100.0,
...
}
BaseHardwareInfo
¶
Bases: BaseModel
Common fields shared by all backends.
All engines must provide these fields. Backend-specific fields defined in subclasses.
CPUHardwareInfo
¶
CUDAHardwareInfo
¶
DetailedHardwareInfo
¶
Bases: BaseModel
Comprehensive hardware profile for debugging and diagnostics.
Returns full backend-specific data plus performance breakdown and raw/boosted score comparison.
HardwareAppStartupInfo
¶
Bases: BaseModel
Essential performance metrics for application startup dashboard.
Returns minimal UI data with transparent score boosting for user-friendly display. Raw scores preserved for debugging.
MLXHardwareInfo
¶
PerformanceBreakdown
¶
Bases: BaseModel
Typed performance breakdown replacing Dict[str, Any].
Scores are normalized 0-100 scale representing component contributions to overall performance.
src.domains.hardware.services
¶
Business logic layer for hardware domain.
Implements Service pattern for hardware operations. Handles hardware detection, profile management, score calculation, and labeling logic.
Architecture
Endpoints → Service → Repository → Entity → Database
Key Responsibilities
- Orchestrate hardware detection through LLM_Engine
- Manage hardware profile lifecycle (get_or_create pattern)
- Calculate boosted scores for UI display (+20 points)
- Generate performance labels (Excellent, Good, Fair, Poor, Weak)
- Format performance_breakdown structure
Example
from src.domains.hardware.services import Hardware_Service from src.domains.hardware.repository import Hardware_Repository from src.database.core import SessionLocal
db = SessionLocal() repo = Hardware_Repository(db) service = Hardware_Service(repo)
profile = service.get_or_create_profile() boosted = service.calculate_boosted_scores(profile) db.commit()
Hardware_Service(repository)
¶
Service layer for hardware domain business logic.
Orchestrates hardware detection, profile management, and score calculations. Separates business logic from data access and API concerns.
Attributes:
| Name | Type | Description |
|---|---|---|
repository |
Hardware_Repository for data access operations. |
Note
Service methods do NOT commit. Commits are handled at endpoint level. Uses repository for all database operations.
Example
service = Hardware_Service(Hardware_Repository(db)) profile = service.get_or_create_profile() boosted = service.calculate_boosted_scores(profile) print(boosted["global_inference_score"]) # +20 boost 85.0
Initialize service with repository.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
repository
|
Hardware_Repository
|
Hardware_Repository instance for data access. |
required |
Source code in backend/src/domains/hardware/services.py
calculate_boosted_scores(profile)
¶
Calculate UI-friendly scores with +20 boost for frontend display.
Returns both raw scores (actual hardware capability) and boosted scores (UI-friendly display with +20 point boost, capped at 100). This makes the boost transparent while maintaining user-friendly presentation.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
profile
|
HardwareProfile
|
HardwareProfile entity with original scores. |
required |
Returns:
| Type | Description |
|---|---|
Dict[str, Any]
|
Dict[str, Any]: Dictionary with both raw and boosted scores/labels. |
Example
profile = service.get_or_create_profile() scores = service.calculate_boosted_scores(profile) print(scores) { "raw_inference_score": 65.0, "boosted_inference_score": 85.0, # min(65 + 20, 100) "global_inference_label": "Excellent", # Based on boosted ... }
Source code in backend/src/domains/hardware/services.py
get_or_create_profile()
¶
Get cached hardware profile or detect new one.
Retrieves existing hardware profile from database. If none exists or if the cached profile backend doesn't match the current engine, performs hardware detection and creates new profile.
Returns:
| Name | Type | Description |
|---|---|---|
HardwareProfile |
HardwareProfile
|
Existing or newly created profile. |
Raises:
| Type | Description |
|---|---|
HardwareException
|
If hardware detection or profile creation fails. |
Note
Does NOT commit. Caller must commit transaction.
Example
service = Hardware_Service(repo) profile = service.get_or_create_profile() print(profile.backend_type) # "mlx", "cuda", or "cpu"
Source code in backend/src/domains/hardware/services.py
refresh_profile()
¶
Re-detect hardware and update existing profile.
Performs fresh hardware detection and updates existing profile with new data. Useful for detecting hardware changes or refreshing dynamic fields (available_memory_gb, disk_available_gb).
Returns:
| Name | Type | Description |
|---|---|---|
HardwareProfile |
HardwareProfile
|
Updated profile entity. |
Raises:
| Type | Description |
|---|---|
HardwareException
|
If refresh fails. |
Note
Does NOT commit. Caller must commit transaction.
Example
service = Hardware_Service(repo) profile = service.refresh_profile() db.commit()
Source code in backend/src/domains/hardware/services.py
warm_up(duration_seconds=5)
¶
Warm up hardware accelerator (GPU/MPS/CUDA).
Delegates to LLM_Engine's warm_up_accelerator() method. Useful before benchmarking or first inference to ensure accurate performance.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
duration_seconds
|
int
|
How long to run warm-up routine. |
5
|
Returns:
| Name | Type | Description |
|---|---|---|
bool |
bool
|
True if warm-up succeeded, False otherwise. |
Raises:
| Type | Description |
|---|---|
HardwareException
|
If warm-up fails critically. |
Example
service = Hardware_Service(repo) success = service.warm_up(duration_seconds=5) if success: ... print("Accelerator ready")
Source code in backend/src/domains/hardware/services.py
recommended_param_range(profile)
¶
Recommended model size window (billions of params) this machine runs comfortably at 4-bit: the smaller of what fits and what runs fast enough.