diff --git a/.github/workflows/e2e-orchestrator.yml b/.github/workflows/e2e-orchestrator.yml index e84be697..de943134 100644 --- a/.github/workflows/e2e-orchestrator.yml +++ b/.github/workflows/e2e-orchestrator.yml @@ -78,15 +78,34 @@ jobs: # =========================================================================== # Summary - Posts test results to PR + # This is the ONLY check you need to mark as "Required" in branch protection # =========================================================================== - summary: - name: Test Summary + e2e-status: + name: E2E Status runs-on: ubuntu-latest needs: [python-openai, nodejs-openai, nodejs-langchain, dotnet-sk, dotnet-af] if: always() steps: + - name: Check E2E Status + run: | + echo "Checking E2E test results..." + + # Get all job results (skipped jobs are fine, failed jobs are not) + results="${{ needs.python-openai.result }} ${{ needs.nodejs-openai.result }} ${{ needs.nodejs-langchain.result }} ${{ needs.dotnet-sk.result }} ${{ needs.dotnet-af.result }}" + + echo "Job results: $results" + + # Check if any job failed or was cancelled + if echo "$results" | grep -qE "failure|cancelled"; then + echo "āŒ One or more E2E tests failed or were cancelled" + exit 1 + fi + + echo "āœ… All E2E tests passed (or were skipped)" + - name: Generate Summary + if: always() id: summary run: | # Determine overall status diff --git a/python/openai/sample-agent/.env.template b/python/openai/sample-agent/.env.template index b51b45e5..c095efb8 100644 --- a/python/openai/sample-agent/.env.template +++ b/python/openai/sample-agent/.env.template @@ -25,11 +25,6 @@ AGENT_ID= CONNECTIONS__SERVICE_CONNECTION__SETTINGS__CLIENTID= CONNECTIONS__SERVICE_CONNECTION__SETTINGS__CLIENTSECRET= CONNECTIONS__SERVICE_CONNECTION__SETTINGS__TENANTID= -CONNECTIONS__SERVICE_CONNECTION__SETTINGS__ALT_BLUEPRINT_NAME=AGENTBLUEPRINT - -CONNECTIONS__AGENTBLUEPRINT__SETTINGS__CLIENTID= -CONNECTIONS__AGENTBLUEPRINT__SETTINGS__CLIENTSECRET= -CONNECTIONS__AGENTBLUEPRINT__SETTINGS__TENANTID= AGENTAPPLICATION__USERAUTHORIZATION__HANDLERS__AGENTIC__SETTINGS__TYPE=AgenticUserAuthorization AGENTAPPLICATION__USERAUTHORIZATION__HANDLERS__AGENTIC__SETTINGS__ALT_BLUEPRINT_NAME=AGENTBLUEPRINT diff --git a/python/openai/sample-agent/agent.py b/python/openai/sample-agent/agent.py index 9cf2ec5e..6429020a 100644 --- a/python/openai/sample-agent/agent.py +++ b/python/openai/sample-agent/agent.py @@ -24,7 +24,7 @@ from token_cache import get_cached_agentic_token # Load environment variables -load_dotenv() +load_dotenv(override=True) # Configure logging logging.basicConfig(level=logging.INFO) @@ -239,17 +239,33 @@ async def setup_mcp_servers(self, auth: Authorization, auth_handler_name: str, c """Set up MCP server connections based on authentication configuration. Authentication priority: - 1. Bearer token from config (BEARER_TOKEN) - for local development/testing - 2. Auth handler (auth_handler_name) - for production agentic auth + 1. Agentic auth (USE_AGENTIC_AUTH=true) - for production/Teams authentication + 2. Bearer token from config (BEARER_TOKEN) - for local development/testing 3. No auth - gracefully skip MCP and run in bare LLM mode If MCP connection fails for any reason, the agent will gracefully fall back to bare LLM mode without MCP tools. """ try: - # Priority 1: Bearer token provided in config (for local dev/testing) - if self.auth_options.bearer_token: - logger.info("šŸ”‘ Using bearer token from config for MCP servers") + # Check if agentic auth is enabled + use_agentic_auth = os.getenv("USE_AGENTIC_AUTH", "false").lower() == "true" + + # Priority 1: Agentic auth enabled (production/Teams authentication) + # When USE_AGENTIC_AUTH=true, always use agentic auth - never fall back to bearer token + if use_agentic_auth: + if auth_handler_name: + logger.info(f"šŸ”’ Using agentic auth handler '{auth_handler_name}' for MCP servers (USE_AGENTIC_AUTH=true)") + else: + logger.info("šŸ”’ Using agentic auth for MCP servers (USE_AGENTIC_AUTH=true, no explicit handler)") + self.agent = await self.tool_service.add_tool_servers_to_agent( + agent=self.agent, + auth=auth, + auth_handler_name=auth_handler_name, + context=context, + ) + # Priority 2: Bearer token provided in config (for local dev/testing when agentic auth is disabled) + elif self.auth_options.bearer_token: + logger.info("šŸ”‘ Using bearer token from config for MCP servers (USE_AGENTIC_AUTH=false)") self.agent = await self.tool_service.add_tool_servers_to_agent( agent=self.agent, auth=auth, @@ -257,7 +273,7 @@ async def setup_mcp_servers(self, auth: Authorization, auth_handler_name: str, c context=context, auth_token=self.auth_options.bearer_token, ) - # Priority 2: Auth handler configured (production agentic auth) + # Priority 3: Auth handler configured without USE_AGENTIC_AUTH flag elif auth_handler_name: logger.info(f"šŸ”’ Using auth handler '{auth_handler_name}' for MCP servers") self.agent = await self.tool_service.add_tool_servers_to_agent( @@ -266,10 +282,10 @@ async def setup_mcp_servers(self, auth: Authorization, auth_handler_name: str, c auth_handler_name=auth_handler_name, context=context, ) - # Priority 3: No auth configured - skip MCP and run bare LLM + # Priority 4: No auth configured - skip MCP and run bare LLM else: logger.warning("āš ļø No authentication configured - running in bare LLM mode without MCP tools") - logger.info("šŸ’” To enable MCP: provide BEARER_TOKEN or configure AUTH_HANDLER_NAME") + logger.info("šŸ’” To enable MCP: set USE_AGENTIC_AUTH=true, provide BEARER_TOKEN, or configure AUTH_HANDLER_NAME") # Agent already initialized without MCP tools except Exception as e: diff --git a/python/openai/sample-agent/host_agent_server.py b/python/openai/sample-agent/host_agent_server.py index 4df50db0..d5125822 100644 --- a/python/openai/sample-agent/host_agent_server.py +++ b/python/openai/sample-agent/host_agent_server.py @@ -48,7 +48,7 @@ logger = logging.getLogger(__name__) # Load configuration -load_dotenv() +load_dotenv(override=True) agents_sdk_config = load_configuration_from_env(environ) @@ -196,6 +196,7 @@ async def initialize_agent(self): def create_auth_configuration(self) -> AgentAuthConfiguration | None: """Create authentication configuration based on available environment variables.""" + # Check for direct CLIENT_ID/TENANT_ID/CLIENT_SECRET env vars first client_id = environ.get("CLIENT_ID") tenant_id = environ.get("TENANT_ID") client_secret = environ.get("CLIENT_SECRET") @@ -250,7 +251,7 @@ async def health(_req: Request) -> Response: if auth_configuration: middlewares.append(jwt_authorization_middleware) - # Anonymous claims middleware + # Anonymous claims middleware - provides claims for unauthenticated requests @web_middleware async def anonymous_claims(request, handler): if not auth_configuration: @@ -298,6 +299,10 @@ async def anonymous_claims(request, handler): ) port = desired_port + 1 + # For Azure App Service, bind to 0.0.0.0 to accept external connections + host = environ.get("WEBSITE_HOSTNAME", None) + bind_host = "0.0.0.0" if host else "localhost" + print("=" * 80) print(f"šŸ¢ Generic Agent Host - {self.agent_class.__name__}") print("=" * 80) @@ -306,13 +311,13 @@ async def anonymous_claims(request, handler): print("šŸŽÆ Compatible with Agents Playground") if port != desired_port: print(f"āš ļø Requested port {desired_port} busy; using fallback {port}") - print(f"\nšŸš€ Starting server on localhost:{port}") - print(f"šŸ“š Bot Framework endpoint: http://localhost:{port}/api/messages") - print(f"ā¤ļø Health: http://localhost:{port}/api/health") + print(f"\nšŸš€ Starting server on {bind_host}:{port}") + print(f"šŸ“š Bot Framework endpoint: http://{bind_host}:{port}/api/messages") + print(f"ā¤ļø Health: http://{bind_host}:{port}/api/health") print("šŸŽÆ Ready for testing!\n") try: - run_app(app, host="localhost", port=port) + run_app(app, host=bind_host, port=port) except KeyboardInterrupt: print("\nšŸ‘‹ Server stopped") except Exception as error: