*** id: abcbe047-e445-4a08-b329-b2ff4768d742 title: Lifecycle sidebar-title: Lifecycle position: 4 slug: /python/guides/lifecycle max-toc-depth: 3 ---------------- ## Request Lifecycle Trace the complete journey of a call through the SignalWire Agents SDK, from incoming call to conversation end. ### The Complete Call Flow Understanding the request lifecycle helps you debug issues and optimize your agents. Here's the complete flow: Complete Call Lifecycle. ### Phase 1: Call Setup When a call arrives at SignalWire: Call Setup. **Key points:** * SignalWire knows which agent to contact based on phone number configuration * The request includes Basic Auth credentials * POST is the default; GET requests are also supported for SWML retrieval ### Phase 2: SWML Generation Your agent builds and returns the SWML document: ```python ## Inside AgentBase._render_swml() def _render_swml(self, request_body=None): """Generate SWML document for this agent.""" # 1. Build the prompt (POM or text) prompt = self._build_prompt() # 2. Collect all SWAIG functions functions = self._tool_registry.get_functions() # 3. Generate webhook URLs with security tokens webhook_url = self._build_webhook_url("/swaig") # 4. Assemble AI configuration ai_config = { "prompt": prompt, "post_prompt": self._post_prompt, "post_prompt_url": self._build_webhook_url("/post_prompt"), "SWAIG": { "defaults": {"web_hook_url": webhook_url}, "functions": functions }, "hints": self._hints, "languages": self._languages, "params": self._params } # 5. Build complete SWML document swml = { "version": "1.0.0", "sections": { "main": [ {"answer": {}}, {"ai": ai_config} ] } } return swml ``` ### Phase 3: AI Conversation Once SignalWire has the SWML, it executes the instructions: AI Conversation Loop. **AI Parameters that control this loop:** | Parameter | Default | Purpose | | ----------------------- | ------- | ----------------------------------- | | `end_of_speech_timeout` | 500ms | Wait time after user stops speaking | | `attention_timeout` | 15000ms | Max silence before AI prompts | | `inactivity_timeout` | 30000ms | Max silence before ending call | | `barge_match_string` | - | Words that immediately interrupt AI | ### Phase 4: Function Calls When the AI needs to call a function: SWAIG Function Call. ### Phase 5: Call End When the call ends, the post-prompt summary is sent: Call Ending. ### Handling Post-Prompt Configure post-prompt handling in your agent: ```python from signalwire_agents import AgentBase class MyAgent(AgentBase): def __init__(self): super().__init__(name="my-agent") # Set the post-prompt instruction self.set_post_prompt( "Summarize this call including: " "1) The caller's main question or issue " "2) How it was resolved " "3) Any follow-up actions needed" ) # Or use structured post-prompt with JSON output self.set_post_prompt_json({ "issue": "string - the caller's main issue", "resolution": "string - how the issue was resolved", "follow_up": "boolean - whether follow-up is needed", "sentiment": "string - caller sentiment (positive/neutral/negative)" }) def on_post_prompt(self, data): """Handle the call summary.""" summary = data.get("post_prompt_data", {}) call_id = data.get("call_id") # Log to your system self.log_call_summary(call_id, summary) # Update CRM self.update_crm(data) ``` ### Request/Response Headers #### SWML Request (GET or POST /) ```http GET / HTTP/1.1 Host: your-agent.com Authorization: Basic c2lnbmFsd2lyZTpwYXNzd29yZA== Accept: application/json X-Forwarded-For: signalwire-ip X-Forwarded-Proto: https ``` #### SWML Response ```http HTTP/1.1 200 OK Content-Type: application/json {"version": "1.0.0", "sections": {...}} ``` #### SWAIG Request (POST /swaig) ```http POST /swaig HTTP/1.1 Host: your-agent.com Authorization: Basic c2lnbmFsd2lyZTpwYXNzd29yZA== Content-Type: application/json {"action": "swaig_action", "function": "...", ...} ``` #### SWAIG Response ```http HTTP/1.1 200 OK Content-Type: application/json {"response": "...", "action": [...]} ``` ### Debugging the Lifecycle #### View SWML Output ```bash ## See what your agent returns curl -u signalwire:password http://localhost:3000/ | jq '.' ## Using swaig-test swaig-test my_agent.py --dump-swml ``` #### Test Function Calls ```bash ## Call a function directly swaig-test my_agent.py --exec get_balance --account_id 12345 ## With verbose output swaig-test my_agent.py --exec get_balance --account_id 12345 --verbose ``` #### Monitor Live Traffic ```python from signalwire_agents import AgentBase class DebugAgent(AgentBase): def __init__(self): super().__init__(name="debug-agent") def on_swml_request(self, request_data=None, callback_path=None, request=None): """Called when SWML is requested.""" if request: print(f"SWML requested from: {request.client.host}") print(f"Headers: {dict(request.headers)}") def on_swaig_request(self, function_name, args, raw_data): """Called before each SWAIG function.""" print(f"Function called: {function_name}") print(f"Arguments: {args}") print(f"Call ID: {raw_data.get('call_id')}") ``` ### Error Handling #### SWML Errors If your agent can't generate SWML: ```python def _render_swml(self): try: return self._build_swml() except Exception as e: # Return minimal valid SWML return { "version": "1.0.0", "sections": { "main": [ {"answer": {}}, {"play": {"url": "https://example.com/error.mp3"}}, {"hangup": {}} ] } } ``` #### SWAIG Errors If a function fails: ```python def get_balance(self, args, raw_data): try: balance = self.lookup_balance(args.get("account_id")) return SwaigFunctionResult(f"Your balance is ${balance}") except DatabaseError: return SwaigFunctionResult( "I'm having trouble accessing account information right now. " "Please try again in a moment." ) except Exception as e: # Log the error but return user-friendly message self.logger.error(f"Function error: {e}") return SwaigFunctionResult( "I encountered an unexpected error. " "Let me transfer you to a representative." ) ``` ### Next Steps Now that you understand the complete lifecycle, let's look at how security works throughout this flow.