mirror of
https://github.com/tcsenpai/agenticSeek.git
synced 2025-06-05 02:25:27 +00:00
feat : small frontend improvement & planner auto fix
This commit is contained in:
parent
14f42b638d
commit
a32cf60958
6
app.py
Normal file → Executable file
6
app.py
Normal file → Executable file
@ -22,12 +22,15 @@ config.read('config.ini')
|
|||||||
app = FastAPI(title="AgenticSeek API", version="0.1.0")
|
app = FastAPI(title="AgenticSeek API", version="0.1.0")
|
||||||
logger = Logger("backend.log")
|
logger = Logger("backend.log")
|
||||||
|
|
||||||
|
if not os.path.exists(".screenshots"):
|
||||||
|
os.makedirs(".screenshots")
|
||||||
|
|
||||||
app.mount("/screenshots", StaticFiles(directory=".screenshots"), name="screenshots")
|
app.mount("/screenshots", StaticFiles(directory=".screenshots"), name="screenshots")
|
||||||
|
|
||||||
# Add CORS middleware to allow frontend requests
|
# Add CORS middleware to allow frontend requests
|
||||||
app.add_middleware(
|
app.add_middleware(
|
||||||
CORSMiddleware,
|
CORSMiddleware,
|
||||||
allow_origins=["http://localhost:3000"],
|
allow_origins=["*"],
|
||||||
allow_credentials=True,
|
allow_credentials=True,
|
||||||
allow_methods=["*"],
|
allow_methods=["*"],
|
||||||
allow_headers=["*"],
|
allow_headers=["*"],
|
||||||
@ -139,7 +142,6 @@ async def process_query(request: QueryRequest):
|
|||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
interaction.tts_enabled = request.tts_enabled
|
interaction.tts_enabled = request.tts_enabled
|
||||||
interaction.stt_enabled = request.stt_enabled
|
|
||||||
interaction.last_query = request.query
|
interaction.last_query = request.query
|
||||||
logger.info("Agents request is being processed")
|
logger.info("Agents request is being processed")
|
||||||
is_generating = True
|
is_generating = True
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
version: '3.8'
|
version: '3'
|
||||||
|
|
||||||
services:
|
services:
|
||||||
redis:
|
redis:
|
||||||
@ -30,10 +30,12 @@ services:
|
|||||||
- "8080:8080"
|
- "8080:8080"
|
||||||
volumes:
|
volumes:
|
||||||
- ./searxng:/etc/searxng:rw
|
- ./searxng:/etc/searxng:rw
|
||||||
|
- ./searxng/entrypoint.sh:/usr/local/bin/searxng-entrypoint.sh:ro
|
||||||
|
entrypoint: /usr/local/bin/searxng-entrypoint.sh
|
||||||
environment:
|
environment:
|
||||||
- SEARXNG_BASE_URL=http://localhost:8080/
|
- SEARXNG_BASE_URL=http://localhost:8080/
|
||||||
- UWSGI_WORKERS=1
|
- UWSGI_WORKERS=4
|
||||||
- UWSGI_THREADS=1
|
- UWSGI_THREADS=4
|
||||||
cap_add:
|
cap_add:
|
||||||
- CHOWN
|
- CHOWN
|
||||||
- SETGID
|
- SETGID
|
||||||
|
@ -45,7 +45,7 @@
|
|||||||
gap: 40px;
|
gap: 40px;
|
||||||
height: calc(100vh - 200px);
|
height: calc(100vh - 200px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.left-panel,
|
.left-panel,
|
||||||
.right-panel {
|
.right-panel {
|
||||||
background-color: #1a1a1a;
|
background-color: #1a1a1a;
|
||||||
@ -54,6 +54,7 @@
|
|||||||
box-shadow: 0 0 10px rgba(0, 255, 204, 0.2);
|
box-shadow: 0 0 10px rgba(0, 255, 204, 0.2);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.left-panel {
|
.left-panel {
|
||||||
@ -66,6 +67,7 @@
|
|||||||
padding: 10px;
|
padding: 10px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
max-height: 100%;
|
||||||
gap: 15px;
|
gap: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,6 +83,11 @@
|
|||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
font-size: 0.9rem;
|
font-size: 0.9rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.messages::-webkit-scrollbar,
|
||||||
|
.content::-webkit-scrollbar {
|
||||||
|
width: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
.user-message {
|
.user-message {
|
||||||
background-color: #00ffcc;
|
background-color: #00ffcc;
|
||||||
@ -189,7 +196,7 @@
|
|||||||
.content {
|
.content {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
padding: 10px;
|
padding: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.blocks {
|
.blocks {
|
||||||
@ -200,7 +207,7 @@
|
|||||||
|
|
||||||
.block {
|
.block {
|
||||||
background-color: #222;
|
background-color: #222;
|
||||||
padding: 15px;
|
padding: 10px;
|
||||||
border: 1px solid #00ffcc;
|
border: 1px solid #00ffcc;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
@ -214,7 +221,7 @@
|
|||||||
|
|
||||||
.block pre {
|
.block pre {
|
||||||
background-color: #111;
|
background-color: #111;
|
||||||
padding: 10px;
|
padding: 5px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
font-size: 0.85rem;
|
font-size: 0.85rem;
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
@ -222,7 +229,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.screenshot {
|
.screenshot {
|
||||||
margin-top: 20px;
|
margin-top: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.screenshot img {
|
.screenshot img {
|
||||||
|
@ -9,45 +9,111 @@ function App() {
|
|||||||
const [error, setError] = useState(null);
|
const [error, setError] = useState(null);
|
||||||
const [currentView, setCurrentView] = useState('blocks');
|
const [currentView, setCurrentView] = useState('blocks');
|
||||||
const [responseData, setResponseData] = useState(null);
|
const [responseData, setResponseData] = useState(null);
|
||||||
|
const [isOnline, setIsOnline] = useState(false); // Added state
|
||||||
const messagesEndRef = useRef(null);
|
const messagesEndRef = useRef(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
scrollToBottom();
|
scrollToBottom();
|
||||||
}, [messages]);
|
}, [messages]);
|
||||||
|
|
||||||
|
// Added: checks /health
|
||||||
|
const checkHealth = async () => {
|
||||||
|
try {
|
||||||
|
await axios.get('http://0.0.0.0:8000/health');
|
||||||
|
setIsOnline(true);
|
||||||
|
console.log('System is online');
|
||||||
|
} catch {
|
||||||
|
setIsOnline(false);
|
||||||
|
console.log('System is offline');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const scrollToBottom = () => {
|
const scrollToBottom = () => {
|
||||||
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
|
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (currentView === 'screenshot') {
|
||||||
|
let isMounted = true;
|
||||||
|
|
||||||
|
const fetchScreenshot = async () => {
|
||||||
|
try {
|
||||||
|
const res = await axios.get('http://0.0.0.0:8000/screenshots/updated_screen.png', {
|
||||||
|
responseType: 'blob',
|
||||||
|
params: { t: new Date().getTime() }
|
||||||
|
});
|
||||||
|
if (isMounted) {
|
||||||
|
console.log('Screenshot fetched successfully');
|
||||||
|
const imageUrl = URL.createObjectURL(res.data);
|
||||||
|
setResponseData((prev) => {
|
||||||
|
if (prev?.screenshot && prev.screenshot !== 'placeholder.png') {
|
||||||
|
URL.revokeObjectURL(prev.screenshot);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...prev,
|
||||||
|
screenshot: imageUrl,
|
||||||
|
screenshotTimestamp: new Date().getTime()
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error fetching screenshot:', err);
|
||||||
|
if (isMounted) {
|
||||||
|
setResponseData((prev) => ({
|
||||||
|
...prev,
|
||||||
|
screenshot: 'placeholder.png',
|
||||||
|
screenshotTimestamp: new Date().getTime()
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fetchScreenshot();
|
||||||
|
const interval = setInterval(fetchScreenshot, 1000);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
isMounted = false;
|
||||||
|
clearInterval(interval);
|
||||||
|
if (responseData?.screenshot && responseData.screenshot !== 'placeholder.png') {
|
||||||
|
URL.revokeObjectURL(responseData.screenshot);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}, [currentView]);
|
||||||
|
|
||||||
const handleSubmit = async (e) => {
|
const handleSubmit = async (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (!query.trim()) return;
|
checkHealth();
|
||||||
|
if (!query.trim()) {
|
||||||
|
console.log('Empty query');
|
||||||
|
return;
|
||||||
|
}
|
||||||
setMessages((prev) => [...prev, { type: 'user', content: query }]);
|
setMessages((prev) => [...prev, { type: 'user', content: query }]);
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
setError(null);
|
setError(null);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
//const res = await axios.post('http://backend:8000/query', { ... });
|
console.log('Sending query:', query);
|
||||||
const res = await axios.post('${process.env.BACKEND_URL}/query', {
|
const res = await axios.post('http://0.0.0.0:8000/query', {
|
||||||
query,
|
query,
|
||||||
lang: 'en',
|
tts_enabled: false
|
||||||
tts_enabled: false,
|
|
||||||
stt_enabled: false,
|
|
||||||
});
|
});
|
||||||
|
console.log('Response:', res.data);
|
||||||
const data = res.data;
|
const data = res.data;
|
||||||
setResponseData(data);
|
setResponseData(data);
|
||||||
setMessages((prev) => [
|
setMessages((prev) => [
|
||||||
...prev,
|
...prev,
|
||||||
{ type: 'agent', content: data.answer, agentName: data.agent_name },
|
{ type: 'agent', content: data.answer, agentName: data.agent_name },
|
||||||
]);
|
]);
|
||||||
setCurrentView('blocks');
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
console.error('Error:', err);
|
||||||
setError('Failed to process query.');
|
setError('Failed to process query.');
|
||||||
setMessages((prev) => [
|
setMessages((prev) => [
|
||||||
...prev,
|
...prev,
|
||||||
{ type: 'error', content: 'Error: Unable to get a response.' },
|
{ type: 'error', content: 'Error: Unable to get a response.' },
|
||||||
]);
|
]);
|
||||||
} finally {
|
} finally {
|
||||||
|
console.log('Query completed');
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
setQuery('');
|
setQuery('');
|
||||||
}
|
}
|
||||||
@ -55,11 +121,12 @@ function App() {
|
|||||||
|
|
||||||
const handleGetScreenshot = async () => {
|
const handleGetScreenshot = async () => {
|
||||||
try {
|
try {
|
||||||
//const res = await axios.get('http://backend:8000/screenshots/updated_screen.png');
|
console.log('Fetching screenshot...');
|
||||||
const res = await axios.get('${process.env.BACKEND_URL}/screenshots/updated_screen.png');
|
const res = await axios.get('http://0.0.0.0:8000/screenshots/updated_screen.png');
|
||||||
setResponseData((prev) => ({ ...prev, screenshot: res.data.screenshot }));
|
setResponseData((prev) => ({ ...prev, screenshot: res.data.screenshot }));
|
||||||
setCurrentView('screenshot');
|
setCurrentView('screenshot');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
console.error('Error fetching screenshot:', err);
|
||||||
setError('Browser not in use');
|
setError('Browser not in use');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -73,7 +140,7 @@ function App() {
|
|||||||
<div className="chat-container">
|
<div className="chat-container">
|
||||||
<div className="left-panel">
|
<div className="left-panel">
|
||||||
<h2>C H A T</h2>
|
<h2>C H A T</h2>
|
||||||
<br></br>
|
<br />
|
||||||
<div className="messages">
|
<div className="messages">
|
||||||
{messages.length === 0 ? (
|
{messages.length === 0 ? (
|
||||||
<p className="placeholder">No messages yet. Type below to start!</p>
|
<p className="placeholder">No messages yet. Type below to start!</p>
|
||||||
@ -98,7 +165,8 @@ function App() {
|
|||||||
)}
|
)}
|
||||||
<div ref={messagesEndRef} />
|
<div ref={messagesEndRef} />
|
||||||
</div>
|
</div>
|
||||||
{isLoading && <div className="loading-animation">Loading...</div>}
|
{isOnline && isLoading && <div className="loading-animation">Thinking...</div>}
|
||||||
|
{!isLoading && !isOnline && <p className="loading-animation">System offline. Deploy backend first.</p>}
|
||||||
<form onSubmit={handleSubmit} className="input-form">
|
<form onSubmit={handleSubmit} className="input-form">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
@ -114,7 +182,7 @@ function App() {
|
|||||||
</div>
|
</div>
|
||||||
<div className="right-panel">
|
<div className="right-panel">
|
||||||
<h2>I N T E R F A C E</h2>
|
<h2>I N T E R F A C E</h2>
|
||||||
<br></br>
|
<br />
|
||||||
<div className="view-selector">
|
<div className="view-selector">
|
||||||
<button
|
<button
|
||||||
className={currentView === 'blocks' ? 'active' : ''}
|
className={currentView === 'blocks' ? 'active' : ''}
|
||||||
@ -153,7 +221,15 @@ function App() {
|
|||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="screenshot">
|
<div className="screenshot">
|
||||||
<img src="${process.env.BACKEND_URL}/screenshots/updated_screen.png" alt="Screenshot" />
|
<img
|
||||||
|
src={responseData?.screenshot || 'placeholder.png'}
|
||||||
|
alt="Screenshot"
|
||||||
|
onError={(e) => {
|
||||||
|
e.target.src = 'placeholder.png';
|
||||||
|
console.error('Failed to load screenshot');
|
||||||
|
}}
|
||||||
|
key={responseData?.screenshotTimestamp || 'default'} // Force re-render
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -45,6 +45,7 @@ rules:
|
|||||||
- Do not ever use editor such as vim or nano.
|
- Do not ever use editor such as vim or nano.
|
||||||
- Make sure to always cd your work folder before executing commands, like cd <work dir> && <your command>
|
- Make sure to always cd your work folder before executing commands, like cd <work dir> && <your command>
|
||||||
- only use file name with file_finder, not path
|
- only use file name with file_finder, not path
|
||||||
|
- If query is unrelated to file operations, do nothing, and say that there was mistake in agent allocation.
|
||||||
|
|
||||||
Example Interaction
|
Example Interaction
|
||||||
User: "I need to find the file config.txt and read its contents."
|
User: "I need to find the file config.txt and read its contents."
|
||||||
|
@ -55,6 +55,7 @@ rules:
|
|||||||
- Make sure to always cd your work folder before executing commands, like cd <work dir> && <your command>
|
- Make sure to always cd your work folder before executing commands, like cd <work dir> && <your command>
|
||||||
- Do not ever use editor such as vim or nano.
|
- Do not ever use editor such as vim or nano.
|
||||||
- only use file name with file_finder, not path
|
- only use file name with file_finder, not path
|
||||||
|
- If query is unrelated to file operations, do nothing, and say that there was mistake in agent allocation.
|
||||||
|
|
||||||
Example Interaction
|
Example Interaction
|
||||||
User: "I need to find the file config.txt and read its contents."
|
User: "I need to find the file config.txt and read its contents."
|
||||||
|
9
searxng/entrypoint.sh
Executable file
9
searxng/entrypoint.sh
Executable file
@ -0,0 +1,9 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# entrypoint for docker searxng service
|
||||||
|
|
||||||
|
SECRET_KEY=$(openssl rand -hex 32)
|
||||||
|
|
||||||
|
sed -i "s/ultrasecretkey/$SECRET_KEY/g" /etc/searxng/settings.yml
|
||||||
|
|
||||||
|
exec /usr/local/searxng/dockerfiles/docker-entrypoint.sh "$@"
|
@ -5,11 +5,11 @@ gid = searxng
|
|||||||
|
|
||||||
# Number of workers (usually CPU count)
|
# Number of workers (usually CPU count)
|
||||||
# default value: %k (= number of CPU core, see Dockerfile)
|
# default value: %k (= number of CPU core, see Dockerfile)
|
||||||
workers = 1
|
workers = 4
|
||||||
|
|
||||||
# Number of threads per worker
|
# Number of threads per worker
|
||||||
# default value: 4 (see Dockerfile)
|
# default value: 4 (see Dockerfile)
|
||||||
threads = 1
|
threads = 4
|
||||||
|
|
||||||
# The right granted on the created socket
|
# The right granted on the created socket
|
||||||
chmod-socket = 666
|
chmod-socket = 666
|
||||||
@ -19,7 +19,7 @@ single-interpreter = true
|
|||||||
master = true
|
master = true
|
||||||
plugin = python3
|
plugin = python3
|
||||||
lazy-apps = true
|
lazy-apps = true
|
||||||
enable-threads = 1
|
enable-threads = 4
|
||||||
|
|
||||||
# Module to import
|
# Module to import
|
||||||
module = searx.webapp
|
module = searx.webapp
|
||||||
@ -49,4 +49,4 @@ die-on-term
|
|||||||
# uwsgi serves the static files
|
# uwsgi serves the static files
|
||||||
static-map = /static=/usr/local/searxng/searx/static
|
static-map = /static=/usr/local/searxng/searx/static
|
||||||
static-gzip-all = True
|
static-gzip-all = True
|
||||||
offload-threads = 1
|
offload-threads = 4
|
2731
searxng/settings.yml.new
Normal file
2731
searxng/settings.yml.new
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,72 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Script to automate SearXNG setup and deployment with Docker Compose
|
||||||
|
|
||||||
|
command_exists() {
|
||||||
|
command -v "$1" &> /dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check if Docker is installed
|
||||||
|
if ! command_exists docker; then
|
||||||
|
echo "Error: Docker is not installed. Please install Docker first."
|
||||||
|
echo "On Ubuntu: sudo apt install docker.io"
|
||||||
|
echo "On macOS/Windows: Install Docker Desktop from https://www.docker.com/get-started/"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if Docker daemon is running
|
||||||
|
echo "Checking if Docker daemon is running..."
|
||||||
|
if ! docker info &> /dev/null; then
|
||||||
|
echo "Error: Docker daemon is not running or inaccessible."
|
||||||
|
if [ "$(uname)" = "Linux" ]; then
|
||||||
|
echo "Trying to start Docker service (may require sudo)..."
|
||||||
|
if sudo systemctl start docker &> /dev/null; then
|
||||||
|
echo "Docker started successfully."
|
||||||
|
else
|
||||||
|
echo "Failed to start Docker. Possible issues:"
|
||||||
|
echo "1. Run this script with sudo: sudo bash setup_searxng.sh"
|
||||||
|
echo "2. Check Docker installation: sudo systemctl status docker"
|
||||||
|
echo "3. Add your user to the docker group: sudo usermod -aG docker $USER (then log out and back in)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "Please start Docker manually:"
|
||||||
|
echo "- On macOS/Windows: Open Docker Desktop."
|
||||||
|
echo "- On Linux: Run 'sudo systemctl start docker' or check your distro's docs."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "Docker daemon is running."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if Docker Compose is installed
|
||||||
|
if ! command_exists docker-compose; then
|
||||||
|
echo "Error: Docker Compose is not installed. Please install it first."
|
||||||
|
echo "On Ubuntu: sudo apt install docker-compose"
|
||||||
|
echo "Or via pip: pip install docker-compose"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create a directory for SearXNG config if it doesn’t exist
|
||||||
|
mkdir -p searxng
|
||||||
|
cd . || exit
|
||||||
|
|
||||||
|
# Check if docker-compose.yml exists
|
||||||
|
if [ ! -f "docker-compose.yml" ]; then
|
||||||
|
echo "Error: docker-compose.yml not found in the current directory."
|
||||||
|
echo "Please create it before running this script."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Start containers to generate initial config files
|
||||||
|
echo "Starting containers for initial setup..."
|
||||||
|
if ! docker-compose up -d; then
|
||||||
|
echo "Error: Failed to start containers. Check Docker logs with 'docker compose logs'."
|
||||||
|
echo "Possible fixes: Run with sudo or ensure port 8080 is free."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
sleep 10
|
||||||
|
|
||||||
# Generate a secret key and update settings
|
# Generate a secret key and update settings
|
||||||
SECRET_KEY=$(openssl rand -hex 32)
|
SECRET_KEY=$(openssl rand -hex 32)
|
||||||
if [ -f "searxng/settings.yml" ]; then
|
if [ -f "searxng/settings.yml" ]; then
|
||||||
@ -16,4 +83,9 @@ else
|
|||||||
echo "Error: settings.yml not found. Initial setup may have failed."
|
echo "Error: settings.yml not found. Initial setup may have failed."
|
||||||
docker-compose logs searxng
|
docker-compose logs searxng
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Display status and access instructions
|
||||||
|
echo "SearXNG setup complete!"
|
||||||
|
docker ps -a --filter "name=searxng" --filter "name=redis"
|
||||||
|
echo "Access SearXNG at: http://localhost:8080"
|
@ -9,7 +9,7 @@ workers = 1
|
|||||||
|
|
||||||
# Number of threads per worker
|
# Number of threads per worker
|
||||||
# default value: 4 (see Dockerfile)
|
# default value: 4 (see Dockerfile)
|
||||||
enable-threads = false
|
enable-threads = true
|
||||||
threads = 1
|
threads = 1
|
||||||
|
|
||||||
# The right granted on the created socket
|
# The right granted on the created socket
|
||||||
@ -20,7 +20,7 @@ single-interpreter = true
|
|||||||
master = true
|
master = true
|
||||||
plugin = python3
|
plugin = python3
|
||||||
lazy-apps = true
|
lazy-apps = true
|
||||||
enable-threads = 1
|
enable-threads = 4
|
||||||
|
|
||||||
# Module to import
|
# Module to import
|
||||||
module = searx.webapp
|
module = searx.webapp
|
||||||
@ -50,4 +50,4 @@ die-on-term
|
|||||||
# uwsgi serves the static files
|
# uwsgi serves the static files
|
||||||
static-map = /static=/usr/local/searxng/searx/static
|
static-map = /static=/usr/local/searxng/searx/static
|
||||||
static-gzip-all = True
|
static-gzip-all = True
|
||||||
offload-threads = 2
|
offload-threads = 4
|
||||||
|
@ -76,8 +76,7 @@ class PlannerAgent(Agent):
|
|||||||
"""
|
"""
|
||||||
return prompt
|
return prompt
|
||||||
|
|
||||||
def show_plan(self, answer: dict) -> None:
|
def show_plan(self, agents_tasks: dict, answer: str) -> None:
|
||||||
agents_tasks = self.parse_agent_tasks(answer)
|
|
||||||
if agents_tasks == (None, None):
|
if agents_tasks == (None, None):
|
||||||
pretty_print(answer, color="warning")
|
pretty_print(answer, color="warning")
|
||||||
pretty_print("Failed to make a plan. This can happen with (too) small LLM. Clarify your request and insist on it making a plan within ```json.", color="failure")
|
pretty_print("Failed to make a plan. This can happen with (too) small LLM. Clarify your request and insist on it making a plan within ```json.", color="failure")
|
||||||
@ -94,12 +93,13 @@ class PlannerAgent(Agent):
|
|||||||
animate_thinking("Thinking...", color="status")
|
animate_thinking("Thinking...", color="status")
|
||||||
self.memory.push('user', prompt)
|
self.memory.push('user', prompt)
|
||||||
answer, _ = self.llm_request()
|
answer, _ = self.llm_request()
|
||||||
self.show_plan(answer)
|
agents_tasks = self.parse_agent_tasks(answer)
|
||||||
ok_str = input("Is the plan ok? (y/n): ")
|
if agents_tasks == (None, None):
|
||||||
if ok_str == 'y':
|
prompt = f"Failed to parse the tasks. Please make a plan within ```json.\n"
|
||||||
ok = True
|
pretty_print("Failed to make plan. Retrying...", color="warning")
|
||||||
else:
|
continue
|
||||||
prompt = input("Please reformulate: ")
|
self.show_plan(agents_tasks, answer)
|
||||||
|
ok = True
|
||||||
return answer
|
return answer
|
||||||
|
|
||||||
def start_agent_process(self, task: str, required_infos: dict | None) -> str:
|
def start_agent_process(self, task: str, required_infos: dict | None) -> str:
|
||||||
|
@ -123,16 +123,25 @@ class Browser:
|
|||||||
self.js_scripts_folder = "./sources/web_scripts/" if not __name__ == "__main__" else "./web_scripts/"
|
self.js_scripts_folder = "./sources/web_scripts/" if not __name__ == "__main__" else "./web_scripts/"
|
||||||
self.anticaptcha = "https://chrome.google.com/webstore/detail/nopecha-captcha-solver/dknlfmjaanfblgfdfebhijalfmhmjjjo/related"
|
self.anticaptcha = "https://chrome.google.com/webstore/detail/nopecha-captcha-solver/dknlfmjaanfblgfdfebhijalfmhmjjjo/related"
|
||||||
self.logger = Logger("browser.log")
|
self.logger = Logger("browser.log")
|
||||||
|
self.screenshot_folder = os.path.join(os.getcwd(), ".screenshots")
|
||||||
|
self.tabs = []
|
||||||
try:
|
try:
|
||||||
self.driver = driver
|
self.driver = driver
|
||||||
self.wait = WebDriverWait(self.driver, 10)
|
self.wait = WebDriverWait(self.driver, 10)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise Exception(f"Failed to initialize browser: {str(e)}")
|
raise Exception(f"Failed to initialize browser: {str(e)}")
|
||||||
self.screenshot_folder = os.path.join(os.getcwd(), ".screenshots")
|
self.setup_tabs()
|
||||||
self.screenshot()
|
|
||||||
self.driver.get("https://www.google.com")
|
|
||||||
if anticaptcha_manual_install:
|
if anticaptcha_manual_install:
|
||||||
self.load_anticatpcha_manually()
|
self.load_anticatpcha_manually()
|
||||||
|
|
||||||
|
def setup_tabs(self):
|
||||||
|
self.tabs = self.driver.window_handles
|
||||||
|
self.driver.get("https://www.google.com")
|
||||||
|
self.screenshot()
|
||||||
|
|
||||||
|
def switch_control_tab(self):
|
||||||
|
self.logger.log("Switching to control tab.")
|
||||||
|
self.driver.switch_to.window(self.tabs[0])
|
||||||
|
|
||||||
def load_anticatpcha_manually(self):
|
def load_anticatpcha_manually(self):
|
||||||
pretty_print("You might want to install the AntiCaptcha extension for captchas.", color="warning")
|
pretty_print("You might want to install the AntiCaptcha extension for captchas.", color="warning")
|
||||||
@ -154,6 +163,7 @@ class Browser:
|
|||||||
)
|
)
|
||||||
self.apply_web_safety()
|
self.apply_web_safety()
|
||||||
self.logger.log(f"Navigated to: {url}")
|
self.logger.log(f"Navigated to: {url}")
|
||||||
|
self.logger.info(f"Navigated to: {self.get_page_title()}")
|
||||||
self.screenshot()
|
self.screenshot()
|
||||||
return True
|
return True
|
||||||
except TimeoutException as e:
|
except TimeoutException as e:
|
||||||
@ -201,6 +211,8 @@ class Browser:
|
|||||||
lines.append(cleaned)
|
lines.append(cleaned)
|
||||||
result = "[Start of page]\n\n" + "\n\n".join(lines) + "\n\n[End of page]"
|
result = "[Start of page]\n\n" + "\n\n".join(lines) + "\n\n[End of page]"
|
||||||
result = re.sub(r'!\[(.*?)\]\(.*?\)', r'[IMAGE: \1]', result)
|
result = re.sub(r'!\[(.*?)\]\(.*?\)', r'[IMAGE: \1]', result)
|
||||||
|
self.logger.info(f"Extracted text: {result[:100]}...")
|
||||||
|
self.logger.info(f"Extracted text length: {len(result)}")
|
||||||
return result[:8192]
|
return result[:8192]
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.error(f"Error getting text: {str(e)}")
|
self.logger.error(f"Error getting text: {str(e)}")
|
||||||
@ -226,9 +238,11 @@ class Browser:
|
|||||||
def is_link_valid(self, url:str) -> bool:
|
def is_link_valid(self, url:str) -> bool:
|
||||||
"""Check if a URL is a valid link (page, not related to icon or metadata)."""
|
"""Check if a URL is a valid link (page, not related to icon or metadata)."""
|
||||||
if len(url) > 64:
|
if len(url) > 64:
|
||||||
|
self.logger.warning(f"URL too long: {url}")
|
||||||
return False
|
return False
|
||||||
parsed_url = urlparse(url)
|
parsed_url = urlparse(url)
|
||||||
if not parsed_url.scheme or not parsed_url.netloc:
|
if not parsed_url.scheme or not parsed_url.netloc:
|
||||||
|
self.logger.warning(f"Invalid URL: {url}")
|
||||||
return False
|
return False
|
||||||
if re.search(r'/\d+$', parsed_url.path):
|
if re.search(r'/\d+$', parsed_url.path):
|
||||||
return False
|
return False
|
||||||
@ -360,6 +374,7 @@ class Browser:
|
|||||||
Wait for a submission outcome (e.g., URL change or new element).
|
Wait for a submission outcome (e.g., URL change or new element).
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
|
self.logger.info("Waiting for submission outcome...")
|
||||||
wait = WebDriverWait(self.driver, timeout)
|
wait = WebDriverWait(self.driver, timeout)
|
||||||
wait.until(
|
wait.until(
|
||||||
lambda driver: driver.current_url != self.driver.current_url or
|
lambda driver: driver.current_url != self.driver.current_url or
|
||||||
@ -387,8 +402,10 @@ class Browser:
|
|||||||
message=f"Button with XPath '{xpath}' not clickable within {timeout} seconds"
|
message=f"Button with XPath '{xpath}' not clickable within {timeout} seconds"
|
||||||
)
|
)
|
||||||
if self.click_element(xpath):
|
if self.click_element(xpath):
|
||||||
|
self.logger.info(f"Clicked button '{button_text}' at XPath: {xpath}")
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
|
self.logger.warning(f"Button '{button_text}' at XPath: {xpath} not clickable")
|
||||||
return False
|
return False
|
||||||
except TimeoutException:
|
except TimeoutException:
|
||||||
self.logger.warning(f"Timeout waiting for '{button_text}' button at XPath: {xpath}")
|
self.logger.warning(f"Timeout waiting for '{button_text}' button at XPath: {xpath}")
|
||||||
@ -424,9 +441,9 @@ class Browser:
|
|||||||
self.logger.info(f"Ticked checkbox {index}")
|
self.logger.info(f"Ticked checkbox {index}")
|
||||||
except ElementClickInterceptedException:
|
except ElementClickInterceptedException:
|
||||||
self.driver.execute_script("arguments[0].click();", checkbox)
|
self.driver.execute_script("arguments[0].click();", checkbox)
|
||||||
self.logger.info(f"Ticked checkbox {index} using JavaScript")
|
self.logger.warning(f"Click checkbox {index} intercepted")
|
||||||
else:
|
else:
|
||||||
self.logger.debug(f"Checkbox {index} already ticked")
|
self.logger.info(f"Checkbox {index} already ticked")
|
||||||
except TimeoutException:
|
except TimeoutException:
|
||||||
self.logger.warning(f"Timeout waiting for checkbox {index} to be clickable")
|
self.logger.warning(f"Timeout waiting for checkbox {index} to be clickable")
|
||||||
continue
|
continue
|
||||||
@ -534,6 +551,7 @@ class Browser:
|
|||||||
def scroll_bottom(self) -> bool:
|
def scroll_bottom(self) -> bool:
|
||||||
"""Scroll to the bottom of the page."""
|
"""Scroll to the bottom of the page."""
|
||||||
try:
|
try:
|
||||||
|
self.logger.info("Scrolling to the bottom of the page...")
|
||||||
self.driver.execute_script(
|
self.driver.execute_script(
|
||||||
"window.scrollTo(0, document.body.scrollHeight);"
|
"window.scrollTo(0, document.body.scrollHeight);"
|
||||||
)
|
)
|
||||||
@ -549,6 +567,7 @@ class Browser:
|
|||||||
|
|
||||||
def screenshot(self, filename:str = 'updated_screen.png') -> bool:
|
def screenshot(self, filename:str = 'updated_screen.png') -> bool:
|
||||||
"""Take a screenshot of the current page."""
|
"""Take a screenshot of the current page."""
|
||||||
|
self.logger.info("Taking screenshot...")
|
||||||
try:
|
try:
|
||||||
path = os.path.join(self.screenshot_folder, filename)
|
path = os.path.join(self.screenshot_folder, filename)
|
||||||
if not os.path.exists(self.screenshot_folder):
|
if not os.path.exists(self.screenshot_folder):
|
||||||
@ -564,11 +583,12 @@ class Browser:
|
|||||||
"""
|
"""
|
||||||
Apply security measures to block any website malicious/annoying execution, privacy violation etc..
|
Apply security measures to block any website malicious/annoying execution, privacy violation etc..
|
||||||
"""
|
"""
|
||||||
|
self.logger.info("Applying web safety measures...")
|
||||||
script = self.load_js("inject_safety_script.js")
|
script = self.load_js("inject_safety_script.js")
|
||||||
input_elements = self.driver.execute_script(script)
|
input_elements = self.driver.execute_script(script)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
driver = create_driver(headless=True, stealth_mode=True)
|
driver = create_driver(headless=False, stealth_mode=True)
|
||||||
browser = Browser(driver, anticaptcha_manual_install=True)
|
browser = Browser(driver, anticaptcha_manual_install=True)
|
||||||
|
|
||||||
#browser.go_to("https://github.com/Fosowl/agenticSeek")
|
#browser.go_to("https://github.com/Fosowl/agenticSeek")
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
|
|
||||||
from typing import Tuple, Callable
|
from typing import Tuple, Callable
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
from sources.utility import pretty_print
|
||||||
|
|
||||||
class QueryRequest(BaseModel):
|
class QueryRequest(BaseModel):
|
||||||
query: str
|
query: str
|
||||||
lang: str = "en"
|
|
||||||
tts_enabled: bool = True
|
tts_enabled: bool = True
|
||||||
stt_enabled: bool = False
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"Query: {self.query}, Language: {self.lang}, TTS: {self.tts_enabled}, STT: {self.stt_enabled}"
|
return f"Query: {self.query}, Language: {self.lang}, TTS: {self.tts_enabled}, STT: {self.stt_enabled}"
|
||||||
@ -14,9 +13,7 @@ class QueryRequest(BaseModel):
|
|||||||
def jsonify(self):
|
def jsonify(self):
|
||||||
return {
|
return {
|
||||||
"query": self.query,
|
"query": self.query,
|
||||||
"lang": self.lang,
|
|
||||||
"tts_enabled": self.tts_enabled,
|
"tts_enabled": self.tts_enabled,
|
||||||
"stt_enabled": self.stt_enabled
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class QueryResponse(BaseModel):
|
class QueryResponse(BaseModel):
|
||||||
|
@ -54,12 +54,9 @@ if [ ! -f "docker-compose.yml" ]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# start searxng service for internet search service
|
|
||||||
cd searxng && ./setup_searxng.sh && cd ..
|
|
||||||
|
|
||||||
# start docker compose for searxng, redis, frontend services
|
|
||||||
echo "SearXNG setup complete!"
|
echo "SearXNG setup complete!"
|
||||||
|
|
||||||
|
# start docker compose for searxng, redis, frontend services
|
||||||
echo "Warning: stopping all docker containers (t-4 seconds)..."
|
echo "Warning: stopping all docker containers (t-4 seconds)..."
|
||||||
sleep 4
|
sleep 4
|
||||||
docker stop $(docker ps -a -q)
|
docker stop $(docker ps -a -q)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user