mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-26 01:12:21 +00:00
ci(oci): fix CI failures — black formatting + recursive_detector ignore
- Run black on litellm/llms/oci/common_utils.py + 3 OCI test files that drifted out of black-compliance during the rebase. - Add the three bounded recursive functions in oci/common_utils.py (`_resolve`, `resolve_oci_schema_anyof`, `sanitize_oci_schema`) to the recursive_detector IGNORE_FUNCTIONS list. All three are bounded: `_resolve` uses a `resolving_stack` cycle guard; the other two are bounded by JSON-schema tree depth (no cycles in well-formed input), matching the pattern of the existing OCI/Vertex schema walkers already on the list.
This commit is contained in:
parent
c04cf12cf2
commit
9f3f10ca1d
5 changed files with 46 additions and 31 deletions
|
|
@ -96,7 +96,9 @@ def sha256_base64(data: bytes) -> str:
|
|||
# OCI HTTP signing specification (RSA-SHA256 request signing), not for password
|
||||
# or secret hashing. This is the correct and mandated algorithm for this purpose.
|
||||
# See: https://docs.oracle.com/en-us/iaas/Content/API/Concepts/signingrequests.htm
|
||||
digest = hashlib.sha256(data).digest() # lgtm[py/weak-sensitive-data-hashing] # noqa: S324
|
||||
digest = hashlib.sha256(
|
||||
data
|
||||
).digest() # lgtm[py/weak-sensitive-data-hashing] # noqa: S324
|
||||
return base64.b64encode(digest).decode()
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -47,6 +47,9 @@ IGNORE_FUNCTIONS = [
|
|||
"_read_image_bytes", # max depth set.
|
||||
"_get_masked_values", # max depth set (default 20) to prevent infinite recursion while masking nested sensitive config dicts.
|
||||
"_redact_sensitive_litellm_params", # max depth set (default 10).
|
||||
"_resolve", # OCI: $ref resolver bounded by `resolving_stack` cycle guard.
|
||||
"resolve_oci_schema_anyof", # OCI: bounded by JSON-schema tree depth (no cycles possible in well-formed input).
|
||||
"sanitize_oci_schema", # OCI: bounded by JSON-schema tree depth.
|
||||
]
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -766,14 +766,24 @@ class TestCohereStreamChunkEdgeCases:
|
|||
|
||||
def test_stream_chunk_max_tokens_finish_reason(self):
|
||||
wrapper = self._wrapper()
|
||||
chunk = {"apiFormat": "COHERE", "text": "truncated", "index": 0, "finishReason": "MAX_TOKENS"}
|
||||
chunk = {
|
||||
"apiFormat": "COHERE",
|
||||
"text": "truncated",
|
||||
"index": 0,
|
||||
"finishReason": "MAX_TOKENS",
|
||||
}
|
||||
result = wrapper.chunk_creator(f"data: {json.dumps(chunk)}")
|
||||
assert result.choices[0].finish_reason == "length"
|
||||
|
||||
def test_stream_chunk_unknown_finish_reason_does_not_raise(self):
|
||||
from litellm.llms.oci.chat.cohere import handle_cohere_stream_chunk
|
||||
|
||||
chunk = {"apiFormat": "COHERE", "text": "", "index": 0, "finishReason": "FUTURE_REASON"}
|
||||
chunk = {
|
||||
"apiFormat": "COHERE",
|
||||
"text": "",
|
||||
"index": 0,
|
||||
"finishReason": "FUTURE_REASON",
|
||||
}
|
||||
# Should not raise — unknown reasons fall through the elif chain unchanged
|
||||
result = handle_cohere_stream_chunk(chunk)
|
||||
assert result.choices[0] is not None
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@ from litellm.llms.oci.common_utils import (
|
|||
validate_oci_environment,
|
||||
)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# OCI_API_VERSION
|
||||
# ---------------------------------------------------------------------------
|
||||
|
|
@ -318,11 +317,7 @@ def test_resolve_schema_anyof_no_anyof_unchanged():
|
|||
|
||||
|
||||
def test_resolve_schema_anyof_nested():
|
||||
schema = {
|
||||
"properties": {
|
||||
"age": {"anyOf": [{"type": "integer"}, {"type": "null"}]}
|
||||
}
|
||||
}
|
||||
schema = {"properties": {"age": {"anyOf": [{"type": "integer"}, {"type": "null"}]}}}
|
||||
result = resolve_oci_schema_anyof(schema)
|
||||
assert result["properties"]["age"]["type"] == "integer"
|
||||
|
||||
|
|
|
|||
|
|
@ -39,7 +39,6 @@ from litellm.llms.oci.common_utils import (
|
|||
)
|
||||
from litellm.types.llms.oci import OCIVendors, OCIToolCall
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Helpers / fixtures
|
||||
# ---------------------------------------------------------------------------
|
||||
|
|
@ -67,7 +66,9 @@ _GENERIC_MODEL = "meta.llama-3-70b-instruct"
|
|||
@patch("litellm.llms.oci.common_utils.load_private_key_from_str")
|
||||
@patch("litellm.llms.oci.common_utils.padding")
|
||||
@patch("litellm.llms.oci.common_utils.hashes")
|
||||
def test_sign_with_manual_credentials_inline_key(mock_hashes, mock_padding, mock_load_key):
|
||||
def test_sign_with_manual_credentials_inline_key(
|
||||
mock_hashes, mock_padding, mock_load_key
|
||||
):
|
||||
"""sign_with_manual_credentials succeeds with an inline oci_key string."""
|
||||
mock_key = MagicMock()
|
||||
mock_key.sign.return_value = b"fake_signature"
|
||||
|
|
@ -88,7 +89,9 @@ def test_sign_with_manual_credentials_inline_key(mock_hashes, mock_padding, mock
|
|||
@patch("litellm.llms.oci.common_utils.load_private_key_from_file")
|
||||
@patch("litellm.llms.oci.common_utils.padding")
|
||||
@patch("litellm.llms.oci.common_utils.hashes")
|
||||
def test_sign_with_manual_credentials_key_file(mock_hashes, mock_padding, mock_load_file):
|
||||
def test_sign_with_manual_credentials_key_file(
|
||||
mock_hashes, mock_padding, mock_load_file
|
||||
):
|
||||
"""sign_with_manual_credentials falls back to oci_key_file when oci_key absent."""
|
||||
mock_key = MagicMock()
|
||||
mock_key.sign.return_value = b"sig_from_file"
|
||||
|
|
@ -117,9 +120,7 @@ def test_sign_with_manual_credentials_authorization_contains_key_id(
|
|||
mock_key.sign.return_value = b"sig"
|
||||
mock_load_key.return_value = mock_key
|
||||
|
||||
result_headers, _ = sign_with_manual_credentials(
|
||||
{}, _MANUAL_CREDS, {}, _API_BASE
|
||||
)
|
||||
result_headers, _ = sign_with_manual_credentials({}, _MANUAL_CREDS, {}, _API_BASE)
|
||||
|
||||
auth = result_headers["authorization"]
|
||||
assert 'keyId="ocid1.tenancy.oc1..xxx/ocid1.user.oc1..xxx/aa:bb:cc:dd"' in auth
|
||||
|
|
@ -143,9 +144,7 @@ def test_sign_oci_request_routes_to_signer_when_present():
|
|||
"""sign_oci_request delegates to sign_with_oci_signer when oci_signer is set."""
|
||||
signer = MagicMock()
|
||||
signer.do_request_sign.return_value = None
|
||||
headers, body = sign_oci_request(
|
||||
{}, {"oci_signer": signer}, {"data": 1}, _API_BASE
|
||||
)
|
||||
headers, body = sign_oci_request({}, {"oci_signer": signer}, {"data": 1}, _API_BASE)
|
||||
signer.do_request_sign.assert_called_once()
|
||||
assert isinstance(body, bytes)
|
||||
|
||||
|
|
@ -229,7 +228,10 @@ def test_adapt_generic_assistant_tool_call_message():
|
|||
{
|
||||
"id": "call_xyz",
|
||||
"type": "function",
|
||||
"function": {"name": "get_weather", "arguments": '{"city": "Rome"}'},
|
||||
"function": {
|
||||
"name": "get_weather",
|
||||
"arguments": '{"city": "Rome"}',
|
||||
},
|
||||
}
|
||||
],
|
||||
}
|
||||
|
|
@ -249,7 +251,10 @@ def test_adapt_generic_multipart_content():
|
|||
"role": "user",
|
||||
"content": [
|
||||
{"type": "text", "text": "Look at this:"},
|
||||
{"type": "image_url", "image_url": {"url": "https://example.com/img.png"}},
|
||||
{
|
||||
"type": "image_url",
|
||||
"image_url": {"url": "https://example.com/img.png"},
|
||||
},
|
||||
],
|
||||
}
|
||||
]
|
||||
|
|
@ -712,17 +717,13 @@ class TestOCIChatConfigGetOptionalParams:
|
|||
|
||||
def test_cohere_maps_stop_to_stop_sequences(self):
|
||||
config = self._config()
|
||||
result = config._get_optional_params(
|
||||
OCIVendors.COHERE, {"stop": ["END"]}
|
||||
)
|
||||
result = config._get_optional_params(OCIVendors.COHERE, {"stop": ["END"]})
|
||||
assert "stopSequences" in result
|
||||
assert result["stopSequences"] == ["END"]
|
||||
|
||||
def test_generic_maps_max_tokens(self):
|
||||
config = self._config()
|
||||
result = config._get_optional_params(
|
||||
OCIVendors.GENERIC, {"max_tokens": 512}
|
||||
)
|
||||
result = config._get_optional_params(OCIVendors.GENERIC, {"max_tokens": 512})
|
||||
assert result["maxTokens"] == 512
|
||||
|
||||
def test_tool_choice_string_auto_converted_to_dict(self):
|
||||
|
|
@ -870,16 +871,20 @@ class TestOCIStreamWrapperChunkCreator:
|
|||
|
||||
def test_cohere_chunk_dispatched_correctly(self):
|
||||
wrapper = self._make_wrapper(_COHERE_MODEL)
|
||||
payload = json.dumps({"apiFormat": "COHERE", "text": "hi", "finishReason": None})
|
||||
payload = json.dumps(
|
||||
{"apiFormat": "COHERE", "text": "hi", "finishReason": None}
|
||||
)
|
||||
result = wrapper.chunk_creator(f"data:{payload}")
|
||||
assert result.choices[0].delta.content == "hi"
|
||||
|
||||
def test_generic_chunk_dispatched_correctly(self):
|
||||
wrapper = self._make_wrapper(_GENERIC_MODEL)
|
||||
payload = json.dumps({
|
||||
"finishReason": "COMPLETE",
|
||||
"index": 0,
|
||||
})
|
||||
payload = json.dumps(
|
||||
{
|
||||
"finishReason": "COMPLETE",
|
||||
"index": 0,
|
||||
}
|
||||
)
|
||||
result = wrapper.chunk_creator(f"data:{payload}")
|
||||
assert result.choices[0].finish_reason == "stop"
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue