Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
41b5772ff0 |
+3
-2
@@ -3,9 +3,10 @@ __pycache__/
|
|||||||
*.py[cod]
|
*.py[cod]
|
||||||
|
|
||||||
# Virtual environments
|
# Virtual environments
|
||||||
agentic_seek_env/
|
venv/
|
||||||
.agentic_seek_env/
|
.venv/
|
||||||
|
|
||||||
|
# Environment variables (secrets)
|
||||||
.env
|
.env
|
||||||
|
|
||||||
# Git metadata
|
# Git metadata
|
||||||
|
|||||||
@@ -1,12 +1,4 @@
|
|||||||
SEARXNG_BASE_URL="http://127.0.0.1:8080"
|
SEARXNG_BASE_URL="http://127.0.0.1:8080"
|
||||||
REDIS_BASE_URL="redis://redis:6379/0"
|
|
||||||
WORK_DIR="/Users/username/Documents/workspace_with_my_files"
|
|
||||||
OLLAMA_PORT="11434"
|
|
||||||
LM_STUDIO_PORT="1234"
|
|
||||||
CUSTOM_ADDITIONAL_LLM_PORT="11435"
|
|
||||||
OPENAI_API_KEY='xxxxx'
|
OPENAI_API_KEY='xxxxx'
|
||||||
DEEPSEEK_API_KEY='xxxxx'
|
DEEPSEEK_API_KEY='xxxxx'
|
||||||
OPENROUTER_API_KEY='xxxxx'
|
OPENROUTER_API_KEY='xxxxx'
|
||||||
TOGETHER_API_KEY='xxxxx'
|
|
||||||
GOOGLE_API_KEY='xxxxx'
|
|
||||||
ANTHROPIC_API_KEY='xxxxx'
|
|
||||||
@@ -19,7 +19,6 @@ agentic_seek_env/*
|
|||||||
.env
|
.env
|
||||||
*/.env
|
*/.env
|
||||||
dsk/
|
dsk/
|
||||||
chrome136/
|
|
||||||
|
|
||||||
### react ###
|
### react ###
|
||||||
.DS_*
|
.DS_*
|
||||||
|
|||||||
+27
-83
@@ -1,102 +1,46 @@
|
|||||||
|
FROM ubuntu:22.04
|
||||||
FROM --platform=linux/amd64 python:3.11-slim
|
# Warning: doesn't work yet, backend is run on host machine for now
|
||||||
ENV DEBIAN_FRONTEND=noninteractive
|
|
||||||
|
|
||||||
# Install essential packages and Chrome dependencies
|
|
||||||
RUN apt-get update -y && apt-get install -y \
|
|
||||||
wget \
|
|
||||||
gnupg2 \
|
|
||||||
ca-certificates \
|
|
||||||
unzip \
|
|
||||||
xvfb \
|
|
||||||
libxss1 \
|
|
||||||
libappindicator1 \
|
|
||||||
fonts-liberation \
|
|
||||||
libnss3 \
|
|
||||||
libatk1.0-0 \
|
|
||||||
libatk-bridge2.0-0 \
|
|
||||||
libcups2 \
|
|
||||||
libdrm2 \
|
|
||||||
libxcomposite1 \
|
|
||||||
libxdamage1 \
|
|
||||||
libxrandr2 \
|
|
||||||
xdg-utils \
|
|
||||||
dbus \
|
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
RUN apt-get update -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 \
|
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
|
|
||||||
RUN apt-get update -y && \
|
|
||||||
apt-get install -y \
|
|
||||||
alsa-utils \
|
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
ENV CHROME_TESTING_VERSION=134.0.6998.88
|
|
||||||
ENV DISPLAY=:99
|
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
RUN set -eux; \
|
RUN apt-get update -qq -y && \
|
||||||
wget -qO /tmp/chrome.zip \
|
apt-get install -y \
|
||||||
"https://storage.googleapis.com/chrome-for-testing-public/${CHROME_TESTING_VERSION}/linux64/chrome-linux64.zip"; \
|
gcc \
|
||||||
unzip -q /tmp/chrome.zip -d /opt; \
|
g++ \
|
||||||
rm /tmp/chrome.zip; \
|
gfortran \
|
||||||
ln -s /opt/chrome-linux64/chrome /usr/local/bin/google-chrome; \
|
libportaudio2 \
|
||||||
ln -s /opt/chrome-linux64/chrome /usr/local/bin/chrome; \
|
portaudio19-dev \
|
||||||
mkdir -p /opt/chrome; \
|
ffmpeg \
|
||||||
ln -s /opt/chrome-linux64/chrome /opt/chrome/chrome; \
|
libavcodec-dev \
|
||||||
google-chrome --version
|
libavformat-dev \
|
||||||
|
libavutil-dev \
|
||||||
RUN set -eux; \
|
gnupg2 \
|
||||||
wget -qO /tmp/chromedriver.zip \
|
wget \
|
||||||
"https://storage.googleapis.com/chrome-for-testing-public/${CHROME_TESTING_VERSION}/linux64/chromedriver-linux64.zip"; \
|
unzip \
|
||||||
unzip -q /tmp/chromedriver.zip -d /tmp; \
|
python3 \
|
||||||
mv /tmp/chromedriver-linux64/chromedriver /usr/local/bin; \
|
python3-pip \
|
||||||
rm /tmp/chromedriver.zip; \
|
libasound2 \
|
||||||
chmod +x /usr/local/bin/chromedriver; \
|
libatk-bridge2.0-0 \
|
||||||
chromedriver --version
|
libgtk-4-1 \
|
||||||
|
libnss3 \
|
||||||
|
xdg-utils \
|
||||||
|
wget && \
|
||||||
|
|
||||||
RUN chmod +x /opt/chrome/chrome
|
RUN chmod +x /opt/chrome/chrome
|
||||||
|
# Install dependencies
|
||||||
RUN pip3 install --upgrade pip setuptools wheel
|
|
||||||
|
|
||||||
COPY requirements.txt .
|
COPY requirements.txt .
|
||||||
RUN pip install --no-cache-dir -r requirements.txt
|
RUN pip install --no-cache-dir -r requirements.txt
|
||||||
|
|
||||||
RUN mkdir -p /opt/workspace
|
|
||||||
RUN mkdir -p /tmp && chmod 1777 /tmp
|
|
||||||
|
|
||||||
# Copy application code
|
# Copy application code
|
||||||
COPY api.py .
|
COPY api.py .
|
||||||
COPY sources/ ./sources/
|
COPY sources/ ./sources/
|
||||||
COPY prompts/ ./prompts/
|
COPY prompts/ ./prompts/
|
||||||
COPY crx/ crx/
|
COPY crx/ crx/
|
||||||
COPY llm_router/ llm_router/
|
COPY llm_router/ llm_router/
|
||||||
|
COPY .env .
|
||||||
COPY config.ini .
|
COPY config.ini .
|
||||||
|
|
||||||
|
# Expose port
|
||||||
EXPOSE 8000
|
EXPOSE 8000
|
||||||
|
|
||||||
# Run the application
|
# Run the application
|
||||||
|
|||||||
@@ -34,13 +34,13 @@ Disclaimer: This demo, including all the files that appear (e.g: CV_candidates.z
|
|||||||
|
|
||||||
> 🛠⚠️️ **Active Work in Progress** – Please note that Code/Bash is not dockerized yet but will be soon (see docker_deployement branch) - Do not deploy over network or production.
|
> 🛠⚠️️ **Active Work in Progress** – Please note that Code/Bash is not dockerized yet but will be soon (see docker_deployement branch) - Do not deploy over network or production.
|
||||||
|
|
||||||
> 🙏 Please also understand that this project began as a side experiment, with no roadmap and no expectations, we didn't expect to end in Github trending. Financial backing is exactly $1/month (shoutout to my single sponsor). Contributions, feedback, and patience are deeply appreciated.
|
> 🙏 This project started as a side-project with zero roadmap and zero funding. It's grown way beyond what I expected by ending in GitHub Trending. Contributions, feedback, and patience are deeply appreciated.
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
Make sure you have chrome driver, docker and python3.10 installed.
|
Make sure you have chrome driver, docker and python3.10 installed.
|
||||||
|
|
||||||
We highly advise you use exactly python3.10 for the setup. Dependencies error might happen otherwise.
|
We highly advice you use exactly python3.10 for the setup. Dependencies error might happen otherwise.
|
||||||
|
|
||||||
For issues related to chrome driver, see the **Chromedriver** section.
|
For issues related to chrome driver, see the **Chromedriver** section.
|
||||||
|
|
||||||
@@ -64,9 +64,9 @@ source agentic_seek_env/bin/activate
|
|||||||
|
|
||||||
Ensure Python, Docker and docker compose, and Google chrome are installed.
|
Ensure Python, Docker and docker compose, and Google chrome are installed.
|
||||||
|
|
||||||
We recommend Python 3.10.0.
|
We recommand Python 3.10.0.
|
||||||
|
|
||||||
**Automatic Installation (recommended):**
|
**Automatic Installation (Recommanded):**
|
||||||
|
|
||||||
For Linux/Macos:
|
For Linux/Macos:
|
||||||
```sh
|
```sh
|
||||||
@@ -140,7 +140,7 @@ See below for a list of local supported provider.
|
|||||||
|
|
||||||
**Update the config.ini**
|
**Update the config.ini**
|
||||||
|
|
||||||
Change the config.ini file to set the provider_name to a supported provider and provider_model to a LLM supported by your provider. We recommend reasoning model such as *Qwen* or *Deepseek*.
|
Change the config.ini file to set the provider_name to a supported provider and provider_model to a LLM supported by your provider. We recommand reasoning model such as *Qwen* or *Deepseek*.
|
||||||
|
|
||||||
See the **FAQ** at the end of the README for required hardware.
|
See the **FAQ** at the end of the README for required hardware.
|
||||||
|
|
||||||
@@ -159,7 +159,7 @@ work_dir = /Users/mlg/Documents/workspace # The workspace for AgenticSeek.
|
|||||||
jarvis_personality = False # Whenever to use a more "Jarvis" like personality (experimental)
|
jarvis_personality = False # Whenever to use a more "Jarvis" like personality (experimental)
|
||||||
languages = en zh # The list of languages, Text to speech will default to the first language on the list
|
languages = en zh # The list of languages, Text to speech will default to the first language on the list
|
||||||
[BROWSER]
|
[BROWSER]
|
||||||
headless_browser = True # Whenever to use headless browser, recommended only if you use web interface.
|
headless_browser = True # Whenever to use headless browser, recommanded only if you use web interface.
|
||||||
stealth_mode = True # Use undetected selenium to reduce browser detection
|
stealth_mode = True # Use undetected selenium to reduce browser detection
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -212,9 +212,9 @@ Example: export `TOGETHER_API_KEY="xxxxx"`
|
|||||||
| togetherAI | No | Use together AI API (non-private) |
|
| togetherAI | No | Use together AI API (non-private) |
|
||||||
| google | No | Use google gemini API (non-private) |
|
| google | No | Use google gemini API (non-private) |
|
||||||
|
|
||||||
*We advise against using gpt-4o or other closedAI models*, performance are poor for web browsing and task planning.
|
*We advice against using gpt-4o or other closedAI models*, performance are poor for web browsing and task planning.
|
||||||
|
|
||||||
Please also note that coding/bash might fail with gemini, it seems to ignore our prompt for format to respect, which are optimized for deepseek r1.
|
Please also note that coding/bash might fail with gemini, it seem to ignore our prompt for format to respect, which are optimized for deepseek r1.
|
||||||
|
|
||||||
Next step: [Start services and run AgenticSeek](#Start-services-and-Run)
|
Next step: [Start services and run AgenticSeek](#Start-services-and-Run)
|
||||||
|
|
||||||
@@ -247,7 +247,7 @@ start ./start_services.cmd # Window
|
|||||||
python3 cli.py
|
python3 cli.py
|
||||||
```
|
```
|
||||||
|
|
||||||
We advise you set `headless_browser` to False in the config.ini for CLI mode.
|
We advice you set `headless_browser` to False in the config.ini for CLI mode.
|
||||||
|
|
||||||
**Options 2:** Run with the Web interface.
|
**Options 2:** Run with the Web interface.
|
||||||
|
|
||||||
@@ -541,7 +541,7 @@ Yes with Ollama, lm-studio or server providers, all speech to text, LLM and text
|
|||||||
**Q: Why should I use AgenticSeek when I have Manus?**
|
**Q: Why should I use AgenticSeek when I have Manus?**
|
||||||
|
|
||||||
This started as Side-Project we did out of interest about AI agents. What’s special about it is that we want to use local model and avoid APIs.
|
This started as Side-Project we did out of interest about AI agents. What’s special about it is that we want to use local model and avoid APIs.
|
||||||
We draw inspiration from Jarvis and Friday (Iron man movies) to make it "cool" but for functionality we take more inspiration from Manus, because that's what people want in the first place: a local manus alternative.
|
We draw inspiration from Jarvis and Friday (Iron man movies) to make it "cool" but for functionnality we take more inspiration from Manus, because that's what people want in the first place: a local manus alternative.
|
||||||
Unlike Manus, AgenticSeek prioritizes independence from external systems, giving you more control, privacy and avoid api cost.
|
Unlike Manus, AgenticSeek prioritizes independence from external systems, giving you more control, privacy and avoid api cost.
|
||||||
|
|
||||||
## Contribute
|
## Contribute
|
||||||
@@ -559,7 +559,3 @@ We’re looking for developers to improve AgenticSeek! Check out open issues or
|
|||||||
> [antoineVIVIES](https://github.com/antoineVIVIES) | Taipei Time
|
> [antoineVIVIES](https://github.com/antoineVIVIES) | Taipei Time
|
||||||
|
|
||||||
> [steveh8758](https://github.com/steveh8758) | Taipei Time
|
> [steveh8758](https://github.com/steveh8758) | Taipei Time
|
||||||
|
|
||||||
## Special Thanks:
|
|
||||||
|
|
||||||
> [tcsenpai](https://github.com/tcsenpai) For dockerization of backend
|
|
||||||
|
|||||||
@@ -22,10 +22,6 @@ from sources.utility import pretty_print
|
|||||||
from sources.logger import Logger
|
from sources.logger import Logger
|
||||||
from sources.schemas import QueryRequest, QueryResponse
|
from sources.schemas import QueryRequest, QueryResponse
|
||||||
|
|
||||||
from dotenv import load_dotenv
|
|
||||||
|
|
||||||
load_dotenv()
|
|
||||||
|
|
||||||
|
|
||||||
from celery import Celery
|
from celery import Celery
|
||||||
|
|
||||||
@@ -38,7 +34,7 @@ config.read('config.ini')
|
|||||||
|
|
||||||
api.add_middleware(
|
api.add_middleware(
|
||||||
CORSMiddleware,
|
CORSMiddleware,
|
||||||
allow_origins=["*"],
|
allow_origins=["http://localhost", "http://localhost:3000"],
|
||||||
allow_credentials=True,
|
allow_credentials=True,
|
||||||
allow_methods=["*"],
|
allow_methods=["*"],
|
||||||
allow_headers=["*"],
|
allow_headers=["*"],
|
||||||
@@ -251,9 +247,4 @@ async def process_query(request: QueryRequest):
|
|||||||
interaction.save_session()
|
interaction.save_session()
|
||||||
|
|
||||||
if __name__ == "__main__":
|
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)
|
uvicorn.run(api, host="0.0.0.0", port=8000)
|
||||||
+32
-34
@@ -3,7 +3,6 @@ version: '3'
|
|||||||
services:
|
services:
|
||||||
redis:
|
redis:
|
||||||
container_name: redis
|
container_name: redis
|
||||||
profiles: ["core", "full"]
|
|
||||||
image: docker.io/valkey/valkey:8-alpine
|
image: docker.io/valkey/valkey:8-alpine
|
||||||
command: valkey-server --save 30 1 --loglevel warning
|
command: valkey-server --save 30 1 --loglevel warning
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
@@ -25,7 +24,6 @@ services:
|
|||||||
|
|
||||||
searxng:
|
searxng:
|
||||||
container_name: searxng
|
container_name: searxng
|
||||||
profiles: ["core", "full"]
|
|
||||||
image: docker.io/searxng/searxng:latest
|
image: docker.io/searxng/searxng:latest
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
ports:
|
ports:
|
||||||
@@ -33,8 +31,8 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- ./searxng:/etc/searxng:rw
|
- ./searxng:/etc/searxng:rw
|
||||||
environment:
|
environment:
|
||||||
- SEARXNG_BASE_URL=${SEARXNG_BASE_URL:-http://localhost:8080/}
|
- SEARXNG_BASE_URL=http://localhost:8080/
|
||||||
- SEARXNG_SECRET_KEY=${SEARXNG_SECRET_KEY}
|
- SEARXNG_SECRET_KEY=$(openssl rand -hex 32)
|
||||||
- UWSGI_WORKERS=4
|
- UWSGI_WORKERS=4
|
||||||
- UWSGI_THREADS=4
|
- UWSGI_THREADS=4
|
||||||
cap_add:
|
cap_add:
|
||||||
@@ -53,7 +51,6 @@ services:
|
|||||||
|
|
||||||
frontend:
|
frontend:
|
||||||
container_name: frontend
|
container_name: frontend
|
||||||
profiles: ["core", "full"]
|
|
||||||
build:
|
build:
|
||||||
context: ./frontend
|
context: ./frontend
|
||||||
dockerfile: Dockerfile.frontend
|
dockerfile: Dockerfile.frontend
|
||||||
@@ -65,38 +62,39 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
- NODE_ENV=development
|
- NODE_ENV=development
|
||||||
- CHOKIDAR_USEPOLLING=true
|
- CHOKIDAR_USEPOLLING=true
|
||||||
- REACT_APP_BACKEND_URL=http://0.0.0.0:${BACKEND_PORT:-8000}
|
- BACKEND_URL=http://backend:8000
|
||||||
networks:
|
networks:
|
||||||
- agentic-seek-net
|
- agentic-seek-net
|
||||||
|
|
||||||
backend:
|
# NOTE: backend service is not working yet due to issue with chromedriver on docker.
|
||||||
container_name: backend
|
# Therefore backend is run on host machine.
|
||||||
profiles: ["backend", "full"]
|
# Open to pull requests to fix this.
|
||||||
build:
|
|
||||||
context: .
|
#backend:
|
||||||
dockerfile: Dockerfile.backend
|
# container_name: backend
|
||||||
ports:
|
# build:
|
||||||
- ${BACKEND_PORT:-7777}:${BACKEND_PORT:-7777}
|
# context: ./
|
||||||
- ${OLLAMA_PORT:-11434}:${OLLAMA_PORT:-11434}
|
# dockerfile: Dockerfile.backend
|
||||||
- ${LM_STUDIO_PORT:-1234}:${LM_STUDIO_PORT:-1234}
|
# stdin_open: true
|
||||||
- ${CUSTOM_ADDITIONAL_LLM_PORT:-8000}:${CUSTOM_ADDITIONAL_LLM_PORT:-8000}
|
# tty: true
|
||||||
volumes:
|
# shm_size: 8g
|
||||||
- ./:/app
|
# ports:
|
||||||
- ${WORK_DIR:-.}:/opt/workspace
|
# - "8000:8000"
|
||||||
command: python3 api.py
|
# volumes:
|
||||||
environment:
|
# - ./:/app
|
||||||
- SEARXNG_URL=${SEARXNG_BASE_URL:-http://searxng:8080}
|
# environment:
|
||||||
- REDIS_URL=${REDIS_BASE_URL:-redis://redis:6379/0}
|
# - NODE_ENV=development
|
||||||
- WORK_DIR=/opt/workspace
|
# - REDIS_URL=redis://redis:6379/0
|
||||||
- OPENAI_API_KEY=${OPENAI_API_KEY}
|
# - SEARXNG_URL=http://searxng:8080
|
||||||
- DEEPSEEK_API_KEY=${DEEPSEEK_API_KEY}
|
# - OLLAMA_URL=http://localhost:11434
|
||||||
- OPENROUTER_API_KEY=${OPENROUTER_API_KEY}
|
# - LM_STUDIO_URL=http://localhost:1234
|
||||||
- TOGETHER_API_KEY=${TOGETHER_API_KEY}
|
# extra_hosts:
|
||||||
- GOOGLE_API_KEY=${GOOGLE_API_KEY}
|
# - "host.docker.internal:host-gateway"
|
||||||
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
|
# depends_on:
|
||||||
- HUGGINGFACE_API_KEY=${HUGGINGFACE_API_KEY}
|
# - redis
|
||||||
- DSK_DEEPSEEK_API_KEY=${DSK_DEEPSEEK_API_KEY}
|
# - searxng
|
||||||
network_mode: "host"
|
# networks:
|
||||||
|
# - agentic-seek-net
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
redis-data:
|
redis-data:
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ import axios from 'axios';
|
|||||||
import './App.css';
|
import './App.css';
|
||||||
import { colors } from './colors';
|
import { colors } from './colors';
|
||||||
|
|
||||||
const BACKEND_URL = process.env.BACKEND_PORT || 'http://0.0.0.0:8000';
|
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const [query, setQuery] = useState('');
|
const [query, setQuery] = useState('');
|
||||||
const [messages, setMessages] = useState([]);
|
const [messages, setMessages] = useState([]);
|
||||||
@@ -29,7 +27,7 @@ function App() {
|
|||||||
|
|
||||||
const checkHealth = async () => {
|
const checkHealth = async () => {
|
||||||
try {
|
try {
|
||||||
await axios.get(`${BACKEND_URL}/health`);
|
await axios.get('http://127.0.0.1:8000/health');
|
||||||
setIsOnline(true);
|
setIsOnline(true);
|
||||||
console.log('System is online');
|
console.log('System is online');
|
||||||
} catch {
|
} catch {
|
||||||
@@ -41,7 +39,7 @@ function App() {
|
|||||||
const fetchScreenshot = async () => {
|
const fetchScreenshot = async () => {
|
||||||
try {
|
try {
|
||||||
const timestamp = new Date().getTime();
|
const timestamp = new Date().getTime();
|
||||||
const res = await axios.get(`${BACKEND_URL}/screenshots/updated_screen.png?timestamp=${timestamp}`, {
|
const res = await axios.get(`http://127.0.0.1:8000/screenshots/updated_screen.png?timestamp=${timestamp}`, {
|
||||||
responseType: 'blob'
|
responseType: 'blob'
|
||||||
});
|
});
|
||||||
console.log('Screenshot fetched successfully');
|
console.log('Screenshot fetched successfully');
|
||||||
@@ -92,7 +90,7 @@ function App() {
|
|||||||
|
|
||||||
const fetchLatestAnswer = async () => {
|
const fetchLatestAnswer = async () => {
|
||||||
try {
|
try {
|
||||||
const res = await axios.get(`${BACKEND_URL}/latest_answer`);
|
const res = await axios.get('http://127.0.0.1:8000/latest_answer');
|
||||||
const data = res.data;
|
const data = res.data;
|
||||||
|
|
||||||
updateData(data);
|
updateData(data);
|
||||||
@@ -143,7 +141,7 @@ function App() {
|
|||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
setError(null);
|
setError(null);
|
||||||
try {
|
try {
|
||||||
const res = await axios.get(`${BACKEND_URL}/stop`);
|
const res = await axios.get('http://127.0.0.1:8000/stop');
|
||||||
setStatus("Requesting stop...");
|
setStatus("Requesting stop...");
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Error stopping the agent:', err);
|
console.error('Error stopping the agent:', err);
|
||||||
@@ -164,7 +162,7 @@ function App() {
|
|||||||
try {
|
try {
|
||||||
console.log('Sending query:', query);
|
console.log('Sending query:', query);
|
||||||
setQuery('waiting for response...');
|
setQuery('waiting for response...');
|
||||||
const res = await axios.post(`${BACKEND_URL}/query`, {
|
const res = await axios.post('http://127.0.0.1:8000/query', {
|
||||||
query,
|
query,
|
||||||
tts_enabled: false
|
tts_enabled: false
|
||||||
});
|
});
|
||||||
|
|||||||
+1
-1
@@ -17,6 +17,7 @@ playsound>=1.3.0
|
|||||||
soundfile>=0.13.1
|
soundfile>=0.13.1
|
||||||
transformers>=4.46.3
|
transformers>=4.46.3
|
||||||
torch>=2.4.1
|
torch>=2.4.1
|
||||||
|
python-dotenv>=1.0.0
|
||||||
ollama>=0.4.7
|
ollama>=0.4.7
|
||||||
scipy>=1.9.3
|
scipy>=1.9.3
|
||||||
soundfile>=0.13.1
|
soundfile>=0.13.1
|
||||||
@@ -40,7 +41,6 @@ fake_useragent>=2.1.0
|
|||||||
selenium_stealth>=1.0.6
|
selenium_stealth>=1.0.6
|
||||||
undetected-chromedriver>=3.5.5
|
undetected-chromedriver>=3.5.5
|
||||||
sentencepiece>=0.2.0
|
sentencepiece>=0.2.0
|
||||||
together>=1.5.0
|
|
||||||
tqdm>4
|
tqdm>4
|
||||||
openai
|
openai
|
||||||
sniffio
|
sniffio
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ class BrowserAgent(Agent):
|
|||||||
self.memory = Memory(self.load_prompt(prompt_path),
|
self.memory = Memory(self.load_prompt(prompt_path),
|
||||||
recover_last_session=False, # session recovery in handled by the interaction class
|
recover_last_session=False, # session recovery in handled by the interaction class
|
||||||
memory_compression=False,
|
memory_compression=False,
|
||||||
model_provider=provider.get_model_name() if provider else None)
|
model_provider=provider.get_model_name())
|
||||||
|
|
||||||
def get_today_date(self) -> str:
|
def get_today_date(self) -> str:
|
||||||
"""Get the date"""
|
"""Get the date"""
|
||||||
@@ -77,14 +77,14 @@ class BrowserAgent(Agent):
|
|||||||
def get_unvisited_links(self) -> List[str]:
|
def get_unvisited_links(self) -> List[str]:
|
||||||
return "\n".join([f"[{i}] {link}" for i, link in enumerate(self.navigable_links) if link not in self.search_history])
|
return "\n".join([f"[{i}] {link}" for i, link in enumerate(self.navigable_links) if link not in self.search_history])
|
||||||
|
|
||||||
def make_newsearch_prompt(self, prompt: str, search_result: dict) -> str:
|
def make_newsearch_prompt(self, user_prompt: str, search_result: dict) -> str:
|
||||||
search_choice = self.stringify_search_results(search_result)
|
search_choice = self.stringify_search_results(search_result)
|
||||||
self.logger.info(f"Search results: {search_choice}")
|
self.logger.info(f"Search results: {search_choice}")
|
||||||
return f"""
|
return f"""
|
||||||
Based on the search result:
|
Based on the search result:
|
||||||
{search_choice}
|
{search_choice}
|
||||||
Your goal is to find accurate and complete information to satisfy the user’s request.
|
Your goal is to find accurate and complete information to satisfy the user’s request.
|
||||||
User request: {prompt}
|
User request: {user_prompt}
|
||||||
To proceed, choose a relevant link from the search results. Announce your choice by saying: "I will navigate to <link>"
|
To proceed, choose a relevant link from the search results. Announce your choice by saying: "I will navigate to <link>"
|
||||||
Do not explain your choice.
|
Do not explain your choice.
|
||||||
"""
|
"""
|
||||||
@@ -235,17 +235,13 @@ class BrowserAgent(Agent):
|
|||||||
return links
|
return links
|
||||||
|
|
||||||
def select_link(self, links: List[str]) -> str | None:
|
def select_link(self, links: List[str]) -> str | None:
|
||||||
"""
|
|
||||||
Select the first unvisited link that is not the current page.
|
|
||||||
Preference is given to links not in search_history.
|
|
||||||
"""
|
|
||||||
for lk in links:
|
for lk in links:
|
||||||
if lk == self.current_page or lk in self.search_history:
|
if lk == self.current_page:
|
||||||
self.logger.info(f"Skipping already visited or current link: {lk}")
|
self.logger.info(f"Already visited {lk}. Skipping.")
|
||||||
continue
|
continue
|
||||||
self.logger.info(f"Selected link: {lk}")
|
self.logger.info(f"Selected link: {lk}")
|
||||||
return lk
|
return lk
|
||||||
self.logger.warning("No suitable link selected.")
|
self.logger.warning("No link selected.")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def get_page_text(self, limit_to_model_ctx = False) -> str:
|
def get_page_text(self, limit_to_model_ctx = False) -> str:
|
||||||
@@ -400,10 +396,7 @@ class BrowserAgent(Agent):
|
|||||||
if (link == None and len(extracted_form) < 3) or Action.GO_BACK.value in answer or link in self.search_history:
|
if (link == None and len(extracted_form) < 3) or Action.GO_BACK.value in answer or link in self.search_history:
|
||||||
pretty_print(f"Going back to results. Still {len(unvisited)}", color="status")
|
pretty_print(f"Going back to results. Still {len(unvisited)}", color="status")
|
||||||
self.status_message = "Going back to search results..."
|
self.status_message = "Going back to search results..."
|
||||||
request_prompt = user_prompt
|
prompt = self.make_newsearch_prompt(user_prompt, unvisited)
|
||||||
if link is None:
|
|
||||||
request_prompt += f"\nYou previously choosen:\n{self.last_answer} but the website is unavailable. Consider other options."
|
|
||||||
prompt = self.make_newsearch_prompt(request_prompt, unvisited)
|
|
||||||
self.search_history.append(link)
|
self.search_history.append(link)
|
||||||
self.current_page = link
|
self.current_page = link
|
||||||
continue
|
continue
|
||||||
|
|||||||
+7
-25
@@ -19,7 +19,6 @@ import time
|
|||||||
import random
|
import random
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
import uuid
|
|
||||||
import tempfile
|
import tempfile
|
||||||
import markdownify
|
import markdownify
|
||||||
import sys
|
import sys
|
||||||
@@ -43,14 +42,7 @@ def get_chrome_path() -> str:
|
|||||||
paths = ["/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
|
paths = ["/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
|
||||||
"/Applications/Google Chrome Beta.app/Contents/MacOS/Google Chrome Beta"]
|
"/Applications/Google Chrome Beta.app/Contents/MacOS/Google Chrome Beta"]
|
||||||
else: # Linux
|
else: # Linux
|
||||||
paths = ["/usr/bin/google-chrome",
|
paths = ["/usr/bin/google-chrome", "/usr/bin/chromium-browser", "/usr/bin/chromium", "/opt/chrome/chrome", "/usr/local/bin/chrome"]
|
||||||
"/opt/chrome/chrome",
|
|
||||||
"/usr/bin/chromium-browser",
|
|
||||||
"/usr/bin/chromium",
|
|
||||||
"/usr/local/bin/chrome",
|
|
||||||
"/opt/google/chrome/chrome-headless-shell",
|
|
||||||
#"/app/chrome_bundle/chrome136/chrome-linux64"
|
|
||||||
]
|
|
||||||
|
|
||||||
for path in paths:
|
for path in paths:
|
||||||
if os.path.exists(path) and os.access(path, os.X_OK):
|
if os.path.exists(path) and os.access(path, os.X_OK):
|
||||||
@@ -83,7 +75,6 @@ def install_chromedriver() -> str:
|
|||||||
chromedriver_path = shutil.which("chromedriver")
|
chromedriver_path = shutil.which("chromedriver")
|
||||||
if not chromedriver_path:
|
if not chromedriver_path:
|
||||||
try:
|
try:
|
||||||
print("ChromeDriver not found, attempting to install automatically...")
|
|
||||||
chromedriver_path = chromedriver_autoinstaller.install()
|
chromedriver_path = chromedriver_autoinstaller.install()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise FileNotFoundError(
|
raise FileNotFoundError(
|
||||||
@@ -129,28 +120,17 @@ def create_driver(headless=False, stealth_mode=True, crx_path="./crx/nopecha.crx
|
|||||||
chrome_options.binary_location = chrome_path
|
chrome_options.binary_location = chrome_path
|
||||||
|
|
||||||
if headless:
|
if headless:
|
||||||
#chrome_options.add_argument("--headless")
|
chrome_options.add_argument("--headless")
|
||||||
chrome_options.add_argument("--headless=new")
|
|
||||||
chrome_options.add_argument("--disable-gpu")
|
chrome_options.add_argument("--disable-gpu")
|
||||||
chrome_options.add_argument("--disable-webgl")
|
chrome_options.add_argument("--disable-webgl")
|
||||||
user_data_dir = tempfile.mkdtemp()
|
user_data_dir = tempfile.mkdtemp()
|
||||||
user_agent = get_random_user_agent()
|
user_agent = get_random_user_agent()
|
||||||
width, height = (1920, 1080)
|
width, height = (1920, 1080)
|
||||||
user_data_dir = tempfile.mkdtemp(prefix="chrome_profile_")
|
chrome_options.add_argument(f"--user-data-dir={user_data_dir}")
|
||||||
chrome_options.add_argument("--no-sandbox")
|
|
||||||
chrome_options.add_argument('--disable-dev-shm-usage')
|
|
||||||
profile_dir = f"/tmp/chrome_profile_{uuid.uuid4().hex[:8]}"
|
|
||||||
chrome_options.add_argument(f'--user-data-dir={profile_dir}')
|
|
||||||
chrome_options.add_argument(f"--accept-lang={lang}-{lang.upper()},{lang};q=0.9")
|
chrome_options.add_argument(f"--accept-lang={lang}-{lang.upper()},{lang};q=0.9")
|
||||||
chrome_options.add_argument("--disable-extensions")
|
|
||||||
chrome_options.add_argument("--disable-background-timer-throttling")
|
|
||||||
chrome_options.add_argument("--timezone=Europe/Paris")
|
chrome_options.add_argument("--timezone=Europe/Paris")
|
||||||
chrome_options.add_argument('--remote-debugging-port=9222')
|
chrome_options.add_argument("--no-sandbox")
|
||||||
chrome_options.add_argument('--disable-background-timer-throttling')
|
chrome_options.add_argument("--disable-dev-shm-usage")
|
||||||
chrome_options.add_argument('--disable-backgrounding-occluded-windows')
|
|
||||||
chrome_options.add_argument('--disable-renderer-backgrounding')
|
|
||||||
chrome_options.add_argument('--disable-features=TranslateUI')
|
|
||||||
chrome_options.add_argument('--disable-ipc-flooding-protection')
|
|
||||||
chrome_options.add_argument("--mute-audio")
|
chrome_options.add_argument("--mute-audio")
|
||||||
chrome_options.add_argument("--disable-notifications")
|
chrome_options.add_argument("--disable-notifications")
|
||||||
chrome_options.add_argument("--autoplay-policy=user-gesture-required")
|
chrome_options.add_argument("--autoplay-policy=user-gesture-required")
|
||||||
@@ -718,6 +698,8 @@ if __name__ == "__main__":
|
|||||||
|
|
||||||
input("press enter to continue")
|
input("press enter to continue")
|
||||||
print("AntiCaptcha / Form Test")
|
print("AntiCaptcha / Form Test")
|
||||||
|
browser.go_to("https://www.google.com/recaptcha/api2/demo")
|
||||||
|
time.sleep(50)
|
||||||
browser.go_to("https://bot.sannysoft.com")
|
browser.go_to("https://bot.sannysoft.com")
|
||||||
time.sleep(5)
|
time.sleep(5)
|
||||||
#txt = browser.get_text()
|
#txt = browser.get_text()
|
||||||
|
|||||||
+43
-3
@@ -1,6 +1,8 @@
|
|||||||
from typing import List, Tuple, Type, Dict
|
from typing import List, Tuple, Type, Dict
|
||||||
import re
|
import re
|
||||||
import langid
|
import langid
|
||||||
|
import nltk
|
||||||
|
from nltk.sentiment.vader import SentimentIntensityAnalyzer
|
||||||
from transformers import MarianMTModel, MarianTokenizer
|
from transformers import MarianMTModel, MarianTokenizer
|
||||||
|
|
||||||
from sources.utility import pretty_print, animate_thinking
|
from sources.utility import pretty_print, animate_thinking
|
||||||
@@ -14,6 +16,7 @@ class LanguageUtility:
|
|||||||
args:
|
args:
|
||||||
supported_language: list of languages for translation, determine which Helsinki-NLP model to load
|
supported_language: list of languages for translation, determine which Helsinki-NLP model to load
|
||||||
"""
|
"""
|
||||||
|
self.sid = None
|
||||||
self.translators_tokenizer = None
|
self.translators_tokenizer = None
|
||||||
self.translators_model = None
|
self.translators_model = None
|
||||||
self.logger = Logger("language.log")
|
self.logger = Logger("language.log")
|
||||||
@@ -22,6 +25,11 @@ class LanguageUtility:
|
|||||||
|
|
||||||
def load_model(self) -> None:
|
def load_model(self) -> None:
|
||||||
animate_thinking("Loading language utility...", color="status")
|
animate_thinking("Loading language utility...", color="status")
|
||||||
|
try:
|
||||||
|
nltk.data.find('vader_lexicon')
|
||||||
|
except LookupError:
|
||||||
|
nltk.download('vader_lexicon')
|
||||||
|
self.sid = SentimentIntensityAnalyzer()
|
||||||
self.translators_tokenizer = {lang: MarianTokenizer.from_pretrained(f"Helsinki-NLP/opus-mt-{lang}-en") for lang in self.supported_language if lang != "en"}
|
self.translators_tokenizer = {lang: MarianTokenizer.from_pretrained(f"Helsinki-NLP/opus-mt-{lang}-en") for lang in self.supported_language if lang != "en"}
|
||||||
self.translators_model = {lang: MarianMTModel.from_pretrained(f"Helsinki-NLP/opus-mt-{lang}-en") for lang in self.supported_language if lang != "en"}
|
self.translators_model = {lang: MarianMTModel.from_pretrained(f"Helsinki-NLP/opus-mt-{lang}-en") for lang in self.supported_language if lang != "en"}
|
||||||
|
|
||||||
@@ -57,17 +65,49 @@ class LanguageUtility:
|
|||||||
translation = model.generate(**inputs)
|
translation = model.generate(**inputs)
|
||||||
return tokenizer.decode(translation[0], skip_special_tokens=True)
|
return tokenizer.decode(translation[0], skip_special_tokens=True)
|
||||||
|
|
||||||
|
def detect_emotion(self, text: str) -> str:
|
||||||
|
"""
|
||||||
|
Detect the dominant emotion in the given text
|
||||||
|
Args:
|
||||||
|
text: string to analyze
|
||||||
|
Returns: string of the dominant emotion
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
scores = self.sid.polarity_scores(text)
|
||||||
|
emotions = {
|
||||||
|
'Happy': max(scores['pos'], 0),
|
||||||
|
'Angry': 0,
|
||||||
|
'Sad': max(scores['neg'], 0),
|
||||||
|
'Fear': 0,
|
||||||
|
'Surprise': 0
|
||||||
|
}
|
||||||
|
if scores['compound'] < -0.5:
|
||||||
|
emotions['Angry'] = abs(scores['compound']) * 0.5
|
||||||
|
emotions['Fear'] = abs(scores['compound']) * 0.5
|
||||||
|
elif scores['compound'] > 0.5:
|
||||||
|
emotions['Happy'] = scores['compound']
|
||||||
|
emotions['Surprise'] = scores['compound'] * 0.5
|
||||||
|
dominant_emotion = max(emotions, key=emotions.get)
|
||||||
|
if emotions[dominant_emotion] == 0:
|
||||||
|
return 'Neutral'
|
||||||
|
self.logger.info(f"Emotion: {dominant_emotion} for text: {text}")
|
||||||
|
return dominant_emotion
|
||||||
|
except Exception as e:
|
||||||
|
raise e
|
||||||
|
|
||||||
def analyze(self, text):
|
def analyze(self, text):
|
||||||
"""
|
"""
|
||||||
Combined analysis of language and emotion
|
Combined analysis of language and emotion
|
||||||
Args:
|
Args:
|
||||||
text: string to analyze
|
text: string to analyze
|
||||||
Returns: dictionary with language related information
|
Returns: dictionary with language and emotion results
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
language = self.detect_language(text)
|
language = self.detect_language(text)
|
||||||
|
emotions = self.detect_emotion(text)
|
||||||
return {
|
return {
|
||||||
"language": language
|
"language": language,
|
||||||
|
"emotions": emotions
|
||||||
}
|
}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise e
|
raise e
|
||||||
@@ -85,4 +125,4 @@ if __name__ == "__main__":
|
|||||||
pretty_print(f"Language: {detector.detect_language(text)}", color="status")
|
pretty_print(f"Language: {detector.detect_language(text)}", color="status")
|
||||||
result = detector.analyze(text)
|
result = detector.analyze(text)
|
||||||
trans = detector.translate(text, result['language'])
|
trans = detector.translate(text, result['language'])
|
||||||
pretty_print(f"Translation: {trans} - from: {result['language']}")
|
pretty_print(f"Translation: {trans} - from: {result['language']} - Emotion: {result['emotions']}")
|
||||||
+17
-12
@@ -50,15 +50,19 @@ class Tools():
|
|||||||
def set_allow_language_exec_bash(value: bool) -> None:
|
def set_allow_language_exec_bash(value: bool) -> None:
|
||||||
self.allow_language_exec_bash = value
|
self.allow_language_exec_bash = value
|
||||||
|
|
||||||
def safe_get_work_dir_path(self):
|
def check_config_dir_validity(self):
|
||||||
path = None
|
"""Check if the config directory is valid."""
|
||||||
path = os.getenv('WORK_DIR', path)
|
path = self.config['MAIN']['work_dir']
|
||||||
if path is None or path == "":
|
if path == "":
|
||||||
path = self.config['MAIN']['work_dir'] if 'MAIN' in self.config and 'work_dir' in self.config['MAIN'] else None
|
print("WARNING: Work directory not set in config.ini")
|
||||||
if path is None or path == "":
|
return False
|
||||||
print("No work directory specified, using default.")
|
if path.lower() == "none":
|
||||||
path = self.create_work_dir()
|
print("WARNING: Work directory set to none in config.ini")
|
||||||
return path
|
return False
|
||||||
|
if not os.path.exists(path):
|
||||||
|
print(f"WARNING: Work directory {path} does not exist")
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
def config_exists(self):
|
def config_exists(self):
|
||||||
"""Check if the config file exists."""
|
"""Check if the config file exists."""
|
||||||
@@ -69,10 +73,11 @@ class Tools():
|
|||||||
default_path = os.path.dirname(os.getcwd())
|
default_path = os.path.dirname(os.getcwd())
|
||||||
if self.config_exists():
|
if self.config_exists():
|
||||||
self.config.read('./config.ini')
|
self.config.read('./config.ini')
|
||||||
workdir_path = self.safe_get_work_dir_path()
|
config_path = self.config['MAIN']['work_dir']
|
||||||
|
dir_path = default_path if not self.check_config_dir_validity() else config_path
|
||||||
else:
|
else:
|
||||||
workdir_path = default_path
|
dir_path = default_path
|
||||||
return workdir_path
|
return dir_path
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def execute(self, blocks:[str], safety:bool) -> str:
|
def execute(self, blocks:[str], safety:bool) -> str:
|
||||||
|
|||||||
+6
-18
@@ -1,22 +1,10 @@
|
|||||||
@echo off
|
@echo off
|
||||||
|
|
||||||
if "%1"=="full" (
|
docker-compose up
|
||||||
echo Starting full deployment...
|
if %ERRORLEVEL% neq 0 (
|
||||||
) else (
|
echo Error: Failed to start containers. Check Docker logs with 'docker compose logs'.
|
||||||
echo Starting partial deployment... (backend run on host), use "full" to run all services in containers
|
echo Possible fixes: Ensure Docker Desktop is running or check if port 8080 is free.
|
||||||
|
exit /b 1
|
||||||
)
|
)
|
||||||
|
|
||||||
REM Stop all containers
|
timeout /t 10 /nobreak >nul
|
||||||
echo Stopping containers...
|
|
||||||
docker stop $(docker ps -aq) >nul 2>&1
|
|
||||||
|
|
||||||
REM Generate secret key
|
|
||||||
for /f %%i in ('powershell -command "[System.Web.Security.Membership]::GeneratePassword(64,0)"') do set SEARXNG_SECRET_KEY=%%i
|
|
||||||
|
|
||||||
if "%1"=="full" (
|
|
||||||
docker compose up -d backend
|
|
||||||
timeout /t 5 /nobreak >nul
|
|
||||||
docker compose --profile full up
|
|
||||||
) else (
|
|
||||||
docker compose --profile core up
|
|
||||||
)
|
|
||||||
+8
-63
@@ -1,35 +1,12 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
source .env
|
|
||||||
|
|
||||||
command_exists() {
|
command_exists() {
|
||||||
command -v "$1" &> /dev/null
|
command -v "$1" &> /dev/null
|
||||||
}
|
}
|
||||||
if [ -z "$WORK_DIR" ]; then
|
|
||||||
echo "Error: WORK_DIR environment variable is not set. Please set it in your .env file."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
#
|
||||||
dir_size_bytes=$(du -s -b "$WORK_DIR" 2>/dev/null | awk '{print $1}')
|
# Check if Docker is installed é running
|
||||||
else
|
#
|
||||||
dir_size_bytes=$(du -s --bytes "$WORK_DIR" 2>/dev/null | awk '{print $1}')
|
|
||||||
fi
|
|
||||||
|
|
||||||
max_size_bytes=$((2 * 1024 * 1024 * 1024))
|
|
||||||
|
|
||||||
echo "Mounting $WORK_DIR ($dir_size_bytes bytes) to docker."
|
|
||||||
|
|
||||||
if [ "$dir_size_bytes" -gt "$max_size_bytes" ]; then
|
|
||||||
echo "Error: WORK_DIR ($WORK_DIR) contains more than 2GB of data ($(du -sh "$WORK_DIR" 2>/dev/null | awk '{print $1}'))."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$1" = "full" ]; then
|
|
||||||
echo "Starting full deployment with backend and all services..."
|
|
||||||
else
|
|
||||||
echo "Starting core deployment with frontend and search services only... use ./start_services.sh full to start backend as well"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! command_exists docker; then
|
if ! command_exists docker; then
|
||||||
echo "Error: Docker is not installed. Please install Docker first."
|
echo "Error: Docker is not installed. Please install Docker first."
|
||||||
@@ -83,47 +60,15 @@ if [ ! -f "docker-compose.yml" ]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Stop all running containers to ensure a clean state
|
# 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)
|
||||||
echo "All containers stopped"
|
echo "All containers stopped"
|
||||||
|
|
||||||
# export searxng secret key
|
if ! $COMPOSE_CMD up; then
|
||||||
export SEARXNG_SECRET_KEY=$(openssl rand -hex 32)
|
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."
|
||||||
if [ "$1" = "full" ]; then
|
exit 1
|
||||||
# First start backend and wait for it to be healthy
|
|
||||||
echo "Full docker deployement. 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
|
|
||||||
if ! $COMPOSE_CMD --profile full 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."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
if ! $COMPOSE_CMD --profile core 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."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
sleep 10
|
sleep 10
|
||||||
@@ -23,7 +23,7 @@ class TestBrowserAgentParsing(unittest.TestCase):
|
|||||||
"https://thriveonai.com/15-ai-startups-in-japan-to-take-note-of",
|
"https://thriveonai.com/15-ai-startups-in-japan-to-take-note-of",
|
||||||
"www.google.com",
|
"www.google.com",
|
||||||
"https://test.org/about?page=1",
|
"https://test.org/about?page=1",
|
||||||
"https://weatherstack.com/documentation"
|
"https://weatherstack.com/documentation",
|
||||||
]
|
]
|
||||||
result = self.agent.extract_links(test_text)
|
result = self.agent.extract_links(test_text)
|
||||||
self.assertEqual(result, expected)
|
self.assertEqual(result, expected)
|
||||||
|
|||||||
Reference in New Issue
Block a user