Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions skillopt_sleep/backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -1311,8 +1311,12 @@ def _read_chat_extra_body() -> Optional[Dict[str, Any]]:

def _is_azure_host(self) -> bool:
from urllib.parse import urlparse
host = (urlparse(self.endpoint).hostname or "").lower()
return host.endswith(self._AZURE_HOST_SUFFIXES)
parsed = urlparse(self.endpoint)
host = (parsed.hostname or "").lower()
return (
parsed.scheme.lower() == "https"
and host.endswith(self._AZURE_HOST_SUFFIXES)
)

def _get_client(self):
if self._client is None:
Expand Down Expand Up @@ -1383,7 +1387,7 @@ def _call(self, prompt: str, *, max_tokens: int = 1024, retries: int = 5) -> str
kwargs["max_tokens"] = self.compat_max_tokens
else:
kwargs["max_completion_tokens"] = 16384
if self.chat_extra_body:
if self._compat_mode() and self.chat_extra_body:
kwargs["extra_body"] = self.chat_extra_body
resp = client.chat.completions.create(**kwargs)
text = (resp.choices[0].message.content or "").strip()
Expand Down
20 changes: 20 additions & 0 deletions tests/test_azure_openai_compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,17 @@ def test_managed_identity_refuses_non_azure_endpoint(self):
be._get_client()
self.assertIn("openai_compatible", str(ctx.exception))

def test_managed_identity_refuses_insecure_azure_endpoint(self):
# A matching Azure hostname is insufficient: AAD bearer credentials
# must never be sent over plaintext HTTP.
env = {"AZURE_OPENAI_ENDPOINT": "http://foo.openai.azure.com"}
with mock.patch.dict(os.environ, env, clear=True):
be = AzureOpenAIBackend(deployment="some-model")
self.assertFalse(be._is_azure_host())
with self.assertRaises(ValueError) as ctx:
be._get_client()
self.assertIn("openai_compatible", str(ctx.exception))

def test_azure_host_detection(self):
with mock.patch.dict(os.environ, {}, clear=True):
be = AzureOpenAIBackend(deployment="gpt-5.5") # table endpoint
Expand Down Expand Up @@ -189,6 +200,15 @@ def test_azure_mode_sends_max_completion_tokens(self):
self.assertEqual(call["max_completion_tokens"], 16384)
self.assertNotIn("max_tokens", call)

def test_azure_mode_ignores_compat_extra_body(self):
body = {"thinking": {"type": "enabled"}}
env = {"SKILLOPT_SLEEP_CHAT_EXTRA_BODY": json.dumps(body)}
be = _backend_with(["hi"], env)
with mock.patch.dict(os.environ, env, clear=True):
be._call("p", retries=1)
(call,) = be._client.chat.completions.calls
self.assertNotIn("extra_body", call)


class TestErrorState(unittest.TestCase):
def test_recovered_retry_clears_last_call_error(self):
Expand Down