Merge pull request #138 from Yif-Yang/fix/sleep-openai-compat-safety
fix(sleep): harden OpenAI compatibility boundaries
This commit is contained in:
@@ -1311,8 +1311,12 @@ class AzureOpenAIBackend(CliBackend):
|
|||||||
|
|
||||||
def _is_azure_host(self) -> bool:
|
def _is_azure_host(self) -> bool:
|
||||||
from urllib.parse import urlparse
|
from urllib.parse import urlparse
|
||||||
host = (urlparse(self.endpoint).hostname or "").lower()
|
parsed = urlparse(self.endpoint)
|
||||||
return host.endswith(self._AZURE_HOST_SUFFIXES)
|
host = (parsed.hostname or "").lower()
|
||||||
|
return (
|
||||||
|
parsed.scheme.lower() == "https"
|
||||||
|
and host.endswith(self._AZURE_HOST_SUFFIXES)
|
||||||
|
)
|
||||||
|
|
||||||
def _get_client(self):
|
def _get_client(self):
|
||||||
if self._client is None:
|
if self._client is None:
|
||||||
@@ -1383,7 +1387,7 @@ class AzureOpenAIBackend(CliBackend):
|
|||||||
kwargs["max_tokens"] = self.compat_max_tokens
|
kwargs["max_tokens"] = self.compat_max_tokens
|
||||||
else:
|
else:
|
||||||
kwargs["max_completion_tokens"] = 16384
|
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
|
kwargs["extra_body"] = self.chat_extra_body
|
||||||
resp = client.chat.completions.create(**kwargs)
|
resp = client.chat.completions.create(**kwargs)
|
||||||
text = (resp.choices[0].message.content or "").strip()
|
text = (resp.choices[0].message.content or "").strip()
|
||||||
|
|||||||
@@ -108,6 +108,17 @@ class TestClientSelection(unittest.TestCase):
|
|||||||
be._get_client()
|
be._get_client()
|
||||||
self.assertIn("openai_compatible", str(ctx.exception))
|
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):
|
def test_azure_host_detection(self):
|
||||||
with mock.patch.dict(os.environ, {}, clear=True):
|
with mock.patch.dict(os.environ, {}, clear=True):
|
||||||
be = AzureOpenAIBackend(deployment="gpt-5.5") # table endpoint
|
be = AzureOpenAIBackend(deployment="gpt-5.5") # table endpoint
|
||||||
@@ -189,6 +200,15 @@ class TestRequestKwargs(unittest.TestCase):
|
|||||||
self.assertEqual(call["max_completion_tokens"], 16384)
|
self.assertEqual(call["max_completion_tokens"], 16384)
|
||||||
self.assertNotIn("max_tokens", call)
|
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):
|
class TestErrorState(unittest.TestCase):
|
||||||
def test_recovered_retry_clears_last_call_error(self):
|
def test_recovered_retry_clears_last_call_error(self):
|
||||||
|
|||||||
Reference in New Issue
Block a user