-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.py
More file actions
133 lines (110 loc) · 4.89 KB
/
Copy pathmain.py
File metadata and controls
133 lines (110 loc) · 4.89 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
import asyncio
from pathlib import Path
from uuid import uuid4
import click
import httpx
from playwright.async_api import async_playwright
from config import get_settings
from agents.executor_agent import build_agent_graph
from agents.state import AgentState
from pages.adams_golf_club import HomePage, LoginPage, MemberDashboardPage
from tools.api_tools import ApiToolkit
from tools.tool_registry import ToolRegistry
from utils.logger import setup_logging, get_logger
@click.command()
@click.argument("goal")
@click.option("--mode", type=click.Choice(["ui", "api"]), default="ui",
help="Test mode: 'ui' (default) drives Playwright; 'api' drives "
"the API toolkit (no browser launched).")
@click.option("--headed", is_flag=True, help="Run browser in headed mode (UI mode only)")
@click.option("--thread-id", default=None, help="Resume from checkpoint")
def run(goal: str, mode: str, headed: bool, thread_id: str | None):
"""Execute an agentic test goal from the command line.
Example:
python main.py "Log in as a member and verify the member dashboard loads"
python main.py --mode api "Create an item via POST /items and verify it persisted"
"""
settings = get_settings()
log_file = Path(settings.log_dir) / "execution.log"
setup_logging(log_file=str(log_file), json_output=False)
if mode == "api":
asyncio.run(_execute_api(goal, thread_id))
else:
asyncio.run(_execute(goal, headed, thread_id))
def _build_initial_state(goal: str) -> AgentState:
return {
"task": goal,
"messages": [],
"steps_taken": [],
"dom_snapshots": [],
"error_history": [],
"screenshots": [],
"current_phase": "init",
"final_result": None,
"iteration_count": 0,
"routing_decision": "pom_tools",
"healing_occurred": False,
"test_context": {},
}
async def _execute(goal: str, headed: bool, thread_id: str | None):
log = get_logger("main")
settings = get_settings()
log.info("execute_start", goal=goal[:100], headed=headed)
if headed:
settings.headless = False
async with async_playwright() as pw:
browser = await pw.chromium.launch(
headless=settings.headless,
slow_mo=settings.slow_mo_ms,
)
context = await browser.new_context()
page = await context.new_page()
# Register tools — adams_golf_club is the default demo app
# (member auth flow exercises POM + visual-fallback together).
registry = ToolRegistry()
registry.register_page(LoginPage(page))
registry.register_page(HomePage(page))
registry.register_page(MemberDashboardPage(page))
tools = registry.get_all_tools()
# Build and run graph (browser tools are added internally when page is provided)
async with await build_agent_graph(tools, settings, page=page) as graph:
initial_state = _build_initial_state(goal)
final_state = await graph.ainvoke(
initial_state,
config={"configurable": {"thread_id": thread_id or str(uuid4())}},
)
click.echo(f"\nResult: {final_state['final_result']}")
click.echo(f"Steps taken: {len(final_state['steps_taken'])}")
click.echo(f"Iterations: {final_state['iteration_count']}")
if final_state["error_history"]:
click.echo(f"Errors encountered: {len(final_state['error_history'])}")
await browser.close()
async def _execute_api(goal: str, thread_id: str | None):
"""API-mode entry point — no browser, no POM, just an HTTP client + ApiToolkit."""
log = get_logger("main")
settings = get_settings()
log.info("execute_api_start", goal=goal[:100], base_url=settings.api_base_url)
async with httpx.AsyncClient(timeout=settings.api_request_timeout_s) as client:
api_toolkit = ApiToolkit.from_settings(client, settings)
try:
registry = ToolRegistry()
tools = registry.get_all_tools()
async with await build_agent_graph(
tools, settings,
api_toolkit=api_toolkit,
healing_enabled=False,
) as graph:
initial_state = _build_initial_state(goal)
final_state = await graph.ainvoke(
initial_state,
config={"configurable": {"thread_id": thread_id or str(uuid4())}},
)
click.echo(f"\nResult: {final_state['final_result']}")
click.echo(f"Steps taken: {len(final_state['steps_taken'])}")
click.echo(f"Iterations: {final_state['iteration_count']}")
if final_state["error_history"]:
click.echo(f"Errors encountered: {len(final_state['error_history'])}")
finally:
await api_toolkit.close()
if __name__ == "__main__":
run()