mirror of
https://github.com/open-jarvis/OpenJarvis.git
synced 2026-07-30 19:02:16 +00:00
Phase 12 — Energy Measurement Upgrade: - EnergyMonitor ABC with multi-vendor support (NVIDIA hw counters, AMD amdsmi, Apple zeus-ml, CPU RAPL sysfs) - EnergyBatch batch-level energy-per-token accounting - SteadyStateDetector CV-based thermal equilibrium detection - EnergyBenchmark with warmup phase - InstrumentedEngine prefers EnergyMonitor over legacy GpuMonitor - Telemetry store/aggregator extended with energy fields Phase 13 — Install, Hosting, Cross-Hardware: - jarvis doctor diagnostic command (8 checks, --json output) - jarvis init post-setup guidance with engine-specific next steps - README Quick Start section - MLX engine backend (Apple Silicon → mlx recommendation) - AMD VRAM/multi-GPU detection via rocm-smi - PyTorch MPS device selection in orchestrator trainers - PWA support (vite-plugin-pwa, service worker, manifest, icons) - Server static file serving fix for PWA files - Dockerfile.gpu.rocm + docker-compose.gpu.rocm.yml for ROCm - Eval framework display module and efficiency metrics 2244 tests pass, 37 skipped. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
73 lines
2.4 KiB
Python
73 lines
2.4 KiB
Python
"""Tests for the MLX engine (OpenAI-compatible)."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import httpx
|
|
import pytest
|
|
import respx
|
|
|
|
from openjarvis.core.registry import EngineRegistry
|
|
from openjarvis.core.types import Message, Role
|
|
from openjarvis.engine._base import EngineConnectionError
|
|
from openjarvis.engine.mlx import MLXEngine
|
|
|
|
|
|
@pytest.fixture()
|
|
def engine() -> MLXEngine:
|
|
EngineRegistry.register_value("mlx", MLXEngine)
|
|
return MLXEngine(host="http://testhost:8080")
|
|
|
|
|
|
class TestMLXGenerate:
|
|
def test_generate_returns_content(self, engine: MLXEngine) -> None:
|
|
with respx.mock:
|
|
respx.post("http://testhost:8080/v1/chat/completions").mock(
|
|
return_value=httpx.Response(
|
|
200,
|
|
json={
|
|
"choices": [
|
|
{
|
|
"message": {"content": "4"},
|
|
"finish_reason": "stop",
|
|
}
|
|
],
|
|
"usage": {
|
|
"prompt_tokens": 8,
|
|
"completion_tokens": 1,
|
|
"total_tokens": 9,
|
|
},
|
|
"model": "mlx-model",
|
|
},
|
|
)
|
|
)
|
|
result = engine.generate(
|
|
[Message(role=Role.USER, content="2+2")], model="mlx-model"
|
|
)
|
|
assert result["content"] == "4"
|
|
|
|
def test_generate_connection_error(self, engine: MLXEngine) -> None:
|
|
with respx.mock:
|
|
respx.post("http://testhost:8080/v1/chat/completions").mock(
|
|
side_effect=httpx.ConnectError("refused")
|
|
)
|
|
with pytest.raises(EngineConnectionError):
|
|
engine.generate(
|
|
[Message(role=Role.USER, content="Hi")], model="m"
|
|
)
|
|
|
|
|
|
class TestMLXHealth:
|
|
def test_health_true(self, engine: MLXEngine) -> None:
|
|
with respx.mock:
|
|
respx.get("http://testhost:8080/v1/models").mock(
|
|
return_value=httpx.Response(200, json={"data": []})
|
|
)
|
|
assert engine.health() is True
|
|
|
|
def test_health_false(self, engine: MLXEngine) -> None:
|
|
with respx.mock:
|
|
respx.get("http://testhost:8080/v1/models").mock(
|
|
side_effect=httpx.ConnectError("refused")
|
|
)
|
|
assert engine.health() is False
|