Spaces:
Paused
Paused
File size: 10,131 Bytes
8c1b9fe 656439d 8c1b9fe | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 | from __future__ import annotations
from auralynq.cli import app
from auralynq.providers import describe_providers, health_snapshot
from auralynq.voice.asr import NullASR
from auralynq.voice.diarize import _heuristic
from auralynq.voice.factory import build_asr, build_tts
from typer.testing import CliRunner
runner = CliRunner()
def test_cli_version():
res = runner.invoke(app, ["--version"])
assert res.exit_code == 0
assert "auralynq" in res.stdout
def test_cli_help_lists_commands():
res = runner.invoke(app, ["--help"])
assert res.exit_code == 0
for cmd in ("ingest", "ask", "talk", "eval", "bench", "data", "serve", "mcp", "index"):
assert cmd in res.stdout
def test_cli_ingest_and_ask(corpus_dir):
res = runner.invoke(app, ["index", "--input", str(corpus_dir)])
assert res.exit_code == 0, res.stdout
from auralynq.agent import runner as ar
ar._CACHE.clear()
res2 = runner.invoke(app, ["ask", "What is the capital of France?", "--json"])
assert res2.exit_code == 0, res2.stdout
assert "answer" in res2.stdout
def test_cli_info(corpus_dir):
res = runner.invoke(app, ["info"])
assert res.exit_code == 0
assert "embeddings" in res.stdout
def test_describe_providers_resolves_fallbacks(monkeypatch):
import auralynq.llm.factory as lf
# Force SLM unavailable so the chain reaches extractive deterministically.
monkeypatch.setattr(lf, "_slm_available", lambda: False)
monkeypatch.setattr(lf, "_ollama_reachable", lambda url: False)
rows = {r["subsystem"]: r["provider"] for r in describe_providers()}
assert rows["embeddings"] == "hash"
assert rows["vector_store"] == "memory"
assert rows["llm"] == "extractive"
assert rows["asr"] == "null"
def test_health_snapshot():
snap = health_snapshot()
assert snap["status"] == "ok"
assert "providers" in snap
def test_factory_fallbacks():
assert build_asr().name == "null"
assert build_tts().name == "null"
assert isinstance(build_asr(), NullASR)
def test_diarize_heuristic_alternates_on_pause():
from auralynq.voice.asr import ASRSegment
segs = [ASRSegment(0.0, 1.0, "a"), ASRSegment(3.0, 4.0, "b")] # >1s gap
out = _heuristic(segs)
assert out[0].speaker != out[1].speaker
def test_llm_model_defaulting_for_commercial_providers():
"""Ollama-style model tags must not leak into OpenAI/Anthropic (would 404)."""
from auralynq.llm.factory import _model_for
# Ollama-looking tag -> provider default
assert _model_for("openai", "llama3.2:3b") == "gpt-4o-mini"
assert _model_for("anthropic", "llama3.2:3b") == "claude-3-5-haiku-latest"
# An explicit provider-appropriate model is respected
assert _model_for("openai", "gpt-4o") == "gpt-4o"
assert _model_for("anthropic", "claude-3-5-sonnet-latest") == "claude-3-5-sonnet-latest"
# Unknown provider passes through unchanged
assert _model_for("ollama", "llama3.2:3b") == "llama3.2:3b"
def test_resilient_llm_falls_back_on_runtime_error():
"""A provider that raises at request time must degrade to extractive, not crash."""
from auralynq.llm.base import LLM, Context
from auralynq.llm.resilient import ResilientLLM
class _Boom(LLM):
name = "boom"
def generate(self, prompt, *, system=None, temperature=None, max_tokens=None):
raise RuntimeError("billing_not_active")
r = ResilientLLM(_Boom())
ctx = [Context(marker=1, text="Paris is the capital of France.", source="geo.md")]
out = r.answer("What is the capital of France?", ctx)
assert out and "[1]" in out # extractive fallback produced a cited answer
assert r.last_fallback == "RuntimeError"
# streaming also degrades cleanly
streamed = "".join(r.stream_answer("What is the capital of France?", ctx))
assert "[1]" in streamed
assert r.served_by == "extractive"
def _boom_llm(label="boom"):
from auralynq.llm.base import LLM
class _Boom(LLM):
name = label
def generate(self, prompt, *, system=None, temperature=None, max_tokens=None):
raise RuntimeError("billing_not_active")
return _Boom()
def _echo_llm(label, text):
from auralynq.llm.base import LLM
class _Echo(LLM):
name = label
def generate(self, prompt, *, system=None, temperature=None, max_tokens=None):
return text
return _Echo()
def test_resilient_llm_prefers_local_backup_over_extractive():
"""A failing hosted provider degrades to a local generative model, not extractive."""
from auralynq.llm.base import Context
from auralynq.llm.resilient import ResilientLLM
r = ResilientLLM(_boom_llm("huggingface"), backups=[lambda: _echo_llm("ollama", "local ans")])
ctx = [Context(marker=1, text="Paris is the capital of France.", source="geo.md")]
assert r.answer("capital?", ctx) == "local ans"
assert r.served_by == "ollama"
assert r.last_fallback == "RuntimeError"
assert "".join(r.stream_answer("capital?", ctx)) == "local ans"
def test_resilient_llm_skips_unconstructable_and_failing_backups():
"""Backups that fail to construct or raise are skipped; extractive is terminal."""
from auralynq.llm.base import Context
from auralynq.llm.resilient import ResilientLLM
def _cannot_construct():
raise RuntimeError("ollama daemon down")
r = ResilientLLM(
_boom_llm("huggingface"),
backups=[_cannot_construct, lambda: _boom_llm("slm")],
)
ctx = [Context(marker=1, text="Paris is the capital of France.", source="geo.md")]
out = r.answer("capital?", ctx)
assert "[1]" in out # fell all the way through to extractive
assert r.served_by == "extractive"
def test_ollama_backup_model_resolves_against_installed_tags(monkeypatch):
"""A hosted model id must not be handed to Ollama verbatim (it would 404)."""
from auralynq.llm import factory
installed = ["llama3.2:3b", "qwen2.5:14b", "nomic-embed-text:latest"]
monkeypatch.setattr(factory, "_installed_ollama_models", lambda _url: installed)
resolve = factory._ollama_backup_model
# Hosted repo path → not installed → falls to the local default.
assert resolve("meta-llama/Llama-3.3-70B-Instruct", "u") == "llama3.2:3b"
# Configured tag that IS installed wins.
assert resolve("qwen2.5:14b", "u") == "qwen2.5:14b"
# Bare family name matches an installed tag.
assert resolve("qwen2.5", "u") == "qwen2.5:14b"
# Default absent → first installed non-embedding model, never the embedder.
monkeypatch.setattr(
factory, "_installed_ollama_models", lambda _url: ["nomic-embed-text:latest", "phi4:14b"]
)
assert resolve("meta-llama/Llama-3.3-70B-Instruct", "u") == "phi4:14b"
# Only embedding models installed → no valid generation backup.
monkeypatch.setattr(factory, "_installed_ollama_models", lambda _url: ["bge-m3:latest"])
assert resolve("meta-llama/Llama-3.3-70B-Instruct", "u") is None
# Tag listing failed → trust an ollama-shaped name, reject a repo path.
monkeypatch.setattr(factory, "_installed_ollama_models", lambda _url: [])
assert resolve("llama3.1:8b", "u") == "llama3.1:8b"
assert resolve("meta-llama/Llama-3.3-70B-Instruct", "u") is None
def test_resilient_llm_healthy_primary_ignores_backups():
"""No failure → primary serves and backups are never constructed."""
from auralynq.llm.base import Context
from auralynq.llm.resilient import ResilientLLM
def _must_not_run():
raise AssertionError("backup constructed despite healthy primary")
r = ResilientLLM(_echo_llm("huggingface", "hosted ans"), backups=[_must_not_run])
ctx = [Context(marker=1, text="Paris is the capital of France.", source="geo.md")]
assert r.answer("capital?", ctx) == "hosted ans"
assert r.served_by == "huggingface"
assert r.last_fallback is None
def test_llm_model_defaulting_for_cohere():
from auralynq.llm.factory import _model_for
assert _model_for("cohere", "llama3.2:3b") == "command-r-08-2024"
assert _model_for("cohere", "command-r-plus") == "command-r-plus"
def test_llm_auto_resolves_cohere_when_only_cohere_key(monkeypatch):
"""With only a Cohere key + sdk present (and no Ollama), auto resolution selects cohere."""
import sys
import types
from auralynq.config import reload_settings
# The conftest blanks all secret envs; set just Cohere for this test.
monkeypatch.setenv("AURALYNQ_LLM__PROVIDER", "auto")
monkeypatch.setenv("COHERE_API_KEY", "test-cohere-key")
monkeypatch.setenv("OPENAI_API_KEY", "")
monkeypatch.setenv("ANTHROPIC_API_KEY", "")
reload_settings()
# Stub the optional `cohere` SDK so importlib.util.find_spec() sees it without
# installing it. find_spec requires a real __spec__ on the module object.
import importlib.machinery
import importlib.util
if importlib.util.find_spec("cohere") is None:
fake = types.ModuleType("cohere")
fake.__spec__ = importlib.machinery.ModuleSpec("cohere", loader=None)
fake.ClientV2 = lambda api_key: object() # type: ignore[attr-defined]
monkeypatch.setitem(sys.modules, "cohere", fake)
# Auto-resolution now prefers Ollama when reachable; simulate no local Ollama
# to exercise the cloud-provider fallback path.
import auralynq.llm.factory as lf
monkeypatch.setattr(lf, "_ollama_reachable", lambda url: False)
monkeypatch.setattr(lf, "_slm_available", lambda: False)
from auralynq.llm.factory import resolved_provider
assert resolved_provider() == "cohere"
def test_cohere_rerank_model_defaulting():
"""A local BGE reranker name must not be sent to Cohere's hosted reranker."""
from auralynq.retrieval.hybrid.rerank import _cohere_rerank_model
assert _cohere_rerank_model("BAAI/bge-reranker-v2-m3") == "rerank-v3.5"
assert _cohere_rerank_model("bge-reranker-large") == "rerank-v3.5"
assert _cohere_rerank_model("rerank-v3.5") == "rerank-v3.5"
assert _cohere_rerank_model("rerank-english-v3.0") == "rerank-english-v3.0"
|