rootxhacker commited on
Commit
dfb38af
Β·
verified Β·
1 Parent(s): e10a54e

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +281 -171
app.py CHANGED
@@ -1,177 +1,291 @@
1
  #!/usr/bin/env python3
 
 
 
 
2
 
3
  import gradio as gr
4
  import os
5
- from typing import Dict, List, Any, Optional
6
  import json
7
- from datetime import datetime
8
  import traceback
9
-
10
- # CrewAI and Julia Browser imports
11
- from crewai import Agent, Task, Crew, Process
12
- from crewai_tools import BaseTool
13
- from crewai.llm import LLM
14
  from julia_browser import AgentSDK
15
 
16
- # Initialize browser
17
  browser = AgentSDK()
 
18
 
19
- class OpenWebsiteTool(BaseTool):
20
- name: str = "open_website"
21
- description: str = "Open a website and get page content. Input: url (string)"
22
-
23
- def _run(self, url: str) -> str:
24
- result = browser.open_website(url)
25
- return f"Opened: {result['title']} at {url}"
26
-
27
- class ListElementsTool(BaseTool):
28
- name: str = "list_elements"
29
- description: str = "List all clickable elements and input fields on current page"
30
-
31
- def _run(self) -> str:
32
- elements = browser.list_elements()
33
- output = []
34
- for elem in elements.get("elements", []):
35
- output.append(f"[{elem['id']}] {elem['type']}: {elem.get('text', 'N/A')}")
36
- return f"Found {elements['total_clickable']} clickable, {elements['total_inputs']} inputs:\n" + "\n".join(output)
37
-
38
- class ClickElementTool(BaseTool):
39
- name: str = "click_element"
40
- description: str = "Click a button or link by its number ID. Input: element_id (int)"
41
 
42
- def _run(self, element_id: int) -> str:
43
- result = browser.click_element(element_id)
44
- return f"Clicked: {result.get('element', 'Unknown')} - {result['status']}"
45
-
46
- class TypeTextTool(BaseTool):
47
- name: str = "type_text"
48
- description: str = "Type text into an input field. Input: field_id (int), text (string)"
 
49
 
50
- def _run(self, field_id: int, text: str) -> str:
51
- result = browser.type_text(field_id, text)
52
- return f"Typed '{text}' into field {field_id} - {result['status']}"
53
-
54
- class SubmitFormTool(BaseTool):
55
- name: str = "submit_form"
56
- description: str = "Submit the current form with typed data"
 
 
 
 
 
57
 
58
- def _run(self) -> str:
59
- result = browser.submit_form()
60
- return f"Form submitted - New page: {result.get('title', 'Unknown')}"
61
-
62
- class GetPageInfoTool(BaseTool):
63
- name: str = "get_page_info"
64
- description: str = "Get current page title, URL, and content"
 
65
 
66
- def _run(self) -> str:
67
- info = browser.get_page_info()
68
- return f"Title: {info['title']}\nURL: {info['url']}\nContent: {info['content'][:300]}..."
69
-
70
- class ScrollDownTool(BaseTool):
71
- name: str = "scroll_down"
72
- description: str = "Scroll down the page. Input: chunks (int, default 1)"
 
73
 
74
- def _run(self, chunks: int = 1) -> str:
75
- result = browser.scroll_down(chunks)
76
- return f"Scrolled down {chunks} chunks - Position: {result['position']}"
77
-
78
- class ScrollUpTool(BaseTool):
79
- name: str = "scroll_up"
80
- description: str = "Scroll up the page. Input: chunks (int, default 1)"
 
81
 
82
- def _run(self, chunks: int = 1) -> str:
83
- result = browser.scroll_up(chunks)
84
- return f"Scrolled up {chunks} chunks - Position: {result['position']}"
85
-
86
- class SearchPageTool(BaseTool):
87
- name: str = "search_page"
88
- description: str = "Search for text within current page. Input: term (string)"
 
 
89
 
90
- def _run(self, term: str) -> str:
91
- result = browser.search_page(term)
92
- return f"Found {result.get('matches', 0)} matches for '{term}'"
93
-
94
- class FollowLinkTool(BaseTool):
95
- name: str = "follow_link"
96
- description: str = "Navigate to a link by its number. Input: link_id (int)"
 
