From 820f026e17ee53a59e50d77e6f04b71a76629eee Mon Sep 17 00:00:00 2001 From: devmrko Date: Wed, 22 Jul 2026 20:07:47 +0900 Subject: [PATCH] refs #703: configure OCI GenAI root compartment --- .../ai_web_agent_console/oci_genai_sdk.py | 7 ++- .../tests/test_oci_genai_settings.py | 51 +++++++++++++++++++ 2 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 ai-web-agent-console/tests/test_oci_genai_settings.py diff --git a/ai-web-agent-console/ai_web_agent_console/oci_genai_sdk.py b/ai-web-agent-console/ai_web_agent_console/oci_genai_sdk.py index 347b250..9ba4e52 100644 --- a/ai-web-agent-console/ai_web_agent_console/oci_genai_sdk.py +++ b/ai-web-agent-console/ai_web_agent_console/oci_genai_sdk.py @@ -26,7 +26,10 @@ ALLOWED_OCI_SETTINGS = frozenset( "OCI_PROFILE", } ) -_COMPARTMENT_ID = re.compile(r"^ocid1\.compartment\.[A-Za-z0-9._-]+$") +# OCI permits the tenancy OCID when the root compartment is selected. +_COMPARTMENT_OR_ROOT_ID = re.compile( + r"^ocid1\.(?:compartment|tenancy)\.[A-Za-z0-9._-]+$" +) class CompletionClient(Protocol): @@ -95,7 +98,7 @@ def load_oci_settings() -> OCISettings: raise ValueError("unsupported OCI authentication mode") compartment_id = values.get("OCI_GENAI_COMPARTMENT_ID", "").strip() - if not _COMPARTMENT_ID.fullmatch(compartment_id): + if not _COMPARTMENT_OR_ROOT_ID.fullmatch(compartment_id): raise ValueError("OCI Generative AI compartment is not configured") return OCISettings( auth_type=auth_type, diff --git a/ai-web-agent-console/tests/test_oci_genai_settings.py b/ai-web-agent-console/tests/test_oci_genai_settings.py new file mode 100644 index 0000000..2e372ec --- /dev/null +++ b/ai-web-agent-console/tests/test_oci_genai_settings.py @@ -0,0 +1,51 @@ +"""OCI GenAI configuration validation tests.""" + +from __future__ import annotations + +import os +import unittest + +from src.oci_genai_sdk import ALLOWED_OCI_SETTINGS, load_oci_settings + + +class OCISettingsTest(unittest.TestCase): + def setUp(self) -> None: + self._previous = {key: os.environ.get(key) for key in ALLOWED_OCI_SETTINGS} + os.environ.update( + { + "OCI_AUTH_TYPE": "config_file", + "OCI_CONFIG_FILE": "/home/opc/.oci/config", + "OCI_PROFILE": "DEFAULT", + } + ) + + def tearDown(self) -> None: + for key, value in self._previous.items(): + if value is None: + os.environ.pop(key, None) + else: + os.environ[key] = value + + def test_accepts_a_child_compartment_ocid(self) -> None: + os.environ["OCI_GENAI_COMPARTMENT_ID"] = "ocid1.compartment.oc1..example" + + settings = load_oci_settings() + + self.assertEqual("ocid1.compartment.oc1..example", settings.compartment_id) + + def test_accepts_a_tenancy_ocid_for_the_root_compartment(self) -> None: + os.environ["OCI_GENAI_COMPARTMENT_ID"] = "ocid1.tenancy.oc1..example" + + settings = load_oci_settings() + + self.assertEqual("ocid1.tenancy.oc1..example", settings.compartment_id) + + def test_rejects_an_invalid_compartment_identifier(self) -> None: + os.environ["OCI_GENAI_COMPARTMENT_ID"] = "not-an-ocid" + + with self.assertRaisesRegex(ValueError, "compartment is not configured"): + load_oci_settings() + + +if __name__ == "__main__": + unittest.main()