Spaces:
Running
Running
File size: 16,421 Bytes
e13333a dfb38af e13333a dfb38af e13333a dfb38af e13333a dfb38af e13333a dfb38af e13333a dfb38af e13333a dfb38af e13333a dfb38af e13333a dfb38af e13333a dfb38af e13333a dfb38af e13333a dfb38af e13333a dfb38af e13333a dfb38af e13333a dfb38af e13333a dfb38af e13333a dfb38af e13333a dfb38af e13333a dfb38af e13333a dfb38af a2e4be1 dfb38af e13333a dfb38af e13333a dfb38af e13333a dfb38af e13333a dfb38af e13333a dfb38af e13333a dfb38af e13333a dfb38af e13333a dfb38af e13333a dfb38af e13333a dfb38af e13333a dfb38af e13333a dfb38af e13333a dfb38af e13333a dfb38af e13333a |
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 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 |
#!/usr/bin/env python3
"""
AI Web Agent using Julia Browser with Direct Groq Integration
No CrewAI - Pure implementation with function calling
"""
import gradio as gr
import os
import json
from typing import Dict, List, Any, Optional
import traceback
from groq import Groq
from julia_browser import AgentSDK
# Initialize browser and Groq client
browser = AgentSDK()
groq_client = Groq(api_key=os.getenv("GROQ_API_KEY"))
class BrowserActions:
"""Direct browser action implementations"""
@staticmethod
def open_website(url: str) -> Dict[str, Any]:
"""Open a website"""
try:
result = browser.open_website(url)
return {"success": True, "message": f"Opened: {result['title']} at {url}", "data": result}
except Exception as e:
return {"success": False, "message": f"Error opening {url}: {str(e)}"}
@staticmethod
def list_elements() -> Dict[str, Any]:
"""List all interactive elements"""
try:
elements = browser.list_elements()
element_list = []
for elem in elements.get("elements", []):
element_list.append(f"[{elem['id']}] {elem['type']}: {elem.get('text', 'N/A')}")
message = f"Found {elements['total_clickable']} clickable, {elements['total_inputs']} inputs:\n" + "\n".join(element_list)
return {"success": True, "message": message, "data": elements}
except Exception as e:
return {"success": False, "message": f"Error listing elements: {str(e)}"}
@staticmethod
def click_element(element_id: int) -> Dict[str, Any]:
"""Click an element by ID"""
try:
result = browser.click_element(element_id)
return {"success": True, "message": f"Clicked: {result.get('element', 'Unknown')} - {result['status']}", "data": result}
except Exception as e:
return {"success": False, "message": f"Error clicking element {element_id}: {str(e)}"}
@staticmethod
def type_text(field_id: int, text: str) -> Dict[str, Any]:
"""Type text into input field"""
try:
result = browser.type_text(field_id, text)
return {"success": True, "message": f"Typed '{text}' into field {field_id} - {result['status']}", "data": result}
except Exception as e:
return {"success": False, "message": f"Error typing into field {field_id}: {str(e)}"}
@staticmethod
def submit_form() -> Dict[str, Any]:
"""Submit current form"""
try:
result = browser.submit_form()
return {"success": True, "message": f"Form submitted - New page: {result.get('title', 'Unknown')}", "data": result}
except Exception as e:
return {"success": False, "message": f"Error submitting form: {str(e)}"}
@staticmethod
def get_page_info() -> Dict[str, Any]:
"""Get current page information"""
try:
info = browser.get_page_info()
message = f"Title: {info['title']}\nURL: {info['url']}\nContent preview: {info['content'][:200]}..."
return {"success": True, "message": message, "data": info}
except Exception as e:
return {"success": False, "message": f"Error getting page info: {str(e)}"}
@staticmethod
def scroll_down(chunks: int = 1) -> Dict[str, Any]:
"""Scroll down the page"""
try:
result = browser.scroll_down(chunks)
return {"success": True, "message": f"Scrolled down {chunks} chunks - Position: {result['position']}", "data": result}
except Exception as e:
return {"success": False, "message": f"Error scrolling down: {str(e)}"}
@staticmethod
def search_page(term: str) -> Dict[str, Any]:
"""Search for text on current page"""
try:
result = browser.search_page(term)
return {"success": True, "message": f"Found {result.get('matches', 0)} matches for '{term}'", "data": result}
except Exception as e:
return {"success": False, "message": f"Error searching page: {str(e)}"}
# Available functions for the AI
AVAILABLE_FUNCTIONS = {
"open_website": {
"function": BrowserActions.open_website,
"description": "Open a website",
"parameters": {
"type": "object",
"properties": {
"url": {"type": "string", "description": "The URL to open"}
},
"required": ["url"]
}
},
"list_elements": {
"function": BrowserActions.list_elements,
"description": "List all clickable elements and input fields on current page",
"parameters": {"type": "object", "properties": {}}
},
"click_element": {
"function": BrowserActions.click_element,
"description": "Click an element by its ID number",
"parameters": {
"type": "object",
"properties": {
"element_id": {"type": "integer", "description": "The ID number of the element to click"}
},
"required": ["element_id"]
}
},
"type_text": {
"function": BrowserActions.type_text,
"description": "Type text into an input field",
"parameters": {
"type": "object",
"properties": {
"field_id": {"type": "integer", "description": "The ID of the input field"},
"text": {"type": "string", "description": "The text to type"}
},
"required": ["field_id", "text"]
}
},
"submit_form": {
"function": BrowserActions.submit_form,
"description": "Submit the current form",
"parameters": {"type": "object", "properties": {}}
},
"get_page_info": {
"function": BrowserActions.get_page_info,
"description": "Get current page title, URL and content",
"parameters": {"type": "object", "properties": {}}
},
"scroll_down": {
"function": BrowserActions.scroll_down,
"description": "Scroll down the page",
"parameters": {
"type": "object",
"properties": {
"chunks": {"type": "integer", "description": "Number of chunks to scroll", "default": 1}
}
}
},
"search_page": {
"function": BrowserActions.search_page,
"description": "Search for text within the current page",
"parameters": {
"type": "object",
"properties": {
"term": {"type": "string", "description": "Text to search for"}
},
"required": ["term"]
}
}
}
class WebAutomationAgent:
"""AI Web Automation Agent with direct Groq integration"""
def __init__(self):
if not os.getenv("GROQ_API_KEY"):
raise ValueError("GROQ_API_KEY environment variable is required")
def execute_task(self, user_instruction: str) -> str:
"""Execute a web automation task using function calling"""
# Prepare function definitions for Groq
functions = []
for name, func_info in AVAILABLE_FUNCTIONS.items():
functions.append({
"type": "function",
"function": {
"name": name,
"description": func_info["description"],
"parameters": func_info["parameters"]
}
})
# System prompt
system_prompt = """You are a web automation expert. Execute the user's web automation task step by step using the available browser functions.
Available functions:
- open_website(url) - Open any website
- list_elements() - See all clickable elements and inputs on page
- click_element(element_id) - Click buttons, links by their ID number
- type_text(field_id, text) - Type into input fields by ID
- submit_form() - Submit forms
- get_page_info() - Get page details
- scroll_down(chunks) - Scroll to see more content
- search_page(term) - Find text on current page
Work step by step:
1. First understand what the user wants
2. Open the website if needed
3. List elements to see what's available
4. Interact with elements as needed
5. Provide clear feedback on each step
Always explain what you're doing and why."""
messages = [
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_instruction}
]
execution_log = []
max_iterations = 10
try:
for iteration in range(max_iterations):
# Call Groq with function calling
response = groq_client.chat.completions.create(
model="moonshotai/kimi-k2-instruct",
messages=messages,
tools=functions,
tool_choice="auto",
max_tokens=1000,
temperature=0.1
)
message = response.choices[0].message
# Add assistant message to conversation
messages.append({
"role": "assistant",
"content": message.content,
"tool_calls": message.tool_calls
})
# Log assistant response
if message.content:
execution_log.append(f"π€ **AI**: {message.content}")
# Execute function calls if any
if message.tool_calls:
for tool_call in message.tool_calls:
function_name = tool_call.function.name
function_args = json.loads(tool_call.function.arguments)
execution_log.append(f"π§ **Executing**: {function_name}({function_args})")
# Execute the function
if function_name in AVAILABLE_FUNCTIONS:
try:
result = AVAILABLE_FUNCTIONS[function_name]["function"](**function_args)
execution_log.append(f"β
**Result**: {result['message']}")
# Add function result to conversation
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": json.dumps(result)
})
except Exception as e:
error_msg = f"Error executing {function_name}: {str(e)}"
execution_log.append(f"β **Error**: {error_msg}")
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": json.dumps({"success": False, "message": error_msg})
})
else:
# No more function calls, task completed
break
return "\n\n".join(execution_log)
except Exception as e:
return f"β **Error**: {str(e)}\n\n{traceback.format_exc()}"
# Initialize agent
agent = WebAutomationAgent()
def execute_user_task(message: str, history: List[List[str]]) -> tuple:
"""Process user message and execute task"""
if not message.strip():
return history, ""
# Add user message
history.append([message, "π€ Executing task..."])
try:
# Execute task
result = agent.execute_task(message)
# Update with result
history[-1][1] = result
except Exception as e:
history[-1][1] = f"β **Error**: {str(e)}"
return history, ""
def clear_history():
return [], ""
# Sample tasks
sample_tasks = [
"Open google.com and search for 'web automation'",
"Go to example.com and list all elements on the page",
"Navigate to github.com and find the login button",
"Open a news website and get the page information",
"Visit stackoverflow.com and scroll down to see more content"
]
# Create Gradio Interface
with gr.Blocks(title="AI Web Agent", theme=gr.themes.Soft()) as demo:
gr.HTML("""
<div style="text-align: center; margin: 20px;">
<h1>π€ AI Web Automation Agent</h1>
<p><strong>Julia Browser + Direct Groq Integration (Qwen-32B)</strong></p>
<p>Pure implementation without CrewAI - Function calling with Groq!</p>
</div>
""")
# Main chat interface
chatbot = gr.Chatbot(
label="Agent Execution",
height=600,
show_copy_button=True
)
# Centered input section
with gr.Row():
with gr.Column(scale=1):
pass # Left spacer
with gr.Column(scale=3):
with gr.Row():
user_input = gr.Textbox(
placeholder="Tell me what to do on the web...",
container=False,
scale=4
)
send_btn = gr.Button("π Execute", variant="primary", scale=1)
clear_btn = gr.Button("ποΈ Clear", variant="secondary", size="sm")
with gr.Column(scale=1):
pass # Right spacer
# Sample tasks section
with gr.Row():
with gr.Column(scale=1):
pass # Left spacer
with gr.Column(scale=2):
gr.HTML("<h3 style='text-align: center;'>π Sample Tasks</h3>")
for i, task in enumerate(sample_tasks):
sample_btn = gr.Button(
f"Sample {i+1}: {task[:35]}...",
variant="outline",
size="sm"
)
sample_btn.click(
lambda t=task: t,
outputs=user_input
)
with gr.Row():
with gr.Column():
gr.HTML("""
<div style="padding: 15px; background: #f8f9fa; border-radius: 8px;">
<h4>π‘ Features:</h4>
<ul style="font-size: 12px;">
<li>Direct Groq function calling</li>
<li>No CrewAI dependencies</li>
<li>Step-by-step execution</li>
<li>Real browser automation</li>
</ul>
</div>
""")
with gr.Column():
gr.HTML("""
<div style="padding: 15px; background: #e3f2fd; border-radius: 8px;">
<h4>βοΈ Setup:</h4>
<p style="font-size: 12px;">
Set GROQ_API_KEY:<br>
<code>export GROQ_API_KEY="gsk_..."</code>
</p>
</div>
""")
with gr.Column(scale=1):
pass # Right spacer
# Event handlers
send_btn.click(
execute_user_task,
inputs=[user_input, chatbot],
outputs=[chatbot, user_input]
)
user_input.submit(
execute_user_task,
inputs=[user_input, chatbot],
outputs=[chatbot, user_input]
)
clear_btn.click(
clear_history,
outputs=[chatbot, user_input]
)
if __name__ == "__main__":
# Check for API key
if not os.getenv("GROQ_API_KEY"):
print("β οΈ Warning: GROQ_API_KEY not found in environment variables")
print("Set it with: export GROQ_API_KEY='your_api_key_here'")
print("π Starting AI Web Automation Agent (Direct Implementation)...")
print("π Available browser functions:")
for name, info in AVAILABLE_FUNCTIONS.items():
print(f" - {name}: {info['description']}")
# For Hugging Face Spaces
port = int(os.getenv("PORT", 7860))
demo.launch(
server_name="0.0.0.0",
server_port=port,
share=False,
show_error=True
) |