diff --git a/scripts/run_integration.py b/scripts/run_integration.py index 84d6d9c2d..a1e3844c9 100644 --- a/scripts/run_integration.py +++ b/scripts/run_integration.py @@ -143,6 +143,26 @@ def arguments(): default="gpt-5-nano", help="Model allowed by the OpenAI MPS selected in the configure CUJ.", ) + parser.add_argument( + "--codex-azure-provider", + default="main.ucode.ci_azure_openai_mps", + help="Existing Azure OpenAI MPS (reasoning-capable model) for the Codex azure_openai CUJ.", + ) + parser.add_argument( + "--codex-azure-provider-model", + default="gpt-5-nano", + help="Reasoning-capable model allowed by the Azure OpenAI MPS.", + ) + parser.add_argument( + "--codex-foundry-provider", + default="main.ucode.ci_foundry_mps", + help="Existing Microsoft Foundry MPS (reasoning-capable model) for the Codex foundry CUJ.", + ) + parser.add_argument( + "--codex-foundry-provider-model", + default="gpt-5-nano", + help="Reasoning-capable model allowed by the Microsoft Foundry MPS.", + ) parser.add_argument( "--parent-schema", default="main.ucode", @@ -355,6 +375,10 @@ def run(command, *, cwd=output, env=base_env, timeout=600) -> str: "claude_bedrock_allow_all_model": args.claude_bedrock_allow_all_model, "codex_provider": args.codex_provider, "codex_provider_model": args.codex_provider_model, + "codex_azure_provider": args.codex_azure_provider, + "codex_azure_provider_model": args.codex_azure_provider_model, + "codex_foundry_provider": args.codex_foundry_provider, + "codex_foundry_provider_model": args.codex_foundry_provider_model, "parent_schema": args.parent_schema, "claude_parent_model": args.claude_parent_model, "codex_parent_model": args.codex_parent_model, @@ -611,6 +635,10 @@ def run(command, *, cwd=output, env=base_env, timeout=600) -> str: "UG_INTEGRATION_CLAUDE_BEDROCK_ALLOW_ALL_MODEL": args.claude_bedrock_allow_all_model, "UG_INTEGRATION_CODEX_PROVIDER": args.codex_provider, "UG_INTEGRATION_CODEX_PROVIDER_MODEL": args.codex_provider_model, + "UG_INTEGRATION_CODEX_AZURE_PROVIDER": args.codex_azure_provider, + "UG_INTEGRATION_CODEX_AZURE_PROVIDER_MODEL": args.codex_azure_provider_model, + "UG_INTEGRATION_CODEX_FOUNDRY_PROVIDER": args.codex_foundry_provider, + "UG_INTEGRATION_CODEX_FOUNDRY_PROVIDER_MODEL": args.codex_foundry_provider_model, "UG_INTEGRATION_PARENT_SCHEMA": args.parent_schema, "UG_INTEGRATION_CLAUDE_PARENT_MODEL": args.claude_parent_model, "UG_INTEGRATION_CODEX_PARENT_MODEL": args.codex_parent_model, diff --git a/tests/integration/conftest.py b/tests/integration/conftest.py index 3752c87d9..3434621cc 100644 --- a/tests/integration/conftest.py +++ b/tests/integration/conftest.py @@ -177,6 +177,26 @@ def codex_provider_model(): return os.environ["UG_INTEGRATION_CODEX_PROVIDER_MODEL"] +@pytest.fixture(scope="session") +def codex_azure_provider(): + return os.environ["UG_INTEGRATION_CODEX_AZURE_PROVIDER"] + + +@pytest.fixture(scope="session") +def codex_azure_provider_model(): + return os.environ["UG_INTEGRATION_CODEX_AZURE_PROVIDER_MODEL"] + + +@pytest.fixture(scope="session") +def codex_foundry_provider(): + return os.environ["UG_INTEGRATION_CODEX_FOUNDRY_PROVIDER"] + + +@pytest.fixture(scope="session") +def codex_foundry_provider_model(): + return os.environ["UG_INTEGRATION_CODEX_FOUNDRY_PROVIDER_MODEL"] + + @pytest.fixture(scope="session") def parent_schema(): return os.environ["UG_INTEGRATION_PARENT_SCHEMA"] diff --git a/tests/integration/test_ug_configure_codex.py b/tests/integration/test_ug_configure_codex.py index bc5d68222..871edb60f 100644 --- a/tests/integration/test_ug_configure_codex.py +++ b/tests/integration/test_ug_configure_codex.py @@ -97,3 +97,67 @@ def test_ug_configure_codex_openai_mps( tui.exit_normally() task.assert_completed(session, "codex") session.assert_not_routed() + + +def _run_codex_openai_dialect_mps_cuj(session, workspace, provider, model, label): + """Configure Codex through an OpenAI-dialect MPS, then complete a task on one of its models. + + Azure OpenAI and Microsoft Foundry both speak the OpenAI dialect the gateway fronts for Codex, + so the CUJ is identical apart from which MPS the picker selects. Codex always sends + `reasoning.effort`, so a completed task also proves two things a plain routing check would miss: + the provider-type allowlist accepts the MPS's type for Codex, and the backing deployment is + reasoning-capable (a non-reasoning model 400s the request's `reasoning.effort`). + """ + task = FileTask(session) + + command = [ + str(session.binary), + "configure", + "--workspace", + workspace, + "--skip-upgrade", + "--disable-databricks-ai-tools", + ] + with ConfigureTerminal(session, "codex", command, f"configure-{label}-provider") as configure: + configure.select_agent("Codex") + configure.choose("How should Codex get its models?", "External Models") + configure.choose("Select a model provider service:", provider) + configure.finish(timeout=240) + assert session.workspace_state()["provider_services"]["codex"] == provider + assert provider in session.run("status").stdout + + command = [str(session.binary), "codex", "--", "--model", model] + with AgentTerminal(session, "codex", command, f"{label}-provider-session") as tui: + tui.boot() + tui.submit(task.prompt) + tui.wait_for_task(task, timeout=300) + tui.exit_normally() + task.assert_completed(session, "codex") + session.assert_not_routed() + + +def test_ug_configure_codex_azure_openai_mps( + live_session, workspace, codex_azure_provider, codex_azure_provider_model +): + """Scenario: select an Azure OpenAI (`azure_openai`) MPS in ug configure's picker for Codex. + + Expected: ug saves the provider and a real Codex session completes a file task on one of its + reasoning-capable models (see _run_codex_openai_dialect_mps_cuj). + """ + _run_codex_openai_dialect_mps_cuj( + live_session, workspace, codex_azure_provider, codex_azure_provider_model, "azure" + ) + + +def test_ug_configure_codex_foundry_mps( + live_session, workspace, codex_foundry_provider, codex_foundry_provider_model +): + """Scenario: select a Microsoft Foundry (`microsoft_foundry`) MPS in ug configure's picker. + + Expected: ug saves the provider and a real Codex session completes a file task, exercising the + `microsoft_foundry` allowlist entry for Codex. Foundry speaks the same OpenAI dialect as Azure + OpenAI, so this reuses the Azure CUJ (see _run_codex_openai_dialect_mps_cuj). + """ + _run_codex_openai_dialect_mps_cuj( + live_session, workspace, codex_foundry_provider, codex_foundry_provider_model, "foundry" + )