Adds the `bigbrain` optional dependency group (airllm>=2.9.0) and a
complete second inference path that runs 8B / 70B / 405B Llama models
locally via layer-by-layer loading — no GPU required, no cloud, fully
sovereign.
Key changes:
- src/timmy/backends.py — TimmyAirLLMAgent (same print_response interface
as Agno Agent); auto-selects AirLLMMLX on Apple
Silicon, AutoModel (PyTorch) everywhere else
- src/timmy/agent.py — _resolve_backend() routing with explicit override,
env-config, and 'auto' Apple-Silicon detection
- src/timmy/cli.py — --backend / --model-size flags on all commands
- src/config.py — timmy_model_backend + airllm_model_size settings
- src/timmy/prompts.py — mentions AirLLM "even bigger brains, still fully
sovereign"
- pyproject.toml — bigbrain optional dep; wheel includes updated
- .env.example — TIMMY_MODEL_BACKEND + AIRLLM_MODEL_SIZE docs
- tests/conftest.py — stubs 'airllm' module so tests run without GPU
- tests/test_backends.py — 13 new tests covering helpers + TimmyAirLLMAgent
- tests/test_agent.py — 7 new tests for backend routing
- README.md — Big Brain section with one-line install
- activate_self_tdd.sh — bootstrap script (venv + install + tests +
watchdog + dashboard); --big-brain flag
All 61 tests pass. Self-TDD watchdog unaffected.
https://claude.ai/code/session_01DMjQ5qMZ8iHeyix1j3GS7c
154 lines
5.2 KiB
Python
154 lines
5.2 KiB
Python
from unittest.mock import MagicMock, patch
|
|
|
|
|
|
def test_create_timmy_returns_agent():
|
|
"""create_timmy should delegate to Agno Agent with correct config."""
|
|
with patch("timmy.agent.Agent") as MockAgent, \
|
|
patch("timmy.agent.Ollama"), \
|
|
patch("timmy.agent.SqliteDb"):
|
|
|
|
mock_instance = MagicMock()
|
|
MockAgent.return_value = mock_instance
|
|
|
|
from timmy.agent import create_timmy
|
|
result = create_timmy()
|
|
|
|
assert result is mock_instance
|
|
MockAgent.assert_called_once()
|
|
|
|
|
|
def test_create_timmy_agent_name():
|
|
with patch("timmy.agent.Agent") as MockAgent, \
|
|
patch("timmy.agent.Ollama"), \
|
|
patch("timmy.agent.SqliteDb"):
|
|
|
|
from timmy.agent import create_timmy
|
|
create_timmy()
|
|
|
|
kwargs = MockAgent.call_args.kwargs
|
|
assert kwargs["name"] == "Timmy"
|
|
|
|
|
|
def test_create_timmy_uses_llama32():
|
|
with patch("timmy.agent.Agent"), \
|
|
patch("timmy.agent.Ollama") as MockOllama, \
|
|
patch("timmy.agent.SqliteDb"):
|
|
|
|
from timmy.agent import create_timmy
|
|
create_timmy()
|
|
|
|
MockOllama.assert_called_once_with(id="llama3.2")
|
|
|
|
|
|
def test_create_timmy_history_config():
|
|
with patch("timmy.agent.Agent") as MockAgent, \
|
|
patch("timmy.agent.Ollama"), \
|
|
patch("timmy.agent.SqliteDb"):
|
|
|
|
from timmy.agent import create_timmy
|
|
create_timmy()
|
|
|
|
kwargs = MockAgent.call_args.kwargs
|
|
assert kwargs["add_history_to_context"] is True
|
|
assert kwargs["num_history_runs"] == 10
|
|
assert kwargs["markdown"] is True
|
|
|
|
|
|
def test_create_timmy_custom_db_file():
|
|
with patch("timmy.agent.Agent"), \
|
|
patch("timmy.agent.Ollama"), \
|
|
patch("timmy.agent.SqliteDb") as MockDb:
|
|
|
|
from timmy.agent import create_timmy
|
|
create_timmy(db_file="custom.db")
|
|
|
|
MockDb.assert_called_once_with(db_file="custom.db")
|
|
|
|
|
|
def test_create_timmy_embeds_system_prompt():
|
|
from timmy.prompts import TIMMY_SYSTEM_PROMPT
|
|
|
|
with patch("timmy.agent.Agent") as MockAgent, \
|
|
patch("timmy.agent.Ollama"), \
|
|
patch("timmy.agent.SqliteDb"):
|
|
|
|
from timmy.agent import create_timmy
|
|
create_timmy()
|
|
|
|
kwargs = MockAgent.call_args.kwargs
|
|
assert kwargs["description"] == TIMMY_SYSTEM_PROMPT
|
|
|
|
|
|
# ── AirLLM path ──────────────────────────────────────────────────────────────
|
|
|
|
def test_create_timmy_airllm_returns_airllm_agent():
|
|
"""backend='airllm' must return a TimmyAirLLMAgent, not an Agno Agent."""
|
|
with patch("timmy.backends.is_apple_silicon", return_value=False):
|
|
from timmy.agent import create_timmy
|
|
from timmy.backends import TimmyAirLLMAgent
|
|
|
|
result = create_timmy(backend="airllm", model_size="8b")
|
|
|
|
assert isinstance(result, TimmyAirLLMAgent)
|
|
|
|
|
|
def test_create_timmy_airllm_does_not_call_agno_agent():
|
|
"""When using the airllm backend, Agno Agent should never be instantiated."""
|
|
with patch("timmy.agent.Agent") as MockAgent, \
|
|
patch("timmy.backends.is_apple_silicon", return_value=False):
|
|
|
|
from timmy.agent import create_timmy
|
|
create_timmy(backend="airllm", model_size="8b")
|
|
|
|
MockAgent.assert_not_called()
|
|
|
|
|
|
def test_create_timmy_explicit_ollama_ignores_autodetect():
|
|
"""backend='ollama' must always use Ollama, even on Apple Silicon."""
|
|
with patch("timmy.agent.Agent") as MockAgent, \
|
|
patch("timmy.agent.Ollama"), \
|
|
patch("timmy.agent.SqliteDb"):
|
|
|
|
from timmy.agent import create_timmy
|
|
create_timmy(backend="ollama")
|
|
|
|
MockAgent.assert_called_once()
|
|
|
|
|
|
# ── _resolve_backend ─────────────────────────────────────────────────────────
|
|
|
|
def test_resolve_backend_explicit_takes_priority():
|
|
from timmy.agent import _resolve_backend
|
|
assert _resolve_backend("airllm") == "airllm"
|
|
assert _resolve_backend("ollama") == "ollama"
|
|
|
|
|
|
def test_resolve_backend_defaults_to_ollama_without_config():
|
|
"""Default config (timmy_model_backend='ollama') → 'ollama'."""
|
|
from timmy.agent import _resolve_backend
|
|
assert _resolve_backend(None) == "ollama"
|
|
|
|
|
|
def test_resolve_backend_auto_uses_airllm_on_apple_silicon():
|
|
"""'auto' on Apple Silicon with airllm stubbed → 'airllm'."""
|
|
with patch("timmy.backends.is_apple_silicon", return_value=True), \
|
|
patch("timmy.agent.settings") as mock_settings:
|
|
mock_settings.timmy_model_backend = "auto"
|
|
mock_settings.airllm_model_size = "70b"
|
|
mock_settings.ollama_model = "llama3.2"
|
|
|
|
from timmy.agent import _resolve_backend
|
|
assert _resolve_backend(None) == "airllm"
|
|
|
|
|
|
def test_resolve_backend_auto_falls_back_on_non_apple():
|
|
"""'auto' on non-Apple Silicon → 'ollama'."""
|
|
with patch("timmy.backends.is_apple_silicon", return_value=False), \
|
|
patch("timmy.agent.settings") as mock_settings:
|
|
mock_settings.timmy_model_backend = "auto"
|
|
mock_settings.airllm_model_size = "70b"
|
|
mock_settings.ollama_model = "llama3.2"
|
|
|
|
from timmy.agent import _resolve_backend
|
|
assert _resolve_backend(None) == "ollama"
|