mirror of
https://github.com/open-jarvis/OpenJarvis.git
synced 2026-07-30 10:52:15 +00:00
Complete implementation across six development phases (v0.1 through v1.0): - Core: Registry system, config, event bus, types (Phase 0) - Intelligence + Inference: Model routing, Ollama/vLLM/llama.cpp/Cloud engines (Phase 1) - Memory: SQLite/FAISS/ColBERT/BM25/Hybrid backends, document ingest, context injection (Phase 2) - Agents: Simple/Orchestrator/Custom/OpenClaw agents, tool system (Phase 3) - Learning: HeuristicRouter, reward functions, GRPO stub, telemetry aggregation (Phase 4) - SDK: Jarvis class, OpenClaw protocol/transport, benchmarks, Docker deployment (Phase 5) 520 tests passing, 8 skipped (optional deps). Ruff lint clean. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
106 lines
3.6 KiB
Python
106 lines
3.6 KiB
Python
"""Tests for the OpenAI-compatible engine base (covers vLLM + llama.cpp)."""
|
|
|
|
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.vllm import VLLMEngine
|
|
|
|
|
|
@pytest.fixture()
|
|
def engine() -> VLLMEngine:
|
|
EngineRegistry.register_value("vllm", VLLMEngine)
|
|
return VLLMEngine(host="http://testhost:8000")
|
|
|
|
|
|
class TestOpenAICompatGenerate:
|
|
def test_generate_returns_content(self, engine: VLLMEngine) -> None:
|
|
with respx.mock:
|
|
respx.post("http://testhost:8000/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": "qwen3:8b",
|
|
},
|
|
)
|
|
)
|
|
result = engine.generate(
|
|
[Message(role=Role.USER, content="2+2")], model="qwen3:8b"
|
|
)
|
|
assert result["content"] == "4"
|
|
assert result["usage"]["total_tokens"] == 9
|
|
|
|
def test_generate_connection_error(self, engine: VLLMEngine) -> None:
|
|
with respx.mock:
|
|
respx.post("http://testhost:8000/v1/chat/completions").mock(
|
|
side_effect=httpx.ConnectError("refused")
|
|
)
|
|
with pytest.raises(EngineConnectionError):
|
|
engine.generate(
|
|
[Message(role=Role.USER, content="Hi")], model="qwen3:8b"
|
|
)
|
|
|
|
|
|
class TestOpenAICompatListModels:
|
|
def test_list_models(self, engine: VLLMEngine) -> None:
|
|
with respx.mock:
|
|
respx.get("http://testhost:8000/v1/models").mock(
|
|
return_value=httpx.Response(
|
|
200,
|
|
json={"data": [{"id": "model-a"}, {"id": "model-b"}]},
|
|
)
|
|
)
|
|
assert engine.list_models() == ["model-a", "model-b"]
|
|
|
|
|
|
class TestOpenAICompatHealth:
|
|
def test_health_true(self, engine: VLLMEngine) -> None:
|
|
with respx.mock:
|
|
respx.get("http://testhost:8000/v1/models").mock(
|
|
return_value=httpx.Response(200, json={"data": []})
|
|
)
|
|
assert engine.health() is True
|
|
|
|
def test_health_false(self, engine: VLLMEngine) -> None:
|
|
with respx.mock:
|
|
respx.get("http://testhost:8000/v1/models").mock(
|
|
side_effect=httpx.ConnectError("refused")
|
|
)
|
|
assert engine.health() is False
|
|
|
|
|
|
class TestOpenAICompatStream:
|
|
@pytest.mark.asyncio
|
|
async def test_stream_sse(self, engine: VLLMEngine) -> None:
|
|
sse_lines = (
|
|
'data: {"choices":[{"delta":{"content":"Hi"}}]}\n'
|
|
'data: {"choices":[{"delta":{"content":" there"}}]}\n'
|
|
"data: [DONE]\n"
|
|
)
|
|
with respx.mock:
|
|
respx.post("http://testhost:8000/v1/chat/completions").mock(
|
|
return_value=httpx.Response(200, text=sse_lines)
|
|
)
|
|
tokens = []
|
|
async for tok in engine.stream(
|
|
[Message(role=Role.USER, content="Hello")], model="m"
|
|
):
|
|
tokens.append(tok)
|
|
assert tokens == ["Hi", " there"]
|