97
 
98
- def _run(self, link_id: int) -> str:
99
- result = browser.follow_link(link_id)
100
- return f"Followed link {link_id} - Now at: {result.get('title', 'Unknown')}"
 
 
 
 
 
101
 
102
- # Initialize all tools
103
- browser_tools = [
104
- OpenWebsiteTool(),
105
- ListElementsTool(),
106
- ClickElementTool(),
107
- TypeTextTool(),
108
- SubmitFormTool(),
109
- GetPageInfoTool(),
110
- ScrollDownTool(),
111
- ScrollUpTool(),
112
- SearchPageTool(),
113
- FollowLinkTool()
114
- ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
115
 
116
  class WebAutomationAgent:
 
 
117
  def __init__(self):
118
- # Configure Groq LLM
119
- self.llm = LLM(
120
- model="groq/qwen2.5-32b-instruct",
121
- api_key=os.getenv("GROQ_API_KEY")
122
- )
123
-
124
- # Create web automation agent
125
- self.agent = Agent(
126
- role="Web Automation Expert",
127
- goal="Execute web tasks using browser tools based on user instructions",
128
- backstory="""You are a skilled web automation expert who can navigate websites,
129
- interact with elements, fill forms, and extract information. You break down
130
- complex tasks into simple browser actions and execute them step by step.""",
131
- tools=browser_tools,
132
- llm=self.llm,
133
- verbose=True,
134
- allow_delegation=False
135
- )
136
 
137
- def execute_task(self, instruction: str) -> str:
138
- """Execute user task"""
139
- task = Task(
140
- description=f"""
141
- Task: {instruction}
142
-
143
- Use the available browser tools to complete this task:
144
- - open_website(url) - Open websites
145
- - list_elements() - See what's clickable on page
146
- - click_element(id) - Click buttons/links
147
- - type_text(field_id, text) - Fill input fields
148
- - submit_form() - Submit forms
149
- - get_page_info() - Get page details
150
- - scroll_down(chunks) - Scroll to see more
151
- - search_page(term) - Find text on page
152
- - follow_link(id) - Navigate to links
153
-
154
- Work step by step and explain what you're doing.
155
- """,
156
- agent=self.agent,
157
- expected_output="Complete step-by-step execution report with results"
158
- )
159
 
160
- crew = Crew(
161
- agents=[self.agent],
162
- tasks=[task],
163
- process=Process.sequential,
164
- verbose=True
165
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
166
 
167
  try:
168
- result = crew.kickoff()
169
- return str(result)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
170
  except Exception as e:
171
- return f"Error: {str(e)}\n{traceback.format_exc()}"
172
 
173
  # Initialize agent
174
- automation_agent = WebAutomationAgent()
175
 
176
  def execute_user_task(message: str, history: List[List[str]]) -> tuple:
177
  """Process user message and execute task"""
@@ -183,11 +297,11 @@ def execute_user_task(message: str, history: List[List[str]]) -> tuple:
183
 
184
  try:
185
  # Execute task
186
- result = automation_agent.execute_task(message)
187
  # Update with result
188
  history[-1][1] = result
189
  except Exception as e:
190
- history[-1][1] = f"❌ Error: {str(e)}"
191
 
192
  return history, ""
193
 
@@ -197,27 +311,24 @@ def clear_history():
197
  # Sample tasks
198
  sample_tasks = [
199
  "Open google.com and search for 'web automation'",
200
- "Go to example.com and list all elements",
201
- "Navigate to a news website and find the main headline",
202
- "Open a shopping site and look for search functionality",
203
- "Visit github.com and find the login button"
204
  ]
205
 
206
- def load_sample(task_text):
207
- return task_text
208
-
209
  # Create Gradio Interface
210
  with gr.Blocks(title="AI Web Agent", theme=gr.themes.Soft()) as demo:
211
 
212
  gr.HTML("""
213
  <div style="text-align: center; margin: 20px;">
214
  <h1>πŸ€– AI Web Automation Agent</h1>
215
- <p><strong>Julia Browser + CrewAI + Groq (Qwen-32B)</strong></p>
216
- <p>Give me web tasks in plain English!</p>
217
  </div>
218
  """)
219
 
220
- # Main chat interface - centered
221
  chatbot = gr.Chatbot(
222
  label="Agent Execution",
223
  height=600,
@@ -225,7 +336,7 @@ with gr.Blocks(title="AI Web Agent", theme=gr.themes.Soft()) as demo:
225
  )
226
 
227
  # Centered input section
228
- with gr.Row(elem_id="input-row"):
229
  with gr.Column(scale=1):
230
  pass # Left spacer
231
  with gr.Column(scale=3):
@@ -233,8 +344,7 @@ with gr.Blocks(title="AI Web Agent", theme=gr.themes.Soft()) as demo:
233
  user_input = gr.Textbox(
234
  placeholder="Tell me what to do on the web...",
235
  container=False,
236
- scale=4,
237
- elem_id="main-input"
238
  )
239
  send_btn = gr.Button("πŸš€ Execute", variant="primary", scale=1)
240
 
@@ -251,7 +361,7 @@ with gr.Blocks(title="AI Web Agent", theme=gr.themes.Soft()) as demo:
251
 
252
  for i, task in enumerate(sample_tasks):
253
  sample_btn = gr.Button(
254
- f"Sample {i+1}: {task[:30]}...",
255
  variant="outline",
256
  size="sm"
257
  )
@@ -264,12 +374,12 @@ with gr.Blocks(title="AI Web Agent", theme=gr.themes.Soft()) as demo:
264
  with gr.Column():
265
  gr.HTML("""
266
  <div style="padding: 15px; background: #f8f9fa; border-radius: 8px;">
267
- <h4>πŸ’‘ Tips:</h4>
268
  <ul style="font-size: 12px;">
269
- <li>Specify the website URL</li>
270
- <li>Describe what to click/type</li>
271
- <li>Ask for information extraction</li>
272
- <li>Request form interactions</li>
273
  </ul>
274
  </div>
275
  """)
@@ -311,12 +421,12 @@ if __name__ == "__main__":
311
  print("⚠️ Warning: GROQ_API_KEY not found in environment variables")
312
  print("Set it with: export GROQ_API_KEY='your_api_key_here'")
313
 
314
- print("πŸš€ Starting AI Web Automation Agent...")
315
- print("πŸ“ Available browser tools:")
316
- for tool in browser_tools:
317
- print(f" - {tool.name}: {tool.description}")
318
 
319
- # For Hugging Face Spaces, use environment port if available
320
  port = int(os.getenv("PORT", 7860))
321
 
322
  demo.launch(
 
1
  #!/usr/bin/env python3
2
+ """
3
+ AI Web Agent using Julia Browser with Direct Groq Integration
4
+ No CrewAI - Pure implementation with function calling
5
+ """
6
 
7
  import gradio as gr
8
  import os
 
9
  import json
10
+ from typing import Dict, List, Any, Optional
11
  import traceback
12
+ from groq import Groq
 
 
 
 
13
  from julia_browser import AgentSDK
14
 
15
+ # Initialize browser and Groq client
16
  browser = AgentSDK()
17
+ groq_client = Groq(api_key=os.getenv("GROQ_API_KEY"))
18
 
19
+ class BrowserActions:
20
+ """Direct browser action implementations"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
 
22
+ @staticmethod
23
+ def open_website(url: str) -> Dict[str, Any]:
24
+ """Open a website"""
25
+ try:
26
+ result = browser.open_website(url)
27
+ return {"success": True, "message": f"Opened: {result['title']} at {url}", "data": result}
28
+ except Exception as e:
29
+ return {"success": False, "message": f"Error opening {url}: {str(e)}"}
30
 
31
+ @staticmethod
32
+ def list_elements() -> Dict[str, Any]:
33
+ """List all interactive elements"""
34
+ try:
35
+ elements = browser.list_elements()
36
+ element_list = []
37
+ for elem in elements.get("elements", []):
38
+ element_list.append(f"[{elem['id']}] {elem['type']}: {elem.get('text', 'N/A')}")
39
+ message = f"Found {elements['total_clickable']} clickable, {elements['total_inputs']} inputs:\n" + "\n".join(element_list)
40
+ return {"success": True, "message": message, "data": elements}
41
+ except Exception as e:
42
+ return {"success": False, "message": f"Error listing elements: {str(e)}"}
43
 
44
+ @staticmethod
45
+ def click_element(element_id: int) -> Dict[str, Any]:
46
+ """Click an element by ID"""
47
+ try:
48
+ result = browser.click_element(element_id)
49
+ return {"success": True, "message": f"Clicked: {result.get('element', 'Unknown')} - {result['status']}", "data": result}
50
+ except Exception as e:
51
+ return {"success": False, "message": f"Error clicking element {element_id}: {str(e)}"}
52
 
53
+ @staticmethod
54
+ def type_text(field_id: int, text: str) -> Dict[str, Any]:
55
+ """Type text into input field"""
56
+ try:
57
+ result = browser.type_text(field_id, text)
58
+ return {"success": True, "message": f"Typed '{text}' into field {field_id} - {result['status']}", "data": result}
59
+ except Exception as e:
60
+ return {"success": False, "message": f"Error typing into field {field_id}: {str(e)}"}
61
 
62
+ @staticmethod
63
+ def submit_form() -> Dict[str, Any]:
64
+ """Submit current form"""
65
+ try:
66
+ result = browser.submit_form()
67
+ return {"success": True, "message": f"Form submitted - New page: {result.get('title', 'Unknown')}", "data": result}
68
+ except Exception as e:
69
+ return {"success": False, "message": f"Error submitting form: {str(e)}"}
70
 
71
+ @staticmethod
72
+ def get_page_info() -> Dict[str, Any]:
73
+ """Get current page information"""
74
+ try:
75
+ info = browser.get_page_info()
76
+ message = f"Title: {info['title']}\nURL: {info['url']}\nContent preview: {info['content'][:200]}..."
77
+ return {"success": True, "message": message, "data": info}
78
+ except Exception as e:
79
+ return {"success": False, "message": f"Error getting page info: {str(e)}"}
80
 
81
+ @staticmethod
82
+ def scroll_down(chunks: int = 1) -> Dict[str, Any]:
83
+ """Scroll down the page"""
84
+ try:
85
+ result = browser.scroll_down(chunks)
86
+ return {"success": True, "message": f"Scrolled down {chunks} chunks - Position: {result['position']}", "data": result}
87
+ except Exception as e:
88
+ return {"success": False, "message": f"Error scrolling down: {str(e)}"}
89
 
90
+ @staticmethod
91
+ def search_page(term: str) -> Dict[str, Any]:
92
+ """Search for text on current page"""
93
+ try:
94
+ result = browser.search_page(term)
95
+ return {"success": True, "message": f"Found {result.get('matches', 0)} matches for '{term}'", "data": result}
96
+ except Exception as e:
97
+ return {"success": False, "message": f"Error searching page: {str(e)}"}
98
 
99
+ # Available functions for the AI
100
+ AVAILABLE_FUNCTIONS = {
101
+ "open_website": {
102
+ "function": BrowserActions.open_website,
103
+ "description": "Open a website",
104
+ "parameters": {
105
+ "type": "object",
106
+ "properties": {
107
+ "url": {"type": "string", "description": "The URL to open"}
108
+ },
109
+ "required": ["url"]
110
+ }
111
+ },
112
+ "list_elements": {
113
+ "function": BrowserActions.list_elements,
114
+ "description": "List all clickable elements and input fields on current page",
115
+ "parameters": {"type": "object", "properties": {}}
116
+ },
117
+ "click_element": {
118
+ "function": BrowserActions.click_element,
119
+ "description": "Click an element by its ID number",
120
+ "parameters": {
121
+ "type": "object",
122
+ "properties": {
123
+ "element_id": {"type": "integer", "description": "The ID number of the element to click"}
124
+ },
125
+ "required": ["element_id"]
126
+ }
127
+ },
128
+ "type_text": {
129
+ "function": BrowserActions.type_text,
130
+ "description": "Type text into an input field",
131
+ "parameters": {
132
+ "type": "object",
133
+ "properties": {
134
+ "field_id": {"type": "integer", "description": "The ID of the input field"},
135
+ "text": {"type": "string", "description": "The text to type"}
136
+ },
137
+ "required": ["field_id", "text"]
138
+ }
139
+ },
140
+ "submit_form": {
141
+ "function": BrowserActions.submit_form,
142
+ "description": "Submit the current form",
143
+ "parameters": {"type": "object", "properties": {}}
144
+ },
145
+ "get_page_info": {
146
+ "function": BrowserActions.get_page_info,
147
+ "description": "Get current page title, URL and content",
148
+ "parameters": {"type": "object", "properties": {}}
149
+ },
150
+ "scroll_down": {
151
+ "function": BrowserActions.scroll_down,
152
+ "description": "Scroll down the page",
153
+ "parameters": {
154
+ "type": "object",
155
+ "properties": {
156
+ "chunks": {"type": "integer", "description": "Number of chunks to scroll", "default": 1}
157
+ }
158
+ }
159
+ },
160
+ "search_page": {
161
+ "function": BrowserActions.search_page,
162
+ "description": "Search for text within the current page",
163
+ "parameters": {
164
+ "type": "object",
165
+ "properties": {
166
+ "term": {"type": "string", "description": "Text to search for"}
167
+ },
168
+ "required": ["term"]
169
+ }
170
+ }
171
+ }
172
 
173
  class WebAutomationAgent:
174
+ """AI Web Automation Agent with direct Groq integration"""
175
+
176
  def __init__(self):
177
+ if not os.getenv("GROQ_API_KEY"):
178
+ raise ValueError("GROQ_API_KEY environment variable is required")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
179
 
180
+ def execute_task(self, user_instruction: str) -> str:
181
+ """Execute a web automation task using function calling"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
182
 
183
+ # Prepare function definitions for Groq
184
+ functions = []
185
+ for name, func_info in AVAILABLE_FUNCTIONS.items():
186
+ functions.append({
187
+ "type": "function",
188
+ "function": {
189
+ "name": name,
190
+ "description": func_info["description"],
191
+ "parameters": func_info["parameters"]
192
+ }
193
+ })
194
+
195
+ # System prompt
196
+ system_prompt = """You are a web automation expert. Execute the user's web automation task step by step using the available browser functions.
197
+
198
+ Available functions:
199
+ - open_website(url) - Open any website
200
+ - list_elements() - See all clickable elements and inputs on page
201
+ - click_element(element_id) - Click buttons, links by their ID number
202
+ - type_text(field_id, text) - Type into input fields by ID
203
+ - submit_form() - Submit forms
204
+ - get_page_info() - Get page details
205
+ - scroll_down(chunks) - Scroll to see more content
206
+ - search_page(term) - Find text on current page
207
+
208
+ Work step by step:
209
+ 1. First understand what the user wants
210
+ 2. Open the website if needed
211
+ 3. List elements to see what's available
212
+ 4. Interact with elements as needed
213
+ 5. Provide clear feedback on each step
214
+
215
+ Always explain what you're doing and why."""
216
+
217
+ messages = [
218
+ {"role": "system", "content": system_prompt},
219
+ {"role": "user", "content": user_instruction}
220
+ ]
221
+
222
+ execution_log = []
223
+ max_iterations = 10
224
 
225
  try:
226
+ for iteration in range(max_iterations):
227
+ # Call Groq with function calling
228
+ response = groq_client.chat.completions.create(
229
+ model="qwen2.5-32b-instruct",
230
+ messages=messages,
231
+ tools=functions,
232
+ tool_choice="auto",
233
+ max_tokens=1000,
234
+ temperature=0.1
235
+ )
236
+
237
+ message = response.choices[0].message
238
+
239
+ # Add assistant message to conversation
240
+ messages.append({
241
+ "role": "assistant",
242
+ "content": message.content,
243
+ "tool_calls": message.tool_calls
244
+ })
245
+
246
+ # Log assistant response
247
+ if message.content:
248
+ execution_log.append(f"πŸ€– **AI**: {message.content}")
249
+
250
+ # Execute function calls if any
251
+ if message.tool_calls:
252
+ for tool_call in message.tool_calls:
253
+ function_name = tool_call.function.name
254
+ function_args = json.loads(tool_call.function.arguments)
255
+
256
+ execution_log.append(f"πŸ”§ **Executing**: {function_name}({function_args})")
257
+
258
+ # Execute the function
259
+ if function_name in AVAILABLE_FUNCTIONS:
260
+ try:
261
+ result = AVAILABLE_FUNCTIONS[function_name]["function"](**function_args)
262
+ execution_log.append(f"βœ… **Result**: {result['message']}")
263
+
264
+ # Add function result to conversation
265
+ messages.append({
266
+ "role": "tool",
267
+ "tool_call_id": tool_call.id,
268
+ "content": json.dumps(result)
269
+ })
270
+ except Exception as e:
271
+ error_msg = f"Error executing {function_name}: {str(e)}"
272
+ execution_log.append(f"❌ **Error**: {error_msg}")
273
+ messages.append({
274
+ "role": "tool",
275
+ "tool_call_id": tool_call.id,
276
+ "content": json.dumps({"success": False, "message": error_msg})
277
+ })
278
+ else:
279
+ # No more function calls, task completed
280
+ break
281
+
282
+ return "\n\n".join(execution_log)
283
+
284
  except Exception as e:
285
+ return f"❌ **Error**: {str(e)}\n\n{traceback.format_exc()}"
286
 
287
  # Initialize agent
288
+ agent = WebAutomationAgent()
289
 
290
  def execute_user_task(message: str, history: List[List[str]]) -> tuple:
291
  """Process user message and execute task"""
 
297
 
298
  try:
299
  # Execute task
300
+ result = agent.execute_task(message)
301
  # Update with result
302
  history[-1][1] = result
303
  except Exception as e:
304
+ history[-1][1] = f"❌ **Error**: {str(e)}"
305
 
306
  return history, ""
307
 
 
311
  # Sample tasks
312
  sample_tasks = [
313
  "Open google.com and search for 'web automation'",
314
+ "Go to example.com and list all elements on the page",
315
+ "Navigate to github.com and find the login button",
316
+ "Open a news website and get the page information",
317
+ "Visit stackoverflow.com and scroll down to see more content"
318
  ]
319
 
 
 
 
320
  # Create Gradio Interface
321
  with gr.Blocks(title="AI Web Agent", theme=gr.themes.Soft()) as demo:
322
 
323
  gr.HTML("""
324
  <div style="text-align: center; margin: 20px;">
325
  <h1>πŸ€– AI Web Automation Agent</h1>
326
+ <p><strong>Julia Browser + Direct Groq Integration (Qwen-32B)</strong></p>
327
+ <p>Pure implementation without CrewAI - Function calling with Groq!</p>
328
  </div>
329
  """)
330
 
331
+ # Main chat interface
332
  chatbot = gr.Chatbot(
333
  label="Agent Execution",
334
  height=600,
 
336
  )
337
 
338
  # Centered input section
339
+ with gr.Row():
340
  with gr.Column(scale=1):
341
  pass # Left spacer
342
  with gr.Column(scale=3):
 
344
  user_input = gr.Textbox(
345
  placeholder="Tell me what to do on the web...",
346
  container=False,
347
+ scale=4
 
348
  )
349
  send_btn = gr.Button("πŸš€ Execute", variant="primary", scale=1)
350
 
 
361
 
362
  for i, task in enumerate(sample_tasks):
363
  sample_btn = gr.Button(
364
+ f"Sample {i+1}: {task[:35]}...",
365
  variant="outline",
366
  size="sm"
367
  )
 
374
  with gr.Column():
375
  gr.HTML("""
376
  <div style="padding: 15px; background: #f8f9fa; border-radius: 8px;">
377
+ <h4>πŸ’‘ Features:</h4>
378
  <ul style="font-size: 12px;">
379
+ <li>Direct Groq function calling</li>
380
+ <li>No CrewAI dependencies</li>
381
+ <li>Step-by-step execution</li>
382
+ <li>Real browser automation</li>
383
  </ul>
384
  </div>
385
  """)
 
421
  print("⚠️ Warning: GROQ_API_KEY not found in environment variables")
422
  print("Set it with: export GROQ_API_KEY='your_api_key_here'")
423
 
424
+ print("πŸš€ Starting AI Web Automation Agent (Direct Implementation)...")
425
+ print("πŸ“ Available browser functions:")
426
+ for name, info in AVAILABLE_FUNCTIONS.items():
427
+ print(f" - {name}: {info['description']}")
428
 
429
+ # For Hugging Face Spaces
430
  port = int(os.getenv("PORT", 7860))
431
 
432
  demo.launch(