Spaces:
Running
Running
File size: 8,843 Bytes
85e594a fcb0cf1 85e594a fcb0cf1 85e594a fcb0cf1 85e594a fcb0cf1 85e594a fcb0cf1 85e594a fcb0cf1 85e594a fcb0cf1 85e594a fcb0cf1 85e594a fcb0cf1 85e594a fcb0cf1 85e594a fcb0cf1 85e594a fcb0cf1 85e594a fcb0cf1 85e594a fcb0cf1 85e594a fcb0cf1 85e594a fcb0cf1 85e594a fcb0cf1 85e594a fcb0cf1 85e594a fcb0cf1 85e594a fcb0cf1 85e594a fcb0cf1 85e594a fcb0cf1 85e594a fcb0cf1 85e594a fcb0cf1 85e594a fcb0cf1 85e594a fcb0cf1 85e594a fcb0cf1 85e594a fcb0cf1 85e594a 481b5bc 85e594a 481b5bc fcb0cf1 85e594a |
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 |
import gradio as gr
import asyncio
import os
from typing import List, Tuple, Optional, Dict, Any
from datetime import datetime
import logging
import signal
import sys
import json
# Configure logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
try:
from mcp_use import MCPClient
from langchain_mcp_adapters.client import MultiServerMCPClient
from langchain_community.tools.sleep.tool import SleepTool
from langchain_mcp_adapters.tools import load_mcp_tools
from langchain.agents import AgentExecutor, create_tool_calling_agent
from langchain.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_mistralai import ChatMistralAI
except ImportError as e:
logger.error(f"Import error: {e}")
raise
# 🤖 Helper pour appeler un coroutine dans un contexte synchrone
def sync_run(coro):
try:
loop = asyncio.get_running_loop()
return loop.run_until_complete(coro)
except RuntimeError:
return asyncio.run(coro)
# ConversationManager reste identique
class ConversationManager:
def __init__(self, max_history_pairs: int = 3, max_context_chars: int = 2000):
self.max_history_pairs = max_history_pairs
self.max_context_chars = max_context_chars
self.session_context = {}
def update_session_context(self, action: str, result: str):
self.session_context.update({
'last_action': action,
'last_result': result[:500],
'timestamp': datetime.now().isoformat()
})
def get_optimized_history(self, full_history: List[Tuple[str, str]]) -> List[Tuple[str, str]]:
recent = full_history[-self.max_history_pairs:] if full_history else []
if self.session_context:
msg = f"[SESSION_CONTEXT] Last action: {self.session_context.get('last_action','none')}"
recent.insert(0, ("system", msg))
return recent
def get_context_summary(self) -> str:
if not self.session_context:
return "Browser session not active."
return f"Browser session active. Last action: {self.session_context.get('last_action')} at {self.session_context.get('timestamp')}"
class BrowserAgent:
def __init__(self, api_key: str):
self.api_key = api_key
self.client = None
self.session = None
self.session_context = None
self.agent_executor = None
self.model = None
self.initialized = False
self.available_tools = {}
self.system_prompt = ""
self.conversation_manager = ConversationManager()
async def generate_tools_prompt(self):
# identique à l’actuel
# …
return tools_prompt
async def get_system_prompt_with_tools(self):
base = """🌐 Browser Agent — Persistent Session & Optimized Memory
You are an intelligent browser automation agent (Playwright via MCP)...
"""
tools_section = await self.generate_tools_prompt()
return base + tools_section
async def initialize_async(self):
mistral_key = os.getenv("mistralkey")
if not mistral_key:
raise ValueError("Mistral API key missing")
self.model = ChatMistralAI(model="mistral-small-latest", api_key=mistral_key)
self.client = MultiServerMCPClient({
"browser": {
"command": "npx",
"args": ["@playwright/mcp@latest", "--browser", "chromium"],
"transport": "stdio"
}
})
self.session_context = self.client.session("browser")
self.session = await self.session_context.__aenter__()
tools = await load_mcp_tools(self.session)
tools.append(SleepTool(description="Wait 4 seconds"))
self.available_tools = {t.name: t for t in tools}
install = self.available_tools.get("browser_install")
if install:
try:
await install.arun({})
except Exception:
pass
self.system_prompt = await self.get_system_prompt_with_tools()
prompt = ChatPromptTemplate.from_messages([
("system", self.system_prompt),
MessagesPlaceholder(variable_name="chat_history"),
("human", "{input}"),
MessagesPlaceholder(variable_name="agent_scratchpad"),
])
agent = create_tool_calling_agent(
llm=self.model, tools=tools, prompt=prompt
)
self.agent_executor = AgentExecutor(
agent=agent, tools=tools, verbose=True,
max_iterations=15, early_stopping_method="generate",
handle_parsing_errors=True, return_intermediate_steps=True,
max_execution_time=180
)
self.initialized = True
return True
async def cleanup_async(self):
if self.session_context:
await self.session_context.__aexit__(None, None, None)
self.session_context = None
if self.client:
await self.client.close()
self.client = None
self.initialized = False
async def process_query_async(self, query: str, chat_history: List[Tuple[str, str]]) -> str:
opt_hist = self.conversation_manager.get_optimized_history(chat_history)
msgs = []
for h, a in opt_hist:
if h: msgs.append(("human", h))
if a: msgs.append(("ai", a))
summary = self.conversation_manager.get_context_summary()
enhanced = f"{query}\n\n[SESSION_INFO]: {summary}"
resp = await self.agent_executor.ainvoke({
"input": enhanced,
"chat_history": msgs
})
out = resp["output"]
self.conversation_manager.update_session_context(query, out)
return out
# Global
agent: Optional[BrowserAgent] = None
def initialize_agent_sync(api_key: str) -> str:
global agent
if not api_key.strip():
return "❌ Clé Mistral requise"
try:
if agent and agent.initialized:
sync_run(agent.cleanup_async())
agent = BrowserAgent(api_key)
sync_run(agent.initialize_async())
info = agent.system_prompt[:1000]
return f"✅ Agent initialisé !\n\n{info}..."
except Exception as e:
return f"❌ Échec init. {e}"
def process_message_sync(message: str, history: List[List[str]]) -> Tuple[str, List[List[str]]]:
global agent
if not agent or not agent.initialized:
err = "❌ Agent non initialisé."
history.append([message, err])
return "", history
if not message.strip():
err = "Veuillez entrer un message."
history.append([message, err])
return "", history
agent_hist = [(m[0], m[1]) for m in history]
stats_before = agent.conversation_manager.get_optimized_history(agent_hist)
try:
resp = sync_run(agent.process_query_async(message, agent_hist))
history.append([message, resp])
return "", history
except Exception as e:
err = f"❌ Erreur: {e}"
history.append([message, err])
return "", history
def get_token_stats_sync(history: List[List[str]]) -> str:
global agent
if not agent or not agent.initialized:
return "Agent non initialisé"
orig = len(history)
opt = len(agent.conversation_manager.get_optimized_history([(m[0],m[1]) for m in history]))
# tests estimés tokens
return f"📊 Paires: {orig} → {opt}"
def create_interface():
with gr.Blocks(title="MCP Browser Agent", theme=gr.themes.Soft()) as interface:
gr.Markdown("# 🌐 MCP Browser Agent")
api_input = gr.Textbox(label="Clé Mistral", type="password")
btn_init = gr.Button("Initialiser")
out_init = gr.Textbox(label="Statut", interactive=False)
btn_init.click(fn=initialize_agent_sync, inputs=[api_input], outputs=[out_init])
chatbot = gr.Chatbot(label="Conversation")
msg_input = gr.Textbox(placeholder="Écris ton message...", lines=2)
btn_send = gr.Button("Envoyer")
btn_send.click(fn=process_message_sync, inputs=[msg_input, chatbot], outputs=[msg_input, chatbot])
msg_input.submit(fn=process_message_sync, inputs=[msg_input, chatbot], outputs=[msg_input, chatbot])
btn_stats = gr.Button("Stats tokens")
out_stats = gr.Textbox(label="Token Stats", interactive=False)
btn_stats.click(fn=get_token_stats_sync, inputs=[chatbot], outputs=[out_stats])
return interface
def signal_handler(signum, frame):
if agent and agent.initialized:
sync_run(agent.cleanup_async())
sys.exit(0)
if __name__ == "__main__":
signal.signal(signal.SIGINT, signal_handler)
signal.signal(signal.SIGTERM, signal_handler)
interface = create_interface()
interface.launch(server_name="0.0.0.0", server_port=7860)
|