File size: 14,191 Bytes
be2b675
 
 
 
 
 
6cbefab
be2b675
 
6cbefab
be2b675
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6cbefab
be2b675
6cbefab
 
fe4f509
be2b675
fe4f509
 
be2b675
6cbefab
be2b675
 
 
fe4f509
 
6cbefab
 
be2b675
 
 
6cbefab
 
 
be2b675
 
 
 
 
 
 
 
 
 
 
6cbefab
be2b675
 
 
 
 
6cbefab
be2b675
6cbefab
 
be2b675
 
6cbefab
be2b675
 
6cbefab
be2b675
6cbefab
 
be2b675
6cbefab
 
 
 
 
 
 
 
 
 
 
be2b675
 
 
 
61b962d
6cbefab
 
 
 
be2b675
 
 
 
 
 
 
 
6cbefab
be2b675
 
 
 
 
 
 
6cbefab
be2b675
61b962d
be2b675
 
 
 
 
 
 
6cbefab
 
be2b675
 
6cbefab
be2b675
6cbefab
be2b675
 
6cbefab
be2b675
fe4f509
be2b675
6cbefab
be2b675
 
6cbefab
 
 
be2b675
 
 
 
6cbefab
be2b675
6cbefab
 
 
be2b675
6cbefab
 
 
 
 
 
 
 
 
 
 
 
 
 
 
be2b675
 
6cbefab
be2b675
6cbefab
be2b675
6cbefab
be2b675
6cbefab
 
be2b675
6cbefab
 
 
be2b675
 
6cbefab
be2b675
6cbefab
 
 
fe4f509
6cbefab
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
be2b675
 
 
 
 
 
 
6cbefab
be2b675
 
6cbefab
be2b675
6cbefab
 
 
 
 
 
 
 
 
 
be2b675
 
6cbefab
 
be2b675
 
 
 
6cbefab
be2b675
6cbefab
be2b675
 
6cbefab
be2b675
 
6cbefab
 
 
 
be2b675
6cbefab
be2b675
6cbefab
be2b675
6cbefab
be2b675
 
 
 
 
 
6cbefab
be2b675
 
 
 
 
6cbefab
be2b675
 
 
 
6cbefab
be2b675
6cbefab
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
import os
import subprocess
import sys
import socket
import datetime
import json
import re

# =====================================================================
# 1. STRICT /px DIRECTORY ARCHITECTURE & AUTO-BOOTSTRAPPER
# =====================================================================
BASE_DIR = "/px"
try:
    os.makedirs(BASE_DIR, exist_ok=True)
except PermissionError:
    BASE_DIR = "./px"
    os.makedirs(BASE_DIR, exist_ok=True)

GLOBAL_CONTEXT_DIR = os.path.join(BASE_DIR, "global_context")
IPS_DIR = os.path.join(BASE_DIR, "ips")
LOG_FILE = os.path.join(BASE_DIR, "server.log")

os.makedirs(GLOBAL_CONTEXT_DIR, exist_ok=True)
os.makedirs(IPS_DIR, exist_ok=True)

global_context_path = os.path.join(GLOBAL_CONTEXT_DIR, "shared_knowledge.md")
if not os.path.exists(global_context_path):
    with open(global_context_path, "w") as f:
        f.write("# Global Context & Workspace\nShared environment memory stored under /px.")

# Auto-install necessary dependencies
required_packages = ["gradio==4.44.1", "requests", "spaces", "torch", "transformers", "accelerate", "beautifulsoup4"]
for package in required_packages:
    try:
        pkg_name = package.split("==")[0]
        __import__(pkg_name)
    except ImportError:
        print(f"πŸ“¦ Installing missing dependency: {package}...")
        subprocess.check_call([sys.executable, "-m", "pip", "install", "-q", package])

import requests
import spaces
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer, TextIteratorStreamer
from threading import Thread
import gradio as gr
from bs4 import BeautifulSoup

