| """Live integration test — hits real OpenRouter, picks pipeline, retrieves chunks. |
| |
| Skipped unless BOTH OPENROUTER_API_KEY is set AND the BBB model artifact |
| is built (the `run_bbb_pipeline` tool can't run without it). Marked `slow` |
| (network round-trips). |
| |
| The dual gate matters because src/llm/explainer.py auto-loads .env at |
| import time; without the model-artifact gate, this test would attempt a |
| real OpenRouter call in CI/dev and then fail because the BBB tool can't |
| execute. In the deployed Docker image both conditions are satisfied |
| (secret + build-time training). |
| """ |
| from __future__ import annotations |
|
|
| import os |
| from pathlib import Path |
|
|
| import pytest |
| from openai import NotFoundError, OpenAI |
|
|
| from src.agents.orchestrator import Orchestrator |
| from src.agents.prompts import ORCHESTRATOR_SYSTEM_PROMPT |
| from src.agents.tools import build_default_tools |
| from src.rag.ingest import ingest_directory |
|
|
|
|
| _FIXTURE_KB = Path(__file__).parent.parent / "fixtures" / "kb_sample" |
| _DEFAULT_MODEL = "google/gemini-2.0-flash-exp:free" |
| _FALLBACK_MODEL = "anthropic/claude-haiku-4-5" |
| _BBB_MODEL_PATH = Path( |
| os.environ.get("BBB_MODEL_PATH", "data/processed/bbb_model.joblib") |
| ) |
|
|
|
|
| @pytest.mark.slow |
| @pytest.mark.skipif( |
| not os.environ.get("OPENROUTER_API_KEY"), |
| reason="OPENROUTER_API_KEY not set", |
| ) |
| @pytest.mark.skipif( |
| not _BBB_MODEL_PATH.exists(), |
| reason=f"BBB model artifact missing at {_BBB_MODEL_PATH} — run python -m src.models.bbb_model", |
| ) |
| class TestOrchestratorLive: |
| @pytest.fixture(scope="class") |
| def rag_dir(self, tmp_path_factory: pytest.TempPathFactory) -> Path: |
| d = tmp_path_factory.mktemp("rag_live") |
| ingest_directory(_FIXTURE_KB, d) |
| return d |
|
|
| @pytest.fixture(scope="class") |
| def client(self) -> OpenAI: |
| return OpenAI( |
| base_url="https://openrouter.ai/api/v1", |
| api_key=os.environ["OPENROUTER_API_KEY"], |
| timeout=30.0, |
| ) |
|
|
| def test_smiles_input_picks_bbb_then_retrieves(self, client: OpenAI, rag_dir: Path) -> None: |
| tools = build_default_tools(rag_index_dir=rag_dir) |
| orch = Orchestrator( |
| llm_client=client, |
| tools=tools, |
| system_prompt=ORCHESTRATOR_SYSTEM_PROMPT, |
| model=os.environ.get("NEUROBRIDGE_AGENT_MODEL", _DEFAULT_MODEL), |
| max_steps=5, |
| ) |
| try: |
| result = orch.run("CCO") |
| except NotFoundError as e: |
| |
| |
| |
| |
| pytest.skip(f"agent model unavailable on OpenRouter: {e}") |
| |
| assert result.finish_reason == "complete", f"got {result.finish_reason}, trace={result.trace}" |
| tool_names = [t.name for t in result.trace] |
| assert "run_bbb_pipeline" in tool_names, f"BBB pipeline not called; trace={tool_names}" |
| assert "retrieve_context" in tool_names, f"RAG not called; trace={tool_names}" |
| assert result.text, "empty final text" |
|
|