Skip to content

Commit fc516f0

Browse files
fix(evaluators): configure Luna scorer API URL (#237)
1 parent 80aea8f commit fc516f0

7 files changed

Lines changed: 274 additions & 79 deletions

File tree

evaluators/contrib/galileo/src/agent_control_evaluator_galileo/luna/client.py

Lines changed: 67 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
import logging
66
import os
7+
import ssl
8+
import warnings
79
from base64 import urlsafe_b64encode
810
from hashlib import sha256
911
from hmac import new as hmac_new
@@ -20,6 +22,11 @@
2022

2123
DEFAULT_TIMEOUT_SECS = 10.0
2224
DEFAULT_INTERNAL_TOKEN_TTL_SECS = 3600
25+
# Keep pooled-connection reuse shorter than typical server keepalive/worker
26+
# recycle windows so requests do not pick up sockets the server already closed.
27+
DEFAULT_KEEPALIVE_EXPIRY_SECS = 1.0
28+
DEFAULT_MAX_CONNECTIONS = 100
29+
DEFAULT_MAX_KEEPALIVE_CONNECTIONS = 20
2330
PUBLIC_SCORER_INVOKE_PATH = "/scorers/invoke"
2431
INTERNAL_SCORER_INVOKE_PATH = "/internal/scorers/invoke"
2532
AuthMode = Literal["public", "internal"]
@@ -56,6 +63,13 @@ def _env_auth_mode() -> AuthMode | None:
5663
value = os.getenv("GALILEO_LUNA_AUTH_MODE")
5764
if value is None or value.strip() == "":
5865
return None
66+
deprecation_message = (
67+
"GALILEO_LUNA_AUTH_MODE is deprecated. Configure exactly one credential "
68+
"(GALILEO_API_KEY for public auth, GALILEO_API_SECRET_KEY for internal "
69+
"auth) or pass auth_mode to GalileoLunaClient."
70+
)
71+
warnings.warn(deprecation_message, DeprecationWarning, stacklevel=2)
72+
logger.warning(deprecation_message)
5973
normalized = value.strip().lower()
6074
if normalized == "public":
6175
return "public"
@@ -164,9 +178,12 @@ class GalileoLunaClient:
164178
"""Thin HTTP client for Galileo Luna direct scorer invocation.
165179
166180
Environment Variables:
167-
GALILEO_API_SECRET_KEY or GALILEO_API_SECRET: Galileo API internal JWT signing secret.
181+
GALILEO_API_SECRET_KEY: Deployment-provided Galileo API internal JWT signing secret.
168182
GALILEO_API_KEY: Galileo API key fallback for public scorer invocation.
169-
GALILEO_LUNA_AUTH_MODE: Auth mode, either "public" or "internal".
183+
GALILEO_LUNA_API_URL: Galileo Luna scorer invoke API URL override.
184+
GALILEO_API_URL: Galileo API URL fallback.
185+
GALILEO_LUNA_CA_FILE: CA bundle used to verify the scorer API endpoint, for
186+
deployments whose API serves an internally-issued TLS certificate.
170187
GALILEO_CONSOLE_URL: Galileo Console URL (optional, defaults to production).
171188
"""
172189

@@ -177,23 +194,27 @@ def __init__(
177194
console_url: str | None = None,
178195
api_url: str | None = None,
179196
auth_mode: AuthMode | None = None,
197+
ca_file: str | None = None,
180198
) -> None:
181199
"""Initialize the Galileo Luna client.
182200
183201
Args:
184202
api_key: Galileo API key. If not provided, reads from GALILEO_API_KEY.
185-
api_secret: Galileo API secret for internal JWT auth. If not provided,
186-
reads from GALILEO_API_SECRET_KEY or GALILEO_API_SECRET.
203+
api_secret: Deployment-provided Galileo API secret for internal JWT auth.
204+
If not provided, reads from GALILEO_API_SECRET_KEY.
187205
console_url: Galileo Console URL. If not provided, reads from
188206
GALILEO_CONSOLE_URL or uses the production console URL.
189-
api_url: Galileo API URL. If not provided, reads from GALILEO_API_URL
190-
before deriving from the console URL.
191-
auth_mode: Auth mode to use. If not provided, reads from
192-
GALILEO_LUNA_AUTH_MODE, or infers from the single available credential.
207+
api_url: Galileo API URL. If not provided, reads from GALILEO_LUNA_API_URL,
208+
then GALILEO_API_URL, before deriving from the console URL.
209+
auth_mode: Auth mode to use. If not provided, inferred from the single
210+
available credential.
211+
ca_file: CA bundle path used to verify the scorer API endpoint. If not
212+
provided, reads from GALILEO_LUNA_CA_FILE. Leave unset for endpoints
213+
with publicly-trusted certificates.
193214
194215
Raises:
195216
ValueError: If credentials are missing, ambiguous, or incompatible with
196-
the selected auth mode.
217+
the selected auth mode, or if the CA bundle cannot be loaded.
197218
"""
198219
resolved_api_secret = (
199220
api_secret or os.getenv("GALILEO_API_SECRET_KEY") or os.getenv("GALILEO_API_SECRET")
@@ -211,12 +232,32 @@ def __init__(
211232
self.console_url = (
212233
console_url or os.getenv("GALILEO_CONSOLE_URL") or "https://console.galileo.ai"
213234
)
214-
self.api_base = (api_url or os.getenv("GALILEO_API_URL") or "").rstrip(
215-
"/"
216-
) or self._derive_api_url(self.console_url)
235+
self.api_base = self._resolve_api_base(api_url)
236+
self.ca_file = (ca_file or os.getenv("GALILEO_LUNA_CA_FILE") or "").strip() or None
237+
self._ssl_context = self._load_ssl_context(self.ca_file)
217238
self._client: httpx.AsyncClient | None = None
218239
logger.info("[GalileoLunaClient] Auth mode selected: %s", self.auth_mode)
219240

241+
def _resolve_api_base(self, api_url: str | None) -> str:
242+
"""Resolve the scorer invoke API base URL from explicit and environment config."""
243+
candidates = [api_url, os.getenv("GALILEO_LUNA_API_URL")]
244+
candidates.append(os.getenv("GALILEO_API_URL"))
245+
246+
for candidate in candidates:
247+
if candidate and candidate.strip():
248+
return candidate.strip().rstrip("/")
249+
return self._derive_api_url(self.console_url)
250+
251+
@staticmethod
252+
def _load_ssl_context(ca_file: str | None) -> ssl.SSLContext | None:
253+
"""Build a TLS verification context from a CA bundle path, if configured."""
254+
if ca_file is None:
255+
return None
256+
try:
257+
return ssl.create_default_context(cafile=ca_file)
258+
except (OSError, ssl.SSLError) as exc:
259+
raise ValueError(f"Failed to load CA bundle from {ca_file!r}: {exc}") from exc
260+
220261
@staticmethod
221262
def _resolve_auth_mode(
222263
auth_mode: AuthMode | None,
@@ -226,24 +267,21 @@ def _resolve_auth_mode(
226267
) -> AuthMode:
227268
if auth_mode == "public":
228269
if not api_key:
229-
raise ValueError(
230-
"GALILEO_API_KEY is required when GALILEO_LUNA_AUTH_MODE=public."
231-
)
270+
raise ValueError("GALILEO_API_KEY is required for public Luna auth.")
232271
return "public"
233272

234273
if auth_mode == "internal":
235274
if not api_secret:
236275
raise ValueError(
237-
"GALILEO_API_SECRET_KEY or GALILEO_API_SECRET is required when "
238-
"GALILEO_LUNA_AUTH_MODE=internal."
276+
"GALILEO_API_SECRET_KEY is required for internal Luna auth."
239277
)
240278
return "internal"
241279

242280
if api_key and api_secret:
243281
raise ValueError(
244-
"Both Galileo API key and API secret are configured. Set "
245-
"GALILEO_LUNA_AUTH_MODE to 'public' or 'internal' to choose the "
246-
"runtime auth mode explicitly."
282+
"Both a Galileo API key and a Galileo API secret are configured. "
283+
"Unset one credential so the auth mode can be inferred, or pass "
284+
"auth_mode='public' or auth_mode='internal' explicitly."
247285
)
248286
if api_secret:
249287
return "internal"
@@ -284,9 +322,18 @@ async def _get_client(self) -> httpx.AsyncClient:
284322
headers = {"Content-Type": "application/json"}
285323
if self.auth_mode == "public" and self.api_key is not None:
286324
headers["Galileo-API-Key"] = self.api_key
325+
verify: ssl.SSLContext | bool = (
326+
self._ssl_context if self._ssl_context is not None else True
327+
)
287328
self._client = httpx.AsyncClient(
288329
headers=headers,
289330
timeout=httpx.Timeout(DEFAULT_TIMEOUT_SECS),
331+
limits=httpx.Limits(
332+
max_connections=DEFAULT_MAX_CONNECTIONS,
333+
max_keepalive_connections=DEFAULT_MAX_KEEPALIVE_CONNECTIONS,
334+
keepalive_expiry=DEFAULT_KEEPALIVE_EXPIRY_SECS,
335+
),
336+
verify=verify,
290337
)
291338
return self._client
292339

evaluators/contrib/galileo/tests/test_luna_coverage_gaps.py

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -457,7 +457,9 @@ def test_client_requires_explicit_mode_when_both_credentials_are_present(monkeyp
457457
monkeypatch.delenv("GALILEO_LUNA_AUTH_MODE", raising=False)
458458
from agent_control_evaluator_galileo.luna.client import GalileoLunaClient
459459

460-
with pytest.raises(ValueError, match="Both Galileo API key and API secret"):
460+
with pytest.raises(
461+
ValueError, match="Both a Galileo API key and a Galileo API secret are configured"
462+
):
461463
GalileoLunaClient()
462464

463465

@@ -468,7 +470,8 @@ def test_client_uses_explicit_public_mode_when_both_credentials_are_present(monk
468470
monkeypatch.setenv("GALILEO_LUNA_AUTH_MODE", "public")
469471
from agent_control_evaluator_galileo.luna.client import GalileoLunaClient
470472

471-
client = GalileoLunaClient()
473+
with pytest.warns(DeprecationWarning, match="GALILEO_LUNA_AUTH_MODE is deprecated"):
474+
client = GalileoLunaClient()
472475

473476
assert client.auth_mode == "public"
474477
endpoint, request_headers = client._endpoint_and_headers(None)
@@ -483,7 +486,8 @@ def test_client_uses_explicit_internal_mode_when_both_credentials_are_present(mo
483486
monkeypatch.setenv("GALILEO_LUNA_AUTH_MODE", "internal")
484487
from agent_control_evaluator_galileo.luna.client import GalileoLunaClient
485488

486-
client = GalileoLunaClient()
489+
with pytest.warns(DeprecationWarning, match="GALILEO_LUNA_AUTH_MODE is deprecated"):
490+
client = GalileoLunaClient()
487491

488492
assert client.auth_mode == "internal"
489493
endpoint, request_headers = client._endpoint_and_headers(None)
@@ -499,8 +503,9 @@ def test_client_rejects_mode_without_matching_credential(monkeypatch):
499503
monkeypatch.setenv("GALILEO_LUNA_AUTH_MODE", "internal")
500504
from agent_control_evaluator_galileo.luna.client import GalileoLunaClient
501505

502-
with pytest.raises(ValueError, match="GALILEO_API_SECRET_KEY"):
503-
GalileoLunaClient()
506+
with pytest.warns(DeprecationWarning, match="GALILEO_LUNA_AUTH_MODE is deprecated"):
507+
with pytest.raises(ValueError, match="GALILEO_API_SECRET_KEY"):
508+
GalileoLunaClient()
504509

505510

506511
def test_client_rejects_invalid_auth_mode(monkeypatch):
@@ -509,8 +514,9 @@ def test_client_rejects_invalid_auth_mode(monkeypatch):
509514
monkeypatch.setenv("GALILEO_LUNA_AUTH_MODE", "sideways")
510515
from agent_control_evaluator_galileo.luna.client import GalileoLunaClient
511516

512-
with pytest.raises(ValueError, match="GALILEO_LUNA_AUTH_MODE"):
513-
GalileoLunaClient()
517+
with pytest.warns(DeprecationWarning, match="GALILEO_LUNA_AUTH_MODE is deprecated"):
518+
with pytest.raises(ValueError, match="GALILEO_LUNA_AUTH_MODE"):
519+
GalileoLunaClient()
514520

515521

516522
class TestDeriveApiUrl:

0 commit comments

Comments
 (0)