From c8740a037447b05ddb3b6114deef9a38a2b6500a Mon Sep 17 00:00:00 2001 From: Christian Meyer Date: Fri, 28 Aug 2026 15:44:24 +0200 Subject: [PATCH] Support selectable Aider providers --- README.md | 38 ++++++++++++++++++++++++++++++++++--- docker-compose.yml | 4 ++++ src/lai_automation/agent.py | 9 ++++++++- tests/test_agent.py | 6 +++++- 4 files changed, 52 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 3b73787..89e2051 100644 --- a/README.md +++ b/README.md @@ -111,15 +111,47 @@ die empfohlene Betriebsart. ## Modelle -Default: +Das Modell wird ausschliesslich ueber `AGENT_MODEL` gewaehlt. Devstral ist der +Default und ein Coding-Agent, nicht das Chat-Modell der Laravel-Anwendung: ```dotenv AGENT_MODEL=mistral/devstral-latest +MISTRAL_API_KEY=... ``` -Das Chat-Modell und Coding-Modell sind bewusst getrennt. Fuer Coding-Agenten sollte ein +Codestral verwendet denselben Mistral-Zugang: + +```dotenv +AGENT_MODEL=mistral/codestral-latest +MISTRAL_API_KEY=... +``` + +Qwen 3.5 27B ueber OpenRouter: + +```dotenv +AGENT_MODEL=openrouter/qwen/qwen3.5-27b +OPENROUTER_API_KEY=... +``` + +Oder mit einem lokalen Ollama auf dem Docker-Host: + +```dotenv +AGENT_MODEL=ollama_chat/qwen3.5:27b +OLLAMA_API_BASE=http://host.docker.internal:11434 +``` + +Nach einer Aenderung wird der Worker neu erstellt, damit Compose die neue Umgebung +uebernimmt: + +```bash +docker compose up -d --force-recreate automation +docker compose exec -T automation lai-agent check +``` + +Aider waehlt das passende Edit-Format anhand des Modells. Das Chat-Modell und +Coding-Modell sind bewusst getrennt. Fuer Coding-Agenten sollte ein agentisch/coding-optimiertes Modell verwendet werden. Ein Wechsel zu OpenHands oder -einem anderen Backend erfordert nur einen neuen Adapter neben `AiderAgent`. +einem anderen Backend erfordert einen neuen Adapter neben `AiderAgent`. ## Tests diff --git a/docker-compose.yml b/docker-compose.yml index 9b082d6..723bb06 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -20,6 +20,10 @@ services: AGENT_COMMAND: ${AGENT_COMMAND:-aider} AGENT_MODEL: ${AGENT_MODEL:-mistral/devstral-latest} MISTRAL_API_KEY: ${MISTRAL_API_KEY:-} + OPENAI_API_KEY: ${OPENAI_API_KEY:-} + OPENAI_API_BASE: ${OPENAI_API_BASE:-} + OPENROUTER_API_KEY: ${OPENROUTER_API_KEY:-} + OLLAMA_API_BASE: ${OLLAMA_API_BASE:-} POLL_SECONDS: ${POLL_SECONDS:-60} MAX_AGENT_ATTEMPTS: ${MAX_AGENT_ATTEMPTS:-2} STALE_CLAIM_MINUTES: ${STALE_CLAIM_MINUTES:-90} diff --git a/src/lai_automation/agent.py b/src/lai_automation/agent.py index be18b38..90e262e 100644 --- a/src/lai_automation/agent.py +++ b/src/lai_automation/agent.py @@ -36,7 +36,14 @@ class AiderAgent: "HOME": coder.pw_dir, "PATH": os.environ.get("PATH", "/usr/local/bin:/usr/bin:/bin"), } - for name in ("MISTRAL_API_KEY", "OPENAI_API_KEY", "ANTHROPIC_API_KEY"): + for name in ( + "MISTRAL_API_KEY", + "OPENAI_API_KEY", + "OPENAI_API_BASE", + "ANTHROPIC_API_KEY", + "OPENROUTER_API_KEY", + "OLLAMA_API_BASE", + ): if os.getenv(name): environment[name] = os.environ[name] diff --git a/tests/test_agent.py b/tests/test_agent.py index 4738fd4..5ab335e 100644 --- a/tests/test_agent.py +++ b/tests/test_agent.py @@ -48,6 +48,8 @@ def test_aider_environment_does_not_include_database_credentials(tmp_path, monke workspace = tmp_path / "repo" (workspace / ".git").mkdir(parents=True) monkeypatch.setenv("TEST_DB_PASSWORD", "must-not-reach-aider") + monkeypatch.setenv("OPENROUTER_API_KEY", "qwen-provider-key") + monkeypatch.setenv("OLLAMA_API_BASE", "http://host.docker.internal:11434") captured = {} def run(command, **kwargs): @@ -63,4 +65,6 @@ def test_aider_environment_does_not_include_database_credentials(tmp_path, monke AiderAgent(config).run(workspace, "fix it") assert "TEST_DB_PASSWORD" not in captured - assert "DB_PASSWORD" not in captured \ No newline at end of file + assert "DB_PASSWORD" not in captured + assert captured["OPENROUTER_API_KEY"] == "qwen-provider-key" + assert captured["OLLAMA_API_BASE"] == "http://host.docker.internal:11434" \ No newline at end of file