diff --git a/litellm/llms/oci/common_utils.py b/litellm/llms/oci/common_utils.py index e617f70c8af..5c450e8334c 100644 --- a/litellm/llms/oci/common_utils.py +++ b/litellm/llms/oci/common_utils.py @@ -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() diff --git a/tests/code_coverage_tests/recursive_detector.py b/tests/code_coverage_tests/recursive_detector.py index 07af2735dfe..254d700ee5a 100644 --- a/tests/code_coverage_tests/recursive_detector.py +++ b/tests/code_coverage_tests/recursive_detector.py @@ -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. ] diff --git a/tests/test_litellm/llms/oci/chat/test_oci_cohere_tool_calls.py b/tests/test_litellm/llms/oci/chat/test_oci_cohere_tool_calls.py index 7ef69a4a2e5..4414d1ddd10 100644 --- a/tests/test_litellm/llms/oci/chat/test_oci_cohere_tool_calls.py +++ b/tests/test_litellm/llms/oci/chat/test_oci_cohere_tool_calls.py @@ -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 diff --git a/tests/test_litellm/llms/oci/test_oci_common_utils.py b/tests/test_litellm/llms/oci/test_oci_common_utils.py index 5d4e2602f5a..bb3171d8fbc 100644 --- a/tests/test_litellm/llms/oci/test_oci_common_utils.py +++ b/tests/test_litellm/llms/oci/test_oci_common_utils.py @@ -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" diff --git a/tests/test_litellm/llms/oci/test_oci_coverage_boost.py b/tests/test_litellm/llms/oci/test_oci_coverage_boost.py index c75d9baab1c..89879426e7e 100644 --- a/tests/test_litellm/llms/oci/test_oci_coverage_boost.py +++ b/tests/test_litellm/llms/oci/test_oci_coverage_boost.py @@ -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"