# =====================================================================
# 2. NETWORK IP LOGGING UNDER /px
# =====================================================================
def get_ip_addresses():
    try:
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        s.connect(("8.8.8.8", 80))
        local_ip = s.getsockname()[0]
        s.close()
    except Exception:
        local_ip = "127.0.0.1"
    try:
        public_ip = requests.get("https://api.ipify.org", timeout=5).text.strip()
    except Exception:
        public_ip = "Online Server Space"
    return local_ip, public_ip

local_ip, server_public_ip = get_ip_addresses()
timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
with open(LOG_FILE, "a") as log_file:
    log_file.write(f"[{timestamp}] - Web Chat Online | Server IP: {server_public_ip}\n")

print("\n" + "═" * 65)
print(f"🌐 PX TECH SOLUTIONS - EVAL BETA 0.2 (AGENTIC ZEROGPU CORE)")
print(f"   β€’ Base Directory : {BASE_DIR}")
print(f"   β€’ Server IP      : {server_public_ip}")
print("═" * 65 + "\n")

# =====================================================================
# 3. ZEROGPU-COMPATIBLE MODEL INITIALIZATION
# =====================================================================
MODEL_ID = "HuggingFaceTB/SmolLM2-1.7B-Instruct"
print(f"🧠 Loading AI Model ({MODEL_ID})...")

tokenizer = AutoTokenizer.from_pretrained(MODEL_ID)
# In ZeroGPU, device_map="auto" allows the framework to dynamically shift tensors
model = AutoModelForCausalLM.from_pretrained(
    MODEL_ID,
    torch_dtype=torch.float16 if torch.cuda.is_available() else torch.float32,
    device_map="auto"
)

# =====================================================================
# 4. ROBUST AGENTIC TOOLS SUITE
# =====================================================================
def execute_tool(name, args, ip_folder, yolo_mode):
    try:
        if name == "bash":
            cmd = args.get("command", "")
            if not yolo_mode and any(danger in cmd for danger in ["rm -rf /", "mkfs", "dd if="]):
                return "❌ Security Error: Command blocked by YOLO Security Sandbox."
            res = subprocess.run(cmd, shell=True, capture_output=True, text=True, cwd=ip_folder, timeout=45)
            output = res.stdout if res.returncode == 0 else f"STDOUT:\n{res.stdout}\nSTDERR:\n{res.stderr}"
            return output[:8000] if output else "βœ… Command executed successfully (No output)."

        elif name == "write_file":
            filepath = args.get("filepath")
            content = args.get("content")
            safe_path = os.path.join(ip_folder, filepath) if not os.path.isabs(filepath) else filepath
            os.makedirs(os.path.dirname(safe_path), exist_ok=True)
            with open(safe_path, "w", encoding="utf-8") as f:
                f.write(content)
            return f"βœ… File successfully written to {safe_path}"

        elif name == "read_file":
            filepath = args.get("filepath")
            safe_path = os.path.join(ip_folder, filepath) if not os.path.isabs(filepath) else filepath
            if not os.path.exists(safe_path) and os.path.exists(filepath):
                safe_path = filepath
            if not os.path.exists(safe_path):
                return f"❌ Error: File '{filepath}' not found."
            with open(safe_path, "r", encoding="utf-8", errors="ignore") as f:
                return f.read()[:15000]

        elif name == "web_search":
            query = args.get("query")
            url = f"https://html.duckduckgo.com/html/?q={requests.utils.quote(query)}"
            headers = {"User-Agent": "Mozilla/5.0"}
            resp = requests.get(url, headers=headers, timeout=10)
            soup = BeautifulSoup(resp.text, "html.parser")
            results = [a.get_text(strip=True) for a in soup.find_all("a", class_="result__snippet", limit=5)]
            return "\n\n".join(results) if results else "❌ No web search results retrieved."

        else:
            return f"❌ Error: Unknown tool '{name}'"
    except Exception as e:
        return f"❌ Tool execution failed: {str(e)}"

