|
| 1 | +# Unit 15: Infrastructure Troubleshooting and Resolution - Subunit: Lambda Refactor Using Official Strands Pattern |
| 2 | + |
| 3 | +## Objective |
| 4 | + |
| 5 | +Refactor the CodeRipple Lambda function to follow the official AWS Strands deployment pattern, eliminating OpenTelemetry compatibility issues by simplifying the architecture and removing complex layer validation and initialization procedures that cause runtime failures. |
| 6 | + |
| 7 | +## Implementation |
| 8 | + |
| 9 | +### Problem Analysis |
| 10 | + |
| 11 | +**Current Architecture Issues:** |
| 12 | +- Complex layer validation causing OpenTelemetry `StopIteration` exceptions |
| 13 | +- Over-engineered initialization with conversation managers and multi-step validation |
| 14 | +- Divergence from official Strands Lambda deployment patterns |
| 15 | +- Unnecessary complexity for Lambda environment constraints |
| 16 | + |
| 17 | +**Official Strands Pattern Analysis:** |
| 18 | +Based on `strands/deploy_to_lambda/lambda/agent_handler.py`, the official approach is: |
| 19 | +```python |
| 20 | +from strands import Agent |
| 21 | +from strands_tools import http_request |
| 22 | + |
| 23 | +def handler(event: Dict[str, Any], _context) -> str: |
| 24 | + weather_agent = Agent( |
| 25 | + system_prompt=WEATHER_SYSTEM_PROMPT, |
| 26 | + tools=[http_request], |
| 27 | + ) |
| 28 | + response = weather_agent(event.get('prompt')) |
| 29 | + return str(response) |
| 30 | +``` |
| 31 | + |
| 32 | +**Key Characteristics:** |
| 33 | +- **Minimal imports**: Only `strands.Agent` and tools |
| 34 | +- **Direct instantiation**: No complex initialization or validation |
| 35 | +- **Simple dependencies**: `strands-agents` and `strands-agents-tools` only |
| 36 | +- **Streamlined execution**: Direct agent call with response return |
| 37 | + |
| 38 | +### Technical Approach |
| 39 | + |
| 40 | +Refactor the Lambda function to match the official Strands pattern: |
| 41 | + |
| 42 | +1. **Eliminate layer validation** - Remove `validate_layer_imports()` entirely |
| 43 | +2. **Simplify agent creation** - Direct `Agent()` instantiation like official example |
| 44 | +3. **Streamline imports** - Minimal imports following Strands pattern |
| 45 | +4. **Reduce complexity** - Remove conversation managers and multi-step initialization |
| 46 | +5. **Focus on core functionality** - Process webhook through simplified agent pattern |
| 47 | + |
| 48 | +### Code Changes |
| 49 | + |
| 50 | +**New Simplified Lambda Function:** |
| 51 | + |
| 52 | +```python |
| 53 | +#!/usr/bin/env python3 |
| 54 | +""" |
| 55 | +CodeRipple Lambda Handler (Simplified Strands Pattern) |
| 56 | +
|
| 57 | +Follows official AWS Strands deployment pattern for reliability and simplicity. |
| 58 | +Based on strands/deploy_to_lambda/lambda/agent_handler.py |
| 59 | +""" |
| 60 | + |
| 61 | +import json |
| 62 | +import logging |
| 63 | +from typing import Dict, Any |
| 64 | + |
| 65 | +# Configure logging |
| 66 | +logging.basicConfig(level=logging.INFO) |
| 67 | +logger = logging.getLogger(__name__) |
| 68 | + |
| 69 | +# CodeRipple system prompt for multi-agent documentation |
| 70 | +CODERIPPLE_SYSTEM_PROMPT = """You are the CodeRipple Orchestrator Agent. |
| 71 | +
|
| 72 | +Your role is to process GitHub webhook events and coordinate documentation updates through the Three Mirror Documentation Framework: |
| 73 | +
|
| 74 | +1. **Tourist Guide Layer** (How to ENGAGE): User workflows, getting started, troubleshooting |
| 75 | +2. **Building Inspector Layer** (What it IS): Current architecture, capabilities, interfaces |
| 76 | +3. **Historian Layer** (Why it BECAME): ADRs, decisions, evolution context |
| 77 | +
|
| 78 | +For each webhook: |
| 79 | +1. Analyze the code changes using git diff analysis |
| 80 | +2. Apply Layer Selection Decision Tree: |
| 81 | + - Does this change how users interact? → Tourist Guide updates |
| 82 | + - Does this change what the system is/does? → Building Inspector updates |
| 83 | + - Does this represent a significant decision? → Historian updates |
| 84 | +3. Generate appropriate documentation updates |
| 85 | +4. Return structured results |
| 86 | +
|
| 87 | +Process webhooks systematically and ensure comprehensive documentation coverage.""" |
| 88 | + |
| 89 | +def lambda_handler(event: Dict[str, Any], context: Any) -> Dict[str, Any]: |
| 90 | + """ |
| 91 | + CodeRipple Lambda Handler (Simplified Strands Pattern) |
| 92 | + |
| 93 | + Processes GitHub webhooks using direct Strands Agent instantiation. |
| 94 | + Follows official Strands deployment pattern for maximum reliability. |
| 95 | + """ |
| 96 | + |
| 97 | + request_id = getattr(context, 'aws_request_id', 'unknown') |
| 98 | + logger.info(f"🚀 CodeRipple Lambda started (Request ID: {request_id})") |
| 99 | + |
| 100 | + try: |
| 101 | + # SIMPLIFIED PATTERN - Direct agent creation (like official Strands example) |
| 102 | + from strands import Agent |
| 103 | + from coderipple.git_analysis_tool import analyze_git_diff |
| 104 | + from coderipple.content_generation_tools import generate_documentation |
| 105 | + from coderipple.webhook_parser import parse_github_webhook |
| 106 | + |
| 107 | + logger.info("📦 Creating CodeRipple agent using Strands pattern") |
| 108 | + |
| 109 | + # Create agent directly (following official Strands pattern) |
| 110 | + coderipple_agent = Agent( |
| 111 | + system_prompt=CODERIPPLE_SYSTEM_PROMPT, |
| 112 | + tools=[ |
| 113 | + analyze_git_diff, |
| 114 | + generate_documentation, |
| 115 | + parse_github_webhook |
| 116 | + ] |
| 117 | + ) |
| 118 | + |
| 119 | + logger.info("📥 Processing webhook event") |
| 120 | + |
| 121 | + # Parse webhook payload |
| 122 | + if 'body' in event: |
| 123 | + # API Gateway event |
| 124 | + webhook_data = event['body'] |
| 125 | + if isinstance(webhook_data, str): |
| 126 | + webhook_data = json.loads(webhook_data) |
| 127 | + else: |
| 128 | + # Direct invocation |
| 129 | + webhook_data = event |
| 130 | + |
| 131 | + # Extract key information for agent processing |
| 132 | + repository = webhook_data.get('repository', {}).get('full_name', 'unknown') |
| 133 | + commits = webhook_data.get('commits', []) |
| 134 | + |
| 135 | + # Create agent prompt from webhook data |
| 136 | + agent_prompt = f""" |
| 137 | +GitHub webhook received for repository: {repository} |
| 138 | +
|
| 139 | +Webhook data: |
| 140 | +{json.dumps(webhook_data, indent=2)} |
| 141 | +
|
| 142 | +Please process this webhook and coordinate appropriate documentation updates according to the Three Mirror Documentation Framework. |
| 143 | +""" |
| 144 | + |
| 145 | + logger.info(f"📂 Repository: {repository}") |
| 146 | + logger.info(f"📝 Commits: {len(commits)}") |
| 147 | + |
| 148 | + # Process through agent (following official Strands pattern) |
| 149 | + logger.info("🤖 Processing webhook through CodeRipple agent") |
| 150 | + response = coderipple_agent(agent_prompt) |
| 151 | + |
| 152 | + logger.info("✅ Webhook processed successfully") |
| 153 | + |
| 154 | + # Return API Gateway compatible response |
| 155 | + return { |
| 156 | + 'statusCode': 200, |
| 157 | + 'headers': { |
| 158 | + 'Content-Type': 'application/json', |
| 159 | + 'Access-Control-Allow-Origin': '*', |
| 160 | + 'X-CodeRipple-Pattern': 'simplified-strands', |
| 161 | + 'X-Request-ID': request_id |
| 162 | + }, |
| 163 | + 'body': json.dumps({ |
| 164 | + 'message': 'Webhook processed successfully', |
| 165 | + 'repository': repository, |
| 166 | + 'commits_processed': len(commits), |
| 167 | + 'agent_response': str(response), |
| 168 | + 'request_id': request_id, |
| 169 | + 'pattern': 'simplified-strands' |
| 170 | + }) |
| 171 | + } |
| 172 | + |
| 173 | + except Exception as e: |
| 174 | + logger.error(f"💥 Error processing webhook: {e}") |
| 175 | + |
| 176 | + # Return error response |
| 177 | + return { |
| 178 | + 'statusCode': 500, |
| 179 | + 'headers': { |
| 180 | + 'Content-Type': 'application/json', |
| 181 | + 'Access-Control-Allow-Origin': '*', |
| 182 | + 'X-CodeRipple-Pattern': 'simplified-strands', |
| 183 | + 'X-Request-ID': request_id |
| 184 | + }, |
| 185 | + 'body': json.dumps({ |
| 186 | + 'message': 'Webhook processing failed', |
| 187 | + 'error': str(e), |
| 188 | + 'request_id': request_id, |
| 189 | + 'pattern': 'simplified-strands' |
| 190 | + }) |
| 191 | + } |
| 192 | + |
| 193 | +def health_check_handler(event: Dict[str, Any], context: Any) -> Dict[str, Any]: |
| 194 | + """Simple health check following Strands pattern""" |
| 195 | + |
| 196 | + try: |
| 197 | + # Test basic Strands import |
| 198 | + from strands import Agent |
| 199 | + |
| 200 | + return { |
| 201 | + 'statusCode': 200, |
| 202 | + 'headers': {'Content-Type': 'application/json'}, |
| 203 | + 'body': json.dumps({ |
| 204 | + 'status': 'healthy', |
| 205 | + 'pattern': 'simplified-strands', |
| 206 | + 'strands_available': True |
| 207 | + }) |
| 208 | + } |
| 209 | + |
| 210 | + except Exception as e: |
| 211 | + return { |
| 212 | + 'statusCode': 503, |
| 213 | + 'headers': {'Content-Type': 'application/json'}, |
| 214 | + 'body': json.dumps({ |
| 215 | + 'status': 'unhealthy', |
| 216 | + 'error': str(e), |
| 217 | + 'pattern': 'simplified-strands' |
| 218 | + }) |
| 219 | + } |
| 220 | + |
| 221 | +if __name__ == "__main__": |
| 222 | + # Local testing (following Strands example pattern) |
| 223 | + print("🧪 Testing CodeRipple Lambda (Simplified Strands Pattern)") |
| 224 | + |
| 225 | + test_event = { |
| 226 | + 'repository': {'name': 'test-repo', 'full_name': 'user/test-repo'}, |
| 227 | + 'commits': [{'id': 'test123', 'message': 'test commit', 'modified': ['README.md']}], |
| 228 | + 'ref': 'refs/heads/main' |
| 229 | + } |
| 230 | + |
| 231 | + class MockContext: |
| 232 | + def __init__(self): |
| 233 | + self.aws_request_id = 'test-request-id-12345' |
| 234 | + |
| 235 | + result = lambda_handler(test_event, MockContext()) |
| 236 | + print(json.dumps(result, indent=2)) |
| 237 | +``` |
| 238 | + |
| 239 | +**Simplified Requirements (matching Strands pattern):** |
| 240 | +``` |
| 241 | +strands-agents |
| 242 | +strands-agents-tools |
| 243 | +``` |
| 244 | + |
| 245 | +### Key Simplifications |
| 246 | + |
| 247 | +#### **Removed Complex Components:** |
| 248 | +1. **Layer validation** - Eliminated `validate_layer_imports()` completely |
| 249 | +2. **Conversation managers** - No `SlidingWindowConversationManager` |
| 250 | +3. **Complex initialization** - No multi-step agent setup |
| 251 | +4. **Specialist agent imports** - Simplified to core tools only |
| 252 | +5. **Environment variable complexity** - Minimal configuration |
| 253 | + |
| 254 | +#### **Adopted Strands Patterns:** |
| 255 | +1. **Direct Agent instantiation** - `Agent(system_prompt, tools)` |
| 256 | +2. **Simple tool imports** - Only essential CodeRipple tools |
| 257 | +3. **Minimal dependencies** - Follow official Strands requirements |
| 258 | +4. **Streamlined execution** - Direct agent call with response |
| 259 | +5. **Standard error handling** - Simple try/catch pattern |
| 260 | + |
| 261 | +#### **Preserved Core Functionality:** |
| 262 | +1. **GitHub webhook processing** - Parse and process webhook data |
| 263 | +2. **Documentation coordination** - Multi-agent logic via system prompt |
| 264 | +3. **API Gateway compatibility** - Standard HTTP response format |
| 265 | +4. **Logging and monitoring** - Essential logging for debugging |
| 266 | +5. **Error handling** - Robust error responses |
| 267 | + |
| 268 | +## AI Interactions |
| 269 | + |
| 270 | +**Context:** Analysis of official Strands Lambda deployment example revealed significant architectural differences from our complex implementation. |
| 271 | + |
| 272 | +**Key Discovery:** Official Strands pattern uses direct `Agent()` instantiation with minimal dependencies, avoiding complex initialization that triggers OpenTelemetry issues. |
| 273 | + |
| 274 | +**Strategic Decision:** Refactor to match proven Strands pattern rather than working around complex initialization issues with exception handling. |
| 275 | + |
| 276 | +**Benefits of Simplified Approach:** |
| 277 | +- Eliminates OpenTelemetry compatibility issues at source |
| 278 | +- Follows officially supported deployment pattern |
| 279 | +- Reduces Lambda cold start time and memory usage |
| 280 | +- Simplifies debugging and maintenance |
| 281 | +- Aligns with Strands best practices |
| 282 | + |
| 283 | +## Files Modified |
| 284 | + |
| 285 | +- `functions/orchestrator/lambda_function.py` - Complete refactor to simplified pattern |
| 286 | +- `functions/orchestrator/requirements.txt` - Simplified dependencies |
| 287 | + |
| 288 | +## Status: Ready for Implementation |
| 289 | + |
| 290 | +**Implementation Steps:** |
| 291 | +1. **Backup current complex implementation** |
| 292 | +2. **Replace with simplified Strands pattern code** |
| 293 | +3. **Update requirements.txt to match Strands example** |
| 294 | +4. **Test locally with simplified pattern** |
| 295 | +5. **Deploy and validate webhook functionality** |
| 296 | +6. **Monitor for OpenTelemetry issues (should be eliminated)** |
| 297 | + |
| 298 | +**Success Criteria:** |
| 299 | +- Lambda function initializes without OpenTelemetry errors |
| 300 | +- Webhook processing completes successfully |
| 301 | +- Agent responses provide documentation coordination |
| 302 | +- No layer validation or complex initialization errors |
| 303 | +- Performance improved with simplified architecture |
| 304 | + |
| 305 | +**Migration Strategy:** |
| 306 | +- **Phase 1**: Deploy simplified version alongside current version |
| 307 | +- **Phase 2**: A/B test both implementations |
| 308 | +- **Phase 3**: Switch traffic to simplified version if successful |
| 309 | +- **Phase 4**: Remove complex implementation after validation |
| 310 | + |
| 311 | +**Long-term Benefits:** |
| 312 | +- **Maintainability**: Simpler codebase following official patterns |
| 313 | +- **Reliability**: Fewer failure points and dependencies |
| 314 | +- **Performance**: Faster cold starts and reduced memory usage |
| 315 | +- **Supportability**: Alignment with official Strands examples |
0 commit comments