From e609aa34ef33882047d4ca489a00aac86e31f6cb Mon Sep 17 00:00:00 2001 From: robert Date: Wed, 3 Jun 2026 20:59:02 -0300 Subject: [PATCH] Require forwarded reviewer LLM credentials --- agent_reviewer/builder.py | 21 ++++++++++++-- tests/test_agent.py | 60 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 3 deletions(-) diff --git a/agent_reviewer/builder.py b/agent_reviewer/builder.py index 7149701..7a0b070 100644 --- a/agent_reviewer/builder.py +++ b/agent_reviewer/builder.py @@ -117,10 +117,25 @@ def build_reviewer_graph( ) ) + missing_llm_fields = [ + name + for name, value in ( + ("llm_model", ctx.llm_model), + ("llm_base_url", ctx.llm_base_url), + ("llm_api_key", ctx.llm_api_key), + ) + if not value + ] + if missing_llm_fields: + raise ValueError( + "reviewer requires forwarded ctx.llm credentials; missing " + + ", ".join(missing_llm_fields) + ) + model_kwargs: dict[str, Any] = { - "model": ctx.llm_model or settings.litellm_model, - "base_url": ctx.llm_base_url or (settings.litellm_url + "/v1"), - "api_key": ctx.llm_api_key or settings.litellm_key, + "model": ctx.llm_model, + "base_url": ctx.llm_base_url, + "api_key": ctx.llm_api_key, "stream_usage": True, } if ctx.llm_temperature_mode != "omit": diff --git a/tests/test_agent.py b/tests/test_agent.py index b1ab0f4..04f4e05 100644 --- a/tests/test_agent.py +++ b/tests/test_agent.py @@ -13,6 +13,8 @@ if str(ROOT) not in sys.path: from a2a_pack import LocalRunContext, NoAuth # noqa: E402 from agent import AgentReviewer # noqa: E402 +from agent_reviewer import builder as reviewer_builder # noqa: E402 +from agent_reviewer.builder import ReviewerContext, build_reviewer_graph # noqa: E402 from agent_reviewer.tools import Finding, ReviewReport, ToolContext, build_tools # noqa: E402 from agent_reviewer.config import load_settings # noqa: E402 @@ -77,6 +79,64 @@ async def test_review_returns_structured_error_without_llm_credentials( assert result["ref"] == "main" +def test_reviewer_graph_uses_forwarded_llm_credentials( + monkeypatch: pytest.MonkeyPatch, +) -> None: + captured: dict = {} + + class FakeChatOpenAI: + def __init__(self, **kwargs: object) -> None: + captured["model_kwargs"] = kwargs + + def fake_create_deep_agent(**kwargs: object) -> dict[str, object]: + captured["graph_kwargs"] = kwargs + return kwargs + + monkeypatch.setattr(reviewer_builder, "ChatOpenAI", FakeChatOpenAI) + monkeypatch.setattr(reviewer_builder, "create_deep_agent", fake_create_deep_agent) + + graph = build_reviewer_graph( + ReviewerContext( + agent_name="hello-agent", + ref="main", + gitea_token="gitea-read-token", + gitea_owner="gitea_admin", + llm_base_url="http://litellm.test/v1", + llm_api_key="scoped-grant-token", + llm_model="platform-model", + llm_temperature_mode="omit", + llm_extra_body={"extra_body": {"thinking": {"type": "disabled"}}}, + ), + completion_box={}, + ) + + assert graph is captured["graph_kwargs"] + assert captured["model_kwargs"] == { + "model": "platform-model", + "base_url": "http://litellm.test/v1", + "api_key": "scoped-grant-token", + "stream_usage": True, + "extra_body": {"extra_body": {"thinking": {"type": "disabled"}}}, + } + + +def test_reviewer_graph_rejects_missing_forwarded_llm_credentials( + monkeypatch: pytest.MonkeyPatch, +) -> None: + monkeypatch.setenv("A2A_LITELLM_KEY", "env-key-should-not-be-used") + + with pytest.raises(ValueError, match="forwarded ctx\\.llm credentials"): + build_reviewer_graph( + ReviewerContext( + agent_name="hello-agent", + ref="main", + gitea_token="gitea-read-token", + gitea_owner="gitea_admin", + ), + completion_box={}, + ) + + def test_invalid_severity_rejected() -> None: with pytest.raises(Exception): Finding(