diff --git a/.env.example b/.env.example index 069f23c..0e98844 100644 --- a/.env.example +++ b/.env.example @@ -1,4 +1,6 @@ SEARXNG_BASE_URL="http://127.0.0.1:8080" OPENAI_API_KEY='xxxxx' DEEPSEEK_API_KEY='xxxxx' -OPENROUTER_API_KEY='xxxxx' \ No newline at end of file +OPENROUTER_API_KEY='xxxxx' +BACKEND_PORT=8000 +WORK_DIR="/tmp/" \ No newline at end of file diff --git a/.gitignore b/.gitignore index 0e82296..f8801e4 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,7 @@ agentic_seek_env/* .env */.env dsk/ +chrome136/ ### react ### .DS_* diff --git a/Dockerfile.backend b/Dockerfile.backend index 1cb8149..9bae45f 100644 --- a/Dockerfile.backend +++ b/Dockerfile.backend @@ -1,38 +1,57 @@ FROM ubuntu:22.04 -# Warning: doesn't work yet, backend is run on host machine for now WORKDIR /app -RUN apt-get update -qq -y && \ -apt-get install -y \ - gcc \ - g++ \ - gfortran \ - libportaudio2 \ - portaudio19-dev \ - ffmpeg \ - libavcodec-dev \ - libavformat-dev \ - libavutil-dev \ - gnupg2 \ - wget \ - unzip \ - python3 \ - python3-pip \ - libasound2 \ - libatk-bridge2.0-0 \ - libgtk-4-1 \ - libnss3 \ - xdg-utils \ - wget && \ +# Install essential packages and Chrome dependencies +RUN apt-get update && apt-get install -y \ + wget \ + unzip \ + curl \ + gnupg \ + python3-dev \ + python3-pip \ + python3-wheel \ + build-essential \ + # Chrome dependencies - comprehensive list + fonts-liberation \ + libasound2 \ + libatk-bridge2.0-0 \ + libdrm2 \ + libxcomposite1 \ + libxdamage1 \ + libxrandr2 \ + libgbm1 \ + libxss1 \ + libnss3 \ + libnspr4 \ + libxshmfence1 \ + libgconf-2-4 \ + libxfixes3 \ + libxinerama1 \ + libgtk-3-0 \ + libgdk-pixbuf2.0-0 \ + libatspi2.0-0 \ + libdrm2 \ + libxkbcommon0 \ + libepoxy0 \ + libgtk-3-0 \ + libharfbuzz0b \ + libegl1-mesa \ + libgles2-mesa \ + # Virtual display for headless operation + xvfb \ + && rm -rf /var/lib/apt/lists/* + +RUN pip3 install --upgrade pip setuptools wheel && \ +pip3 install selenium -RUN chmod +x /opt/chrome/chrome # Install dependencies COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt # Copy application code COPY api.py . +COPY chrome_bundle/ ./chrome_bundle/ COPY sources/ ./sources/ COPY prompts/ ./prompts/ COPY crx/ crx/ @@ -40,6 +59,28 @@ COPY llm_router/ llm_router/ COPY .env . COPY config.ini . +# Install Chrome and ChromeDriver from chrome-for-testing +RUN wget -O chrome-linux64.zip https://storage.googleapis.com/chrome-for-testing-public/136.0.7103.113/linux64/chrome-linux64.zip && \ + wget -O chromedriver-linux64.zip https://storage.googleapis.com/chrome-for-testing-public/136.0.7103.113/linux64/chromedriver-linux64.zip && \ + unzip chrome-linux64.zip && \ + unzip chromedriver-linux64.zip && \ + mkdir -p /opt/google && \ + ls -la && \ + mv chrome-linux64 /opt/google/chrome && \ + mv chromedriver-linux64/chromedriver /usr/local/bin/chromedriver && \ + chmod +x /opt/google/chrome/chrome && \ + chmod +x /usr/local/bin/chromedriver + +RUN ln -s /opt/google/chrome/chrome /usr/local/bin/chrome + +# Verify Chrome and ChromeDriver installation +RUN google-chrome --version +RUN chromedriver --version > /dev/null 2>&1 + +ENV CHROME_BIN=/opt/google/chrome/chrome +ENV CHROMEDRIVER_PATH=/usr/local/bin/chromedriver +ENV DISPLAY=:99 + # Expose port EXPOSE 8000 diff --git a/README.md b/README.md index 34612c5..ecf2486 100644 --- a/README.md +++ b/README.md @@ -559,3 +559,7 @@ We’re looking for developers to improve AgenticSeek! Check out open issues or > [antoineVIVIES](https://github.com/antoineVIVIES) | Taipei Time > [steveh8758](https://github.com/steveh8758) | Taipei Time + +## Special Thanks: + + > [tcsenpai](https://github.com/tcsenpai) For dockerization of backend diff --git a/api.py b/api.py index fa82896..3689c04 100755 --- a/api.py +++ b/api.py @@ -22,6 +22,10 @@ from sources.utility import pretty_print from sources.logger import Logger from sources.schemas import QueryRequest, QueryResponse +from dotenv import load_dotenv + +load_dotenv() + from celery import Celery @@ -247,4 +251,9 @@ async def process_query(request: QueryRequest): interaction.save_session() if __name__ == "__main__": + envport = os.getenv("BACKEND_PORT") + if envport: + port = int(envport) + else: + port = 8000 uvicorn.run(api, host="0.0.0.0", port=8000) \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 2f78cb9..f2a0d6c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -31,7 +31,7 @@ services: volumes: - ./searxng:/etc/searxng:rw environment: - - SEARXNG_BASE_URL=http://localhost:8080/ + - SEARXNG_BASE_URL=${SEARXNG_BASE_URL:-http://localhost:8080/} - SEARXNG_SECRET_KEY=$(openssl rand -hex 32) - UWSGI_WORKERS=4 - UWSGI_THREADS=4 @@ -62,13 +62,28 @@ services: environment: - NODE_ENV=development - CHOKIDAR_USEPOLLING=true - - BACKEND_URL=http://backend:8000 + - REACT_APP_BACKEND_URL=http://0.0.0.0:${BACKEND_PORT:-8000} networks: - agentic-seek-net + + backend: + container_name: backend + build: + context: . + dockerfile: Dockerfile.backend + ports: + - ${BACKEND_PORT:-8000}:${BACKEND_PORT:-8000} + volumes: + - ./:/app + - ${WORK_DIR}:${WORK_DIR} + command: python3 api.py + environment: + - SEARXNG_URL=http://localhost:8080 + - WORK_DIR=${WORK_DIR} + network_mode: "host" # NOTE: backend service is not working yet due to issue with chromedriver on docker. # Therefore backend is run on host machine. - # Open to pull requests to fix this. #backend: # container_name: backend diff --git a/frontend/agentic-seek-front/src/App.js b/frontend/agentic-seek-front/src/App.js index c1e6ba8..a9e6407 100644 --- a/frontend/agentic-seek-front/src/App.js +++ b/frontend/agentic-seek-front/src/App.js @@ -4,6 +4,8 @@ import axios from 'axios'; import './App.css'; import { colors } from './colors'; +const BACKEND_URL = process.env.REACT_APP_BACKEND_URL || 'http://0.0.0.0:8000'; + function App() { const [query, setQuery] = useState(''); const [messages, setMessages] = useState([]); @@ -27,7 +29,7 @@ function App() { const checkHealth = async () => { try { - await axios.get('http://127.0.0.1:8000/health'); + await axios.get(`${BACKEND_URL}/health`); setIsOnline(true); console.log('System is online'); } catch { @@ -39,7 +41,7 @@ function App() { const fetchScreenshot = async () => { try { const timestamp = new Date().getTime(); - const res = await axios.get(`http://127.0.0.1:8000/screenshots/updated_screen.png?timestamp=${timestamp}`, { + const res = await axios.get(`${BACKEND_URL}/screenshots/updated_screen.png?timestamp=${timestamp}`, { responseType: 'blob' }); console.log('Screenshot fetched successfully'); @@ -90,7 +92,7 @@ function App() { const fetchLatestAnswer = async () => { try { - const res = await axios.get('http://127.0.0.1:8000/latest_answer'); + const res = await axios.get(`${BACKEND_URL}/latest_answer`); const data = res.data; updateData(data); @@ -141,7 +143,7 @@ function App() { setIsLoading(false); setError(null); try { - const res = await axios.get('http://127.0.0.1:8000/stop'); + const res = await axios.get(`${BACKEND_URL}/stop`); setStatus("Requesting stop..."); } catch (err) { console.error('Error stopping the agent:', err); @@ -162,7 +164,7 @@ function App() { try { console.log('Sending query:', query); setQuery('waiting for response...'); - const res = await axios.post('http://127.0.0.1:8000/query', { + const res = await axios.post(`${BACKEND_URL}/query`, { query, tts_enabled: false }); diff --git a/requirements.txt b/requirements.txt index a63646c..eba75e1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -41,6 +41,7 @@ fake_useragent>=2.1.0 selenium_stealth>=1.0.6 undetected-chromedriver>=3.5.5 sentencepiece>=0.2.0 +python-dotenv>=1.0.0 tqdm>4 openai sniffio diff --git a/sources/browser.py b/sources/browser.py index 3604e9b..f3ebad4 100644 --- a/sources/browser.py +++ b/sources/browser.py @@ -42,7 +42,14 @@ def get_chrome_path() -> str: paths = ["/Applications/Google Chrome.app/Contents/MacOS/Google Chrome", "/Applications/Google Chrome Beta.app/Contents/MacOS/Google Chrome Beta"] else: # Linux - paths = ["/usr/bin/google-chrome", "/usr/bin/chromium-browser", "/usr/bin/chromium", "/opt/chrome/chrome", "/usr/local/bin/chrome"] + paths = ["/usr/bin/google-chrome", + "/usr/bin/chromium-browser", + "/usr/bin/chromium", + "/opt/chrome/chrome", + "opt/google/chrome/chrome", + "/usr/local/bin/chrome", + #"/app/chrome_bundle/chrome136/chrome-linux64" + ] for path in paths: if os.path.exists(path) and os.access(path, os.X_OK): @@ -73,8 +80,13 @@ def install_chromedriver() -> str: Install the ChromeDriver if not already installed. Return the path. """ chromedriver_path = shutil.which("chromedriver") + #if not chromedriver_path: + # if os.path.exists("/app/chrome_bundle/chrome136/chromedriver"): + # print("Using bundled ChromeDriver from /app/chrome_bundle/chrome136/chromedriver") + # chromedriver_path = "/app/chrome_bundle/chrome136/chromedriver" if not chromedriver_path: try: + print("ChromeDriver not found, attempting to install automatically...") chromedriver_path = chromedriver_autoinstaller.install() except Exception as e: raise FileNotFoundError( diff --git a/start_services.sh b/start_services.sh index 995f6ff..32759e8 100755 --- a/start_services.sh +++ b/start_services.sh @@ -1,5 +1,7 @@ #!/bin/bash +source .env + command_exists() { command -v "$1" &> /dev/null } @@ -60,12 +62,58 @@ if [ ! -f "docker-compose.yml" ]; then exit 1 fi -# start docker compose for searxng, redis, frontend services +# Download and extract Chrome bundle if not present +echo "Checking Chrome bundle..." +if [ ! -d "chrome_bundle/chrome136" ]; then + echo "Chrome bundle not found. Downloading..." + mkdir -p chrome_bundle + curl -L https://github.com/tcsenpai/agenticSeek/releases/download/utility/chrome136.zip -o /tmp/chrome136.zip + if [ $? -ne 0 ]; then + echo "Error: Failed to download Chrome bundle" + exit 1 + fi + unzip -q /tmp/chrome136.zip -d chrome_bundle/ + if [ $? -ne 0 ]; then + echo "Error: Failed to extract Chrome bundle" + exit 1 + fi + rm /tmp/chrome136.zip + echo "Chrome bundle downloaded and extracted successfully" +else + echo "Chrome bundle already exists" +fi + +# Stop all running containers to ensure a clean state echo "Warning: stopping all docker containers (t-4 seconds)..." sleep 4 docker stop $(docker ps -a -q) echo "All containers stopped" +# First start backend and wait for it to be healthy +echo "Starting backend service..." +if ! $COMPOSE_CMD up -d backend; then + echo "Error: Failed to start backend container." + exit 1 +fi + +# Wait for backend to be healthy (check if it's running and not restarting) +echo "Waiting for backend to be ready..." +for i in {1..30}; do + if [ "$(docker inspect -f '{{.State.Running}}' backend)" = "true" ] && \ + [ "$(docker inspect -f '{{.State.Restarting}}' backend)" = "false" ]; then + echo "backend is ready!" + break + fi + if [ $i -eq 30 ]; then + echo "Error: backend failed to start properly after 30 seconds" + $COMPOSE_CMD logs backend + exit 1 + fi + sleep 1 +done + +# start remaining services for searxng, redis, frontend services + if ! $COMPOSE_CMD up; then echo "Error: Failed to start containers. Check Docker logs with '$COMPOSE_CMD logs'." echo "Possible fixes: Run with sudo or ensure port 8080 is free."