first commit
This commit is contained in:
365
agent-livekit/debug_browser_actions.py
Normal file
365
agent-livekit/debug_browser_actions.py
Normal file
@@ -0,0 +1,365 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Browser Action Debugging Utility
|
||||
|
||||
This utility helps debug browser automation issues by:
|
||||
1. Testing MCP server connectivity
|
||||
2. Validating browser state
|
||||
3. Testing selector discovery and execution
|
||||
4. Providing detailed logging for troubleshooting
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
import json
|
||||
import sys
|
||||
from typing import Dict, Any, List
|
||||
from mcp_chrome_client import MCPChromeClient
|
||||
|
||||
# Configure logging
|
||||
logging.basicConfig(
|
||||
level=logging.DEBUG,
|
||||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
||||
handlers=[
|
||||
logging.StreamHandler(sys.stdout),
|
||||
logging.FileHandler('browser_debug.log')
|
||||
]
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class BrowserActionDebugger:
|
||||
"""Debug utility for browser automation issues"""
|
||||
|
||||
def __init__(self, config: Dict[str, Any]):
|
||||
self.config = config
|
||||
self.client = MCPChromeClient(config)
|
||||
self.logger = logging.getLogger(__name__)
|
||||
|
||||
async def run_full_diagnostic(self) -> Dict[str, Any]:
|
||||
"""Run a comprehensive diagnostic of browser automation"""
|
||||
results = {
|
||||
"connectivity": None,
|
||||
"browser_state": None,
|
||||
"page_content": None,
|
||||
"interactive_elements": None,
|
||||
"selector_tests": [],
|
||||
"action_tests": []
|
||||
}
|
||||
|
||||
try:
|
||||
# Test 1: MCP Server Connectivity
|
||||
self.logger.info("🔍 TEST 1: Testing MCP server connectivity...")
|
||||
results["connectivity"] = await self._test_connectivity()
|
||||
|
||||
# Test 2: Browser State
|
||||
self.logger.info("🔍 TEST 2: Checking browser state...")
|
||||
results["browser_state"] = await self._test_browser_state()
|
||||
|
||||
# Test 3: Page Content
|
||||
self.logger.info("🔍 TEST 3: Getting page content...")
|
||||
results["page_content"] = await self._test_page_content()
|
||||
|
||||
# Test 4: Interactive Elements
|
||||
self.logger.info("🔍 TEST 4: Finding interactive elements...")
|
||||
results["interactive_elements"] = await self._test_interactive_elements()
|
||||
|
||||
# Test 5: Selector Generation
|
||||
self.logger.info("🔍 TEST 5: Testing selector generation...")
|
||||
results["selector_tests"] = await self._test_selector_generation()
|
||||
|
||||
# Test 6: Action Execution
|
||||
self.logger.info("🔍 TEST 6: Testing action execution...")
|
||||
results["action_tests"] = await self._test_action_execution()
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"💥 Diagnostic failed: {e}")
|
||||
results["error"] = str(e)
|
||||
|
||||
return results
|
||||
|
||||
async def _test_connectivity(self) -> Dict[str, Any]:
|
||||
"""Test MCP server connectivity"""
|
||||
try:
|
||||
await self.client.connect()
|
||||
return {
|
||||
"status": "success",
|
||||
"server_type": self.client.server_type,
|
||||
"server_url": self.client.server_url,
|
||||
"connected": self.client.session is not None
|
||||
}
|
||||
except Exception as e:
|
||||
return {
|
||||
"status": "failed",
|
||||
"error": str(e)
|
||||
}
|
||||
|
||||
async def _test_browser_state(self) -> Dict[str, Any]:
|
||||
"""Test browser state and availability"""
|
||||
try:
|
||||
# Try to get current URL
|
||||
result = await self.client._call_mcp_tool("chrome_get_web_content", {
|
||||
"format": "text",
|
||||
"selector": "title"
|
||||
})
|
||||
|
||||
return {
|
||||
"status": "success",
|
||||
"browser_available": True,
|
||||
"page_title": result.get("content", [{}])[0].get("text", "Unknown") if result.get("content") else "Unknown"
|
||||
}
|
||||
except Exception as e:
|
||||
return {
|
||||
"status": "failed",
|
||||
"browser_available": False,
|
||||
"error": str(e)
|
||||
}
|
||||
|
||||
async def _test_page_content(self) -> Dict[str, Any]:
|
||||
"""Test page content retrieval"""
|
||||
try:
|
||||
result = await self.client._call_mcp_tool("chrome_get_web_content", {
|
||||
"format": "text"
|
||||
})
|
||||
|
||||
content = result.get("content", [])
|
||||
if content and len(content) > 0:
|
||||
text_content = content[0].get("text", "")
|
||||
return {
|
||||
"status": "success",
|
||||
"content_length": len(text_content),
|
||||
"has_content": len(text_content) > 0,
|
||||
"preview": text_content[:200] + "..." if len(text_content) > 200 else text_content
|
||||
}
|
||||
else:
|
||||
return {
|
||||
"status": "success",
|
||||
"content_length": 0,
|
||||
"has_content": False,
|
||||
"preview": ""
|
||||
}
|
||||
except Exception as e:
|
||||
return {
|
||||
"status": "failed",
|
||||
"error": str(e)
|
||||
}
|
||||
|
||||
async def _test_interactive_elements(self) -> Dict[str, Any]:
|
||||
"""Test interactive element discovery"""
|
||||
try:
|
||||
result = await self.client._call_mcp_tool("chrome_get_interactive_elements", {
|
||||
"types": ["button", "a", "input", "select", "textarea"]
|
||||
})
|
||||
|
||||
elements = result.get("elements", [])
|
||||
|
||||
# Analyze elements
|
||||
element_summary = {}
|
||||
for element in elements:
|
||||
tag = element.get("tagName", "unknown").lower()
|
||||
element_summary[tag] = element_summary.get(tag, 0) + 1
|
||||
|
||||
return {
|
||||
"status": "success",
|
||||
"total_elements": len(elements),
|
||||
"element_types": element_summary,
|
||||
"sample_elements": elements[:5] if elements else []
|
||||
}
|
||||
except Exception as e:
|
||||
return {
|
||||
"status": "failed",
|
||||
"error": str(e)
|
||||
}
|
||||
|
||||
async def _test_selector_generation(self) -> List[Dict[str, Any]]:
|
||||
"""Test selector generation for various elements"""
|
||||
tests = []
|
||||
|
||||
try:
|
||||
# Get interactive elements first
|
||||
result = await self.client._call_mcp_tool("chrome_get_interactive_elements", {
|
||||
"types": ["button", "a", "input"]
|
||||
})
|
||||
|
||||
elements = result.get("elements", [])[:5] # Test first 5 elements
|
||||
|
||||
for i, element in enumerate(elements):
|
||||
test_result = {
|
||||
"element_index": i,
|
||||
"element_tag": element.get("tagName", "unknown"),
|
||||
"element_text": element.get("textContent", "")[:50],
|
||||
"element_attributes": element.get("attributes", {}),
|
||||
"generated_selector": None,
|
||||
"selector_valid": False
|
||||
}
|
||||
|
||||
try:
|
||||
# Generate selector
|
||||
selector = self.client._extract_best_selector(element)
|
||||
test_result["generated_selector"] = selector
|
||||
|
||||
# Test if selector is valid by trying to use it
|
||||
validation_result = await self.client._call_mcp_tool("chrome_get_web_content", {
|
||||
"selector": selector,
|
||||
"textOnly": False
|
||||
})
|
||||
|
||||
test_result["selector_valid"] = validation_result.get("content") is not None
|
||||
|
||||
except Exception as e:
|
||||
test_result["error"] = str(e)
|
||||
|
||||
tests.append(test_result)
|
||||
|
||||
except Exception as e:
|
||||
tests.append({
|
||||
"error": f"Failed to get elements for selector testing: {e}"
|
||||
})
|
||||
|
||||
return tests
|
||||
|
||||
async def _test_action_execution(self) -> List[Dict[str, Any]]:
|
||||
"""Test action execution with safe, non-destructive actions"""
|
||||
tests = []
|
||||
|
||||
# Test 1: Try to get page title (safe action)
|
||||
test_result = {
|
||||
"action": "get_page_title",
|
||||
"description": "Safe action to get page title",
|
||||
"status": None,
|
||||
"error": None
|
||||
}
|
||||
|
||||
try:
|
||||
result = await self.client._call_mcp_tool("chrome_get_web_content", {
|
||||
"selector": "title",
|
||||
"textOnly": True
|
||||
})
|
||||
test_result["status"] = "success"
|
||||
test_result["result"] = result
|
||||
except Exception as e:
|
||||
test_result["status"] = "failed"
|
||||
test_result["error"] = str(e)
|
||||
|
||||
tests.append(test_result)
|
||||
|
||||
# Test 2: Try keyboard action (safe - just Escape key)
|
||||
test_result = {
|
||||
"action": "keyboard_escape",
|
||||
"description": "Safe keyboard action (Escape key)",
|
||||
"status": None,
|
||||
"error": None
|
||||
}
|
||||
|
||||
try:
|
||||
result = await self.client._call_mcp_tool("chrome_keyboard", {
|
||||
"keys": "Escape"
|
||||
})
|
||||
test_result["status"] = "success"
|
||||
test_result["result"] = result
|
||||
except Exception as e:
|
||||
test_result["status"] = "failed"
|
||||
test_result["error"] = str(e)
|
||||
|
||||
tests.append(test_result)
|
||||
|
||||
return tests
|
||||
|
||||
async def test_specific_selector(self, selector: str) -> Dict[str, Any]:
|
||||
"""Test a specific selector"""
|
||||
self.logger.info(f"🔍 Testing specific selector: {selector}")
|
||||
|
||||
result = {
|
||||
"selector": selector,
|
||||
"validation": None,
|
||||
"click_test": None
|
||||
}
|
||||
|
||||
try:
|
||||
# Test 1: Validate selector exists
|
||||
validation = await self.client._call_mcp_tool("chrome_get_web_content", {
|
||||
"selector": selector,
|
||||
"textOnly": False
|
||||
})
|
||||
|
||||
result["validation"] = {
|
||||
"status": "success" if validation.get("content") else "not_found",
|
||||
"content": validation.get("content")
|
||||
}
|
||||
|
||||
# Test 2: Try clicking (only if element was found)
|
||||
if validation.get("content"):
|
||||
try:
|
||||
click_result = await self.client._call_mcp_tool("chrome_click_element", {
|
||||
"selector": selector
|
||||
})
|
||||
result["click_test"] = {
|
||||
"status": "success",
|
||||
"result": click_result
|
||||
}
|
||||
except Exception as click_error:
|
||||
result["click_test"] = {
|
||||
"status": "failed",
|
||||
"error": str(click_error)
|
||||
}
|
||||
else:
|
||||
result["click_test"] = {
|
||||
"status": "skipped",
|
||||
"reason": "Element not found"
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
result["validation"] = {
|
||||
"status": "failed",
|
||||
"error": str(e)
|
||||
}
|
||||
|
||||
return result
|
||||
|
||||
async def cleanup(self):
|
||||
"""Cleanup resources"""
|
||||
try:
|
||||
await self.client.disconnect()
|
||||
except Exception as e:
|
||||
self.logger.warning(f"Cleanup warning: {e}")
|
||||
|
||||
|
||||
async def main():
|
||||
"""Main function for running diagnostics"""
|
||||
# Default configuration - adjust as needed
|
||||
config = {
|
||||
'mcp_server_type': 'http',
|
||||
'mcp_server_url': 'http://localhost:3000/mcp',
|
||||
'mcp_server_command': '',
|
||||
'mcp_server_args': []
|
||||
}
|
||||
|
||||
debugger = BrowserActionDebugger(config)
|
||||
|
||||
try:
|
||||
print("🚀 Starting Browser Action Diagnostics...")
|
||||
results = await debugger.run_full_diagnostic()
|
||||
|
||||
print("\n" + "="*60)
|
||||
print("📊 DIAGNOSTIC RESULTS")
|
||||
print("="*60)
|
||||
|
||||
for test_name, test_result in results.items():
|
||||
print(f"\n{test_name.upper()}:")
|
||||
print(json.dumps(test_result, indent=2, default=str))
|
||||
|
||||
# Save results to file
|
||||
with open('browser_diagnostic_results.json', 'w') as f:
|
||||
json.dump(results, f, indent=2, default=str)
|
||||
|
||||
print(f"\n✅ Diagnostics complete! Results saved to browser_diagnostic_results.json")
|
||||
|
||||
except Exception as e:
|
||||
print(f"💥 Diagnostic failed: {e}")
|
||||
finally:
|
||||
await debugger.cleanup()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
Reference in New Issue
Block a user