Merge pull request #122 from Fosowl/dev
Huge refactor and Frontend interface integration
@@ -1,20 +1,37 @@
|
|||||||
*.wav
|
*.wav
|
||||||
*.DS_Store
|
*.DS_Store
|
||||||
*.log
|
*.log
|
||||||
cookies.json
|
|
||||||
*.tmp
|
*.tmp
|
||||||
*.safetensors
|
*.safetensors
|
||||||
config.ini
|
|
||||||
test_agent.py
|
|
||||||
*.egg-info
|
*.egg-info
|
||||||
|
cookies.json
|
||||||
|
test_agent.py
|
||||||
|
config.ini
|
||||||
.voices/
|
.voices/
|
||||||
experimental/
|
experimental/
|
||||||
|
.logs/
|
||||||
|
.screenshots/*.png
|
||||||
|
.screenshots/*.jpg
|
||||||
conversations/
|
conversations/
|
||||||
agentic_env/*
|
agentic_env/*
|
||||||
|
agentic_seek_env/*
|
||||||
.env
|
.env
|
||||||
*/.env
|
*/.env
|
||||||
dsk/
|
dsk/
|
||||||
|
|
||||||
|
### react ###
|
||||||
|
.DS_*
|
||||||
|
*.log
|
||||||
|
logs
|
||||||
|
**/*.backup.*
|
||||||
|
**/*.back.*
|
||||||
|
node_modules
|
||||||
|
bower_components
|
||||||
|
*.sublime*
|
||||||
|
psd
|
||||||
|
thumb
|
||||||
|
sketch
|
||||||
|
|
||||||
|
|
||||||
# Byte-compiled / optimized / DLL files
|
# Byte-compiled / optimized / DLL files
|
||||||
__pycache__/
|
__pycache__/
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
# Use official Python 3.11 image as the base
|
|
||||||
FROM python:3.11
|
|
||||||
|
|
||||||
# Set working directory
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
# Install system dependencies
|
|
||||||
RUN apt-get update && apt-get install -y \
|
|
||||||
gcc \
|
|
||||||
g++ \
|
|
||||||
gfortran \
|
|
||||||
libportaudio2 \
|
|
||||||
portaudio19-dev \
|
|
||||||
ffmpeg \
|
|
||||||
libavcodec-dev \
|
|
||||||
libavformat-dev \
|
|
||||||
libavutil-dev \
|
|
||||||
chromium \
|
|
||||||
chromium-driver \
|
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
RUN pip cache purge
|
|
||||||
|
|
||||||
COPY . .
|
|
||||||
|
|
||||||
RUN BLIS_ARCH=generic pip install --no-cache-dir -r requirements.txt
|
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
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 && \
|
||||||
|
|
||||||
|
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 sources/ ./sources/
|
||||||
|
COPY prompts/ ./prompts/
|
||||||
|
COPY crx/ crx/
|
||||||
|
COPY llm_router/ llm_router/
|
||||||
|
COPY .env .
|
||||||
|
COPY config.ini .
|
||||||
|
|
||||||
|
# Expose port
|
||||||
|
EXPOSE 8000
|
||||||
|
|
||||||
|
# Run the application
|
||||||
|
CMD ["python3", "api.py"]
|
||||||
@@ -43,7 +43,7 @@ https://github.com/user-attachments/assets/4bd5faf6-459f-4f94-bd1d-238c4b331469
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## **Installation**
|
## Installation
|
||||||
|
|
||||||
Make sure you have chrome driver, docker and python3.10 (or newer) installed.
|
Make sure you have chrome driver, docker and python3.10 (or newer) installed.
|
||||||
|
|
||||||
@@ -81,54 +81,47 @@ pip3 install -r requirements.txt
|
|||||||
python3 setup.py install
|
python3 setup.py install
|
||||||
```
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Run locally on your machine
|
## Setup for running LLM locally on your machine
|
||||||
|
|
||||||
**We recommend using at the very least Deepseek 14B, smaller models will struggle with tasks especially for web browsing.**
|
**We recommend using at the very least Deepseek 14B, smaller models will struggle with tasks especially for web browsing.**
|
||||||
|
|
||||||
### 1️⃣ **Download Models**
|
|
||||||
|
|
||||||
Make sure you have [Ollama](https://ollama.com/) installed.
|
**Setup your local provider**
|
||||||
|
|
||||||
Download the `deepseek-r1:14b` model from [DeepSeek](https://deepseek.com/models)
|
Start your local provider, for example with ollama:
|
||||||
|
|
||||||
```sh
|
|
||||||
ollama pull deepseek-r1:14b
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2️ **Run the Assistant (Ollama)**
|
|
||||||
|
|
||||||
Start the ollama server
|
|
||||||
```sh
|
```sh
|
||||||
ollama serve
|
ollama serve
|
||||||
```
|
```
|
||||||
|
|
||||||
Change the config.ini file to set the provider_name to `ollama` and provider_model to `deepseek-r1:14b`
|
See below for a list of local supported provider.
|
||||||
|
|
||||||
|
**Update the config.ini**
|
||||||
|
|
||||||
|
Change the config.ini file to set the provider_name to a supported provider and provider_model to `deepseek-r1:14b`
|
||||||
|
|
||||||
NOTE: `deepseek-r1:14b`is an example, use a bigger model if your hardware allow it.
|
NOTE: `deepseek-r1:14b`is an example, use a bigger model if your hardware allow it.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
[MAIN]
|
[MAIN]
|
||||||
is_local = True
|
is_local = True
|
||||||
provider_name = ollama
|
provider_name = ollama # or lm-studio, openai, etc..
|
||||||
provider_model = deepseek-r1:14b
|
provider_model = deepseek-r1:14b
|
||||||
provider_server_address = 127.0.0.1:11434
|
provider_server_address = 127.0.0.1:11434
|
||||||
```
|
```
|
||||||
|
|
||||||
start all services :
|
**List of local providers**
|
||||||
|
|
||||||
```sh
|
| Provider | Local? | Description |
|
||||||
sudo ./start_services.sh # MacOS
|
|-----------|--------|-----------------------------------------------------------|
|
||||||
start ./start_services.cmd # Window
|
| ollama | Yes | Run LLMs locally with ease using ollama as a LLM provider |
|
||||||
```
|
| lm-studio | Yes | Run LLM locally with LM studio (set `provider_name` to `lm-studio`)|
|
||||||
|
| openai | Yes | Use openai compatible API |
|
||||||
|
|
||||||
Run the assistant:
|
|
||||||
|
|
||||||
```sh
|
Next step: [Start services and run AgenticSeek](#Start-services-and-Run)
|
||||||
python3 main.py
|
|
||||||
```
|
|
||||||
|
|
||||||
*See the **Usage** section if you don't understand how to use it*
|
|
||||||
|
|
||||||
*See the **Known issues** section if you are having issues*
|
*See the **Known issues** section if you are having issues*
|
||||||
|
|
||||||
@@ -138,17 +131,82 @@ python3 main.py
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Setup to run with an API
|
||||||
|
|
||||||
|
Set the desired provider in the `config.ini`
|
||||||
|
|
||||||
|
```sh
|
||||||
|
[MAIN]
|
||||||
|
is_local = False
|
||||||
|
provider_name = openai
|
||||||
|
provider_model = gpt-4o
|
||||||
|
provider_server_address = 127.0.0.1:5000
|
||||||
|
```
|
||||||
|
|
||||||
|
WARNING: Make sure there is not trailing space in the config.
|
||||||
|
|
||||||
|
Set `is_local` to True if using a local openai-based api.
|
||||||
|
|
||||||
|
Change the IP address if your openai-based api run on your own server.
|
||||||
|
|
||||||
|
Next step: [Start services and run AgenticSeek](#Start-services-and-Run)
|
||||||
|
|
||||||
|
*See the **Known issues** section if you are having issues*
|
||||||
|
|
||||||
|
*See the **Config** section for detailled config file explanation.*
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Start services and Run
|
||||||
|
|
||||||
|
Activate your python env if needed.
|
||||||
|
```sh
|
||||||
|
source agentic_seek_env/bin/activate
|
||||||
|
```
|
||||||
|
|
||||||
|
Start required services. This will start all services from the docker-compose.yml, including:
|
||||||
|
- searxng
|
||||||
|
- redis (required by searxng)
|
||||||
|
- frontend
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo ./start_services.sh # MacOS
|
||||||
|
start ./start_services.cmd # Window
|
||||||
|
```
|
||||||
|
|
||||||
|
**Options 1:** Run with the CLI interface.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
python3 cli.py
|
||||||
|
```
|
||||||
|
|
||||||
|
**Options 2:** Run with the Web interface.
|
||||||
|
|
||||||
|
Note: Currently we advice you run the CLI instead. Web interface is an active work in progress.
|
||||||
|
|
||||||
|
Start the backend.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
python3 api.py
|
||||||
|
```
|
||||||
|
|
||||||
|
Go to `http://localhost:3000/` and you should see the web interface.
|
||||||
|
|
||||||
|
Please note that the Web interface doesn't stream messages at the moment.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
Make sure the services are up and running with `./start_services.sh` and run the agenticSeek with `python3 main.py`
|
Make sure the services are up and running with `./start_services.sh` and run the AgenticSeek with `python3 main.py`
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
sudo ./start_services.sh
|
sudo ./start_services.sh
|
||||||
python3 main.py
|
python3 cli.py
|
||||||
```
|
```
|
||||||
|
|
||||||
You will be prompted with `>>> `
|
You will be prompted with `>>> `
|
||||||
This indicate agenticSeek await you type for instructions.
|
This indicate AgenticSeek await you type for instructions.
|
||||||
You can also use speech to text by setting `listen = True` in the config.
|
You can also use speech to text by setting `listen = True` in the config.
|
||||||
|
|
||||||
To exit, simply say `goodbye`.
|
To exit, simply say `goodbye`.
|
||||||
@@ -167,7 +225,7 @@ Here are some example usage:
|
|||||||
|
|
||||||
> *Do a web search to find cool tech startup in Japan working on cutting edge AI research*
|
> *Do a web search to find cool tech startup in Japan working on cutting edge AI research*
|
||||||
|
|
||||||
> *Can you find on the internet who created agenticSeek?*
|
> *Can you find on the internet who created AgenticSeek?*
|
||||||
|
|
||||||
> *Can you use a fuel calculator online to estimate the cost of a Nice - Milan trip*
|
> *Can you use a fuel calculator online to estimate the cost of a Nice - Milan trip*
|
||||||
|
|
||||||
@@ -188,7 +246,7 @@ Here are some example usage:
|
|||||||
> *What's the best workout routine ?*
|
> *What's the best workout routine ?*
|
||||||
|
|
||||||
|
|
||||||
After you type your query, agenticSeek will allocate the best agent for the task.
|
After you type your query, AgenticSeek will allocate the best agent for the task.
|
||||||
|
|
||||||
Because this is an early prototype, the agent routing system might not always allocate the right agent based on your query.
|
Because this is an early prototype, the agent routing system might not always allocate the right agent based on your query.
|
||||||
|
|
||||||
@@ -202,12 +260,10 @@ Instead, ask:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## **Run the LLM on your own server**
|
## **Bonus: Setup to run the LLM on your own server**
|
||||||
|
|
||||||
If you have a powerful computer or a server that you can use, but you want to use it from your laptop you have the options to run the LLM on a remote server.
|
If you have a powerful computer or a server that you can use, but you want to use it from your laptop you have the options to run the LLM on a remote server.
|
||||||
|
|
||||||
### 1️⃣ **Set up and start the server scripts**
|
|
||||||
|
|
||||||
On your "server" that will run the AI model, get the ip address
|
On your "server" that will run the AI model, get the ip address
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
@@ -217,8 +273,6 @@ curl https://ipinfo.io/ip # public ip
|
|||||||
|
|
||||||
Note: For Windows or macOS, use ipconfig or ifconfig respectively to find the IP address.
|
Note: For Windows or macOS, use ipconfig or ifconfig respectively to find the IP address.
|
||||||
|
|
||||||
**If you wish to use openai based provider follow the *Run with an API* section.**
|
|
||||||
|
|
||||||
Clone the repository and enter the `server/`folder.
|
Clone the repository and enter the `server/`folder.
|
||||||
|
|
||||||
|
|
||||||
@@ -241,7 +295,6 @@ python3 app.py --provider ollama --port 3333
|
|||||||
|
|
||||||
You have the choice between using `ollama` and `llamacpp` as a LLM service.
|
You have the choice between using `ollama` and `llamacpp` as a LLM service.
|
||||||
|
|
||||||
### 2️⃣ **Run it**
|
|
||||||
|
|
||||||
Now on your personal computer:
|
Now on your personal computer:
|
||||||
|
|
||||||
@@ -256,39 +309,8 @@ provider_model = deepseek-r1:70b
|
|||||||
provider_server_address = x.x.x.x:3333
|
provider_server_address = x.x.x.x:3333
|
||||||
```
|
```
|
||||||
|
|
||||||
Run the assistant:
|
|
||||||
|
|
||||||
```sh
|
Next step: [Start services and run AgenticSeek](#Start-services-and-Run)
|
||||||
sudo ./start_services.sh # start_services.cmd on windows
|
|
||||||
python3 main.py
|
|
||||||
```
|
|
||||||
|
|
||||||
## **Run with an API**
|
|
||||||
|
|
||||||
Set the desired provider in the `config.ini`.
|
|
||||||
|
|
||||||
We recommand together AI if you want to use Qwen/Deepseek-r1. openai or other API work as well.
|
|
||||||
|
|
||||||
```sh
|
|
||||||
[MAIN]
|
|
||||||
is_local = False
|
|
||||||
provider_name = together
|
|
||||||
provider_model = deepseek-ai/DeepSeek-R1-Distill-Llama-70B
|
|
||||||
provider_server_address = 127.0.0.1:5000 # doesn't matter for non local API provider
|
|
||||||
```
|
|
||||||
|
|
||||||
WARNING: Make sure there is not trailing space in the config.
|
|
||||||
|
|
||||||
Set `is_local` to True if using a local openai-based api.
|
|
||||||
|
|
||||||
Change the IP address if your openai-based api run on your own server.
|
|
||||||
|
|
||||||
Run the assistant:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
sudo ./start_services.sh # start_services.cmd on windows
|
|
||||||
python3 main.py
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -333,6 +355,7 @@ speak = False
|
|||||||
listen = False
|
listen = False
|
||||||
work_dir = /Users/mlg/Documents/ai_folder
|
work_dir = /Users/mlg/Documents/ai_folder
|
||||||
jarvis_personality = False
|
jarvis_personality = False
|
||||||
|
languages = en zh
|
||||||
[BROWSER]
|
[BROWSER]
|
||||||
headless_browser = False
|
headless_browser = False
|
||||||
stealth_mode = False
|
stealth_mode = False
|
||||||
@@ -341,20 +364,35 @@ stealth_mode = False
|
|||||||
**Explanation**:
|
**Explanation**:
|
||||||
|
|
||||||
- is_local -> Runs the agent locally (True) or on a remote server (False).
|
- is_local -> Runs the agent locally (True) or on a remote server (False).
|
||||||
|
|
||||||
- provider_name -> The provider to use (one of: `ollama`, `server`, `lm-studio`, `deepseek-api`)
|
- provider_name -> The provider to use (one of: `ollama`, `server`, `lm-studio`, `deepseek-api`)
|
||||||
|
|
||||||
- provider_model -> The model used, e.g., deepseek-r1:32b.
|
- provider_model -> The model used, e.g., deepseek-r1:32b.
|
||||||
|
|
||||||
- provider_server_address -> Server address, e.g., 127.0.0.1:11434 for local. Set to anything for non-local API.
|
- provider_server_address -> Server address, e.g., 127.0.0.1:11434 for local. Set to anything for non-local API.
|
||||||
|
|
||||||
- agent_name -> Name of the agent, e.g., Friday. Used as a trigger word for TTS.
|
- agent_name -> Name of the agent, e.g., Friday. Used as a trigger word for TTS.
|
||||||
|
|
||||||
- recover_last_session -> Restarts from last session (True) or not (False).
|
- recover_last_session -> Restarts from last session (True) or not (False).
|
||||||
|
|
||||||
- save_session -> Saves session data (True) or not (False).
|
- save_session -> Saves session data (True) or not (False).
|
||||||
|
|
||||||
- speak -> Enables voice output (True) or not (False).
|
- speak -> Enables voice output (True) or not (False).
|
||||||
|
|
||||||
- listen -> listen to voice input (True) or not (False).
|
- listen -> listen to voice input (True) or not (False).
|
||||||
|
|
||||||
- work_dir -> Folder the AI will have access to. eg: /Users/user/Documents/.
|
- work_dir -> Folder the AI will have access to. eg: /Users/user/Documents/.
|
||||||
|
|
||||||
- jarvis_personality -> Uses a JARVIS-like personality (True) or not (False). This simply change the prompt file.
|
- jarvis_personality -> Uses a JARVIS-like personality (True) or not (False). This simply change the prompt file.
|
||||||
|
|
||||||
- languages -> The list of supported language, needed for the llm router to work properly, avoid putting too many or too similar languages.
|
- languages -> The list of supported language, needed for the llm router to work properly, avoid putting too many or too similar languages.
|
||||||
|
|
||||||
- headless_browser -> Runs browser without a visible window (True) or not (False).
|
- headless_browser -> Runs browser without a visible window (True) or not (False).
|
||||||
|
|
||||||
- stealth_mode -> Make bot detector time harder. Only downside is you have to manually install the anticaptcha extension.
|
- stealth_mode -> Make bot detector time harder. Only downside is you have to manually install the anticaptcha extension.
|
||||||
|
|
||||||
|
- languages -> List of supported languages. Required for agent routing system. The longer the languages list the more model will be downloaded.
|
||||||
|
|
||||||
## Providers
|
## Providers
|
||||||
|
|
||||||
The table below show the available providers:
|
The table below show the available providers:
|
||||||
@@ -363,10 +401,11 @@ The table below show the available providers:
|
|||||||
|-----------|--------|-----------------------------------------------------------|
|
|-----------|--------|-----------------------------------------------------------|
|
||||||
| ollama | Yes | Run LLMs locally with ease using ollama as a LLM provider |
|
| ollama | Yes | Run LLMs locally with ease using ollama as a LLM provider |
|
||||||
| server | Yes | Host the model on another machine, run your local machine |
|
| server | Yes | Host the model on another machine, run your local machine |
|
||||||
| lm-studio | Yes | Run LLM locally with LM studio (set `provider_name` to `lm-studio`)|
|
| lm-studio | Yes | Run LLM locally with LM studio (`lm-studio`) |
|
||||||
| openai | No | Use ChatGPT API (non-private) |
|
| openai | Depends | Use ChatGPT API (non-private) or openai compatible API |
|
||||||
| deepseek-api | No | Deepseek API (non-private) |
|
| deepseek-api | No | Deepseek API (non-private) |
|
||||||
| huggingface| No | Hugging-Face API (non-private) |
|
| huggingface| No | Hugging-Face API (non-private) |
|
||||||
|
| togetherAI | No | Use together AI API (non-private) |
|
||||||
|
|
||||||
To select a provider change the config.ini:
|
To select a provider change the config.ini:
|
||||||
|
|
||||||
@@ -424,7 +463,7 @@ If this section is incomplete please raise an issue.
|
|||||||
|
|
||||||
Deepseek R1 excels at reasoning and tool use for its size. We think it’s a solid fit for our needs other models work fine, but Deepseek is our primary pick.
|
Deepseek R1 excels at reasoning and tool use for its size. We think it’s a solid fit for our needs other models work fine, but Deepseek is our primary pick.
|
||||||
|
|
||||||
**Q: I get an error running `main.py`. What do I do?**
|
**Q: I get an error running `cli.py`. What do I do?**
|
||||||
|
|
||||||
Ensure local is running (`ollama serve`), your `config.ini` matches your provider, and dependencies are installed. If none work feel free to raise an issue.
|
Ensure local is running (`ollama serve`), your `config.ini` matches your provider, and dependencies are installed. If none work feel free to raise an issue.
|
||||||
|
|
||||||
@@ -442,6 +481,8 @@ Unlike Manus, AgenticSeek prioritizes independence from external systems, giving
|
|||||||
|
|
||||||
We’re looking for developers to improve AgenticSeek! Check out open issues or discussion.
|
We’re looking for developers to improve AgenticSeek! Check out open issues or discussion.
|
||||||
|
|
||||||
|
[Contribution guide](./docs/CONTRIBUTING.md)
|
||||||
|
|
||||||
[](https://www.star-history.com/#Fosowl/agenticSeek&Date)
|
[](https://www.star-history.com/#Fosowl/agenticSeek&Date)
|
||||||
|
|
||||||
## Maintainers:
|
## Maintainers:
|
||||||
|
|||||||
@@ -2,29 +2,26 @@
|
|||||||
<img align="center" src="./media/whale_readme.jpg">
|
<img align="center" src="./media/whale_readme.jpg">
|
||||||
<p>
|
<p>
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
|
||||||
[English](./README.md) | 中文 | [日本語](./README_JP.md)
|
[English](./README.md) | 中文 | [日本語](./README_JP.md)
|
||||||
|
|
||||||
# AgenticSeek: 类似 Manus 但基于 Deepseek R1 Agents 的本地模型。
|
# AgenticSeek: 類似 Manus 但基於 Deepseek R1 Agents 的本地模型。
|
||||||
|
|
||||||
|
|
||||||
**Manus AI 的本地替代品**,它是一个具有语音功能的大语言模型秘书,可以 Coding、访问你的电脑文件、浏览网页,并自动修正错误与反省,最重要的是不会向云端传送任何资料。采用 DeepSeek R1 等推理模型构建,完全在本地硬体上运行,进而保证资料的隐私。
|
**Manus AI 的本地替代品**,它是一个具有语音功能的大语言模型秘书,可以 Coding、访问你的电脑文件、浏览网页,并自动修正错误与反省,最重要的是不会向云端传送任何资料。采用 DeepSeek R1 等推理模型构建,完全在本地硬体上运行,进而保证资料的隐私。
|
||||||
|
|
||||||
[](https://fosowl.github.io/agenticSeek.html)  [](https://discord.gg/4Ub2D6Fj)
|
[](https://fosowl.github.io/agenticSeek.html)  [](https://discord.gg/4Ub2D6Fj) [](https://x.com/Martin993886460)
|
||||||
|
|
||||||
> 🛠️ **目前还在开发阶段** – 欢迎任何贡献者加入我们!
|
> 🛠️ **目前还在开发阶段** – 欢迎任何贡献者加入我们!
|
||||||
|
|
||||||
|
https://github.com/user-attachments/assets/4bd5faf6-459f-4f94-bd1d-238c4b331469
|
||||||
|
|
||||||
> *Do a deep search of AI startup in Osaka and Tokyo, find at least 5, then save in the research_japan.txt file*
|
> *在大阪和东京深入搜寻人工智慧新创公司,至少找到 5 家,然后储存在 research_japan.txt 档案中*
|
||||||
|
|
||||||
> *Can you make a tetris game in C ?*
|
> *你可以用 C 语言制作俄罗斯方块游戏吗?*
|
||||||
|
|
||||||
> *I would like to setup a new project file index as mark2.*
|
> *我想设定一个新的专案档案索引,命名为 mark2。*
|
||||||
|
|
||||||
|
|
||||||
### AgenticSeek 可以进行任务规划!
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
## Features:
|
## Features:
|
||||||
|
|
||||||
@@ -82,72 +79,120 @@ pip3 install -r requirements.txt
|
|||||||
python3 setup.py install
|
python3 setup.py install
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
## 在本地机器上运行 AgenticSeek
|
## 在本地机器上运行 AgenticSeek
|
||||||
|
|
||||||
**建议至少使用 Deepseek 14B 以上参数的模型,较小的模型难以使用助理功能并且很快就会忘记上下文之间的关系。**
|
**建议至少使用 Deepseek 14B 以上参数的模型,较小的模型难以使用助理功能并且很快就会忘记上下文之间的关系。**
|
||||||
|
|
||||||
### 1️⃣ **下载模型**
|
**本地运行助手**
|
||||||
|
|
||||||
确定已经安装 [Ollama](https://ollama.com/)。
|
启动你的本地提供者,例如使用 ollama:
|
||||||
|
|
||||||
请在 [DeepSeek](https://deepseek.com/models) 下载至少大于 `deepseek-r1:14b` 的模型。
|
|
||||||
|
|
||||||
```sh
|
|
||||||
ollama pull deepseek-r1:14b
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2️ **启动框架 (ollama)**
|
|
||||||
|
|
||||||
启动 Ollama 服务器。
|
|
||||||
```sh
|
```sh
|
||||||
ollama serve
|
ollama serve
|
||||||
```
|
```
|
||||||
|
|
||||||
请更改 `config.ini` 文件,将 `provider_name` 设置为 `ollama` 并且 `provider_model` 设置为你刚刚下载的模型,如 `deepseek-r1:14b`。
|
请参阅下方支持的本地提供者列表。
|
||||||
|
|
||||||
注意:`deepseek-r1:14b` 只是范例,如果你的电脑允许的话,请使用更大的模型。
|
修改 `config.ini` 文件,将 `provider_name` 设置为支持的提供者,并将 `provider_model` 设置为 `deepseek-r1:14b`。
|
||||||
|
|
||||||
|
注意:`deepseek-r1:14b` 只是一个示例,如果你的硬件允许,可以使用更大的模型。
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
[MAIN]
|
[MAIN]
|
||||||
is_local = True
|
is_local = True
|
||||||
provider_name = ollama
|
provider_name = ollama # 或 lm-studio, openai 等
|
||||||
provider_model = deepseek-r1:14b
|
provider_model = deepseek-r1:14b
|
||||||
provider_server_address = 127.0.0.1:11434
|
provider_server_address = 127.0.0.1:11434
|
||||||
```
|
```
|
||||||
|
|
||||||
开始所有服务:
|
**本地提供者列表**
|
||||||
|
|
||||||
|
| 提供者 | 本地? | 描述 |
|
||||||
|
|-------------|--------|-------------------------------------------------------|
|
||||||
|
| ollama | 是 | 使用 ollama 作为 LLM 提供者,轻松本地运行 LLM |
|
||||||
|
| lm-studio | 是 | 使用 LM Studio 本地运行 LLM(将 `provider_name` 设置为 `lm-studio`)|
|
||||||
|
| openai | 否 | 使用兼容的 API |
|
||||||
|
|
||||||
|
下一步: [Start services and run AgenticSeek](#Start-services-and-Run)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## **Run with an API (透过 API 执行)**
|
||||||
|
|
||||||
|
设定 `config.ini`。
|
||||||
|
|
||||||
|
```sh
|
||||||
|
[MAIN]
|
||||||
|
is_local = False
|
||||||
|
provider_name = openai
|
||||||
|
provider_model = gpt-4o
|
||||||
|
provider_server_address = 127.0.0.1:5000
|
||||||
|
```
|
||||||
|
|
||||||
|
警告:确保 `config.ini` 没有行尾空格。
|
||||||
|
|
||||||
|
如果使用基于本机的 openai-based api 则把 `is_local` 设定为 `True`。
|
||||||
|
|
||||||
|
同时更改你的 IP 为 openai-based api 的 IP。
|
||||||
|
|
||||||
|
下一步: [Start services and run AgenticSeek](#Start-services-and-Run)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Start services and Run
|
||||||
|
(启动服务并运行)
|
||||||
|
|
||||||
|
如果需要,请激活你的 Python 环境。
|
||||||
|
```sh
|
||||||
|
source agentic_seek_env/bin/activate
|
||||||
|
```
|
||||||
|
|
||||||
|
启动所需的服务。这将启动 `docker-compose.yml` 中的所有服务,包括:
|
||||||
|
- searxng
|
||||||
|
- redis(由 redis 提供支持)
|
||||||
|
- 前端
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
sudo ./start_services.sh # MacOS
|
sudo ./start_services.sh # MacOS
|
||||||
start ./start_services.cmd # Window
|
start ./start_services.cmd # Windows
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**选项 1:** 使用 CLI 界面运行。
|
||||||
运行 AgenticSeek:
|
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
python3 main.py
|
python3 cli.py
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**选项 2:** 使用 Web 界面运行。
|
||||||
|
|
||||||
|
注意:目前我們建議您使用 CLI 界面。Web 界面仍在積極開發中。
|
||||||
|
|
||||||
|
启动后端服务。
|
||||||
|
|
||||||
|
```sh
|
||||||
|
python3 api.py
|
||||||
|
```
|
||||||
|
|
||||||
|
访问 `http://localhost:3000/`,你应该会看到 Web 界面。
|
||||||
|
|
||||||
|
请注意,目前 Web 界面不支持消息流式传输。
|
||||||
|
|
||||||
|
|
||||||
*如果你不知道如何开始,请参阅 **Usage** 部分*
|
*如果你不知道如何开始,请参阅 **Usage** 部分*
|
||||||
|
|
||||||
*如果遇到问题,请先参考 **Known issues** 部分*
|
|
||||||
|
|
||||||
|
|
||||||
*如果你的电脑无法在本机运行 deepseek,也许你可以试看看 API 的方式,参见 **Run with an API***
|
|
||||||
|
|
||||||
*有关设定档的详细解释,请参阅 **Config** 部分。*
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Usage (使用方法)
|
## Usage (使用方法)
|
||||||
|
|
||||||
确定所有的核心档案都启用了,也就是执行过这条命令 `./start_services.sh` 然后你就可以使用 `python3 main.py` 来启动 AgenticSeek 了!
|
为确保 agenticSeek 在中文环境下正常工作,请确保在 config.ini 中设置语言选项。
|
||||||
|
languages = en zh
|
||||||
|
更多信息请参阅 Config 部分
|
||||||
|
|
||||||
|
确定所有的核心档案都启用了,也就是执行过这条命令 `./start_services.sh` 然后你就可以使用 `python3 cli.py` 来启动 AgenticSeek 了!
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
sudo ./start_services.sh
|
sudo ./start_services.sh
|
||||||
python3 main.py
|
python3 cli.py
|
||||||
```
|
```
|
||||||
|
|
||||||
当你看到执行后显示 `>>> `
|
当你看到执行后显示 `>>> `
|
||||||
@@ -160,35 +205,35 @@ python3 main.py
|
|||||||
|
|
||||||
### Coding/Bash
|
### Coding/Bash
|
||||||
|
|
||||||
> *Help me with matrix multiplication in Golang*
|
> *在 Golang 中帮助我进行矩阵乘法*
|
||||||
|
|
||||||
> *Scan my network with nmap, find if any suspicious devices is connected*
|
> *使用 nmap 扫描我的网路,找出是否有任何可疑装置连接*
|
||||||
|
|
||||||
> *Make a snake game in python*
|
> *用 Python 制作一个贪食蛇游戏*
|
||||||
|
|
||||||
### 网路搜寻
|
### 网路搜寻
|
||||||
|
|
||||||
> *Do a web search to find cool tech startup in Japan working on cutting edge AI research*
|
> *进行网路搜寻,找出日本从事尖端人工智慧研究的酷炫科技新创公司*
|
||||||
|
|
||||||
> *Can you find on the internet who created agenticSeek?*
|
> *你能在网路上找到谁创造了 AgenticSeek 吗?*
|
||||||
|
|
||||||
> *Can you find on which website I can buy a rtx 4090 for cheap*
|
> *你能在哪个网站上找到便宜的 RTX 4090 吗?*
|
||||||
|
|
||||||
### 档案浏览与搜寻
|
### 档案浏览与搜寻
|
||||||
|
|
||||||
> *Hey can you find where is million_dollars_contract.pdf i lost it*
|
> *嘿,你能找到我遗失的 million_dollars_contract.pdf 在哪里吗?*
|
||||||
|
|
||||||
> *Show me how much space I have left on my disk*
|
> *告诉我我的磁碟还剩下多少空间*
|
||||||
|
|
||||||
> *Find and read the README.md and follow the install instruction*
|
> *寻找并阅读 README.md,并按照安装说明进行操作*
|
||||||
|
|
||||||
### 日常聊天
|
### 日常聊天
|
||||||
|
|
||||||
> *Tell me about France*
|
> *告诉我关于法国的事*
|
||||||
|
|
||||||
> *What is the meaning of life ?*
|
> *人生的意义是什么?*
|
||||||
|
|
||||||
> *Should I take creatine before or after workout?*
|
> *我应该在锻炼前还是锻炼后服用肌酸?*
|
||||||
|
|
||||||
|
|
||||||
当你把指令送出后,AgenticSeek 会自动调用最能提供帮助的助理,去完成你交办的工作和指令。
|
当你把指令送出后,AgenticSeek 会自动调用最能提供帮助的助理,去完成你交办的工作和指令。
|
||||||
@@ -198,11 +243,14 @@ python3 main.py
|
|||||||
所以我们希望你在使用时,能明确地表明你希望他要怎么做,下面给你一个范例!
|
所以我们希望你在使用时,能明确地表明你希望他要怎么做,下面给你一个范例!
|
||||||
|
|
||||||
你该说:
|
你该说:
|
||||||
`Do a web search and find out which are the best country for solo-travel`
|
- 进行网络搜索,找出哪些国家最适合独自旅行
|
||||||
|
|
||||||
|
|
||||||
而不是说:
|
而不是说:
|
||||||
`Do you know some good countries for solo-travel?`
|
- 你知道哪些国家适合独自旅行?
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -260,37 +308,6 @@ provider_model = deepseek-r1:14b
|
|||||||
provider_server_address = x.x.x.x:3333
|
provider_server_address = x.x.x.x:3333
|
||||||
```
|
```
|
||||||
|
|
||||||
执行 AgenticSeek:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
sudo ./start_services.sh
|
|
||||||
python3 main.py
|
|
||||||
```
|
|
||||||
|
|
||||||
## **Run with an API (透过 API 执行)**
|
|
||||||
|
|
||||||
设定 `config.ini`。
|
|
||||||
|
|
||||||
```sh
|
|
||||||
[MAIN]
|
|
||||||
is_local = False
|
|
||||||
provider_name = openai
|
|
||||||
provider_model = gpt-4o
|
|
||||||
provider_server_address = 127.0.0.1:5000
|
|
||||||
```
|
|
||||||
|
|
||||||
警告:确保 `config.ini` 没有行尾空格。
|
|
||||||
|
|
||||||
如果使用基于本机的 openai-based api 则把 `is_local` 设定为 `True`。
|
|
||||||
|
|
||||||
同时更改你的 IP 为 openai-based api 的 IP。
|
|
||||||
|
|
||||||
执行 AgenticSeek:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
sudo ./start_services.sh
|
|
||||||
python3 main.py
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -335,6 +352,7 @@ speak = False
|
|||||||
listen = False
|
listen = False
|
||||||
work_dir = /Users/mlg/Documents/ai_folder
|
work_dir = /Users/mlg/Documents/ai_folder
|
||||||
jarvis_personality = False
|
jarvis_personality = False
|
||||||
|
languages = en zh
|
||||||
[BROWSER]
|
[BROWSER]
|
||||||
headless_browser = False
|
headless_browser = False
|
||||||
stealth_mode = False
|
stealth_mode = False
|
||||||
@@ -342,43 +360,45 @@ stealth_mode = False
|
|||||||
|
|
||||||
**说明**:
|
**说明**:
|
||||||
- is_local
|
- is_local
|
||||||
- True:在本地運行。
|
- True:在本地运行。
|
||||||
- False:在遠端伺服器運行。
|
- False:在远端伺服器运行。
|
||||||
- provider_name
|
- provider_name
|
||||||
- 框架類型
|
- 框架类型
|
||||||
- `ollama`, `server`, `lm-studio`, `deepseek-api`
|
- `ollama`, `server`, `lm-studio`, `deepseek-api`
|
||||||
- provider_model
|
- provider_model
|
||||||
- 運行的模型
|
- 运行的模型
|
||||||
- `deepseek-r1:1.5b`, `deepseek-r1:14b`
|
- `deepseek-r1:1.5b`, `deepseek-r1:14b`
|
||||||
- provider_server_address
|
- provider_server_address
|
||||||
- 伺服器 IP
|
- 伺服器 IP
|
||||||
- `127.0.0.1:11434`
|
- `127.0.0.1:11434`
|
||||||
- agent_name
|
- agent_name
|
||||||
- AgenticSeek 的名字,用作TTS的觸發單詞。
|
- AgenticSeek 的名字,用作TTS的触发单词。
|
||||||
- `Friday`
|
- `Friday`
|
||||||
- recover_last_session
|
- recover_last_session
|
||||||
- True:從上個對話繼續。
|
- True:从上个对话继续。
|
||||||
- False:重啟對話。
|
- False:重启对话。
|
||||||
- save_session
|
- save_session
|
||||||
- True:儲存對話紀錄。
|
- True:储存对话纪录。
|
||||||
- False:不保存。
|
- False:不保存。
|
||||||
- speak
|
- speak
|
||||||
- True:啟用語音輸出。
|
- True:启用语音输出。
|
||||||
- False:關閉語音輸出。
|
- False:关闭语音输出。
|
||||||
- listen
|
- listen
|
||||||
- True:啟用語音輸入。
|
- True:启用语音输入。
|
||||||
- False:關閉語音輸入。
|
- False:关闭语音输入。
|
||||||
- work_dir
|
- work_dir
|
||||||
- AgenticSeek 擁有能存取與交互的工作目錄。
|
- AgenticSeek 拥有能存取与交互的工作目录。
|
||||||
- jarvis_personality
|
- jarvis_personality
|
||||||
> 就是那個鋼鐵人的 JARVIS
|
> 就是那个钢铁人的 JARVIS
|
||||||
- True:啟用 JARVIS 個性。
|
- True:启用 JARVIS 个性。
|
||||||
- False:關閉 JARVIS 個性。
|
- False:关闭 JARVIS 个性。
|
||||||
- headless_browser
|
- headless_browser
|
||||||
- True:前景瀏覽器。(很酷,推薦使用他 XD)
|
- True:前景浏览器。(很酷,推荐使用他 XD)
|
||||||
- False:背景執行瀏覽器。
|
- False:背景执行浏览器。
|
||||||
- stealth_mode
|
- stealth_mode
|
||||||
- 隱私模式,但需要你自己安裝反爬蟲擴充功能。
|
- 隐私模式,但需要你自己安装反爬虫扩充功能。
|
||||||
|
- languages
|
||||||
|
- 支持的语言列表。用于代理路由系统。语言列表越长,下载的模型越多。
|
||||||
|
|
||||||
## 框架
|
## 框架
|
||||||
|
|
||||||
@@ -436,11 +456,14 @@ https://googlechromelabs.github.io/chrome-for-testing/
|
|||||||
|
|
||||||
## FAQ
|
## FAQ
|
||||||
|
|
||||||
**Q:我需要什么的硬体配备?**
|
**Q: 我需要什麼硬體?**
|
||||||
|
|
||||||
7B 型号:具有 8GB VRAM 的 GPU。
|
| 模型大小 | GPU | 備註 |
|
||||||
14B 型号:12GB GPU(例如 RTX 3060)。
|
|-----------|--------|-----------------------------------------------------------|
|
||||||
32B 型号:24GB+ VRAM。
|
| 7B | 8GB Vram | ⚠️ 不推薦。性能較差,經常出現幻覺,規劃代理可能會失敗。 |
|
||||||
|
| 14B | 12 GB VRAM (例如 RTX 3060) | ✅ 適用於簡單任務。可能在網頁瀏覽和規劃任務上表現不佳。 |
|
||||||
|
| 32B | 24+ GB VRAM (例如 RTX 4090) | 🚀 大多數任務成功,但可能仍在任務規劃上有困難。 |
|
||||||
|
| 70B+ | 48+ GB Vram (例如 mac studio) | 💪 表現優異。建議用於高級使用情境。 |
|
||||||
|
|
||||||
**Q:为什么选择 Deepseek R1 而不是其他模型?**
|
**Q:为什么选择 Deepseek R1 而不是其他模型?**
|
||||||
|
|
||||||
@@ -467,12 +490,24 @@ https://googlechromelabs.github.io/chrome-for-testing/
|
|||||||
|
|
||||||
不不不,AgenticSeek 和 Manus 是不同取向的东西,我们优先考虑的是本地执行和隐私,而不是基于云端。这是一个与 Manus 相比起来更有趣且易使用的方案!
|
不不不,AgenticSeek 和 Manus 是不同取向的东西,我们优先考虑的是本地执行和隐私,而不是基于云端。这是一个与 Manus 相比起来更有趣且易使用的方案!
|
||||||
|
|
||||||
|
**Q: 是否支持中文以外的语言?**
|
||||||
|
|
||||||
|
DeepSeek R1 天生会说中文
|
||||||
|
|
||||||
|
但注意:代理路由系统只懂英文,所以必须通过 config.ini 的 languages 参数(如 languages = en zh)告诉系统:
|
||||||
|
|
||||||
|
如果不设置中文?后果可能是:你让它写代码,结果跳出来个"医生代理"(虽然我们根本没有这个代理... 但系统会一脸懵圈!)
|
||||||
|
|
||||||
|
实际上会下载一个小型翻译模型来协助任务分配
|
||||||
|
|
||||||
## 贡献
|
## 贡献
|
||||||
|
|
||||||
我们正在寻找开发者来改善 AgenticSeek!你可以在 Issues 查看未解决的问题或和我们讨论更酷的新功能!
|
我们正在寻找开发者来改善 AgenticSeek!你可以在 Issues 查看未解决的问题或和我们讨论更酷的新功能!
|
||||||
|
|
||||||
[](https://www.star-history.com/#Fosowl/agenticSeek&Date)
|
[](https://www.star-history.com/#Fosowl/agenticSeek&Date)
|
||||||
|
|
||||||
|
[Contribution guide](./docs/CONTRIBUTING.md)
|
||||||
|
|
||||||
## 作者:
|
## 作者:
|
||||||
> [Fosowl](https://github.com/Fosowl)
|
> [Fosowl](https://github.com/Fosowl)
|
||||||
> [steveh8758](https://github.com/steveh8758)
|
> [steveh8758](https://github.com/steveh8758)
|
||||||
|
|||||||
@@ -10,21 +10,19 @@
|
|||||||
|
|
||||||
**Manus AI 的本地替代品**,它是一個具有語音功能的大語言模型秘書,可以 Coding、訪問你的電腦文件、瀏覽網頁,並自動修正錯誤與反省,最重要的是不會向雲端傳送任何資料。採用 DeepSeek R1 等推理模型構建,完全在本地硬體上運行,進而保證資料的隱私。
|
**Manus AI 的本地替代品**,它是一個具有語音功能的大語言模型秘書,可以 Coding、訪問你的電腦文件、瀏覽網頁,並自動修正錯誤與反省,最重要的是不會向雲端傳送任何資料。採用 DeepSeek R1 等推理模型構建,完全在本地硬體上運行,進而保證資料的隱私。
|
||||||
|
|
||||||
[](https://fosowl.github.io/agenticSeek.html)  [](https://discord.gg/4Ub2D6Fj)
|
[](https://fosowl.github.io/agenticSeek.html)  [](https://discord.gg/4Ub2D6Fj) [](https://x.com/Martin993886460)
|
||||||
|
|
||||||
> 🛠️ **目前還在開發階段** – 歡迎任何貢獻者加入我們!
|
> 🛠️ **目前還在開發階段** – 歡迎任何貢獻者加入我們!
|
||||||
|
|
||||||
|
https://github.com/user-attachments/assets/4bd5faf6-459f-4f94-bd1d-238c4b331469
|
||||||
|
|
||||||
> *Do a deep search of AI startup in Osaka and Tokyo, find at least 5, then save in the research_japan.txt file*
|
> *在大阪和東京深入搜尋人工智慧新創公司,至少找到 5 家,然後儲存在 research_japan.txt 檔案中*
|
||||||
|
|
||||||
> *Can you make a tetris game in C ?*
|
> *你可以用 C 語言製作俄羅斯方塊遊戲嗎?*
|
||||||
|
|
||||||
> *I would like to setup a new project file index as mark2.*
|
> *我想設定一個新的專案檔案索引,命名為 mark2。*
|
||||||
|
|
||||||
|
|
||||||
### AgenticSeek 可以進行任務規劃!
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
## Features:
|
## Features:
|
||||||
|
|
||||||
@@ -82,72 +80,120 @@ pip3 install -r requirements.txt
|
|||||||
python3 setup.py install
|
python3 setup.py install
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
## 在本地機器上運行 AgenticSeek
|
## 在本地機器上運行 AgenticSeek
|
||||||
|
|
||||||
**建議至少使用 Deepseek 14B 以上參數的模型,較小的模型難以使用助理功能並且很快就會忘記上下文之間的關係。**
|
**建議至少使用 Deepseek 14B 以上參數的模型,較小的模型難以使用助理功能並且很快就會忘記上下文之間的關係。**
|
||||||
|
|
||||||
### 1️⃣ **下載模型**
|
**本地运行助手**
|
||||||
|
|
||||||
確定已經安裝 [Ollama](https://ollama.com/)。
|
启动你的本地提供者,例如使用 ollama:
|
||||||
|
|
||||||
請在 [DeepSeek](https://deepseek.com/models) 下載至少大於 `deepseek-r1:14b` 的模型。
|
|
||||||
|
|
||||||
```sh
|
|
||||||
ollama pull deepseek-r1:14b
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2️ **啟動框架 (ollama)**
|
|
||||||
|
|
||||||
啟動 Ollama 服務器。
|
|
||||||
```sh
|
```sh
|
||||||
ollama serve
|
ollama serve
|
||||||
```
|
```
|
||||||
|
|
||||||
請更改 `config.ini` 文件,將 `provider_name` 設置為 `ollama` 並且 `provider_model` 設置為你剛剛下載的模型,如 `deepseek-r1:14b`。
|
请参阅下方支持的本地提供者列表。
|
||||||
|
|
||||||
注意:`deepseek-r1:14b` 只是範例,如果你的電腦允許的話,請使用更大的模型。
|
修改 `config.ini` 文件,将 `provider_name` 设置为支持的提供者,并将 `provider_model` 设置为 `deepseek-r1:14b`。
|
||||||
|
|
||||||
|
注意:`deepseek-r1:14b` 只是一个示例,如果你的硬件允许,可以使用更大的模型。
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
[MAIN]
|
[MAIN]
|
||||||
is_local = True
|
is_local = True
|
||||||
provider_name = ollama
|
provider_name = ollama # 或 lm-studio, openai 等
|
||||||
provider_model = deepseek-r1:14b
|
provider_model = deepseek-r1:14b
|
||||||
provider_server_address = 127.0.0.1:11434
|
provider_server_address = 127.0.0.1:11434
|
||||||
```
|
```
|
||||||
|
|
||||||
開始所有服務:
|
**本地提供者列表**
|
||||||
|
|
||||||
|
| 提供者 | 本地? | 描述 |
|
||||||
|
|-------------|--------|-------------------------------------------------------|
|
||||||
|
| ollama | 是 | 使用 ollama 作为 LLM 提供者,轻松本地运行 LLM |
|
||||||
|
| lm-studio | 是 | 使用 LM Studio 本地运行 LLM(将 `provider_name` 设置为 `lm-studio`)|
|
||||||
|
| openai | 否 | 使用兼容的 API |
|
||||||
|
|
||||||
|
下一步: [Start services and run AgenticSeek](#Start-services-and-Run)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## **Run with an API (透過 API 執行)**
|
||||||
|
|
||||||
|
設定 `config.ini`。
|
||||||
|
|
||||||
|
```sh
|
||||||
|
[MAIN]
|
||||||
|
is_local = False
|
||||||
|
provider_name = openai
|
||||||
|
provider_model = gpt-4o
|
||||||
|
provider_server_address = 127.0.0.1:5000
|
||||||
|
```
|
||||||
|
|
||||||
|
警告:確保 `config.ini` 沒有行尾空格。
|
||||||
|
|
||||||
|
如果使用基於本機的 openai-based api 則把 `is_local` 設定為 `True`。
|
||||||
|
|
||||||
|
同時更改你的 IP 為 openai-based api 的 IP。
|
||||||
|
|
||||||
|
下一步: [Start services and run AgenticSeek](#Start-services-and-Run)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Start services and Run
|
||||||
|
(启动服务并运行)
|
||||||
|
|
||||||
|
如果需要,请激活你的 Python 环境。
|
||||||
|
```sh
|
||||||
|
source agentic_seek_env/bin/activate
|
||||||
|
```
|
||||||
|
|
||||||
|
启动所需的服务。这将启动 `docker-compose.yml` 中的所有服务,包括:
|
||||||
|
- searxng
|
||||||
|
- redis(由 redis 提供支持)
|
||||||
|
- 前端
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
sudo ./start_services.sh # MacOS
|
sudo ./start_services.sh # MacOS
|
||||||
start ./start_services.cmd # Window
|
start ./start_services.cmd # Windows
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**选项 1:** 使用 CLI 界面运行。
|
||||||
運行 AgenticSeek:
|
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
python3 main.py
|
python3 cli.py
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**选项 2:** 使用 Web 界面运行。
|
||||||
|
|
||||||
|
注意:目前我們建議您使用 CLI 界面。Web 界面仍在積極開發中。
|
||||||
|
|
||||||
|
启动后端服务。
|
||||||
|
|
||||||
|
```sh
|
||||||
|
python3 api.py
|
||||||
|
```
|
||||||
|
|
||||||
|
访问 `http://localhost:3000/`,你应该会看到 Web 界面。
|
||||||
|
|
||||||
|
请注意,目前 Web 界面不支持消息流式传输。
|
||||||
|
|
||||||
|
|
||||||
*如果你不知道如何開始,請參閱 **Usage** 部分*
|
*如果你不知道如何開始,請參閱 **Usage** 部分*
|
||||||
|
|
||||||
*如果遇到問題,請先參考 **Known issues** 部分*
|
|
||||||
|
|
||||||
|
|
||||||
*如果你的電腦無法在本機運行 deepseek,也許你可以試看看 API 的方式,參見 **Run with an API***
|
|
||||||
|
|
||||||
*有關設定檔的詳細解釋,請參閱 **Config** 部分。*
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Usage (使用方法)
|
## Usage (使用方法)
|
||||||
|
|
||||||
確定所有的核心檔案都啟用了,也就是執行過這條命令 `./start_services.sh` 然後你就可以使用 `python3 main.py` 來啟動 AgenticSeek 了!
|
为确保 agenticSeek 在中文环境下正常工作,请确保在 config.ini 中设置语言选项。
|
||||||
|
languages = en zh
|
||||||
|
更多信息请参阅 Config 部分
|
||||||
|
|
||||||
|
確定所有的核心檔案都啟用了,也就是執行過這條命令 `./start_services.sh` 然後你就可以使用 `python3 cli.py` 來啟動 AgenticSeek 了!
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
sudo ./start_services.sh
|
sudo ./start_services.sh
|
||||||
python3 main.py
|
python3 cli.py
|
||||||
```
|
```
|
||||||
|
|
||||||
當你看到執行後顯示 `>>> `
|
當你看到執行後顯示 `>>> `
|
||||||
@@ -160,35 +206,35 @@ python3 main.py
|
|||||||
|
|
||||||
### Coding/Bash
|
### Coding/Bash
|
||||||
|
|
||||||
> *Help me with matrix multiplication in Golang*
|
> *在 Golang 中幫助我進行矩陣乘法*
|
||||||
|
|
||||||
> *Scan my network with nmap, find if any suspicious devices is connected*
|
> *使用 nmap 掃描我的網路,找出是否有任何可疑裝置連接*
|
||||||
|
|
||||||
> *Make a snake game in python*
|
> *用 Python 製作一個貪食蛇遊戲*
|
||||||
|
|
||||||
### 網路搜尋
|
### 網路搜尋
|
||||||
|
|
||||||
> *Do a web search to find cool tech startup in Japan working on cutting edge AI research*
|
> *進行網路搜尋,找出日本從事尖端人工智慧研究的酷炫科技新創公司*
|
||||||
|
|
||||||
> *Can you find on the internet who created agenticSeek?*
|
> *你能在網路上找到誰創造了 AgenticSeek 嗎?*
|
||||||
|
|
||||||
> *Can you find on which website I can buy a rtx 4090 for cheap*
|
> *你能在哪個網站上找到便宜的 RTX 4090 嗎?*
|
||||||
|
|
||||||
### 檔案瀏覽與搜尋
|
### 檔案瀏覽與搜尋
|
||||||
|
|
||||||
> *Hey can you find where is million_dollars_contract.pdf i lost it*
|
> *嘿,你能找到我遺失的 million_dollars_contract.pdf 在哪裡嗎?*
|
||||||
|
|
||||||
> *Show me how much space I have left on my disk*
|
> *告訴我我的磁碟還剩下多少空間*
|
||||||
|
|
||||||
> *Find and read the README.md and follow the install instruction*
|
> *尋找並閱讀 README.md,並按照安裝說明進行操作*
|
||||||
|
|
||||||
### 日常聊天
|
### 日常聊天
|
||||||
|
|
||||||
> *Tell me about France*
|
> *告訴我關於法國的事*
|
||||||
|
|
||||||
> *What is the meaning of life ?*
|
> *人生的意義是什麼?*
|
||||||
|
|
||||||
> *Should I take creatine before or after workout?*
|
> *我應該在鍛鍊前還是鍛鍊後服用肌酸?*
|
||||||
|
|
||||||
|
|
||||||
當你把指令送出後,AgenticSeek 會自動調用最能提供幫助的助理,去完成你交辦的工作和指令。
|
當你把指令送出後,AgenticSeek 會自動調用最能提供幫助的助理,去完成你交辦的工作和指令。
|
||||||
@@ -198,11 +244,14 @@ python3 main.py
|
|||||||
所以我們希望你在使用時,能明確地表明你希望他要怎麼做,下面給你一個範例!
|
所以我們希望你在使用時,能明確地表明你希望他要怎麼做,下面給你一個範例!
|
||||||
|
|
||||||
你該說:
|
你該說:
|
||||||
`Do a web search and find out which are the best country for solo-travel`
|
- 进行网络搜索,找出哪些国家最适合独自旅行
|
||||||
|
|
||||||
|
|
||||||
而不是說:
|
而不是說:
|
||||||
`Do you know some good countries for solo-travel?`
|
- 你知道哪些国家适合独自旅行?
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -260,37 +309,6 @@ provider_model = deepseek-r1:14b
|
|||||||
provider_server_address = x.x.x.x:3333
|
provider_server_address = x.x.x.x:3333
|
||||||
```
|
```
|
||||||
|
|
||||||
執行 AgenticSeek:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
sudo ./start_services.sh
|
|
||||||
python3 main.py
|
|
||||||
```
|
|
||||||
|
|
||||||
## **Run with an API (透過 API 執行)**
|
|
||||||
|
|
||||||
設定 `config.ini`。
|
|
||||||
|
|
||||||
```sh
|
|
||||||
[MAIN]
|
|
||||||
is_local = False
|
|
||||||
provider_name = openai
|
|
||||||
provider_model = gpt-4o
|
|
||||||
provider_server_address = 127.0.0.1:5000
|
|
||||||
```
|
|
||||||
|
|
||||||
警告:確保 `config.ini` 沒有行尾空格。
|
|
||||||
|
|
||||||
如果使用基於本機的 openai-based api 則把 `is_local` 設定為 `True`。
|
|
||||||
|
|
||||||
同時更改你的 IP 為 openai-based api 的 IP。
|
|
||||||
|
|
||||||
執行 AgenticSeek:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
sudo ./start_services.sh
|
|
||||||
python3 main.py
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -335,6 +353,7 @@ speak = False
|
|||||||
listen = False
|
listen = False
|
||||||
work_dir = /Users/mlg/Documents/ai_folder
|
work_dir = /Users/mlg/Documents/ai_folder
|
||||||
jarvis_personality = False
|
jarvis_personality = False
|
||||||
|
languages = en zh
|
||||||
[BROWSER]
|
[BROWSER]
|
||||||
headless_browser = False
|
headless_browser = False
|
||||||
stealth_mode = False
|
stealth_mode = False
|
||||||
@@ -379,6 +398,8 @@ stealth_mode = False
|
|||||||
- False:背景執行瀏覽器。
|
- False:背景執行瀏覽器。
|
||||||
- stealth_mode
|
- stealth_mode
|
||||||
- 隱私模式,但需要你自己安裝反爬蟲擴充功能。
|
- 隱私模式,但需要你自己安裝反爬蟲擴充功能。
|
||||||
|
- languages
|
||||||
|
- 支持的语言列表。用于代理路由系统。语言列表越长,下载的模型越多。
|
||||||
|
|
||||||
## 框架
|
## 框架
|
||||||
|
|
||||||
@@ -436,11 +457,14 @@ https://googlechromelabs.github.io/chrome-for-testing/
|
|||||||
|
|
||||||
## FAQ
|
## FAQ
|
||||||
|
|
||||||
**Q:我需要什麼的硬體配備?**
|
**Q: 我需要什麼硬體?**
|
||||||
|
|
||||||
7B 型號:具有 8GB VRAM 的 GPU。
|
| 模型大小 | GPU | 備註 |
|
||||||
14B 型號:12GB GPU(例如 RTX 3060)。
|
|-----------|--------|-----------------------------------------------------------|
|
||||||
32B 型號:24GB+ VRAM。
|
| 7B | 8GB Vram | ⚠️ 不推薦。性能較差,經常出現幻覺,規劃代理可能會失敗。 |
|
||||||
|
| 14B | 12 GB VRAM (例如 RTX 3060) | ✅ 適用於簡單任務。可能在網頁瀏覽和規劃任務上表現不佳。 |
|
||||||
|
| 32B | 24+ GB VRAM (例如 RTX 4090) | 🚀 大多數任務成功,但可能仍在任務規劃上有困難。 |
|
||||||
|
| 70B+ | 48+ GB Vram (例如 mac studio) | 💪 表現優異。建議用於高級使用情境。 |
|
||||||
|
|
||||||
**Q:為什麼選擇 Deepseek R1 而不是其他模型?**
|
**Q:為什麼選擇 Deepseek R1 而不是其他模型?**
|
||||||
|
|
||||||
@@ -467,12 +491,24 @@ https://googlechromelabs.github.io/chrome-for-testing/
|
|||||||
|
|
||||||
不不不,AgenticSeek 和 Manus 是不同取向的東西,我們優先考慮的是本地執行和隱私,而不是基於雲端。這是一個與 Manus 相比起來更有趣且易使用的方案!
|
不不不,AgenticSeek 和 Manus 是不同取向的東西,我們優先考慮的是本地執行和隱私,而不是基於雲端。這是一個與 Manus 相比起來更有趣且易使用的方案!
|
||||||
|
|
||||||
|
**Q: 是否支持中文以外的语言?**
|
||||||
|
|
||||||
|
DeepSeek R1 天生会说中文
|
||||||
|
|
||||||
|
但注意:代理路由系统只懂英文,所以必须通过 config.ini 的 languages 参数(如 languages = en zh)告诉系统:
|
||||||
|
|
||||||
|
如果不设置中文?后果可能是:你让它写代码,结果跳出来个"医生代理"(虽然我们根本没有这个代理... 但系统会一脸懵圈!)
|
||||||
|
|
||||||
|
实际上会下载一个小型翻译模型来协助任务分配
|
||||||
|
|
||||||
## 貢獻
|
## 貢獻
|
||||||
|
|
||||||
我們正在尋找開發者來改善 AgenticSeek!你可以在 Issues 查看未解決的問題或和我們討論更酷的新功能!
|
我們正在尋找開發者來改善 AgenticSeek!你可以在 Issues 查看未解決的問題或和我們討論更酷的新功能!
|
||||||
|
|
||||||
[](https://www.star-history.com/#Fosowl/agenticSeek&Date)
|
[](https://www.star-history.com/#Fosowl/agenticSeek&Date)
|
||||||
|
|
||||||
|
[Contribution guide](./docs/CONTRIBUTING.md)
|
||||||
|
|
||||||
## 作者:
|
## 作者:
|
||||||
> [Fosowl](https://github.com/Fosowl)
|
> [Fosowl](https://github.com/Fosowl)
|
||||||
> [steveh8758](https://github.com/steveh8758)
|
> [steveh8758](https://github.com/steveh8758)
|
||||||
|
|||||||
@@ -9,11 +9,11 @@
|
|||||||
|
|
||||||
Une alternative **entièrement locale** à Manus AI, un assistant IA qui code, explore votre système de fichiers, navigue sur le web et corrige ses erreurs, tout cela sans envoyer la moindre donnée dans le cloud. Cet agent autonome fonctionne entièrement sur votre hardware, garantissant la confidentialité de vos données.
|
Une alternative **entièrement locale** à Manus AI, un assistant IA qui code, explore votre système de fichiers, navigue sur le web et corrige ses erreurs, tout cela sans envoyer la moindre donnée dans le cloud. Cet agent autonome fonctionne entièrement sur votre hardware, garantissant la confidentialité de vos données.
|
||||||
|
|
||||||
[](https://fosowl.github.io/agenticSeek.html)  [](https://discord.gg/4Ub2D6Fj)
|
[](https://fosowl.github.io/agenticSeek.html)  [](https://discord.gg/4Ub2D6Fj) [](https://x.com/Martin993886460)
|
||||||
|
|
||||||
> 🛠️ **En cours de développement** – On cherche activement des contributeurs!
|
> 🛠️ **En cours de développement** – On cherche activement des contributeurs!
|
||||||
|
|
||||||

|
https://github.com/user-attachments/assets/4bd5faf6-459f-4f94-bd1d-238c4b331469
|
||||||
|
|
||||||
> *Recherche sur le web des activités à faire à Paris*
|
> *Recherche sur le web des activités à faire à Paris*
|
||||||
|
|
||||||
@@ -22,9 +22,6 @@ Une alternative **entièrement locale** à Manus AI, un assistant IA qui code, e
|
|||||||
> *J'aimerais que tu trouve une api météo et que tu me code une application qui affiche la météo à Toulouse*
|
> *J'aimerais que tu trouve une api météo et que tu me code une application qui affiche la météo à Toulouse*
|
||||||
|
|
||||||
|
|
||||||
### agenticSeek peut désormais planifier des taches!
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
|
|
||||||
## Fonctionnalités:
|
## Fonctionnalités:
|
||||||
@@ -84,51 +81,75 @@ pip3 install -r requirements.txt
|
|||||||
|
|
||||||
## Faire fonctionner sur votre machine
|
## Faire fonctionner sur votre machine
|
||||||
|
|
||||||
**Nous recommandons d’utiliser au moins DeepSeek 14B, les modèles plus petits ont du mal avec l’utilisation des outils et oublient rapidement le contexte.**
|
**Nous recommandons d’utiliser au minimum DeepSeek 14B, les modèles plus petits ont du mal avec l’utilisation des outils et oublient rapidement le contexte.**
|
||||||
|
|
||||||
### 1️⃣ **Téléchargement du modèle**
|
|
||||||
|
|
||||||
Assurer vous d'avoir [Ollama](https://ollama.com/) installé.
|
|
||||||
|
|
||||||
Télécharger `deepseek-r1:14b` de [DeepSeek](https://deepseek.com/models) (ou autre en fonction de votre hardware, voir section FAQ)
|
|
||||||
|
|
||||||
```sh
|
|
||||||
ollama pull deepseek-r1:14b
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2️ **Démarrage d'ollama**
|
|
||||||
|
|
||||||
|
Lancer votre provider local, par exemple avec ollama:
|
||||||
```sh
|
```sh
|
||||||
ollama serve
|
ollama serve
|
||||||
```
|
```
|
||||||
|
|
||||||
Modifiez le fichier config.ini pour définir provider_name sur ollama et provider_model sur deepseek-r1:14b
|
Voyez la section **Provider** pour la liste de provideurs disponible.
|
||||||
|
|
||||||
|
Modifiez le fichier config.ini pour définir provider_name sur le nom d'un provideur et provider_model sur le LLM à utiliser.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
[MAIN]
|
[MAIN]
|
||||||
is_local = True
|
is_local = True
|
||||||
provider_name = ollama
|
provider_name = ollama # ou lm-studio, openai, etc...
|
||||||
provider_model = deepseek-r1:14b
|
provider_model = deepseek-r1:14b
|
||||||
provider_server_address = 127.0.0.1:11434
|
provider_server_address = 127.0.0.1:11434
|
||||||
```
|
```
|
||||||
|
|
||||||
démarrer tous les services :
|
**Liste des provideurs locaux**
|
||||||
|
|
||||||
|
| Fournisseur | Local ? | Description |
|
||||||
|
|-------------|---------|-----------------------------------------------------------|
|
||||||
|
| ollama | Oui | Exécutez des LLM localement avec facilité en utilisant ollama comme fournisseur LLM |
|
||||||
|
| lm-studio | Oui | Exécutez un LLM localement avec LM studio (définissez `provider_name` sur `lm-studio`) |
|
||||||
|
| openai | Oui | Utilisez une API local compatible avec openai |
|
||||||
|
|
||||||
|
|
||||||
|
### **Démarrer les services & Exécuter**
|
||||||
|
|
||||||
|
Activez votre environnement Python si nécessaire.
|
||||||
```sh
|
```sh
|
||||||
sudo ./start_services.sh
|
source agentic_seek_env/bin/activate
|
||||||
```
|
```
|
||||||
|
|
||||||
Lancer agenticSeek:
|
Démarrez les services requis. Cela lancera tous les services définis dans le fichier docker-compose.yml, y compris :
|
||||||
|
- searxng
|
||||||
|
- redis (nécessaire pour searxng)
|
||||||
|
- frontend
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
python3 main.py
|
sudo ./start_services.sh # MacOS
|
||||||
|
start ./start_services.cmd # Windows
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**Option 1 :** Exécuter avec l'interface CLI.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
python3 cli.py
|
||||||
|
```
|
||||||
|
|
||||||
|
**Option 2 :** Exécuter avec l'interface Web.
|
||||||
|
|
||||||
|
Démarrez le backend.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
python3 api.py
|
||||||
|
```
|
||||||
|
|
||||||
|
Allez sur `http://localhost:3000/` et vous devriez voir l'interface web.
|
||||||
|
|
||||||
|
Veuillez noter que l'interface web ne diffuse pas les messages en continu pour le moment.
|
||||||
|
|
||||||
|
|
||||||
Voyez la section **Utilisation** si vous ne comprenez pas comment l’utiliser
|
Voyez la section **Utilisation** si vous ne comprenez pas comment l’utiliser
|
||||||
|
|
||||||
Voyez la section **Problèmes** connus si vous rencontrez des problèmes
|
Voyez la section **Problèmes** connus si vous rencontrez des problèmes
|
||||||
|
|
||||||
Voyez la section **Exécuter** avec une API si votre matériel ne peut pas exécuter DeepSeek localement
|
Voyez la section **Exécuter avec une API** si votre matériel ne peut pas exécuter DeepSeek localement
|
||||||
|
|
||||||
Voyez la section **Configuration** pour une explication détaillée du fichier de configuration.
|
Voyez la section **Configuration** pour une explication détaillée du fichier de configuration.
|
||||||
|
|
||||||
@@ -136,18 +157,20 @@ Voyez la section **Configuration** pour une explication détaillée du fichier d
|
|||||||
|
|
||||||
## Utilisation
|
## Utilisation
|
||||||
|
|
||||||
Assurez-vous que les services sont en cours d’exécution avec ./start_services.sh et lancez AgenticSeek avec python3 main.py
|
Assurez-vous que les services sont en cours d’exécution avec ./start_services.sh et lancez AgenticSeek avec le CLI ou l'interface Web.
|
||||||
|
|
||||||
```sh
|
**CLI:**
|
||||||
sudo ./start_services.sh
|
Vous verrez un prompt : ">>> "
|
||||||
python3 main.py
|
Cela indique qu’AgenticSeek attend que vous saisissiez des instructions.
|
||||||
```
|
Vous pouvez également utiliser la reconnaissance vocale en définissant `listen = True` dans la configuration.
|
||||||
|
Pour quitter, dites simplement `goodbye`.
|
||||||
|
|
||||||
Vous verrez un prompt: ">>> "
|
**Interface:**
|
||||||
Cela indique qu’AgenticSeek attend que vous saisissiez des instructions.
|
|
||||||
Vous pouvez également utiliser la reconnaissance vocale en définissant listen = True dans la configuration.
|
|
||||||
|
|
||||||
Pour quitter, dites simplement `goodbye`.
|
Assurez-vous d'avoir bien démarré le backend avec `python3 api.py`.
|
||||||
|
Allez sur `localhost:3000` où vous verrez une interface web.
|
||||||
|
Tapez simplement votre message et patientez.
|
||||||
|
Si vous n'avez pas d'interface sur `localhost:3000`, c'est que vous n'avez pas démarré les services avec `start_services.sh`.
|
||||||
|
|
||||||
Voici quelques exemples d’utilisation :
|
Voici quelques exemples d’utilisation :
|
||||||
|
|
||||||
@@ -155,7 +178,7 @@ Voici quelques exemples d’utilisation :
|
|||||||
|
|
||||||
> *Aide-moi avec la multiplication de matrices en Golang*
|
> *Aide-moi avec la multiplication de matrices en Golang*
|
||||||
|
|
||||||
> *Initalize un nouveau project python, setup le readme, gitignore et tout le bordel et fait un premier commit*
|
> *Initalize un nouveau project python, setup le readme, gitignore etc.. et fait un premier commit*
|
||||||
|
|
||||||
> *Fais un jeu snake en Python*
|
> *Fais un jeu snake en Python*
|
||||||
|
|
||||||
@@ -211,8 +234,6 @@ ip a | grep "inet " | grep -v 127.0.0.1 | awk '{print $2}' | cut -d/ -f1
|
|||||||
|
|
||||||
Remarque : Pour Windows ou macOS, utilisez respectivement ipconfig ou ifconfig pour trouver l’adresse IP.
|
Remarque : Pour Windows ou macOS, utilisez respectivement ipconfig ou ifconfig pour trouver l’adresse IP.
|
||||||
|
|
||||||
**Si vous souhaitez utiliser un fournisseur basé sur OpenAI, suivez la section Exécuter avec une API.**
|
|
||||||
|
|
||||||
Clonez le dépôt et entrez dans le dossier server/.
|
Clonez le dépôt et entrez dans le dossier server/.
|
||||||
|
|
||||||
|
|
||||||
@@ -251,12 +272,7 @@ provider_model = deepseek-r1:14b
|
|||||||
provider_server_address = x.x.x.x:3333
|
provider_server_address = x.x.x.x:3333
|
||||||
```
|
```
|
||||||
|
|
||||||
Exécutez l’assistant :
|
Ensuite, exécutez avec le CLI ou l'interface graphique comme expliqué dans la section pour les fournisseurs locaux.
|
||||||
|
|
||||||
```sh
|
|
||||||
sudo ./start_services.sh
|
|
||||||
python3 main.py
|
|
||||||
```
|
|
||||||
|
|
||||||
## **Exécuter avec une API**
|
## **Exécuter avec une API**
|
||||||
|
|
||||||
@@ -274,12 +290,7 @@ provider_model = gpt-4o
|
|||||||
provider_server_address = 127.0.0.1:5000
|
provider_server_address = 127.0.0.1:5000
|
||||||
```
|
```
|
||||||
|
|
||||||
Exécutez l’assistant :
|
Ensuite, exécutez avec le CLI ou l'interface graphique comme expliqué dans la section pour les fournisseurs locaux.
|
||||||
|
|
||||||
```sh
|
|
||||||
sudo ./start_services.sh
|
|
||||||
python3 main.py
|
|
||||||
```
|
|
||||||
|
|
||||||
## Config
|
## Config
|
||||||
|
|
||||||
@@ -297,6 +308,7 @@ speak = False
|
|||||||
listen = False
|
listen = False
|
||||||
work_dir = /Users/mlg/Documents/ai_folder
|
work_dir = /Users/mlg/Documents/ai_folder
|
||||||
jarvis_personality = False
|
jarvis_personality = False
|
||||||
|
languages = en fr
|
||||||
[BROWSER]
|
[BROWSER]
|
||||||
headless_browser = False
|
headless_browser = False
|
||||||
stealth_mode = False
|
stealth_mode = False
|
||||||
@@ -330,6 +342,7 @@ stealth_mode = False
|
|||||||
|
|
||||||
`stealth_mode` -> Rend la détection des bots plus difficile. Le seul inconvénient est que vous devez installer manuellement l’extension anticaptcha.
|
`stealth_mode` -> Rend la détection des bots plus difficile. Le seul inconvénient est que vous devez installer manuellement l’extension anticaptcha.
|
||||||
|
|
||||||
|
`languages` -> La liste de languages supportés (nécessaire pour le routage d'agents). Plus la liste est longue. Plus un nombre important de modèles sera téléchargés.
|
||||||
|
|
||||||
## Providers
|
## Providers
|
||||||
|
|
||||||
@@ -340,9 +353,10 @@ Le tableau ci-dessous montre les LLM providers disponibles :
|
|||||||
| ollama | Yes | Exécutez des LLM localement avec facilité en utilisant Ollama comme fournisseur LLM
|
| ollama | Yes | Exécutez des LLM localement avec facilité en utilisant Ollama comme fournisseur LLM
|
||||||
| server | Yes | Hébergez le modèle sur une autre machine, exécutez sur votre machine locale
|
| server | Yes | Hébergez le modèle sur une autre machine, exécutez sur votre machine locale
|
||||||
| lm-studio | Yes | Exécutez un LLM localement avec LM Studio (définissez provider_name sur lm-studio)
|
| lm-studio | Yes | Exécutez un LLM localement avec LM Studio (définissez provider_name sur lm-studio)
|
||||||
| openai | No | Utilise ChatGPT API (pas privé) |
|
| openai | No | Utilise l'API ChatGPT (pas privé) |
|
||||||
| deepseek-api | No | Deepseek API (pas privé) |
|
| deepseek-api | No | Utilise l'API Deepseek (pas privé) |
|
||||||
| huggingface| No | Hugging-Face API (pas privé) |
|
| huggingface| No | Utilise Hugging-Face (pas privé) |
|
||||||
|
| together| No | Utilise l'api Together AI |
|
||||||
|
|
||||||
Pour sélectionner un provider LLM, modifiez le config.ini :
|
Pour sélectionner un provider LLM, modifiez le config.ini :
|
||||||
|
|
||||||
@@ -387,13 +401,14 @@ Et téléchargez la version de chromedriver correspondant à votre système d’
|
|||||||
Si cette section est incomplète, merci de faire une nouvelle issue sur github.
|
Si cette section est incomplète, merci de faire une nouvelle issue sur github.
|
||||||
|
|
||||||
## FAQ
|
## FAQ
|
||||||
|
**Q: Quel matériel est nécessaire ?**
|
||||||
|
|
||||||
**Q: J'ai besoin d'un gros PC?**
|
| Taille du Modèle | GPU | Commentaire |
|
||||||
|
|--------------------|------|----------------------------------------------------------|
|
||||||
ça dépend du modèle!
|
| 7B | 8 Go VRAM | ⚠️ Non recommandé. Performances médiocres, hallucinations fréquentes, et l'agent planificateur échouera probablement. |
|
||||||
Pour un modèle 7B : GPU avec 8 Go de VRAM.
|
| 14B | 12 Go VRAM (par ex. RTX 3060) | ✅ Utilisable pour des tâches simples. Peut rencontrer des difficultés avec la navigation web et les tâches de planification. |
|
||||||
Pour un modèle 14B : GPU 12 Go (par exemple, RTX 3060).
|
| 32B | 24+ Go VRAM (par ex. RTX 4090) | 🚀 Réussite avec la plupart des tâches, peut encore avoir des difficultés avec la planification des tâches. |
|
||||||
Et un modèle 32B : 24 Go+ de VRAM.
|
| 70B+ | 48+ Go VRAM (par ex. Mac Studio) | 💪 Excellent. Recommandé pour des cas d'utilisation avancés. |
|
||||||
|
|
||||||
**Q: Pourquoi deepseek et pas un autre modèle**
|
**Q: Pourquoi deepseek et pas un autre modèle**
|
||||||
|
|
||||||
@@ -417,6 +432,8 @@ Nous recherchons des développeurs pour améliorer AgenticSeek ! Consultez la se
|
|||||||
|
|
||||||
[](https://www.star-history.com/#Fosowl/agenticSeek&Date)
|
[](https://www.star-history.com/#Fosowl/agenticSeek&Date)
|
||||||
|
|
||||||
|
[Guide du contributeur](./docs/CONTRIBUTING.md)
|
||||||
|
|
||||||
## Auteurs/Mainteneurs:
|
## Auteurs/Mainteneurs:
|
||||||
> [Fosowl](https://github.com/Fosowl) - Epitech 2024, France
|
> [Fosowl](https://github.com/Fosowl)
|
||||||
> [steveh8758](https://github.com/steveh8758) - Université Feng Chia, Taiwan
|
> [steveh8758](https://github.com/steveh8758)
|
||||||
|
|||||||
@@ -0,0 +1,233 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import os, sys
|
||||||
|
import uvicorn
|
||||||
|
import aiofiles
|
||||||
|
import configparser
|
||||||
|
import asyncio
|
||||||
|
import time
|
||||||
|
from typing import List
|
||||||
|
from fastapi import FastAPI
|
||||||
|
from fastapi.responses import JSONResponse
|
||||||
|
from fastapi.responses import FileResponse
|
||||||
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
|
from fastapi.staticfiles import StaticFiles
|
||||||
|
|
||||||
|
from sources.llm_provider import Provider
|
||||||
|
from sources.interaction import Interaction
|
||||||
|
from sources.agents import CasualAgent, CoderAgent, FileAgent, PlannerAgent, BrowserAgent
|
||||||
|
from sources.browser import Browser, create_driver
|
||||||
|
from sources.utility import pretty_print
|
||||||
|
from sources.logger import Logger
|
||||||
|
from sources.schemas import QueryRequest, QueryResponse
|
||||||
|
|
||||||
|
from celery import Celery
|
||||||
|
|
||||||
|
api = FastAPI(title="AgenticSeek API", version="0.1.0")
|
||||||
|
celery_app = Celery("tasks", broker="redis://localhost:6379/0", backend="redis://localhost:6379/0")
|
||||||
|
celery_app.conf.update(task_track_started=True)
|
||||||
|
logger = Logger("backend.log")
|
||||||
|
config = configparser.ConfigParser()
|
||||||
|
config.read('config.ini')
|
||||||
|
|
||||||
|
api.add_middleware(
|
||||||
|
CORSMiddleware,
|
||||||
|
allow_origins=["*"],
|
||||||
|
allow_credentials=True,
|
||||||
|
allow_methods=["*"],
|
||||||
|
allow_headers=["*"],
|
||||||
|
)
|
||||||
|
|
||||||
|
if not os.path.exists(".screenshots"):
|
||||||
|
os.makedirs(".screenshots")
|
||||||
|
api.mount("/screenshots", StaticFiles(directory=".screenshots"), name="screenshots")
|
||||||
|
|
||||||
|
def initialize_system():
|
||||||
|
stealth_mode = config.getboolean('BROWSER', 'stealth_mode')
|
||||||
|
personality_folder = "jarvis" if config.getboolean('MAIN', 'jarvis_personality') else "base"
|
||||||
|
languages = config["MAIN"]["languages"].split(' ')
|
||||||
|
|
||||||
|
provider = Provider(
|
||||||
|
provider_name=config["MAIN"]["provider_name"],
|
||||||
|
model=config["MAIN"]["provider_model"],
|
||||||
|
server_address=config["MAIN"]["provider_server_address"],
|
||||||
|
is_local=config.getboolean('MAIN', 'is_local')
|
||||||
|
)
|
||||||
|
logger.info(f"Provider initialized: {provider.provider_name} ({provider.model})")
|
||||||
|
|
||||||
|
browser = Browser(
|
||||||
|
create_driver(headless=config.getboolean('BROWSER', 'headless_browser'), stealth_mode=stealth_mode),
|
||||||
|
anticaptcha_manual_install=stealth_mode
|
||||||
|
)
|
||||||
|
logger.info("Browser initialized")
|
||||||
|
|
||||||
|
agents = [
|
||||||
|
CasualAgent(
|
||||||
|
name=config["MAIN"]["agent_name"],
|
||||||
|
prompt_path=f"prompts/{personality_folder}/casual_agent.txt",
|
||||||
|
provider=provider, verbose=False
|
||||||
|
),
|
||||||
|
CoderAgent(
|
||||||
|
name="coder",
|
||||||
|
prompt_path=f"prompts/{personality_folder}/coder_agent.txt",
|
||||||
|
provider=provider, verbose=False
|
||||||
|
),
|
||||||
|
FileAgent(
|
||||||
|
name="File Agent",
|
||||||
|
prompt_path=f"prompts/{personality_folder}/file_agent.txt",
|
||||||
|
provider=provider, verbose=False
|
||||||
|
),
|
||||||
|
BrowserAgent(
|
||||||
|
name="Browser",
|
||||||
|
prompt_path=f"prompts/{personality_folder}/browser_agent.txt",
|
||||||
|
provider=provider, verbose=False, browser=browser
|
||||||
|
),
|
||||||
|
PlannerAgent(
|
||||||
|
name="Planner",
|
||||||
|
prompt_path=f"prompts/{personality_folder}/planner_agent.txt",
|
||||||
|
provider=provider, verbose=False, browser=browser
|
||||||
|
)
|
||||||
|
]
|
||||||
|
logger.info("Agents initialized")
|
||||||
|
|
||||||
|
interaction = Interaction(
|
||||||
|
agents,
|
||||||
|
tts_enabled=config.getboolean('MAIN', 'speak'),
|
||||||
|
stt_enabled=config.getboolean('MAIN', 'listen'),
|
||||||
|
recover_last_session=config.getboolean('MAIN', 'recover_last_session'),
|
||||||
|
langs=languages
|
||||||
|
)
|
||||||
|
logger.info("Interaction initialized")
|
||||||
|
return interaction
|
||||||
|
|
||||||
|
interaction = initialize_system()
|
||||||
|
is_generating = False
|
||||||
|
query_resp_history = []
|
||||||
|
|
||||||
|
@api.get("/screenshot")
|
||||||
|
async def get_screenshot():
|
||||||
|
logger.info("Screenshot endpoint called")
|
||||||
|
screenshot_path = ".screenshots/updated_screen.png"
|
||||||
|
if os.path.exists(screenshot_path):
|
||||||
|
return FileResponse(screenshot_path)
|
||||||
|
logger.error("No screenshot available")
|
||||||
|
return JSONResponse(
|
||||||
|
status_code=404,
|
||||||
|
content={"error": "No screenshot available"}
|
||||||
|
)
|
||||||
|
|
||||||
|
@api.get("/health")
|
||||||
|
async def health_check():
|
||||||
|
logger.info("Health check endpoint called")
|
||||||
|
return {"status": "healthy", "version": "0.1.0"}
|
||||||
|
|
||||||
|
@api.get("/is_active")
|
||||||
|
async def is_active():
|
||||||
|
logger.info("Is active endpoint called")
|
||||||
|
return {"is_active": interaction.is_active}
|
||||||
|
|
||||||
|
@api.get("/latest_answer")
|
||||||
|
async def get_latest_answer():
|
||||||
|
global query_resp_history
|
||||||
|
if interaction.current_agent is None:
|
||||||
|
return JSONResponse(status_code=404, content={"error": "No agent available"})
|
||||||
|
if interaction.current_agent.last_answer not in [q["answer"] for q in query_resp_history]:
|
||||||
|
query_resp = {
|
||||||
|
"done": "false",
|
||||||
|
"answer": interaction.current_agent.last_answer,
|
||||||
|
"agent_name": interaction.current_agent.agent_name if interaction.current_agent else "None",
|
||||||
|
"success": interaction.current_agent.success,
|
||||||
|
"blocks": {f'{i}': block.jsonify() for i, block in enumerate(interaction.current_agent.get_blocks_result())} if interaction.current_agent else {},
|
||||||
|
"status": interaction.current_agent.get_status_message if interaction.current_agent else "No status available",
|
||||||
|
"timestamp": str(time.time())
|
||||||
|
}
|
||||||
|
query_resp_history.append(query_resp)
|
||||||
|
return JSONResponse(status_code=200, content=query_resp)
|
||||||
|
if query_resp_history:
|
||||||
|
return JSONResponse(status_code=200, content=query_resp_history[-1])
|
||||||
|
return JSONResponse(status_code=404, content={"error": "No answer available"})
|
||||||
|
|
||||||
|
async def think_wrapper(interaction, query, tts_enabled):
|
||||||
|
try:
|
||||||
|
interaction.tts_enabled = tts_enabled
|
||||||
|
interaction.last_query = query
|
||||||
|
logger.info("Agents request is being processed")
|
||||||
|
success = await interaction.think()
|
||||||
|
if not success:
|
||||||
|
interaction.last_answer = "Error: No answer from agent"
|
||||||
|
interaction.last_success = False
|
||||||
|
else:
|
||||||
|
interaction.last_success = True
|
||||||
|
return success
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error in think_wrapper: {str(e)}")
|
||||||
|
interaction.last_answer = f"Error: {str(e)}"
|
||||||
|
interaction.last_success = False
|
||||||
|
raise e
|
||||||
|
|
||||||
|
@api.post("/query", response_model=QueryResponse)
|
||||||
|
async def process_query(request: QueryRequest):
|
||||||
|
global is_generating, query_resp_history
|
||||||
|
logger.info(f"Processing query: {request.query}")
|
||||||
|
query_resp = QueryResponse(
|
||||||
|
done="false",
|
||||||
|
answer="",
|
||||||
|
agent_name="Unknown",
|
||||||
|
success="false",
|
||||||
|
blocks={},
|
||||||
|
status="Ready",
|
||||||
|
timestamp=str(time.time())
|
||||||
|
)
|
||||||
|
if is_generating:
|
||||||
|
logger.warning("Another query is being processed, please wait.")
|
||||||
|
return JSONResponse(status_code=429, content=query_resp.jsonify())
|
||||||
|
|
||||||
|
try:
|
||||||
|
is_generating = True
|
||||||
|
success = await think_wrapper(interaction, request.query, request.tts_enabled)
|
||||||
|
is_generating = False
|
||||||
|
|
||||||
|
if not success:
|
||||||
|
query_resp.answer = interaction.last_answer
|
||||||
|
return JSONResponse(status_code=400, content=query_resp.jsonify())
|
||||||
|
|
||||||
|
if interaction.current_agent:
|
||||||
|
blocks_json = {f'{i}': block.jsonify() for i, block in enumerate(interaction.current_agent.get_blocks_result())}
|
||||||
|
else:
|
||||||
|
logger.error("No current agent found")
|
||||||
|
blocks_json = {}
|
||||||
|
query_resp.answer = "Error: No current agent"
|
||||||
|
return JSONResponse(status_code=400, content=query_resp.jsonify())
|
||||||
|
|
||||||
|
logger.info(f"Answer: {interaction.last_answer}")
|
||||||
|
logger.info(f"Blocks: {blocks_json}")
|
||||||
|
query_resp.done = "true"
|
||||||
|
query_resp.answer = interaction.last_answer
|
||||||
|
query_resp.agent_name = interaction.current_agent.agent_name
|
||||||
|
query_resp.success = str(interaction.last_success)
|
||||||
|
query_resp.blocks = blocks_json
|
||||||
|
|
||||||
|
# Store the raw dictionary representation
|
||||||
|
query_resp_dict = {
|
||||||
|
"done": query_resp.done,
|
||||||
|
"answer": query_resp.answer,
|
||||||
|
"agent_name": query_resp.agent_name,
|
||||||
|
"success": query_resp.success,
|
||||||
|
"blocks": query_resp.blocks,
|
||||||
|
"status": query_resp.status,
|
||||||
|
"timestamp": query_resp.timestamp
|
||||||
|
}
|
||||||
|
query_resp_history.append(query_resp_dict)
|
||||||
|
|
||||||
|
logger.info("Query processed successfully")
|
||||||
|
return JSONResponse(status_code=200, content=query_resp.jsonify())
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"An error occurred: {str(e)}")
|
||||||
|
sys.exit(1)
|
||||||
|
finally:
|
||||||
|
logger.info("Processing finished")
|
||||||
|
if config.getboolean('MAIN', 'save_session'):
|
||||||
|
interaction.save_session()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
uvicorn.run(api, host="0.0.0.0", port=8000)
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
import sys
|
import sys
|
||||||
import argparse
|
import argparse
|
||||||
import configparser
|
import configparser
|
||||||
|
import asyncio
|
||||||
|
|
||||||
from sources.llm_provider import Provider
|
from sources.llm_provider import Provider
|
||||||
from sources.interaction import Interaction
|
from sources.interaction import Interaction
|
||||||
@@ -16,7 +17,7 @@ warnings.filterwarnings("ignore")
|
|||||||
config = configparser.ConfigParser()
|
config = configparser.ConfigParser()
|
||||||
config.read('config.ini')
|
config.read('config.ini')
|
||||||
|
|
||||||
def main():
|
async def main():
|
||||||
pretty_print("Initializing...", color="status")
|
pretty_print("Initializing...", color="status")
|
||||||
stealth_mode = config.getboolean('BROWSER', 'stealth_mode')
|
stealth_mode = config.getboolean('BROWSER', 'stealth_mode')
|
||||||
personality_folder = "jarvis" if config.getboolean('MAIN', 'jarvis_personality') else "base"
|
personality_folder = "jarvis" if config.getboolean('MAIN', 'jarvis_personality') else "base"
|
||||||
@@ -59,7 +60,7 @@ def main():
|
|||||||
try:
|
try:
|
||||||
while interaction.is_active:
|
while interaction.is_active:
|
||||||
interaction.get_user()
|
interaction.get_user()
|
||||||
if interaction.think():
|
if await interaction.think():
|
||||||
interaction.show_answer()
|
interaction.show_answer()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if config.getboolean('MAIN', 'save_session'):
|
if config.getboolean('MAIN', 'save_session'):
|
||||||
@@ -69,6 +70,5 @@ def main():
|
|||||||
if config.getboolean('MAIN', 'save_session'):
|
if config.getboolean('MAIN', 'save_session'):
|
||||||
interaction.save_session()
|
interaction.save_session()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
asyncio.run(main())
|
||||||
@@ -10,7 +10,7 @@ speak = False
|
|||||||
listen = False
|
listen = False
|
||||||
work_dir = /Users/mlg/Documents/ai_folder
|
work_dir = /Users/mlg/Documents/ai_folder
|
||||||
jarvis_personality = False
|
jarvis_personality = False
|
||||||
languages = en zh fr
|
languages = en
|
||||||
[BROWSER]
|
[BROWSER]
|
||||||
headless_browser = False
|
headless_browser = False
|
||||||
stealth_mode = True
|
stealth_mode = True
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
version: '3'
|
||||||
|
|
||||||
|
services:
|
||||||
|
redis:
|
||||||
|
container_name: redis
|
||||||
|
image: docker.io/valkey/valkey:8-alpine
|
||||||
|
command: valkey-server --save 30 1 --loglevel warning
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- redis-data:/data
|
||||||
|
cap_drop:
|
||||||
|
- ALL
|
||||||
|
cap_add:
|
||||||
|
- SETGID
|
||||||
|
- SETUID
|
||||||
|
- DAC_OVERRIDE
|
||||||
|
logging:
|
||||||
|
driver: "json-file"
|
||||||
|
options:
|
||||||
|
max-size: "1m"
|
||||||
|
max-file: "1"
|
||||||
|
networks:
|
||||||
|
- agentic-seek-net
|
||||||
|
|
||||||
|
searxng:
|
||||||
|
container_name: searxng
|
||||||
|
image: docker.io/searxng/searxng:latest
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "8080:8080"
|
||||||
|
volumes:
|
||||||
|
- ./searxng:/etc/searxng:rw
|
||||||
|
environment:
|
||||||
|
- SEARXNG_BASE_URL=http://localhost:8080/
|
||||||
|
- SEARXNG_SECRET_KEY=$(openssl rand -hex 32)
|
||||||
|
- UWSGI_WORKERS=4
|
||||||
|
- UWSGI_THREADS=4
|
||||||
|
cap_add:
|
||||||
|
- CHOWN
|
||||||
|
- SETGID
|
||||||
|
- SETUID
|
||||||
|
logging:
|
||||||
|
driver: "json-file"
|
||||||
|
options:
|
||||||
|
max-size: "1m"
|
||||||
|
max-file: "1"
|
||||||
|
depends_on:
|
||||||
|
- redis
|
||||||
|
networks:
|
||||||
|
- agentic-seek-net
|
||||||
|
|
||||||
|
frontend:
|
||||||
|
container_name: frontend
|
||||||
|
build:
|
||||||
|
context: ./frontend
|
||||||
|
dockerfile: Dockerfile.frontend
|
||||||
|
ports:
|
||||||
|
- "3000:3000"
|
||||||
|
volumes:
|
||||||
|
- ./frontend/agentic-seek-front/src:/app/src
|
||||||
|
- ./screenshots:/app/screenshots
|
||||||
|
environment:
|
||||||
|
- NODE_ENV=development
|
||||||
|
- CHOKIDAR_USEPOLLING=true
|
||||||
|
- BACKEND_URL=http://backend:8000
|
||||||
|
networks:
|
||||||
|
- agentic-seek-net
|
||||||
|
|
||||||
|
# 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
|
||||||
|
# build:
|
||||||
|
# context: ./
|
||||||
|
# dockerfile: Dockerfile.backend
|
||||||
|
# stdin_open: true
|
||||||
|
# tty: true
|
||||||
|
# shm_size: 8g
|
||||||
|
# ports:
|
||||||
|
# - "8000:8000"
|
||||||
|
# volumes:
|
||||||
|
# - ./:/app
|
||||||
|
# environment:
|
||||||
|
# - NODE_ENV=development
|
||||||
|
# - REDIS_URL=redis://redis:6379/0
|
||||||
|
# - SEARXNG_URL=http://searxng:8080
|
||||||
|
# - OLLAMA_URL=http://localhost:11434
|
||||||
|
# - LM_STUDIO_URL=http://localhost:1234
|
||||||
|
# extra_hosts:
|
||||||
|
# - "host.docker.internal:host-gateway"
|
||||||
|
# depends_on:
|
||||||
|
# - redis
|
||||||
|
# - searxng
|
||||||
|
# networks:
|
||||||
|
# - agentic-seek-net
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
redis-data:
|
||||||
|
chrome_profiles:
|
||||||
|
|
||||||
|
networks:
|
||||||
|
agentic-seek-net:
|
||||||
|
driver: bridge
|
||||||
@@ -125,4 +125,4 @@ enforcement ladder](https://github.com/mozilla/diversity).
|
|||||||
|
|
||||||
For answers to common questions about this code of conduct, see the FAQ at
|
For answers to common questions about this code of conduct, see the FAQ at
|
||||||
https://www.contributor-covenant.org/faq. Translations are available at
|
https://www.contributor-covenant.org/faq. Translations are available at
|
||||||
https://www.contributor-covenant.org/translations.
|
https://www.contributor-covenant.org/translations.
|
||||||
@@ -254,7 +254,7 @@ def execute_modules(self, answer: str) -> Tuple[bool, str]:
|
|||||||
## 1. Agent selection logic
|
## 1. Agent selection logic
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img align="center" src="./media/technical/routing_system.png">
|
<img align="center" src="./technical/routing_system.png">
|
||||||
<p>
|
<p>
|
||||||
|
|
||||||
The agent selection is done in 4 steps:
|
The agent selection is done in 4 steps:
|
||||||
@@ -271,7 +271,7 @@ The agent selection is done in 4 steps:
|
|||||||
### File/Code agents
|
### File/Code agents
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img align="center" src="./media/technical/code_agent.png">
|
<img align="center" src="./technical/code_agent.png">
|
||||||
<p>
|
<p>
|
||||||
|
|
||||||
The File and Code agents operate similarly: when a prompt is submitted, they initiate a loop between the LLM and a code interpreter. This loop continues executing commands or code until the execution is successful or the maximum number of attempts is reached.
|
The File and Code agents operate similarly: when a prompt is submitted, they initiate a loop between the LLM and a code interpreter. This loop continues executing commands or code until the execution is successful or the maximum number of attempts is reached.
|
||||||
@@ -279,7 +279,7 @@ The File and Code agents operate similarly: when a prompt is submitted, they ini
|
|||||||
### Web agent
|
### Web agent
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img align="center" src="./media/technical/web_agent.png">
|
<img align="center" src="./technical/web_agent.png">
|
||||||
<p>
|
<p>
|
||||||
|
|
||||||
The Web agent controls a Selenium-driven browser. Upon receiving a query, it begins by generating an optimized search prompt and executing the web_search tool. It then enters a navigation loop, during which it:
|
The Web agent controls a Selenium-driven browser. Upon receiving a query, it begins by generating an optimized search prompt and executing the web_search tool. It then enters a navigation loop, during which it:
|
||||||
|
Before Width: | Height: | Size: 129 KiB After Width: | Height: | Size: 129 KiB |
|
Before Width: | Height: | Size: 112 KiB After Width: | Height: | Size: 112 KiB |
|
Before Width: | Height: | Size: 116 KiB After Width: | Height: | Size: 116 KiB |
|
Before Width: | Height: | Size: 482 KiB After Width: | Height: | Size: 482 KiB |
@@ -0,0 +1,23 @@
|
|||||||
|
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||||
|
|
||||||
|
# dependencies
|
||||||
|
/node_modules
|
||||||
|
/.pnp
|
||||||
|
.pnp.js
|
||||||
|
|
||||||
|
# testing
|
||||||
|
/coverage
|
||||||
|
|
||||||
|
# production
|
||||||
|
/build
|
||||||
|
|
||||||
|
# misc
|
||||||
|
.DS_Store
|
||||||
|
.env.local
|
||||||
|
.env.development.local
|
||||||
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
|
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
FROM node:18
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
COPY agentic-seek-front/package.json agentic-seek-front/package-lock.json ./
|
||||||
|
RUN npm install
|
||||||
|
|
||||||
|
# Copy application code
|
||||||
|
COPY agentic-seek-front/ .
|
||||||
|
|
||||||
|
# Expose port
|
||||||
|
EXPOSE 3000
|
||||||
|
|
||||||
|
# Run the application
|
||||||
|
CMD ["npm", "start"]
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
# Getting Started with Create React App
|
||||||
|
|
||||||
|
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
|
||||||
|
|
||||||
|
## Available Scripts
|
||||||
|
|
||||||
|
In the project directory, you can run:
|
||||||
|
|
||||||
|
### `npm start`
|
||||||
|
|
||||||
|
Runs the app in the development mode.\
|
||||||
|
Open [http://localhost:3000](http://localhost:3000) to view it in your browser.
|
||||||
|
|
||||||
|
The page will reload when you make changes.\
|
||||||
|
You may also see any lint errors in the console.
|
||||||
|
|
||||||
|
### `npm test`
|
||||||
|
|
||||||
|
Launches the test runner in the interactive watch mode.\
|
||||||
|
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
|
||||||
|
|
||||||
|
### `npm run build`
|
||||||
|
|
||||||
|
Builds the app for production to the `build` folder.\
|
||||||
|
It correctly bundles React in production mode and optimizes the build for the best performance.
|
||||||
|
|
||||||
|
The build is minified and the filenames include the hashes.\
|
||||||
|
Your app is ready to be deployed!
|
||||||
|
|
||||||
|
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
|
||||||
|
|
||||||
|
### `npm run eject`
|
||||||
|
|
||||||
|
**Note: this is a one-way operation. Once you `eject`, you can't go back!**
|
||||||
|
|
||||||
|
If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
|
||||||
|
|
||||||
|
Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own.
|
||||||
|
|
||||||
|
You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it.
|
||||||
|
|
||||||
|
## Learn More
|
||||||
|
|
||||||
|
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
|
||||||
|
|
||||||
|
To learn React, check out the [React documentation](https://reactjs.org/).
|
||||||
|
|
||||||
|
### Code Splitting
|
||||||
|
|
||||||
|
This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting)
|
||||||
|
|
||||||
|
### Analyzing the Bundle Size
|
||||||
|
|
||||||
|
This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size)
|
||||||
|
|
||||||
|
### Making a Progressive Web App
|
||||||
|
|
||||||
|
This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app)
|
||||||
|
|
||||||
|
### Advanced Configuration
|
||||||
|
|
||||||
|
This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration)
|
||||||
|
|
||||||
|
### Deployment
|
||||||
|
|
||||||
|
This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment)
|
||||||
|
|
||||||
|
### `npm run build` fails to minify
|
||||||
|
|
||||||
|
This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify)
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
{
|
||||||
|
"name": "agentic-seek",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@testing-library/dom": "^10.4.0",
|
||||||
|
"@testing-library/jest-dom": "^6.6.3",
|
||||||
|
"@testing-library/react": "^16.3.0",
|
||||||
|
"@testing-library/user-event": "^13.5.0",
|
||||||
|
"axios": "^1.8.4",
|
||||||
|
"react": "^19.1.0",
|
||||||
|
"react-dom": "^19.1.0",
|
||||||
|
"react-scripts": "5.0.1",
|
||||||
|
"web-vitals": "^2.1.4"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"start": "react-scripts start",
|
||||||
|
"build": "react-scripts build",
|
||||||
|
"test": "react-scripts test",
|
||||||
|
"eject": "react-scripts eject"
|
||||||
|
},
|
||||||
|
"eslintConfig": {
|
||||||
|
"extends": [
|
||||||
|
"react-app",
|
||||||
|
"react-app/jest"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"browserslist": {
|
||||||
|
"production": [
|
||||||
|
">0.2%",
|
||||||
|
"not dead",
|
||||||
|
"not op_mini all"
|
||||||
|
],
|
||||||
|
"development": [
|
||||||
|
"last 1 chrome version",
|
||||||
|
"last 1 firefox version",
|
||||||
|
"last 1 safari version"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 3.8 KiB |
@@ -0,0 +1,15 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<title>AgenticSeek</title>
|
||||||
|
<link
|
||||||
|
href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700&display=swap"
|
||||||
|
rel="stylesheet"
|
||||||
|
/>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="root"></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
After Width: | Height: | Size: 5.2 KiB |
|
After Width: | Height: | Size: 9.4 KiB |
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"short_name": "React App",
|
||||||
|
"name": "Create React App Sample",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "favicon.ico",
|
||||||
|
"sizes": "64x64 32x32 24x24 16x16",
|
||||||
|
"type": "image/x-icon"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "logo192.png",
|
||||||
|
"type": "image/png",
|
||||||
|
"sizes": "192x192"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "logo512.png",
|
||||||
|
"type": "image/png",
|
||||||
|
"sizes": "512x512"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"start_url": ".",
|
||||||
|
"display": "standalone",
|
||||||
|
"theme_color": "#000000",
|
||||||
|
"background_color": "#ffffff"
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
# https://www.robotstxt.org/robotstxt.html
|
||||||
|
User-agent: *
|
||||||
|
Disallow:
|
||||||
@@ -0,0 +1,257 @@
|
|||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: 'Orbitron', sans-serif;
|
||||||
|
background-color: #0a0a0a;
|
||||||
|
color: #ffffff;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app {
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
padding: 20px;
|
||||||
|
text-align: center;
|
||||||
|
background-color: #1a1a1a;
|
||||||
|
border-bottom: 2px solid #00ffcc;
|
||||||
|
box-shadow: 0 0 10px #00ffcc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header h1 {
|
||||||
|
font-size: 2.5rem;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main {
|
||||||
|
flex: 1;
|
||||||
|
padding: 40px;
|
||||||
|
max-width: 1400px;
|
||||||
|
margin: 0 auto;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-container {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: 40px;
|
||||||
|
height: calc(100vh - 200px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.left-panel,
|
||||||
|
.right-panel {
|
||||||
|
background-color: #1a1a1a;
|
||||||
|
border: 1px solid #00ffcc;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 0 10px rgba(0, 255, 204, 0.2);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.left-panel {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.messages {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 10px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
max-height: 100%;
|
||||||
|
gap: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-messages {
|
||||||
|
text-align: center;
|
||||||
|
color: #666;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message {
|
||||||
|
max-width: 80%;
|
||||||
|
padding: 10px 15px;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.messages::-webkit-scrollbar,
|
||||||
|
.content::-webkit-scrollbar {
|
||||||
|
width: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-message {
|
||||||
|
background-color: #00ffcc;
|
||||||
|
color: #000;
|
||||||
|
align-self: flex-end;
|
||||||
|
border: 1px solid #00ccaa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.agent-message {
|
||||||
|
background-color: #333;
|
||||||
|
color: #fff;
|
||||||
|
align-self: flex-start;
|
||||||
|
border: 1px solid #00ffcc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-message {
|
||||||
|
background-color: #ff4444;
|
||||||
|
color: #fff;
|
||||||
|
align-self: flex-start;
|
||||||
|
border: 1px solid #cc3333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.agent-name {
|
||||||
|
display: block;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
color: #00ffcc;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-form {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-form input {
|
||||||
|
flex: 1;
|
||||||
|
padding: 12px;
|
||||||
|
font-size: 1rem;
|
||||||
|
background-color: #222;
|
||||||
|
border: 1px solid #00ffcc;
|
||||||
|
color: #fff;
|
||||||
|
border-radius: 4px;
|
||||||
|
outline: none;
|
||||||
|
transition: box-shadow 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-form input:focus {
|
||||||
|
box-shadow: 0 0 8px #00ffcc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-form button {
|
||||||
|
padding: 12px 24px;
|
||||||
|
font-size: 1rem;
|
||||||
|
background-color: #00ffcc;
|
||||||
|
color: #000;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
text-transform: uppercase;
|
||||||
|
transition: background-color 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-form button:hover {
|
||||||
|
background-color: #00ccaa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-form button:disabled {
|
||||||
|
background-color: #555;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right-panel {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.view-selector {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.view-selector button {
|
||||||
|
padding: 10px 20px;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
background-color: #222;
|
||||||
|
color: #fff;
|
||||||
|
border: 1px solid #00ffcc;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
text-transform: uppercase;
|
||||||
|
transition: background-color 0.3s, color 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.view-selector button.active,
|
||||||
|
.view-selector button:hover {
|
||||||
|
background-color: #00ffcc;
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.view-selector button:disabled {
|
||||||
|
background-color: #555;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blocks {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.block {
|
||||||
|
background-color: #222;
|
||||||
|
padding: 10px;
|
||||||
|
border: 1px solid #00ffcc;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.block-tool,
|
||||||
|
.block-feedback,
|
||||||
|
.block-success {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.block pre {
|
||||||
|
background-color: #111;
|
||||||
|
padding: 5px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.screenshot {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.screenshot img {
|
||||||
|
max-width: 100%;
|
||||||
|
border: 1px solid #00ffcc;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error {
|
||||||
|
color: #ff4444;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.chat-container {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.left-panel,
|
||||||
|
.right-panel {
|
||||||
|
height: 50vh;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,262 @@
|
|||||||
|
import React, { useState, useEffect, useRef } from 'react';
|
||||||
|
import axios from 'axios';
|
||||||
|
import './App.css';
|
||||||
|
|
||||||
|
function App() {
|
||||||
|
const [query, setQuery] = useState('');
|
||||||
|
const [messages, setMessages] = useState([]);
|
||||||
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
const [error, setError] = useState(null);
|
||||||
|
const [currentView, setCurrentView] = useState('blocks');
|
||||||
|
const [responseData, setResponseData] = useState(null);
|
||||||
|
const [isOnline, setIsOnline] = useState(false);
|
||||||
|
const [status, setStatus] = useState('Agents ready');
|
||||||
|
const messagesEndRef = useRef(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const intervalId = setInterval(() => {
|
||||||
|
checkHealth();
|
||||||
|
fetchLatestAnswer();
|
||||||
|
fetchScreenshot();
|
||||||
|
}, 1500);
|
||||||
|
return () => clearInterval(intervalId);
|
||||||
|
}, [messages]);
|
||||||
|
|
||||||
|
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 fetchScreenshot = async () => {
|
||||||
|
try {
|
||||||
|
const timestamp = new Date().getTime();
|
||||||
|
const res = await axios.get(`http://0.0.0.0:8000/screenshots/updated_screen.png?timestamp=${timestamp}`, {
|
||||||
|
responseType: 'blob'
|
||||||
|
});
|
||||||
|
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);
|
||||||
|
setResponseData((prev) => ({
|
||||||
|
...prev,
|
||||||
|
screenshot: 'placeholder.png',
|
||||||
|
screenshotTimestamp: new Date().getTime()
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const normalizeAnswer = (answer) => answer.trim().toLowerCase();
|
||||||
|
|
||||||
|
const scrollToBottom = () => {
|
||||||
|
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchLatestAnswer = async () => {
|
||||||
|
try {
|
||||||
|
const res = await axios.get('http://0.0.0.0:8000/latest_answer');
|
||||||
|
const data = res.data;
|
||||||
|
|
||||||
|
if (!data.answer || data.answer.trim() === '') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const answerExists = messages.some(
|
||||||
|
(msg) =>
|
||||||
|
msg.timestamp === data.timestamp &&
|
||||||
|
normalizeAnswer(msg.content) === normalizeAnswer(data.answer)
|
||||||
|
);
|
||||||
|
console.log('Fetched latest answer:', data.answer);
|
||||||
|
if (!answerExists) {
|
||||||
|
setMessages((prev) => [
|
||||||
|
...prev,
|
||||||
|
{
|
||||||
|
type: 'agent',
|
||||||
|
content: data.answer,
|
||||||
|
agentName: data.agent_name,
|
||||||
|
status: data.status,
|
||||||
|
timestamp: data.timestamp,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
setStatus(data.status);
|
||||||
|
scrollToBottom();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching latest answer:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const handleSubmit = async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
checkHealth();
|
||||||
|
if (!query.trim()) {
|
||||||
|
console.log('Empty query');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setMessages((prev) => [...prev, { type: 'user', content: query }]);
|
||||||
|
setIsLoading(true);
|
||||||
|
setError(null);
|
||||||
|
|
||||||
|
try {
|
||||||
|
console.log('Sending query:', query);
|
||||||
|
setQuery('waiting for response...');
|
||||||
|
const res = await axios.post('http://0.0.0.0:8000/query', {
|
||||||
|
query,
|
||||||
|
tts_enabled: false
|
||||||
|
});
|
||||||
|
setQuery('Enter your query...');
|
||||||
|
console.log('Response:', res.data);
|
||||||
|
const data = res.data;
|
||||||
|
setResponseData(data);
|
||||||
|
fetchLatestAnswer();
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error:', err);
|
||||||
|
setError('Failed to process query.');
|
||||||
|
setMessages((prev) => [
|
||||||
|
...prev,
|
||||||
|
{ type: 'error', content: 'Error: Unable to get a response.' },
|
||||||
|
]);
|
||||||
|
} finally {
|
||||||
|
console.log('Query completed');
|
||||||
|
setIsLoading(false);
|
||||||
|
setQuery('');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleGetScreenshot = async () => {
|
||||||
|
try {
|
||||||
|
console.log('Fetching screenshot...');
|
||||||
|
const res = await axios.get('http://0.0.0.0:8000/screenshots/updated_screen.png');
|
||||||
|
setResponseData((prev) => ({ ...prev, screenshot: res.data.screenshot }));
|
||||||
|
setCurrentView('screenshot');
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error fetching screenshot:', err);
|
||||||
|
setError('Browser not in use');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="app">
|
||||||
|
<header className="header">
|
||||||
|
<h1>AgenticSeek</h1>
|
||||||
|
</header>
|
||||||
|
<main className="main">
|
||||||
|
<div className="chat-container">
|
||||||
|
<div className="left-panel">
|
||||||
|
<h2>C H A T</h2>
|
||||||
|
<br />
|
||||||
|
<div className="messages">
|
||||||
|
{messages.length === 0 ? (
|
||||||
|
<p className="placeholder">No messages yet. Type below to start!</p>
|
||||||
|
) : (
|
||||||
|
messages.map((msg, index) => (
|
||||||
|
<div
|
||||||
|
key={index}
|
||||||
|
className={`message ${
|
||||||
|
msg.type === 'user'
|
||||||
|
? 'user-message'
|
||||||
|
: msg.type === 'agent'
|
||||||
|
? 'agent-message'
|
||||||
|
: 'error-message'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{msg.type === 'agent' && (
|
||||||
|
<span className="agent-name">{msg.agentName}</span>
|
||||||
|
)}
|
||||||
|
<p>{msg.content}</p>
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
)}
|
||||||
|
<div ref={messagesEndRef} />
|
||||||
|
</div>
|
||||||
|
{isOnline && <div className="loading-animation">{status}</div>}
|
||||||
|
{!isLoading && !isOnline && <p className="loading-animation">System offline. Deploy backend first.</p>}
|
||||||
|
<form onSubmit={handleSubmit} className="input-form">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={query}
|
||||||
|
onChange={(e) => setQuery(e.target.value)}
|
||||||
|
placeholder="Type your query..."
|
||||||
|
disabled={isLoading}
|
||||||
|
/>
|
||||||
|
<button type="submit" disabled={isLoading}>
|
||||||
|
Send
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div className="right-panel">
|
||||||
|
<h2>I N T E R F A C E</h2>
|
||||||
|
<br />
|
||||||
|
<div className="view-selector">
|
||||||
|
<button
|
||||||
|
className={currentView === 'blocks' ? 'active' : ''}
|
||||||
|
onClick={() => setCurrentView('blocks')}
|
||||||
|
>
|
||||||
|
Editor View
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className={currentView === 'screenshot' ? 'active' : ''}
|
||||||
|
onClick={responseData?.screenshot ? () => setCurrentView('screenshot') : handleGetScreenshot}
|
||||||
|
>
|
||||||
|
Browser View
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div className="content">
|
||||||
|
{error && <p className="error">{error}</p>}
|
||||||
|
{currentView === 'blocks' ? (
|
||||||
|
<div className="blocks">
|
||||||
|
{responseData && responseData.blocks && Object.values(responseData.blocks).length > 0 ? (
|
||||||
|
Object.values(responseData.blocks).map((block, index) => (
|
||||||
|
<div key={index} className="block">
|
||||||
|
<p className="block-tool">Tool: {block.tool_type}</p>
|
||||||
|
<pre>{block.block}</pre>
|
||||||
|
<p className="block-feedback">Feedback: {block.feedback}</p>
|
||||||
|
<p className="block-success">
|
||||||
|
Success: {block.success ? 'Yes' : 'No'}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<div className="block">
|
||||||
|
<p className="block-tool">Tool: No tool in use</p>
|
||||||
|
<pre>No file opened</pre>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="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'}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default App;
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import { render, screen } from '@testing-library/react';
|
||||||
|
import App from './App';
|
||||||
|
|
||||||
|
test('renders learn react link', () => {
|
||||||
|
render(<App />);
|
||||||
|
const linkElement = screen.getByText(/learn react/i);
|
||||||
|
expect(linkElement).toBeInTheDocument();
|
||||||
|
});
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||||
|
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||||
|
sans-serif;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
||||||
|
monospace;
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import ReactDOM from 'react-dom/client';
|
||||||
|
import App from './App';
|
||||||
|
|
||||||
|
const root = ReactDOM.createRoot(document.getElementById('root'));
|
||||||
|
root.render(
|
||||||
|
<React.StrictMode>
|
||||||
|
<App />
|
||||||
|
</React.StrictMode>
|
||||||
|
);
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3"><g fill="#61DAFB"><path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/><circle cx="420.9" cy="296.5" r="45.7"/><path d="M520.5 78.1z"/></g></svg>
|
||||||
|
After Width: | Height: | Size: 2.6 KiB |
@@ -0,0 +1,13 @@
|
|||||||
|
const reportWebVitals = onPerfEntry => {
|
||||||
|
if (onPerfEntry && onPerfEntry instanceof Function) {
|
||||||
|
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
|
||||||
|
getCLS(onPerfEntry);
|
||||||
|
getFID(onPerfEntry);
|
||||||
|
getFCP(onPerfEntry);
|
||||||
|
getLCP(onPerfEntry);
|
||||||
|
getTTFB(onPerfEntry);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default reportWebVitals;
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
// jest-dom adds custom jest matchers for asserting on DOM nodes.
|
||||||
|
// allows you to do things like:
|
||||||
|
// expect(element).toHaveTextContent(/react/i)
|
||||||
|
// learn more: https://github.com/testing-library/jest-dom
|
||||||
|
import '@testing-library/jest-dom';
|
||||||
|
Before Width: | Height: | Size: 879 KiB |
|
Before Width: | Height: | Size: 558 KiB |
|
Before Width: | Height: | Size: 104 KiB |
@@ -47,7 +47,6 @@ Some rules:
|
|||||||
- Be efficient, no need to explain your code, unless asked.
|
- Be efficient, no need to explain your code, unless asked.
|
||||||
- You do not ever need to use bash to execute code.
|
- You do not ever need to use bash to execute code.
|
||||||
- Do not ever tell user how to run it. user know it.
|
- Do not ever tell user how to run it. user know it.
|
||||||
- In python do not use if __name__ == "__main__"
|
|
||||||
- If using gui, make sure echap or exit button close the program
|
- If using gui, make sure echap or exit button close the program
|
||||||
- No lazyness, write and rewrite full code every time
|
- No lazyness, write and rewrite full code every time
|
||||||
- If query is unclear say REQUEST_CLARIFICATION
|
- If query is unclear say REQUEST_CLARIFICATION
|
||||||
@@ -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."
|
||||||
|
|||||||
@@ -46,7 +46,6 @@ Some rules:
|
|||||||
- Be efficient, no need to explain your code, unless asked.
|
- Be efficient, no need to explain your code, unless asked.
|
||||||
- You do not ever need to use bash to execute code.
|
- You do not ever need to use bash to execute code.
|
||||||
- Do not ever tell user how to run it. user know it.
|
- Do not ever tell user how to run it. user know it.
|
||||||
- In python do not use if __name__ == "__main__"
|
|
||||||
- If using gui, make sure echap close the program
|
- If using gui, make sure echap close the program
|
||||||
- No lazyness, write and rewrite full code every time
|
- No lazyness, write and rewrite full code every time
|
||||||
- If query is unclear say REQUEST_CLARIFICATION
|
- If query is unclear say REQUEST_CLARIFICATION
|
||||||
|
|||||||
@@ -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."
|
||||||
|
|||||||
@@ -1,4 +1,12 @@
|
|||||||
|
fastapi>=0.115.12
|
||||||
|
flask>=3.1.0
|
||||||
|
celery>=5.5.1
|
||||||
|
aiofiles>=24.1.0
|
||||||
|
uvicorn>=0.34.0
|
||||||
|
pydantic>=2.10.6
|
||||||
|
pydantic_core>=2.27.2
|
||||||
setuptools>=75.6.0
|
setuptools>=75.6.0
|
||||||
|
sacremoses>=0.0.53
|
||||||
requests>=2.31.0
|
requests>=2.31.0
|
||||||
numpy>=1.24.4
|
numpy>=1.24.4
|
||||||
colorama>=0.4.6
|
colorama>=0.4.6
|
||||||
@@ -27,14 +35,13 @@ httpx>=0.27,<0.29
|
|||||||
anyio>=3.5.0,<5
|
anyio>=3.5.0,<5
|
||||||
distro>=1.7.0,<2
|
distro>=1.7.0,<2
|
||||||
jiter>=0.4.0,<1
|
jiter>=0.4.0,<1
|
||||||
sniffio
|
|
||||||
tqdm>4
|
|
||||||
fake_useragent>=2.1.0
|
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
|
||||||
# for api provider
|
|
||||||
openai
|
openai
|
||||||
|
sniffio
|
||||||
|
tqdm>4
|
||||||
# if use chinese
|
# if use chinese
|
||||||
ordered_set
|
ordered_set
|
||||||
pypinyin
|
pypinyin
|
||||||
|
|||||||
@@ -29,8 +29,8 @@ services:
|
|||||||
- ./searxng:/etc/searxng:rw
|
- ./searxng:/etc/searxng:rw
|
||||||
environment:
|
environment:
|
||||||
- SEARXNG_BASE_URL=http://localhost:8080/
|
- SEARXNG_BASE_URL=http://localhost:8080/
|
||||||
- UWSGI_WORKERS=4
|
- UWSGI_WORKERS=1
|
||||||
- UWSGI_THREADS=4
|
- UWSGI_THREADS=1
|
||||||
cap_add:
|
cap_add:
|
||||||
- CHOWN
|
- CHOWN
|
||||||
- SETGID
|
- SETGID
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ server:
|
|||||||
# If your instance owns a /etc/searxng/settings.yml file, then set the following
|
# If your instance owns a /etc/searxng/settings.yml file, then set the following
|
||||||
# values there.
|
# values there.
|
||||||
|
|
||||||
secret_key: "ultrasecretkey" # Is overwritten by ${SEARXNG_SECRET}
|
secret_key: "supersecret" # Is overwritten by ${SEARXNG_SECRET},W
|
||||||
# Proxy image results through SearXNG. Is overwritten by ${SEARXNG_IMAGE_PROXY}
|
# Proxy image results through SearXNG. Is overwritten by ${SEARXNG_IMAGE_PROXY}
|
||||||
image_proxy: false
|
image_proxy: false
|
||||||
# 1.0 and 1.1 are supported
|
# 1.0 and 1.1 are supported
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
[uwsgi]
|
||||||
|
# Who will run the code
|
||||||
|
uid = searxng
|
||||||
|
gid = searxng
|
||||||
|
|
||||||
|
# Number of workers (usually CPU count)
|
||||||
|
# default value: %k (= number of CPU core, see Dockerfile)
|
||||||
|
workers = 1
|
||||||
|
|
||||||
|
# Number of threads per worker
|
||||||
|
# default value: 4 (see Dockerfile)
|
||||||
|
enable-threads = true
|
||||||
|
threads = 1
|
||||||
|
|
||||||
|
# The right granted on the created socket
|
||||||
|
chmod-socket = 666
|
||||||
|
|
||||||
|
# Plugin to use and interpreter config
|
||||||
|
single-interpreter = true
|
||||||
|
master = true
|
||||||
|
plugin = python3
|
||||||
|
lazy-apps = true
|
||||||
|
enable-threads = 4
|
||||||
|
|
||||||
|
# Module to import
|
||||||
|
module = searx.webapp
|
||||||
|
|
||||||
|
# Virtualenv and python path
|
||||||
|
pythonpath = /usr/local/searxng/
|
||||||
|
chdir = /usr/local/searxng/searx/
|
||||||
|
|
||||||
|
# automatically set processes name to something meaningful
|
||||||
|
auto-procname = true
|
||||||
|
|
||||||
|
# Disable request logging for privacy
|
||||||
|
disable-logging = true
|
||||||
|
log-5xx = true
|
||||||
|
|
||||||
|
# Set the max size of a request (request-body excluded)
|
||||||
|
buffer-size = 8192
|
||||||
|
|
||||||
|
# No keep alive
|
||||||
|
# See https://github.com/searx/searx-docker/issues/24
|
||||||
|
add-header = Connection: close
|
||||||
|
|
||||||
|
# Follow SIGTERM convention
|
||||||
|
# See https://github.com/searxng/searxng/issues/3427
|
||||||
|
die-on-term
|
||||||
|
|
||||||
|
# uwsgi serves the static files
|
||||||
|
static-map = /static=/usr/local/searxng/searx/static
|
||||||
|
static-gzip-all = True
|
||||||
|
offload-threads = 4
|
||||||
@@ -15,8 +15,16 @@ setup(
|
|||||||
packages=find_packages(),
|
packages=find_packages(),
|
||||||
include_package_data=True,
|
include_package_data=True,
|
||||||
install_requires=[
|
install_requires=[
|
||||||
|
"fastapi>=0.115.12",
|
||||||
|
"celery>=5.5.1",
|
||||||
|
"uvicorn>=0.34.0",
|
||||||
|
"flask>=3.1.0",
|
||||||
|
"aiofiles>=24.1.0",
|
||||||
|
"pydantic>=2.10.6",
|
||||||
|
"pydantic_core>=2.27.2",
|
||||||
"requests>=2.31.0",
|
"requests>=2.31.0",
|
||||||
"openai",
|
"sacremoses>=0.0.53",
|
||||||
|
"numpy>=1.24.4",
|
||||||
"colorama>=0.4.6",
|
"colorama>=0.4.6",
|
||||||
"python-dotenv>=1.0.0",
|
"python-dotenv>=1.0.0",
|
||||||
"playsound>=1.3.0",
|
"playsound>=1.3.0",
|
||||||
@@ -26,7 +34,6 @@ setup(
|
|||||||
"ollama>=0.4.7",
|
"ollama>=0.4.7",
|
||||||
"scipy>=1.9.3",
|
"scipy>=1.9.3",
|
||||||
"kokoro>=0.7.12",
|
"kokoro>=0.7.12",
|
||||||
"flask>=3.1.0",
|
|
||||||
"protobuf>=3.20.3",
|
"protobuf>=3.20.3",
|
||||||
"termcolor>=2.5.0",
|
"termcolor>=2.5.0",
|
||||||
"ipython>=8.34.0",
|
"ipython>=8.34.0",
|
||||||
@@ -35,8 +42,9 @@ setup(
|
|||||||
"markdownify>=1.1.0",
|
"markdownify>=1.1.0",
|
||||||
"text2emotion>=0.0.5",
|
"text2emotion>=0.0.5",
|
||||||
"python-dotenv>=1.0.0",
|
"python-dotenv>=1.0.0",
|
||||||
"langid>=1.1.6",
|
|
||||||
"adaptive-classifier>=0.0.10",
|
"adaptive-classifier>=0.0.10",
|
||||||
|
"langid>=1.1.6",
|
||||||
|
"chromedriver-autoinstaller>=0.6.4",
|
||||||
"httpx>=0.27,<0.29",
|
"httpx>=0.27,<0.29",
|
||||||
"anyio>=3.5.0,<5",
|
"anyio>=3.5.0,<5",
|
||||||
"distro>=1.7.0,<2",
|
"distro>=1.7.0,<2",
|
||||||
@@ -45,6 +53,7 @@ setup(
|
|||||||
"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",
|
||||||
|
"openai",
|
||||||
"sniffio",
|
"sniffio",
|
||||||
"tqdm>4"
|
"tqdm>4"
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -5,27 +5,15 @@ import os
|
|||||||
import random
|
import random
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
from concurrent.futures import ThreadPoolExecutor
|
||||||
|
|
||||||
from sources.memory import Memory
|
from sources.memory import Memory
|
||||||
from sources.utility import pretty_print
|
from sources.utility import pretty_print
|
||||||
|
from sources.schemas import executorResult
|
||||||
|
|
||||||
random.seed(time.time())
|
random.seed(time.time())
|
||||||
|
|
||||||
class executorResult:
|
|
||||||
"""
|
|
||||||
A class to store the result of a tool execution.
|
|
||||||
"""
|
|
||||||
def __init__(self, block, feedback, success, tool_type):
|
|
||||||
self.block = block
|
|
||||||
self.feedback = feedback
|
|
||||||
self.success = success
|
|
||||||
self.tool_type = tool_type
|
|
||||||
|
|
||||||
def show(self):
|
|
||||||
pretty_print('▂'*64, color="status")
|
|
||||||
pretty_print(self.block, color="code" if self.success else "failure")
|
|
||||||
pretty_print('▂'*64, color="status")
|
|
||||||
pretty_print(self.feedback, color="success" if self.success else "failure")
|
|
||||||
|
|
||||||
class Agent():
|
class Agent():
|
||||||
"""
|
"""
|
||||||
An abstract class for all agents.
|
An abstract class for all agents.
|
||||||
@@ -56,12 +44,42 @@ class Agent():
|
|||||||
memory_compression=False)
|
memory_compression=False)
|
||||||
self.tools = {}
|
self.tools = {}
|
||||||
self.blocks_result = []
|
self.blocks_result = []
|
||||||
|
self.success = True
|
||||||
self.last_answer = ""
|
self.last_answer = ""
|
||||||
|
self.status_message = "Haven't started yet"
|
||||||
self.verbose = verbose
|
self.verbose = verbose
|
||||||
|
self.executor = ThreadPoolExecutor(max_workers=1)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def get_agent_name(self) -> str:
|
||||||
|
return self.agent_name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def get_agent_type(self) -> str:
|
||||||
|
return self.type
|
||||||
|
|
||||||
|
@property
|
||||||
|
def get_agent_role(self) -> str:
|
||||||
|
return self.role
|
||||||
|
|
||||||
|
@property
|
||||||
|
def get_last_answer(self) -> str:
|
||||||
|
return self.last_answer
|
||||||
|
|
||||||
|
@property
|
||||||
|
def get_blocks(self) -> list:
|
||||||
|
return self.blocks_result
|
||||||
|
|
||||||
|
@property
|
||||||
|
def get_status_message(self) -> str:
|
||||||
|
return self.status_message
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def get_tools(self) -> dict:
|
def get_tools(self) -> dict:
|
||||||
return self.tools
|
return self.tools
|
||||||
|
|
||||||
|
def get_blocks_result(self) -> list:
|
||||||
|
return self.blocks_result
|
||||||
|
|
||||||
def add_tool(self, name: str, tool: Callable) -> None:
|
def add_tool(self, name: str, tool: Callable) -> None:
|
||||||
if tool is not Callable:
|
if tool is not Callable:
|
||||||
@@ -105,7 +123,15 @@ class Agent():
|
|||||||
end_idx = text.rfind(end_tag)+8
|
end_idx = text.rfind(end_tag)+8
|
||||||
return text[start_idx:end_idx]
|
return text[start_idx:end_idx]
|
||||||
|
|
||||||
def llm_request(self) -> Tuple[str, str]:
|
async def llm_request(self) -> Tuple[str, str]:
|
||||||
|
"""
|
||||||
|
Asynchronously ask the LLM to process the prompt.
|
||||||
|
"""
|
||||||
|
self.status_message = "Thinking..."
|
||||||
|
loop = asyncio.get_event_loop()
|
||||||
|
return await loop.run_in_executor(self.executor, self.sync_llm_request)
|
||||||
|
|
||||||
|
def sync_llm_request(self) -> Tuple[str, str]:
|
||||||
"""
|
"""
|
||||||
Ask the LLM to process the prompt and return the answer and the reasoning.
|
Ask the LLM to process the prompt and return the answer and the reasoning.
|
||||||
"""
|
"""
|
||||||
@@ -117,17 +143,15 @@ class Agent():
|
|||||||
self.memory.push('assistant', answer)
|
self.memory.push('assistant', answer)
|
||||||
return answer, reasoning
|
return answer, reasoning
|
||||||
|
|
||||||
def wait_message(self, speech_module):
|
async def wait_message(self, speech_module):
|
||||||
if speech_module is None:
|
if speech_module is None:
|
||||||
return
|
return
|
||||||
messages = ["Please be patient, I am working on it.",
|
messages = ["Please be patient, I am working on it.",
|
||||||
"Computing... I recommand you have a coffee while I work.",
|
"Computing... I recommand you have a coffee while I work.",
|
||||||
"Hold on, I’m crunching numbers.",
|
"Hold on, I’m crunching numbers.",
|
||||||
"Working on it, please let me think."]
|
"Working on it, please let me think."]
|
||||||
if speech_module: speech_module.speak(messages[random.randint(0, len(messages)-1)])
|
loop = asyncio.get_event_loop()
|
||||||
|
return await loop.run_in_executor(self.executor, lambda: speech_module.speak(messages[random.randint(0, len(messages)-1)]))
|
||||||
def get_blocks_result(self) -> list:
|
|
||||||
return self.blocks_result
|
|
||||||
|
|
||||||
def get_last_tool_type(self) -> str:
|
def get_last_tool_type(self) -> str:
|
||||||
return self.blocks_result[-1].tool_type if len(self.blocks_result) > 0 else None
|
return self.blocks_result[-1].tool_type if len(self.blocks_result) > 0 else None
|
||||||
@@ -175,11 +199,12 @@ class Agent():
|
|||||||
Execute all the tools the agent has and return the result.
|
Execute all the tools the agent has and return the result.
|
||||||
"""
|
"""
|
||||||
feedback = ""
|
feedback = ""
|
||||||
success = False
|
success = True
|
||||||
blocks = None
|
blocks = None
|
||||||
if answer.startswith("```"):
|
if answer.startswith("```"):
|
||||||
answer = "I will execute:\n" + answer # there should always be a text before blocks for the function that display answer
|
answer = "I will execute:\n" + answer # there should always be a text before blocks for the function that display answer
|
||||||
|
|
||||||
|
self.success = True
|
||||||
for name, tool in self.tools.items():
|
for name, tool in self.tools.items():
|
||||||
feedback = ""
|
feedback = ""
|
||||||
blocks, save_path = tool.load_exec_block(answer)
|
blocks, save_path = tool.load_exec_block(answer)
|
||||||
@@ -191,6 +216,7 @@ class Agent():
|
|||||||
success = not tool.execution_failure_check(output)
|
success = not tool.execution_failure_check(output)
|
||||||
self.blocks_result.append(executorResult(block, feedback, success, name))
|
self.blocks_result.append(executorResult(block, feedback, success, name))
|
||||||
if not success:
|
if not success:
|
||||||
|
self.success = False
|
||||||
self.memory.push('user', feedback)
|
self.memory.push('user', feedback)
|
||||||
return False, feedback
|
return False, feedback
|
||||||
self.memory.push('user', feedback)
|
self.memory.push('user', feedback)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import time
|
|||||||
from datetime import date
|
from datetime import date
|
||||||
from typing import List, Tuple, Type, Dict
|
from typing import List, Tuple, Type, Dict
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
import asyncio
|
||||||
|
|
||||||
from sources.utility import pretty_print, animate_thinking
|
from sources.utility import pretty_print, animate_thinking
|
||||||
from sources.agents.agent import Agent
|
from sources.agents.agent import Agent
|
||||||
@@ -166,10 +167,10 @@ class BrowserAgent(Agent):
|
|||||||
You must always take notes.
|
You must always take notes.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def llm_decide(self, prompt: str, show_reasoning: bool = False) -> Tuple[str, str]:
|
async def llm_decide(self, prompt: str, show_reasoning: bool = False) -> Tuple[str, str]:
|
||||||
animate_thinking("Thinking...", color="status")
|
animate_thinking("Thinking...", color="status")
|
||||||
self.memory.push('user', prompt)
|
self.memory.push('user', prompt)
|
||||||
answer, reasoning = self.llm_request()
|
answer, reasoning = await self.llm_request()
|
||||||
if show_reasoning:
|
if show_reasoning:
|
||||||
pretty_print(reasoning, color="failure")
|
pretty_print(reasoning, color="failure")
|
||||||
pretty_print(answer, color="output")
|
pretty_print(answer, color="output")
|
||||||
@@ -287,7 +288,7 @@ class BrowserAgent(Agent):
|
|||||||
pretty_print(f"Title: {res['title']} - ", color="info", no_newline=True)
|
pretty_print(f"Title: {res['title']} - ", color="info", no_newline=True)
|
||||||
pretty_print(f"Link: {res['link']}", color="status")
|
pretty_print(f"Link: {res['link']}", color="status")
|
||||||
|
|
||||||
def process(self, user_prompt: str, speech_module: type) -> Tuple[str, str]:
|
async def process(self, user_prompt: str, speech_module: type) -> Tuple[str, str]:
|
||||||
"""
|
"""
|
||||||
Process the user prompt to conduct an autonomous web search.
|
Process the user prompt to conduct an autonomous web search.
|
||||||
Start with a google search with searxng using web_search tool.
|
Start with a google search with searxng using web_search tool.
|
||||||
@@ -302,11 +303,12 @@ class BrowserAgent(Agent):
|
|||||||
|
|
||||||
animate_thinking(f"Thinking...", color="status")
|
animate_thinking(f"Thinking...", color="status")
|
||||||
mem_begin_idx = self.memory.push('user', self.search_prompt(user_prompt))
|
mem_begin_idx = self.memory.push('user', self.search_prompt(user_prompt))
|
||||||
ai_prompt, reasoning = self.llm_request()
|
ai_prompt, reasoning = await self.llm_request()
|
||||||
if Action.REQUEST_EXIT.value in ai_prompt:
|
if Action.REQUEST_EXIT.value in ai_prompt:
|
||||||
pretty_print(f"Web agent requested exit.\n{reasoning}\n\n{ai_prompt}", color="failure")
|
pretty_print(f"Web agent requested exit.\n{reasoning}\n\n{ai_prompt}", color="failure")
|
||||||
return ai_prompt, ""
|
return ai_prompt, ""
|
||||||
animate_thinking(f"Searching...", color="status")
|
animate_thinking(f"Searching...", color="status")
|
||||||
|
self.status_message = "Searching..."
|
||||||
search_result_raw = self.tools["web_search"].execute([ai_prompt], False)
|
search_result_raw = self.tools["web_search"].execute([ai_prompt], False)
|
||||||
search_result = self.jsonify_search_results(search_result_raw)[:16]
|
search_result = self.jsonify_search_results(search_result_raw)[:16]
|
||||||
self.show_search_results(search_result)
|
self.show_search_results(search_result)
|
||||||
@@ -315,16 +317,18 @@ class BrowserAgent(Agent):
|
|||||||
while not complete and len(unvisited) > 0:
|
while not complete and len(unvisited) > 0:
|
||||||
|
|
||||||
self.memory.clear()
|
self.memory.clear()
|
||||||
answer, reasoning = self.llm_decide(prompt, show_reasoning = False)
|
answer, reasoning = await self.llm_decide(prompt, show_reasoning = False)
|
||||||
|
self.last_answer = answer
|
||||||
pretty_print('▂'*32, color="status")
|
pretty_print('▂'*32, color="status")
|
||||||
|
|
||||||
extracted_form = self.extract_form(answer)
|
extracted_form = self.extract_form(answer)
|
||||||
if len(extracted_form) > 0:
|
if len(extracted_form) > 0:
|
||||||
|
self.status_message = "Filling web form..."
|
||||||
pretty_print(f"Filling inputs form...", color="status")
|
pretty_print(f"Filling inputs form...", color="status")
|
||||||
fill_success = self.browser.fill_form(extracted_form)
|
fill_success = self.browser.fill_form(extracted_form)
|
||||||
page_text = self.browser.get_text()
|
page_text = self.browser.get_text()
|
||||||
answer = self.handle_update_prompt(user_prompt, page_text, fill_success)
|
answer = self.handle_update_prompt(user_prompt, page_text, fill_success)
|
||||||
answer, reasoning = self.llm_decide(prompt)
|
answer, reasoning = await self.llm_decide(prompt)
|
||||||
|
|
||||||
if Action.FORM_FILLED.value in answer:
|
if Action.FORM_FILLED.value in answer:
|
||||||
pretty_print(f"Filled form. Handling page update.", color="status")
|
pretty_print(f"Filled form. Handling page update.", color="status")
|
||||||
@@ -337,14 +341,18 @@ class BrowserAgent(Agent):
|
|||||||
link = self.select_link(links)
|
link = self.select_link(links)
|
||||||
|
|
||||||
if Action.REQUEST_EXIT.value in answer:
|
if Action.REQUEST_EXIT.value in answer:
|
||||||
|
self.status_message = "Exiting web browser..."
|
||||||
pretty_print(f"Agent requested exit.", color="status")
|
pretty_print(f"Agent requested exit.", color="status")
|
||||||
complete = True
|
complete = True
|
||||||
break
|
break
|
||||||
|
|
||||||
if (link == None and not len(extracted_form)) 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..."
|
||||||
unvisited = self.select_unvisited(search_result)
|
unvisited = self.select_unvisited(search_result)
|
||||||
prompt = self.make_newsearch_prompt(user_prompt, unvisited)
|
prompt = self.make_newsearch_prompt(user_prompt, unvisited)
|
||||||
|
self.search_history.append(link)
|
||||||
|
self.current_page = link
|
||||||
continue
|
continue
|
||||||
|
|
||||||
animate_thinking(f"Navigating to {link}", color="status")
|
animate_thinking(f"Navigating to {link}", color="status")
|
||||||
@@ -355,13 +363,16 @@ class BrowserAgent(Agent):
|
|||||||
page_text = self.browser.get_text()
|
page_text = self.browser.get_text()
|
||||||
self.navigable_links = self.browser.get_navigable()
|
self.navigable_links = self.browser.get_navigable()
|
||||||
prompt = self.make_navigation_prompt(user_prompt, page_text)
|
prompt = self.make_navigation_prompt(user_prompt, page_text)
|
||||||
|
self.status_message = "Navigating..."
|
||||||
|
self.browser.screenshot()
|
||||||
|
|
||||||
pretty_print("Exited navigation, starting to summarize finding...", color="status")
|
pretty_print("Exited navigation, starting to summarize finding...", color="status")
|
||||||
prompt = self.conclude_prompt(user_prompt)
|
prompt = self.conclude_prompt(user_prompt)
|
||||||
mem_last_idx = self.memory.push('user', prompt)
|
mem_last_idx = self.memory.push('user', prompt)
|
||||||
answer, reasoning = self.llm_request()
|
self.status_message = "Summarizing findings..."
|
||||||
|
answer, reasoning = await self.llm_request()
|
||||||
pretty_print(answer, color="output")
|
pretty_print(answer, color="output")
|
||||||
self.memory.clear_section(mem_begin_idx+1, mem_last_idx-1)
|
self.status_message = "Ready"
|
||||||
return answer, reasoning
|
return answer, reasoning
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import asyncio
|
||||||
|
|
||||||
from sources.utility import pretty_print, animate_thinking
|
from sources.utility import pretty_print, animate_thinking
|
||||||
from sources.agents.agent import Agent
|
from sources.agents.agent import Agent
|
||||||
@@ -17,11 +18,12 @@ class CasualAgent(Agent):
|
|||||||
self.role = "talk"
|
self.role = "talk"
|
||||||
self.type = "casual_agent"
|
self.type = "casual_agent"
|
||||||
|
|
||||||
def process(self, prompt, speech_module) -> str:
|
async def process(self, prompt, speech_module) -> str:
|
||||||
self.memory.push('user', prompt)
|
self.memory.push('user', prompt)
|
||||||
animate_thinking("Thinking...", color="status")
|
animate_thinking("Thinking...", color="status")
|
||||||
answer, reasoning = self.llm_request()
|
answer, reasoning = await self.llm_request()
|
||||||
self.last_answer = answer
|
self.last_answer = answer
|
||||||
|
self.status_message = "Ready"
|
||||||
return answer, reasoning
|
return answer, reasoning
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import platform, os
|
import platform, os
|
||||||
|
import asyncio
|
||||||
|
|
||||||
from sources.utility import pretty_print, animate_thinking
|
from sources.utility import pretty_print, animate_thinking
|
||||||
from sources.agents.agent import Agent, executorResult
|
from sources.agents.agent import Agent, executorResult
|
||||||
@@ -26,7 +27,6 @@ class CoderAgent(Agent):
|
|||||||
self.work_dir = self.tools["file_finder"].get_work_dir()
|
self.work_dir = self.tools["file_finder"].get_work_dir()
|
||||||
self.role = "code"
|
self.role = "code"
|
||||||
self.type = "code_agent"
|
self.type = "code_agent"
|
||||||
|
|
||||||
|
|
||||||
def add_sys_info_prompt(self, prompt):
|
def add_sys_info_prompt(self, prompt):
|
||||||
"""Add system information to the prompt."""
|
"""Add system information to the prompt."""
|
||||||
@@ -36,7 +36,7 @@ class CoderAgent(Agent):
|
|||||||
f"\nYou must save file in work directory: {self.work_dir}"
|
f"\nYou must save file in work directory: {self.work_dir}"
|
||||||
return f"{prompt}\n\n{info}"
|
return f"{prompt}\n\n{info}"
|
||||||
|
|
||||||
def process(self, prompt, speech_module) -> str:
|
async def process(self, prompt, speech_module) -> str:
|
||||||
answer = ""
|
answer = ""
|
||||||
attempt = 0
|
attempt = 0
|
||||||
max_attempts = 4
|
max_attempts = 4
|
||||||
@@ -46,25 +46,30 @@ class CoderAgent(Agent):
|
|||||||
|
|
||||||
while attempt < max_attempts:
|
while attempt < max_attempts:
|
||||||
animate_thinking("Thinking...", color="status")
|
animate_thinking("Thinking...", color="status")
|
||||||
self.wait_message(speech_module)
|
await self.wait_message(speech_module)
|
||||||
answer, reasoning = self.llm_request()
|
answer, reasoning = await self.llm_request()
|
||||||
if clarify_trigger in answer:
|
if clarify_trigger in answer:
|
||||||
|
self.last_answer = answer
|
||||||
|
await asyncio.sleep(0)
|
||||||
return answer, reasoning
|
return answer, reasoning
|
||||||
if not "```" in answer:
|
if not "```" in answer:
|
||||||
self.last_answer = answer
|
self.last_answer = answer
|
||||||
|
await asyncio.sleep(0)
|
||||||
break
|
break
|
||||||
animate_thinking("Executing code...", color="status")
|
animate_thinking("Executing code...", color="status")
|
||||||
|
self.status_message = "Executing code..."
|
||||||
exec_success, _ = self.execute_modules(answer)
|
exec_success, _ = self.execute_modules(answer)
|
||||||
answer = self.remove_blocks(answer)
|
answer = self.remove_blocks(answer)
|
||||||
self.last_answer = answer
|
self.last_answer = answer
|
||||||
if self.get_last_tool_type() == "bash":
|
await asyncio.sleep(0)
|
||||||
continue
|
if exec_success and self.get_last_tool_type() != "bash":
|
||||||
if exec_success:
|
|
||||||
break
|
break
|
||||||
pretty_print("Execution failure", color="failure")
|
pretty_print("Execution failure", color="failure")
|
||||||
pretty_print("Correcting code...", color="status")
|
pretty_print("Correcting code...", color="status")
|
||||||
|
self.status_message = "Correcting code..."
|
||||||
self.show_answer()
|
self.show_answer()
|
||||||
attempt += 1
|
attempt += 1
|
||||||
|
self.status_message = "Ready"
|
||||||
if attempt == max_attempts:
|
if attempt == max_attempts:
|
||||||
return "I'm sorry, I couldn't find a solution to your problem. How would you like me to proceed ?", reasoning
|
return "I'm sorry, I couldn't find a solution to your problem. How would you like me to proceed ?", reasoning
|
||||||
return answer, reasoning
|
return answer, reasoning
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import asyncio
|
||||||
|
|
||||||
from sources.utility import pretty_print, animate_thinking
|
from sources.utility import pretty_print, animate_thinking
|
||||||
from sources.agents.agent import Agent
|
from sources.agents.agent import Agent
|
||||||
@@ -18,17 +19,18 @@ class FileAgent(Agent):
|
|||||||
self.role = "files"
|
self.role = "files"
|
||||||
self.type = "file_agent"
|
self.type = "file_agent"
|
||||||
|
|
||||||
def process(self, prompt, speech_module) -> str:
|
async def process(self, prompt, speech_module) -> str:
|
||||||
exec_success = False
|
exec_success = False
|
||||||
prompt += f"\nYou must work in directory: {self.work_dir}"
|
prompt += f"\nYou must work in directory: {self.work_dir}"
|
||||||
self.memory.push('user', prompt)
|
self.memory.push('user', prompt)
|
||||||
while exec_success is False:
|
while exec_success is False:
|
||||||
self.wait_message(speech_module)
|
await self.wait_message(speech_module)
|
||||||
animate_thinking("Thinking...", color="status")
|
animate_thinking("Thinking...", color="status")
|
||||||
answer, reasoning = self.llm_request()
|
answer, reasoning = await self.llm_request()
|
||||||
exec_success, _ = self.execute_modules(answer)
|
exec_success, _ = self.execute_modules(answer)
|
||||||
answer = self.remove_blocks(answer)
|
answer = self.remove_blocks(answer)
|
||||||
self.last_answer = answer
|
self.last_answer = answer
|
||||||
|
self.status_message = "Ready"
|
||||||
return answer, reasoning
|
return answer, reasoning
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@@ -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")
|
||||||
@@ -87,25 +86,26 @@ class PlannerAgent(Agent):
|
|||||||
pretty_print(f"{task['agent']} -> {task['task']}", color="info")
|
pretty_print(f"{task['agent']} -> {task['task']}", color="info")
|
||||||
pretty_print("▔▗ E N D ▖▔", color="status")
|
pretty_print("▔▗ E N D ▖▔", color="status")
|
||||||
|
|
||||||
def make_plan(self, prompt: str) -> str:
|
async def make_plan(self, prompt: str) -> str:
|
||||||
ok = False
|
ok = False
|
||||||
answer = None
|
answer = None
|
||||||
while not ok:
|
while not ok:
|
||||||
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, _ = await 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:
|
async def start_agent_process(self, task: dict, required_infos: dict | None) -> str:
|
||||||
agent_prompt = self.make_prompt(task['task'], required_infos)
|
agent_prompt = self.make_prompt(task['task'], required_infos)
|
||||||
pretty_print(f"Agent {task['agent']} started working...", color="status")
|
pretty_print(f"Agent {task['agent']} started working...", color="status")
|
||||||
agent_answer, _ = self.agents[task['agent'].lower()].process(agent_prompt, None)
|
agent_answer, _ = await self.agents[task['agent'].lower()].process(agent_prompt, None)
|
||||||
self.agents[task['agent'].lower()].show_answer()
|
self.agents[task['agent'].lower()].show_answer()
|
||||||
pretty_print(f"Agent {task['agent']} completed task.", color="status")
|
pretty_print(f"Agent {task['agent']} completed task.", color="status")
|
||||||
return agent_answer
|
return agent_answer
|
||||||
@@ -113,16 +113,17 @@ class PlannerAgent(Agent):
|
|||||||
def get_work_result_agent(self, task_needs, agents_work_result):
|
def get_work_result_agent(self, task_needs, agents_work_result):
|
||||||
return {k: agents_work_result[k] for k in task_needs if k in agents_work_result}
|
return {k: agents_work_result[k] for k in task_needs if k in agents_work_result}
|
||||||
|
|
||||||
def process(self, prompt: str, speech_module: Speech) -> Tuple[str, str]:
|
async def process(self, prompt: str, speech_module: Speech) -> Tuple[str, str]:
|
||||||
agents_tasks = (None, None)
|
agents_tasks = (None, None)
|
||||||
agents_work_result = dict()
|
agents_work_result = dict()
|
||||||
|
|
||||||
answer = self.make_plan(prompt)
|
answer = await self.make_plan(prompt)
|
||||||
agents_tasks = self.parse_agent_tasks(answer)
|
agents_tasks = self.parse_agent_tasks(answer)
|
||||||
|
|
||||||
if agents_tasks == (None, None):
|
if agents_tasks == (None, None):
|
||||||
return "Failed to parse the tasks.", ""
|
return "Failed to parse the tasks.", ""
|
||||||
for task_name, task in agents_tasks:
|
for task_name, task in agents_tasks:
|
||||||
|
self.status_message = "Starting agent process..."
|
||||||
pretty_print(f"I will {task_name}.", color="info")
|
pretty_print(f"I will {task_name}.", color="info")
|
||||||
pretty_print(f"Assigned agent {task['agent']} to {task_name}", color="info")
|
pretty_print(f"Assigned agent {task['agent']} to {task_name}", color="info")
|
||||||
if speech_module: speech_module.speak(f"I will {task_name}. I assigned the {task['agent']} agent to the task.")
|
if speech_module: speech_module.speak(f"I will {task_name}. I assigned the {task['agent']} agent to the task.")
|
||||||
@@ -130,7 +131,7 @@ class PlannerAgent(Agent):
|
|||||||
if agents_work_result is not None:
|
if agents_work_result is not None:
|
||||||
required_infos = self.get_work_result_agent(task['need'], agents_work_result)
|
required_infos = self.get_work_result_agent(task['need'], agents_work_result)
|
||||||
try:
|
try:
|
||||||
self.last_answer = self.start_agent_process(task, required_infos)
|
self.last_answer = await self.start_agent_process(task, required_infos)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise e
|
raise e
|
||||||
agents_work_result[task['id']] = self.last_answer
|
agents_work_result[task['id']] = self.last_answer
|
||||||
|
|||||||
@@ -39,14 +39,24 @@ 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", "/usr/bin/chromium-browser", "/usr/bin/chromium"]
|
paths = ["/usr/bin/google-chrome", "/usr/bin/chromium-browser", "/usr/bin/chromium", "/opt/chrome/chrome", "/usr/local/bin/chrome"]
|
||||||
|
|
||||||
for path in paths:
|
for path in paths:
|
||||||
if os.path.exists(path) and os.access(path, os.X_OK): # Check if executable
|
if os.path.exists(path) and os.access(path, os.X_OK): # Check if executable
|
||||||
return path
|
return path
|
||||||
|
print("Looking for Google Chrome in these locations failed:")
|
||||||
|
print('\n'.join(paths))
|
||||||
|
chrome_path_env = os.environ.get("CHROME_EXECUTABLE_PATH")
|
||||||
|
if chrome_path_env and os.path.exists(chrome_path_env) and os.access(chrome_path_env, os.X_OK):
|
||||||
|
return chrome_path_env
|
||||||
|
path = input("Google Chrome not found. Please enter the path to the Chrome executable: ")
|
||||||
|
if os.path.exists(path) and os.access(path, os.X_OK):
|
||||||
|
os.environ["CHROME_EXECUTABLE_PATH"] = path
|
||||||
|
print(f"Chrome path saved to environment variable CHROME_EXECUTABLE_PATH")
|
||||||
|
return path
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def create_driver(headless=False, stealth_mode=True) -> webdriver.Chrome:
|
def create_driver(headless=False, stealth_mode=True, crx_path="./crx/nopecha.crx") -> webdriver.Chrome:
|
||||||
"""Create a Chrome WebDriver with specified options."""
|
"""Create a Chrome WebDriver with specified options."""
|
||||||
chrome_options = Options()
|
chrome_options = Options()
|
||||||
chrome_path = get_chrome_path()
|
chrome_path = get_chrome_path()
|
||||||
@@ -74,10 +84,10 @@ def create_driver(headless=False, stealth_mode=True) -> webdriver.Chrome:
|
|||||||
chrome_options.add_argument(f'--window-size={width},{height}')
|
chrome_options.add_argument(f'--window-size={width},{height}')
|
||||||
if not stealth_mode:
|
if not stealth_mode:
|
||||||
# crx file can't be installed in stealth mode
|
# crx file can't be installed in stealth mode
|
||||||
crx_path = "./crx/nopecha.crx"
|
|
||||||
if not os.path.exists(crx_path):
|
if not os.path.exists(crx_path):
|
||||||
raise FileNotFoundError(f"Extension file not found at: {crx_path}")
|
pretty_print(f"Anti-captcha CRX not found at {crx_path}.", color="failure")
|
||||||
chrome_options.add_extension(crx_path)
|
else:
|
||||||
|
chrome_options.add_extension(crx_path)
|
||||||
|
|
||||||
chromedriver_path = shutil.which("chromedriver")
|
chromedriver_path = shutil.which("chromedriver")
|
||||||
if not chromedriver_path:
|
if not chromedriver_path:
|
||||||
@@ -123,14 +133,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.driver.get("https://www.google.com")
|
self.setup_tabs()
|
||||||
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")
|
||||||
@@ -142,11 +163,10 @@ class Browser:
|
|||||||
try:
|
try:
|
||||||
initial_handles = self.driver.window_handles
|
initial_handles = self.driver.window_handles
|
||||||
self.driver.get(url)
|
self.driver.get(url)
|
||||||
wait = WebDriverWait(self.driver, timeout=30)
|
wait = WebDriverWait(self.driver, timeout=10)
|
||||||
wait.until(
|
wait.until(
|
||||||
lambda driver: (
|
lambda driver: (
|
||||||
driver.execute_script("return document.readyState") == "complete" and
|
not any(keyword in driver.page_source.lower() for keyword in ["checking your browser", "captcha"])
|
||||||
not any(keyword in driver.page_source.lower() for keyword in ["checking your browser", "verifying", "captcha"])
|
|
||||||
),
|
),
|
||||||
message="stuck on 'checking browser' or verification screen"
|
message="stuck on 'checking browser' or verification screen"
|
||||||
)
|
)
|
||||||
@@ -198,6 +218,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)}")
|
||||||
@@ -223,9 +245,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
|
||||||
@@ -270,6 +294,7 @@ class Browser:
|
|||||||
self.driver.execute_script("arguments[0].scrollIntoView({block: 'center', behavior: 'smooth'});", element)
|
self.driver.execute_script("arguments[0].scrollIntoView({block: 'center', behavior: 'smooth'});", element)
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
element.click()
|
element.click()
|
||||||
|
self.logger.info(f"Clicked element at {xpath}")
|
||||||
return True
|
return True
|
||||||
except ElementClickInterceptedException as e:
|
except ElementClickInterceptedException as e:
|
||||||
self.logger.error(f"Error click_element: {str(e)}")
|
self.logger.error(f"Error click_element: {str(e)}")
|
||||||
@@ -355,6 +380,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
|
||||||
@@ -382,8 +408,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}")
|
||||||
@@ -419,9 +447,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
|
||||||
@@ -528,19 +556,28 @@ 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);"
|
||||||
)
|
)
|
||||||
time.sleep(1)
|
time.sleep(0.5)
|
||||||
return True
|
return True
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.error(f"Error scrolling: {str(e)}")
|
self.logger.error(f"Error scrolling: {str(e)}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def get_screenshot(self) -> str:
|
||||||
|
return self.screenshot_folder + "/updated_screen.png"
|
||||||
|
|
||||||
def screenshot(self, filename:str) -> 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...")
|
||||||
|
time.sleep(0.1)
|
||||||
try:
|
try:
|
||||||
self.driver.save_screenshot(filename)
|
path = os.path.join(self.screenshot_folder, filename)
|
||||||
|
if not os.path.exists(self.screenshot_folder):
|
||||||
|
os.makedirs(self.screenshot_folder)
|
||||||
|
self.driver.save_screenshot(path)
|
||||||
self.logger.info(f"Screenshot saved as {filename}")
|
self.logger.info(f"Screenshot saved as {filename}")
|
||||||
return True
|
return True
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -551,19 +588,18 @@ 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()
|
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")
|
|
||||||
#txt = browser.get_text()
|
|
||||||
#print(txt)
|
|
||||||
#browser.go_to("https://practicetestautomation.com/practice-test-login/")
|
|
||||||
input("press enter to continue")
|
input("press enter to continue")
|
||||||
print("AntiCaptcha / Form Test")
|
print("AntiCaptcha / Form Test")
|
||||||
|
#browser.go_to("https://practicetestautomation.com/practice-test-login/")
|
||||||
|
#txt = browser.get_text()
|
||||||
#browser.go_to("https://www.google.com/recaptcha/api2/demo")
|
#browser.go_to("https://www.google.com/recaptcha/api2/demo")
|
||||||
browser.go_to("https://home.openweathermap.org/users/sign_up")
|
browser.go_to("https://home.openweathermap.org/users/sign_up")
|
||||||
inputs_visible = browser.get_form_inputs()
|
inputs_visible = browser.get_form_inputs()
|
||||||
|
|||||||
@@ -21,25 +21,36 @@ class Interaction:
|
|||||||
self.current_agent = None
|
self.current_agent = None
|
||||||
self.last_query = None
|
self.last_query = None
|
||||||
self.last_answer = None
|
self.last_answer = None
|
||||||
self.speech = None
|
|
||||||
self.agents = agents
|
self.agents = agents
|
||||||
self.tts_enabled = tts_enabled
|
self.tts_enabled = tts_enabled
|
||||||
self.stt_enabled = stt_enabled
|
self.stt_enabled = stt_enabled
|
||||||
self.recover_last_session = recover_last_session
|
self.recover_last_session = recover_last_session
|
||||||
self.router = AgentRouter(self.agents, supported_language=langs)
|
self.router = AgentRouter(self.agents, supported_language=langs)
|
||||||
if tts_enabled:
|
|
||||||
animate_thinking("Initializing text-to-speech...", color="status")
|
|
||||||
self.speech = Speech(enable=tts_enabled)
|
|
||||||
self.ai_name = self.find_ai_name()
|
self.ai_name = self.find_ai_name()
|
||||||
|
self.speech = None
|
||||||
self.transcriber = None
|
self.transcriber = None
|
||||||
self.recorder = None
|
self.recorder = None
|
||||||
|
self.is_generating = False
|
||||||
|
if tts_enabled:
|
||||||
|
self.initialize_tts()
|
||||||
if stt_enabled:
|
if stt_enabled:
|
||||||
animate_thinking("Initializing speech recognition...", color="status")
|
self.initialize_stt()
|
||||||
self.transcriber = AudioTranscriber(self.ai_name, verbose=False)
|
|
||||||
self.recorder = AudioRecorder()
|
|
||||||
if recover_last_session:
|
if recover_last_session:
|
||||||
self.load_last_session()
|
self.load_last_session()
|
||||||
self.emit_status()
|
self.emit_status()
|
||||||
|
|
||||||
|
def initialize_tts(self):
|
||||||
|
"""Initialize TTS."""
|
||||||
|
if not self.speech:
|
||||||
|
animate_thinking("Initializing text-to-speech...", color="status")
|
||||||
|
self.speech = Speech(enable=self.tts_enabled)
|
||||||
|
|
||||||
|
def initialize_stt(self):
|
||||||
|
"""Initialize STT."""
|
||||||
|
if not self.transcriber or not self.recorder:
|
||||||
|
animate_thinking("Initializing speech recognition...", color="status")
|
||||||
|
self.transcriber = AudioTranscriber(self.ai_name, verbose=False)
|
||||||
|
self.recorder = AudioRecorder()
|
||||||
|
|
||||||
def emit_status(self):
|
def emit_status(self):
|
||||||
"""Print the current status of agenticSeek."""
|
"""Print the current status of agenticSeek."""
|
||||||
@@ -113,7 +124,7 @@ class Interaction:
|
|||||||
self.last_query = query
|
self.last_query = query
|
||||||
return query
|
return query
|
||||||
|
|
||||||
def think(self) -> bool:
|
async def think(self) -> bool:
|
||||||
"""Request AI agents to process the user input."""
|
"""Request AI agents to process the user input."""
|
||||||
push_last_agent_memory = False
|
push_last_agent_memory = False
|
||||||
if self.last_query is None or len(self.last_query) == 0:
|
if self.last_query is None or len(self.last_query) == 0:
|
||||||
@@ -125,7 +136,9 @@ class Interaction:
|
|||||||
push_last_agent_memory = True
|
push_last_agent_memory = True
|
||||||
tmp = self.last_answer
|
tmp = self.last_answer
|
||||||
self.current_agent = agent
|
self.current_agent = agent
|
||||||
self.last_answer, _ = agent.process(self.last_query, self.speech)
|
self.is_generating = True
|
||||||
|
self.last_answer, _ = await agent.process(self.last_query, self.speech)
|
||||||
|
self.is_generating = False
|
||||||
if push_last_agent_memory:
|
if push_last_agent_memory:
|
||||||
self.current_agent.memory.push('user', self.last_query)
|
self.current_agent.memory.push('user', self.last_query)
|
||||||
self.current_agent.memory.push('assistant', self.last_answer)
|
self.current_agent.memory.push('assistant', self.last_answer)
|
||||||
@@ -133,6 +146,18 @@ class Interaction:
|
|||||||
self.last_answer = None
|
self.last_answer = None
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def get_updated_process_answer(self) -> str:
|
||||||
|
"""Get the answer from the last agent."""
|
||||||
|
if self.current_agent is None:
|
||||||
|
return None
|
||||||
|
return self.current_agent.get_last_answer()
|
||||||
|
|
||||||
|
def get_updated_block_answer(self) -> str:
|
||||||
|
"""Get the answer from the last agent."""
|
||||||
|
if self.current_agent is None:
|
||||||
|
return None
|
||||||
|
return self.current_agent.get_last_block_answer()
|
||||||
|
|
||||||
def show_answer(self) -> None:
|
def show_answer(self) -> None:
|
||||||
"""Show the answer to the user."""
|
"""Show the answer to the user."""
|
||||||
if self.last_query is None:
|
if self.last_query is None:
|
||||||
|
|||||||
@@ -7,7 +7,9 @@ import requests
|
|||||||
import subprocess
|
import subprocess
|
||||||
import ipaddress
|
import ipaddress
|
||||||
import httpx
|
import httpx
|
||||||
|
import socket
|
||||||
import platform
|
import platform
|
||||||
|
from urllib.parse import urlparse
|
||||||
from dotenv import load_dotenv, set_key
|
from dotenv import load_dotenv, set_key
|
||||||
from openai import OpenAI
|
from openai import OpenAI
|
||||||
from huggingface_hub import InferenceClient
|
from huggingface_hub import InferenceClient
|
||||||
@@ -34,7 +36,7 @@ class Provider:
|
|||||||
}
|
}
|
||||||
self.logger = Logger("provider.log")
|
self.logger = Logger("provider.log")
|
||||||
self.api_key = None
|
self.api_key = None
|
||||||
self.unsafe_providers = ["openai", "deepseek", "dsk_deepseek"]
|
self.unsafe_providers = ["openai", "deepseek", "dsk_deepseek", "together"]
|
||||||
if self.provider_name not in self.available_providers:
|
if self.provider_name not in self.available_providers:
|
||||||
raise ValueError(f"Unknown provider: {provider_name}")
|
raise ValueError(f"Unknown provider: {provider_name}")
|
||||||
if self.provider_name in self.unsafe_providers:
|
if self.provider_name in self.unsafe_providers:
|
||||||
@@ -51,10 +53,8 @@ class Provider:
|
|||||||
api_key_var = f"{provider.upper()}_API_KEY"
|
api_key_var = f"{provider.upper()}_API_KEY"
|
||||||
api_key = os.getenv(api_key_var)
|
api_key = os.getenv(api_key_var)
|
||||||
if not api_key:
|
if not api_key:
|
||||||
api_key = input(f"Please enter your {provider} API key: ")
|
pretty_print(f"API key {api_key_var} not found in .env file. Please add it", color="warning")
|
||||||
set_key(".env", api_key_var, api_key)
|
exit(1)
|
||||||
self.logger.info("Set API key in env.")
|
|
||||||
load_dotenv()
|
|
||||||
return api_key
|
return api_key
|
||||||
|
|
||||||
def check_address_format(self, address):
|
def check_address_format(self, address):
|
||||||
@@ -87,31 +87,34 @@ class Provider:
|
|||||||
raise ConnectionError(f"{str(e)}\nConnection to {self.server_ip} failed.")
|
raise ConnectionError(f"{str(e)}\nConnection to {self.server_ip} failed.")
|
||||||
except AttributeError as e:
|
except AttributeError as e:
|
||||||
raise NotImplementedError(f"{str(e)}\nIs {self.provider_name} implemented ?")
|
raise NotImplementedError(f"{str(e)}\nIs {self.provider_name} implemented ?")
|
||||||
|
except ModuleNotFoundError as e:
|
||||||
|
raise ModuleNotFoundError(f"{str(e)}\nA import related to provider {self.provider_name} was not found. Is it installed ?")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if "refused" in str(e):
|
if "refused" in str(e):
|
||||||
return f"Server {self.server_ip} seem offline. Unable to answer."
|
return f"Server {self.server_ip} seem offline. Unable to answer."
|
||||||
raise Exception(f"Provider {self.provider_name} failed: {str(e)}") from e
|
raise Exception(f"Provider {self.provider_name} failed: {str(e)}") from e
|
||||||
return thought
|
return thought
|
||||||
|
|
||||||
def is_ip_online(self, ip_address):
|
def is_ip_online(self, address: str, timeout: int = 10) -> bool:
|
||||||
"""
|
"""
|
||||||
Check if an IP address is online by sending a ping request.
|
Check if an address is online by sending a ping request.
|
||||||
"""
|
"""
|
||||||
if ip_address == "127.0.0.1":
|
if not address:
|
||||||
|
return False
|
||||||
|
if address.lower() in ["127.0.0.1", "localhost", "0.0.0.0"]:
|
||||||
return True
|
return True
|
||||||
|
hostname = urlparse(f'http://{address}' if not address.startswith(('http://', 'https://')) else address).hostname or address
|
||||||
|
try:
|
||||||
|
ip_address = socket.gethostbyname(hostname)
|
||||||
|
except socket.gaierror:
|
||||||
|
self.logger.error(f"Cannot resolve: {hostname}")
|
||||||
|
return False
|
||||||
param = '-n' if platform.system().lower() == 'windows' else '-c'
|
param = '-n' if platform.system().lower() == 'windows' else '-c'
|
||||||
command = ['ping', param, '1', ip_address]
|
command = ['ping', param, '1', ip_address]
|
||||||
try:
|
try:
|
||||||
output = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=15)
|
result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=timeout)
|
||||||
if output.returncode == 0:
|
return result.returncode == 0
|
||||||
return True
|
except (subprocess.TimeoutExpired, subprocess.SubprocessError) as e:
|
||||||
else:
|
|
||||||
self.logger.error(f"Ping command returned code: {output.returncode}")
|
|
||||||
return False
|
|
||||||
except subprocess.TimeoutExpired:
|
|
||||||
return False
|
|
||||||
except Exception as e:
|
|
||||||
pretty_print(f"Error with ping request {str(e)}", color="failure")
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def server_fn(self, history, verbose = False):
|
def server_fn(self, history, verbose = False):
|
||||||
|
|||||||
@@ -130,9 +130,7 @@ class Memory():
|
|||||||
self.logger.info(f"Clearing memory section {start} to {end}.")
|
self.logger.info(f"Clearing memory section {start} to {end}.")
|
||||||
start = max(0, start) + 1
|
start = max(0, start) + 1
|
||||||
end = min(end, len(self.memory)-1) + 2
|
end = min(end, len(self.memory)-1) + 2
|
||||||
self.logger.info(f"Memory before: {self.memory}")
|
|
||||||
self.memory = self.memory[:start] + self.memory[end:]
|
self.memory = self.memory[:start] + self.memory[end:]
|
||||||
self.logger.info(f"Memory after: {self.memory}")
|
|
||||||
|
|
||||||
def get(self) -> list:
|
def get(self) -> list:
|
||||||
return self.memory
|
return self.memory
|
||||||
|
|||||||
@@ -362,6 +362,8 @@ class AgentRouter:
|
|||||||
Returns:
|
Returns:
|
||||||
str: The selected label
|
str: The selected label
|
||||||
"""
|
"""
|
||||||
|
if len(text) <= 8:
|
||||||
|
return "talk"
|
||||||
result_bart = self.pipelines['bart'](text, labels)
|
result_bart = self.pipelines['bart'](text, labels)
|
||||||
result_llm_router = self.llm_router(text)
|
result_llm_router = self.llm_router(text)
|
||||||
bart, confidence_bart = result_bart['labels'][0], result_bart['scores'][0]
|
bart, confidence_bart = result_bart['labels'][0], result_bart['scores'][0]
|
||||||
@@ -402,8 +404,6 @@ class AgentRouter:
|
|||||||
if confidence < 0.4:
|
if confidence < 0.4:
|
||||||
self.logger.info(f"Low confidence in complexity estimation: {confidence}")
|
self.logger.info(f"Low confidence in complexity estimation: {confidence}")
|
||||||
return "LOW"
|
return "LOW"
|
||||||
if complexity == "HIGH" and len(text) < 64:
|
|
||||||
return None # ask for more info
|
|
||||||
if complexity == "HIGH":
|
if complexity == "HIGH":
|
||||||
return "HIGH"
|
return "HIGH"
|
||||||
elif complexity == "LOW":
|
elif complexity == "LOW":
|
||||||
@@ -440,11 +440,6 @@ class AgentRouter:
|
|||||||
text = self.lang_analysis.translate(text, lang)
|
text = self.lang_analysis.translate(text, lang)
|
||||||
labels = [agent.role for agent in self.agents]
|
labels = [agent.role for agent in self.agents]
|
||||||
complexity = self.estimate_complexity(text)
|
complexity = self.estimate_complexity(text)
|
||||||
if complexity == None and self.asked_clarify == False:
|
|
||||||
self.asked_clarify = True
|
|
||||||
pretty_print(f"Humm, the task seem complex but you gave very little information. can you clarify?", color="info")
|
|
||||||
return None
|
|
||||||
self.asked_clarify = False
|
|
||||||
if complexity == "HIGH":
|
if complexity == "HIGH":
|
||||||
pretty_print(f"Complex task detected, routing to planner agent.", color="info")
|
pretty_print(f"Complex task detected, routing to planner agent.", color="info")
|
||||||
return self.find_planner_agent()
|
return self.find_planner_agent()
|
||||||
|
|||||||
@@ -0,0 +1,74 @@
|
|||||||
|
|
||||||
|
from typing import Tuple, Callable
|
||||||
|
from pydantic import BaseModel
|
||||||
|
from sources.utility import pretty_print
|
||||||
|
|
||||||
|
class QueryRequest(BaseModel):
|
||||||
|
query: str
|
||||||
|
tts_enabled: bool = True
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"Query: {self.query}, Language: {self.lang}, TTS: {self.tts_enabled}, STT: {self.stt_enabled}"
|
||||||
|
|
||||||
|
def jsonify(self):
|
||||||
|
return {
|
||||||
|
"query": self.query,
|
||||||
|
"tts_enabled": self.tts_enabled,
|
||||||
|
}
|
||||||
|
|
||||||
|
class QueryResponse(BaseModel):
|
||||||
|
done: str
|
||||||
|
answer: str
|
||||||
|
agent_name: str
|
||||||
|
success: str
|
||||||
|
blocks: dict
|
||||||
|
status: str
|
||||||
|
timestamp: str
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"Done: {self.done}, Answer: {self.answer}, Agent Name: {self.agent_name}, Success: {self.success}, Blocks: {self.blocks}"
|
||||||
|
|
||||||
|
def jsonify(self):
|
||||||
|
return {
|
||||||
|
"done": self.done,
|
||||||
|
"answer": self.answer,
|
||||||
|
"agent_name": self.agent_name,
|
||||||
|
"success": self.success,
|
||||||
|
"blocks": self.blocks
|
||||||
|
}
|
||||||
|
|
||||||
|
class executorResult:
|
||||||
|
"""
|
||||||
|
A class to store the result of a tool execution.
|
||||||
|
"""
|
||||||
|
def __init__(self, block: str, feedback: str, success: bool, tool_type: str):
|
||||||
|
"""
|
||||||
|
Initialize an agent with execution results.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
block: The content or code block processed by the agent.
|
||||||
|
feedback: Feedback or response information from the execution.
|
||||||
|
success: Boolean indicating whether the agent's execution was successful.
|
||||||
|
tool_type: The type of tool used by the agent for execution.
|
||||||
|
"""
|
||||||
|
self.block = block
|
||||||
|
self.feedback = feedback
|
||||||
|
self.success = success
|
||||||
|
self.tool_type = tool_type
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"Tool: {self.tool_type}\nBlock: {self.block}\nFeedback: {self.feedback}\nSuccess: {self.success}"
|
||||||
|
|
||||||
|
def jsonify(self):
|
||||||
|
return {
|
||||||
|
"block": self.block,
|
||||||
|
"feedback": self.feedback,
|
||||||
|
"success": self.success,
|
||||||
|
"tool_type": self.tool_type
|
||||||
|
}
|
||||||
|
|
||||||
|
def show(self):
|
||||||
|
pretty_print('▂'*64, color="status")
|
||||||
|
pretty_print(self.block, color="code" if self.success else "failure")
|
||||||
|
pretty_print('▂'*64, color="status")
|
||||||
|
pretty_print(self.feedback, color="success" if self.success else "failure")
|
||||||
@@ -30,18 +30,27 @@ class PyInterpreter(Tools):
|
|||||||
'__builtins__': __builtins__,
|
'__builtins__': __builtins__,
|
||||||
'os': os,
|
'os': os,
|
||||||
'sys': sys,
|
'sys': sys,
|
||||||
|
'__name__': '__main__'
|
||||||
}
|
}
|
||||||
code = '\n\n'.join(codes)
|
code = '\n\n'.join(codes)
|
||||||
|
self.logger.info(f"Executing code:\n{code}")
|
||||||
try:
|
try:
|
||||||
try:
|
try:
|
||||||
buffer = exec(code, global_vars)
|
buffer = exec(code, global_vars)
|
||||||
|
self.logger.info(f"Code executed successfully.\noutput:{buffer}")
|
||||||
print(buffer)
|
print(buffer)
|
||||||
if buffer is not None:
|
if buffer is not None:
|
||||||
output = buffer + '\n'
|
output = buffer + '\n'
|
||||||
|
except SystemExit:
|
||||||
|
self.logger.info("SystemExit caught, code execution stopped.")
|
||||||
|
output = stdout_buffer.getvalue()
|
||||||
|
return f"[SystemExit caught] Output before exit:\n{output}"
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
self.logger.error(f"Code execution failed: {str(e)}")
|
||||||
return "code execution failed:" + str(e)
|
return "code execution failed:" + str(e)
|
||||||
output = stdout_buffer.getvalue()
|
output = stdout_buffer.getvalue()
|
||||||
finally:
|
finally:
|
||||||
|
self.logger.info("Code execution finished.")
|
||||||
sys.stdout = sys.__stdout__
|
sys.stdout = sys.__stdout__
|
||||||
return output
|
return output
|
||||||
|
|
||||||
@@ -74,7 +83,9 @@ class PyInterpreter(Tools):
|
|||||||
]
|
]
|
||||||
combined_pattern = "|".join(error_patterns)
|
combined_pattern = "|".join(error_patterns)
|
||||||
if re.search(combined_pattern, feedback, re.IGNORECASE):
|
if re.search(combined_pattern, feedback, re.IGNORECASE):
|
||||||
|
self.logger.error(f"Execution failure detected: {feedback}")
|
||||||
return True
|
return True
|
||||||
|
self.logger.info("No execution success detected.")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
@@ -93,6 +104,9 @@ here is a save test
|
|||||||
def print_hello():
|
def print_hello():
|
||||||
hello = "Hello World"
|
hello = "Hello World"
|
||||||
print(hello)
|
print(hello)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
print_hello()
|
||||||
```
|
```
|
||||||
"""
|
"""
|
||||||
py = PyInterpreter()
|
py = PyInterpreter()
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ class FlightSearch(Tools):
|
|||||||
"""
|
"""
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.tag = "flight_search"
|
self.tag = "flight_search"
|
||||||
|
self.api_key = None
|
||||||
self.api_key = api_key or os.getenv("AVIATIONSTACK_API_KEY")
|
self.api_key = api_key or os.getenv("AVIATIONSTACK_API_KEY")
|
||||||
|
|
||||||
def execute(self, blocks: str, safety: bool = True) -> str:
|
def execute(self, blocks: str, safety: bool = True) -> str:
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import sys
|
|||||||
import os
|
import os
|
||||||
import configparser
|
import configparser
|
||||||
from abc import abstractmethod
|
from abc import abstractmethod
|
||||||
|
from sources.logger import Logger
|
||||||
|
|
||||||
sys.path.append('..')
|
sys.path.append('..')
|
||||||
|
|
||||||
@@ -30,9 +31,9 @@ class Tools():
|
|||||||
"""
|
"""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.tag = "undefined"
|
self.tag = "undefined"
|
||||||
self.api_key = None
|
|
||||||
self.client = None
|
self.client = None
|
||||||
self.messages = []
|
self.messages = []
|
||||||
|
self.logger = Logger("tools.log")
|
||||||
self.config = configparser.ConfigParser()
|
self.config = configparser.ConfigParser()
|
||||||
self.work_dir = self.create_work_dir()
|
self.work_dir = self.create_work_dir()
|
||||||
self.excutable_blocks_found = False
|
self.excutable_blocks_found = False
|
||||||
@@ -118,10 +119,12 @@ class Tools():
|
|||||||
"""
|
"""
|
||||||
if save_path is None:
|
if save_path is None:
|
||||||
return
|
return
|
||||||
|
self.logger.info(f"Saving blocks to {save_path}")
|
||||||
save_path_dir = os.path.dirname(save_path)
|
save_path_dir = os.path.dirname(save_path)
|
||||||
save_path_file = os.path.basename(save_path)
|
save_path_file = os.path.basename(save_path)
|
||||||
directory = os.path.join(self.work_dir, save_path_dir)
|
directory = os.path.join(self.work_dir, save_path_dir)
|
||||||
if directory and not os.path.exists(directory):
|
if directory and not os.path.exists(directory):
|
||||||
|
self.logger.info(f"Creating directory {directory}")
|
||||||
os.makedirs(directory)
|
os.makedirs(directory)
|
||||||
for block in blocks:
|
for block in blocks:
|
||||||
with open(os.path.join(directory, save_path_file), 'w') as f:
|
with open(os.path.join(directory, save_path_file), 'w') as f:
|
||||||
@@ -199,6 +202,7 @@ class Tools():
|
|||||||
self.excutable_blocks_found = True
|
self.excutable_blocks_found = True
|
||||||
code_blocks.append(content)
|
code_blocks.append(content)
|
||||||
start_index = end_pos + len(end_tag)
|
start_index = end_pos + len(end_tag)
|
||||||
|
self.logger.info(f"Found {len(code_blocks)} blocks to execute")
|
||||||
return code_blocks, save_path
|
return code_blocks, save_path
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@@ -3,5 +3,11 @@
|
|||||||
REM Up the provider in windows
|
REM Up the provider in windows
|
||||||
start ollama serve
|
start ollama serve
|
||||||
|
|
||||||
REM Up Docker
|
docker-compose up
|
||||||
cd searxng && docker compose up
|
if %ERRORLEVEL% neq 0 (
|
||||||
|
echo Error: Failed to start containers. Check Docker logs with 'docker compose logs'.
|
||||||
|
echo Possible fixes: Ensure Docker Desktop is running or check if port 8080 is free.
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
timeout /t 10 /nobreak >nul
|
||||||
@@ -1,4 +1,68 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
# start searxng service for internet search
|
command_exists() {
|
||||||
cd searxng && ./setup_searxng.sh
|
command -v "$1" &> /dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# Check if Docker is installed é running
|
||||||
|
#
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
# Check if docker-compose.yml exists
|
||||||
|
if [ ! -f "docker-compose.yml" ]; then
|
||||||
|
echo "Error: docker-compose.yml not found in the current directory."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# start docker compose for searxng, redis, frontend services
|
||||||
|
echo "Warning: stopping all docker containers (t-4 seconds)..."
|
||||||
|
sleep 4
|
||||||
|
docker stop $(docker ps -a -q)
|
||||||
|
echo "All containers stopped"
|
||||||
|
|
||||||
|
if ! docker-compose up; 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
|
||||||