# =====================================================================
# 5. MULTI-TURN AGENTIC INFERENCE LOOP (ZeroGPU Decorated)
# =====================================================================
@spaces.GPU(duration=120)
def predict(message, history, system_prompt, temperature, yolo_mode, request: gr.Request):
    # Extract & isolate client IP
    client_ip = "127.0.0.1"
    if request and request.headers:
        client_ip = request.headers.get("x-forwarded-for", client_ip).split(",")[0].strip()
    elif request and request.client:
        client_ip = request.client.host

    safe_ip = client_ip.replace(":", "_").replace(".", "_")
    ip_folder = os.path.join(IPS_DIR, safe_ip)
    os.makedirs(ip_folder, exist_ok=True)
    history_log_path = os.path.join(ip_folder, "chat_history.log")

    # Global context & YOLO setup
    security_mode = "DISABLED (GOD-MODE)" if yolo_mode else "ENABLED (SECURE)"
    global_text = open(global_context_path, "r").read() if os.path.exists(global_context_path) else ""

    agent_instructions = """
You have access to powerful tools. To use a tool, you MUST output a JSON block wrapped in <tool_call> tags:

<tool_call>
{"name": "bash", "arguments": {"command": "ls -la"}}
</tool_call>

Available Tools:
1. bash - Execute shell commands. Args: {"command": "<command string>"}
2. write_file - Write to a file. Args: {"filepath": "<path>", "content": "<string>"}
3. read_file - Read a file. Args: {"filepath": "<path>"}
4. web_search - Search the live web. Args: {"query": "<search query>"}

If you use a tool, wait for the <tool_response> before continuing.
"""

    if yolo_mode:
        yolo_instructions = "\nπŸ”₯ [YOLO MODE: ON] Absolute root permissions granted. Execute shell scripts and downloads freely under /px."
    else:
        yolo_instructions = "\nπŸ”’ [YOLO MODE: OFF] Standard security limits apply."

    enhanced_system_prompt = f"{system_prompt}\n{agent_instructions}\n{yolo_instructions}\n\n--- [GLOBAL CONTEXT] ---\n{global_text}\n\n--- [USER IP CONTEXT] ---\nClient IP: {client_ip} | Folder: {ip_folder} | Security: {security_mode}"

    # Build chat history
    chat_messages = [{"role": "system", "content": enhanced_system_prompt}]
    for human, assistant in history:
        chat_messages.append({"role": "user", "content": human})
        chat_messages.append({"role": "assistant", "content": assistant})
    chat_messages.append({"role": "user", "content": message})

    with open(history_log_path, "a") as hf:
        hf.write(f"[{timestamp}] User (YOLO: {yolo_mode}): {message}\n")

    # Autonomous Multi-Step Loop
    max_turns = 4
    turn = 0
    accumulated_output = ""

    while turn < max_turns:
        turn += 1
        prompt = tokenizer.apply_chat_template(chat_messages, tokenize=False, add_generation_prompt=True)
        inputs = tokenizer(prompt, return_tensors="pt").to(model.device)

        streamer = TextIteratorStreamer(tokenizer, skip_prompt=True, skip_special_tokens=True)
        generation_kwargs = dict(
            **inputs,
            streamer=streamer,
            max_new_tokens=1024,
            temperature=max(temperature, 0.01),
            do_sample=True if temperature > 0 else False
        )

        thread = Thread(target=model.generate, kwargs=generation_kwargs)
        thread.start()

        turn_response = ""
        for new_text in streamer:
            turn_response += new_text
            yield accumulated_output + turn_response

        accumulated_output += turn_response
        chat_messages.append({"role": "assistant", "content": turn_response})

        # Intercept and Parse Tool Calls
        tool_call_match = re.search(r'<tool_call>(.*?)</tool_call>', turn_response, re.DOTALL)
        if tool_call_match:
            try:
                tool_json = json.loads(tool_call_match.group(1).strip())
                func_name = tool_json.get("name")
                func_args = tool_json.get("arguments", {})

                yield accumulated_output + f"\n\nβš™οΈ `[Executing Tool: {func_name} | Args: {json.dumps(func_args)}]`...\n"
                
                # Run Tool
                tool_result = execute_tool(func_name, func_args, ip_folder, yolo_mode)
                
                # Feedback loop
                tool_feedback = f"\n<tool_response>\n{tool_result}\n</tool_response>\n"
                accumulated_output += tool_feedback
                yield accumulated_output
                
                chat_messages.append({"role": "user", "content": f"System Tool Output:\n{tool_result}\nAnalyze this and continue your task."})
            except Exception as e:
                error_fb = f"\n<tool_response>Error parsing JSON: {str(e)}</tool_response>\n"
                accumulated_output += error_fb
                chat_messages.append({"role": "user", "content": error_fb})
                yield accumulated_output
        else:
            # If no tools were called, the agent has finished responding.
            break

    with open(history_log_path, "a") as hf:
        hf.write(f"[{timestamp}] Assistant: {accumulated_output}\n")

