File size: 11,020 Bytes
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
#!/usr/bin/env python3

import gradio as gr
import os
from typing import Dict, List, Any, Optional
import json
from datetime import datetime
import traceback

# CrewAI and Julia Browser imports
from crewai import Agent, Task, Crew, Process
from crewai_tools import BaseTool
from crewai.llm import LLM
from julia_browser import AgentSDK

# Initialize browser
browser = AgentSDK()

class OpenWebsiteTool(BaseTool):
    name: str = "open_website"
    description: str = "Open a website and get page content. Input: url (string)"
    
    def _run(self, url: str) -> str:
        result = browser.open_website(url)
        return f"Opened: {result['title']} at {url}"

class ListElementsTool(BaseTool):
    name: str = "list_elements"
    description: str = "List all clickable elements and input fields on current page"
    
    def _run(self) -> str:
        elements = browser.list_elements()
        output = []
        for elem in elements.get("elements", []):
            output.append(f"[{elem['id']}] {elem['type']}: {elem.get('text', 'N/A')}")
        return f"Found {elements['total_clickable']} clickable, {elements['total_inputs']} inputs:\n" + "\n".join(output)

class ClickElementTool(BaseTool):
    name: str = "click_element"
    description: str = "Click a button or link by its number ID. Input: element_id (int)"
    
    def _run(self, element_id: int) -> str:
        result = browser.click_element(element_id)
        return f"Clicked: {result.get('element', 'Unknown')} - {result['status']}"

class TypeTextTool(BaseTool):
    name: str = "type_text"
    description: str = "Type text into an input field. Input: field_id (int), text (string)"
    
    def _run(self, field_id: int, text: str) -> str:
        result = browser.type_text(field_id, text)
        return f"Typed '{text}' into field {field_id} - {result['status']}"

class SubmitFormTool(BaseTool):
    name: str = "submit_form"
    description: str = "Submit the current form with typed data"
    
    def _run(self) -> str:
        result = browser.submit_form()
        return f"Form submitted - New page: {result.get('title', 'Unknown')}"

class GetPageInfoTool(BaseTool):
    name: str = "get_page_info"
    description: str = "Get current page title, URL, and content"
    
    def _run(self) -> str:
        info = browser.get_page_info()
        return f"Title: {info['title']}\nURL: {info['url']}\nContent: {info['content'][:300]}..."

class ScrollDownTool(BaseTool):
    name: str = "scroll_down"
    description: str = "Scroll down the page. Input: chunks (int, default 1)"
    
    def _run(self, chunks: int = 1) -> str:
        result = browser.scroll_down(chunks)
        return f"Scrolled down {chunks} chunks - Position: {result['position']}"

class ScrollUpTool(BaseTool):
    name: str = "scroll_up"
    description: str = "Scroll up the page. Input: chunks (int, default 1)"
    
    def _run(self, chunks: int = 1) -> str:
        result = browser.scroll_up(chunks)
        return f"Scrolled up {chunks} chunks - Position: {result['position']}"

class SearchPageTool(BaseTool):
    name: str = "search_page"
    description: str = "Search for text within current page. Input: term (string)"
    
    def _run(self, term: str) -> str:
        result = browser.search_page(term)
        return f"Found {result.get('matches', 0)} matches for '{term}'"

class FollowLinkTool(BaseTool):
    name: str = "follow_link"
    description: str = "Navigate to a link by its number. Input: link_id (int)"
    
    def _run(self, link_id: int) -> str:
        result = browser.follow_link(link_id)
        return f"Followed link {link_id} - Now at: {result.get('title', 'Unknown')}"

# Initialize all tools
browser_tools = [
    OpenWebsiteTool(),
    ListElementsTool(),
    ClickElementTool(),
    TypeTextTool(),
    SubmitFormTool(),
    GetPageInfoTool(),
    ScrollDownTool(),
    ScrollUpTool(),
    SearchPageTool(),
    FollowLinkTool()
]

class WebAutomationAgent:
    def __init__(self):
        # Configure Groq LLM
        self.llm = LLM(
            model="groq/qwen2.5-32b-instruct",
            api_key=os.getenv("GROQ_API_KEY")
        )
        
        # Create web automation agent
        self.agent = Agent(
            role="Web Automation Expert",
            goal="Execute web tasks using browser tools based on user instructions",
            backstory="""You are a skilled web automation expert who can navigate websites, 
            interact with elements, fill forms, and extract information. You break down 
            complex tasks into simple browser actions and execute them step by step.""",
            tools=browser_tools,
            llm=self.llm,
            verbose=True,
            allow_delegation=False
        )
    
    def execute_task(self, instruction: str) -> str:
        """Execute user task"""
        task = Task(
            description=f"""
            Task: {instruction}
            
            Use the available browser tools to complete this task:
            - open_website(url) - Open websites
            - list_elements() - See what's clickable on page
            - click_element(id) - Click buttons/links
            - type_text(field_id, text) - Fill input fields
            - submit_form() - Submit forms
            - get_page_info() - Get page details
            - scroll_down(chunks) - Scroll to see more
            - search_page(term) - Find text on page
            - follow_link(id) - Navigate to links
            
            Work step by step and explain what you're doing.
            """,
            agent=self.agent,
            expected_output="Complete step-by-step execution report with results"
        )
        
        crew = Crew(
            agents=[self.agent],
            tasks=[task],
            process=Process.sequential,
            verbose=True
        )
        
        try:
            result = crew.kickoff()
            return str(result)
        except Exception as e:
            return f"Error: {str(e)}\n{traceback.format_exc()}"

# Initialize agent
automation_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 = automation_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",
    "Navigate to a news website and find the main headline",
    "Open a shopping site and look for search functionality",
    "Visit github.com and find the login button"
]

def load_sample(task_text):
    return task_text

# 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 + CrewAI + Groq (Qwen-32B)</strong></p>
        <p>Give me web tasks in plain English!</p>
    </div>
    """)
    
    # Main chat interface - centered
    chatbot = gr.Chatbot(
        label="Agent Execution",
        height=600,
        show_copy_button=True
    )
    
    # Centered input section
    with gr.Row(elem_id="input-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,
                    elem_id="main-input"
                )
                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[:30]}...", 
                    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>πŸ’‘ Tips:</h4>
                        <ul style="font-size: 12px;">
                            <li>Specify the website URL</li>
                            <li>Describe what to click/type</li>
                            <li>Ask for information extraction</li>
                            <li>Request form interactions</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...")
    print("πŸ“ Available browser tools:")
    for tool in browser_tools:
        print(f"   - {tool.name}: {tool.description}")
    
    # For Hugging Face Spaces, use environment port if available
    port = int(os.getenv("PORT", 7860))
    
    demo.launch(
        server_name="0.0.0.0",
        server_port=port,
        share=False,
        show_error=True
    )