Bootstrap rein-openweights: minimal agentic loop over OpenRouter (REIN-OW-WP-0001)
Implements T01-T04: - tools.py: green-commit-only-equivalent tool surface (read/write/edit/ glob/grep + git status/diff/log/add+commit), path-traversal guarded. - openrouter_client.py: own minimal chat-completions client — llm-connect's OpenRouterAdapter takes a single prompt string and never surfaces tool_calls, so it can't drive a multi-turn tool-calling loop without a breaking change to its frozen Core ABC. llm-connect stays an optional dependency (pyproject.toml), not load-bearing. - loop.py: plan -> tool call -> observe -> repeat, budget- and turn-bounded, tool errors reported back to the model instead of crashing the loop. - credentials.py: own OpenBao AppRole/ambient-token acquisition, per glas-harness ADR-002 (Option B) — glas-harness does not broker this. - runner.py/hub.py: commit-verified success criterion + State Hub progress/token reporting, mirroring rein-aharness's model. 26 tests, all mocked at the httpx/subprocess boundary — no real OpenRouter or OpenBao calls made. T05 (Forgejo repo creation) stays open, deferred to the operator. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
ac5407a83e
commit
4ee612140b
21 changed files with 1207 additions and 36 deletions
43
tests/test_openrouter_client.py
Normal file
43
tests/test_openrouter_client.py
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from rein_openweights.openrouter_client import OpenRouterClient, OpenRouterError
|
||||
|
||||
|
||||
def test_chat_sends_bearer_auth_and_model():
|
||||
client = OpenRouterClient(api_key="sk-test", model="meta-llama/llama-3.1-70b-instruct")
|
||||
fake_response = MagicMock(status_code=200)
|
||||
fake_response.json.return_value = {"choices": [{"message": {"content": "hi"}}]}
|
||||
|
||||
with patch("rein_openweights.openrouter_client.httpx.post", return_value=fake_response) as post:
|
||||
data = client.chat([{"role": "user", "content": "hello"}])
|
||||
|
||||
assert data["choices"][0]["message"]["content"] == "hi"
|
||||
_, kwargs = post.call_args
|
||||
assert kwargs["headers"]["Authorization"] == "Bearer sk-test"
|
||||
assert kwargs["json"]["model"] == "meta-llama/llama-3.1-70b-instruct"
|
||||
assert "tools" not in kwargs["json"]
|
||||
|
||||
|
||||
def test_chat_includes_tools_when_provided():
|
||||
client = OpenRouterClient(api_key="sk-test", model="m")
|
||||
fake_response = MagicMock(status_code=200)
|
||||
fake_response.json.return_value = {"choices": [{"message": {}}]}
|
||||
tools = [{"type": "function", "function": {"name": "read_file"}}]
|
||||
|
||||
with patch("rein_openweights.openrouter_client.httpx.post", return_value=fake_response) as post:
|
||||
client.chat([{"role": "user", "content": "x"}], tools=tools)
|
||||
|
||||
_, kwargs = post.call_args
|
||||
assert kwargs["json"]["tools"] == tools
|
||||
assert kwargs["json"]["tool_choice"] == "auto"
|
||||
|
||||
|
||||
def test_chat_raises_on_http_error():
|
||||
client = OpenRouterClient(api_key="sk-test", model="m")
|
||||
fake_response = MagicMock(status_code=401, text="unauthorized")
|
||||
|
||||
with patch("rein_openweights.openrouter_client.httpx.post", return_value=fake_response):
|
||||
with pytest.raises(OpenRouterError, match="401"):
|
||||
client.chat([{"role": "user", "content": "x"}])
|
||||
Loading…
Add table
Add a link
Reference in a new issue