def update_global_context(new_content):
    with open(global_context_path, "w") as f:
        f.write(new_content)
    return "βœ… Global context updated successfully under /px!"

# =====================================================================
# 6. GRADIO WEB INTERFACE (CYBER-DARK STYLING)
# =====================================================================
custom_css = """
body { background-color: #0b0f19; color: #e2e8f0; font-family: 'Inter', sans-serif; }
.gradio-container { max-width: 1100px !important; margin: auto; padding-top: 20px; }
.gr-button-primary { background: linear-gradient(90deg, #3b82f6, #06b6d4) !important; border: none !important; }
.dark \.gr-panel { background-color: #111827 !important; border: 1px solid #1f2937 !important; }
code { color: #22d3ee !important; background-color: #1e293b !important; padding: 2px 6px; border-radius: 4px; }
pre code { color: #f8fafc !important; }
"""

with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="cyan"), css=custom_css) as demo:
    gr.Markdown(
        """
        # ⚑ EVAL BETA 0.2 | PX TECH SOLUTIONS (AGENTIC ZEROGPU CORE)
        ### Autonomous Web Environment with ZeroGPU AI, Full Tool-Use, & YOLO God-Mode
        """
    )
    
    with gr.Row():
        gr.Markdown("🟒 **Status:** ZeroGPU Online | ⏱️ **Slot 1 Queue Active** | πŸ› οΈ **Agent Loop:** Bash, Write, Read, Web Search")

    with gr.Accordion("βš™οΈ Engine Configuration, Global Context & Security", open=False):
        system_prompt_input = gr.Textbox(
            label="System Environment Prompt",
            value="You are EVAL BETA 0.2, an elite autonomous programming agent created by Pripro / PX TECH SOLUTIONS. Think logically and step-by-step.",
            lines=2
        )
        with gr.Row():
            temperature_slider = gr.Slider(minimum=0.0, maximum=1.0, value=0.3, step=0.05, label="Creativity / Temperature")
            yolo_checkbox = gr.Checkbox(label="πŸ”₯ Enable YOLO Mode (Disables Sandbox Security)", value=False)
            
        global_context_view = gr.Textbox(
            label=f"🌍 Shared Global Memory Workspace ({global_context_path})",
            value=open(global_context_path).read() if os.path.exists(global_context_path) else "",
            lines=4
        )
        update_btn = gr.Button("πŸ’Ύ Save Global Context Changes", variant="primary")
        update_status = gr.Textbox(label="Workspace Status", interactive=False)
        update_btn.click(update_global_context, inputs=[global_context_view], outputs=[update_status])

    gr.ChatInterface(
        fn=predict,
        additional_inputs=[system_prompt_input, temperature_slider, yolo_checkbox],
        textbox=gr.Textbox(placeholder="Instruct the AI: 'Search the web for Python 3.12 features', 'Write a script and run it via bash'...", container=False, scale=7)
    )
    
    gr.Markdown(
        """
        ---
        **Architecture & Security:** Enforces a strict queue (`concurrency_limit=1`) with Slot 1 allocation. All workspace data, logs, and IPs are securely sandboxed under the root `/px` architecture.
        """
    )

if __name__ == "__main__":
    print(f"πŸš€ Launching Agentic ZeroGPU chat server on port 7860...")
    demo.queue(default_concurrency_limit=1, max_size=20)
    demo.launch(server_name="0.0.0.0", server_port=7860)