Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a4a98fa601 | ||
|
|
0eb42e4a42 | ||
|
|
4c0a72ff61 | ||
|
|
021644d786 | ||
|
|
725ceeea20 | ||
|
|
69bbd31abf | ||
|
|
2944ff6892 | ||
|
|
6fd329acc6 | ||
|
|
05aa5c2104 | ||
|
|
a976afdbd7 | ||
|
|
29aafda41d | ||
|
|
37c9652e6d | ||
|
|
f8fc43f6d7 | ||
|
|
3fc2ac5235 | ||
|
|
31205febfe | ||
|
|
1fbd8b135b | ||
|
|
7b8a2271a3 | ||
|
|
4e985d8df6 | ||
|
|
74d38100c4 | ||
|
|
9e4613df71 | ||
|
|
2b8bc83a97 | ||
|
|
872d01f8e5 | ||
|
|
d1ecb15703 | ||
|
|
7cde1549b2 | ||
|
|
1432266694 | ||
|
|
1036c5fbc1 | ||
|
|
77ed25306e | ||
|
|
d47761a6a5 | ||
|
|
e135683426 | ||
|
|
53a47be80c | ||
|
|
75ce967107 | ||
|
|
9a8ce59520 | ||
|
|
a97010739b | ||
|
|
7066f62942 | ||
|
|
fc268ed087 | ||
|
|
781275c881 | ||
|
|
2a69fbef4b | ||
|
|
fa4cef0e74 | ||
|
|
f63b2c436d | ||
|
|
cb6210293a | ||
|
|
165dcb8d97 | ||
|
|
1e827a903b | ||
|
|
16f7f7c917 | ||
|
|
f350378170 | ||
|
|
9611bf4081 | ||
|
|
aeb9145d6e | ||
|
|
889545f356 | ||
|
|
782a933971 | ||
|
|
42cd21346f | ||
|
|
abc765861a | ||
|
|
141bc5f097 | ||
|
|
8a713e5ec0 | ||
|
|
176863be55 | ||
|
|
0decd84541 | ||
|
|
edc0e69a82 | ||
|
|
60dd8a597f | ||
|
|
28d255b8e9 | ||
|
|
4554c6ef89 | ||
|
|
fd8d940e58 | ||
|
|
87c8a3fb09 | ||
|
|
be86fd07e1 | ||
|
|
d80bda8468 | ||
|
|
308ba0e595 | ||
|
|
f72c1b9b07 | ||
|
|
deaf142a0d | ||
|
|
e0123bfc67 | ||
|
|
fa26cce44f | ||
|
|
cb0206e9ee | ||
|
|
a2f75e7281 | ||
|
|
3e652a6b99 | ||
|
|
fd987e5b5c | ||
|
|
d30f3c5616 | ||
|
|
db62bb289e | ||
|
|
23caf437b1 | ||
|
|
f5b1c4ab37 | ||
|
|
29044ebbfe | ||
|
|
50d098fa96 | ||
|
|
ba74f1efa2 | ||
|
|
b3ec49c611 | ||
|
|
2a5fb69af6 | ||
|
|
2eb62e63eb | ||
|
|
30aecb406d | ||
|
|
860e52c3fa | ||
|
|
c7ffe46771 | ||
|
|
12a6705724 | ||
|
|
0605fb4526 | ||
|
|
100dda4370 | ||
|
|
b226c5d1da | ||
|
|
bf740d8eb5 | ||
|
|
f8f987e8ba | ||
|
|
29ddf31f82 | ||
|
|
41d83e8c46 | ||
|
|
611eddb715 | ||
|
|
40b94c953b | ||
|
|
ae7be9ca8d | ||
|
|
7439ea35c7 | ||
|
|
3550e6ee46 | ||
|
|
69dd856b45 | ||
|
|
e33e3e15e4 | ||
|
|
6bb0e5a16b | ||
|
|
c388f6c871 | ||
|
|
d214f3a9c7 | ||
|
|
8888f3c481 | ||
|
|
807358937e | ||
|
|
eebf3f3ff3 | ||
|
|
5a06b21a67 | ||
|
|
fd18953e14 | ||
|
|
c305e025b8 | ||
|
|
5d2d1c7b08 | ||
|
|
a6cf09673b | ||
|
|
d485a32e06 | ||
|
|
939b241a45 | ||
|
|
01a27c10ad | ||
|
|
24fea22540 | ||
|
|
1730172aaa | ||
|
|
93acf4dfaf | ||
|
|
aa4c95a3f4 | ||
|
|
2b3946d685 | ||
|
|
a8e0f42939 | ||
|
|
7844d71108 | ||
|
|
7e6b08bc5d | ||
|
|
c74fb8a005 | ||
|
|
43acef2378 | ||
|
|
4a0843f0aa | ||
|
|
583b80707d | ||
|
|
8bde81b0b5 | ||
|
|
48a94d7042 | ||
|
|
b4a692047d | ||
|
|
95a0edc2b6 | ||
|
|
f271a5b205 | ||
|
|
0cec4126d8 | ||
|
|
946f3a8993 | ||
|
|
c09dee0329 | ||
|
|
0a1a3eaf77 | ||
|
|
361c53fb4d | ||
|
|
d04fa90391 | ||
|
|
743541955d | ||
|
|
80fea7f40d | ||
|
|
5aa08f7e88 | ||
|
|
4cf5761603 | ||
|
|
e8e7a6ee3d | ||
|
|
e869379751 | ||
|
|
42bb65e8f6 | ||
|
|
517b4a79e0 | ||
|
|
665e886e31 | ||
|
|
a58b1cf9f8 | ||
|
|
81760bfd9c | ||
|
|
e952ac3e61 | ||
|
|
e0fa4e6356 | ||
|
|
da6056e94c | ||
|
|
5542914725 | ||
|
|
12af55f6c4 | ||
|
|
2f8d2d4954 | ||
|
|
dc6a35bcf9 | ||
|
|
a7c38819d6 | ||
|
|
c81c0ffde6 | ||
|
|
444e7bce22 | ||
|
|
d3f20819ff | ||
|
|
9f0fdd547e | ||
|
|
be1bfc5cf2 | ||
|
|
a3b0bb22aa | ||
|
|
fc74d4361a | ||
|
|
54cc2a03ec | ||
|
|
eff5921217 | ||
|
|
2ed59939a0 | ||
|
|
7c9d963db5 | ||
|
|
3ab570e760 | ||
|
|
e235aecbc1 | ||
|
|
67e074165c | ||
|
|
e7a4f41101 | ||
|
|
12eb3d5a02 | ||
|
|
34fbaa1f3c | ||
|
|
d59e5d7eb8 | ||
|
|
b96e83dbbe | ||
|
|
eadcfb66d1 | ||
|
|
1c4a550c6f | ||
|
|
95aeaf74fa | ||
|
|
ec1f7d31fb | ||
|
|
abae98cf77 | ||
|
|
819a3fb98d | ||
|
|
7d74a348c9 | ||
|
|
a3ad635728 | ||
|
|
58656ab43c | ||
|
|
f3da9f2965 | ||
|
|
58f46d4351 | ||
|
|
36de7eb389 | ||
|
|
50f9e11a35 | ||
|
|
500605d5da | ||
|
|
6ec9647d19 | ||
|
|
16b8f1a451 |
@@ -3,10 +3,9 @@ __pycache__/
|
|||||||
*.py[cod]
|
*.py[cod]
|
||||||
|
|
||||||
# Virtual environments
|
# Virtual environments
|
||||||
venv/
|
agentic_seek_env/
|
||||||
.venv/
|
.agentic_seek_env/
|
||||||
|
|
||||||
# Environment variables (secrets)
|
|
||||||
.env
|
.env
|
||||||
|
|
||||||
# Git metadata
|
# Git metadata
|
||||||
|
|||||||
@@ -1,4 +1,20 @@
|
|||||||
SEARXNG_BASE_URL="http://127.0.0.1:8080"
|
SEARXNG_BASE_URL="http://searxng:8080"
|
||||||
|
REDIS_BASE_URL="redis://redis:6379/0"
|
||||||
|
WORK_DIR="/Users/username/Documents/workspace_with_my_files"
|
||||||
|
OLLAMA_PORT="11434"
|
||||||
|
LM_STUDIO_PORT="1234"
|
||||||
|
BACKEND_PORT="7777"
|
||||||
|
# Set this to your server's public IP/hostname when accessing the UI from a remote machine.
|
||||||
|
# Example: REACT_APP_BACKEND_URL=http://192.168.1.100:7777
|
||||||
|
REACT_APP_BACKEND_URL=http://localhost:7777
|
||||||
|
CUSTOM_ADDITIONAL_LLM_PORT="11435"
|
||||||
OPENAI_API_KEY='xxxxx'
|
OPENAI_API_KEY='xxxxx'
|
||||||
DEEPSEEK_API_KEY='xxxxx'
|
DEEPSEEK_API_KEY='xxxxx'
|
||||||
OPENROUTER_API_KEY='xxxxx'
|
OPENROUTER_API_KEY='xxxxx'
|
||||||
|
TOGETHER_API_KEY='xxxxx'
|
||||||
|
GOOGLE_API_KEY='xxxxx'
|
||||||
|
ANTHROPIC_API_KEY='xxxxx'
|
||||||
|
MINIMAX_API_KEY='xxxxx'
|
||||||
|
# Optional: MiniMax API base URL (default: https://api.minimax.io/v1)
|
||||||
|
# For mainland China users: https://api.minimaxi.com/v1
|
||||||
|
# MINIMAX_BASE_URL='https://api.minimax.io/v1'
|
||||||
@@ -23,16 +23,13 @@ A clear and concise description of what you expected to happen.
|
|||||||
**Screenshots**
|
**Screenshots**
|
||||||
If applicable, add screenshots to help explain your problem.
|
If applicable, add screenshots to help explain your problem.
|
||||||
|
|
||||||
|
**LLM Model used**
|
||||||
|
The model you used, for example deepseek-r1:14b
|
||||||
|
|
||||||
**Desktop (please complete the following information):**
|
**Desktop (please complete the following information):**
|
||||||
- OS: [e.g. iOS]
|
- OS: [e.g. iOS]
|
||||||
- Browser [e.g. chrome, safari]
|
- Browser [e.g. chrome, safari]
|
||||||
- Version [e.g. 22]
|
- Version [e.g. 22]
|
||||||
|
|
||||||
**Smartphone (please complete the following information):**
|
|
||||||
- Device: [e.g. iPhone6]
|
|
||||||
- OS: [e.g. iOS8.1]
|
|
||||||
- Browser [e.g. stock browser, safari]
|
|
||||||
- Version [e.g. 22]
|
|
||||||
|
|
||||||
**Additional context**
|
**Additional context**
|
||||||
Add any other context about the problem here.
|
Add any other context about the problem here.
|
||||||
|
|||||||
@@ -6,6 +6,8 @@
|
|||||||
*.egg-info
|
*.egg-info
|
||||||
cookies.json
|
cookies.json
|
||||||
test_agent.py
|
test_agent.py
|
||||||
|
searxng/uwsgi.ini.new
|
||||||
|
searxng/settings.yml.new
|
||||||
config.ini
|
config.ini
|
||||||
.voices/
|
.voices/
|
||||||
experimental/
|
experimental/
|
||||||
@@ -19,6 +21,7 @@ agentic_seek_env/*
|
|||||||
.env
|
.env
|
||||||
*/.env
|
*/.env
|
||||||
dsk/
|
dsk/
|
||||||
|
chrome136/
|
||||||
|
|
||||||
### react ###
|
### react ###
|
||||||
.DS_*
|
.DS_*
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
3.10
|
||||||
@@ -1,10 +1,31 @@
|
|||||||
FROM ubuntu:22.04
|
|
||||||
# Warning: doesn't work yet, backend is run on host machine for now
|
|
||||||
|
|
||||||
WORKDIR /app
|
FROM --platform=linux/amd64 python:3.11.12
|
||||||
|
ENV DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
RUN apt-get update -qq -y && \
|
# Install essential packages and Chrome dependencies
|
||||||
apt-get install -y \
|
RUN apt-get update -y && apt-get install -y \
|
||||||
|
wget \
|
||||||
|
gnupg2 \
|
||||||
|
ca-certificates \
|
||||||
|
unzip \
|
||||||
|
xvfb \
|
||||||
|
libxss1 \
|
||||||
|
#libappindicator1 \
|
||||||
|
fonts-liberation \
|
||||||
|
libnss3 \
|
||||||
|
libatk1.0-0 \
|
||||||
|
libatk-bridge2.0-0 \
|
||||||
|
libcups2 \
|
||||||
|
libdrm2 \
|
||||||
|
libxcomposite1 \
|
||||||
|
libxdamage1 \
|
||||||
|
libxrandr2 \
|
||||||
|
xdg-utils \
|
||||||
|
dbus \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
RUN apt-get update -y && \
|
||||||
|
apt-get install -y \
|
||||||
gcc \
|
gcc \
|
||||||
g++ \
|
g++ \
|
||||||
gfortran \
|
gfortran \
|
||||||
@@ -24,23 +45,58 @@ apt-get install -y \
|
|||||||
libgtk-4-1 \
|
libgtk-4-1 \
|
||||||
libnss3 \
|
libnss3 \
|
||||||
xdg-utils \
|
xdg-utils \
|
||||||
wget && \
|
wget \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
|
||||||
|
RUN apt-get update -y && \
|
||||||
|
apt-get install -y \
|
||||||
|
alsa-utils \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
ENV CHROME_TESTING_VERSION=134.0.6998.88
|
||||||
|
ENV DISPLAY=:99
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
RUN set -eux; \
|
||||||
|
wget -qO /tmp/chrome.zip \
|
||||||
|
"https://storage.googleapis.com/chrome-for-testing-public/${CHROME_TESTING_VERSION}/linux64/chrome-linux64.zip"; \
|
||||||
|
unzip -q /tmp/chrome.zip -d /opt; \
|
||||||
|
rm /tmp/chrome.zip; \
|
||||||
|
ln -s /opt/chrome-linux64/chrome /usr/local/bin/google-chrome; \
|
||||||
|
ln -s /opt/chrome-linux64/chrome /usr/local/bin/chrome; \
|
||||||
|
mkdir -p /opt/chrome; \
|
||||||
|
ln -s /opt/chrome-linux64/chrome /opt/chrome/chrome; \
|
||||||
|
google-chrome --version
|
||||||
|
|
||||||
|
RUN set -eux; \
|
||||||
|
wget -qO /tmp/chromedriver.zip \
|
||||||
|
"https://storage.googleapis.com/chrome-for-testing-public/${CHROME_TESTING_VERSION}/linux64/chromedriver-linux64.zip"; \
|
||||||
|
unzip -q /tmp/chromedriver.zip -d /tmp; \
|
||||||
|
mv /tmp/chromedriver-linux64/chromedriver /usr/local/bin; \
|
||||||
|
rm /tmp/chromedriver.zip; \
|
||||||
|
chmod +x /usr/local/bin/chromedriver; \
|
||||||
|
chromedriver --version
|
||||||
|
|
||||||
RUN chmod +x /opt/chrome/chrome
|
RUN chmod +x /opt/chrome/chrome
|
||||||
# Install dependencies
|
|
||||||
|
RUN pip3 install --upgrade pip setuptools wheel
|
||||||
|
|
||||||
COPY requirements.txt .
|
COPY requirements.txt .
|
||||||
RUN pip install --no-cache-dir -r requirements.txt
|
RUN pip install --no-cache-dir -r requirements.txt
|
||||||
|
|
||||||
|
RUN mkdir -p /opt/workspace
|
||||||
|
RUN mkdir -p /tmp && chmod 1777 /tmp
|
||||||
|
|
||||||
# Copy application code
|
# Copy application code
|
||||||
COPY api.py .
|
COPY api.py .
|
||||||
COPY sources/ ./sources/
|
COPY sources/ ./sources/
|
||||||
COPY prompts/ ./prompts/
|
COPY prompts/ ./prompts/
|
||||||
COPY crx/ crx/
|
COPY crx/ crx/
|
||||||
COPY llm_router/ llm_router/
|
COPY llm_router/ llm_router/
|
||||||
COPY .env .
|
|
||||||
COPY config.ini .
|
COPY config.ini .
|
||||||
|
|
||||||
# Expose port
|
|
||||||
EXPOSE 8000
|
EXPOSE 8000
|
||||||
|
|
||||||
# Run the application
|
# Run the application
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<img align="center" src="./media/agentic_seek_logo.png" width="300" height="300" alt="Agentic Seek Logo">
|
<img align="center" src="./media/agentic_seek_logo.png" width="300" height="300" alt="Agentic Seek Logo">
|
||||||
<p>
|
<p>
|
||||||
|
|
||||||
English | [中文](./README_CHS.md) | [繁體中文](./README_CHT.md) | [Français](./README_FR.md) | [日本語](./README_JP.md)
|
English | [中文](./README_CHS.md) | [繁體中文](./README_CHT.md) | [Français](./README_FR.md) | [日本語](./README_JP.md) | [Português (Brasil)](./README_PTBR.md) | [Español](./README_ES.md) | [Türkçe](./README_TR.md)
|
||||||
|
|
||||||
*A **100% local alternative to Manus AI**, this voice-enabled AI assistant autonomously browses the web, writes code, and plans tasks while keeping all data on your device. Tailored for local reasoning models, it runs entirely on your hardware, ensuring complete privacy and zero cloud dependency.*
|
*A **100% local alternative to Manus AI**, this voice-enabled AI assistant autonomously browses the web, writes code, and plans tasks while keeping all data on your device. Tailored for local reasoning models, it runs entirely on your hardware, ensuring complete privacy and zero cloud dependency.*
|
||||||
|
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
* 📋 Plans & Executes Complex Tasks - From trip planning to complex projects — it can split big tasks into steps and get things done using multiple AI agents.
|
* 📋 Plans & Executes Complex Tasks - From trip planning to complex projects — it can split big tasks into steps and get things done using multiple AI agents.
|
||||||
|
|
||||||
* 🎙️ Voice-Enabled - Clean, fast, futuristic voice and speech to text allowing you to talk to it like it's your personal AI from a sci-fi movie
|
* 🎙️ Voice-Enabled - Clean, fast, futuristic voice and speech to text allowing you to talk to it like it's your personal AI from a sci-fi movie. (In progress)
|
||||||
|
|
||||||
### **Demo**
|
### **Demo**
|
||||||
|
|
||||||
@@ -32,19 +32,21 @@ https://github.com/user-attachments/assets/b8ca60e9-7b3b-4533-840e-08f9ac426316
|
|||||||
|
|
||||||
Disclaimer: This demo, including all the files that appear (e.g: CV_candidates.zip), are entirely fictional. We are not a corporation, we seek open-source contributors not candidates.
|
Disclaimer: This demo, including all the files that appear (e.g: CV_candidates.zip), are entirely fictional. We are not a corporation, we seek open-source contributors not candidates.
|
||||||
|
|
||||||
> 🛠⚠️️ **Active Work in Progress** – Please note that Code/Bash is not dockerized yet but will be soon (see docker_deployement branch) - Do not deploy over network or production.
|
> 🛠⚠️️ **Active Work in Progress**
|
||||||
|
|
||||||
> 🙏 This project started as a side-project with zero roadmap and zero funding. It's grown way beyond what I expected by ending in GitHub Trending. Contributions, feedback, and patience are deeply appreciated.
|
> 🙏 This project started as a side-project and has zero roadmap and zero funding. It's grown way beyond what I expected by ending in GitHub Trending. Contributions, feedback, and patience are deeply appreciated.
|
||||||
|
|
||||||
## Installation
|
## Prerequisites
|
||||||
|
|
||||||
Make sure you have chrome driver, docker and python3.10 installed.
|
Before you begin, ensure you have the following software installed:
|
||||||
|
|
||||||
We highly advice you use exactly python3.10 for the setup. Dependencies error might happen otherwise.
|
* **Git:** For cloning the repository. [Download Git](https://git-scm.com/downloads)
|
||||||
|
* **Python 3.10.x:** We strongly recommend using Python version 3.10.x. Using other versions might lead to dependency errors. [Download Python 3.10](https://www.python.org/downloads/release/python-3100/) (pick a 3.10.x version).
|
||||||
|
* **Docker Engine & Docker Compose:** For running bundled services like SearxNG.
|
||||||
|
* Install Docker Desktop (which includes Docker Compose V2): [Windows](https://docs.docker.com/desktop/install/windows-install/) | [Mac](https://docs.docker.com/desktop/install/mac-install/) | [Linux](https://docs.docker.com/desktop/install/linux-install/)
|
||||||
|
* Alternatively, install Docker Engine and Docker Compose separately on Linux: [Docker Engine](https://docs.docker.com/engine/install/) | [Docker Compose](https://docs.docker.com/compose/install/) (ensure you install Compose V2, e.g., `sudo apt-get install docker-compose-plugin`).
|
||||||
|
|
||||||
For issues related to chrome driver, see the **Chromedriver** section.
|
### 1. **Clone the repository and setup**
|
||||||
|
|
||||||
### 1️⃣ **Clone the repository and setup**
|
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
git clone https://github.com/Fosowl/agenticSeek.git
|
git clone https://github.com/Fosowl/agenticSeek.git
|
||||||
@@ -52,73 +54,62 @@ cd agenticSeek
|
|||||||
mv .env.example .env
|
mv .env.example .env
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2️ **Create a virtual env**
|
### 2. Change the .env file content
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
python3 -m venv agentic_seek_env
|
SEARXNG_BASE_URL="http://searxng:8080" # http://127.0.0.1:8080 if running on host
|
||||||
source agentic_seek_env/bin/activate
|
REDIS_BASE_URL="redis://redis:6379/0"
|
||||||
# On Windows: agentic_seek_env\Scripts\activate
|
WORK_DIR="/Users/mlg/Documents/workspace_for_ai"
|
||||||
|
OLLAMA_PORT="11434"
|
||||||
|
LM_STUDIO_PORT="1234"
|
||||||
|
CUSTOM_ADDITIONAL_LLM_PORT="11435"
|
||||||
|
OPENAI_API_KEY='optional'
|
||||||
|
DEEPSEEK_API_KEY='optional'
|
||||||
|
OPENROUTER_API_KEY='optional'
|
||||||
|
TOGETHER_API_KEY='optional'
|
||||||
|
GOOGLE_API_KEY='optional'
|
||||||
|
ANTHROPIC_API_KEY='optional'
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3️⃣ **Install package**
|
|
||||||
|
|
||||||
Ensure Python, Docker and docker compose, and Google chrome are installed.
|
Update the `.env` file with your own values as needed:
|
||||||
|
|
||||||
We recommand Python 3.10.0.
|
- **SEARXNG_BASE_URL**: Leave unchanged unless running on host with CLI mode.
|
||||||
|
- **REDIS_BASE_URL**: Leave unchanged
|
||||||
|
- **WORK_DIR**: Path to your working directory on your local machine. AgenticSeek will be able to read and interact with these files.
|
||||||
|
- **OLLAMA_PORT**: Port number for the Ollama service.
|
||||||
|
- **LM_STUDIO_PORT**: Port number for the LM Studio service.
|
||||||
|
- **CUSTOM_ADDITIONAL_LLM_PORT**: Port for any additional custom LLM service.
|
||||||
|
|
||||||
**Automatic Installation (Recommanded):**
|
**API Key are totally optional for user who choose to run LLM locally. Which is the primary purpose of this project. Leave empty if you have sufficient hardware**
|
||||||
|
|
||||||
For Linux/Macos:
|
### 3. **Start Docker**
|
||||||
|
|
||||||
|
Make sure Docker is installed and running on your system. You can start Docker using the following commands:
|
||||||
|
|
||||||
|
- **On Linux/macOS:**
|
||||||
|
Open a terminal and run:
|
||||||
|
```sh
|
||||||
|
sudo systemctl start docker
|
||||||
|
```
|
||||||
|
Or launch Docker Desktop from your applications menu if installed.
|
||||||
|
|
||||||
|
- **On Windows:**
|
||||||
|
Start Docker Desktop from the Start menu.
|
||||||
|
|
||||||
|
You can verify Docker is running by executing:
|
||||||
```sh
|
```sh
|
||||||
./install.sh
|
docker info
|
||||||
```
|
```
|
||||||
|
If you see information about your Docker installation, it is running correctly.
|
||||||
|
|
||||||
For windows:
|
See the table of [Local Providers](#list-of-local-providers) below for a summary.
|
||||||
|
|
||||||
```sh
|
Next step: [Run AgenticSeek locally](#start-services-and-run)
|
||||||
./install.bat
|
|
||||||
```
|
|
||||||
|
|
||||||
**Manually:**
|
*See the [Troubleshooting](#troubleshooting) section if you are having issues.*
|
||||||
|
*If your hardware can't run LLMs locally, see [Setup to run with an API](#setup-to-run-with-an-api).*
|
||||||
**Note: For any OS, ensure the ChromeDriver you install matches your installed Chrome version. Run `google-chrome --version`. See known issues if you have chrome >135**
|
*For detailed `config.ini` explanations, see [Config Section](#config).*
|
||||||
|
|
||||||
- *Linux*:
|
|
||||||
|
|
||||||
Update Package List: `sudo apt update`
|
|
||||||
|
|
||||||
Install Dependencies: `sudo apt install -y alsa-utils portaudio19-dev python3-pyaudio libgtk-3-dev libnotify-dev libgconf-2-4 libnss3 libxss1`
|
|
||||||
|
|
||||||
Install ChromeDriver matching your Chrome browser version:
|
|
||||||
`sudo apt install -y chromium-chromedriver`
|
|
||||||
|
|
||||||
Install requirements: `pip3 install -r requirements.txt`
|
|
||||||
|
|
||||||
- *Macos*:
|
|
||||||
|
|
||||||
Update brew : `brew update`
|
|
||||||
|
|
||||||
Install chromedriver : `brew install --cask chromedriver`
|
|
||||||
|
|
||||||
Install portaudio: `brew install portaudio`
|
|
||||||
|
|
||||||
Upgrade pip : `python3 -m pip install --upgrade pip`
|
|
||||||
|
|
||||||
Upgrade wheel : : `pip3 install --upgrade setuptools wheel`
|
|
||||||
|
|
||||||
Install requirements: `pip3 install -r requirements.txt`
|
|
||||||
|
|
||||||
- *Windows*:
|
|
||||||
|
|
||||||
Install pyreadline3 `pip install pyreadline3`
|
|
||||||
|
|
||||||
Install portaudio manually (e.g., via vcpkg or prebuilt binaries) and then run: `pip install pyaudio`
|
|
||||||
|
|
||||||
Download and install chromedriver manually from: https://sites.google.com/chromium.org/driver/getting-started
|
|
||||||
|
|
||||||
Place chromedriver in a directory included in your PATH.
|
|
||||||
|
|
||||||
Install requirements: `pip3 install -r requirements.txt`
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -126,11 +117,19 @@ Install requirements: `pip3 install -r requirements.txt`
|
|||||||
|
|
||||||
**Hardware Requirements:**
|
**Hardware Requirements:**
|
||||||
|
|
||||||
To run LLMs locally, you'll need sufficient hardware. At a minimum, a GPU capable of running Qwen/Deepseek 14B is required. See the FAQ for detailed model/performance recommendations.
|
To run LLMs locally, you'll need sufficient hardware. At a minimum, a GPU capable of running Magistral, Qwen or Deepseek 14B is required. See the FAQ for detailed model/performance recommendations.
|
||||||
|
|
||||||
**Setup your local provider**
|
**Setup your local provider**
|
||||||
|
|
||||||
Start your local provider, for example with ollama:
|
Start your local provider (for example with ollama):
|
||||||
|
|
||||||
|
Unless you wish to to run AgenticSeek on host (CLI mode), export or set the provider listen address:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
export OLLAMA_HOST=0.0.0.0:11434
|
||||||
|
```
|
||||||
|
|
||||||
|
Then, start you provider:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
ollama serve
|
ollama serve
|
||||||
@@ -140,7 +139,7 @@ See below for a list of local supported provider.
|
|||||||
|
|
||||||
**Update the config.ini**
|
**Update the config.ini**
|
||||||
|
|
||||||
Change the config.ini file to set the provider_name to a supported provider and provider_model to a LLM supported by your provider. We recommand reasoning model such as *Qwen* or *Deepseek*.
|
Change the config.ini file to set the provider_name to a supported provider and provider_model to a LLM supported by your provider. We recommend reasoning model such as *Magistral* or *Deepseek*.
|
||||||
|
|
||||||
See the **FAQ** at the end of the README for required hardware.
|
See the **FAQ** at the end of the README for required hardware.
|
||||||
|
|
||||||
@@ -153,19 +152,23 @@ provider_server_address = 127.0.0.1:11434
|
|||||||
agent_name = Jarvis # name of your AI
|
agent_name = Jarvis # name of your AI
|
||||||
recover_last_session = True # whenever to recover the previous session
|
recover_last_session = True # whenever to recover the previous session
|
||||||
save_session = True # whenever to remember the current session
|
save_session = True # whenever to remember the current session
|
||||||
speak = True # text to speech
|
speak = False # text to speech
|
||||||
listen = False # Speech to text, only for CLI
|
listen = False # Speech to text, only for CLI, experimental
|
||||||
work_dir = /Users/mlg/Documents/workspace # The workspace for AgenticSeek.
|
|
||||||
jarvis_personality = False # Whenever to use a more "Jarvis" like personality (experimental)
|
jarvis_personality = False # Whenever to use a more "Jarvis" like personality (experimental)
|
||||||
languages = en zh # The list of languages, Text to speech will default to the first language on the list
|
languages = en zh # The list of languages, Text to speech will default to the first language on the list
|
||||||
[BROWSER]
|
[BROWSER]
|
||||||
headless_browser = True # Whenever to use headless browser, recommanded only if you use web interface.
|
headless_browser = True # leave unchanged unless using CLI on host.
|
||||||
stealth_mode = True # Use undetected selenium to reduce browser detection
|
stealth_mode = True # Use undetected selenium to reduce browser detection
|
||||||
```
|
```
|
||||||
|
|
||||||
Warning: Do *NOT* set provider_name to `openai` if using LM-studio for running LLMs. Set it to `lm-studio`.
|
**Warning**:
|
||||||
|
|
||||||
Note: Some provider (eg: lm-studio) require you to have `http://` in front of the IP. For example `http://127.0.0.1:1234`
|
- The `config.ini` file format does not support comments.
|
||||||
|
Do not copy and paste the example configuration directly, as comments will cause errors. Instead, manually modify the `config.ini` file with your desired settings, excluding any comments.
|
||||||
|
|
||||||
|
- Do *NOT* set provider_name to `openai` if using LM-studio for running LLMs. Set it to `lm-studio`.
|
||||||
|
|
||||||
|
- Some provider (eg: lm-studio) require you to have `http://` in front of the IP. For example `http://127.0.0.1:1234`
|
||||||
|
|
||||||
**List of local providers**
|
**List of local providers**
|
||||||
|
|
||||||
@@ -177,93 +180,139 @@ Note: Some provider (eg: lm-studio) require you to have `http://` in front of th
|
|||||||
|
|
||||||
Next step: [Start services and run AgenticSeek](#Start-services-and-Run)
|
Next step: [Start services and run AgenticSeek](#Start-services-and-Run)
|
||||||
|
|
||||||
*See the **Known issues** section if you are having issues*
|
*See the [Troubleshooting](#troubleshooting) section if you are having issues.*
|
||||||
|
*If your hardware can't run LLMs locally, see [Setup to run with an API](#setup-to-run-with-an-api).*
|
||||||
*See the **Run with an API** section if your hardware can't run deepseek locally*
|
*For detailed `config.ini` explanations, see [Config Section](#config).*
|
||||||
|
|
||||||
*See the **Config** section for detailled config file explanation.*
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Setup to run with an API
|
## Setup to run with an API
|
||||||
|
|
||||||
Set the desired provider in the `config.ini`. See below for a list of API providers.
|
This setup uses external, cloud-based LLM providers. You'll need an API key from your chosen service.
|
||||||
|
|
||||||
```sh
|
**1. Choose an API Provider and Get an API Key:**
|
||||||
|
|
||||||
|
Refer to the [List of API Providers](#list-of-api-providers) below. Visit their websites to sign up and obtain an API key.
|
||||||
|
|
||||||
|
**2. Set Your API Key as an Environment Variable:**
|
||||||
|
|
||||||
|
|
||||||
|
* **Linux/macOS:**
|
||||||
|
Open your terminal and use the `export` command. It's best to add this to your shell's profile file (e.g., `~/.bashrc`, `~/.zshrc`) for persistence.
|
||||||
|
```sh
|
||||||
|
export PROVIDER_API_KEY="your_api_key_here"
|
||||||
|
# Replace PROVIDER_API_KEY with the specific variable name, e.g., OPENAI_API_KEY, GOOGLE_API_KEY
|
||||||
|
```
|
||||||
|
Example for TogetherAI:
|
||||||
|
```sh
|
||||||
|
export TOGETHER_API_KEY="xxxxxxxxxxxxxxxxxxxxxx"
|
||||||
|
```
|
||||||
|
* **Windows:**
|
||||||
|
* **Command Prompt (Temporary for current session):**
|
||||||
|
```cmd
|
||||||
|
set PROVIDER_API_KEY=your_api_key_here
|
||||||
|
```
|
||||||
|
* **PowerShell (Temporary for current session):**
|
||||||
|
```powershell
|
||||||
|
$env:PROVIDER_API_KEY="your_api_key_here"
|
||||||
|
```
|
||||||
|
* **Permanently:** Search for "environment variables" in the Windows search bar, click "Edit the system environment variables," then click the "Environment Variables..." button. Add a new User variable with the appropriate name (e.g., `OPENAI_API_KEY`) and your key as the value.
|
||||||
|
|
||||||
|
*(See FAQ: [How do I set API keys?](#how-do-i-set-api-keys) for more details).*
|
||||||
|
|
||||||
|
|
||||||
|
**3. Update `config.ini`:**
|
||||||
|
```ini
|
||||||
[MAIN]
|
[MAIN]
|
||||||
is_local = False
|
is_local = False
|
||||||
provider_name = google
|
provider_name = openai # Or google, deepseek, togetherAI, huggingface
|
||||||
provider_model = gemini-2.0-flash
|
provider_model = gpt-3.5-turbo # Or gemini-1.5-flash, deepseek-chat, mistralai/Mixtral-8x7B-Instruct-v0.1 etc.
|
||||||
provider_server_address = 127.0.0.1:5000 # doesn't matter
|
provider_server_address = # Typically ignored or can be left blank when is_local = False for most APIs
|
||||||
|
# ... other settings ...
|
||||||
```
|
```
|
||||||
Warning: Make sure there is not trailing space in the config.
|
*Warning:* Make sure there are no trailing spaces in the `config.ini` values.
|
||||||
|
|
||||||
Export your API key: `export <<PROVIDER>>_API_KEY="xxx"`
|
**List of API Providers**
|
||||||
|
|
||||||
Example: export `TOGETHER_API_KEY="xxxxx"`
|
| Provider | `provider_name` | Local? | Description | API Key Link (Examples) |
|
||||||
|
|--------------|-----------------|--------|---------------------------------------------------|---------------------------------------------|
|
||||||
|
| OpenAI | `openai` | No | Use ChatGPT models via OpenAI's API. | [platform.openai.com/signup](https://platform.openai.com/signup) |
|
||||||
|
| Google Gemini| `google` | No | Use Google Gemini models via Google AI Studio. | [aistudio.google.com/keys](https://aistudio.google.com/keys) |
|
||||||
|
| Deepseek | `deepseek` | No | Use Deepseek models via their API. | [platform.deepseek.com](https://platform.deepseek.com) |
|
||||||
|
| Hugging Face | `huggingface` | No | Use models from Hugging Face Inference API. | [huggingface.co/settings/tokens](https://huggingface.co/settings/tokens) |
|
||||||
|
| TogetherAI | `togetherAI` | No | Use various open-source models via TogetherAI API.| [api.together.ai/settings/api-keys](https://api.together.ai/settings/api-keys) |
|
||||||
|
| OpenRouter | `openrouter` | No | Use OpenRouter Models| [https://openrouter.ai/](https://openrouter.ai/) |
|
||||||
|
| MiniMax | `minimax` | No | Use MiniMax models (e.g., MiniMax-M2.7, MiniMax-M2.5).| [platform.minimax.io](https://platform.minimax.io/user-center/basic-information) |
|
||||||
|
|
||||||
**List of API providers**
|
*Note:*
|
||||||
|
* We advise against using `gpt-4o` or other OpenAI models for complex web browsing and task planning as current prompt optimizations are geared towards models like Deepseek.
|
||||||
| Provider | Local? | Description |
|
* Coding/bash tasks might encounter issues with Gemini, as it may not strictly follow formatting prompts optimized for Deepseek.
|
||||||
|-----------|--------|-----------------------------------------------------------|
|
* The `provider_server_address` in `config.ini` is generally not used when `is_local = False` as the API endpoint is usually hardcoded in the respective provider's library.
|
||||||
| openai | Depends | Use ChatGPT API |
|
|
||||||
| deepseek | No | Deepseek API (non-private) |
|
|
||||||
| huggingface| No | Hugging-Face API (non-private) |
|
|
||||||
| togetherAI | No | Use together AI API (non-private) |
|
|
||||||
| google | No | Use google gemini API (non-private) |
|
|
||||||
|
|
||||||
*We advice against using gpt-4o or other closedAI models*, performance are poor for web browsing and task planning.
|
|
||||||
|
|
||||||
Please also note that coding/bash might fail with gemini, it seem to ignore our prompt for format to respect, which are optimized for deepseek r1.
|
|
||||||
|
|
||||||
Next step: [Start services and run AgenticSeek](#Start-services-and-Run)
|
Next step: [Start services and run AgenticSeek](#Start-services-and-Run)
|
||||||
|
|
||||||
*See the **Known issues** section if you are having issues*
|
*See the **Known issues** section if you are having issues*
|
||||||
|
|
||||||
*See the **Config** section for detailled config file explanation.*
|
*See the **Config** section for detailed config file explanation.*
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Start services and Run
|
## Start services and Run
|
||||||
|
|
||||||
Activate your python env if needed.
|
By default AgenticSeek is run fully in docker.
|
||||||
```sh
|
|
||||||
source agentic_seek_env/bin/activate
|
**Option 1:** Run in Docker, use web interface:
|
||||||
```
|
|
||||||
|
|
||||||
Start required services. This will start all services from the docker-compose.yml, including:
|
Start required services. This will start all services from the docker-compose.yml, including:
|
||||||
- searxng
|
- searxng
|
||||||
- redis (required by searxng)
|
- redis (required by searxng)
|
||||||
- frontend
|
- frontend
|
||||||
|
- backend (if using `full` when using the web interface)
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
sudo ./start_services.sh # MacOS
|
./start_services.sh full # MacOS
|
||||||
start ./start_services.cmd # Window
|
start start_services.cmd full # Window
|
||||||
```
|
```
|
||||||
|
|
||||||
**Options 1:** Run with the CLI interface.
|
**Warning:** This step will download and load all Docker images, which may take up to 30 minutes. After starting the services, please wait until the backend service is fully running (you should see **backend: "GET /health HTTP/1.1" 200 OK** in the log) before sending any messages. The backend services might take 5 minute to start on first run.
|
||||||
|
|
||||||
```sh
|
|
||||||
python3 cli.py
|
|
||||||
```
|
|
||||||
|
|
||||||
We advice you set `headless_browser` to False in the config.ini for CLI mode.
|
|
||||||
|
|
||||||
**Options 2:** Run with the Web interface.
|
|
||||||
|
|
||||||
Start the backend.
|
|
||||||
|
|
||||||
```sh
|
|
||||||
python3 api.py
|
|
||||||
```
|
|
||||||
|
|
||||||
Go to `http://localhost:3000/` and you should see the web interface.
|
Go to `http://localhost:3000/` and you should see the web interface.
|
||||||
|
|
||||||
|
*Troubleshooting service start:* If these scripts fail, ensure Docker Engine is running and Docker Compose (V2, `docker compose`) is correctly installed. Check the output in the terminal for error messages. See [FAQ: Help! I get an error when running AgenticSeek or its scripts.](#faq-troubleshooting)
|
||||||
|
|
||||||
|
**Option 2:** CLI mode:
|
||||||
|
|
||||||
|
To run with CLI interface you would have to install package on host:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
./install.sh
|
||||||
|
./install.bat # windows
|
||||||
|
```
|
||||||
|
|
||||||
|
Then you must change the SEARXNG_BASE_URL in `config.ini` to:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
SEARXNG_BASE_URL="http://localhost:8080"
|
||||||
|
```
|
||||||
|
|
||||||
|
Start required services. This will start some services from the docker-compose.yml, including:
|
||||||
|
- searxng
|
||||||
|
- redis (required by searxng)
|
||||||
|
- frontend
|
||||||
|
|
||||||
|
```sh
|
||||||
|
./start_services.sh # MacOS
|
||||||
|
start start_services.cmd # Window
|
||||||
|
```
|
||||||
|
|
||||||
|
Run: uv run: `uv run python -m ensurepip` to ensure uv has pip enabled.
|
||||||
|
|
||||||
|
Use the CLI: `uv run cli.py`
|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
Make sure the services are up and running with `./start_services.sh` and run the AgenticSeek with `python3 cli.py` for CLI mode or `python3 api.py` then go to `localhost:3000` for web interface.
|
Make sure the services are up and running with `./start_services.sh full` and go to `localhost:3000` for web interface.
|
||||||
|
|
||||||
You can also use speech to text by setting `listen = True` in the config. Only for CLI mode.
|
You can also use speech to text by setting `listen = True` in the config. Only for CLI mode.
|
||||||
|
|
||||||
@@ -349,7 +398,7 @@ Set the `provider_server_address` to the ip address of the machine that will run
|
|||||||
is_local = False
|
is_local = False
|
||||||
provider_name = server
|
provider_name = server
|
||||||
provider_model = deepseek-r1:70b
|
provider_model = deepseek-r1:70b
|
||||||
provider_server_address = x.x.x.x:3333
|
provider_server_address = http://x.x.x.x:3333
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
@@ -359,6 +408,8 @@ Next step: [Start services and run AgenticSeek](#Start-services-and-Run)
|
|||||||
|
|
||||||
## Speech to Text
|
## Speech to Text
|
||||||
|
|
||||||
|
Warning: speech to text only work in CLI mode at the moment.
|
||||||
|
|
||||||
Please note that currently speech to text only work in english.
|
Please note that currently speech to text only work in english.
|
||||||
|
|
||||||
The speech-to-text functionality is disabled by default. To enable it, set the listen option to True in the config.ini file:
|
The speech-to-text functionality is disabled by default. To enable it, set the listen option to True in the config.ini file:
|
||||||
@@ -392,88 +443,158 @@ Example config:
|
|||||||
is_local = True
|
is_local = True
|
||||||
provider_name = ollama
|
provider_name = ollama
|
||||||
provider_model = deepseek-r1:32b
|
provider_model = deepseek-r1:32b
|
||||||
provider_server_address = 127.0.0.1:11434
|
provider_server_address = http://127.0.0.1:11434 # Example for Ollama; use http://127.0.0.1:1234 for LM-Studio
|
||||||
agent_name = Friday
|
agent_name = Friday
|
||||||
recover_last_session = False
|
recover_last_session = False
|
||||||
save_session = False
|
save_session = False
|
||||||
speak = False
|
speak = False
|
||||||
listen = False
|
listen = False
|
||||||
work_dir = /Users/mlg/Documents/ai_folder
|
|
||||||
jarvis_personality = False
|
jarvis_personality = False
|
||||||
languages = en zh
|
languages = en zh # List of languages for TTS and potentially routing.
|
||||||
[BROWSER]
|
[BROWSER]
|
||||||
headless_browser = False
|
headless_browser = False
|
||||||
stealth_mode = False
|
stealth_mode = False
|
||||||
```
|
```
|
||||||
|
|
||||||
**Explanation**:
|
**Explanation of `config.ini` Settings**:
|
||||||
|
|
||||||
- is_local -> Runs the agent locally (True) or on a remote server (False).
|
* **`[MAIN]` Section:**
|
||||||
|
* `is_local`: `True` if using a local LLM provider (Ollama, LM-Studio, local OpenAI-compatible server) or the self-hosted server option. `False` if using a cloud-based API (OpenAI, Google, etc.).
|
||||||
|
* `provider_name`: Specifies the LLM provider.
|
||||||
|
* Local options: `ollama`, `lm-studio`, `openai` (for local OpenAI-compatible servers), `server` (for the self-hosted server setup).
|
||||||
|
* API options: `openai`, `google`, `deepseek`, `huggingface`, `togetherAI`.
|
||||||
|
* `provider_model`: The specific model name or ID for the chosen provider (e.g., `deepseekcoder:6.7b` for Ollama, `gpt-3.5-turbo` for OpenAI API, `mistralai/Mixtral-8x7B-Instruct-v0.1` for TogetherAI).
|
||||||
|
* `provider_server_address`: The address of your LLM provider.
|
||||||
|
* For local providers: e.g., `http://127.0.0.1:11434` for Ollama, `http://127.0.0.1:1234` for LM-Studio.
|
||||||
|
* For the `server` provider type: The address of your self-hosted LLM server (e.g., `http://your_server_ip:3333`).
|
||||||
|
* For cloud APIs (`is_local = False`): This is often ignored or can be left blank, as the API endpoint is usually handled by the client library.
|
||||||
|
* `agent_name`: Name of the AI assistant (e.g., Friday). Used as a trigger word for speech-to-text if enabled.
|
||||||
|
* `recover_last_session`: `True` to attempt to restore the previous session's state, `False` to start fresh.
|
||||||
|
* `save_session`: `True` to save the current session's state for potential recovery, `False` otherwise.
|
||||||
|
* `speak`: `True` to enable text-to-speech voice output, `False` to disable.
|
||||||
|
* `listen`: `True` to enable speech-to-text voice input (CLI mode only), `False` to disable.
|
||||||
|
* `work_dir`: **Crucial:** The directory where AgenticSeek will read/write files. **Ensure this path is valid and accessible on your system.**
|
||||||
|
* `jarvis_personality`: `True` to use a more "Jarvis-like" system prompt (experimental), `False` for the standard prompt.
|
||||||
|
* `languages`: A comma-separated list of languages (e.g., `en, zh, fr`). Used for TTS voice selection (defaults to the first) and can assist the LLM router. Avoid too many or very similar languages for router efficiency.
|
||||||
|
* **`[BROWSER]` Section:**
|
||||||
|
* `headless_browser`: `True` to run the automated browser without a visible window (recommended for web interface or non-interactive use). `False` to show the browser window (useful for CLI mode or debugging).
|
||||||
|
* `stealth_mode`: `True` to enable measures to make browser automation harder to detect. May require manual installation of browser extensions like anticaptcha.
|
||||||
|
|
||||||
- provider_name -> The provider to use (one of: `ollama`, `server`, `lm-studio`, `deepseek-api`)
|
|
||||||
|
|
||||||
- provider_model -> The model used, e.g., deepseek-r1:32b.
|
This section summarizes the supported LLM provider types. Configure them in `config.ini`.
|
||||||
|
|
||||||
- provider_server_address -> Server address, e.g., 127.0.0.1:11434 for local. Set to anything for non-local API.
|
**Local Providers (Run on Your Own Hardware):**
|
||||||
|
|
||||||
- agent_name -> Name of the agent, e.g., Friday. Used as a trigger word for TTS.
|
| Provider Name in `config.ini` | `is_local` | Description | Setup Section |
|
||||||
|
|-------------------------------|------------|-----------------------------------------------------------------------------|------------------------------------------------------------------|
|
||||||
|
| `ollama` | `True` | Use Ollama to serve local LLMs. | [Setup for running LLM locally](#setup-for-running-llm-locally-on-your-machine) |
|
||||||
|
| `lm-studio` | `True` | Use LM-Studio to serve local LLMs. | [Setup for running LLM locally](#setup-for-running-llm-locally-on-your-machine) |
|
||||||
|
| `openai` (for local server) | `True` | Connect to a local server that exposes an OpenAI-compatible API (e.g., llama.cpp). | [Setup for running LLM locally](#setup-for-running-llm-locally-on-your-machine) |
|
||||||
|
| `server` | `False` | Connect to the AgenticSeek self-hosted LLM server running on another machine. | [Setup to run the LLM on your own server](#setup-to-run-the-llm-on-your-own-server) |
|
||||||
|
|
||||||
- recover_last_session -> Restarts from last session (True) or not (False).
|
**API Providers (Cloud-Based):**
|
||||||
|
|
||||||
- save_session -> Saves session data (True) or not (False).
|
| Provider Name in `config.ini` | `is_local` | Description | Setup Section |
|
||||||
|
|-------------------------------|------------|--------------------------------------------------|-----------------------------------------------------|
|
||||||
|
| `openai` | `False` | Use OpenAI's official API (e.g., GPT-3.5, GPT-4). | [Setup to run with an API](#setup-to-run-with-an-api) |
|
||||||
|
| `google` | `False` | Use Google's Gemini models via API. | [Setup to run with an API](#setup-to-run-with-an-api) |
|
||||||
|
| `deepseek` | `False` | Use Deepseek's official API. | [Setup to run with an API](#setup-to-run-with-an-api) |
|
||||||
|
| `huggingface` | `False` | Use Hugging Face Inference API. | [Setup to run with an API](#setup-to-run-with-an-api) |
|
||||||
|
| `togetherAI` | `False` | Use TogetherAI's API for various open models. | [Setup to run with an API](#setup-to-run-with-an-api) |
|
||||||
|
|
||||||
- speak -> Enables voice output (True) or not (False).
|
---
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
- listen -> listen to voice input (True) or not (False).
|
If you encounter issues, this section provides guidance.
|
||||||
|
|
||||||
- work_dir -> Folder the AI will have access to. eg: /Users/user/Documents/.
|
# Known Issues
|
||||||
|
|
||||||
- jarvis_personality -> Uses a JARVIS-like personality (True) or not (False). This simply change the prompt file.
|
## ChromeDriver Issues
|
||||||
|
|
||||||
- languages -> The list of supported language, needed for the llm router to work properly, avoid putting too many or too similar languages.
|
**Error Example:** `SessionNotCreatedException: Message: session not created: This version of ChromeDriver only supports Chrome version XXX`
|
||||||
|
|
||||||
- headless_browser -> Runs browser without a visible window (True) or not (False).
|
### Root Cause
|
||||||
|
ChromeDriver version incompatibility occurs when:
|
||||||
|
1. Your installed ChromeDriver version doesn't match your Chrome browser version
|
||||||
|
2. In Docker environments, `undetected_chromedriver` may download its own ChromeDriver version, bypassing the mounted binary
|
||||||
|
|
||||||
- stealth_mode -> Make bot detector time harder. Only downside is you have to manually install the anticaptcha extension.
|
### Solution Steps
|
||||||
|
|
||||||
- languages -> List of supported languages. Required for agent routing system. The longer the languages list the more model will be downloaded.
|
#### 1. Check Your Chrome Version
|
||||||
|
Open Google Chrome → `Settings > About Chrome` to find your version (e.g., "Version 134.0.6998.88")
|
||||||
|
|
||||||
## Providers
|
#### 2. Download Matching ChromeDriver
|
||||||
|
|
||||||
The table below show the available providers:
|
**For Chrome 115 and newer:** Use the [Chrome for Testing API](https://googlechromelabs.github.io/chrome-for-testing/)
|
||||||
|
- Visit the Chrome for Testing availability dashboard
|
||||||
|
- Find your Chrome version or the closest available match
|
||||||
|
- Download the ChromeDriver for your OS (Linux64 for Docker environments)
|
||||||
|
|
||||||
| Provider | Local? | Description |
|
**For older Chrome versions:** Use the [legacy ChromeDriver downloads](https://chromedriver.chromium.org/downloads)
|
||||||
|-----------|--------|-----------------------------------------------------------|
|
|
||||||
| 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 |
|
|
||||||
| lm-studio | Yes | Run LLM locally with LM studio (`lm-studio`) |
|
|
||||||
| openai | Depends | Use ChatGPT API (non-private) or openai compatible API |
|
|
||||||
| deepseek-api | No | Deepseek API (non-private) |
|
|
||||||
| huggingface| No | Hugging-Face API (non-private) |
|
|
||||||
| togetherAI | No | Use together AI API (non-private) |
|
|
||||||
| google | No | Use google gemini API (non-private) |
|
|
||||||
|
|
||||||
To select a provider change the config.ini:
|

|
||||||
|
|
||||||
|
#### 3. Install ChromeDriver (Choose One Method)
|
||||||
|
|
||||||
|
**Method A: Project Root Directory (Recommended for Docker)**
|
||||||
|
```bash
|
||||||
|
# Place the downloaded chromedriver binary in your project root
|
||||||
|
cp path/to/downloaded/chromedriver ./chromedriver
|
||||||
|
chmod +x ./chromedriver # Make executable on Linux/macOS
|
||||||
```
|
```
|
||||||
is_local = True
|
|
||||||
provider_name = ollama
|
**Method B: System PATH**
|
||||||
provider_model = deepseek-r1:32b
|
```bash
|
||||||
provider_server_address = 127.0.0.1:5000
|
# Linux/macOS
|
||||||
|
sudo mv chromedriver /usr/local/bin/
|
||||||
|
sudo chmod +x /usr/local/bin/chromedriver
|
||||||
|
|
||||||
|
# Windows: Place chromedriver.exe in a folder that's in your PATH
|
||||||
```
|
```
|
||||||
`is_local`: should be True for any locally running LLM, otherwise False.
|
|
||||||
|
|
||||||
`provider_name`: Select the provider to use by it's name, see the provider list above.
|
#### 4. Verify Installation
|
||||||
|
```bash
|
||||||
|
# Test the ChromeDriver version
|
||||||
|
./chromedriver --version
|
||||||
|
# OR if in PATH:
|
||||||
|
chromedriver --version
|
||||||
|
```
|
||||||
|
|
||||||
`provider_model`: Set the model to use by the agent.
|
### Docker-Specific Notes
|
||||||
|
|
||||||
`provider_server_address`: can be set to anything if you are not using the server provider.
|
⚠️ **Important for Docker Users:**
|
||||||
|
- The Docker volume mount approach may not work with stealth mode (`undetected_chromedriver`)
|
||||||
|
- **Solution**: Place ChromeDriver in the project root directory as `./chromedriver`
|
||||||
|
- The application will automatically detect and use this binary
|
||||||
|
- You should see: `"Using ChromeDriver from project root: ./chromedriver"` in the logs
|
||||||
|
|
||||||
# Known issues
|
### Troubleshooting Tips
|
||||||
|
|
||||||
## Chromedriver Issues
|
1. **Still getting version mismatch?**
|
||||||
|
- Verify the ChromeDriver is executable: `ls -la ./chromedriver`
|
||||||
|
- Check the ChromeDriver version: `./chromedriver --version`
|
||||||
|
- Ensure it matches your Chrome browser version
|
||||||
|
|
||||||
**Known error #1:** *chromedriver mismatch*
|
2. **Docker container issues?**
|
||||||
|
- Check backend logs: `docker logs backend`
|
||||||
|
- Look for the message: `"Using ChromeDriver from project root"`
|
||||||
|
- If not found, verify the file exists and is executable
|
||||||
|
|
||||||
|
3. **Chrome for Testing versions**
|
||||||
|
- Use the exact version match when possible
|
||||||
|
- For version 134.0.6998.88, use ChromeDriver 134.0.6998.165 (closest available)
|
||||||
|
- Major version numbers must match (134 = 134)
|
||||||
|
|
||||||
|
### Version Compatibility Matrix
|
||||||
|
|
||||||
|
| Chrome Version | ChromeDriver Version | Status |
|
||||||
|
|----------------|---------------------|---------|
|
||||||
|
| 134.0.6998.x | 134.0.6998.165 | ✅ Works |
|
||||||
|
| 133.0.6943.x | 133.0.6943.141 | ✅ Works |
|
||||||
|
| 132.0.6834.x | 132.0.6834.159 | ✅ Works |
|
||||||
|
|
||||||
|
*For the latest compatibility, check the [Chrome for Testing dashboard](https://googlechromelabs.github.io/chrome-for-testing/)*
|
||||||
|
|
||||||
`Exception: Failed to initialize browser: Message: session not created: This version of ChromeDriver only supports Chrome version 113
|
`Exception: Failed to initialize browser: Message: session not created: This version of ChromeDriver only supports Chrome version 113
|
||||||
Current browser version is 134.0.6998.89 with binary path`
|
Current browser version is 134.0.6998.89 with binary path`
|
||||||
@@ -497,23 +618,28 @@ If this section is incomplete please raise an issue.
|
|||||||
## connection adapters Issues
|
## connection adapters Issues
|
||||||
|
|
||||||
```
|
```
|
||||||
Exception: Provider lm-studio failed: HTTP request failed: No connection adapters were found for '127.0.0.1:11434/v1/chat/completions'
|
Exception: Provider lm-studio failed: HTTP request failed: No connection adapters were found for '127.0.0.1:1234/v1/chat/completions'` (Note: port may vary)
|
||||||
```
|
```
|
||||||
|
|
||||||
Make sure you have `http://` in front of the provider IP address :
|
* **Cause:** The `provider_server_address` in `config.ini` for `lm-studio` (or other similar local OpenAI-compatible servers) is missing the `http://` prefix or is pointing to the wrong port.
|
||||||
|
* **Solution:**
|
||||||
|
* Ensure the address includes `http://`. LM-Studio typically defaults to `http://127.0.0.1:1234`.
|
||||||
|
* Correct `config.ini`: `provider_server_address = http://127.0.0.1:1234` (or your actual LM-Studio server port).
|
||||||
|
|
||||||
`provider_server_address = http://127.0.0.1:11434`
|
## SearxNG Base URL Not Provided
|
||||||
|
|
||||||
## SearxNG base URL must be provided
|
|
||||||
|
|
||||||
```
|
```
|
||||||
raise ValueError("SearxNG base URL must be provided either as an argument or via the SEARXNG_BASE_URL environment variable.")
|
raise ValueError("SearxNG base URL must be provided either as an argument or via the SEARXNG_BASE_URL environment variable.")
|
||||||
ValueError: SearxNG base URL must be provided either as an argument or via the SEARXNG_BASE_URL environment variable.
|
ValueError: SearxNG base URL must be provided either as an argument or via the SEARXNG_BASE_URL environment variable.`
|
||||||
```
|
```
|
||||||
|
|
||||||
Maybe you didn't move `.env.example` as `.env` ? You can also export SEARXNG_BASE_URL:
|
This might arise if you are running the CLI mode with the wrong base url for searxng.
|
||||||
|
|
||||||
`export SEARXNG_BASE_URL="http://127.0.0.1:8080"`
|
The SEARXNG_BASE_URL should be depending on whenever you run in docker or on host:
|
||||||
|
|
||||||
|
**Run on host**: `SEARXNG_BASE_URL="http://localhost:8080"`
|
||||||
|
|
||||||
|
**Run fully in docker (web interface)**: `SEARXNG_BASE_URL="http://searxng:8080"`
|
||||||
|
|
||||||
## FAQ
|
## FAQ
|
||||||
|
|
||||||
@@ -524,13 +650,9 @@ Maybe you didn't move `.env.example` as `.env` ? You can also export SEARXNG_BAS
|
|||||||
| 7B | 8GB Vram | ⚠️ Not recommended. Performance is poor, frequent hallucinations, and planner agents will likely fail. |
|
| 7B | 8GB Vram | ⚠️ Not recommended. Performance is poor, frequent hallucinations, and planner agents will likely fail. |
|
||||||
| 14B | 12 GB VRAM (e.g. RTX 3060) | ✅ Usable for simple tasks. May struggle with web browsing and planning tasks. |
|
| 14B | 12 GB VRAM (e.g. RTX 3060) | ✅ Usable for simple tasks. May struggle with web browsing and planning tasks. |
|
||||||
| 32B | 24+ GB VRAM (e.g. RTX 4090) | 🚀 Success with most tasks, might still struggle with task planning |
|
| 32B | 24+ GB VRAM (e.g. RTX 4090) | 🚀 Success with most tasks, might still struggle with task planning |
|
||||||
| 70B+ | 48+ GB Vram (eg. mac studio) | 💪 Excellent. Recommended for advanced use cases. |
|
| 70B+ | 48+ GB Vram | 💪 Excellent. Recommended for advanced use cases. |
|
||||||
|
|
||||||
**Q: Why Deepseek R1 over other models?**
|
**Q: I get an error what do I do?**
|
||||||
|
|
||||||
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 `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.
|
||||||
|
|
||||||
@@ -540,17 +662,32 @@ Yes with Ollama, lm-studio or server providers, all speech to text, LLM and text
|
|||||||
|
|
||||||
**Q: Why should I use AgenticSeek when I have Manus?**
|
**Q: Why should I use AgenticSeek when I have Manus?**
|
||||||
|
|
||||||
This started as Side-Project we did out of interest about AI agents. What’s special about it is that we want to use local model and avoid APIs.
|
|
||||||
We draw inspiration from Jarvis and Friday (Iron man movies) to make it "cool" but for functionnality we take more inspiration from Manus, because that's what people want in the first place: a local manus alternative.
|
|
||||||
Unlike Manus, AgenticSeek prioritizes independence from external systems, giving you more control, privacy and avoid api cost.
|
Unlike Manus, AgenticSeek prioritizes independence from external systems, giving you more control, privacy and avoid api cost.
|
||||||
|
|
||||||
|
**Q: Who is behind the project ?**
|
||||||
|
|
||||||
|
The project was created by me, along with two friends who serve as maintainers and contributors from the open-source community on GitHub. We’re just a group of passionate individuals, not a startup or affiliated with any organization.
|
||||||
|
|
||||||
|
Any AgenticSeek account on X other than my personal account (https://x.com/Martin993886460) is an impersonation.
|
||||||
|
|
||||||
## Contribute
|
## Contribute
|
||||||
|
|
||||||
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)
|
[Contribution guide](./docs/CONTRIBUTING.md)
|
||||||
|
|
||||||
[](https://www.star-history.com/#Fosowl/agenticSeek&Date)
|
|
||||||
|
## Sponsors:
|
||||||
|
|
||||||
|
Want to level up AgenticSeek capabilities with features like flight search, trip planning, or snagging the best shopping deals? Consider crafting a custom tool with SerpApi to unlock more Jarvis-like capabilities. With SerpApi, you can turbocharge your agent for specialized tasks while staying in full control.
|
||||||
|
|
||||||
|
<a href="https://serpapi.com/"><img src="./media/banners/sponsor_banner_serpapi.png" height="350" alt="SerpApi Banner" ></a>
|
||||||
|
|
||||||
|
See [Contributing.md](./docs/CONTRIBUTING.md) to learn how to integrate custom tools!
|
||||||
|
|
||||||
|
### **Patron sponsor**:
|
||||||
|
|
||||||
|
- [tatra-labs](https://github.com/tatra-labs)
|
||||||
|
|
||||||
## Maintainers:
|
## Maintainers:
|
||||||
|
|
||||||
@@ -558,4 +695,8 @@ We’re looking for developers to improve AgenticSeek! Check out open issues or
|
|||||||
|
|
||||||
> [antoineVIVIES](https://github.com/antoineVIVIES) | Taipei Time
|
> [antoineVIVIES](https://github.com/antoineVIVIES) | Taipei Time
|
||||||
|
|
||||||
> [steveh8758](https://github.com/steveh8758) | Taipei Time
|
## Special Thanks:
|
||||||
|
|
||||||
|
> [tcsenpai](https://github.com/tcsenpai) and [plitc](https://github.com/plitc) For helping with backend dockerization
|
||||||
|
|
||||||
|
[](https://www.star-history.com/#Fosowl/agenticSeek&Date)
|
||||||
@@ -0,0 +1,682 @@
|
|||||||
|
# AgenticSeek: Una Alternativa Privada y Local a Manus
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img align="center" src="./media/agentic_seek_logo.png" width="300" height="300" alt="Agentic Seek Logo">
|
||||||
|
<p>
|
||||||
|
|
||||||
|
English | [中文](./README_CHS.md) | [繁體中文](./README_CHT.md) | [Français](./README_FR.md) | [日本語](./README_JP.md) | [Português (Brasil)](./README_PTBR.md) | [Español](./README_ES.md) | [Türkçe](./README_TR.md)
|
||||||
|
|
||||||
|
*Un asistente de IA con capacidad de voz que es una **alternativa 100% local a Manus AI**, navega autónomamente por la web, escribe código y planifica tareas manteniendo todos los datos en tu dispositivo. Diseñado para modelos de razonamiento local, funciona completamente en tu hardware, garantizando privacidad total y cero dependencia de la nube.*
|
||||||
|
|
||||||
|
[](https://fosowl.github.io/agenticSeek.html)  [](https://discord.gg/8hGDaME3TC) [](https://x.com/Martin993886460) [](https://github.com/Fosowl/agenticSeek/stargazers)
|
||||||
|
|
||||||
|
### ¿Por qué AgenticSeek?
|
||||||
|
|
||||||
|
* 🔒 Totalmente Local & Privado - Todo funciona en tu máquina, sin nube, sin compartir datos. Tus archivos, conversaciones y búsquedas permanecen privados.
|
||||||
|
|
||||||
|
* 🌐 Navegación Web Inteligente - AgenticSeek puede navegar por Internet de forma autónoma: buscar, leer, extraer información, completar formularios web, todo sin manos.
|
||||||
|
|
||||||
|
* 💻 Asistente de Programación Autónomo - ¿Necesitas código? Puede escribir, depurar y ejecutar programas en Python, C, Go, Java y más, sin supervisión.
|
||||||
|
|
||||||
|
* 🧠 Selección Inteligente de Agentes - Tú pides, él elige automáticamente el mejor agente para la tarea. Como tener un equipo de expertos siempre disponible.
|
||||||
|
|
||||||
|
* 📋 Planifica y Ejecuta Tareas Complejas - Desde planificación de viajes hasta proyectos complejos, puede dividir grandes tareas en pasos y completarlos utilizando múltiples agentes de IA.
|
||||||
|
|
||||||
|
* 🎙️ Compatibilidad con Voz - Voz limpia, rápida y futurista con reconocimiento de voz, permitiéndote conversar como si fuera tu IA personal de una película de ciencia ficción. (En desarrollo)
|
||||||
|
|
||||||
|
### **Demo**
|
||||||
|
|
||||||
|
> *¿Puedes buscar el proyecto agenticSeek, aprender qué habilidades se necesitan, luego abrir CV_candidates.zip y decirme cuáles coinciden mejor con el proyecto?*
|
||||||
|
|
||||||
|
https://github.com/user-attachments/assets/b8ca60e9-7b3b-4533-840e-08f9ac426316
|
||||||
|
|
||||||
|
Descargo de responsabilidad: Esta demostración y todos los archivos que aparecen (ej: CV_candidates.zip) son completamente ficticios. No somos una corporación, buscamos colaboradores de código abierto, no candidatos.
|
||||||
|
|
||||||
|
> 🛠⚠️️ **Trabajo Activo en Progreso**
|
||||||
|
|
||||||
|
> 🙏 Este proyecto comenzó como un proyecto paralelo y no tiene hoja de ruta ni financiación. Creció mucho más allá de lo esperado al aparecer en GitHub Trending. Las contribuciones, comentarios y paciencia son profundamente apreciados.
|
||||||
|
|
||||||
|
## Prerrequisitos
|
||||||
|
|
||||||
|
Antes de comenzar, asegúrate de tener instalado:
|
||||||
|
|
||||||
|
* **Git:** Para clonar el repositorio. [Descargar Git](https://git-scm.com/downloads)
|
||||||
|
* **Python 3.10.x:** Se recomienda encarecidamente Python 3.10.x. Otras versiones pueden causar errores de dependencia. [Descargar Python 3.10](https://www.python.org/downloads/release/python-3100/) (selecciona la versión 3.10.x).
|
||||||
|
* **Docker Engine & Docker Compose:** Para ejecutar servicios empaquetados como SearxNG.
|
||||||
|
* Instalar Docker Desktop (incluye Docker Compose V2): [Windows](https://docs.docker.com/desktop/install/windows-install/) | [Mac](https://docs.docker.com/desktop/install/mac-install/) | [Linux](https://docs.docker.com/desktop/install/linux-install/)
|
||||||
|
* O instalar Docker Engine y Docker Compose por separado en Linux: [Docker Engine](https://docs.docker.com/engine/install/) | [Docker Compose](https://docs.docker.com/compose/install/) (asegúrate de instalar Compose V2, por ejemplo `sudo apt-get install docker-compose-plugin`).
|
||||||
|
|
||||||
|
### 1. **Clonar el repositorio y configurar**
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git clone https://github.com/Fosowl/agenticSeek.git
|
||||||
|
cd agenticSeek
|
||||||
|
mv .env.example .env
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Modificar el contenido del archivo .env
|
||||||
|
|
||||||
|
```sh
|
||||||
|
SEARXNG_BASE_URL="http://searxng:8080" # Si ejecutas en modo CLI en el host, usa http://127.0.0.1:8080
|
||||||
|
REDIS_BASE_URL="redis://redis:6379/0"
|
||||||
|
WORK_DIR="/Users/mlg/Documents/workspace_for_ai"
|
||||||
|
OLLAMA_PORT="11434"
|
||||||
|
LM_STUDIO_PORT="1234"
|
||||||
|
CUSTOM_ADDITIONAL_LLM_PORT="11435"
|
||||||
|
OPENAI_API_KEY='optional'
|
||||||
|
DEEPSEEK_API_KEY='optional'
|
||||||
|
OPENROUTER_API_KEY='optional'
|
||||||
|
TOGETHER_API_KEY='optional'
|
||||||
|
GOOGLE_API_KEY='optional'
|
||||||
|
ANTHROPIC_API_KEY='optional'
|
||||||
|
```
|
||||||
|
|
||||||
|
Actualiza el archivo `.env` según sea necesario:
|
||||||
|
|
||||||
|
- **SEARXNG_BASE_URL**: Mantener sin cambios a menos que ejecutes en modo CLI en el host.
|
||||||
|
- **REDIS_BASE_URL**: Mantener sin cambios
|
||||||
|
- **WORK_DIR**: Ruta al directorio de trabajo local. AgenticSeek podrá leer e interactuar con estos archivos.
|
||||||
|
- **OLLAMA_PORT**: Número de puerto para el servicio Ollama.
|
||||||
|
- **LM_STUDIO_PORT**: Número de puerto para el servicio LM Studio.
|
||||||
|
- **CUSTOM_ADDITIONAL_LLM_PORT**: Puerto para cualquier servicio LLM adicional personalizado.
|
||||||
|
|
||||||
|
**Las claves API son completamente opcionales para quienes optan por ejecutar LLM localmente, que es el objetivo principal de este proyecto. Déjalas en blanco si tienes hardware suficiente.**
|
||||||
|
|
||||||
|
### 3. **Iniciar Docker**
|
||||||
|
|
||||||
|
Asegúrate de que Docker esté instalado y ejecutándose en tu sistema. Puedes iniciar Docker con los siguientes comandos:
|
||||||
|
|
||||||
|
- **Linux/macOS:**
|
||||||
|
Abre una terminal y ejecuta:
|
||||||
|
```sh
|
||||||
|
sudo systemctl start docker
|
||||||
|
```
|
||||||
|
O inicia Docker Desktop desde el menú de aplicaciones, si está instalado.
|
||||||
|
|
||||||
|
- **Windows:**
|
||||||
|
Inicia Docker Desktop desde el menú Inicio.
|
||||||
|
|
||||||
|
Puedes verificar si Docker se está ejecutando ejecutando:
|
||||||
|
```sh
|
||||||
|
docker info
|
||||||
|
```
|
||||||
|
Si ves información sobre tu instalación de Docker, está funcionando correctamente.
|
||||||
|
|
||||||
|
Consulta la [Lista de proveedores locales](#lista-de-proveedores-locales) a continuación para obtener un resumen.
|
||||||
|
|
||||||
|
Siguiente paso: [Ejecutar AgenticSeek localmente](#iniciar-servicios-y-ejecutar)
|
||||||
|
|
||||||
|
*Si tienes problemas, consulta la sección [Solución de problemas](#solución-de-problemas).*
|
||||||
|
*Si tu hardware no puede ejecutar LLM localmente, consulta [Configuración para ejecutar con una API](#configuración-para-ejecutar-con-una-api).*
|
||||||
|
*Para explicaciones detalladas de `config.ini`, consulta la [sección Configuración](#configuración).*
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Configuración para ejecutar LLM localmente en tu máquina
|
||||||
|
|
||||||
|
**Requisitos de hardware:**
|
||||||
|
|
||||||
|
Para ejecutar LLM localmente, necesitarás hardware suficiente. Como mínimo, se requiere una GPU capaz de ejecutar Magistral, Qwen o Deepseek 14B. Consulta el FAQ para recomendaciones detalladas de modelo/rendimiento.
|
||||||
|
|
||||||
|
**Configura tu proveedor local**
|
||||||
|
|
||||||
|
Inicia tu proveedor local, por ejemplo con ollama:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
ollama serve
|
||||||
|
```
|
||||||
|
|
||||||
|
Consulta la lista de proveedores locales admitidos a continuación.
|
||||||
|
|
||||||
|
**Actualizar config.ini**
|
||||||
|
|
||||||
|
Cambia el archivo config.ini para establecer provider_name en un proveedor admitido y provider_model en un LLM admitido por tu proveedor. Recomendamos modelos de razonamiento como *Magistral* o *Deepseek*.
|
||||||
|
|
||||||
|
Consulta el **FAQ** al final del README para el hardware necesario.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
[MAIN]
|
||||||
|
is_local = True # Ya sea que ejecutes localmente o con un proveedor remoto.
|
||||||
|
provider_name = ollama # o lm-studio, openai, etc.
|
||||||
|
provider_model = deepseek-r1:14b # elige un modelo compatible con tu hardware
|
||||||
|
provider_server_address = 127.0.0.1:11434
|
||||||
|
agent_name = Jarvis # el nombre de tu IA
|
||||||
|
recover_last_session = True # recuperar sesión anterior
|
||||||
|
save_session = True # recordar sesión actual
|
||||||
|
speak = False # texto a voz
|
||||||
|
listen = False # voz a texto, solo para CLI, experimental
|
||||||
|
jarvis_personality = False # usar personalidad más "Jarvis" (experimental)
|
||||||
|
languages = en zh # Lista de idiomas, TTS usará el primero de la lista por defecto
|
||||||
|
[BROWSER]
|
||||||
|
headless_browser = True # mantener sin cambios a menos que uses CLI en el host.
|
||||||
|
stealth_mode = True # Usa selenium indetectable para reducir la detección del navegador
|
||||||
|
```
|
||||||
|
|
||||||
|
**Advertencia**:
|
||||||
|
|
||||||
|
- El formato del archivo `config.ini` no admite comentarios.
|
||||||
|
No copies y pegues la configuración de ejemplo directamente, ya que los comentarios causarán errores. En su lugar, modifica manualmente el archivo `config.ini` con tu configuración deseada, sin comentarios.
|
||||||
|
|
||||||
|
- *NO* establezcas provider_name como `openai` si estás usando LM-studio para ejecutar LLM. Úsalo como `lm-studio`.
|
||||||
|
|
||||||
|
- Algunos proveedores (ej: lm-studio) requieren `http://` antes de la IP. Ejemplo: `http://127.0.0.1:1234`
|
||||||
|
|
||||||
|
**Lista de proveedores locales**
|
||||||
|
|
||||||
|
| Proveedor | ¿Local? | Descripción |
|
||||||
|
|-----------|--------|-----------------------------------------------------------|
|
||||||
|
| ollama | Sí | Ejecuta LLM localmente fácilmente usando ollama |
|
||||||
|
| lm-studio | Sí | Ejecuta LLM localmente con LM studio (establecer `provider_name` = `lm-studio`)|
|
||||||
|
| openai | Sí | Usa API compatible con openai (ej: servidor llama.cpp) |
|
||||||
|
|
||||||
|
Siguiente paso: [Iniciar servicios y ejecutar AgenticSeek](#iniciar-servicios-y-ejecutar)
|
||||||
|
|
||||||
|
*Si tienes problemas, consulta la sección [Solución de problemas](#solución-de-problemas).*
|
||||||
|
*Si tu hardware no puede ejecutar LLM localmente, consulta [Configuración para ejecutar con una API](#configuración-para-ejecutar-con-una-api).*
|
||||||
|
*Para explicaciones detalladas de `config.ini`, consulta la [sección Configuración](#configuración).*
|
||||||
|
|
||||||
|
## Configuración para ejecutar con una API
|
||||||
|
|
||||||
|
Esta configuración utiliza proveedores de LLM externos basados en la nube. Necesitarás obtener claves API del servicio elegido.
|
||||||
|
|
||||||
|
**1. Elige un proveedor de API y obtén una clave API:**
|
||||||
|
|
||||||
|
Consulta la [Lista de proveedores de API](#lista-de-proveedores-de-api) a continuación. Visita sus sitios web para registrarte y obtener claves API.
|
||||||
|
|
||||||
|
**2. Establece tu clave API como variable de entorno:**
|
||||||
|
|
||||||
|
* **Linux/macOS:**
|
||||||
|
Abre una terminal y usa el comando `export`. Es mejor agregarlo al archivo de configuración de tu shell (ej: `~/.bashrc`, `~/.zshrc`) para que sea persistente.
|
||||||
|
```sh
|
||||||
|
export PROVIDER_API_KEY="your_api_key_here"
|
||||||
|
# Reemplaza PROVIDER_API_KEY con el nombre de variable específico, ej: OPENAI_API_KEY, GOOGLE_API_KEY
|
||||||
|
```
|
||||||
|
Ejemplo de TogetherAI:
|
||||||
|
```sh
|
||||||
|
export TOGETHER_API_KEY="xxxxxxxxxxxxxxxxxxxxxx"
|
||||||
|
```
|
||||||
|
* **Windows:**
|
||||||
|
* **Símbolo del sistema (temporal para la sesión actual):**
|
||||||
|
```cmd
|
||||||
|
set PROVIDER_API_KEY=your_api_key_here
|
||||||
|
```
|
||||||
|
* **PowerShell (temporal para la sesión actual):**
|
||||||
|
```powershell
|
||||||
|
$env:PROVIDER_API_KEY="your_api_key_here"
|
||||||
|
```
|
||||||
|
* **Permanente:** Busca "variables de entorno" en la barra de búsqueda de Windows, haz clic en "Editar las variables de entorno del sistema", luego en el botón "Variables de entorno...". Agrega una nueva variable de usuario con el nombre apropiado (ej: `OPENAI_API_KEY`) y tu clave como valor.
|
||||||
|
|
||||||
|
*(Para más detalles, consulta el FAQ: [¿Cómo configuro una clave API?](#cómo-configuro-una-clave-api)).*
|
||||||
|
|
||||||
|
|
||||||
|
**3. Actualiza `config.ini`:**
|
||||||
|
```ini
|
||||||
|
[MAIN]
|
||||||
|
is_local = False
|
||||||
|
provider_name = openai # o google, deepseek, togetherAI, huggingface
|
||||||
|
provider_model = gpt-3.5-turbo # o gemini-1.5-flash, deepseek-chat, mistralai/Mixtral-8x7B-Instruct-v0.1, etc.
|
||||||
|
provider_server_address = # Cuando is_local = False, generalmente se ignora o puede dejarse en blanco para la mayoría de las API
|
||||||
|
# ... otras configuraciones ...
|
||||||
|
```
|
||||||
|
*Advertencia:* Asegúrate de que no haya espacios al final de los valores en config.
|
||||||
|
|
||||||
|
**Lista de proveedores de API**
|
||||||
|
|
||||||
|
| Proveedor | `provider_name` | ¿Local? | Descripción | Enlace de clave API (ejemplo) |
|
||||||
|
|--------------|-----------------|--------|---------------------------------------------------|---------------------------------------------|
|
||||||
|
| OpenAI | `openai` | No | Usa modelos ChatGPT a través de la API de OpenAI. | [platform.openai.com/signup](https://platform.openai.com/signup) |
|
||||||
|
| Google Gemini| `google` | No | Usa modelos Google Gemini a través de Google AI Studio. | [aistudio.google.com/keys](https://aistudio.google.com/keys) |
|
||||||
|
| Deepseek | `deepseek` | No | Usa modelos Deepseek a través de su API. | [platform.deepseek.com](https://platform.deepseek.com) |
|
||||||
|
| Hugging Face | `huggingface` | No | Usa modelos del Hugging Face Inference API. | [huggingface.co/settings/tokens](https://huggingface.co/settings/tokens) |
|
||||||
|
| TogetherAI | `togetherAI` | No | Usa varios modelos de código abierto a través de la API de TogetherAI.| [api.together.ai/settings/api-keys](https://api.together.ai/settings/api-keys) |
|
||||||
|
|
||||||
|
*Nota:*
|
||||||
|
* No recomendamos usar `gpt-4o` u otros modelos OpenAI para navegación web compleja y planificación de tareas, ya que la optimización actual de prompts está dirigida a modelos como Deepseek.
|
||||||
|
* Las tareas de codificación/bash pueden fallar con Gemini, ya que tiende a ignorar nuestro formato de prompt optimizado para Deepseek r1.
|
||||||
|
* Cuando `is_local = False`, `provider_server_address` en `config.ini` generalmente no se usa, ya que los endpoints de API suelen estar codificados en las bibliotecas del proveedor correspondiente.
|
||||||
|
|
||||||
|
Siguiente paso: [Iniciar servicios y ejecutar AgenticSeek](#iniciar-servicios-y-ejecutar)
|
||||||
|
|
||||||
|
*Si tienes problemas, consulta la sección **Problemas conocidos***
|
||||||
|
|
||||||
|
*Para explicaciones detalladas del archivo de configuración, consulta la **sección Configuración**.*
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Iniciar servicios y ejecutar
|
||||||
|
|
||||||
|
Por defecto, AgenticSeek se ejecuta completamente en Docker.
|
||||||
|
|
||||||
|
**Opción 1:** Ejecutar en Docker con interfaz web:
|
||||||
|
|
||||||
|
Inicia los servicios necesarios. Esto iniciará todos los servicios del docker-compose.yml, incluyendo:
|
||||||
|
- searxng
|
||||||
|
- redis (requerido para searxng)
|
||||||
|
- frontend
|
||||||
|
- backend (si usas `full` para la interfaz web)
|
||||||
|
|
||||||
|
```sh
|
||||||
|
./start_services.sh full # MacOS
|
||||||
|
start start_services.cmd full # Windows
|
||||||
|
```
|
||||||
|
|
||||||
|
**Advertencia:** Este paso descargará y cargará todas las imágenes de Docker, lo que puede tardar hasta 30 minutos. Después de iniciar los servicios, espera hasta que el servicio backend esté completamente ejecutándose (deberías ver **backend: "GET /health HTTP/1.1" 200 OK** en el registro) antes de enviar cualquier mensaje. En la primera ejecución, el servicio backend puede tardar 5 minutos en iniciarse.
|
||||||
|
|
||||||
|
Ve a `http://localhost:3000/` y deberías ver la interfaz web.
|
||||||
|
|
||||||
|
*Solución de problemas de inicio de servicios:* Si estos scripts fallan, asegúrate de que Docker Engine esté ejecutándose y que Docker Compose (V2, `docker compose`) esté correctamente instalado. Revisa los mensajes de error en la salida de la terminal. Consulta [FAQ: ¡Ayuda! Obtengo errores al ejecutar AgenticSeek o sus scripts.](#faq-solución-de-problemas)
|
||||||
|
|
||||||
|
**Opción 2:** Modo CLI:
|
||||||
|
|
||||||
|
Para ejecutar con la interfaz CLI, debes instalar los paquetes en el host:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
./install.sh
|
||||||
|
./install.bat # windows
|
||||||
|
```
|
||||||
|
|
||||||
|
Luego debes cambiar SEARXNG_BASE_URL en `config.ini` a:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
SEARXNG_BASE_URL="http://localhost:8080"
|
||||||
|
```
|
||||||
|
|
||||||
|
Inicia los servicios necesarios. Esto iniciará algunos servicios del docker-compose.yml, incluyendo:
|
||||||
|
- searxng
|
||||||
|
- redis (requerido para searxng)
|
||||||
|
- frontend
|
||||||
|
|
||||||
|
```sh
|
||||||
|
./start_services.sh # MacOS
|
||||||
|
start start_services.cmd # Windows
|
||||||
|
```
|
||||||
|
|
||||||
|
Ejecuta: uv run: `uv run python -m ensurepip` para asegurarte de que uv tenga pip habilitado.
|
||||||
|
|
||||||
|
Usa CLI: `uv run cli.py`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Uso
|
||||||
|
|
||||||
|
Asegúrate de que los servicios estén ejecutándose con `./start_services.sh full` y luego ve a `localhost:3000` para la interfaz web.
|
||||||
|
|
||||||
|
También puedes usar voz a texto configurando `listen = True`. Solo para modo CLI.
|
||||||
|
|
||||||
|
Para salir, simplemente di/escribe `goodbye`.
|
||||||
|
|
||||||
|
Algunos ejemplos de uso:
|
||||||
|
|
||||||
|
> *¡Haz un juego de la serpiente en python!*
|
||||||
|
|
||||||
|
> *Busca en la web los mejores cafés en Rennes, Francia, y guarda una lista de tres con sus direcciones en rennes_cafes.txt.*
|
||||||
|
|
||||||
|
> *Escribe un programa Go para calcular el factorial de un número, guárdalo como factorial.go en tu workspace*
|
||||||
|
|
||||||
|
> *Busca en la carpeta summer_pictures todos los archivos JPG, renómbralos con la fecha de hoy y guarda la lista de archivos renombrados en photos_list.txt*
|
||||||
|
|
||||||
|
> *Busca en línea películas de ciencia ficción populares de 2024 y elige tres para ver esta noche. Guarda la lista en movie_night.txt.*
|
||||||
|
|
||||||
|
> *Busca en la web los últimos artículos de noticias de IA de 2025, selecciona tres y escribe un script Python para extraer títulos y resúmenes. Guarda el script como news_scraper.py y los resúmenes en ai_news.txt en /home/projects*
|
||||||
|
|
||||||
|
> *Viernes, busca en la web una API gratuita de precios de acciones, regístrate con supersuper7434567@gmail.com y escribe un script Python para obtener los precios diarios de Tesla usando la API, guardando los resultados en stock_prices.csv*
|
||||||
|
|
||||||
|
*Ten en cuenta que el llenado de formularios sigue siendo experimental y puede fallar.*
|
||||||
|
|
||||||
|
Después de ingresar tu consulta, AgenticSeek asignará el mejor agente para la tarea.
|
||||||
|
|
||||||
|
Como este es un prototipo inicial, el sistema de enrutamiento de agentes puede no asignar siempre el agente correcto a tu consulta.
|
||||||
|
|
||||||
|
Por lo tanto, sé muy explícito sobre lo que quieres y cómo la IA podría proceder, por ejemplo, si quieres que realice una búsqueda web, no digas:
|
||||||
|
|
||||||
|
`¿Conoces algunos buenos países para viajar solo?`
|
||||||
|
|
||||||
|
En su lugar, di:
|
||||||
|
|
||||||
|
`Realiza una búsqueda web y descubre cuáles son los mejores países para viajar solo`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## **Configuración para ejecutar LLM en tu propio servidor**
|
||||||
|
|
||||||
|
Si tienes una computadora potente o un servidor al que puedes acceder, pero quieres usarlo desde tu laptop, puedes optar por ejecutar el LLM en un servidor remoto usando nuestro servidor llm personalizado.
|
||||||
|
|
||||||
|
En tu "servidor" que ejecutará el modelo de IA, obtén la dirección IP
|
||||||
|
|
||||||
|
```sh
|
||||||
|
ip a | grep "inet " | grep -v 127.0.0.1 | awk '{print $2}' | cut -d/ -f1 # IP local
|
||||||
|
curl https://ipinfo.io/ip # IP pública
|
||||||
|
```
|
||||||
|
|
||||||
|
Nota: Para Windows o macOS, usa ipconfig o ifconfig para encontrar la dirección IP.
|
||||||
|
|
||||||
|
Clona el repositorio y entra en la carpeta `server/`.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git clone --depth 1 https://github.com/Fosowl/agenticSeek.git
|
||||||
|
cd agenticSeek/llm_server/
|
||||||
|
```
|
||||||
|
|
||||||
|
Instala los requisitos específicos del servidor:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
pip3 install -r requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
Ejecuta el script del servidor.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
python3 app.py --provider ollama --port 3333
|
||||||
|
```
|
||||||
|
|
||||||
|
Puedes elegir entre usar `ollama` y `llamacpp` como servicio LLM.
|
||||||
|
|
||||||
|
Ahora en tu computadora personal:
|
||||||
|
|
||||||
|
Cambia el archivo `config.ini` para establecer `provider_name` como `server` y `provider_model` como `deepseek-r1:xxb`.
|
||||||
|
Establece `provider_server_address` a la dirección IP de la máquina que ejecutará el modelo.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
[MAIN]
|
||||||
|
is_local = False
|
||||||
|
provider_name = server
|
||||||
|
provider_model = deepseek-r1:70b
|
||||||
|
provider_server_address = http://x.x.x.x:3333
|
||||||
|
```
|
||||||
|
|
||||||
|
Siguiente paso: [Iniciar servicios y ejecutar AgenticSeek](#iniciar-servicios-y-ejecutar)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Voz a Texto
|
||||||
|
|
||||||
|
Advertencia: La voz a texto solo funciona en modo CLI en este momento.
|
||||||
|
|
||||||
|
Ten en cuenta que la voz a texto solo funciona en inglés en este momento.
|
||||||
|
|
||||||
|
La funcionalidad de voz a texto está deshabilitada por defecto. Para habilitarla, establece listen en True en el archivo config.ini:
|
||||||
|
|
||||||
|
```
|
||||||
|
listen = True
|
||||||
|
```
|
||||||
|
|
||||||
|
Cuando está habilitado, la función de voz a texto escucha una palabra clave de activación, que es el nombre del agente, antes de procesar tu entrada. Puedes personalizar el nombre del agente actualizando el valor `agent_name` en *config.ini*:
|
||||||
|
|
||||||
|
```
|
||||||
|
agent_name = Friday
|
||||||
|
```
|
||||||
|
|
||||||
|
Para un mejor reconocimiento, recomendamos usar un nombre común en inglés como "John" o "Emma" como nombre de agente.
|
||||||
|
|
||||||
|
Una vez que veas que comienza a aparecer la transcripción, di el nombre del agente en voz alta para activarlo (ej: "Friday").
|
||||||
|
|
||||||
|
Di tu consulta claramente.
|
||||||
|
|
||||||
|
Termina tu solicitud con una frase de confirmación para indicar al sistema que proceda. Ejemplos de frases de confirmación incluyen:
|
||||||
|
```
|
||||||
|
"do it", "go ahead", "execute", "run", "start", "thanks", "would ya", "please", "okay?", "proceed", "continue", "go on", "do that", "go it", "do you understand?"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuración
|
||||||
|
|
||||||
|
Ejemplo de configuración:
|
||||||
|
```
|
||||||
|
[MAIN]
|
||||||
|
is_local = True
|
||||||
|
provider_name = ollama
|
||||||
|
provider_model = deepseek-r1:32b
|
||||||
|
provider_server_address = http://127.0.0.1:11434 # Ejemplo de Ollama; LM-Studio usa http://127.0.0.1:1234
|
||||||
|
agent_name = Friday
|
||||||
|
recover_last_session = False
|
||||||
|
save_session = False
|
||||||
|
speak = False
|
||||||
|
listen = False
|
||||||
|
|
||||||
|
jarvis_personality = False
|
||||||
|
languages = en zh # Lista de idiomas para TTS y enrutamiento potencial.
|
||||||
|
[BROWSER]
|
||||||
|
headless_browser = False
|
||||||
|
stealth_mode = False
|
||||||
|
```
|
||||||
|
|
||||||
|
**Explicación de la configuración de `config.ini`**:
|
||||||
|
|
||||||
|
* **Sección `[MAIN]`:**
|
||||||
|
* `is_local`: `True` si usas proveedores de LLM locales (Ollama, LM-Studio, servidor local compatible con OpenAI) o la opción de servidor autoalojado. `False` si usas API basadas en la nube (OpenAI, Google, etc.).
|
||||||
|
* `provider_name`: Especifica el proveedor de LLM.
|
||||||
|
* Opciones locales: `ollama`, `lm-studio`, `openai` (para servidor local compatible con OpenAI), `server` (para configuración de servidor autoalojado).
|
||||||
|
* Opciones de API: `openai`, `google`, `deepseek`, `huggingface`, `togetherAI`.
|
||||||
|
* `provider_model`: Nombre o ID específico del modelo del proveedor seleccionado (ej: `deepseekcoder:6.7b` para Ollama, `gpt-3.5-turbo` para API de OpenAI, `mistralai/Mixtral-8x7B-Instruct-v0.1` para TogetherAI).
|
||||||
|
* `provider_server_address`: La dirección de tu proveedor de LLM.
|
||||||
|
* Para proveedores locales: ej: `http://127.0.0.1:11434` para Ollama, `http://127.0.0.1:1234` para LM-Studio.
|
||||||
|
* Para el tipo de proveedor `server`: La dirección de tu servidor LLM autoalojado (ej: `http://your_server_ip:3333`).
|
||||||
|
* Para API en la nube (`is_local = False`): Esto generalmente se ignora o puede dejarse en blanco, ya que los endpoints de API suelen ser manejados por las bibliotecas del cliente.
|
||||||
|
* `agent_name`: El nombre del asistente de IA (ej: Friday). Si está habilitado, se utiliza como palabra de activación para voz a texto.
|
||||||
|
* `recover_last_session`: `True` para intentar recuperar el estado de la sesión anterior, `False` para comenzar de nuevo.
|
||||||
|
* `save_session`: `True` para guardar el estado de la sesión actual para una posible recuperación, `False` en caso contrario.
|
||||||
|
* `speak`: `True` para habilitar la salida de voz de texto a voz, `False` para deshabilitar.
|
||||||
|
* `listen`: `True` para habilitar la entrada de voz de voz a texto (solo modo CLI), `False` para deshabilitar.
|
||||||
|
* `work_dir`: **Crítico:** El directorio donde AgenticSeek leerá/escribirá archivos. **Asegúrate de que esta ruta sea válida y accesible en tu sistema.**
|
||||||
|
* `jarvis_personality`: `True` para usar prompts del sistema más al estilo "Jarvis" (experimental), `False` para usar prompts estándar.
|
||||||
|
* `languages`: Lista de idiomas separados por comas (ej: `en, zh, fr`). Se utiliza para la selección de voz TTS (predeterminado el primero) y puede ayudar al enrutador LLM. Para evitar ineficiencias del enrutador, evita usar demasiados idiomas o idiomas muy similares.
|
||||||
|
* **Sección `[BROWSER]`:**
|
||||||
|
* `headless_browser`: `True` para ejecutar el navegador automatizado sin una ventana visible (recomendado para interfaz web o uso no interactivo). `False` para mostrar la ventana del navegador (útil para modo CLI o depuración).
|
||||||
|
* `stealth_mode`: `True` para habilitar medidas que dificultan la detección de la automatización del navegador. Puede requerir la instalación manual de extensiones del navegador como anticaptcha.
|
||||||
|
|
||||||
|
Esta sección resume los tipos de proveedores de LLM admitidos. Configúralos en `config.ini`.
|
||||||
|
|
||||||
|
**Proveedores locales (ejecutándose en tu propio hardware):**
|
||||||
|
|
||||||
|
| Nombre del proveedor en config.ini | `is_local` | Descripción | Sección de configuración |
|
||||||
|
|-------------------------------|------------|-----------------------------------------------------------------------------|------------------------------------------------------------------|
|
||||||
|
| `ollama` | `True` | Proporciona LLM localmente fácilmente usando Ollama. | [Configuración para ejecutar LLM localmente en tu máquina](#configuración-para-ejecutar-llm-localmente-en-tu-máquina) |
|
||||||
|
| `lm-studio` | `True` | Proporciona LLM localmente con LM-Studio. | [Configuración para ejecutar LLM localmente en tu máquina](#configuración-para-ejecutar-llm-localmente-en-tu-máquina) |
|
||||||
|
| `openai` (para servidor local) | `True` | Conéctate a un servidor local que exponga una API compatible con OpenAI (ej: llama.cpp). | [Configuración para ejecutar LLM localmente en tu máquina](#configuración-para-ejecutar-llm-localmente-en-tu-máquina) |
|
||||||
|
| `server` | `False` | Conéctate al servidor LLM autoalojado de AgenticSeek que se ejecuta en otra máquina. | [Configuración para ejecutar LLM en tu propio servidor](#configuración-para-ejecutar-llm-en-tu-propio-servidor) |
|
||||||
|
|
||||||
|
**Proveedores de API (basados en la nube):**
|
||||||
|
|
||||||
|
| Nombre del proveedor en config.ini | `is_local` | Descripción | Sección de configuración |
|
||||||
|
|-------------------------------|------------|--------------------------------------------------|-----------------------------------------------------|
|
||||||
|
| `openai` | `False` | Usa la API oficial de OpenAI (ej: GPT-3.5, GPT-4). | [Configuración para ejecutar con una API](#configuración-para-ejecutar-con-una-api) |
|
||||||
|
| `google` | `False` | Usa modelos Google Gemini a través de API. | [Configuración para ejecutar con una API](#configuración-para-ejecutar-con-una-api) |
|
||||||
|
| `deepseek` | `False` | Usa la API oficial de Deepseek. | [Configuración para ejecutar con una API](#configuración-para-ejecutar-con-una-api) |
|
||||||
|
| `huggingface` | `False` | Usa Hugging Face Inference API. | [Configuración para ejecutar con una API](#configuración-para-ejecutar-con-una-api) |
|
||||||
|
| `togetherAI` | `False` | Usa varios modelos abiertos a través de la API de TogetherAI. | [Configuración para ejecutar con una API](#configuración-para-ejecutar-con-una-api) |
|
||||||
|
|
||||||
|
---
|
||||||
|
## Solución de problemas
|
||||||
|
|
||||||
|
Si encuentras problemas, esta sección proporciona orientación.
|
||||||
|
|
||||||
|
# Problemas conocidos
|
||||||
|
|
||||||
|
## Problemas de ChromeDriver
|
||||||
|
|
||||||
|
**Ejemplo de error:** `SessionNotCreatedException: Message: session not created: This version of ChromeDriver only supports Chrome version XXX`
|
||||||
|
|
||||||
|
### Causa principal
|
||||||
|
La incompatibilidad de versión de ChromeDriver ocurre cuando:
|
||||||
|
1. La versión de ChromeDriver que instalaste no coincide con la versión del navegador Chrome
|
||||||
|
2. En entornos Docker, `undetected_chromedriver` puede descargar su propia versión de ChromeDriver, evitando los binarios montados
|
||||||
|
|
||||||
|
### Pasos de solución
|
||||||
|
|
||||||
|
#### 1. Verifica tu versión de Chrome
|
||||||
|
Abre Google Chrome → `Configuración > Acerca de Chrome` para encontrar tu versión (ej: "Versión 134.0.6998.88")
|
||||||
|
|
||||||
|
#### 2. Descarga ChromeDriver coincidente
|
||||||
|
|
||||||
|
**Para Chrome 115 y versiones más recientes:** Usa [Chrome for Testing API](https://googlechromelabs.github.io/chrome-for-testing/)
|
||||||
|
- Visita el panel de disponibilidad de Chrome for Testing
|
||||||
|
- Encuentra tu versión de Chrome o la coincidencia disponible más cercana
|
||||||
|
- Descarga ChromeDriver para tu sistema operativo (usa Linux64 para entornos Docker)
|
||||||
|
|
||||||
|
**Para versiones antiguas de Chrome:** Usa [Descargas heredadas de ChromeDriver](https://chromedriver.chromium.org/downloads)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
#### 3. Instala ChromeDriver (elige un método)
|
||||||
|
|
||||||
|
**Método A: Directorio raíz del proyecto (recomendado para Docker)**
|
||||||
|
```bash
|
||||||
|
# Coloca el binario de chromedriver descargado en el directorio raíz del proyecto
|
||||||
|
cp path/to/downloaded/chromedriver ./chromedriver
|
||||||
|
chmod +x ./chromedriver # Hazlo ejecutable en Linux/macOS
|
||||||
|
```
|
||||||
|
|
||||||
|
**Método B: PATH del sistema**
|
||||||
|
```bash
|
||||||
|
# Linux/macOS
|
||||||
|
sudo mv chromedriver /usr/local/bin/
|
||||||
|
sudo chmod +x /usr/local/bin/chromedriver
|
||||||
|
|
||||||
|
# Windows: Coloca chromedriver.exe en una carpeta en PATH
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 4. Verifica la instalación
|
||||||
|
```bash
|
||||||
|
# Prueba la versión de ChromeDriver
|
||||||
|
./chromedriver --version
|
||||||
|
# O si está en PATH:
|
||||||
|
chromedriver --version
|
||||||
|
```
|
||||||
|
|
||||||
|
### Instrucciones específicas de Docker
|
||||||
|
|
||||||
|
⚠️ **Importante para usuarios de Docker:**
|
||||||
|
- El método de montaje de volúmenes de Docker puede no funcionar con el modo sigiloso (`undetected_chromedriver`)
|
||||||
|
- **Solución:** Coloca ChromeDriver en el directorio raíz del proyecto como `./chromedriver`
|
||||||
|
- La aplicación lo detectará automáticamente y usará este binario
|
||||||
|
- Deberías ver en los registros: `"Using ChromeDriver from project root: ./chromedriver"`
|
||||||
|
|
||||||
|
### Consejos para solución de problemas
|
||||||
|
|
||||||
|
1. **¿Sigues teniendo incompatibilidad de versión?**
|
||||||
|
- Verifica que ChromeDriver sea ejecutable: `ls -la ./chromedriver`
|
||||||
|
- Comprueba la versión de ChromeDriver: `./chromedriver --version`
|
||||||
|
- Asegúrate de que coincida con tu versión del navegador Chrome
|
||||||
|
|
||||||
|
2. **¿Problemas con el contenedor Docker?**
|
||||||
|
- Revisa los registros del backend: `docker logs backend`
|
||||||
|
- Busca el mensaje: `"Using ChromeDriver from project root"`
|
||||||
|
- Si no se encuentra, verifica que el archivo exista y sea ejecutable
|
||||||
|
|
||||||
|
3. **Versiones de Chrome for Testing**
|
||||||
|
- Usa una coincidencia exacta cuando sea posible
|
||||||
|
- Para la versión 134.0.6998.88, usa ChromeDriver 134.0.6998.165 (la versión disponible más cercana)
|
||||||
|
- El número de versión principal debe coincidir (134 = 134)
|
||||||
|
|
||||||
|
### Matriz de compatibilidad de versiones
|
||||||
|
|
||||||
|
| Versión de Chrome | Versión de ChromeDriver | Estado |
|
||||||
|
|----------------|---------------------|---------|
|
||||||
|
| 134.0.6998.x | 134.0.6998.165 | ✅ Disponible |
|
||||||
|
| 133.0.6943.x | 133.0.6943.141 | ✅ Disponible |
|
||||||
|
| 132.0.6834.x | 132.0.6834.159 | ✅ Disponible |
|
||||||
|
|
||||||
|
*Para la compatibilidad más reciente, consulta el [Panel de Chrome for Testing](https://googlechromelabs.github.io/chrome-for-testing/)*
|
||||||
|
|
||||||
|
`Exception: Failed to initialize browser: Message: session not created: This version of ChromeDriver only supports Chrome version 113
|
||||||
|
Current browser version is 134.0.6998.89 with binary path`
|
||||||
|
|
||||||
|
Esto sucede si tu navegador y la versión de chromedriver no coinciden.
|
||||||
|
|
||||||
|
Necesitas navegar para descargar la versión más reciente:
|
||||||
|
|
||||||
|
https://developer.chrome.com/docs/chromedriver/downloads
|
||||||
|
|
||||||
|
Si usas Chrome versión 115 o superior, ve a:
|
||||||
|
|
||||||
|
https://googlechromelabs.github.io/chrome-for-testing/
|
||||||
|
|
||||||
|
y descarga la versión de chromedriver que coincida con tu sistema operativo.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Si esta sección está incompleta, abre un issue.
|
||||||
|
|
||||||
|
## Problemas de adaptadores de conexión
|
||||||
|
|
||||||
|
```
|
||||||
|
Exception: Provider lm-studio failed: HTTP request failed: No connection adapters were found for '127.0.0.1:1234/v1/chat/completions'` (nota: el puerto puede variar)
|
||||||
|
```
|
||||||
|
|
||||||
|
* **Causa:** Falta el prefijo `http://` en `provider_server_address` para `lm-studio` (u otro servidor local compatible con OpenAI similar) en `config.ini`, o apunta al puerto incorrecto.
|
||||||
|
* **Solución:**
|
||||||
|
* Asegúrate de que la dirección incluya `http://`. LM-Studio normalmente usa `http://127.0.0.1:1234` por defecto.
|
||||||
|
* `config.ini` correcto: `provider_server_address = http://127.0.0.1:1234` (o tu puerto real del servidor LM-Studio).
|
||||||
|
|
||||||
|
## URL base de SearxNG no proporcionada
|
||||||
|
|
||||||
|
```
|
||||||
|
raise ValueError("SearxNG base URL must be provided either as an argument or via the SEARXNG_BASE_URL environment variable.")
|
||||||
|
ValueError: SearxNG base URL must be provided either as an argument or via the SEARXNG_BASE_URL environment variable.`
|
||||||
|
```
|
||||||
|
|
||||||
|
Esto puede ocurrir si ejecutas el modo CLI con la URL base de searxng incorrecta.
|
||||||
|
|
||||||
|
SEARXNG_BASE_URL debe diferir según si ejecutas en Docker o en el host:
|
||||||
|
|
||||||
|
**Ejecutando en el host:** `SEARXNG_BASE_URL="http://localhost:8080"`
|
||||||
|
|
||||||
|
**Ejecutando completamente en Docker (interfaz web):** `SEARXNG_BASE_URL="http://searxng:8080"`
|
||||||
|
|
||||||
|
## FAQ
|
||||||
|
|
||||||
|
**P: ¿Qué hardware necesito?**
|
||||||
|
|
||||||
|
| Tamaño del modelo | GPU | Comentarios |
|
||||||
|
|-----------|--------|-----------------------------------------------------------|
|
||||||
|
| 7B | 8GB VRAM | ⚠️ No recomendado. Rendimiento pobre, alucinaciones frecuentes, los agentes de planificación pueden fallar. |
|
||||||
|
| 14B | 12 GB VRAM (ej: RTX 3060) | ✅ Utilizable para tareas simples. Puede tener dificultades con la navegación web y la planificación de tareas. |
|
||||||
|
| 32B | 24+ GB VRAM (ej: RTX 4090) | 🚀 Éxito en la mayoría de las tareas, aún puede tener dificultades con la planificación de tareas |
|
||||||
|
| 70B+ | 48+ GB VRAM | 💪 Excelente. Recomendado para casos de uso avanzados. |
|
||||||
|
|
||||||
|
**P: ¿Qué hago si encuentro errores?**
|
||||||
|
|
||||||
|
Asegúrate de que lo local esté ejecutándose (`ollama serve`), que tu `config.ini` coincida con tu proveedor y que las dependencias estén instaladas. Si nada funciona, no dudes en abrir un issue.
|
||||||
|
|
||||||
|
**P: ¿Realmente puede ejecutarse 100% localmente?**
|
||||||
|
|
||||||
|
Sí, con proveedores Ollama, lm-studio o server, todos los modelos de voz a texto, LLM y texto a voz se ejecutan localmente. Las opciones no locales (OpenAI u otras API) son opcionales.
|
||||||
|
|
||||||
|
**P: ¿Por qué debería usar AgenticSeek cuando tengo Manus?**
|
||||||
|
|
||||||
|
A diferencia de Manus, AgenticSeek prioriza la independencia de los sistemas externos, dándote más control, privacidad y evitando costos de API.
|
||||||
|
|
||||||
|
**P: ¿Quién está detrás de este proyecto?**
|
||||||
|
|
||||||
|
Este proyecto fue creado por mí, con dos amigos como mantenedores y contribuyentes de la comunidad de código abierto en GitHub. Solo somos individuos apasionados, no una startup, ni estamos afiliados a ninguna organización.
|
||||||
|
|
||||||
|
Cualquier cuenta de AgenticSeek en X además de mi cuenta personal (https://x.com/Martin993886460) es impostora.
|
||||||
|
|
||||||
|
## Contribuir
|
||||||
|
|
||||||
|
¡Buscamos desarrolladores para mejorar AgenticSeek! Revisa los issues abiertos o discusiones.
|
||||||
|
|
||||||
|
[Guía de contribución](./docs/CONTRIBUTING.md)
|
||||||
|
|
||||||
|
## Patrocinadores:
|
||||||
|
|
||||||
|
¿Quieres mejorar las capacidades de AgenticSeek con funciones como búsqueda de vuelos, planificación de viajes o obtención de las mejores ofertas de compras? Considera usar SerpApi para crear herramientas personalizadas que desbloqueen más funcionalidades al estilo Jarvis. Con SerpApi, puedes acelerar tu agente para tareas profesionales mientras mantienes el control total.
|
||||||
|
|
||||||
|
<a href="https://serpapi.com/"><img src="./media/banners/sponsor_banner_serpapi.png" height="350" alt="SerpApi Banner" ></a>
|
||||||
|
|
||||||
|
¡Consulta [Contributing.md](./docs/CONTRIBUTING.md) para aprender cómo integrar herramientas personalizadas!
|
||||||
|
|
||||||
|
### **Patrocinadores**:
|
||||||
|
|
||||||
|
- [tatra-labs](https://github.com/tatra-labs)
|
||||||
|
|
||||||
|
## Mantenedores:
|
||||||
|
|
||||||
|
> [Fosowl](https://github.com/Fosowl) | Hora de París
|
||||||
|
|
||||||
|
> [antoineVIVIES](https://github.com/antoineVIVIES) | Hora de Taipei
|
||||||
|
|
||||||
|
## Agradecimientos especiales:
|
||||||
|
|
||||||
|
> [tcsenpai](https://github.com/tcsenpai) y [plitc](https://github.com/plitc) por ayudar con la dockerización del backend
|
||||||
|
|
||||||
|
[](https://www.star-history.com/#Fosowl/agenticSeek&Date)
|
||||||
@@ -1,55 +1,52 @@
|
|||||||
|
# AgenticSeek : Une Alternative Privée et Locale à Manus
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img align="center" src="./media/whale_readme.jpg">
|
<img align="center" src="./media/agentic_seek_logo.png" width="300" height="300" alt="Agentic Seek Logo">
|
||||||
<p>
|
<p>
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
English | [中文](./README_CHS.md) | [繁體中文](./README_CHT.md) | [Français](./README_FR.md) | [日本語](./README_JP.md) | [Português (Brasil)](./README_PTBR.md) | [Español](./README_ES.md) | [Türkçe](./README_TR.md)
|
||||||
[English](./README.md) | [繁體中文](./README_CHT.md) | [日本語](./README_JP.md) | Français
|
|
||||||
|
|
||||||
# AgenticSeek: Une IA comme Manus mais à base d'agents DeepSeek R1 fonctionnant en local.
|
*Un assistant IA avec reconnaissance vocale qui est une **alternative 100% locale à Manus AI**, navigue de manière autonome sur le web, écrit du code et planifie des tâches tout en gardant toutes les données sur votre appareil. Conçu pour des modèles de raisonnement locaux, il fonctionne entièrement sur votre matériel, garantissant une confidentialité totale et zéro dépendance au cloud.*
|
||||||
|
|
||||||
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/8hGDaME3TC) [](https://x.com/Martin993886460) [](https://github.com/Fosowl/agenticSeek/stargazers)
|
||||||
|
|
||||||
[](https://fosowl.github.io/agenticSeek.html)  [](https://discord.gg/8hGDaME3TC) [](https://x.com/Martin993886460)
|
### Pourquoi choisir AgenticSeek ?
|
||||||
|
|
||||||
> 🛠️ **En cours de développement** – On cherche activement des contributeurs!
|
* 🔒 Totalement Local & Privé - Tout fonctionne sur votre machine, sans cloud, sans partage de données. Vos fichiers, conversations et recherches restent privés.
|
||||||
|
|
||||||
https://github.com/user-attachments/assets/4bd5faf6-459f-4f94-bd1d-238c4b331469
|
* 🌐 Navigation Web Intelligente - AgenticSeek peut naviguer sur Internet de manière autonome : rechercher, lire, extraire des informations, remplir des formulaires web, le tout sans intervention manuelle.
|
||||||
|
|
||||||
> *Recherche sur le web des activités à faire à Paris*
|
* 💻 Assistant de Programmation Autonome - Besoin de code ? Il peut écrire, déboguer et exécuter des programmes en Python, C, Go, Java et plus encore, sans supervision.
|
||||||
|
|
||||||
> *Code le jeu snake en python*
|
* 🧠 Sélection Intelligente d'Agents - Vous demandez, il choisit automatiquement le meilleur agent pour la tâche. Comme avoir une équipe d'experts toujours disponible.
|
||||||
|
|
||||||
> *J'aimerais que tu trouve une api météo et que tu me code une application qui affiche la météo à Toulouse*
|
* 📋 Planifie et Exécute des Tâches Complexes - De la planification de voyage aux projets complexes, il peut décomposer de grandes tâches en étapes et les compléter en utilisant plusieurs agents IA.
|
||||||
|
|
||||||
|
* 🎙️ Prise en Charge Vocale - Voix claire, rapide et futuriste avec reconnaissance vocale, vous permettant de converser comme avec votre IA personnelle de film de science-fiction. (En développement)
|
||||||
|
|
||||||
|
### **Démo**
|
||||||
|
|
||||||
## Fonctionnalités:
|
> *Peux-tu rechercher le projet agenticSeek, apprendre quelles compétences sont nécessaires, puis ouvrir CV_candidates.zip et me dire lesquels correspondent le mieux au projet ?*
|
||||||
|
|
||||||
- **100% Local**: Fonctionne en local sur votre PC. Vos données restent les vôtres.
|
https://github.com/user-attachments/assets/b8ca60e9-7b3b-4533-840e-08f9ac426316
|
||||||
|
|
||||||
- **Accès à vos Fichiers**: Utilise bash pour naviguer et manipuler vos fichiers.
|
Avertissement : Cette démonstration et tous les fichiers qui apparaissent (ex: CV_candidates.zip) sont entièrement fictifs. Nous ne sommes pas une entreprise, nous recherchons des contributeurs open source, pas des candidats.
|
||||||
|
|
||||||
- **Codage semi-autonome**: Peut écrire, déboguer et exécuter du code en Python, C, Golang et d'autres langages à venir.
|
> 🛠⚠️️ **Travail Actif en Cours**
|
||||||
|
|
||||||
- **Routage d'Agent**: Sélectionne automatiquement l’agent approprié pour la tâche.
|
> 🙏 Ce projet a commencé comme un projet parallèle et n'a ni feuille de route ni financement. Il a grandi bien au-delà des attentes en apparaissant dans GitHub Trending. Les contributions, commentaires et de la patience sont profondément appréciés.
|
||||||
|
|
||||||
- **Planification**: Pour les taches complexe utilise plusieurs agents.
|
## Prérequis
|
||||||
|
|
||||||
- **Navigation Web Autonome**: Navigation web autonome.
|
Avant de commencer, assurez-vous d'avoir installé :
|
||||||
|
|
||||||
- **Memoire efficace**: Gestion efficace de la mémoire et des sessions.
|
* **Git:** Pour cloner le dépôt. [Télécharger Git](https://git-scm.com/downloads)
|
||||||
|
* **Python 3.10.x:** Python 3.10.x est fortement recommandé. D'autres versions peuvent causer des erreurs de dépendance. [Télécharger Python 3.10](https://www.python.org/downloads/release/python-3100/) (sélectionnez la version 3.10.x).
|
||||||
|
* **Docker Engine & Docker Compose:** Pour exécuter des services empaquetés comme SearxNG.
|
||||||
|
* Installer Docker Desktop (inclut Docker Compose V2): [Windows](https://docs.docker.com/desktop/install/windows-install/) | [Mac](https://docs.docker.com/desktop/install/mac-install/) | [Linux](https://docs.docker.com/desktop/install/linux-install/)
|
||||||
|
* Ou installer Docker Engine et Docker Compose séparément sur Linux: [Docker Engine](https://docs.docker.com/engine/install/) | [Docker Compose](https://docs.docker.com/compose/install/) (assurez-vous d'installer Compose V2, par exemple `sudo apt-get install docker-compose-plugin`).
|
||||||
|
|
||||||
---
|
### 1. **Cloner le dépôt et configurer**
|
||||||
|
|
||||||
## **Installation**
|
|
||||||
|
|
||||||
Assurez-vous d’avoir installé le pilote Chrome, Docker et Python 3.10.
|
|
||||||
|
|
||||||
Nous vous conseillons fortement d'utiliser exactement Python 3.10 pour l'installation. Des erreurs de dépendances pourraient survenir autrement.
|
|
||||||
|
|
||||||
Pour les problèmes liés au pilote Chrome, consultez la section Chromedriver.
|
|
||||||
|
|
||||||
### 1️⃣ Cloner le repo et configurer
|
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
git clone https://github.com/Fosowl/agenticSeek.git
|
git clone https://github.com/Fosowl/agenticSeek.git
|
||||||
@@ -57,245 +54,310 @@ cd agenticSeek
|
|||||||
mv .env.example .env
|
mv .env.example .env
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2 **Créer un environnement virtuel**
|
### 2. Modifier le contenu du fichier .env
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
python3 -m venv agentic_seek_env
|
SEARXNG_BASE_URL="http://searxng:8080" # Si vous exécutez en mode CLI sur l'hôte, utilisez http://127.0.0.1:8080
|
||||||
source agentic_seek_env/bin/activate
|
REDIS_BASE_URL="redis://redis:6379/0"
|
||||||
# Sur Windows: agentic_seek_env\Scripts\activate
|
WORK_DIR="/Users/mlg/Documents/workspace_for_ai"
|
||||||
|
OLLAMA_PORT="11434"
|
||||||
|
LM_STUDIO_PORT="1234"
|
||||||
|
CUSTOM_ADDITIONAL_LLM_PORT="11435"
|
||||||
|
OPENAI_API_KEY='optional'
|
||||||
|
DEEPSEEK_API_KEY='optional'
|
||||||
|
OPENROUTER_API_KEY='optional'
|
||||||
|
TOGETHER_API_KEY='optional'
|
||||||
|
GOOGLE_API_KEY='optional'
|
||||||
|
ANTHROPIC_API_KEY='optional'
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3️⃣ **Installation**
|
Mettez à jour le fichier `.env` selon vos besoins :
|
||||||
|
|
||||||
**Automatique:**
|
- **SEARXNG_BASE_URL**: Gardez inchangé sauf si vous exécutez en mode CLI sur l'hôte.
|
||||||
|
- **REDIS_BASE_URL**: Gardez inchangé
|
||||||
|
- **WORK_DIR**: Chemin vers le répertoire de travail local. AgenticSeek pourra lire et interagir avec ces fichiers.
|
||||||
|
- **OLLAMA_PORT**: Numéro de port pour le service Ollama.
|
||||||
|
- **LM_STUDIO_PORT**: Numéro de port pour le service LM Studio.
|
||||||
|
- **CUSTOM_ADDITIONAL_LLM_PORT**: Port pour tout service LLM personnalisé supplémentaire.
|
||||||
|
|
||||||
|
**Les clés API sont complètement optionnelles pour ceux qui choisissent d'exécuter LLM localement, ce qui est l'objectif principal de ce projet. Laissez-les vides si vous avez du matériel suffisant.**
|
||||||
|
|
||||||
|
### 3. **Démarrer Docker**
|
||||||
|
|
||||||
|
Assurez-vous que Docker est installé et fonctionne sur votre système. Vous pouvez démarrer Docker avec les commandes suivantes :
|
||||||
|
|
||||||
|
- **Linux/macOS:**
|
||||||
|
Ouvrez un terminal et exécutez :
|
||||||
|
```sh
|
||||||
|
sudo systemctl start docker
|
||||||
|
```
|
||||||
|
Ou démarrez Docker Desktop depuis le menu des applications, s'il est installé.
|
||||||
|
|
||||||
|
- **Windows:**
|
||||||
|
Démarrez Docker Desktop depuis le menu Démarrer.
|
||||||
|
|
||||||
|
Vous pouvez vérifier si Docker fonctionne en exécutant :
|
||||||
```sh
|
```sh
|
||||||
./install.sh
|
docker info
|
||||||
```
|
```
|
||||||
|
Si vous voyez des informations sur votre installation Docker, cela fonctionne correctement.
|
||||||
|
|
||||||
**Manuel:**
|
Consultez la [Liste des fournisseurs locaux](#liste-des-fournisseurs-locaux) ci-dessous pour un résumé.
|
||||||
|
|
||||||
**Note : Pour tous les systèmes d'exploitation, assurez-vous que le ChromeDriver que vous installez correspond à la version de Chrome installée. Exécutez `google-chrome --version`. Consultez les problèmes connus si vous avez Chrome >135**
|
Prochaine étape: [Exécuter AgenticSeek localement](#démarrer-les-services-et-exécuter)
|
||||||
|
|
||||||
- *Linux*:
|
*Si vous rencontrez des problèmes, consultez la section [Dépannage](#dépannage).*
|
||||||
|
*Si votre matériel ne peut pas exécuter LLM localement, consultez [Configuration pour exécuter avec une API](#configuration-pour-exécuter-avec-une-api).*
|
||||||
|
*Pour des explications détaillées de `config.ini`, consultez la [section Configuration](#configuration).*
|
||||||
|
|
||||||
Mettre à jour la liste des paquets : `sudo apt update`
|
---
|
||||||
|
|
||||||
Installer les dépendances : `sudo apt install -y alsa-utils portaudio19-dev python3-pyaudio libgtk-3-dev libnotify-dev libgconf-2-4 libnss3 libxss1`
|
## Configuration pour exécuter LLM localement sur votre machine
|
||||||
|
|
||||||
Installer ChromeDriver correspondant à la version de votre navigateur Chrome :
|
**Exigences matérielles:**
|
||||||
`sudo apt install -y chromium-chromedriver`
|
|
||||||
|
|
||||||
Installer les prérequis : `pip3 install -r requirements.txt`
|
Pour exécuter LLM localement, vous aurez besoin de matériel suffisant. Au minimum, une GPU capable d'exécuter Magistral, Qwen ou Deepseek 14B est requise. Consultez la FAQ pour des recommandations détaillées de modèle/performance.
|
||||||
|
|
||||||
- *macOS*:
|
**Configurez votre fournisseur local**
|
||||||
|
|
||||||
Mettre à jour brew : `brew update`
|
Démarrez votre fournisseur local, par exemple avec ollama:
|
||||||
|
|
||||||
Installer chromedriver : `brew install --cask chromedriver`
|
|
||||||
|
|
||||||
Installer portaudio : `brew install portaudio`
|
|
||||||
|
|
||||||
Mettre à jour pip : `python3 -m pip install --upgrade pip`
|
|
||||||
|
|
||||||
Mettre à jour wheel : `pip3 install --upgrade setuptools wheel`
|
|
||||||
|
|
||||||
Installer les prérequis : `pip3 install -r requirements.txt`
|
|
||||||
|
|
||||||
- *Windows*:
|
|
||||||
|
|
||||||
Installer pyreadline3 : `pip install pyreadline3`
|
|
||||||
|
|
||||||
Installer portaudio manuellement (par exemple, via vcpkg ou des binaires précompilés) puis exécutez : `pip install pyaudio`
|
|
||||||
|
|
||||||
Télécharger et installer chromedriver manuellement depuis : https://sites.google.com/chromium.org/driver/getting-started
|
|
||||||
|
|
||||||
Placez chromedriver dans un répertoire inclus dans votre PATH.
|
|
||||||
|
|
||||||
Installer les prérequis : `pip3 install -r requirements.txt`
|
|
||||||
|
|
||||||
|
|
||||||
## Faire fonctionner sur votre machine
|
|
||||||
|
|
||||||
**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.**
|
|
||||||
|
|
||||||
Lancer votre provider local, par exemple avec ollama:
|
|
||||||
```sh
|
```sh
|
||||||
ollama serve
|
ollama serve
|
||||||
```
|
```
|
||||||
|
|
||||||
**Configurer le config.ini**
|
Consultez la liste des fournisseurs locaux pris en charge ci-dessous.
|
||||||
|
|
||||||
Modifiez le fichier config.ini pour définir provider_name sur un fournisseur supporté et provider_model sur un LLM compatible avec votre fournisseur. Nous recommandons des modèles de raisonnement comme *Qwen* ou *Deepseek*.
|
**Mettre à jour config.ini**
|
||||||
|
|
||||||
Consultez la section **FAQ** à la fin du README pour connaître le matériel requis.
|
Changez le fichier config.ini pour définir provider_name sur un fournisseur pris en charge et provider_model sur un LLM pris en charge par votre fournisseur. Nous recommandons des modèles de raisonnement comme *Magistral* ou *Deepseek*.
|
||||||
|
|
||||||
|
Consultez la **FAQ** à la fin du README pour le matériel nécessaire.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
[MAIN]
|
[MAIN]
|
||||||
is_local = True # Si vous exécutez localement ou avec un fournisseur distant.
|
is_local = True # Que vous exécutiez localement ou avec un fournisseur distant.
|
||||||
provider_name = ollama # ou lm-studio, openai, etc..
|
provider_name = ollama # ou lm-studio, openai, etc.
|
||||||
provider_model = deepseek-r1:14b # choisissez un modèle adapté à votre matériel
|
provider_model = deepseek-r1:14b # choisissez un modèle compatible avec votre matériel
|
||||||
provider_server_address = 127.0.0.1:11434
|
provider_server_address = 127.0.0.1:11434
|
||||||
agent_name = Jarvis # nom de votre IA
|
agent_name = Jarvis # le nom de votre IA
|
||||||
recover_last_session = True # récupérer ou non la session précédente
|
recover_last_session = True # récupérer la session précédente
|
||||||
save_session = True # mémoriser ou non la session actuelle
|
save_session = True # mémoriser la session actuelle
|
||||||
speak = True # synthèse vocale
|
speak = False # texte vers parole
|
||||||
listen = False # reconnaissance vocale, uniquement pour CLI
|
listen = False # parole vers texte, uniquement pour CLI, expérimental
|
||||||
work_dir = /Users/mlg/Documents/workspace # L'espace de travail pour AgenticSeek.
|
jarvis_personality = False # utiliser une personnalité plus "Jarvis" (expérimental)
|
||||||
jarvis_personality = False # Utiliser une personnalité plus "Jarvis", non recommandé avec des petits modèles
|
languages = en zh # Liste des langues, TTS utilisera la première de la liste par défaut
|
||||||
languages = en fr # Liste des langages, la synthèse vocale utilisera par défaut la première langue de la liste
|
|
||||||
[BROWSER]
|
[BROWSER]
|
||||||
headless_browser = True # Utiliser ou non le navigateur sans interface graphique, recommandé uniquement avec l'interface web.
|
headless_browser = True # garder inchangé sauf si vous utilisez CLI sur l'hôte.
|
||||||
stealth_mode = True # Utiliser selenium non détectable pour réduire la détection du navigateur
|
stealth_mode = True # Utilise selenium indétectable pour réduire la détection du navigateur
|
||||||
```
|
```
|
||||||
|
|
||||||
Remarque : Certains fournisseurs (ex : lm-studio) nécessitent `http://` devant l'adresse IP. Par exemple `http://127.0.0.1:1234`
|
**Avertissement**:
|
||||||
|
|
||||||
|
- Le format du fichier `config.ini` ne prend pas en charge les commentaires.
|
||||||
|
Ne copiez et collez pas directement la configuration d'exemple, car les commentaires causeront des erreurs. Modifiez plutôt manuellement le fichier `config.ini` avec votre configuration souhaitée, sans commentaires.
|
||||||
|
|
||||||
|
- *NE* définissez PAS provider_name sur `openai` si vous utilisez LM-studio pour exécuter LLM. Utilisez `lm-studio`.
|
||||||
|
|
||||||
**Liste des provideurs locaux**
|
- Certains fournisseurs (ex: lm-studio) nécessitent `http://` avant l'IP. Exemple: `http://127.0.0.1:1234`
|
||||||
|
|
||||||
|
**Liste des fournisseurs locaux**
|
||||||
|
|
||||||
| Fournisseur | Local ? | Description |
|
| Fournisseur | Local ? | Description |
|
||||||
|-------------|---------|-----------------------------------------------------------|
|
|-----------|--------|-----------------------------------------------------------|
|
||||||
| ollama | Oui | Exécutez des LLM localement avec facilité en utilisant ollama comme fournisseur LLM |
|
| ollama | Oui | Exécute LLM localement facilement en utilisant ollama |
|
||||||
| lm-studio | Oui | Exécutez un LLM localement avec LM studio (définissez `provider_name` sur `lm-studio`) |
|
| lm-studio | Oui | Exécute LLM localement avec LM studio (définir `provider_name` = `lm-studio`)|
|
||||||
| openai | Oui | Utilisez une API local compatible avec openai |
|
| openai | Oui | Utilise une API compatible avec openai (ex: serveur llama.cpp) |
|
||||||
|
|
||||||
|
Prochaine étape: [Démarrer les services et exécuter AgenticSeek](#démarrer-les-services-et-exécuter)
|
||||||
|
|
||||||
|
*Si vous rencontrez des problèmes, consultez la section [Dépannage](#dépannage).*
|
||||||
|
*Si votre matériel ne peut pas exécuter LLM localement, consultez [Configuration pour exécuter avec une API](#configuration-pour-exécuter-avec-une-api).*
|
||||||
|
*Pour des explications détaillées de `config.ini`, consultez la [section Configuration](#configuration).*
|
||||||
|
|
||||||
|
## Configuration pour exécuter avec une API
|
||||||
|
|
||||||
|
Cette configuration utilise des fournisseurs de LLM externes basés sur le cloud. Vous devrez obtenir des clés API du service choisi.
|
||||||
|
|
||||||
|
**1. Choisissez un fournisseur d'API et obtenez une clé API:**
|
||||||
|
|
||||||
|
Consultez la [Liste des fournisseurs d'API](#liste-des-fournisseurs-dapi) ci-dessous. Visitez leurs sites web pour vous inscrire et obtenir des clés API.
|
||||||
|
|
||||||
|
**2. Définissez votre clé API comme variable d'environnement:**
|
||||||
|
|
||||||
|
* **Linux/macOS:**
|
||||||
|
Ouvrez un terminal et utilisez la commande `export`. Il est préférable de l'ajouter au fichier de configuration de votre shell (ex: `~/.bashrc`, `~/.zshrc`) pour qu'elle soit persistante.
|
||||||
|
```sh
|
||||||
|
export PROVIDER_API_KEY="your_api_key_here"
|
||||||
|
# Remplacez PROVIDER_API_KEY par le nom de variable spécifique, ex: OPENAI_API_KEY, GOOGLE_API_KEY
|
||||||
|
```
|
||||||
|
Exemple TogetherAI:
|
||||||
|
```sh
|
||||||
|
export TOGETHER_API_KEY="xxxxxxxxxxxxxxxxxxxxxx"
|
||||||
|
```
|
||||||
|
* **Windows:**
|
||||||
|
* **Invite de commandes (temporaire pour la session actuelle):**
|
||||||
|
```cmd
|
||||||
|
set PROVIDER_API_KEY=your_api_key_here
|
||||||
|
```
|
||||||
|
* **PowerShell (temporaire pour la session actuelle):**
|
||||||
|
```powershell
|
||||||
|
$env:PROVIDER_API_KEY="your_api_key_here"
|
||||||
|
```
|
||||||
|
* **Permanent:** Recherchez "variables d'environnement" dans la barre de recherche Windows, cliquez sur "Modifier les variables d'environnement système", puis sur le bouton "Variables d'environnement...". Ajoutez une nouvelle variable utilisateur avec le nom approprié (ex: `OPENAI_API_KEY`) et votre clé comme valeur.
|
||||||
|
|
||||||
|
*(Pour plus de détails, consultez la FAQ: [Comment configurer une clé API ?](#comment-configurer-une-clé-api)).*
|
||||||
|
|
||||||
|
|
||||||
### **Démarrer les services & Exécuter**
|
**3. Mettez à jour `config.ini`:**
|
||||||
|
```ini
|
||||||
|
[MAIN]
|
||||||
|
is_local = False
|
||||||
|
provider_name = openai # ou google, deepseek, togetherAI, huggingface
|
||||||
|
provider_model = gpt-3.5-turbo # ou gemini-1.5-flash, deepseek-chat, mistralai/Mixtral-8x7B-Instruct-v0.1, etc.
|
||||||
|
provider_server_address = # Lorsque is_local = False, généralement ignoré ou peut être laissé vide pour la plupart des API
|
||||||
|
# ... autres configurations ...
|
||||||
|
```
|
||||||
|
*Avertissement:* Assurez-vous qu'il n'y a pas d'espaces à la fin des valeurs dans config.
|
||||||
|
|
||||||
|
**Liste des fournisseurs d'API**
|
||||||
|
|
||||||
|
| Fournisseur | `provider_name` | Local ? | Description | Lien de clé API (exemple) |
|
||||||
|
|--------------|-----------------|--------|---------------------------------------------------|---------------------------------------------|
|
||||||
|
| OpenAI | `openai` | Non | Utilise les modèles ChatGPT via l'API OpenAI. | [platform.openai.com/signup](https://platform.openai.com/signup) |
|
||||||
|
| Google Gemini| `google` | Non | Utilise les modèles Google Gemini via Google AI Studio. | [aistudio.google.com/keys](https://aistudio.google.com/keys) |
|
||||||
|
| Deepseek | `deepseek` | Non | Utilise les modèles Deepseek via leur API. | [platform.deepseek.com](https://platform.deepseek.com) |
|
||||||
|
| Hugging Face | `huggingface` | Non | Utilise les modèles du Hugging Face Inference API. | [huggingface.co/settings/tokens](https://huggingface.co/settings/tokens) |
|
||||||
|
| TogetherAI | `togetherAI` | Non | Utilise divers modèles open source via l'API TogetherAI.| [api.together.ai/settings/api-keys](https://api.together.ai/settings/api-keys) |
|
||||||
|
|
||||||
|
*Note:*
|
||||||
|
* Nous ne recommandons pas d'utiliser `gpt-4o` ou d'autres modèles OpenAI pour la navigation web complexe et la planification de tâches, car l'optimisation actuelle des prompts cible des modèles comme Deepseek.
|
||||||
|
* Les tâches de codage/bash peuvent échouer avec Gemini, car il a tendance à ignorer notre format de prompt optimisé pour Deepseek r1.
|
||||||
|
* Lorsque `is_local = False`, `provider_server_address` dans `config.ini` n'est généralement pas utilisé, car les endpoints d'API sont généralement gérés par les bibliothèques du fournisseur correspondant.
|
||||||
|
|
||||||
|
Prochaine étape: [Démarrer les services et exécuter AgenticSeek](#démarrer-les-services-et-exécuter)
|
||||||
|
|
||||||
|
*Si vous rencontrez des problèmes, consultez la section **Problèmes connus***
|
||||||
|
|
||||||
|
*Pour des explications détaillées du fichier de configuration, consultez la **section Configuration**.*
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Démarrer les services et exécuter
|
||||||
|
|
||||||
|
Par défaut, AgenticSeek s'exécute entièrement dans Docker.
|
||||||
|
|
||||||
|
**Option 1:** Exécuter dans Docker avec interface web:
|
||||||
|
|
||||||
|
Démarrez les services nécessaires. Cela démarrera tous les services du docker-compose.yml, y compris:
|
||||||
|
- searxng
|
||||||
|
- redis (requis pour searxng)
|
||||||
|
- frontend
|
||||||
|
- backend (si vous utilisez `full` pour l'interface web)
|
||||||
|
|
||||||
Activez votre environnement Python si nécessaire.
|
|
||||||
```sh
|
```sh
|
||||||
source agentic_seek_env/bin/activate
|
./start_services.sh full # MacOS
|
||||||
|
start start_services.cmd full # Windows
|
||||||
```
|
```
|
||||||
|
|
||||||
Démarrez les services requis. Cela lancera tous les services définis dans le fichier docker-compose.yml, y compris :
|
**Avertissement:** Cette étape téléchargera et chargera toutes les images Docker, ce qui peut prendre jusqu'à 30 minutes. Après avoir démarré les services, attendez que le service backend soit complètement opérationnel (vous devriez voir **backend: "GET /health HTTP/1.1" 200 OK** dans les logs) avant d'envoyer des messages. Lors du premier démarrage, le service backend peut prendre 5 minutes pour démarrer.
|
||||||
|
|
||||||
|
Allez à `http://localhost:3000/` et vous devriez voir l'interface web.
|
||||||
|
|
||||||
|
*Dépannage du démarrage des services:* Si ces scripts échouent, assurez-vous que Docker Engine fonctionne et que Docker Compose (V2, `docker compose`) est correctement installé. Vérifiez les messages d'erreur dans la sortie du terminal. Consultez [FAQ: Aide ! J'obtiens des erreurs lors de l'exécution d'AgenticSeek ou de ses scripts.](#faq-dépannage)
|
||||||
|
|
||||||
|
**Option 2:** Mode CLI:
|
||||||
|
|
||||||
|
Pour exécuter avec l'interface CLI, vous devez installer les packages sur l'hôte:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
./install.sh
|
||||||
|
./install.bat # windows
|
||||||
|
```
|
||||||
|
|
||||||
|
Ensuite, vous devez changer SEARXNG_BASE_URL dans `config.ini` en:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
SEARXNG_BASE_URL="http://localhost:8080"
|
||||||
|
```
|
||||||
|
|
||||||
|
Démarrez les services nécessaires. Cela démarrera certains services du docker-compose.yml, y compris:
|
||||||
- searxng
|
- searxng
|
||||||
- redis (nécessaire pour searxng)
|
- redis (requis pour searxng)
|
||||||
- frontend
|
- frontend
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
sudo ./start_services.sh # MacOS
|
./start_services.sh # MacOS
|
||||||
start ./start_services.cmd # Windows
|
start start_services.cmd # Windows
|
||||||
```
|
```
|
||||||
|
|
||||||
**Option 1 :** Exécuter avec l'interface CLI.
|
Exécutez: uv run: `uv run python -m ensurepip` pour vous assurer que uv a pip activé.
|
||||||
|
|
||||||
```sh
|
Utilisez CLI: `uv run cli.py`
|
||||||
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 **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 **Configuration** pour une explication détaillée du fichier de configuration.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Utilisation
|
## Utilisation
|
||||||
|
|
||||||
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.
|
Assurez-vous que les services fonctionnent avec `./start_services.sh full` puis allez à `localhost:3000` pour l'interface web.
|
||||||
|
|
||||||
**CLI:**
|
Vous pouvez également utiliser la parole vers texte en définissant `listen = True`. Uniquement pour le mode CLI.
|
||||||
Vous verrez un prompt : ">>> "
|
|
||||||
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`.
|
|
||||||
|
|
||||||
**Interface:**
|
Pour quitter, dites/tapez simplement `goodbye`.
|
||||||
|
|
||||||
Assurez-vous d'avoir bien démarré le backend avec `python3 api.py`.
|
Quelques exemples d'utilisation:
|
||||||
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 :
|
> *Fais un jeu de serpent en python !*
|
||||||
|
|
||||||
### Programmation
|
> *Recherche sur le web les meilleurs cafés à Rennes, France, et sauvegarde une liste de trois avec leurs adresses dans rennes_cafes.txt.*
|
||||||
|
|
||||||
> *Aide-moi avec la multiplication de matrices en Golang*
|
> *Écris un programme Go pour calculer la factorielle d'un nombre, sauvegarde-le comme factorial.go dans ton workspace*
|
||||||
|
|
||||||
> *Initalize un nouveau project python, setup le readme, gitignore etc.. et fait un premier commit*
|
> *Recherche dans le dossier summer_pictures tous les fichiers JPG, renomme-les avec la date d'aujourd'hui et sauvegarde la liste des fichiers renommés dans photos_list.txt*
|
||||||
|
|
||||||
> *Fais un jeu snake en Python*
|
> *Recherche en ligne les films de science-fiction populaires de 2024 et choisis-en trois à regarder ce soir. Sauvegarde la liste dans movie_night.txt.*
|
||||||
|
|
||||||
### Recherche web
|
> *Recherche sur le web les derniers articles d'actualité sur l'IA de 2025, sélectionne-en trois et écris un script Python pour extraire les titres et résumés. Sauvegarde le script comme news_scraper.py et les résumés dans ai_news.txt dans /home/projects*
|
||||||
|
|
||||||
> *Fais une recherche sur le web pour trouver des startups technologiques au Japon qui travaillent sur des recherches avancées en IA*
|
> *Vendredi, recherche sur le web une API gratuite de prix d'actions, inscris-toi avec supersuper7434567@gmail.com et écris un script Python pour obtenir les prix quotidiens de Tesla en utilisant l'API, en sauvegardant les résultats dans stock_prices.csv*
|
||||||
|
|
||||||
> *Peux-tu trouver sur internet qui a créé agenticSeek ?*
|
*Notez que le remplissage de formulaires est toujours expérimental et peut échouer.*
|
||||||
|
|
||||||
> *Peux-tu trouver sur quel site je peux acheter une RTX 4090 à bas prix ?*
|
|
||||||
|
|
||||||
### Fichier
|
|
||||||
|
|
||||||
> *Hé, peux-tu trouver où est contrat.pdf ? Je l’ai perdu*
|
|
||||||
|
|
||||||
> *Montre-moi combien d’espace il me reste sur mon disque*
|
|
||||||
|
|
||||||
> *Trouve et lis le fichier README.md et suis les instructions d’installation*
|
|
||||||
|
|
||||||
### Conversation
|
|
||||||
|
|
||||||
> *Parle-moi de la France*
|
|
||||||
|
|
||||||
> *Quel est le sens de la vie ?*
|
|
||||||
|
|
||||||
> *Donne moi une recette simple pour ce midi j'ai pas d'inspi*
|
|
||||||
|
|
||||||
Après avoir saisi votre requête, AgenticSeek attribuera le meilleur agent pour la tâche.
|
Après avoir saisi votre requête, AgenticSeek attribuera le meilleur agent pour la tâche.
|
||||||
|
|
||||||
Le système de routage des agents peut parfois ne pas toujours attribuer le bon agent en fonction de votre requête.
|
Comme il s'agit d'un prototype initial, le système de routage des agents peut ne pas toujours attribuer l'agent correct à votre requête.
|
||||||
|
|
||||||
Par conséquent, vous devez être assez explicite sur ce que vous voulez et sur la manière dont l’IA doit procéder. Par exemple, si vous voulez qu’elle effectue une recherche sur le web, ne dites pas :
|
Par conséquent, soyez très explicite sur ce que vous voulez et comment l'IA pourrait procéder, par exemple si vous voulez qu'elle effectue une recherche web, ne dites pas:
|
||||||
|
|
||||||
Connait-tu de bons pays pour voyager seul ?
|
`Connais-tu de bons pays pour voyager seul ?`
|
||||||
|
|
||||||
Dites plutôt :
|
Dites plutôt:
|
||||||
|
|
||||||
Fait une recherche sur le web, quels sont les meilleurs pays pour voyager seul?
|
`Effectue une recherche web et découvre quels sont les meilleurs pays pour voyager seul`
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## **Exécuter le LLM sur votre propre serveur**
|
## **Configuration pour exécuter LLM sur votre propre serveur**
|
||||||
|
|
||||||
Si vous disposez d’un ordinateur puissant ou d’un serveur que vous voulez utiliser, mais que vous souhaitez y accéder depuis votre ordinateur portable, vous avez la possibilité d’exécuter le LLM sur un serveur distant.
|
Si vous avez un ordinateur puissant ou un serveur auquel vous pouvez accéder, mais que vous voulez l'utiliser depuis votre ordinateur portable, vous pouvez choisir d'exécuter le LLM sur un serveur distant en utilisant notre serveur llm personnalisé.
|
||||||
|
|
||||||
### 1️⃣ **Configurer et démarrer les scripts du serveur**
|
Sur votre "serveur" qui exécutera le modèle d'IA, obtenez l'adresse IP
|
||||||
|
|
||||||
Sur votre "serveur" qui exécutera le modèle IA, obtenez l’adresse IP
|
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
ip a | grep "inet " | grep -v 127.0.0.1 | awk '{print $2}' | cut -d/ -f1
|
ip a | grep "inet " | grep -v 127.0.0.1 | awk '{print $2}' | cut -d/ -f1 # IP locale
|
||||||
|
curl https://ipinfo.io/ip # IP publique
|
||||||
```
|
```
|
||||||
|
|
||||||
Remarque : Pour Windows ou macOS, utilisez respectivement ipconfig ou ifconfig pour trouver l’adresse IP.
|
Note: Pour Windows ou macOS, utilisez ipconfig ou ifconfig pour trouver l'adresse IP.
|
||||||
|
|
||||||
Clonez le dépôt et entrez dans le dossier server/.
|
|
||||||
|
|
||||||
|
Clonez le dépôt et entrez dans le dossier `server/`.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
git clone --depth 1 https://github.com/Fosowl/agenticSeek.git
|
git clone --depth 1 https://github.com/Fosowl/agenticSeek.git
|
||||||
cd agenticSeek/server/
|
cd agenticSeek/llm_server/
|
||||||
```
|
```
|
||||||
|
|
||||||
Installez les dépendances spécifiques au serveur :
|
Installez les exigences spécifiques au serveur:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
pip3 install -r requirements.txt
|
pip3 install -r requirements.txt
|
||||||
@@ -307,192 +369,314 @@ Exécutez le script du serveur.
|
|||||||
python3 app.py --provider ollama --port 3333
|
python3 app.py --provider ollama --port 3333
|
||||||
```
|
```
|
||||||
|
|
||||||
Vous avez le choix entre utiliser ollama et llamacpp comme service LLM.
|
Vous pouvez choisir d'utiliser `ollama` et `llamacpp` comme service LLM.
|
||||||
|
|
||||||
### 2️⃣ **Lancer**
|
Maintenant sur votre ordinateur personnel:
|
||||||
|
|
||||||
Maintenant, sur votre ordinateur personnel :
|
Changez le fichier `config.ini` pour définir `provider_name` sur `server` et `provider_model` sur `deepseek-r1:xxb`.
|
||||||
|
Définissez `provider_server_address` sur l'adresse IP de la machine qui exécutera le modèle.
|
||||||
Modifiez le fichier config.ini pour définir provider_name sur server et provider_model sur deepseek-r1:14b.
|
|
||||||
|
|
||||||
Définissez provider_server_address sur l’adresse IP de la machine qui exécutera le modèle.
|
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
[MAIN]
|
[MAIN]
|
||||||
is_local = False
|
is_local = False
|
||||||
provider_name = server
|
provider_name = server
|
||||||
provider_model = deepseek-r1:14b
|
provider_model = deepseek-r1:70b
|
||||||
provider_server_address = x.x.x.x:3333
|
provider_server_address = http://x.x.x.x:3333
|
||||||
```
|
```
|
||||||
|
|
||||||
Ensuite, exécutez avec le CLI ou l'interface graphique comme expliqué dans la section pour les fournisseurs locaux.
|
Prochaine étape: [Démarrer les services et exécuter AgenticSeek](#démarrer-les-services-et-exécuter)
|
||||||
|
|
||||||
## **Exécuter avec une API externe**
|
---
|
||||||
|
|
||||||
AVERTISSEMENT : Assurez-vous qu’il n’y a pas d’espace en fin de ligne dans la configuration.
|
## Parole vers Texte
|
||||||
|
|
||||||
```sh
|
Avertissement: La speech-to-text ne fonctionne qu'en mode CLI pour le moment.
|
||||||
[MAIN]
|
|
||||||
is_local = False
|
Notez que la parole vers texte ne fonctionne qu'en anglais pour le moment.
|
||||||
provider_name = openai
|
|
||||||
provider_model = gpt-4o
|
La fonctionnalité de parole vers texte est désactivée par défaut. Pour l'activer, définissez listen sur True dans le fichier config.ini:
|
||||||
provider_server_address = 127.0.0.1:5000 # n'importe pas
|
|
||||||
|
```
|
||||||
|
listen = True
|
||||||
```
|
```
|
||||||
|
|
||||||
**Liste de provideurs API**
|
Lorsqu'elle est activée, la fonction de parole vers texte écoute un mot-clé de déclenchement, qui est le nom de l'agent, avant de traiter votre entrée. Vous pouvez personnaliser le nom de l'agent en mettant à jour la valeur `agent_name` dans *config.ini*:
|
||||||
| Fournisseur | Local ? | Description |
|
|
||||||
|--------------|---------|-----------------------------------------------------------|
|
|
||||||
| openai | Non | Utilise l'API ChatGPT |
|
|
||||||
| deepseek-api | Non | API Deepseek (non privé) |
|
|
||||||
| huggingface | Non | API Hugging-Face (non privé) |
|
|
||||||
| togetherAI | Non | Utilise l'API Together AI (non privé) |
|
|
||||||
| google | Non | Utilise l'API Google Gemini (non privé) |
|
|
||||||
|
|
||||||
Ensuite, exécutez avec le CLI ou l'interface graphique comme expliqué dans la section pour les fournisseurs locaux.
|
```
|
||||||
|
agent_name = Friday
|
||||||
|
```
|
||||||
|
|
||||||
## Config
|
Pour une meilleure reconnaissance, nous recommandons d'utiliser un nom commun en anglais comme "John" ou "Emma" comme nom d'agent.
|
||||||
|
|
||||||
Exemple de configuration :
|
Une fois que vous voyez la transcription commencer à apparaître, dites le nom de l'agent à haute voix pour le réveiller (ex: "Friday").
|
||||||
|
|
||||||
|
Dites votre requête clairement.
|
||||||
|
|
||||||
|
Terminez votre demande par une phrase de confirmation pour indiquer au système de continuer. Les exemples de phrases de confirmation incluent:
|
||||||
|
```
|
||||||
|
"do it", "go ahead", "execute", "run", "start", "thanks", "would ya", "please", "okay?", "proceed", "continue", "go on", "do that", "go it", "do you understand?"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
Exemple de configuration:
|
||||||
```
|
```
|
||||||
[MAIN]
|
[MAIN]
|
||||||
is_local = True
|
is_local = True
|
||||||
provider_name = ollama
|
provider_name = ollama
|
||||||
provider_model = deepseek-r1:1.5b
|
provider_model = deepseek-r1:32b
|
||||||
provider_server_address = 127.0.0.1:11434
|
provider_server_address = http://127.0.0.1:11434 # Exemple Ollama; LM-Studio utilise http://127.0.0.1:1234
|
||||||
agent_name = Friday
|
agent_name = Friday
|
||||||
recover_last_session = False
|
recover_last_session = False
|
||||||
save_session = False
|
save_session = False
|
||||||
speak = False
|
speak = False
|
||||||
listen = False
|
listen = False
|
||||||
work_dir = /Users/mlg/Documents/ai_folder
|
|
||||||
jarvis_personality = False
|
jarvis_personality = False
|
||||||
languages = en fr
|
languages = en zh # Liste des langues pour TTS et routage potentiel.
|
||||||
[BROWSER]
|
[BROWSER]
|
||||||
headless_browser = False
|
headless_browser = False
|
||||||
stealth_mode = False
|
stealth_mode = False
|
||||||
```
|
```
|
||||||
|
|
||||||
**Explication du fichier config.ini**:
|
**Explication des paramètres de `config.ini`**:
|
||||||
|
|
||||||
`is_local` -> Exécute l’agent localement (True) ou sur un serveur distant (False).
|
* **Section `[MAIN]`:**
|
||||||
|
* `is_local`: `True` si vous utilisez des fournisseurs de LLM locaux (Ollama, LM-Studio, serveur local compatible OpenAI) ou l'option de serveur auto-hébergé. `False` si vous utilisez des API basées sur le cloud (OpenAI, Google, etc.).
|
||||||
|
* `provider_name`: Spécifie le fournisseur de LLM.
|
||||||
|
* Options locales: `ollama`, `lm-studio`, `openai` (pour serveur local compatible OpenAI), `server` (pour configuration de serveur auto-hébergé).
|
||||||
|
* Options d'API: `openai`, `google`, `deepseek`, `huggingface`, `togetherAI`.
|
||||||
|
* `provider_model`: Nom ou ID spécifique du modèle du fournisseur sélectionné (ex: `deepseekcoder:6.7b` pour Ollama, `gpt-3.5-turbo` pour API OpenAI, `mistralai/Mixtral-8x7B-Instruct-v0.1` pour TogetherAI).
|
||||||
|
* `provider_server_address`: L'adresse de votre fournisseur de LLM.
|
||||||
|
* Pour les fournisseurs locaux: ex: `http://127.0.0.1:11434` pour Ollama, `http://127.0.0.1:1234` pour LM-Studio.
|
||||||
|
* Pour le type de fournisseur `server`: L'adresse de votre serveur LLM auto-hébergé (ex: `http://your_server_ip:3333`).
|
||||||
|
* Pour les API cloud (`is_local = False`): Ceci est généralement ignoré ou peut être laissé vide, car les endpoints d'API sont généralement gérés par les bibliothèques clientes.
|
||||||
|
* `agent_name`: Le nom de l'assistant IA (ex: Friday). Si activé, utilisé comme mot de déclenchement pour la parole vers texte.
|
||||||
|
* `recover_last_session`: `True` pour tenter de récupérer l'état de la session précédente, `False` pour recommencer.
|
||||||
|
* `save_session`: `True` pour sauvegarder l'état de la session actuelle pour une récupération potentielle, `False` sinon.
|
||||||
|
* `speak`: `True` pour activer la sortie vocale de texte vers parole, `False` pour désactiver.
|
||||||
|
* `listen`: `True` pour activer l'entrée vocale de parole vers texte (uniquement mode CLI), `False` pour désactiver.
|
||||||
|
* `work_dir`: **Critique:** Le répertoire où AgenticSeek lira/écrira des fichiers. **Assurez-vous que ce chemin est valide et accessible sur votre système.**
|
||||||
|
* `jarvis_personality`: `True` pour utiliser des invites système plus "Jarvis-like" (expérimental), `False` pour utiliser des invites standard.
|
||||||
|
* `languages`: Liste de langues séparées par des virgules (ex: `en, zh, fr`). Utilisé pour la sélection de voix TTS (première par défaut) et peut aider le routeur LLM. Pour éviter les inefficacités du routeur, évitez d'utiliser trop de langues ou des langues très similaires.
|
||||||
|
* **Section `[BROWSER]`:**
|
||||||
|
* `headless_browser`: `True` pour exécuter le navigateur automatisé sans fenêtre visible (recommandé pour l'interface web ou l'utilisation non interactive). `False` pour afficher la fenêtre du navigateur (utile pour le mode CLI ou le débogage).
|
||||||
|
* `stealth_mode`: `True` pour activer des mesures qui rendent plus difficile la détection de l'automatisation du navigateur. Peut nécessiter l'installation manuelle d'extensions de navigateur comme anticaptcha.
|
||||||
|
|
||||||
`provider_name` -> Le fournisseur à utiliser (parmi : ollama, server, lm-studio, deepseek-api).
|
Cette section résume les types de fournisseurs de LLM pris en charge. Configurez-les dans `config.ini`.
|
||||||
|
|
||||||
`provider_model` -> Le modèle utilisé, par exemple, deepseek-r1:1.5b.
|
**Fournisseurs locaux (fonctionnant sur votre propre matériel):**
|
||||||
|
|
||||||
`provider_server_address` -> Adresse du serveur, par exemple, 127.0.0.1:11434 pour local. Définissez n’importe quoi pour une API non locale.
|
| Nom du fournisseur dans config.ini | `is_local` | Description | Section de configuration |
|
||||||
|
|-------------------------------|------------|-----------------------------------------------------------------------------|------------------------------------------------------------------|
|
||||||
|
| `ollama` | `True` | Fournit LLM localement facilement en utilisant Ollama. | [Configuration pour exécuter LLM localement sur votre machine](#configuration-pour-exécuter-llm-localement-sur-votre-machine) |
|
||||||
|
| `lm-studio` | `True` | Fournit LLM localement avec LM-Studio. | [Configuration pour exécuter LLM localement sur votre machine](#configuration-pour-exécuter-llm-localement-sur-votre-machine) |
|
||||||
|
| `openai` (pour serveur local) | `True` | Connectez-vous à un serveur local exposant une API compatible OpenAI (ex: llama.cpp). | [Configuration pour exécuter LLM localement sur votre machine](#configuration-pour-exécuter-llm-localement-sur-votre-machine) |
|
||||||
|
| `server` | `False` | Connectez-vous au serveur LLM auto-hébergé d'AgenticSeek fonctionnant sur une autre machine. | [Configuration pour exécuter LLM sur votre propre serveur](#configuration-pour-exécuter-llm-sur-votre-propre-serveur) |
|
||||||
|
|
||||||
`agent_name` -> Nom de l’agent, par exemple, Friday. Utilisé comme mot déclencheur pour la reconnaissance vocale.
|
**Fournisseurs d'API (basés sur le cloud):**
|
||||||
|
|
||||||
`recover_last_session` -> Reprend la dernière session (True) ou non (False).
|
| Nom du fournisseur dans config.ini | `is_local` | Description | Section de configuration |
|
||||||
|
|-------------------------------|------------|--------------------------------------------------|-----------------------------------------------------|
|
||||||
|
| `openai` | `False` | Utilise l'API officielle d'OpenAI (ex: GPT-3.5, GPT-4). | [Configuration pour exécuter avec une API](#configuration-pour-exécuter-avec-une-api) |
|
||||||
|
| `google` | `False` | Utilise les modèles Google Gemini via API. | [Configuration pour exécuter avec une API](#configuration-pour-exécuter-avec-une-api) |
|
||||||
|
| `deepseek` | `False` | Utilise l'API officielle de Deepseek. | [Configuration pour exécuter avec une API](#configuration-pour-exécuter-avec-une-api) |
|
||||||
|
| `huggingface` | `False` | Utilise Hugging Face Inference API. | [Configuration pour exécuter avec une API](#configuration-pour-exécuter-avec-une-api) |
|
||||||
|
| `togetherAI` | `False` | Utilise divers modèles ouverts via l'API TogetherAI. | [Configuration pour exécuter avec une API](#configuration-pour-exécuter-avec-une-api) |
|
||||||
|
|
||||||
`save_session` -> Sauvegarde les données de la session (True) ou non (False).
|
---
|
||||||
|
## Dépannage
|
||||||
|
|
||||||
`speak` -> Active la sortie vocale (True) ou non (False).
|
Si vous rencontrez des problèmes, cette section fournit des conseils.
|
||||||
|
|
||||||
`listen` -> Écoute les entrées vocales (True) ou non (False).
|
|
||||||
|
|
||||||
`work_dir` -> Dossier auquel l’IA aura accès, par exemple : /Users/user/Documents/.
|
|
||||||
|
|
||||||
`jarvis_personality` -> Utilise une personnalité inspiré de Jarvis (True) ou non (False). Cela utilise simplement une prompt alternative. Marche moins bien en français.
|
|
||||||
|
|
||||||
`headless_browser` -> Exécute le navigateur sans fenêtre visible (True) ou non (False).
|
|
||||||
|
|
||||||
`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
|
|
||||||
|
|
||||||
Le tableau ci-dessous montre les LLM providers disponibles :
|
|
||||||
|
|
||||||
| Provider | Local? | Description |
|
|
||||||
|-----------|--------|-----------------------------------------------------------|
|
|
||||||
| 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
|
|
||||||
| lm-studio | Yes | Exécutez un LLM localement avec LM Studio (définissez provider_name sur lm-studio)
|
|
||||||
| openai | No | Utilise l'API ChatGPT (pas privé) |
|
|
||||||
| deepseek-api | No | Utilise l'API Deepseek (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 :
|
|
||||||
|
|
||||||
```
|
|
||||||
is_local = False
|
|
||||||
provider_name = openai
|
|
||||||
provider_model = gpt-4o
|
|
||||||
provider_server_address = 127.0.0.1:5000
|
|
||||||
```
|
|
||||||
|
|
||||||
`is_local` : doit être True pour tout LLM exécuté localement, sinon False.
|
|
||||||
|
|
||||||
`provider_name` : Sélectionnez le fournisseur à utiliser par son nom, voir la liste des fournisseurs ci-dessus.
|
|
||||||
|
|
||||||
`provider_model` : Définissez le modèle à utiliser par l’agent.
|
|
||||||
|
|
||||||
`provider_server_address` : peut être défini sur n’importe quoi si vous n’utilisez pas le fournisseur server.
|
|
||||||
|
|
||||||
# Problèmes connus
|
# Problèmes connus
|
||||||
|
|
||||||
## Problèmes avec Chromedriver
|
## Problèmes de ChromeDriver
|
||||||
|
|
||||||
Erreur #1:**incompatibilité**
|
**Exemple d'erreur:** `SessionNotCreatedException: Message: session not created: This version of ChromeDriver only supports Chrome version XXX`
|
||||||
|
|
||||||
|
### Cause racine
|
||||||
|
L'incompatibilité de version de ChromeDriver se produit lorsque:
|
||||||
|
1. La version de ChromeDriver que vous avez installée ne correspond pas à la version du navigateur Chrome
|
||||||
|
2. Dans les environnements Docker, `undetected_chromedriver` peut télécharger sa propre version de ChromeDriver, contournant les binaires montés
|
||||||
|
|
||||||
|
### Étapes de résolution
|
||||||
|
|
||||||
|
#### 1. Vérifiez votre version de Chrome
|
||||||
|
Ouvrez Google Chrome → `Paramètres > À propos de Chrome` pour trouver votre version (ex: "Version 134.0.6998.88")
|
||||||
|
|
||||||
|
#### 2. Téléchargez ChromeDriver correspondant
|
||||||
|
|
||||||
|
**Pour Chrome 115 et versions ultérieures:** Utilisez [Chrome for Testing API](https://googlechromelabs.github.io/chrome-for-testing/)
|
||||||
|
- Visitez le tableau de disponibilité de Chrome for Testing
|
||||||
|
- Trouvez votre version de Chrome ou la correspondance disponible la plus proche
|
||||||
|
- Téléchargez ChromeDriver pour votre système d'exploitation (utilisez Linux64 pour les environnements Docker)
|
||||||
|
|
||||||
|
**Pour les anciennes versions de Chrome:** Utilisez [Téléchargements hérités de ChromeDriver](https://chromedriver.chromium.org/downloads)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
#### 3. Installez ChromeDriver (choisissez une méthode)
|
||||||
|
|
||||||
|
**Méthode A: Répertoire racine du projet (recommandé pour Docker)**
|
||||||
|
```bash
|
||||||
|
# Placez le binaire chromedriver téléchargé dans le répertoire racine du projet
|
||||||
|
cp path/to/downloaded/chromedriver ./chromedriver
|
||||||
|
chmod +x ./chromedriver # Rendez-le exécutable sur Linux/macOS
|
||||||
|
```
|
||||||
|
|
||||||
|
**Méthode B: PATH système**
|
||||||
|
```bash
|
||||||
|
# Linux/macOS
|
||||||
|
sudo mv chromedriver /usr/local/bin/
|
||||||
|
sudo chmod +x /usr/local/bin/chromedriver
|
||||||
|
|
||||||
|
# Windows: Placez chromedriver.exe dans un dossier du PATH
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 4. Vérifiez l'installation
|
||||||
|
```bash
|
||||||
|
# Testez la version de ChromeDriver
|
||||||
|
./chromedriver --version
|
||||||
|
# Ou s'il est dans PATH:
|
||||||
|
chromedriver --version
|
||||||
|
```
|
||||||
|
|
||||||
|
### Instructions spécifiques à Docker
|
||||||
|
|
||||||
|
⚠️ **Important pour les utilisateurs de Docker:**
|
||||||
|
- La méthode de montage de volumes Docker peut ne pas fonctionner avec le mode furtif (`undetected_chromedriver`)
|
||||||
|
- **Solution:** Placez ChromeDriver dans le répertoire racine du projet en tant que `./chromedriver`
|
||||||
|
- L'application le détectera automatiquement et utilisera ce binaire
|
||||||
|
- Vous devriez voir dans les logs: `"Using ChromeDriver from project root: ./chromedriver"`
|
||||||
|
|
||||||
|
### Conseils de dépannage
|
||||||
|
|
||||||
|
1. **Toujours une incompatibilité de version ?**
|
||||||
|
- Vérifiez que ChromeDriver est exécutable: `ls -la ./chromedriver`
|
||||||
|
- Vérifiez la version de ChromeDriver: `./chromedriver --version`
|
||||||
|
- Assurez-vous qu'elle correspond à votre version du navigateur Chrome
|
||||||
|
|
||||||
|
2. **Problèmes avec le conteneur Docker ?**
|
||||||
|
- Vérifiez les logs du backend: `docker logs backend`
|
||||||
|
- Recherchez le message: `"Using ChromeDriver from project root"`
|
||||||
|
- S'il n'est pas trouvé, vérifiez que le fichier existe et est exécutable
|
||||||
|
|
||||||
|
3. **Versions de Chrome for Testing**
|
||||||
|
- Utilisez une correspondance exacte lorsque possible
|
||||||
|
- Pour la version 134.0.6998.88, utilisez ChromeDriver 134.0.6998.165 (la version disponible la plus proche)
|
||||||
|
- Le numéro de version principal doit correspondre (134 = 134)
|
||||||
|
|
||||||
|
### Matrice de compatibilité des versions
|
||||||
|
|
||||||
|
| Version de Chrome | Version de ChromeDriver | Statut |
|
||||||
|
|----------------|---------------------|---------|
|
||||||
|
| 134.0.6998.x | 134.0.6998.165 | ✅ Disponible |
|
||||||
|
| 133.0.6943.x | 133.0.6943.141 | ✅ Disponible |
|
||||||
|
| 132.0.6834.x | 132.0.6834.159 | ✅ Disponible |
|
||||||
|
|
||||||
|
*Pour la compatibilité la plus récente, consultez le [Tableau de Chrome for Testing](https://googlechromelabs.github.io/chrome-for-testing/)*
|
||||||
|
|
||||||
`Exception: Failed to initialize browser: Message: session not created: This version of ChromeDriver only supports Chrome version 113
|
`Exception: Failed to initialize browser: Message: session not created: This version of ChromeDriver only supports Chrome version 113
|
||||||
Current browser version is 134.0.6998.89 with binary path`
|
Current browser version is 134.0.6998.89 with binary path`
|
||||||
|
|
||||||
Cela se produit s’il y a une incompatibilité entre votre navigateur et la version de chromedriver.
|
Cela se produit si votre navigateur et la version de chromedriver ne correspondent pas.
|
||||||
|
|
||||||
Vous devez naviguer pour télécharger la dernière version :
|
Vous devez naviguer pour télécharger la dernière version:
|
||||||
|
|
||||||
https://developer.chrome.com/docs/chromedriver/downloads
|
https://developer.chrome.com/docs/chromedriver/downloads
|
||||||
|
|
||||||
Si vous utilisez Chrome version 115 ou plus récent, allez sur :
|
Si vous utilisez Chrome version 115 ou supérieure, allez à:
|
||||||
|
|
||||||
https://googlechromelabs.github.io/chrome-for-testing/
|
https://googlechromelabs.github.io/chrome-for-testing/
|
||||||
|
|
||||||
Et téléchargez la version de chromedriver correspondant à votre système d’exploitation.
|
et téléchargez la version de chromedriver correspondant à votre système d'exploitation.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Si cette section est incomplète, merci de faire une nouvelle issue sur github.
|
Si cette section est incomplète, ouvrez un issue.
|
||||||
|
|
||||||
|
## Problèmes d'adaptateurs de connexion
|
||||||
|
|
||||||
|
```
|
||||||
|
Exception: Provider lm-studio failed: HTTP request failed: No connection adapters were found for '127.0.0.1:1234/v1/chat/completions'` (note: le port peut varier)
|
||||||
|
```
|
||||||
|
|
||||||
|
* **Cause:** Il manque le préfixe `http://` dans `provider_server_address` pour `lm-studio` (ou un autre serveur local compatible OpenAI similaire) dans `config.ini`, ou il pointe vers le mauvais port.
|
||||||
|
* **Solution:**
|
||||||
|
* Assurez-vous que l'adresse inclut `http://`. LM-Studio utilise généralement `http://127.0.0.1:1234` par défaut.
|
||||||
|
* `config.ini` correct: `provider_server_address = http://127.0.0.1:1234` (ou votre port réel du serveur LM-Studio).
|
||||||
|
|
||||||
|
## URL de base de SearxNG non fournie
|
||||||
|
|
||||||
|
```
|
||||||
|
raise ValueError("SearxNG base URL must be provided either as an argument or via the SEARXNG_BASE_URL environment variable.")
|
||||||
|
ValueError: SearxNG base URL must be provided either as an argument or via the SEARXNG_BASE_URL environment variable.`
|
||||||
|
```
|
||||||
|
|
||||||
|
Cela peut se produire si vous exécutez le mode CLI avec une URL de base de searxng incorrecte.
|
||||||
|
|
||||||
|
SEARXNG_BASE_URL doit différer selon que vous exécutez dans Docker ou sur l'hôte:
|
||||||
|
|
||||||
|
**Exécution sur l'hôte:** `SEARXNG_BASE_URL="http://localhost:8080"`
|
||||||
|
|
||||||
|
**Exécution complètement dans Docker (interface web):** `SEARXNG_BASE_URL="http://searxng:8080"`
|
||||||
|
|
||||||
## FAQ
|
## FAQ
|
||||||
**Q: Quel matériel est nécessaire ?**
|
|
||||||
|
|
||||||
| Taille du Modèle | GPU | Commentaire |
|
**Q: De quel matériel ai-je besoin ?**
|
||||||
|--------------------|------|----------------------------------------------------------|
|
|
||||||
| 7B | 8 Go VRAM | ⚠️ Non recommandé. Performances médiocres, hallucinations fréquentes, et l'agent planificateur échouera probablement. |
|
|
||||||
| 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. |
|
|
||||||
| 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. |
|
|
||||||
| 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**
|
| Taille du modèle | GPU | Commentaires |
|
||||||
|
|-----------|--------|-----------------------------------------------------------|
|
||||||
|
| 7B | 8GB VRAM | ⚠️ Non recommandé. Performances médiocres, hallucinations fréquentes, les agents de planification peuvent échouer. |
|
||||||
|
| 14B | 12 GB VRAM (ex: RTX 3060) | ✅ Utilisable pour des tâches simples. Peut avoir des difficultés avec la navigation web et la planification de tâches. |
|
||||||
|
| 32B | 24+ GB VRAM (ex: RTX 4090) | 🚀 Réussit la plupart des tâches, peut encore avoir des difficultés avec la planification de tâches |
|
||||||
|
| 70B+ | 48+ GB VRAM | 💪 Excellent. Recommandé pour les cas d'utilisation avancés. |
|
||||||
|
|
||||||
DeepSeek R1 excelle dans le raisonnement et l’utilisation d’outils pour sa taille. Nous pensons que c’est un choix solide pour nos besoins, bien que d’autres modèles fonctionnent également (bien que moins bien pour un nombre équivalent de paramètres).
|
**Q: Que faire si je rencontre des erreurs ?**
|
||||||
|
|
||||||
**Q: J'ai une erreur quand je lance le programme, je fait quoi?**
|
Assurez-vous que le local fonctionne (`ollama serve`), que votre `config.ini` correspond à votre fournisseur et que les dépendances sont installées. Si rien ne fonctionne, n'hésitez pas à ouvrir un issue.
|
||||||
|
|
||||||
Assurez-vous qu’Ollama est en cours d’exécution (ollama serve), que votre config.ini correspond à votre fournisseur, et que les dépendances sont installées. Si cela ne fonctionne pas, n’hésitez pas à signaler un problème.
|
**Q: Peut-il vraiment fonctionner à 100% localement ?**
|
||||||
|
|
||||||
**Q: C'est vraiment 100% local?**
|
Oui, avec les fournisseurs Ollama, lm-studio ou server, tous les modèles de parole vers texte, LLM et texte vers parole fonctionnent localement. Les options non locales (OpenAI ou autres API) sont optionnelles.
|
||||||
|
|
||||||
Oui, avec les fournisseurs Ollama, lm-studio ou Server, toute la reconnaissance vocale, le LLM et la synthèse vocale fonctionnent localement. Les options non locales (OpenAI ou autres API) sont facultatives.
|
**Q: Pourquoi devrais-je utiliser AgenticSeek quand j'ai Manus ?**
|
||||||
|
|
||||||
**Q: En quoi c'est supérieur à Manus**
|
Contrairement à Manus, AgenticSeek privilégie l'indépendance des systèmes externes, vous donnant plus de contrôle, de confidentialité et évitant les coûts d'API.
|
||||||
|
|
||||||
Il ne l'est certainement pas, mais nous privilégions l’exécution locale et la confidentialité par rapport à une approche basée sur le cloud. C’est une alternative plus accessible et surtout moins cher !
|
**Q: Qui est derrière ce projet ?**
|
||||||
|
|
||||||
## Contribution
|
Ce projet a été créé par moi, avec deux amis comme mainteneurs et des contributeurs de la communauté open source sur GitHub. Nous sommes juste des individus passionnés, pas une startup, ni affiliés à aucune organisation.
|
||||||
|
|
||||||
Nous recherchons des développeurs pour améliorer AgenticSeek ! Consultez la section "issues" github ou les discussions.
|
Tout compte AgenticSeek sur X autre que mon compte personnel (https://x.com/Martin993886460) est un imposteur.
|
||||||
|
|
||||||
[](https://www.star-history.com/#Fosowl/agenticSeek&Date)
|
## Contribuer
|
||||||
|
|
||||||
[Guide du contributeur](./docs/CONTRIBUTING.md)
|
Nous recherchons des développeurs pour améliorer AgenticSeek ! Consultez les problèmes ouverts ou les discussions.
|
||||||
|
|
||||||
|
[Guide de contribution](./docs/CONTRIBUTING.md)
|
||||||
|
|
||||||
|
## Sponsors:
|
||||||
|
|
||||||
|
Vous voulez améliorer les capacités d'AgenticSeek avec des fonctionnalités comme la recherche de vols, la planification de voyages ou l'obtention des meilleures offres d'achat ? Envisagez d'utiliser SerpApi pour créer des outils personnalisés qui débloquent plus de fonctionnalités de type Jarvis. Avec SerpApi, vous pouvez accélérer votre agent pour des tâches professionnelles tout en gardant le contrôle total.
|
||||||
|
|
||||||
|
<a href="https://serpapi.com/"><img src="./media/banners/sponsor_banner_serpapi.png" height="350" alt="SerpApi Banner" ></a>
|
||||||
|
|
||||||
|
Consultez [Contributing.md](./docs/CONTRIBUTING.md) pour apprendre comment intégrer des outils personnalisés !
|
||||||
|
|
||||||
|
### **Sponsors**:
|
||||||
|
|
||||||
|
- [tatra-labs](https://github.com/tatra-labs)
|
||||||
|
|
||||||
## Mainteneurs:
|
## Mainteneurs:
|
||||||
> [Fosowl](https://github.com/Fosowl)
|
|
||||||
> [steveh8758](https://github.com/steveh8758)
|
> [Fosowl](https://github.com/Fosowl) | Heure de Paris
|
||||||
> [antoineVIVIES](https://github.com/antoineVIVIES) | Taipei Time
|
|
||||||
|
> [antoineVIVIES](https://github.com/antoineVIVIES) | Heure de Taipei
|
||||||
|
|
||||||
|
## Remerciements spéciaux:
|
||||||
|
|
||||||
|
> [tcsenpai](https://github.com/tcsenpai) et [plitc](https://github.com/plitc) pour avoir aidé à la dockerisation du backend
|
||||||
|
|
||||||
|
[](https://www.star-history.com/#Fosowl/agenticSeek&Date)
|
||||||
|
|||||||
@@ -1,48 +1,52 @@
|
|||||||
# AgenticSeek: プライベートなローカルManus代替
|
# AgenticSeek: Manusのプライベートでローカルな代替品
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img align="center" src="./media/agentic_seek_logo.png" width="300" height="300" alt="Agentic Seek ロゴ">
|
<img align="center" src="./media/agentic_seek_logo.png" width="300" height="300" alt="Agentic Seek Logo">
|
||||||
<p>
|
<p>
|
||||||
|
|
||||||
[English](./README.md) | [中文](./README_CHS.md) | [繁體中文](./README_CHT.md) | [Français](./README_FR.md) | 日本語
|
English | [中文](./README_CHS.md) | [繁體中文](./README_CHT.md) | [Français](./README_FR.md) | [日本語](./README_JP.md) | [Português (Brasil)](./README_PTBR.md) | [Español](./README_ES.md) | [Türkçe](./README_TR.md)
|
||||||
|
|
||||||
*Manus AIの**100%ローカルな代替**となるこの音声対応AIアシスタントは、自律的にウェブを閲覧し、コードを書き、タスクを計画しながら、すべてのデータをあなたのデバイスに保持します。ローカル推論モデルに合わせて調整されており、完全にあなたのハードウェア上で動作するため、完全なプライバシーとクラウドへの依存ゼロを保証します。*
|
*音声対応のAIアシスタントで、**100%ローカルで動作するManus AIの代替品**です。自律的にウェブを閲覧し、コードを書き、タスクを計画し、すべてのデータをデバイス上に保持します。ローカル推論モデル向けに設計されており、完全にあなたのハードウェア上で動作し、プライバシーを保証し、クラウドへの依存をゼロにします。*
|
||||||
|
|
||||||
[](https://fosowl.github.io/agenticSeek.html)  [](https://discord.gg/8hGDaME3TC) [](https://x.com/Martin993886460) [](https://github.com/Fosowl/agenticSeek/stargazers)
|
[](https://fosowl.github.io/agenticSeek.html)  [](https://discord.gg/8hGDaME3TC) [](https://x.com/Martin993886460) [](https://github.com/Fosowl/agenticSeek/stargazers)
|
||||||
|
|
||||||
### なぜAgenticSeekなのか?
|
### なぜAgenticSeekを選ぶのか?
|
||||||
|
|
||||||
* 🔒 完全ローカル&プライベート - すべてがあなたのマシン上で実行されます — クラウドなし、データ共有なし。あなたのファイル、会話、検索はプライベートに保たれます。
|
* 🔒 完全にローカル&プライベート - すべてがあなたのマシン上で動作し、クラウドなし、データ共有なし。あなたのファイル、会話、検索はプライベートのままです。
|
||||||
|
|
||||||
* 🌐 スマートなウェブブラウジング - AgenticSeekは自分でインターネットを閲覧できます — 検索、読み取り、情報抽出、ウェブフォーム入力 — すべてハンズフリーで。
|
* 🌐 インテリジェントなウェブブラウジング - AgenticSeekは自律的にインターネットを閲覧できます:検索、読み取り、情報抽出、ウェブフォーム入力、すべて手動操作なしで。
|
||||||
|
|
||||||
* 💻 自律型コーディングアシスタント - コードが必要ですか?Python、C、Go、Javaなどでプログラムを書き、デバッグし、実行できます — すべて監視なしで。
|
* 💻 自律的なプログラミングアシスタント - コードが必要ですか?Python、C、Go、Javaなどのプログラムを監督なしで書き、デバッグし、実行できます。
|
||||||
|
|
||||||
* 🧠 スマートエージェント選択 - あなたが尋ねると、タスクに最適なエージェントを自動的に見つけ出します。まるで専門家チームが助けてくれるようです。
|
* 🧠 インテリジェントなエージェント選択 - あなたが要求すると、自動的に最適なエージェントがタスクに割り当てられます。常に利用可能な専門家チームを持っているようなものです。
|
||||||
|
|
||||||
* 📋 複雑なタスクの計画と実行 - 旅行計画から複雑なプロジェクトまで — 大きなタスクをステップに分割し、複数のAIエージェントを使って物事を成し遂げることができます。
|
* 📋 複雑なタスクの計画と実行 - 旅行計画から複雑なプロジェクトまで、大きなタスクをステップに分解し、複数のAIエージェントを使用して完了できます。
|
||||||
|
|
||||||
* 🎙️ 音声対応 - クリーンで高速、未来的な音声と音声認識により、まるでSF映画のパーソナルAIのように話しかけることができます。
|
* 🎙️ 音声サポート - クリーンで高速で未来的な音声と音声認識機能により、SF映画のようなパーソナルAIと会話できます。(開発中)
|
||||||
|
|
||||||
### **デモ**
|
### **デモ**
|
||||||
|
|
||||||
> *agenticSeekプロジェクトを検索し、必要なスキルを学び、その後CV_candidates.zipを開いて、プロジェクトに最も適した候補者を教えてください。*
|
> *agenticSeekプロジェクトを検索して必要なスキルを学び、CV_candidates.zipを開いて、どの候補がプロジェクトに最も適しているか教えてくれますか?*
|
||||||
|
|
||||||
https://github.com/user-attachments/assets/b8ca60e9-7b3b-4533-840e-08f9ac426316
|
https://github.com/user-attachments/assets/b8ca60e9-7b3b-4533-840e-08f9ac426316
|
||||||
|
|
||||||
免責事項:このデモは、表示されるすべてのファイル(例:CV_candidates.zip)を含め、完全に架空のものです。私たちは企業ではなく、候補者ではなくオープンソースの貢献者を求めています。
|
免責事項:このデモと表示されるすべてのファイル(例:CV_candidates.zip)は完全に架空のものです。私たちは企業ではなく、候補者ではなくオープンソースの貢献者を求めています。
|
||||||
|
|
||||||
> 🛠️ **作業中** – 貢献者を募集中です!
|
> 🛠⚠️️ **アクティブな開発中**
|
||||||
|
|
||||||
## インストール
|
> 🙏 このプロジェクトはサイドプロジェクトとして始まり、ロードマップも資金もありませんでした。GitHub Trendingに登場して予想以上に成長しました。貢献、フィードバック、忍耐に深く感謝します。
|
||||||
|
|
||||||
Chrome Driver、Docker、Python 3.10がインストールされていることを確認してください。
|
## 前提条件
|
||||||
|
|
||||||
セットアップにはPython 3.10を正確に使用することを強くお勧めします。そうでない場合、依存関係のエラーが発生する可能性があります。
|
始める前に、以下がインストールされていることを確認してください:
|
||||||
|
|
||||||
Chromeドライバーに関する問題については、**Chromedriver**セクションを参照してください。
|
* **Git:** リポジトリをクローンするため。[Gitをダウンロード](https://git-scm.com/downloads)
|
||||||
|
* **Python 3.10.x:** Python 3.10.xを強く推奨します。他のバージョンでは依存関係エラーが発生する可能性があります。[Python 3.10をダウンロード](https://www.python.org/downloads/release/python-3100/)(3.10.xバージョンを選択)。
|
||||||
|
* **Docker Engine & Docker Compose:** SearxNGなどのパッケージ化されたサービスを実行するため。
|
||||||
|
* Docker Desktopをインストール(Docker Compose V2を含む):[Windows](https://docs.docker.com/desktop/install/windows-install/) | [Mac](https://docs.docker.com/desktop/install/mac-install/) | [Linux](https://docs.docker.com/desktop/install/linux-install/)
|
||||||
|
* またはLinuxでDocker EngineとDocker Composeを別々にインストール:[Docker Engine](https://docs.docker.com/engine/install/) | [Docker Compose](https://docs.docker.com/compose/install/)(Compose V2をインストールしていることを確認、例:`sudo apt-get install docker-compose-plugin`)。
|
||||||
|
|
||||||
### 1️⃣ **リポジトリのクローンとセットアップ**
|
### 1. **リポジトリをクローンして設定**
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
git clone https://github.com/Fosowl/agenticSeek.git
|
git clone https://github.com/Fosowl/agenticSeek.git
|
||||||
@@ -50,287 +54,308 @@ cd agenticSeek
|
|||||||
mv .env.example .env
|
mv .env.example .env
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2️ **仮想環境の作成**
|
### 2. .envファイルの内容を変更
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
python3 -m venv agentic_seek_env
|
SEARXNG_BASE_URL="http://searxng:8080" # ホストでCLIモードを実行する場合はhttp://127.0.0.1:8080を使用
|
||||||
source agentic_seek_env/bin/activate
|
REDIS_BASE_URL="redis://redis:6379/0"
|
||||||
# Windowsの場合: agentic_seek_env\Scripts\activate
|
WORK_DIR="/Users/mlg/Documents/workspace_for_ai"
|
||||||
|
OLLAMA_PORT="11434"
|
||||||
|
LM_STUDIO_PORT="1234"
|
||||||
|
CUSTOM_ADDITIONAL_LLM_PORT="11435"
|
||||||
|
OPENAI_API_KEY='optional'
|
||||||
|
DEEPSEEK_API_KEY='optional'
|
||||||
|
OPENROUTER_API_KEY='optional'
|
||||||
|
TOGETHER_API_KEY='optional'
|
||||||
|
GOOGLE_API_KEY='optional'
|
||||||
|
ANTHROPIC_API_KEY='optional'
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3️⃣ **パッケージのインストール**
|
必要に応じて`.env`ファイルを更新してください:
|
||||||
|
|
||||||
Python、Dockerとdocker compose、Google Chromeがインストールされていることを確認してください。
|
- **SEARXNG_BASE_URL**: ホストでCLIモードを実行する場合を除き、変更しないでください。
|
||||||
|
- **REDIS_BASE_URL**: 変更しないでください
|
||||||
|
- **WORK_DIR**: ローカル作業ディレクトリへのパス。AgenticSeekはこれらのファイルを読み取り、操作できます。
|
||||||
|
- **OLLAMA_PORT**: Ollamaサービスのポート番号。
|
||||||
|
- **LM_STUDIO_PORT**: LM Studioサービスのポート番号。
|
||||||
|
- **CUSTOM_ADDITIONAL_LLM_PORT**: 追加のカスタムLLMサービスのポート。
|
||||||
|
|
||||||
Python 3.10.0を推奨します。
|
**APIキーは、ローカルでLLMを実行することを選択するユーザーには完全にオプションであり、これがこのプロジェクトの主な目的です。ハードウェアが十分にある場合は空のままにしてください。**
|
||||||
|
|
||||||
**自動インストール(推奨):**
|
### 3. **Dockerを起動**
|
||||||
|
|
||||||
Linux/Macosの場合:
|
Dockerがインストールされ、システム上で実行されていることを確認してください。以下のコマンドでDockerを起動できます:
|
||||||
|
|
||||||
|
- **Linux/macOS:**
|
||||||
|
ターミナルを開いて実行:
|
||||||
|
```sh
|
||||||
|
sudo systemctl start docker
|
||||||
|
```
|
||||||
|
または、インストールされている場合はアプリケーションメニューからDocker Desktopを起動。
|
||||||
|
|
||||||
|
- **Windows:**
|
||||||
|
スタートメニューからDocker Desktopを起動。
|
||||||
|
|
||||||
|
Dockerが実行されているかは以下で確認できます:
|
||||||
```sh
|
```sh
|
||||||
./install.sh
|
docker info
|
||||||
```
|
```
|
||||||
|
Dockerインストール情報が表示されれば正常に動作しています。
|
||||||
|
|
||||||
** テキスト読み上げ(TTS)機能で日本語をサポートするには、fugashi(日本語分かち書きライブラリ)をインストールする必要があります:**
|
要約については以下の[ローカルプロバイダーリスト](#ローカルプロバイダーリスト)を参照してください。
|
||||||
|
|
||||||
** 注意: 日本語のテキスト読み上げ(TTS)機能には多くの依存関係が必要で、問題が発生する可能性があります。mecabrcに関する問題が発生することがあります。現在のところ、この問題を修正する方法が見つかっていません。当面は日本語でのテキスト読み上げ機能を無効にすることをお勧めします。**
|
次のステップ:[ローカルでAgenticSeekを実行](#サービスを起動して実行)
|
||||||
|
|
||||||
必要なライブラリをインストールする場合は以下のコマンドを実行してください:
|
*問題が発生した場合は、[トラブルシューティング](#トラブルシューティング)セクションを参照してください。*
|
||||||
|
*ハードウェアがローカルでLLMを実行できない場合は、[APIを使用した実行設定](#apiを使用した実行設定)を参照してください。*
|
||||||
```
|
*詳細な`config.ini`の説明については、[設定セクション](#設定)を参照してください。*
|
||||||
pip3 install --upgrade pyopenjtalk jaconv mojimoji unidic fugashi
|
|
||||||
pip install unidic-lite
|
|
||||||
python -m unidic download
|
|
||||||
```
|
|
||||||
|
|
||||||
Windowsの場合:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
./install.bat
|
|
||||||
```
|
|
||||||
|
|
||||||
**手動:**
|
|
||||||
|
|
||||||
**注意:どのOSでも、インストールするChromeDriverがインストール済みのChromeバージョンと一致していることを確認してください。`google-chrome --version`を実行してください。Chrome >135の場合の既知の問題を参照してください。**
|
|
||||||
|
|
||||||
- *Linux*:
|
|
||||||
|
|
||||||
パッケージリストの更新:`sudo apt update`
|
|
||||||
|
|
||||||
依存関係のインストール:`sudo apt install -y alsa-utils portaudio19-dev python3-pyaudio libgtk-3-dev libnotify-dev libgconf-2-4 libnss3 libxss1`
|
|
||||||
|
|
||||||
Chromeブラウザのバージョンに一致するChromeDriverのインストール:
|
|
||||||
`sudo apt install -y chromium-chromedriver`
|
|
||||||
|
|
||||||
要件のインストール:`pip3 install -r requirements.txt`
|
|
||||||
|
|
||||||
- *Macos*:
|
|
||||||
|
|
||||||
brewの更新:`brew update`
|
|
||||||
|
|
||||||
chromedriverのインストール:`brew install --cask chromedriver`
|
|
||||||
|
|
||||||
portaudioのインストール:`brew install portaudio`
|
|
||||||
|
|
||||||
pipのアップグレード:`python3 -m pip install --upgrade pip`
|
|
||||||
|
|
||||||
wheelのアップグレード:`pip3 install --upgrade setuptools wheel`
|
|
||||||
|
|
||||||
要件のインストール:`pip3 install -r requirements.txt`
|
|
||||||
|
|
||||||
- *Windows*:
|
|
||||||
|
|
||||||
pyreadline3のインストール:`pip install pyreadline3`
|
|
||||||
|
|
||||||
portaudioの手動インストール(例:vcpkgまたはビルド済みバイナリ経由)後、実行:`pip install pyaudio`
|
|
||||||
|
|
||||||
chromedriverの手動ダウンロードとインストール:https://sites.google.com/chromium.org/driver/getting-started
|
|
||||||
|
|
||||||
PATHに含まれるディレクトリにchromedriverを配置します。
|
|
||||||
|
|
||||||
要件のインストール:`pip3 install -r requirements.txt`
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## マシン上でローカルにLLMを実行するためのセットアップ
|
## マシン上でローカルにLLMを実行する設定
|
||||||
|
|
||||||
**少なくともDeepseek 14Bの使用を推奨します。より小さなモデルは、特にウェブブラウジングのタスクで苦労します。**
|
**ハードウェア要件:**
|
||||||
|
|
||||||
|
LLMをローカルで実行するには、十分なハードウェアが必要です。少なくともMagistral、Qwen、またはDeepseek 14Bを実行できるGPUが必要です。詳細なモデル/パフォーマンスの推奨事項についてはFAQを参照してください。
|
||||||
|
|
||||||
**ローカルプロバイダーのセットアップ**
|
**ローカルプロバイダーを設定**
|
||||||
|
|
||||||
ローカルプロバイダーを開始します。例えばollamaの場合:
|
例えばollamaを使用してローカルプロバイダーを起動:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
ollama serve
|
ollama serve
|
||||||
```
|
```
|
||||||
|
|
||||||
サポートされているローカルプロバイダーのリストについては、以下を参照してください。
|
サポートされているローカルプロバイダーのリストは以下を参照してください。
|
||||||
|
|
||||||
**config.iniの更新**
|
**config.iniを更新**
|
||||||
|
|
||||||
config.iniファイルを変更して、provider_nameをサポートされているプロバイダーに、provider_modelをプロバイダーがサポートするLLMに設定します。*Qwen*や*Deepseek*などの推論モデルを推奨します。
|
config.iniファイルを変更して、provider_nameをサポートされているプロバイダーに、provider_modelをプロバイダーがサポートするLLMに設定します。*Magistral*や*Deepseek*などの推論モデルをお勧めします。
|
||||||
|
|
||||||
必要なハードウェアについては、READMEの最後にある**FAQ**を参照してください。
|
必要なハードウェアについては、READMEの最後にある**FAQ**を参照してください。
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
[MAIN]
|
[MAIN]
|
||||||
is_local = True # ローカルで実行するか、リモートプロバイダーで実行するか。
|
is_local = True # ローカルで実行するかリモートプロバイダーを使用するか
|
||||||
provider_name = ollama # またはlm-studio、openaiなど。
|
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
|
||||||
agent_name = Jarvis # AIの名前
|
agent_name = Jarvis # AIの名前
|
||||||
recover_last_session = True # 前のセッションを復元するかどうか
|
recover_last_session = True # 前のセッションを復元
|
||||||
save_session = True # 現在のセッションを記憶するかどうか
|
save_session = True # 現在のセッションを記憶
|
||||||
speak = True # テキスト読み上げ
|
speak = False # テキスト読み上げ
|
||||||
listen = False # 音声認識、CLIのみ
|
listen = False # 音声認識、CLIのみ、実験的
|
||||||
work_dir = /Users/mlg/Documents/workspace # AgenticSeekのワークスペース。
|
jarvis_personality = False # より「Jarvis」的な性格を使用(実験的)
|
||||||
jarvis_personality = False # より「Jarvis」らしい性格を使用するかどうか(実験的)
|
languages = en zh # 言語リスト、TTSはデフォルトでリストの最初を使用
|
||||||
languages = en zh # 言語のリスト、テキスト読み上げはリストの最初の言語にデフォルト設定されます
|
|
||||||
[BROWSER]
|
[BROWSER]
|
||||||
headless_browser = True # ヘッドレスブラウザを使用するかどうか、ウェブインターフェースを使用する場合のみ推奨。
|
headless_browser = True # ホストでCLIを使用する場合を除き変更しない
|
||||||
stealth_mode = True # undetected seleniumを使用してブラウザ検出を減らす
|
stealth_mode = True # 検出されにくいseleniumを使用してブラウザ検出を減らす
|
||||||
```
|
```
|
||||||
|
|
||||||
警告:LM-studioを使用してLLMを実行する場合、provider_nameを`openai`に設定しないでください。`lm-studio`に設定してください。
|
**警告**:
|
||||||
|
|
||||||
注意:一部のプロバイダー(例:lm-studio)では、IPの前に`http://`が必要です。例:`http://127.0.0.1:1234`
|
- `config.ini`ファイル形式はコメントをサポートしていません。
|
||||||
|
コメントがエラーを引き起こすため、サンプル設定を直接コピー&ペーストしないでください。代わりに、コメントなしで希望の設定で`config.ini`ファイルを手動で変更してください。
|
||||||
|
|
||||||
**ローカルプロバイダーのリスト**
|
- LM-studioを使用してLLMを実行する場合、provider_nameを`openai`に設定*しない*でください。`lm-studio`として使用してください。
|
||||||
|
|
||||||
|
- 一部のプロバイダー(例:lm-studio)では、IPの前に`http://`が必要です。例:`http://127.0.0.1:1234`
|
||||||
|
|
||||||
|
**ローカルプロバイダーリスト**
|
||||||
|
|
||||||
| プロバイダー | ローカル? | 説明 |
|
| プロバイダー | ローカル? | 説明 |
|
||||||
|-----------|--------|-----------------------------------------------------------|
|
|-----------|--------|-----------------------------------------------------------|
|
||||||
| ollama | はい | ollamaをLLMプロバイダーとして使用して、LLMをローカルで簡単に実行します |
|
| ollama | はい | ollamaを使用して簡単にローカルでLLMを実行 |
|
||||||
| lm-studio | はい | LM studioでLLMをローカル実行します(`provider_name`を`lm-studio`に設定)|
|
| lm-studio | はい | LM studioでローカルにLLMを実行(`provider_name` = `lm-studio`に設定)|
|
||||||
| openai | はい | openai互換API(例:llama.cppサーバー)を使用します |
|
| openai | はい | OpenAI互換API(例:llama.cppサーバー)を使用 |
|
||||||
|
|
||||||
次のステップ:[サービスの開始とAgenticSeekの実行](#サービスの開始と実行)
|
次のステップ:[サービスを起動してAgenticSeekを実行](#サービスを起動して実行)
|
||||||
|
|
||||||
*問題が発生した場合は、**既知の問題**セクションを参照してください*
|
*問題が発生した場合は、[トラブルシューティング](#トラブルシューティング)セクションを参照してください。*
|
||||||
|
*ハードウェアがローカルでLLMを実行できない場合は、[APIを使用した実行設定](#apiを使用した実行設定)を参照してください。*
|
||||||
|
*詳細な`config.ini`の説明については、[設定セクション](#設定)を参照してください。*
|
||||||
|
|
||||||
*ハードウェアがローカルでdeepseekを実行できない場合は、**APIで実行**セクションを参照してください*
|
## APIを使用した実行設定
|
||||||
|
|
||||||
*詳細な設定ファイルの説明については、**設定**セクションを参照してください。*
|
この設定では、外部のクラウドベースのLLMプロバイダーを使用します。選択したサービスからAPIキーを取得する必要があります。
|
||||||
|
|
||||||
---
|
**1. APIプロバイダーを選択し、APIキーを取得:**
|
||||||
|
|
||||||
## APIで実行するためのセットアップ
|
以下の[APIプロバイダーリスト](#apiプロバイダーリスト)を参照してください。ウェブサイトにアクセスして登録し、APIキーを取得してください。
|
||||||
|
|
||||||
`config.ini`で目的のプロバイダーを設定します。APIプロバイダーのリストについては、以下を参照してください。
|
**2. APIキーを環境変数として設定:**
|
||||||
|
|
||||||
```sh
|
* **Linux/macOS:**
|
||||||
|
ターミナルを開き、`export`コマンドを使用します。永続的にするにはシェルの設定ファイル(例:`~/.bashrc`、`~/.zshrc`)に追加するのがベストです。
|
||||||
|
```sh
|
||||||
|
export PROVIDER_API_KEY="your_api_key_here"
|
||||||
|
# PROVIDER_API_KEYを特定の変数名に置き換えてください、例:OPENAI_API_KEY、GOOGLE_API_KEY
|
||||||
|
```
|
||||||
|
TogetherAIの例:
|
||||||
|
```sh
|
||||||
|
export TOGETHER_API_KEY="xxxxxxxxxxxxxxxxxxxxxx"
|
||||||
|
```
|
||||||
|
* **Windows:**
|
||||||
|
* **コマンドプロンプト(現在のセッション限定):**
|
||||||
|
```cmd
|
||||||
|
set PROVIDER_API_KEY=your_api_key_here
|
||||||
|
```
|
||||||
|
* **PowerShell(現在のセッション限定):**
|
||||||
|
```powershell
|
||||||
|
$env:PROVIDER_API_KEY="your_api_key_here"
|
||||||
|
```
|
||||||
|
* **永続的:** Windowsの検索バーで「環境変数」を検索し、「システムの環境変数を編集」をクリックしてから「環境変数...」ボタンをクリックします。適切な名前(例:`OPENAI_API_KEY`)とキーを値として新しいユーザー変数を追加します。
|
||||||
|
|
||||||
|
*(詳細については、FAQを参照してください:[APIキーを設定する方法?](#apiキーを設定する方法))。*
|
||||||
|
|
||||||
|
|
||||||
|
**3. `config.ini`を更新:**
|
||||||
|
```ini
|
||||||
[MAIN]
|
[MAIN]
|
||||||
is_local = False
|
is_local = False
|
||||||
provider_name = google
|
provider_name = openai # またはgoogle、deepseek、togetherAI、huggingface
|
||||||
provider_model = gemini-2.0-flash
|
provider_model = gpt-3.5-turbo # またはgemini-1.5-flash、deepseek-chat、mistralai/Mixtral-8x7B-Instruct-v0.1など
|
||||||
provider_server_address = 127.0.0.1:5000 # 関係ありません
|
provider_server_address = # is_local = Falseの場合、ほとんどのAPIでは無視されるか空にできる
|
||||||
|
# ... その他の設定 ...
|
||||||
```
|
```
|
||||||
警告:設定に末尾のスペースがないことを確認してください。
|
*警告:* configの値に末尾のスペースがないことを確認してください。
|
||||||
|
|
||||||
APIキーをエクスポートします:`export <<PROVIDER>>_API_KEY="xxx"`
|
**APIプロバイダーリスト**
|
||||||
|
|
||||||
例:`export TOGETHER_API_KEY="xxxxx"`
|
| プロバイダー | `provider_name` | ローカル? | 説明 | APIキーリンク(例) |
|
||||||
|
|--------------|-----------------|--------|---------------------------------------------------|---------------------------------------------|
|
||||||
|
| OpenAI | `openai` | いいえ | OpenAIのAPIを通じてChatGPTモデルを使用。 | [platform.openai.com/signup](https://platform.openai.com/signup) |
|
||||||
|
| Google Gemini| `google` | いいえ | Google AI Studioを通じてGoogle Geminiモデルを使用。 | [aistudio.google.com/keys](https://aistudio.google.com/keys) |
|
||||||
|
| Deepseek | `deepseek` | いいえ | 彼らのAPIを通じてDeepseekモデルを使用。 | [platform.deepseek.com](https://platform.deepseek.com) |
|
||||||
|
| Hugging Face | `huggingface` | いいえ | Hugging Face Inference APIのモデルを使用。 | [huggingface.co/settings/tokens](https://huggingface.co/settings/tokens) |
|
||||||
|
| TogetherAI | `togetherAI` | いいえ | TogetherAI APIを通じて様々なオープンソースモデルを使用。| [api.together.ai/settings/api-keys](https://api.together.ai/settings/api-keys) |
|
||||||
|
| OpenRouter | `openrouter` | いいえ | OpenRouter APIを通じて様々なオープンソースモデルを使用。| [openrouter.api](https://openrouter.ai/) |
|
||||||
|
|
||||||
**APIプロバイダーのリスト**
|
*注:*
|
||||||
|
* 複雑なウェブブラウジングとタスクプランニングには`gpt-4o`や他のOpenAIモデルの使用は推奨しません。現在のプロンプト最適化はDeepseekなどのモデルを対象としているためです。
|
||||||
|
* コーディング/bashタスクはGeminiで失敗する可能性があります。Deepseek r1用に最適化されたプロンプト形式を無視する傾向があるためです。
|
||||||
|
* `is_local = False`の場合、`config.ini`の`provider_server_address`は通常使用されません。APIエンドポイントは通常、対応するプロバイダーのライブラリで処理されるためです。
|
||||||
|
|
||||||
| プロバイダー | ローカル? | 説明 |
|
次のステップ:[サービスを起動してAgenticSeekを実行](#サービスを起動して実行)
|
||||||
|-----------|--------|-----------------------------------------------------------|
|
|
||||||
| openai | 場合による | ChatGPT APIを使用 |
|
|
||||||
| deepseek-api | いいえ | Deepseek API(非プライベート) |
|
|
||||||
| huggingface| いいえ | Hugging-Face API(非プライベート) |
|
|
||||||
| togetherAI | いいえ | together AI APIを使用(非プライベート) |
|
|
||||||
| google | いいえ | google gemini APIを使用(非プライベート) |
|
|
||||||
|
|
||||||
*gpt-4oや他のclosedAIモデルの使用は推奨しません*。ウェブブラウジングやタスク計画のパフォーマンスが悪いです。
|
|
||||||
|
|
||||||
また、geminiではコーディング/bashが失敗する可能性があることに注意してください。deepseek r1用に最適化されたフォーマットのプロンプトを無視するようです。
|
|
||||||
|
|
||||||
次のステップ:[サービスの開始とAgenticSeekの実行](#サービスの開始と実行)
|
|
||||||
|
|
||||||
*問題が発生した場合は、**既知の問題**セクションを参照してください*
|
*問題が発生した場合は、**既知の問題**セクションを参照してください*
|
||||||
|
|
||||||
*詳細な設定ファイルの説明については、**設定**セクションを参照してください。*
|
*詳細な設定ファイルの説明については、**設定セクション**を参照してください。*
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## サービスの開始と実行
|
## サービスを起動して実行
|
||||||
|
|
||||||
|
デフォルトでは、AgenticSeekは完全にDocker内で実行されます。
|
||||||
|
|
||||||
|
**オプション1:** DockerでWebインターフェースを使用して実行:
|
||||||
|
|
||||||
|
必要なサービスを起動します。これにより、docker-compose.ymlのすべてのサービスが起動します:
|
||||||
|
- searxng
|
||||||
|
- redis(searxngに必要)
|
||||||
|
- frontend
|
||||||
|
- backend(Webインターフェースに`full`を使用する場合)
|
||||||
|
|
||||||
必要に応じてPython環境をアクティブ化します。
|
|
||||||
```sh
|
```sh
|
||||||
source agentic_seek_env/bin/activate
|
./start_services.sh full # MacOS
|
||||||
|
start start_services.cmd full # Windows
|
||||||
```
|
```
|
||||||
|
|
||||||
必要なサービスを開始します。これにより、docker-compose.ymlからすべてのサービスが開始されます。これには以下が含まれます:
|
**警告:** このステップではすべてのDockerイメージがダウンロードされロードされます。最大30分かかる場合があります。サービスを起動した後、メッセージを送信する前にバックエンドサービスが完全に実行されていることを確認してください(ログに**backend: "GET /health HTTP/1.1" 200 OK**が表示されるはずです)。初回実行時、バックエンドサービスは起動に5分かかる場合があります。
|
||||||
|
|
||||||
|
`http://localhost:3000/`にアクセスすると、Webインターフェースが表示されます。
|
||||||
|
|
||||||
|
*サービス起動のトラブルシューティング:* これらのスクリプトが失敗する場合は、Docker Engineが実行中でDocker Compose(V2、`docker compose`)が正しくインストールされていることを確認してください。ターミナル出力のエラーメッセージを確認してください。[FAQ: ヘルプ!AgenticSeekまたはそのスクリプトを実行するとエラーが発生します](#faq-トラブルシューティング)を参照してください。
|
||||||
|
|
||||||
|
**オプション2:** CLIモード:
|
||||||
|
|
||||||
|
CLIインターフェースで実行するには、ホストにパッケージをインストールする必要があります:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
./install.sh
|
||||||
|
./install.bat # windows
|
||||||
|
```
|
||||||
|
|
||||||
|
次に、`config.ini`のSEARXNG_BASE_URLを以下に変更する必要があります:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
SEARXNG_BASE_URL="http://localhost:8080"
|
||||||
|
```
|
||||||
|
|
||||||
|
必要なサービスを起動します。これにより、docker-compose.ymlの一部のサービスが起動します:
|
||||||
- searxng
|
- searxng
|
||||||
- redis(searxngに必要)
|
- redis(searxngに必要)
|
||||||
- frontend
|
- frontend
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
sudo ./start_services.sh # MacOS
|
./start_services.sh # MacOS
|
||||||
start ./start_services.cmd # Window
|
start start_services.cmd # Windows
|
||||||
```
|
```
|
||||||
|
|
||||||
**オプション1:** CLIインターフェースで実行します。
|
実行:uv run: `uv run python -m ensurepip` でuvがpipを有効にしていることを確認します。
|
||||||
|
|
||||||
```sh
|
CLIを使用:`uv run cli.py`
|
||||||
python3 cli.py
|
|
||||||
```
|
|
||||||
|
|
||||||
CLIモードでは、config.iniで`headless_browser`をFalseに設定することをお勧めします。
|
|
||||||
|
|
||||||
**オプション2:** Webインターフェースで実行します。
|
|
||||||
|
|
||||||
バックエンドを開始します。
|
|
||||||
|
|
||||||
```sh
|
|
||||||
python3 api.py
|
|
||||||
```
|
|
||||||
|
|
||||||
`http://localhost:3000/`にアクセスすると、Webインターフェースが表示されます。
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 使用方法
|
## 使用方法
|
||||||
|
|
||||||
`./start_services.sh`でサービスが起動していることを確認し、CLIモードの場合は`python3 cli.py`で、Webインターフェースの場合は`python3 api.py`を実行してから`localhost:3000`にアクセスしてAgenticSeekを実行します。
|
サービスが`./start_services.sh full`で実行されていることを確認し、`localhost:3000`にアクセスしてWebインターフェースを使用します。
|
||||||
|
|
||||||
設定で`listen = True`を設定することで、音声認識を使用することもできます。CLIモードのみ。
|
`listen = True`を設定することで音声認識も使用できます。CLIモードのみ。
|
||||||
|
|
||||||
終了するには、単に`goodbye`と発言/入力します。
|
終了するには、単に`goodbye`と言う/入力します。
|
||||||
|
|
||||||
以下に使用例をいくつか示します:
|
使用例:
|
||||||
|
|
||||||
> *Pythonでスネークゲームを作って!*
|
> *Pythonでスネークゲームを作って!*
|
||||||
|
|
||||||
> *フランスのレンヌでトップのカフェをウェブ検索し、3つのカフェのリストとその住所をrennes_cafes.txtに保存して。*
|
> *ウェブでフランスのレンヌの最高のカフェを検索し、3つとその住所をrennes_cafes.txtに保存して*
|
||||||
|
|
||||||
> *数値の階乗を計算するGoプログラムを書いて、それをfactorial.goとしてワークスペースに保存して。*
|
> *階乗を計算するGoプログラムを書き、factorial.goとしてワークスペースに保存して*
|
||||||
|
|
||||||
> *summer_picturesフォルダ内のすべてのJPGファイルを検索し、今日の日付で名前を変更し、名前変更されたファイルのリストをphotos_list.txtに保存して。*
|
> *summer_picturesフォルダ内のすべてのJPGファイルを検索し、今日の日付で名前を変更し、名前変更されたファイルのリストをphotos_list.txtに保存して*
|
||||||
|
|
||||||
> *2024年の人気のSF映画をオンラインで検索し、今夜観る映画を3つ選んで。リストをmovie_night.txtに保存して。*
|
> *オンラインで2024年の人気SF映画を検索し、今夜見るために3つ選び、movie_night.txtに保存して*
|
||||||
|
|
||||||
> *2025年の最新AIニュース記事をウェブで検索し、3つ選択して、それらのタイトルと要約をスクレイピングするPythonスクリプトを書いて。スクリプトをnews_scraper.pyとして、要約を/home/projectsのai_news.txtに保存して。*
|
> *ウェブで2025年の最新AIニュース記事を検索し、3つ選び、タイトルと要約を抽出するPythonスクリプトを書き、スクリプトをnews_scraper.pyとして保存し、要約をai_news.txtに保存(/home/projects)*
|
||||||
|
|
||||||
> *金曜日、無料の株価APIをウェブで検索し、supersuper7434567@gmail.comで登録し、そのAPIを使用してテスラの日々の価格を取得するPythonスクリプトを書いて、結果をstock_prices.csvに保存して。*
|
> *金曜日、無料の株価APIをウェブ検索し、supersuper7434567@gmail.comで登録し、APIを使用してテスラの日次株価を取得するPythonスクリプトを書き、結果をstock_prices.csvに保存して*
|
||||||
|
|
||||||
*フォーム入力機能はまだ実験的であり、失敗する可能性があることに注意してください。*
|
*フォーム入力はまだ実験的であり、失敗する可能性があることに注意してください。*
|
||||||
|
|
||||||
|
クエリを入力すると、AgenticSeekが最適なエージェントをタスクに割り当てます。
|
||||||
|
|
||||||
|
これは初期プロトタイプであるため、エージェントルーティングシステムは常にクエリに正しいエージェントを割り当てられるとは限りません。
|
||||||
|
|
||||||
クエリを入力すると、AgenticSeekはタスクに最適なエージェントを割り当てます。
|
したがって、あなたが何を望んでいるか、そしてAIがどのように進めるかを非常に明確に表現する必要があります。例えば、ウェブ検索をしてほしい場合は、次のように言わないでください:
|
||||||
|
|
||||||
これは初期のプロトタイプであるため、エージェントルーティングシステムがクエリに基づいて常に適切なエージェントを割り当てるとは限りません。
|
`一人旅に適した国を知っていますか?`
|
||||||
|
|
||||||
したがって、何をしたいのか、AIがどのように進むべきかについて非常に明確にする必要があります。たとえば、ウェブ検索を実行させたい場合は、次のように言わないでください:
|
代わりに、次のように言ってください:
|
||||||
|
|
||||||
`一人旅に適した良い国を知っていますか?`
|
`ウェブ検索を実行し、一人旅に最適な国を見つけてください`
|
||||||
|
|
||||||
代わりに、次のように尋ねてください:
|
|
||||||
|
|
||||||
`ウェブ検索をして、一人旅に最適な国を見つけてください`
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## **独自のサーバーでLLMを実行するためのセットアップ**
|
## **独自のサーバーでLLMを実行する設定**
|
||||||
|
|
||||||
強力なコンピューターまたは使用できるサーバーがあるが、ラップトップから使用したい場合は、カスタムLLMサーバーを使用してリモートサーバーでLLMを実行するオプションがあります。
|
強力なコンピューターやアクセス可能なサーバーを持っているが、ラップトップから使用したい場合は、カスタムllmサーバーを使用してリモートサーバーでLLMを実行することを選択できます。
|
||||||
|
|
||||||
AIモデルを実行する「サーバー」で、IPアドレスを取得します。
|
AIモデルを実行する「サーバー」で、IPアドレスを取得します
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
ip a | grep "inet " | grep -v 127.0.0.1 | awk '{print $2}' | cut -d/ -f1 # ローカルIP
|
ip a | grep "inet " | grep -v 127.0.0.1 | awk '{print $2}' | cut -d/ -f1 # ローカルIP
|
||||||
curl https://ipinfo.io/ip # パブリックIP
|
curl https://ipinfo.io/ip # パブリックIP
|
||||||
```
|
```
|
||||||
|
|
||||||
注意:WindowsまたはmacOSの場合、それぞれipconfigまたはifconfigを使用してIPアドレスを見つけます。
|
注:WindowsまたはmacOSでは、IPアドレスを見つけるためにipconfigまたはifconfigを使用してください。
|
||||||
|
|
||||||
リポジトリをクローンし、`server/`フォルダに入ります。
|
|
||||||
|
|
||||||
|
リポジトリをクローンし、`server/`フォルダに移動します。
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
git clone --depth 1 https://github.com/Fosowl/agenticSeek.git
|
git clone --depth 1 https://github.com/Fosowl/agenticSeek.git
|
||||||
cd agenticSeek/server/
|
cd agenticSeek/llm_server/
|
||||||
```
|
```
|
||||||
|
|
||||||
サーバー固有の要件をインストールします:
|
サーバー固有の要件をインストールします:
|
||||||
@@ -339,16 +364,15 @@ cd agenticSeek/server/
|
|||||||
pip3 install -r requirements.txt
|
pip3 install -r requirements.txt
|
||||||
```
|
```
|
||||||
|
|
||||||
サーバー スクリプトを実行します。
|
サーバースクリプトを実行します。
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
python3 app.py --provider ollama --port 3333
|
python3 app.py --provider ollama --port 3333
|
||||||
```
|
```
|
||||||
|
|
||||||
LLMサービスとして`ollama`と`llamacpp`のどちらかを選択できます。
|
LLMサービスとして`ollama`と`llamacpp`のどちらを使用するか選択できます。
|
||||||
|
|
||||||
|
次に、あなたのパーソナルコンピューターで:
|
||||||
次に、個人のコンピュータで:
|
|
||||||
|
|
||||||
`config.ini`ファイルを変更して、`provider_name`を`server`に、`provider_model`を`deepseek-r1:xxb`に設定します。
|
`config.ini`ファイルを変更して、`provider_name`を`server`に、`provider_model`を`deepseek-r1:xxb`に設定します。
|
||||||
`provider_server_address`をモデルを実行するマシンのIPアドレスに設定します。
|
`provider_server_address`をモデルを実行するマシンのIPアドレスに設定します。
|
||||||
@@ -358,16 +382,17 @@ LLMサービスとして`ollama`と`llamacpp`のどちらかを選択できま
|
|||||||
is_local = False
|
is_local = False
|
||||||
provider_name = server
|
provider_name = server
|
||||||
provider_model = deepseek-r1:70b
|
provider_model = deepseek-r1:70b
|
||||||
provider_server_address = x.x.x.x:3333
|
provider_server_address = http://x.x.x.x:3333
|
||||||
```
|
```
|
||||||
|
|
||||||
|
次のステップ:[サービスを起動してAgenticSeekを実行](#サービスを起動して実行)
|
||||||
次のステップ:[サービスの開始とAgenticSeekの実行](#サービスの開始と実行)
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 音声認識
|
## 音声認識
|
||||||
|
|
||||||
|
警告:現在、音声認識はCLIモードでのみ機能します。
|
||||||
|
|
||||||
現在、音声認識は英語でのみ機能することに注意してください。
|
現在、音声認識は英語でのみ機能することに注意してください。
|
||||||
|
|
||||||
音声認識機能はデフォルトで無効になっています。有効にするには、config.iniファイルでlistenオプションをTrueに設定します:
|
音声認識機能はデフォルトで無効になっています。有効にするには、config.iniファイルでlistenオプションをTrueに設定します:
|
||||||
@@ -376,19 +401,19 @@ provider_server_address = x.x.x.x:3333
|
|||||||
listen = True
|
listen = True
|
||||||
```
|
```
|
||||||
|
|
||||||
有効にすると、音声認識機能は、入力を処理し始める前にトリガーキーワード(エージェントの名前)をリッスンします。*config.ini*ファイルで`agent_name`の値を更新することで、エージェントの名前をカスタマイズできます:
|
有効にすると、音声認識機能はトリガーワード、つまりエージェントの名前をリッスンし、その後入力を処理し始めます。*config.ini*ファイルの`agent_name`値を更新することでエージェントの名前をカスタマイズできます:
|
||||||
|
|
||||||
```
|
```
|
||||||
agent_name = Friday
|
agent_name = Friday
|
||||||
```
|
```
|
||||||
|
|
||||||
最適な認識のためには、エージェント名として「John」や「Emma」のような一般的な英語の名前を使用することをお勧めします。
|
最高の認識のためには、エージェント名として「John」や「Emma」などの一般的な英語名を使用することをお勧めします。
|
||||||
|
|
||||||
トランスクリプトが表示され始めたら、エージェントの名前を声に出して起動します(例:「Friday」)。
|
文字起こしが表示され始めたら、エージェントの名前を大声で言って起動します(例:「Friday」)。
|
||||||
|
|
||||||
クエリをはっきりと話します。
|
クエリを明確に言います。
|
||||||
|
|
||||||
システムに処理を進めるよう合図するために、確認フレーズでリクエストを終了します。確認フレーズの例は次のとおりです:
|
確認フレーズでリクエストを終了して、システムに続行するように指示します。確認フレーズの例:
|
||||||
```
|
```
|
||||||
"do it", "go ahead", "execute", "run", "start", "thanks", "would ya", "please", "okay?", "proceed", "continue", "go on", "do that", "go it", "do you understand?"
|
"do it", "go ahead", "execute", "run", "start", "thanks", "would ya", "please", "okay?", "proceed", "continue", "go on", "do that", "go it", "do you understand?"
|
||||||
```
|
```
|
||||||
@@ -401,170 +426,258 @@ agent_name = Friday
|
|||||||
is_local = True
|
is_local = True
|
||||||
provider_name = ollama
|
provider_name = ollama
|
||||||
provider_model = deepseek-r1:32b
|
provider_model = deepseek-r1:32b
|
||||||
provider_server_address = 127.0.0.1:11434
|
provider_server_address = http://127.0.0.1:11434 # Ollama例;LM-Studioはhttp://127.0.0.1:1234を使用
|
||||||
agent_name = Friday
|
agent_name = Friday
|
||||||
recover_last_session = False
|
recover_last_session = False
|
||||||
save_session = False
|
save_session = False
|
||||||
speak = False
|
speak = False
|
||||||
listen = False
|
listen = False
|
||||||
work_dir = /Users/mlg/Documents/ai_folder
|
|
||||||
jarvis_personality = False
|
jarvis_personality = False
|
||||||
languages = en zh
|
languages = en zh # TTSおよび潜在的なルーティングの言語リスト。
|
||||||
[BROWSER]
|
[BROWSER]
|
||||||
headless_browser = False
|
headless_browser = False
|
||||||
stealth_mode = False
|
stealth_mode = False
|
||||||
```
|
```
|
||||||
|
|
||||||
**説明**:
|
**`config.ini`設定の説明**:
|
||||||
|
|
||||||
- is_local -> エージェントをローカルで実行する(True)か、リモートサーバーで実行する(False)か。
|
* **`[MAIN]`セクション:**
|
||||||
|
* `is_local`: ローカルLLMプロバイダー(Ollama、LM-Studio、ローカルOpenAI互換サーバー)またはセルフホストサーバーオプションを使用する場合は`True`。クラウドベースのAPI(OpenAI、Googleなど)を使用する場合は`False`。
|
||||||
|
* `provider_name`: LLMプロバイダーを指定します。
|
||||||
|
* ローカルオプション:`ollama`、`lm-studio`、`openai`(ローカルOpenAI互換サーバー用)、`server`(セルフホストサーバー設定用)。
|
||||||
|
* APIオプション:`openai`、`google`、`deepseek`、`huggingface`、`togetherAI`。
|
||||||
|
* `provider_model`: 選択したプロバイダーの特定のモデル名またはID(例:Ollamaの`deepseekcoder:6.7b`、OpenAI APIの`gpt-3.5-turbo`、TogetherAIの`mistralai/Mixtral-8x7B-Instruct-v0.1`)。
|
||||||
|
* `provider_server_address`: あなたのLLMプロバイダーのアドレス。
|
||||||
|
* ローカルプロバイダー用:例:Ollamaの`http://127.0.0.1:11434`、LM-Studioの`http://127.0.0.1:1234`。
|
||||||
|
* `server`プロバイダータイプ用:あなたのセルフホストLLMサーバーのアドレス(例:`http://your_server_ip:3333`)。
|
||||||
|
* クラウドAPI用(`is_local = False`):これは通常無視されるか空にできます。APIエンドポイントは通常クライアントライブラリで処理されるためです。
|
||||||
|
* `agent_name`: AIアシスタントの名前(例:Friday)。有効な場合、音声認識のトリガーワードとして使用されます。
|
||||||
|
* `recover_last_session`: `True`は前のセッションの状態を復元しようとし、`False`は最初から開始します。
|
||||||
|
* `save_session`: `True`は現在のセッションの状態を潜在的な復元用に保存し、`False`はしません。
|
||||||
|
* `speak`: `True`はテキスト読み上げ音声出力を有効にし、`False`は無効にします。
|
||||||
|
* `listen`: `True`は音声認識音声入力を有効にし(CLIモードのみ)、`False`は無効にします。
|
||||||
|
* `work_dir`: **重要:** AgenticSeekがファイルを読み書きするディレクトリ。**このパスがシステムで有効かつアクセス可能であることを確認してください。**
|
||||||
|
* `jarvis_personality`: `True`はより「Jarvis-like」なシステムプロンプトを使用(実験的)、`False`は標準プロンプトを使用。
|
||||||
|
* `languages`: カンマ区切りの言語リスト(例:`en, zh, fr`)。TTS音声選択(デフォルトは最初)に使用され、LLMルーターを支援できます。ルーターの非効率性を避けるため、多すぎる言語や非常に類似した言語の使用は避けてください。
|
||||||
|
* **`[BROWSER]`セクション:**
|
||||||
|
* `headless_browser`: `True`は可視ウィンドウなしで自動化ブラウザを実行(Webインターフェースまたは非対話的使用に推奨)。`False`はブラウザウィンドウを表示(CLIモードまたはデバッグに有用)。
|
||||||
|
* `stealth_mode`: `True`はブラウザ自動化の検出を困難にする措置を有効にします。anticaptchaなどのブラウザ拡張機能の手動インストールが必要な場合があります。
|
||||||
|
|
||||||
- provider_name -> 使用するプロバイダー(`ollama`、`server`、`lm-studio`、`deepseek-api`のいずれか)
|
このセクションはサポートされているLLMプロバイダータイプをまとめています。`config.ini`で設定します。
|
||||||
|
|
||||||
- provider_model -> 使用するモデル、例:deepseek-r1:32b。
|
**ローカルプロバイダー(独自のハードウェアで実行):**
|
||||||
|
|
||||||
- provider_server_address -> サーバーアドレス、例:ローカルの場合は127.0.0.1:11434。非ローカルAPIの場合は何でも設定します。
|
| config.iniのプロバイダー名 | `is_local` | 説明 | 設定セクション |
|
||||||
|
|-------------------------------|------------|-----------------------------------------------------------------------------|------------------------------------------------------------------|
|
||||||
|
| `ollama` | `True` | Ollamaを使用してローカルでLLMを簡単に提供。 | [マシン上でローカルにLLMを実行する設定](#マシン上でローカルにllmを実行する設定) |
|
||||||
|
| `lm-studio` | `True` | LM-StudioでローカルにLLMを提供。 | [マシン上でローカルにLLMを実行する設定](#マシン上でローカルにllmを実行する設定) |
|
||||||
|
| `openai`(ローカルサーバー用) | `True` | OpenAI互換APIを公開するローカルサーバー(例:llama.cpp)に接続。 | [マシン上でローカルにLLMを実行する設定](#マシン上でローカルにllmを実行する設定) |
|
||||||
|
| `server` | `False` | 別のマシンで実行されているAgenticSeekセルフホストLLMサーバーに接続。 | [独自のサーバーでLLMを実行する設定](#独自のサーバーでllmを実行する設定) |
|
||||||
|
|
||||||
- agent_name -> エージェントの名前、例:Friday。TTSのトリガーワードとして使用されます。
|
**APIプロバイダー(クラウドベース):**
|
||||||
|
|
||||||
- recover_last_session -> 前回のセッションから再開する(True)かしない(False)か。
|
| config.iniのプロバイダー名 | `is_local` | 説明 | 設定セクション |
|
||||||
|
|-------------------------------|------------|--------------------------------------------------|-----------------------------------------------------|
|
||||||
|
| `openai` | `False` | OpenAIの公式API(例:GPT-3.5、GPT-4)を使用。 | [APIを使用した実行設定](#apiを使用した実行設定) |
|
||||||
|
| `google` | `False` | APIを通じてGoogleのGeminiモデルを使用。 | [APIを使用した実行設定](#apiを使用した実行設定) |
|
||||||
|
| `deepseek` | `False` | Deepseekの公式APIを使用。 | [APIを使用した実行設定](#apiを使用した実行設定) |
|
||||||
|
| `huggingface` | `False` | Hugging Face Inference APIを使用。 | [APIを使用した実行設定](#apiを使用した実行設定) |
|
||||||
|
| `togetherAI` | `False` | TogetherAIのAPIを通じて様々なオープンモデルを使用。 | [APIを使用した実行設定](#apiを使用した実行設定) |
|
||||||
|
|
||||||
- save_session -> セッションデータを保存する(True)かしない(False)か。
|
---
|
||||||
|
## トラブルシューティング
|
||||||
|
|
||||||
- speak -> 音声出力を有効にする(True)かしない(False)か。
|
問題が発生した場合、このセクションはガイダンスを提供します。
|
||||||
|
|
||||||
- listen -> 音声入力をリッスンする(True)かしない(False)か。
|
|
||||||
|
|
||||||
- work_dir -> AIがアクセスできるフォルダ。例:/Users/user/Documents/。
|
|
||||||
|
|
||||||
- jarvis_personality -> JARVISのような性格を使用する(True)かしない(False)か。これは単にプロンプトファイルを変更します。
|
|
||||||
|
|
||||||
- languages -> サポートされている言語のリスト。LLMルーターが正しく機能するために必要です。あまりにも多くの言語や類似した言語を入れすぎないようにしてください。
|
|
||||||
|
|
||||||
- headless_browser -> 表示ウィンドウなしでブラウザを実行する(True)かしない(False)か。
|
|
||||||
|
|
||||||
- stealth_mode -> ボット検出を困難にします。唯一の欠点は、anticaptcha拡張機能を手動でインストールする必要があることです。
|
|
||||||
|
|
||||||
- languages -> サポートされている言語のリスト。エージェントルーティングシステムに必要です。言語リストが長いほど、ダウンロードされるモデルが多くなります。
|
|
||||||
|
|
||||||
## プロバイダー
|
|
||||||
|
|
||||||
以下の表は、利用可能なプロバイダーを示しています:
|
|
||||||
|
|
||||||
| プロバイダー | ローカル? | 説明 |
|
|
||||||
|-----------|--------|-----------------------------------------------------------|
|
|
||||||
| ollama | はい | ollamaをLLMプロバイダーとして使用して、LLMをローカルで簡単に実行します |
|
|
||||||
| server | はい | モデルを別のマシンでホストし、ローカルマシンで実行します |
|
|
||||||
| lm-studio | はい | LM studioでLLMをローカル実行します(`lm-studio`) |
|
|
||||||
| openai | 場合による | ChatGPT API(非プライベート)またはopenai互換APIを使用 |
|
|
||||||
| deepseek-api | いいえ | Deepseek API(非プライベート) |
|
|
||||||
| huggingface| いいえ | Hugging-Face API(非プライベート) |
|
|
||||||
| togetherAI | いいえ | together AI APIを使用(非プライベート) |
|
|
||||||
| google | いいえ | google gemini APIを使用(非プライベート) |
|
|
||||||
|
|
||||||
プロバイダーを選択するには、config.iniを変更します:
|
|
||||||
|
|
||||||
```
|
|
||||||
is_local = True
|
|
||||||
provider_name = ollama
|
|
||||||
provider_model = deepseek-r1:32b
|
|
||||||
provider_server_address = 127.0.0.1:5000
|
|
||||||
```
|
|
||||||
`is_local`: ローカルで実行されるLLMの場合はTrue、それ以外の場合はFalseである必要があります。
|
|
||||||
|
|
||||||
`provider_name`: 使用するプロバイダーを名前で選択します。上記のプロバイダーリストを参照してください。
|
|
||||||
|
|
||||||
`provider_model`: エージェントが使用するモデルを設定します。
|
|
||||||
|
|
||||||
`provider_server_address`: サーバーアドレス。APIプロバイダーには使用されません。
|
|
||||||
|
|
||||||
# 既知の問題
|
# 既知の問題
|
||||||
|
|
||||||
## Chromedriverの問題
|
## ChromeDriverの問題
|
||||||
|
|
||||||
**既知のエラー #1:** *chromedriverの不一致*
|
**エラー例:** `SessionNotCreatedException: Message: session not created: This version of ChromeDriver only supports Chrome version XXX`
|
||||||
|
|
||||||
|
### 根本原因
|
||||||
|
ChromeDriverのバージョン非互換性は以下で発生します:
|
||||||
|
1. インストールしたChromeDriverのバージョンがChromeブラウザのバージョンと一致しない
|
||||||
|
2. Docker環境では、`undetected_chromedriver`が独自のChromeDriverバージョンをダウンロードし、マウントされたバイナリを回避する可能性がある
|
||||||
|
|
||||||
|
### 解決手順
|
||||||
|
|
||||||
|
#### 1. Chromeのバージョンを確認
|
||||||
|
Google Chromeを開く → `設定 > Chromeについて`でバージョンを確認(例:「バージョン 134.0.6998.88」)
|
||||||
|
|
||||||
|
#### 2. 一致するChromeDriverをダウンロード
|
||||||
|
|
||||||
|
**Chrome 115以降の場合:** [Chrome for Testing API](https://googlechromelabs.github.io/chrome-for-testing/)を使用
|
||||||
|
- Chrome for Testingの可用性ダッシュボードにアクセス
|
||||||
|
- あなたのChromeバージョンまたは最も近い利用可能な一致を見つける
|
||||||
|
- オペレーティングシステム用のChromeDriverをダウンロード(Docker環境ではLinux64を使用)
|
||||||
|
|
||||||
|
**古いChromeバージョンの場合:** [レガシーChromeDriverダウンロード](https://chromedriver.chromium.org/downloads)を使用
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
#### 3. ChromeDriverをインストール(方法を選択)
|
||||||
|
|
||||||
|
**方法A:プロジェクトルートディレクトリ(Docker推奨)**
|
||||||
|
```bash
|
||||||
|
# ダウンロードしたchromedriverバイナリをプロジェクトルートディレクトリに配置
|
||||||
|
cp path/to/downloaded/chromedriver ./chromedriver
|
||||||
|
chmod +x ./chromedriver # Linux/macOSで実行可能にする
|
||||||
|
```
|
||||||
|
|
||||||
|
**方法B:システムPATH**
|
||||||
|
```bash
|
||||||
|
# Linux/macOS
|
||||||
|
sudo mv chromedriver /usr/local/bin/
|
||||||
|
sudo chmod +x /usr/local/bin/chromedriver
|
||||||
|
|
||||||
|
# Windows: chromedriver.exeをPATH内のフォルダに配置
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 4. インストールを確認
|
||||||
|
```bash
|
||||||
|
# ChromeDriverのバージョンをテスト
|
||||||
|
./chromedriver --version
|
||||||
|
# またはPATHにある場合:
|
||||||
|
chromedriver --version
|
||||||
|
```
|
||||||
|
|
||||||
|
### Docker固有の指示
|
||||||
|
|
||||||
|
⚠️ **Dockerユーザーへの重要:**
|
||||||
|
- Dockerボリュームマウント方法はステルスモード(`undetected_chromedriver`)では機能しない可能性があります
|
||||||
|
- **解決策:** ChromeDriverをプロジェクトルートディレクトリに`./chromedriver`として配置
|
||||||
|
- アプリケーションが自動的に検出し、このバイナリを使用します
|
||||||
|
- ログに次のメッセージが表示されるはずです:`"Using ChromeDriver from project root: ./chromedriver"`
|
||||||
|
|
||||||
|
### トラブルシューティングのヒント
|
||||||
|
|
||||||
|
1. **まだバージョンの不一致が発生しますか?**
|
||||||
|
- ChromeDriverが実行可能か確認:`ls -la ./chromedriver`
|
||||||
|
- ChromeDriverのバージョンを確認:`./chromedriver --version`
|
||||||
|
- Chromeブラウザのバージョンと一致することを確認
|
||||||
|
|
||||||
|
2. **Dockerコンテナの問題ですか?**
|
||||||
|
- バックエンドログを確認:`docker logs backend`
|
||||||
|
- メッセージを探す:`"Using ChromeDriver from project root"`
|
||||||
|
- 見つからない場合は、ファイルが存在し実行可能であることを確認
|
||||||
|
|
||||||
|
3. **Chrome for Testingのバージョン**
|
||||||
|
- 可能な限り完全一致を使用
|
||||||
|
- バージョン134.0.6998.88の場合、ChromeDriver 134.0.6998.165を使用(最も近い利用可能バージョン)
|
||||||
|
- メジャーバージョン番号は一致する必要があります(134 = 134)
|
||||||
|
|
||||||
|
### バージョン互換性マトリックス
|
||||||
|
|
||||||
|
| Chromeバージョン | ChromeDriverバージョン | ステータス |
|
||||||
|
|----------------|---------------------|---------|
|
||||||
|
| 134.0.6998.x | 134.0.6998.165 | ✅ 利用可能 |
|
||||||
|
| 133.0.6943.x | 133.0.6943.141 | ✅ 利用可能 |
|
||||||
|
| 132.0.6834.x | 132.0.6834.159 | ✅ 利用可能 |
|
||||||
|
|
||||||
|
*最新の互換性については、[Chrome for Testingダッシュボード](https://googlechromelabs.github.io/chrome-for-testing/)を確認*
|
||||||
|
|
||||||
`Exception: Failed to initialize browser: Message: session not created: This version of ChromeDriver only supports Chrome version 113
|
`Exception: Failed to initialize browser: Message: session not created: This version of ChromeDriver only supports Chrome version 113
|
||||||
Current browser version is 134.0.6998.89 with binary path`
|
Current browser version is 134.0.6998.89 with binary path`
|
||||||
|
|
||||||
これは、ブラウザとchromedriverのバージョンが一致しない場合に発生します。
|
ブラウザとchromedriverのバージョンが一致しない場合に発生します。
|
||||||
|
|
||||||
最新バージョンをダウンロードするためにナビゲートする必要があります:
|
最新バージョンをダウンロードする必要があります:
|
||||||
|
|
||||||
https://developer.chrome.com/docs/chromedriver/downloads
|
https://developer.chrome.com/docs/chromedriver/downloads
|
||||||
|
|
||||||
Chromeバージョン115以降を使用している場合は、以下にアクセスしてください:
|
Chromeバージョン115以降を使用している場合は、以下にアクセス:
|
||||||
|
|
||||||
https://googlechromelabs.github.io/chrome-for-testing/
|
https://googlechromelabs.github.io/chrome-for-testing/
|
||||||
|
|
||||||
そして、OSに一致するchromedriverバージョンをダウンロードします。
|
オペレーティングシステムに一致するchromedriverバージョンをダウンロードします。
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
このセクションが不完全な場合は、問題を提起してください。
|
このセクションが不完全な場合は、issueを開いてください。
|
||||||
|
|
||||||
## 接続アダプタの問題
|
## 接続アダプターの問題
|
||||||
|
|
||||||
```
|
```
|
||||||
Exception: Provider lm-studio failed: HTTP request failed: No connection adapters were found for '127.0.0.1:11434/v1/chat/completions'
|
Exception: Provider lm-studio failed: HTTP request failed: No connection adapters were found for '127.0.0.1:1234/v1/chat/completions'`(注:ポートは異なる場合があります)
|
||||||
```
|
```
|
||||||
|
|
||||||
プロバイダーのIPアドレスの前に`http://`があることを確認してください:
|
* **原因:** `config.ini`の`lm-studio`(または他の類似のローカルOpenAI互換サーバー)の`provider_server_address`に`http://`プレフィックスが欠けているか、間違ったポートを指している。
|
||||||
|
* **解決策:**
|
||||||
|
* アドレスに`http://`が含まれていることを確認。LM-Studioは通常デフォルトで`http://127.0.0.1:1234`を使用。
|
||||||
|
* 正しい`config.ini`:`provider_server_address = http://127.0.0.1:1234`(または実際のLM-Studioサーバーポート)。
|
||||||
|
|
||||||
`provider_server_address = http://127.0.0.1:11434`
|
## SearxNGベースURLが提供されていない
|
||||||
|
|
||||||
## SearxNGのベースURLを指定する必要があります
|
|
||||||
|
|
||||||
```
|
```
|
||||||
raise ValueError("SearxNG base URL must be provided either as an argument or via the SEARXNG_BASE_URL environment variable.")
|
raise ValueError("SearxNG base URL must be provided either as an argument or via the SEARXNG_BASE_URL environment variable.")
|
||||||
ValueError: SearxNG base URL must be provided either as an argument or via the SEARXNG_BASE_URL environment variable.
|
ValueError: SearxNG base URL must be provided either as an argument or via the SEARXNG_BASE_URL environment variable.`
|
||||||
```
|
```
|
||||||
|
|
||||||
`.env.example`を`.env`として移動しなかった可能性がありますか?SEARXNG_BASE_URLをエクスポートすることもできます:
|
間違ったsearxngベースURLでCLIモードを実行すると発生する可能性があります。
|
||||||
|
|
||||||
`export SEARXNG_BASE_URL="http://127.0.0.1:8080"`
|
SEARXNG_BASE_URLは、Dockerで実行するかホストで実行するかによって異なります:
|
||||||
|
|
||||||
|
**ホストで実行:** `SEARXNG_BASE_URL="http://localhost:8080"`
|
||||||
|
|
||||||
|
**完全にDocker内で実行(Webインターフェース):** `SEARXNG_BASE_URL="http://searxng:8080"`
|
||||||
|
|
||||||
## FAQ
|
## FAQ
|
||||||
|
|
||||||
**Q: どのようなハードウェアが必要ですか?**
|
**Q: どのようなハードウェアが必要ですか?**
|
||||||
|
|
||||||
| モデルサイズ | GPU | コメント |
|
| モデルサイズ | GPU | コメント |
|
||||||
|-----------|------------|--------------------------------------------------------------------------|
|
|-----------|--------|-----------------------------------------------------------|
|
||||||
| 7B | 8GB VRAM | ⚠️ 非推奨。パフォーマンスが悪く、幻覚が頻繁に発生し、プランナーエージェントは失敗する可能性が高いです。 |
|
| 7B | 8GB VRAM | ⚠️ 非推奨。パフォーマンスが低く、頻繁な幻覚、計画エージェントが失敗する可能性があります。 |
|
||||||
| 14B | 12GB VRAM(例:RTX 3060) | ✅ 簡単なタスクには使用可能。ウェブブラウジングや計画タスクで苦労する可能性があります。 |
|
| 14B | 12 GB VRAM(例:RTX 3060) | ✅ 単純なタスクに使用可能。ウェブブラウジングとタスク計画に困難がある可能性があります。 |
|
||||||
| 32B | 24GB以上のVRAM(例:RTX 4090) | 🚀 ほとんどのタスクで成功しますが、タスク計画でまだ苦労する可能性があります。 |
|
| 32B | 24+ GB VRAM(例:RTX 4090) | 🚀 ほとんどのタスクで成功、タスク計画にまだ困難がある可能性があります |
|
||||||
| 70B+ | 48GB以上のVRAM(例:mac studio) | 💪 素晴らしい。高度なユースケースに推奨されます。 |
|
| 70B+ | 48+ GB VRAM | 💪 優れています。高度な使用例に推奨。 |
|
||||||
|
|
||||||
**Q: なぜ他のモデルではなくDeepseek R1なのですか?**
|
**Q: エラーが発生したらどうすればよいですか?**
|
||||||
|
|
||||||
Deepseek R1は、そのサイズに対して推論とツール使用に優れています。私たちのニーズに合っていると考えており、他のモデルも正常に動作しますが、Deepseekが私たちの主要な選択肢です。
|
ローカルが実行されていること(`ollama serve`)、`config.ini`がプロバイダーと一致していること、依存関係がインストールされていることを確認してください。どれも機能しない場合は、遠慮なくissueを開いてください。
|
||||||
|
|
||||||
**Q: `cli.py`を実行するとエラーが発生します。どうすればよいですか?**
|
|
||||||
|
|
||||||
ローカルが実行されていること(`ollama serve`)、`config.ini`がプロバイダーと一致していること、依存関係がインストールされていることを確認してください。それでも解決しない場合は、遠慮なく問題を提起してください。
|
|
||||||
|
|
||||||
**Q: 本当に100%ローカルで実行できますか?**
|
**Q: 本当に100%ローカルで実行できますか?**
|
||||||
|
|
||||||
はい、Ollama、lm-studio、またはサーバープロバイダーを使用すると、すべての音声認識、LLM、テキスト読み上げモデルがローカルで実行されます。非ローカルオプション(OpenAIまたはその他のAPI)はオプションです。
|
はい、Ollama、lm-studio、またはserverプロバイダーを使用すると、すべての音声認識、LLM、テキスト読み上げモデルがローカルで実行されます。非ローカルオプション(OpenAI或其他API)はオプションです。
|
||||||
|
|
||||||
**Q: Manusがあるのに、なぜAgenticSeekを使うべきなのですか?**
|
**Q: Manusがあるのに、なぜAgenticSeekを使用する必要がありますか?**
|
||||||
|
|
||||||
これは、AIエージェントへの関心から始めたサイドプロジェクトです。特別なのは、ローカルモデルを使用し、APIを避けたいということです。
|
Manusとは異なり、AgenticSeekは外部システムからの独立性を優先し、より多くの制御、プライバシー、APIコストの回避を提供します。
|
||||||
私たちはJarvisとFriday(アイアンマン映画)からインスピレーションを得て「クール」にしましたが、機能性についてはManusからより多くのインスピレーションを得ています。なぜなら、それが人々が最初に望むもの、つまりローカルなManusの代替だからです。
|
|
||||||
Manusとは異なり、AgenticSeekは外部システムからの独立性を優先し、より多くの制御、プライバシーを提供し、APIコストを回避します。
|
|
||||||
|
|
||||||
## 貢献する
|
**Q: このプロジェクトの背後には誰がいますか?**
|
||||||
|
|
||||||
AgenticSeekを改善するための開発者を募集しています!オープンな問題やディスカッションを確認してください。
|
このプロジェクトは私によって作成され、2人の友人がメンテナーとして、GitHub上のオープンソースコミュニティの貢献者と共に運営されています。私たちは単なる情熱的な個人であり、スタートアップではなく、どの組織にも所属していません。
|
||||||
|
|
||||||
|
私の個人アカウント(https://x.com/Martin993886460)以外のX上のAgenticSeekアカウントはすべて偽物です。
|
||||||
|
|
||||||
|
## 貢献
|
||||||
|
|
||||||
|
AgenticSeekを改善する開発者を探しています!オープンなissueやディスカッションを確認してください。
|
||||||
|
|
||||||
[貢献ガイド](./docs/CONTRIBUTING.md)
|
[貢献ガイド](./docs/CONTRIBUTING.md)
|
||||||
|
|
||||||
[](https://www.star-history.com/#Fosowl/agenticSeek&Date)
|
## スポンサー:
|
||||||
|
|
||||||
|
フライト検索、旅行計画、または最高の買い物のお得な情報の取得などの機能でAgenticSeekの能力を向上させたいですか?SerpApiを使用してカスタムツールを作成し、より多くのJarvisのような機能を解放することを検討してください。SerpApiを使用すると、プロフェッショナルなタスクのためにエージェントを加速させながら、完全な制御を維持できます。
|
||||||
|
|
||||||
|
<a href="https://serpapi.com/"><img src="./media/banners/sponsor_banner_serpapi.png" height="350" alt="SerpApi Banner" ></a>
|
||||||
|
|
||||||
|
[Contributing.md](./docs/CONTRIBUTING.md)をチェックして、カスタムツールを統合する方法を学びましょう!
|
||||||
|
|
||||||
|
### **スポンサー**:
|
||||||
|
|
||||||
|
- [tatra-labs](https://github.com/tatra-labs)
|
||||||
|
|
||||||
## メンテナー:
|
## メンテナー:
|
||||||
|
|
||||||
> [Fosowl](https://github.com/Fosowl) | パリ時間
|
> [Fosowl](https://github.com/Fosowl) | パリ時間
|
||||||
|
|
||||||
> [antoineVIVIES](https://github.com/antoineVIVIES) | Taipei Time
|
> [antoineVIVIES](https://github.com/antoineVIVIES) | 台北時間
|
||||||
|
|
||||||
> [steveh8758](https://github.com/steveh8758) | 台北時間 |(常に忙しい)
|
## 特別な感謝:
|
||||||
|
|
||||||
|
> [tcsenpai](https://github.com/tcsenpai) と [plitc](https://github.com/plitc) がバックエンドのDocker化を支援
|
||||||
|
|
||||||
|
[](https://www.star-history.com/#Fosowl/agenticSeek&Date)
|
||||||
|
|||||||
@@ -0,0 +1,682 @@
|
|||||||
|
# AgenticSeek: Uma Alternativa Privada e Local ao Manus
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img align="center" src="./media/agentic_seek_logo.png" width="300" height="300" alt="Agentic Seek Logo">
|
||||||
|
<p>
|
||||||
|
|
||||||
|
English | [中文](./README_CHS.md) | [繁體中文](./README_CHT.md) | [Français](./README_FR.md) | [日本語](./README_JP.md) | [Português (Brasil)](./README_PTBR.md) | [Español](./README_ES.md) | [Türkçe](./README_TR.md)
|
||||||
|
|
||||||
|
*Um assistente de IA com reconhecimento de voz que é uma **alternativa 100% local ao Manus AI**, navega autonomamente na web, escreve código e planeja tarefas enquanto mantém todos os dados no seu dispositivo. Projetado para modelos de raciocínio local, funciona inteiramente no seu hardware, garantindo total privacidade e zero dependência de nuvem.*
|
||||||
|
|
||||||
|
[](https://fosowl.github.io/agenticSeek.html)  [](https://discord.gg/8hGDaME3TC) [](https://x.com/Martin993886460) [](https://github.com/Fosowl/agenticSeek/stargazers)
|
||||||
|
|
||||||
|
### Por que escolher o AgenticSeek?
|
||||||
|
|
||||||
|
* 🔒 Totalmente Local & Privado - Tudo funciona na sua máquina, sem nuvem, sem compartilhamento de dados. Seus arquivos, conversas e pesquisas permanecem privados.
|
||||||
|
|
||||||
|
* 🌐 Navegação Web Inteligente - O AgenticSeek pode navegar na Internet autonomamente: pesquisar, ler, extrair informações, preencher formulários web, tudo sem intervenção manual.
|
||||||
|
|
||||||
|
* 💻 Assistente de Programação Autônomo - Precisa de código? Ele pode escrever, depurar e executar programas em Python, C, Go, Java e muito mais, sem supervisão.
|
||||||
|
|
||||||
|
* 🧠 Seleção Inteligente de Agentes - Você pergunta, ele escolhe automaticamente o melhor agente para a tarefa. Como ter uma equipe de especialistas sempre disponível.
|
||||||
|
|
||||||
|
* 📋 Planeja e Executa Tarefas Complexas - Desde o planejamento de viagens até projetos complexos, ele pode decompor grandes tarefas em etapas e completá-las usando múltiplos agentes de IA.
|
||||||
|
|
||||||
|
* 🎙️ Suporte de Voz - Voz clara, rápida e futurista com reconhecimento de voz, permitindo que você converse como com sua IA pessoal de filme de ficção científica. (Em desenvolvimento)
|
||||||
|
|
||||||
|
### **Demo**
|
||||||
|
|
||||||
|
> *Você pode pesquisar o projeto agenticSeek, aprender quais habilidades são necessárias e, em seguida, abrir CV_candidates.zip e me dizer quais correspondem melhor ao projeto?*
|
||||||
|
|
||||||
|
https://github.com/user-attachments/assets/b8ca60e9-7b3b-4533-840e-08f9ac426316
|
||||||
|
|
||||||
|
Aviso: Esta demonstração e todos os arquivos que aparecem (ex: CV_candidates.zip) são totalmente fictícios. Não somos uma empresa, estamos procurando contribuidores de código aberto, não candidatos.
|
||||||
|
|
||||||
|
> 🛠⚠️️ **Trabalho Ativo em Andamento**
|
||||||
|
|
||||||
|
> 🙏 Este projeto começou como um projeto paralelo e não tem roadmap nem financiamento. Cresceu muito além das expectativas ao aparecer no GitHub Trending. Contribuições, comentários e paciência são profundamente apreciados.
|
||||||
|
|
||||||
|
## Pré-requisitos
|
||||||
|
|
||||||
|
Antes de começar, certifique-se de ter instalado:
|
||||||
|
|
||||||
|
* **Git:** Para clonar o repositório. [Baixar Git](https://git-scm.com/downloads)
|
||||||
|
* **Python 3.10.x:** Python 3.10.x é altamente recomendado. Outras versões podem causar erros de dependência. [Baixar Python 3.10](https://www.python.org/downloads/release/python-3100/) (selecione a versão 3.10.x).
|
||||||
|
* **Docker Engine & Docker Compose:** Para executar serviços empacotados como SearxNG.
|
||||||
|
* Instalar Docker Desktop (inclui Docker Compose V2): [Windows](https://docs.docker.com/desktop/install/windows-install/) | [Mac](https://docs.docker.com/desktop/install/mac-install/) | [Linux](https://docs.docker.com/desktop/install/linux-install/)
|
||||||
|
* Ou instalar Docker Engine e Docker Compose separadamente no Linux: [Docker Engine](https://docs.docker.com/engine/install/) | [Docker Compose](https://docs.docker.com/compose/install/) (certifique-se de instalar Compose V2, por exemplo `sudo apt-get install docker-compose-plugin`).
|
||||||
|
|
||||||
|
### 1. **Clonar o repositório e configurar**
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git clone https://github.com/Fosowl/agenticSeek.git
|
||||||
|
cd agenticSeek
|
||||||
|
mv .env.example .env
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Modificar o conteúdo do arquivo .env
|
||||||
|
|
||||||
|
```sh
|
||||||
|
SEARXNG_BASE_URL="http://searxng:8080" # Se você executar no modo CLI no host, use http://127.0.0.1:8080
|
||||||
|
REDIS_BASE_URL="redis://redis:6379/0"
|
||||||
|
WORK_DIR="/Users/mlg/Documents/workspace_for_ai"
|
||||||
|
OLLAMA_PORT="11434"
|
||||||
|
LM_STUDIO_PORT="1234"
|
||||||
|
CUSTOM_ADDITIONAL_LLM_PORT="11435"
|
||||||
|
OPENAI_API_KEY='optional'
|
||||||
|
DEEPSEEK_API_KEY='optional'
|
||||||
|
OPENROUTER_API_KEY='optional'
|
||||||
|
TOGETHER_API_KEY='optional'
|
||||||
|
GOOGLE_API_KEY='optional'
|
||||||
|
ANTHROPIC_API_KEY='optional'
|
||||||
|
```
|
||||||
|
|
||||||
|
Atualize o arquivo `.env` conforme necessário:
|
||||||
|
|
||||||
|
- **SEARXNG_BASE_URL**: Mantenha inalterado, a menos que você execute no modo CLI no host.
|
||||||
|
- **REDIS_BASE_URL**: Mantenha inalterado
|
||||||
|
- **WORK_DIR**: Caminho para o diretório de trabalho local. O AgenticSeek poderá ler e interagir com esses arquivos.
|
||||||
|
- **OLLAMA_PORT**: Número da porta para o serviço Ollama.
|
||||||
|
- **LM_STUDIO_PORT**: Número da porta para o serviço LM Studio.
|
||||||
|
- **CUSTOM_ADDITIONAL_LLM_PORT**: Porta para qualquer serviço LLM personalizado adicional.
|
||||||
|
|
||||||
|
**As chaves de API são completamente opcionais para aqueles que optam por executar LLM localmente, que é o objetivo principal deste projeto. Deixe-as vazias se você tiver hardware suficiente.**
|
||||||
|
|
||||||
|
### 3. **Iniciar o Docker**
|
||||||
|
|
||||||
|
Certifique-se de que o Docker está instalado e funcionando no seu sistema. Você pode iniciar o Docker com os seguintes comandos:
|
||||||
|
|
||||||
|
- **Linux/macOS:**
|
||||||
|
Abra um terminal e execute:
|
||||||
|
```sh
|
||||||
|
sudo systemctl start docker
|
||||||
|
```
|
||||||
|
Ou inicie o Docker Desktop a partir do menu de aplicativos, se instalado.
|
||||||
|
|
||||||
|
- **Windows:**
|
||||||
|
Inicie o Docker Desktop a partir do menu Iniciar.
|
||||||
|
|
||||||
|
Você pode verificar se o Docker está funcionando executando:
|
||||||
|
```sh
|
||||||
|
docker info
|
||||||
|
```
|
||||||
|
Se você vir informações sobre sua instalação do Docker, ele está funcionando corretamente.
|
||||||
|
|
||||||
|
Consulte a [Lista de provedores locais](#lista-de-provedores-locais) abaixo para um resumo.
|
||||||
|
|
||||||
|
Próxima etapa: [Executar o AgenticSeek localmente](#iniciar-os-serviços-e-executar)
|
||||||
|
|
||||||
|
*Se você encontrar problemas, consulte a seção [Solução de problemas](#solução-de-problemas).*
|
||||||
|
*Se seu hardware não puder executar LLM localmente, consulte [Configuração para executar com uma API](#configuração-para-executar-com-uma-api).*
|
||||||
|
*Para explicações detalhadas do `config.ini`, consulte a [seção Configuração](#configuração).*
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Configuração para executar LLM localmente na sua máquina
|
||||||
|
|
||||||
|
**Requisitos de hardware:**
|
||||||
|
|
||||||
|
Para executar LLM localmente, você precisará de hardware suficiente. No mínimo, uma GPU capaz de executar Magistral, Qwen ou Deepseek 14B é necessária. Consulte o FAQ para recomendações detalhadas de modelo/desempenho.
|
||||||
|
|
||||||
|
**Configure seu provedor local**
|
||||||
|
|
||||||
|
Inicie seu provedor local, por exemplo com ollama:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
ollama serve
|
||||||
|
```
|
||||||
|
|
||||||
|
Consulte a lista de provedores locais suportados abaixo.
|
||||||
|
|
||||||
|
**Atualizar config.ini**
|
||||||
|
|
||||||
|
Altere o arquivo config.ini para definir provider_name como um provedor suportado e provider_model como um LLM suportado pelo seu provedor. Recomendamos modelos de raciocínio como *Magistral* ou *Deepseek*.
|
||||||
|
|
||||||
|
Consulte o **FAQ** no final do README para o hardware necessário.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
[MAIN]
|
||||||
|
is_local = True # Se você está executando localmente ou com um provedor remoto.
|
||||||
|
provider_name = ollama # ou lm-studio, openai, etc.
|
||||||
|
provider_model = deepseek-r1:14b # escolha um modelo compatível com seu hardware
|
||||||
|
provider_server_address = 127.0.0.1:11434
|
||||||
|
agent_name = Jarvis # o nome da sua IA
|
||||||
|
recover_last_session = True # recuperar a sessão anterior
|
||||||
|
save_session = True # memorizar a sessão atual
|
||||||
|
speak = False # texto para fala
|
||||||
|
listen = False # fala para texto, apenas para CLI, experimental
|
||||||
|
jarvis_personality = False # usar uma personalidade mais "Jarvis" (experimental)
|
||||||
|
languages = en zh # Lista de idiomas, TTS usará o primeiro da lista por padrão
|
||||||
|
[BROWSER]
|
||||||
|
headless_browser = True # mantenha inalterado, a menos que use CLI no host.
|
||||||
|
stealth_mode = True # Use selenium indetectável para reduzir a detecção do navegador
|
||||||
|
```
|
||||||
|
|
||||||
|
**Aviso**:
|
||||||
|
|
||||||
|
- O formato do arquivo `config.ini` não suporta comentários.
|
||||||
|
Não copie e cole diretamente a configuração de exemplo, pois os comentários causarão erros. Em vez disso, modifique manualmente o arquivo `config.ini` com sua configuração desejada, sem comentários.
|
||||||
|
|
||||||
|
- *NÃO* defina provider_name como `openai` se você estiver usando LM-studio para executar LLM. Use-o como `lm-studio`.
|
||||||
|
|
||||||
|
- Alguns provedores (ex: lm-studio) exigem `http://` antes do IP. Exemplo: `http://127.0.0.1:1234`
|
||||||
|
|
||||||
|
**Lista de provedores locais**
|
||||||
|
|
||||||
|
| Provedor | Local ? | Descrição |
|
||||||
|
|-----------|--------|-----------------------------------------------------------|
|
||||||
|
| ollama | Sim | Executa LLM localmente facilmente usando ollama |
|
||||||
|
| lm-studio | Sim | Executa LLM localmente com LM studio (defina `provider_name` = `lm-studio`)|
|
||||||
|
| openai | Sim | Use uma API compatível com openai (ex: servidor llama.cpp) |
|
||||||
|
|
||||||
|
Próxima etapa: [Iniciar os serviços e executar o AgenticSeek](#iniciar-os-serviços-e-executar)
|
||||||
|
|
||||||
|
*Se você encontrar problemas, consulte a seção [Solução de problemas](#solução-de-problemas).*
|
||||||
|
*Se seu hardware não puder executar LLM localmente, consulte [Configuração para executar com uma API](#configuração-para-executar-com-uma-api).*
|
||||||
|
*Para explicações detalhadas do `config.ini`, consulte a [seção Configuração](#configuração).*
|
||||||
|
|
||||||
|
## Configuração para executar com uma API
|
||||||
|
|
||||||
|
Esta configuração usa provedores de LLM externos baseados em nuvem. Você precisará obter chaves de API do serviço escolhido.
|
||||||
|
|
||||||
|
**1. Escolha um provedor de API e obtenha uma chave de API:**
|
||||||
|
|
||||||
|
Consulte a [Lista de provedores de API](#lista-de-provedores-de-api) abaixo. Visite seus sites para se inscrever e obter chaves de API.
|
||||||
|
|
||||||
|
**2. Defina sua chave de API como variável de ambiente:**
|
||||||
|
|
||||||
|
* **Linux/macOS:**
|
||||||
|
Abra um terminal e use o comando `export`. É melhor adicioná-lo ao arquivo de configuração do seu shell (ex: `~/.bashrc`, `~/.zshrc`) para que seja persistente.
|
||||||
|
```sh
|
||||||
|
export PROVIDER_API_KEY="your_api_key_here"
|
||||||
|
# Substitua PROVIDER_API_KEY pelo nome de variável específico, ex: OPENAI_API_KEY, GOOGLE_API_KEY
|
||||||
|
```
|
||||||
|
Exemplo TogetherAI:
|
||||||
|
```sh
|
||||||
|
export TOGETHER_API_KEY="xxxxxxxxxxxxxxxxxxxxxx"
|
||||||
|
```
|
||||||
|
* **Windows:**
|
||||||
|
* **Prompt de comando (temporário para a sessão atual):**
|
||||||
|
```cmd
|
||||||
|
set PROVIDER_API_KEY=your_api_key_here
|
||||||
|
```
|
||||||
|
* **PowerShell (temporário para a sessão atual):**
|
||||||
|
```powershell
|
||||||
|
$env:PROVIDER_API_KEY="your_api_key_here"
|
||||||
|
```
|
||||||
|
* **Permanente:** Pesquise "variáveis de ambiente" na barra de pesquisa do Windows, clique em "Editar variáveis de ambiente do sistema" e depois no botão "Variáveis de ambiente...". Adicione uma nova variável de usuário com o nome apropriado (ex: `OPENAI_API_KEY`) e sua chave como valor.
|
||||||
|
|
||||||
|
*(Para mais detalhes, consulte o FAQ: [Como configurar uma chave de API?](#como-configurar-uma-chave-de-api)).*
|
||||||
|
|
||||||
|
|
||||||
|
**3. Atualize `config.ini`:**
|
||||||
|
```ini
|
||||||
|
[MAIN]
|
||||||
|
is_local = False
|
||||||
|
provider_name = openai # ou google, deepseek, togetherAI, huggingface
|
||||||
|
provider_model = gpt-3.5-turbo # ou gemini-1.5-flash, deepseek-chat, mistralai/Mixtral-8x7B-Instruct-v0.1, etc.
|
||||||
|
provider_server_address = # Quando is_local = False, geralmente ignorado ou pode ser deixado vazio para a maioria das APIs
|
||||||
|
# ... outras configurações ...
|
||||||
|
```
|
||||||
|
*Aviso:* Certifique-se de que não há espaços no final dos valores no config.
|
||||||
|
|
||||||
|
**Lista de provedores de API**
|
||||||
|
|
||||||
|
| Provedor | `provider_name` | Local ? | Descrição | Link da chave de API (exemplo) |
|
||||||
|
|--------------|-----------------|--------|---------------------------------------------------|---------------------------------------------|
|
||||||
|
| OpenAI | `openai` | Não | Use os modelos ChatGPT via API OpenAI. | [platform.openai.com/signup](https://platform.openai.com/signup) |
|
||||||
|
| Google Gemini| `google` | Não | Use os modelos Google Gemini via Google AI Studio. | [aistudio.google.com/keys](https://aistudio.google.com/keys) |
|
||||||
|
| Deepseek | `deepseek` | Não | Use os modelos Deepseek via sua API. | [platform.deepseek.com](https://platform.deepseek.com) |
|
||||||
|
| Hugging Face | `huggingface` | Não | Use modelos do Hugging Face Inference API. | [huggingface.co/settings/tokens](https://huggingface.co/settings/tokens) |
|
||||||
|
| TogetherAI | `togetherAI` | Não | Use vários modelos open source via API TogetherAI.| [api.together.ai/settings/api-keys](https://api.together.ai/settings/api-keys) |
|
||||||
|
|
||||||
|
*Nota:*
|
||||||
|
* Não recomendamos usar `gpt-4o` ou outros modelos OpenAI para navegação web complexa e planejamento de tarefas, pois a otimização atual de prompts visa modelos como Deepseek.
|
||||||
|
* Tarefas de codificação/bash podem falhar com Gemini, pois tende a ignorar nosso formato de prompt otimizado para Deepseek r1.
|
||||||
|
* Quando `is_local = False`, `provider_server_address` no `config.ini` geralmente não é usado, pois os endpoints de API são geralmente gerenciados pelas bibliotecas do provedor correspondente.
|
||||||
|
|
||||||
|
Próxima etapa: [Iniciar os serviços e executar o AgenticSeek](#iniciar-os-serviços-e-executar)
|
||||||
|
|
||||||
|
*Se você encontrar problemas, consulte a seção **Problemas conhecidos***
|
||||||
|
|
||||||
|
*Para explicações detalhadas do arquivo de configuração, consulte a **seção Configuração**.*
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Iniciar os serviços e executar
|
||||||
|
|
||||||
|
Por padrão, o AgenticSeek é executado inteiramente no Docker.
|
||||||
|
|
||||||
|
**Opção 1:** Executar no Docker com interface web:
|
||||||
|
|
||||||
|
Inicie os serviços necessários. Isso iniciará todos os serviços do docker-compose.yml, incluindo:
|
||||||
|
- searxng
|
||||||
|
- redis (necessário para searxng)
|
||||||
|
- frontend
|
||||||
|
- backend (se você usar `full` para a interface web)
|
||||||
|
|
||||||
|
```sh
|
||||||
|
./start_services.sh full # MacOS
|
||||||
|
start start_services.cmd full # Windows
|
||||||
|
```
|
||||||
|
|
||||||
|
**Aviso:** Esta etapa baixará e carregará todas as imagens do Docker, o que pode levar até 30 minutos. Depois de iniciar os serviços, aguarde até que o serviço backend esteja totalmente operacional (você deve ver **backend: "GET /health HTTP/1.1" 200 OK** nos logs) antes de enviar mensagens. Na primeira inicialização, o serviço backend pode levar 5 minutos para iniciar.
|
||||||
|
|
||||||
|
Vá para `http://localhost:3000/` e você deve ver a interface web.
|
||||||
|
|
||||||
|
*Solução de problemas de inicialização de serviços:* Se esses scripts falharem, certifique-se de que o Docker Engine está funcionando e que o Docker Compose (V2, `docker compose`) está instalado corretamente. Verifique as mensagens de erro na saída do terminal. Consulte [FAQ: Ajuda! Estou recebendo erros ao executar o AgenticSeek ou seus scripts.](#faq-solução-de-problemas)
|
||||||
|
|
||||||
|
**Opção 2:** Modo CLI:
|
||||||
|
|
||||||
|
Para executar com a interface CLI, você precisa instalar os pacotes no host:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
./install.sh
|
||||||
|
./install.bat # windows
|
||||||
|
```
|
||||||
|
|
||||||
|
Em seguida, você precisa alterar SEARXNG_BASE_URL no `config.ini` para:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
SEARXNG_BASE_URL="http://localhost:8080"
|
||||||
|
```
|
||||||
|
|
||||||
|
Inicie os serviços necessários. Isso iniciará alguns serviços do docker-compose.yml, incluindo:
|
||||||
|
- searxng
|
||||||
|
- redis (necessário para searxng)
|
||||||
|
- frontend
|
||||||
|
|
||||||
|
```sh
|
||||||
|
./start_services.sh # MacOS
|
||||||
|
start start_services.cmd # Windows
|
||||||
|
```
|
||||||
|
|
||||||
|
Execute: uv run: `uv run python -m ensurepip` para garantir que o uv tenha o pip ativado.
|
||||||
|
|
||||||
|
Use CLI: `uv run cli.py`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Uso
|
||||||
|
|
||||||
|
Certifique-se de que os serviços estão funcionando com `./start_services.sh full` e vá para `localhost:3000` para a interface web.
|
||||||
|
|
||||||
|
Você também pode usar fala para texto definindo `listen = True`. Apenas para o modo CLI.
|
||||||
|
|
||||||
|
Para sair, basta dizer/digitar `goodbye`.
|
||||||
|
|
||||||
|
Alguns exemplos de uso:
|
||||||
|
|
||||||
|
> *Faça um jogo da cobra em python!*
|
||||||
|
|
||||||
|
> *Pesquise na web os melhores cafés em Rennes, França, e salve uma lista de três com seus endereços em rennes_cafes.txt.*
|
||||||
|
|
||||||
|
> *Escreva um programa Go para calcular o fatorial de um número, salve-o como factorial.go em seu workspace*
|
||||||
|
|
||||||
|
> *Pesquise na pasta summer_pictures todos os arquivos JPG, renomeie-os com a data de hoje e salve a lista de arquivos renomeados em photos_list.txt*
|
||||||
|
|
||||||
|
> *Pesquise online os filmes de ficção científica populares de 2024 e escolha três para assistir esta noite. Salve a lista em movie_night.txt.*
|
||||||
|
|
||||||
|
> *Pesquise na web os últimos artigos de notícias sobre IA de 2025, selecione três e escreva um script Python para extrair os títulos e resumos. Salve o script como news_scraper.py e os resumos em ai_news.txt em /home/projects*
|
||||||
|
|
||||||
|
> *Sexta-feira, pesquise na web uma API gratuita de preços de ações, inscreva-se com supersuper7434567@gmail.com e escreva um script Python para obter os preços diários da Tesla usando a API, salvando os resultados em stock_prices.csv*
|
||||||
|
|
||||||
|
*Observe que o preenchimento de formulários ainda é experimental e pode falhar.*
|
||||||
|
|
||||||
|
Depois de inserir sua consulta, o AgenticSeek atribuirá o melhor agente para a tarefa.
|
||||||
|
|
||||||
|
Como este é um protótipo inicial, o sistema de roteamento de agentes pode nem sempre atribuir o agente correto à sua consulta.
|
||||||
|
|
||||||
|
Portanto, seja muito explícito sobre o que você quer e como a IA pode proceder, por exemplo, se você quiser que ela faça uma pesquisa na web, não diga:
|
||||||
|
|
||||||
|
`Você conhece bons países para viajar sozinho?`
|
||||||
|
|
||||||
|
Em vez disso, diga:
|
||||||
|
|
||||||
|
`Execute uma pesquisa na web e descubra quais são os melhores países para viajar sozinho`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## **Configuração para executar LLM em seu próprio servidor**
|
||||||
|
|
||||||
|
Se você tem um computador poderoso ou um servidor que pode acessar, mas quer usá-lo do seu laptop, você pode optar por executar o LLM em um servidor remoto usando nosso servidor llm personalizado.
|
||||||
|
|
||||||
|
No seu "servidor" que executará o modelo de IA, obtenha o endereço IP
|
||||||
|
|
||||||
|
```sh
|
||||||
|
ip a | grep "inet " | grep -v 127.0.0.1 | awk '{print $2}' | cut -d/ -f1 # IP local
|
||||||
|
curl https://ipinfo.io/ip # IP público
|
||||||
|
```
|
||||||
|
|
||||||
|
Nota: Para Windows ou macOS, use ipconfig ou ifconfig para encontrar o endereço IP.
|
||||||
|
|
||||||
|
Clone o repositório e entre na pasta `server/`.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git clone --depth 1 https://github.com/Fosowl/agenticSeek.git
|
||||||
|
cd agenticSeek/llm_server/
|
||||||
|
```
|
||||||
|
|
||||||
|
Instale os requisitos específicos do servidor:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
pip3 install -r requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
Execute o script do servidor.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
python3 app.py --provider ollama --port 3333
|
||||||
|
```
|
||||||
|
|
||||||
|
Você pode escolher usar `ollama` e `llamacpp` como serviço LLM.
|
||||||
|
|
||||||
|
Agora no seu computador pessoal:
|
||||||
|
|
||||||
|
Altere o arquivo `config.ini` para definir `provider_name` como `server` e `provider_model` como `deepseek-r1:xxb`.
|
||||||
|
Defina `provider_server_address` como o endereço IP da máquina que executará o modelo.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
[MAIN]
|
||||||
|
is_local = False
|
||||||
|
provider_name = server
|
||||||
|
provider_model = deepseek-r1:70b
|
||||||
|
provider_server_address = http://x.x.x.x:3333
|
||||||
|
```
|
||||||
|
|
||||||
|
Próxima etapa: [Iniciar os serviços e executar o AgenticSeek](#iniciar-os-serviços-e-executar)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Fala para Texto
|
||||||
|
|
||||||
|
Aviso: Fala para texto funciona apenas no modo CLI no momento.
|
||||||
|
|
||||||
|
Observe que a fala para texto funciona apenas em inglês no momento.
|
||||||
|
|
||||||
|
A funcionalidade de fala para texto está desativada por padrão. Para ativá-la, defina listen como True no arquivo config.ini:
|
||||||
|
|
||||||
|
```
|
||||||
|
listen = True
|
||||||
|
```
|
||||||
|
|
||||||
|
Quando ativada, a funcionalidade de fala para texto ouve uma palavra-chave de gatilho, que é o nome do agente, antes de processar sua entrada. Você pode personalizar o nome do agente atualizando o valor `agent_name` em *config.ini*:
|
||||||
|
|
||||||
|
```
|
||||||
|
agent_name = Friday
|
||||||
|
```
|
||||||
|
|
||||||
|
Para melhor reconhecimento, recomendamos usar um nome comum em inglês como "John" ou "Emma" como nome do agente.
|
||||||
|
|
||||||
|
Assim que você vir a transcrição começar a aparecer, diga o nome do agente em voz alta para acordá-lo (ex: "Friday").
|
||||||
|
|
||||||
|
Diga sua consulta claramente.
|
||||||
|
|
||||||
|
Termine sua solicitação com uma frase de confirmação para indicar ao sistema para continuar. Exemplos de frases de confirmação incluem:
|
||||||
|
```
|
||||||
|
"do it", "go ahead", "execute", "run", "start", "thanks", "would ya", "please", "okay?", "proceed", "continue", "go on", "do that", "go it", "do you understand?"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuração
|
||||||
|
|
||||||
|
Exemplo de configuração:
|
||||||
|
```
|
||||||
|
[MAIN]
|
||||||
|
is_local = True
|
||||||
|
provider_name = ollama
|
||||||
|
provider_model = deepseek-r1:32b
|
||||||
|
provider_server_address = http://127.0.0.1:11434 # Exemplo Ollama; LM-Studio usa http://127.0.0.1:1234
|
||||||
|
agent_name = Friday
|
||||||
|
recover_last_session = False
|
||||||
|
save_session = False
|
||||||
|
speak = False
|
||||||
|
listen = False
|
||||||
|
|
||||||
|
jarvis_personality = False
|
||||||
|
languages = en zh # Lista de idiomas para TTS e roteamento potencial.
|
||||||
|
[BROWSER]
|
||||||
|
headless_browser = False
|
||||||
|
stealth_mode = False
|
||||||
|
```
|
||||||
|
|
||||||
|
**Explicação das configurações de `config.ini`**:
|
||||||
|
|
||||||
|
* **Seção `[MAIN]`:**
|
||||||
|
* `is_local`: `True` se você estiver usando provedores de LLM locais (Ollama, LM-Studio, servidor local compatível com OpenAI) ou a opção de servidor auto-hospedado. `False` se você estiver usando APIs baseadas em nuvem (OpenAI, Google, etc.).
|
||||||
|
* `provider_name`: Especifica o provedor de LLM.
|
||||||
|
* Opções locais: `ollama`, `lm-studio`, `openai` (para servidor local compatível com OpenAI), `server` (para configuração de servidor auto-hospedado).
|
||||||
|
* Opções de API: `openai`, `google`, `deepseek`, `huggingface`, `togetherAI`.
|
||||||
|
* `provider_model`: Nome ou ID específico do modelo do provedor selecionado (ex: `deepseekcoder:6.7b` para Ollama, `gpt-3.5-turbo` para API OpenAI, `mistralai/Mixtral-8x7B-Instruct-v0.1` para TogetherAI).
|
||||||
|
* `provider_server_address`: O endereço do seu provedor de LLM.
|
||||||
|
* Para provedores locais: ex: `http://127.0.0.1:11434` para Ollama, `http://127.0.0.1:1234` para LM-Studio.
|
||||||
|
* Para o tipo de provedor `server`: O endereço do seu servidor LLM auto-hospedado (ex: `http://your_server_ip:3333`).
|
||||||
|
* Para APIs em nuvem (`is_local = False`): Isso geralmente é ignorado ou pode ser deixado em branco, pois os endpoints da API são geralmente gerenciados pelas bibliotecas do provedor correspondente.
|
||||||
|
* `agent_name`: O nome do assistente de IA (ex: Friday). Se ativado, usado como palavra de gatilho para fala para texto.
|
||||||
|
* `recover_last_session`: `True` para tentar recuperar o estado da sessão anterior, `False` para começar do zero.
|
||||||
|
* `save_session`: `True` para salvar o estado da sessão atual para possível recuperação, `False` caso contrário.
|
||||||
|
* `speak`: `True` para ativar a saída de voz de texto para fala, `False` para desativar.
|
||||||
|
* `listen`: `True` para ativar a entrada de voz de fala para texto (apenas modo CLI), `False` para desativar.
|
||||||
|
* `work_dir`: **Crítico:** O diretório onde o AgenticSeek lerá/escreverá arquivos. **Certifique-se de que este caminho é válido e acessível no seu sistema.**
|
||||||
|
* `jarvis_personality`: `True` para usar prompts de sistema mais "Jarvis-like" (experimental), `False` para usar prompts padrão.
|
||||||
|
* `languages`: Lista de idiomas separados por vírgulas (ex: `en, zh, fr`). Usado para seleção de voz TTS (primeira por padrão) e pode ajudar o roteador LLM. Para evitar ineficiências do roteador, evite usar muitos idiomas ou idiomas muito semelhantes.
|
||||||
|
* **Seção `[BROWSER]`:**
|
||||||
|
* `headless_browser`: `True` para executar o navegador automatizado sem janela visível (recomendado para interface web ou uso não interativo). `False` para exibir a janela do navegador (útil para modo CLI ou depuração).
|
||||||
|
* `stealth_mode`: `True` para ativar medidas que tornam mais difícil a detecção da automação do navegador. Pode exigir instalação manual de extensões de navegador como anticaptcha.
|
||||||
|
|
||||||
|
Esta seção resume os tipos de provedores de LLM suportados. Configure-os em `config.ini`.
|
||||||
|
|
||||||
|
**Provedores locais (executando em seu próprio hardware):**
|
||||||
|
|
||||||
|
| Nome do provedor em config.ini | `is_local` | Descrição | Seção de configuração |
|
||||||
|
|-------------------------------|------------|-----------------------------------------------------------------------------|------------------------------------------------------------------|
|
||||||
|
| `ollama` | `True` | Fornece LLM localmente facilmente usando Ollama. | [Configuração para executar LLM localmente na sua máquina](#configuração-para-executar-llm-localmente-na-sua-máquina) |
|
||||||
|
| `lm-studio` | `True` | Fornece LLM localmente com LM-Studio. | [Configuração para executar LLM localmente na sua máquina](#configuração-para-executar-llm-localmente-na-sua-máquina) |
|
||||||
|
| `openai` (para servidor local) | `True` | Conecte-se a um servidor local expondo uma API compatível com OpenAI (ex: llama.cpp). | [Configuração para executar LLM localmente na sua máquina](#configuração-para-executar-llm-localmente-na-sua-máquina) |
|
||||||
|
| `server` | `False` | Conecte-se ao servidor LLM auto-hospedado do AgenticSeek em execução em outra máquina. | [Configuração para executar LLM em seu próprio servidor](#configuração-para-executar-llm-em-seu-próprio-servidor) |
|
||||||
|
|
||||||
|
**Provedores de API (baseados em nuvem):**
|
||||||
|
|
||||||
|
| Nome do provedor em config.ini | `is_local` | Descrição | Seção de configuração |
|
||||||
|
|-------------------------------|------------|--------------------------------------------------|-----------------------------------------------------|
|
||||||
|
| `openai` | `False` | Use a API oficial da OpenAI (ex: GPT-3.5, GPT-4). | [Configuração para executar com uma API](#configuração-para-executar-com-uma-api) |
|
||||||
|
| `google` | `False` | Use os modelos Google Gemini via API. | [Configuração para executar com uma API](#configuração-para-executar-com-uma-api) |
|
||||||
|
| `deepseek` | `False` | Use a API oficial da Deepseek. | [Configuração para executar com uma API](#configuração-para-executar-com-uma-api) |
|
||||||
|
| `huggingface` | `False` | Use Hugging Face Inference API. | [Configuração para executar com uma API](#configuração-para-executar-com-uma-api) |
|
||||||
|
| `togetherAI` | `False` | Use vários modelos abertos via API TogetherAI. | [Configuração para executar com uma API](#configuração-para-executar-com-uma-api) |
|
||||||
|
|
||||||
|
---
|
||||||
|
## Solução de problemas
|
||||||
|
|
||||||
|
Se você encontrar problemas, esta seção fornece orientações.
|
||||||
|
|
||||||
|
# Problemas conhecidos
|
||||||
|
|
||||||
|
## Problemas do ChromeDriver
|
||||||
|
|
||||||
|
**Exemplo de erro:** `SessionNotCreatedException: Message: session not created: This version of ChromeDriver only supports Chrome version XXX`
|
||||||
|
|
||||||
|
### Causa raiz
|
||||||
|
A incompatibilidade de versão do ChromeDriver ocorre quando:
|
||||||
|
1. A versão do ChromeDriver que você instalou não corresponde à versão do navegador Chrome
|
||||||
|
2. Em ambientes Docker, `undetected_chromedriver` pode baixar sua própria versão do ChromeDriver, contornando os binários montados
|
||||||
|
|
||||||
|
### Etapas de solução
|
||||||
|
|
||||||
|
#### 1. Verifique sua versão do Chrome
|
||||||
|
Abra o Google Chrome → `Configurações > Sobre o Chrome` para encontrar sua versão (ex: "Versão 134.0.6998.88")
|
||||||
|
|
||||||
|
#### 2. Baixe o ChromeDriver correspondente
|
||||||
|
|
||||||
|
**Para Chrome 115 e superior:** Use [Chrome for Testing API](https://googlechromelabs.github.io/chrome-for-testing/)
|
||||||
|
- Visite o painel de disponibilidade do Chrome for Testing
|
||||||
|
- Encontre sua versão do Chrome ou a correspondência disponível mais próxima
|
||||||
|
- Baixe o ChromeDriver para seu sistema operacional (use Linux64 para ambientes Docker)
|
||||||
|
|
||||||
|
**Para versões mais antigas do Chrome:** Use [Downloads legados do ChromeDriver](https://chromedriver.chromium.org/downloads)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
#### 3. Instale o ChromeDriver (escolha um método)
|
||||||
|
|
||||||
|
**Método A: Diretório raiz do projeto (recomendado para Docker)**
|
||||||
|
```bash
|
||||||
|
# Coloque o binário chromedriver baixado no diretório raiz do projeto
|
||||||
|
cp path/to/downloaded/chromedriver ./chromedriver
|
||||||
|
chmod +x ./chromedriver # Torne-o executável no Linux/macOS
|
||||||
|
```
|
||||||
|
|
||||||
|
**Método B: PATH do sistema**
|
||||||
|
```bash
|
||||||
|
# Linux/macOS
|
||||||
|
sudo mv chromedriver /usr/local/bin/
|
||||||
|
sudo chmod +x /usr/local/bin/chromedriver
|
||||||
|
|
||||||
|
# Windows: Coloque chromedriver.exe em uma pasta do PATH
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 4. Verifique a instalação
|
||||||
|
```bash
|
||||||
|
# Teste a versão do ChromeDriver
|
||||||
|
./chromedriver --version
|
||||||
|
# Ou se estiver no PATH:
|
||||||
|
chromedriver --version
|
||||||
|
```
|
||||||
|
|
||||||
|
### Instruções específicas do Docker
|
||||||
|
|
||||||
|
⚠️ **Importante para usuários do Docker:**
|
||||||
|
- O método de montagem de volumes do Docker pode não funcionar com o modo furtivo (`undetected_chromedriver`)
|
||||||
|
- **Solução:** Coloque o ChromeDriver no diretório raiz do projeto como `./chromedriver`
|
||||||
|
- O aplicativo o detectará automaticamente e usará este binário
|
||||||
|
- Você deve ver nos logs: `"Using ChromeDriver from project root: ./chromedriver"`
|
||||||
|
|
||||||
|
### Dicas de solução de problemas
|
||||||
|
|
||||||
|
1. **Ainda há incompatibilidade de versão?**
|
||||||
|
- Verifique se o ChromeDriver é executável: `ls -la ./chromedriver`
|
||||||
|
- Verifique a versão do ChromeDriver: `./chromedriver --version`
|
||||||
|
- Certifique-se de que corresponde à versão do seu navegador Chrome
|
||||||
|
|
||||||
|
2. **Problemas com o contêiner Docker?**
|
||||||
|
- Verifique os logs do backend: `docker logs backend`
|
||||||
|
- Procure a mensagem: `"Using ChromeDriver from project root"`
|
||||||
|
- Se não for encontrado, verifique se o arquivo existe e é executável
|
||||||
|
|
||||||
|
3. **Versões do Chrome for Testing**
|
||||||
|
- Use uma correspondência exata quando possível
|
||||||
|
- Para a versão 134.0.6998.88, use o ChromeDriver 134.0.6998.165 (a versão disponível mais próxima)
|
||||||
|
- O número da versão principal deve corresponder (134 = 134)
|
||||||
|
|
||||||
|
### Matriz de compatibilidade de versões
|
||||||
|
|
||||||
|
| Versão do Chrome | Versão do ChromeDriver | Status |
|
||||||
|
|----------------|---------------------|---------|
|
||||||
|
| 134.0.6998.x | 134.0.6998.165 | ✅ Disponível |
|
||||||
|
| 133.0.6943.x | 133.0.6943.141 | ✅ Disponível |
|
||||||
|
| 132.0.6834.x | 132.0.6834.159 | ✅ Disponível |
|
||||||
|
|
||||||
|
*Para a compatibilidade mais recente, consulte o [Painel do Chrome for Testing](https://googlechromelabs.github.io/chrome-for-testing/)*
|
||||||
|
|
||||||
|
`Exception: Failed to initialize browser: Message: session not created: This version of ChromeDriver only supports Chrome version 113
|
||||||
|
Current browser version is 134.0.6998.89 with binary path`
|
||||||
|
|
||||||
|
Isso acontece se seu navegador e a versão do chromedriver não corresponderem.
|
||||||
|
|
||||||
|
Você precisa navegar para baixar a versão mais recente:
|
||||||
|
|
||||||
|
https://developer.chrome.com/docs/chromedriver/downloads
|
||||||
|
|
||||||
|
Se você estiver usando o Chrome versão 115 ou superior, vá para:
|
||||||
|
|
||||||
|
https://googlechromelabs.github.io/chrome-for-testing/
|
||||||
|
|
||||||
|
e baixe a versão do chromedriver correspondente ao seu sistema operacional.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Se esta seção estiver incompleta, abra um issue.
|
||||||
|
|
||||||
|
## Problemas de adaptadores de conexão
|
||||||
|
|
||||||
|
```
|
||||||
|
Exception: Provider lm-studio failed: HTTP request failed: No connection adapters were found for '127.0.0.1:1234/v1/chat/completions'` (nota: a porta pode variar)
|
||||||
|
```
|
||||||
|
|
||||||
|
* **Causa:** Falta o prefixo `http://` em `provider_server_address` para `lm-studio` (ou outro servidor local compatível com OpenAI semelhante) no `config.ini`, ou ele aponta para a porta errada.
|
||||||
|
* **Solução:**
|
||||||
|
* Certifique-se de que o endereço inclui `http://`. O LM-Studio geralmente usa `http://127.0.0.1:1234` por padrão.
|
||||||
|
* `config.ini` correto: `provider_server_address = http://127.0.0.1:1234` (ou sua porta real do servidor LM-Studio).
|
||||||
|
|
||||||
|
## URL base do SearxNG não fornecida
|
||||||
|
|
||||||
|
```
|
||||||
|
raise ValueError("SearxNG base URL must be provided either as an argument or via the SEARXNG_BASE_URL environment variable.")
|
||||||
|
ValueError: SearxNG base URL must be provided either as an argument or via the SEARXNG_BASE_URL environment variable.`
|
||||||
|
```
|
||||||
|
|
||||||
|
Isso pode acontecer se você executar o modo CLI com uma URL base do searxng incorreta.
|
||||||
|
|
||||||
|
SEARXNG_BASE_URL deve diferir dependendo se você está executando no Docker ou no host:
|
||||||
|
|
||||||
|
**Execução no host:** `SEARXNG_BASE_URL="http://localhost:8080"`
|
||||||
|
|
||||||
|
**Execução completamente no Docker (interface web):** `SEARXNG_BASE_URL="http://searxng:8080"`
|
||||||
|
|
||||||
|
## FAQ
|
||||||
|
|
||||||
|
**P: De qual hardware eu preciso?**
|
||||||
|
|
||||||
|
| Tamanho do modelo | GPU | Comentários |
|
||||||
|
|-----------|--------|-----------------------------------------------------------|
|
||||||
|
| 7B | 8GB VRAM | ⚠️ Não recomendado. Desempenho ruim, alucinações frequentes, agentes de planejamento podem falhar. |
|
||||||
|
| 14B | 12 GB VRAM (ex: RTX 3060) | ✅ Utilizável para tarefas simples. Pode ter dificuldades com navegação web e planejamento de tarefas. |
|
||||||
|
| 32B | 24+ GB VRAM (ex: RTX 4090) | 🚀 Consegue a maioria das tarefas, ainda pode ter dificuldades com planejamento de tarefas |
|
||||||
|
| 70B+ | 48+ GB VRAM | 💪 Excelente. Recomendado para casos de uso avançados. |
|
||||||
|
|
||||||
|
**P: O que fazer se eu encontrar erros?**
|
||||||
|
|
||||||
|
Certifique-se de que o local está funcionando (`ollama serve`), que seu `config.ini` corresponde ao seu provedor e que as dependências estão instaladas. Se nada funcionar, sinta-se à vontade para abrir um issue.
|
||||||
|
|
||||||
|
**P: Ele pode realmente funcionar 100% localmente?**
|
||||||
|
|
||||||
|
Sim, com os provedores Ollama, lm-studio ou server, todos os modelos de fala para texto, LLM e texto para fala funcionam localmente. As opções não locais (OpenAI ou outras APIs) são opcionais.
|
||||||
|
|
||||||
|
**P: Por que eu deveria usar o AgenticSeek quando tenho o Manus?**
|
||||||
|
|
||||||
|
Ao contrário do Manus, o AgenticSeek prioriza a independência de sistemas externos, dando a você mais controle, privacidade e evitando custos de API.
|
||||||
|
|
||||||
|
**P: Quem está por trás deste projeto?**
|
||||||
|
|
||||||
|
Este projeto foi criado por mim, com dois amigos como mantenedores e contribuidores da comunidade de código aberto no GitHub. Somos apenas indivíduos apaixonados, não uma startup, nem afiliados a qualquer organização.
|
||||||
|
|
||||||
|
Qualquer conta AgenticSeek no X diferente da minha conta pessoal (https://x.com/Martin993886460) é um impostor.
|
||||||
|
|
||||||
|
## Contribuir
|
||||||
|
|
||||||
|
Estamos procurando desenvolvedores para melhorar o AgenticSeek! Verifique os problemas abertos ou discussões.
|
||||||
|
|
||||||
|
[Guia de contribuição](./docs/CONTRIBUTING.md)
|
||||||
|
|
||||||
|
## Patrocinadores:
|
||||||
|
|
||||||
|
Você quer melhorar as capacidades do AgenticSeek com recursos como pesquisa de voos, planejamento de viagens ou obtenção das melhores ofertas de compra? Considere usar o SerpApi para criar ferramentas personalizadas que desbloqueiem mais recursos do tipo Jarvis. Com o SerpApi, você pode acelerar seu agente para tarefas profissionais enquanto mantém o controle total.
|
||||||
|
|
||||||
|
<a href="https://serpapi.com/"><img src="./media/banners/sponsor_banner_serpapi.png" height="350" alt="SerpApi Banner" ></a>
|
||||||
|
|
||||||
|
Confira [Contributing.md](./docs/CONTRIBUTING.md) para aprender como integrar ferramentas personalizadas!
|
||||||
|
|
||||||
|
### **Patrocinadores**:
|
||||||
|
|
||||||
|
- [tatra-labs](https://github.com/tatra-labs)
|
||||||
|
|
||||||
|
## Mantenedores:
|
||||||
|
|
||||||
|
> [Fosowl](https://github.com/Fosowl) | Horário de Paris
|
||||||
|
|
||||||
|
> [antoineVIVIES](https://github.com/antoineVIVIES) | Horário de Taipei
|
||||||
|
|
||||||
|
## Agradecimentos especiais:
|
||||||
|
|
||||||
|
> [tcsenpai](https://github.com/tcsenpai) e [plitc](https://github.com/plitc) por ajudar na dockerização do backend
|
||||||
|
|
||||||
|
[](https://www.star-history.com/#Fosowl/agenticSeek&Date)
|
||||||
@@ -0,0 +1,692 @@
|
|||||||
|
# AgenticSeek: Gizlilik Odaklı, Yerel Manus Alternatifi
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img align="center" src="./media/agentic_seek_logo.png" width="300" height="300" alt="Agentic Seek Logo">
|
||||||
|
<p>
|
||||||
|
|
||||||
|
English | [中文](./README_CHS.md) | [繁體中文](./README_CHT.md) | [Français](./README_FR.md) | [日本語](./README_JP.md) | [Português (Brasil)](./README_PTBR.md) | [Español](./README_ES.md) | [Türkçe](./README_TR.md)
|
||||||
|
|
||||||
|
*Sesli komut destekli bu yapay zeka asistanı, **Manus AI'ya %100 yerel bir alternatiftir**. Web'de otonom olarak gezinir, kod yazar ve görevleri planlar; tüm verilerinizi cihazınızda tutar. Yerel akıl yürütme modelleri için tasarlanmış olup tamamen kendi donanımınızda çalışır, eksiksiz gizlilik ve sıfır bulut bağımlılığı sağlar.*
|
||||||
|
|
||||||
|
[](https://fosowl.github.io/agenticSeek.html)  [](https://discord.gg/8hGDaME3TC) [](https://x.com/Martin993886460) [](https://github.com/Fosowl/agenticSeek/stargazers)
|
||||||
|
|
||||||
|
### Neden AgenticSeek?
|
||||||
|
|
||||||
|
* 🔒 Tamamen Yerel ve Gizli - Her şey kendi makinenizde çalışır — bulut yok, veri paylaşımı yok. Dosyalarınız, konuşmalarınız ve aramalarınız gizli kalır.
|
||||||
|
|
||||||
|
* 🌐 Akıllı Web Tarama - AgenticSeek internette kendi başına gezinebilir — arama yapar, okur, bilgi çıkarır, web formlarını doldurur — tamamen eller serbest.
|
||||||
|
|
||||||
|
* 💻 Otonom Kodlama Asistanı - Koda mı ihtiyacınız var? Python, C, Go, Java ve daha fazlasında program yazabilir, hata ayıklayabilir ve çalıştırabilir — denetim olmadan.
|
||||||
|
|
||||||
|
* 🧠 Akıllı Ajan Seçimi - Siz sorarsınız, o otomatik olarak iş için en uygun ajanı belirler. Elinizin altında uzmanlardan oluşan bir ekip gibi.
|
||||||
|
|
||||||
|
* 📋 Karmaşık Görevleri Planlar ve Yürütür - Seyahat planlamasından karmaşık projelere kadar — büyük görevleri adımlara bölebilir ve birden fazla yapay zeka ajanı kullanarak işleri tamamlayabilir.
|
||||||
|
|
||||||
|
* 🎙️ Ses Desteği - Temiz, hızlı, fütüristik ses ve konuşmadan metne dönüştürme ile bilim kurgu filmlerindeki kişisel yapay zekanızla konuşuyormuş gibi etkileşim kurabilirsiniz. (Geliştirme aşamasında)
|
||||||
|
|
||||||
|
### **Demo**
|
||||||
|
|
||||||
|
> *AgenticSeek projesini arayabilir misin, hangi becerilerin gerektiğini öğrenip ardından CV_candidates.zip dosyasını açarak projeye en uygun adayları söyleyebilir misin?*
|
||||||
|
|
||||||
|
https://github.com/user-attachments/assets/b8ca60e9-7b3b-4533-840e-08f9ac426316
|
||||||
|
|
||||||
|
Uyarı: Bu demo ve görünen tüm dosyalar (ör: CV_candidates.zip) tamamen kurgusaldır. Bir şirket değiliz, aday değil açık kaynak katkıda bulunanlar arıyoruz.
|
||||||
|
|
||||||
|
> 🛠⚠️️ **Aktif Olarak Geliştirilmektedir**
|
||||||
|
|
||||||
|
> 🙏 Bu proje bir yan proje olarak başladı ve sıfır yol haritası, sıfır finansmanla geliştirilmektedir. GitHub Trending'e girerek beklentilerin çok ötesine geçti. Katkılar, geri bildirimler ve sabır derinden takdir edilmektedir.
|
||||||
|
|
||||||
|
## Ön Gereksinimler
|
||||||
|
|
||||||
|
Başlamadan önce aşağıdaki yazılımların yüklü olduğundan emin olun:
|
||||||
|
|
||||||
|
* **Git:** Depoyu klonlamak için. [Git İndir](https://git-scm.com/downloads)
|
||||||
|
* **Python 3.10.x:** Python 3.10.x sürümünü kullanmanızı şiddetle tavsiye ederiz. Diğer sürümler bağımlılık hatalarına yol açabilir. [Python 3.10 İndir](https://www.python.org/downloads/release/python-3100/) (3.10.x sürümünü seçin).
|
||||||
|
* **Docker Engine ve Docker Compose:** SearxNG gibi paketlenmiş servisleri çalıştırmak için.
|
||||||
|
* Docker Desktop yükleyin (Docker Compose V2 dahildir): [Windows](https://docs.docker.com/desktop/install/windows-install/) | [Mac](https://docs.docker.com/desktop/install/mac-install/) | [Linux](https://docs.docker.com/desktop/install/linux-install/)
|
||||||
|
* Alternatif olarak, Linux üzerinde Docker Engine ve Docker Compose'u ayrı ayrı yükleyin: [Docker Engine](https://docs.docker.com/engine/install/) | [Docker Compose](https://docs.docker.com/compose/install/) (Compose V2 yüklediğinizden emin olun, ör: `sudo apt-get install docker-compose-plugin`).
|
||||||
|
|
||||||
|
### 1. **Depoyu klonlayın ve kurulumu yapın**
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git clone https://github.com/Fosowl/agenticSeek.git
|
||||||
|
cd agenticSeek
|
||||||
|
mv .env.example .env
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. .env dosyasının içeriğini değiştirin
|
||||||
|
|
||||||
|
```sh
|
||||||
|
SEARXNG_BASE_URL="http://searxng:8080" # CLI modunda çalıştırıyorsanız http://127.0.0.1:8080 kullanın
|
||||||
|
REDIS_BASE_URL="redis://redis:6379/0"
|
||||||
|
WORK_DIR="/Users/mlg/Documents/workspace_for_ai"
|
||||||
|
OLLAMA_PORT="11434"
|
||||||
|
LM_STUDIO_PORT="1234"
|
||||||
|
CUSTOM_ADDITIONAL_LLM_PORT="11435"
|
||||||
|
OPENAI_API_KEY='optional'
|
||||||
|
DEEPSEEK_API_KEY='optional'
|
||||||
|
OPENROUTER_API_KEY='optional'
|
||||||
|
TOGETHER_API_KEY='optional'
|
||||||
|
GOOGLE_API_KEY='optional'
|
||||||
|
ANTHROPIC_API_KEY='optional'
|
||||||
|
```
|
||||||
|
|
||||||
|
`.env` dosyasını ihtiyaçlarınıza göre güncelleyin:
|
||||||
|
|
||||||
|
- **SEARXNG_BASE_URL**: CLI modunda çalışmıyorsanız değiştirmeyin.
|
||||||
|
- **REDIS_BASE_URL**: Değiştirmeyin.
|
||||||
|
- **WORK_DIR**: Yerel makinenizdeki çalışma dizininin yolu. AgenticSeek bu dosyaları okuyabilir ve onlarla etkileşim kurabilir.
|
||||||
|
- **OLLAMA_PORT**: Ollama servisi için port numarası.
|
||||||
|
- **LM_STUDIO_PORT**: LM Studio servisi için port numarası.
|
||||||
|
- **CUSTOM_ADDITIONAL_LLM_PORT**: Ek özel LLM servisi için port.
|
||||||
|
|
||||||
|
**API anahtarları, LLM'yi yerel olarak çalıştırmayı tercih eden kullanıcılar için tamamen isteğe bağlıdır. Bu projenin birincil amacı da budur. Yeterli donanımınız varsa boş bırakın.**
|
||||||
|
|
||||||
|
### 3. **Docker'ı Başlatın**
|
||||||
|
|
||||||
|
Docker'ın sisteminizde yüklü ve çalışır durumda olduğundan emin olun. Docker'ı aşağıdaki komutlarla başlatabilirsiniz:
|
||||||
|
|
||||||
|
- **Linux/macOS:**
|
||||||
|
Terminal açın ve çalıştırın:
|
||||||
|
```sh
|
||||||
|
sudo systemctl start docker
|
||||||
|
```
|
||||||
|
Veya yüklüyse Docker Desktop'ı uygulama menüsünden başlatın.
|
||||||
|
|
||||||
|
- **Windows:**
|
||||||
|
Başlat menüsünden Docker Desktop'ı başlatın.
|
||||||
|
|
||||||
|
Docker'ın çalıştığını doğrulamak için:
|
||||||
|
```sh
|
||||||
|
docker info
|
||||||
|
```
|
||||||
|
Docker kurulumunuz hakkında bilgi görüyorsanız, düzgün çalışıyor demektir.
|
||||||
|
|
||||||
|
Özet için aşağıdaki [Yerel Sağlayıcılar](#yerel-sağlayıcılar-listesi) tablosuna bakın.
|
||||||
|
|
||||||
|
Sonraki adım: [AgenticSeek'i yerel olarak çalıştırın](#servisleri-başlatın-ve-çalıştırın)
|
||||||
|
|
||||||
|
*Sorun yaşıyorsanız [Sorun Giderme](#sorun-giderme) bölümüne bakın.*
|
||||||
|
*Donanımınız LLM'leri yerel olarak çalıştıramıyorsa [API ile Çalıştırma Kurulumu](#api-ile-çalıştırma-kurulumu) bölümüne bakın.*
|
||||||
|
*Ayrıntılı `config.ini` açıklamaları için [Yapılandırma](#yapılandırma) bölümüne bakın.*
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## LLM'yi Makinenizde Yerel Olarak Çalıştırma Kurulumu
|
||||||
|
|
||||||
|
**Donanım Gereksinimleri:**
|
||||||
|
|
||||||
|
LLM'leri yerel olarak çalıştırmak için yeterli donanıma ihtiyacınız olacaktır. En azından Magistral, Qwen veya Deepseek 14B çalıştırabilecek bir GPU gereklidir. Ayrıntılı model/performans önerileri için SSS bölümüne bakın.
|
||||||
|
|
||||||
|
**Yerel sağlayıcınızı ayarlayın**
|
||||||
|
|
||||||
|
Yerel sağlayıcınızı başlatın (örneğin ollama ile):
|
||||||
|
|
||||||
|
AgenticSeek'i ana makinede (CLI modu) çalıştırmayacaksanız, sağlayıcı dinleme adresini ayarlayın:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
export OLLAMA_HOST=0.0.0.0:11434
|
||||||
|
```
|
||||||
|
|
||||||
|
Ardından sağlayıcınızı başlatın:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
ollama serve
|
||||||
|
```
|
||||||
|
|
||||||
|
Desteklenen yerel sağlayıcıların listesi için aşağıya bakın.
|
||||||
|
|
||||||
|
**config.ini dosyasını güncelleyin**
|
||||||
|
|
||||||
|
config.ini dosyasında provider_name'i desteklenen bir sağlayıcıya ve provider_model'i sağlayıcınızın desteklediği bir LLM'e ayarlayın. *Magistral* veya *Deepseek* gibi akıl yürütme modellerini öneriyoruz.
|
||||||
|
|
||||||
|
Gerekli donanım için README sonundaki **SSS** bölümüne bakın.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
[MAIN]
|
||||||
|
is_local = True # Yerel mi yoksa uzak sağlayıcı ile mi çalıştırıyorsunuz.
|
||||||
|
provider_name = ollama # veya lm-studio, openai, vb.
|
||||||
|
provider_model = deepseek-r1:14b # donanımınıza uygun bir model seçin
|
||||||
|
provider_server_address = 127.0.0.1:11434
|
||||||
|
agent_name = Jarvis # yapay zekanızın adı
|
||||||
|
recover_last_session = True # önceki oturumu kurtarıp kurtarmayacağı
|
||||||
|
save_session = True # mevcut oturumu hatırlayıp hatırlamayacağı
|
||||||
|
speak = False # metinden sese dönüştürme
|
||||||
|
listen = False # sesten metne dönüştürme, yalnızca CLI için, deneysel
|
||||||
|
jarvis_personality = False # daha "Jarvis" benzeri bir kişilik kullanıp kullanmayacağı (deneysel)
|
||||||
|
languages = en zh # Dil listesi, metinden sese varsayılan olarak listedeki ilk dili kullanır
|
||||||
|
[BROWSER]
|
||||||
|
headless_browser = True # CLI modunda değilseniz değiştirmeyin.
|
||||||
|
stealth_mode = True # Tarayıcı algılamasını azaltmak için gizli selenium kullanır
|
||||||
|
```
|
||||||
|
|
||||||
|
**Uyarı**:
|
||||||
|
|
||||||
|
- `config.ini` dosya biçimi yorum satırlarını desteklemez.
|
||||||
|
Örnek yapılandırmayı doğrudan kopyalayıp yapıştırmayın, çünkü yorum satırları hatalara neden olur. Bunun yerine `config.ini` dosyasını yorum satırları olmadan istediğiniz ayarlarla manuel olarak düzenleyin.
|
||||||
|
|
||||||
|
- LLM çalıştırmak için LM-studio kullanıyorsanız provider_name'i `openai` olarak *AYARLAMAYIN*. `lm-studio` olarak ayarlayın.
|
||||||
|
|
||||||
|
- Bazı sağlayıcılar (ör: lm-studio) IP'nin önünde `http://` gerektirir. Örneğin: `http://127.0.0.1:1234`
|
||||||
|
|
||||||
|
**Yerel Sağlayıcılar Listesi**
|
||||||
|
|
||||||
|
| Sağlayıcı | Yerel mi? | Açıklama |
|
||||||
|
|-----------|--------|-----------------------------------------------------------|
|
||||||
|
| ollama | Evet | Ollama kullanarak LLM'leri kolayca yerel olarak çalıştırın |
|
||||||
|
| lm-studio | Evet | LM Studio ile LLM'leri yerel olarak çalıştırın (`provider_name` değerini `lm-studio` olarak ayarlayın)|
|
||||||
|
| openai | Evet | OpenAI uyumlu API kullanın (ör: llama.cpp sunucusu) |
|
||||||
|
|
||||||
|
Sonraki adım: [Servisleri başlatın ve AgenticSeek'i çalıştırın](#servisleri-başlatın-ve-çalıştırın)
|
||||||
|
|
||||||
|
*Sorun yaşıyorsanız [Sorun Giderme](#sorun-giderme) bölümüne bakın.*
|
||||||
|
*Donanımınız LLM'leri yerel olarak çalıştıramıyorsa [API ile Çalıştırma Kurulumu](#api-ile-çalıştırma-kurulumu) bölümüne bakın.*
|
||||||
|
*Ayrıntılı `config.ini` açıklamaları için [Yapılandırma](#yapılandırma) bölümüne bakın.*
|
||||||
|
|
||||||
|
## API ile Çalıştırma Kurulumu
|
||||||
|
|
||||||
|
Bu kurulum harici, bulut tabanlı LLM sağlayıcılarını kullanır. Seçtiğiniz servisten bir API anahtarına ihtiyacınız olacaktır.
|
||||||
|
|
||||||
|
**1. Bir API Sağlayıcısı Seçin ve API Anahtarı Alın:**
|
||||||
|
|
||||||
|
Aşağıdaki [API Sağlayıcıları Listesi](#api-sağlayıcıları-listesi) bölümüne bakın. Kaydolmak ve API anahtarı almak için web sitelerini ziyaret edin.
|
||||||
|
|
||||||
|
**2. API Anahtarınızı Ortam Değişkeni Olarak Ayarlayın:**
|
||||||
|
|
||||||
|
* **Linux/macOS:**
|
||||||
|
Terminal açın ve `export` komutunu kullanın. Kalıcılık için bunu shell profil dosyanıza (ör: `~/.bashrc`, `~/.zshrc`) eklemeniz önerilir.
|
||||||
|
```sh
|
||||||
|
export PROVIDER_API_KEY="api_anahtarınız"
|
||||||
|
# PROVIDER_API_KEY'i ilgili değişken adıyla değiştirin, ör: OPENAI_API_KEY, GOOGLE_API_KEY
|
||||||
|
```
|
||||||
|
TogetherAI örneği:
|
||||||
|
```sh
|
||||||
|
export TOGETHER_API_KEY="xxxxxxxxxxxxxxxxxxxxxx"
|
||||||
|
```
|
||||||
|
* **Windows:**
|
||||||
|
* **Komut İstemi (Geçerli oturum için geçici):**
|
||||||
|
```cmd
|
||||||
|
set PROVIDER_API_KEY=api_anahtarınız
|
||||||
|
```
|
||||||
|
* **PowerShell (Geçerli oturum için geçici):**
|
||||||
|
```powershell
|
||||||
|
$env:PROVIDER_API_KEY="api_anahtarınız"
|
||||||
|
```
|
||||||
|
* **Kalıcı:** Windows arama çubuğunda "ortam değişkenleri" arayın, "Sistem ortam değişkenlerini düzenle" seçeneğine tıklayın, ardından "Ortam Değişkenleri..." düğmesine tıklayın. Uygun adla (ör: `OPENAI_API_KEY`) yeni bir Kullanıcı değişkeni ekleyin ve değer olarak anahtarınızı girin.
|
||||||
|
|
||||||
|
*(Daha fazla ayrıntı için SSS: [API anahtarlarını nasıl ayarlarım?](#api-anahtarlarını-nasıl-ayarlarım) bölümüne bakın.)*
|
||||||
|
|
||||||
|
|
||||||
|
**3. `config.ini` Dosyasını Güncelleyin:**
|
||||||
|
```ini
|
||||||
|
[MAIN]
|
||||||
|
is_local = False
|
||||||
|
provider_name = openai # Veya google, deepseek, togetherAI, huggingface
|
||||||
|
provider_model = gpt-3.5-turbo # Veya gemini-1.5-flash, deepseek-chat, mistralai/Mixtral-8x7B-Instruct-v0.1 vb.
|
||||||
|
provider_server_address = # is_local = False olduğunda çoğu API için genellikle yok sayılır veya boş bırakılabilir
|
||||||
|
# ... diğer ayarlar ...
|
||||||
|
```
|
||||||
|
*Uyarı:* config.ini değerlerinin sonunda boşluk olmadığından emin olun.
|
||||||
|
|
||||||
|
**API Sağlayıcıları Listesi**
|
||||||
|
|
||||||
|
| Sağlayıcı | `provider_name` | Yerel mi? | Açıklama | API Anahtarı Bağlantısı (Örnekler) |
|
||||||
|
|--------------|-----------------|--------|---------------------------------------------------|---------------------------------------------|
|
||||||
|
| OpenAI | `openai` | Hayır | OpenAI API'si üzerinden ChatGPT modellerini kullanın. | [platform.openai.com/signup](https://platform.openai.com/signup) |
|
||||||
|
| Google Gemini| `google` | Hayır | Google AI Studio üzerinden Google Gemini modellerini kullanın. | [aistudio.google.com/keys](https://aistudio.google.com/keys) |
|
||||||
|
| Deepseek | `deepseek` | Hayır | Deepseek API'si üzerinden Deepseek modellerini kullanın. | [platform.deepseek.com](https://platform.deepseek.com) |
|
||||||
|
| Hugging Face | `huggingface` | Hayır | Hugging Face Inference API'den modelleri kullanın. | [huggingface.co/settings/tokens](https://huggingface.co/settings/tokens) |
|
||||||
|
| TogetherAI | `togetherAI` | Hayır | TogetherAI API'si üzerinden çeşitli açık kaynak modelleri kullanın.| [api.together.ai/settings/api-keys](https://api.together.ai/settings/api-keys) |
|
||||||
|
| OpenRouter | `openrouter` | Hayır | OpenRouter Modellerini kullanın| [https://openrouter.ai/](https://openrouter.ai/) |
|
||||||
|
|
||||||
|
*Not:*
|
||||||
|
* Karmaşık web tarama ve görev planlama için `gpt-4o` veya diğer OpenAI modellerini kullanmanızı önermiyoruz, çünkü mevcut prompt optimizasyonları Deepseek gibi modellere yöneliktir.
|
||||||
|
* Kodlama/bash görevleri Gemini ile sorun yaşayabilir, çünkü Deepseek için optimize edilmiş biçimlendirme talimatlarına kesinlikle uymayabilir.
|
||||||
|
* `is_local = False` olduğunda `config.ini` içindeki `provider_server_address` genellikle kullanılmaz, çünkü API uç noktaları genellikle ilgili sağlayıcı kütüphanesinde sabit kodlanmıştır.
|
||||||
|
|
||||||
|
Sonraki adım: [Servisleri başlatın ve AgenticSeek'i çalıştırın](#servisleri-başlatın-ve-çalıştırın)
|
||||||
|
|
||||||
|
*Sorun yaşıyorsanız **Bilinen Sorunlar** bölümüne bakın*
|
||||||
|
|
||||||
|
*Ayrıntılı yapılandırma dosyası açıklamaları için **Yapılandırma** bölümüne bakın.*
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Servisleri Başlatın ve Çalıştırın
|
||||||
|
|
||||||
|
Varsayılan olarak AgenticSeek tamamen Docker içinde çalıştırılır.
|
||||||
|
|
||||||
|
**Seçenek 1:** Docker'da çalıştırın, web arayüzünü kullanın:
|
||||||
|
|
||||||
|
Gerekli servisleri başlatın. Bu, docker-compose.yml'deki tüm servisleri başlatacaktır:
|
||||||
|
- searxng
|
||||||
|
- redis (searxng için gerekli)
|
||||||
|
- frontend
|
||||||
|
- backend (web arayüzü kullanırken `full` parametresi ile)
|
||||||
|
|
||||||
|
```sh
|
||||||
|
./start_services.sh full # MacOS
|
||||||
|
start start_services.cmd full # Windows
|
||||||
|
```
|
||||||
|
|
||||||
|
**Uyarı:** Bu adım tüm Docker imajlarını indirip yükleyecektir, bu işlem 30 dakikaya kadar sürebilir. Servisleri başlattıktan sonra, herhangi bir mesaj göndermeden önce backend servisinin tamamen çalışır duruma gelmesini bekleyin (loglarda **backend: "GET /health HTTP/1.1" 200 OK** mesajını görmelisiniz). İlk çalıştırmada backend servisinin başlaması 5 dakika sürebilir.
|
||||||
|
|
||||||
|
`http://localhost:3000/` adresine gidin ve web arayüzünü görmelisiniz.
|
||||||
|
|
||||||
|
*Servis başlatma sorun giderme:* Bu betikler başarısız olursa, Docker Engine'in çalıştığından ve Docker Compose'un (V2, `docker compose`) doğru şekilde yüklendiğinden emin olun. Hata mesajları için terminal çıktısını kontrol edin. Bkz. [SSS: AgenticSeek'i veya betiklerini çalıştırırken hata alıyorum.](#sss-sorun-giderme)
|
||||||
|
|
||||||
|
**Seçenek 2:** CLI modu:
|
||||||
|
|
||||||
|
CLI arayüzü ile çalıştırmak için paketleri ana makineye yüklemeniz gerekir:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
./install.sh
|
||||||
|
./install.bat # windows
|
||||||
|
```
|
||||||
|
|
||||||
|
Ardından `config.ini` içindeki SEARXNG_BASE_URL değerini şu şekilde değiştirmelisiniz:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
SEARXNG_BASE_URL="http://localhost:8080"
|
||||||
|
```
|
||||||
|
|
||||||
|
Gerekli servisleri başlatın. Bu, docker-compose.yml'deki bazı servisleri başlatacaktır:
|
||||||
|
- searxng
|
||||||
|
- redis (searxng için gerekli)
|
||||||
|
- frontend
|
||||||
|
|
||||||
|
```sh
|
||||||
|
./start_services.sh # MacOS
|
||||||
|
start start_services.cmd # Windows
|
||||||
|
```
|
||||||
|
|
||||||
|
Çalıştırın: `uv run python -m ensurepip` komutu ile uv'nin pip'i etkin olduğundan emin olun.
|
||||||
|
|
||||||
|
CLI'yi kullanın: `uv run cli.py`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Kullanım
|
||||||
|
|
||||||
|
Servislerin `./start_services.sh full` ile çalıştığından emin olun ve web arayüzü için `localhost:3000` adresine gidin.
|
||||||
|
|
||||||
|
Yapılandırmada `listen = True` ayarlayarak konuşmadan metne dönüştürmeyi de kullanabilirsiniz. Yalnızca CLI modu için.
|
||||||
|
|
||||||
|
Çıkmak için `goodbye` yazın/söyleyin.
|
||||||
|
|
||||||
|
İşte bazı kullanım örnekleri:
|
||||||
|
|
||||||
|
> *Python'da bir yılan oyunu yap!*
|
||||||
|
|
||||||
|
> *Rennes, Fransa'daki en iyi kafeleri internette ara ve adres bilgileriyle birlikte üçünü rennes_cafes.txt dosyasına kaydet.*
|
||||||
|
|
||||||
|
> *Bir sayının faktöriyelini hesaplayan bir Go programı yaz, çalışma alanına factorial.go olarak kaydet*
|
||||||
|
|
||||||
|
> *summer_pictures klasöründeki tüm JPG dosyalarını ara, bugünün tarihiyle yeniden adlandır ve yeniden adlandırılan dosyaların listesini photos_list.txt dosyasına kaydet*
|
||||||
|
|
||||||
|
> *2024'ün popüler bilim kurgu filmlerini internette ara ve bu gece izlemek için üçünü seç. Listeyi movie_night.txt dosyasına kaydet.*
|
||||||
|
|
||||||
|
> *2025'in en son yapay zeka haberleri makalelerini internette ara, üçünü seç ve başlıklarını ve özetlerini kazımak için bir Python betiği yaz. Betiği news_scraper.py olarak ve özetleri /home/projects içinde ai_news.txt olarak kaydet*
|
||||||
|
|
||||||
|
> *Cuma, internette ücretsiz bir hisse senedi fiyat API'si ara, supersuper7434567@gmail.com ile kaydol ve ardından API'yi kullanarak Tesla'nın günlük fiyatlarını çekmek için bir Python betiği yaz, sonuçları stock_prices.csv dosyasına kaydet*
|
||||||
|
|
||||||
|
*Form doldurma yeteneklerinin hâlâ deneysel olduğunu ve başarısız olabileceğini unutmayın.*
|
||||||
|
|
||||||
|
Sorgunuzu yazdıktan sonra AgenticSeek göreve en uygun ajanı atayacaktır.
|
||||||
|
|
||||||
|
Bu erken bir prototip olduğundan, ajan yönlendirme sistemi sorgunuza göre her zaman doğru ajanı atamayabilir.
|
||||||
|
|
||||||
|
Bu nedenle, ne istediğiniz ve yapay zekanın nasıl ilerlemesi gerektiği konusunda çok açık olun. Örneğin, web araması yapmasını istiyorsanız şunu demeyin:
|
||||||
|
|
||||||
|
`Solo seyahat için iyi ülkeler biliyor musun?`
|
||||||
|
|
||||||
|
Bunun yerine şöyle sorun:
|
||||||
|
|
||||||
|
`Web araması yap ve solo seyahat için en iyi ülkeleri bul`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## **LLM'yi Kendi Sunucunuzda Çalıştırma Kurulumu**
|
||||||
|
|
||||||
|
Güçlü bir bilgisayarınız veya kullanabileceğiniz bir sunucunuz varsa, ancak dizüstü bilgisayarınızdan kullanmak istiyorsanız, özel LLM sunucumuzu kullanarak LLM'yi uzak bir sunucuda çalıştırma seçeneğiniz vardır.
|
||||||
|
|
||||||
|
Yapay zeka modelini çalıştıracak "sunucunuzda" IP adresini alın
|
||||||
|
|
||||||
|
```sh
|
||||||
|
ip a | grep "inet " | grep -v 127.0.0.1 | awk '{print $2}' | cut -d/ -f1 # yerel IP
|
||||||
|
curl https://ipinfo.io/ip # genel IP
|
||||||
|
```
|
||||||
|
|
||||||
|
Not: Windows veya macOS için IP adresini bulmak üzere sırasıyla ipconfig veya ifconfig kullanın.
|
||||||
|
|
||||||
|
Depoyu klonlayın ve `server/` klasörüne girin.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git clone --depth 1 https://github.com/Fosowl/agenticSeek.git
|
||||||
|
cd agenticSeek/llm_server/
|
||||||
|
```
|
||||||
|
|
||||||
|
Sunucuya özel gereksinimleri yükleyin:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
pip3 install -r requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
Sunucu betiğini çalıştırın.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
python3 app.py --provider ollama --port 3333
|
||||||
|
```
|
||||||
|
|
||||||
|
LLM servisi olarak `ollama` veya `llamacpp` arasında seçim yapabilirsiniz.
|
||||||
|
|
||||||
|
Şimdi kişisel bilgisayarınızda:
|
||||||
|
|
||||||
|
`config.ini` dosyasında `provider_name` değerini `server` ve `provider_model` değerini `deepseek-r1:xxb` olarak ayarlayın.
|
||||||
|
`provider_server_address` değerini modeli çalıştıracak makinenin IP adresine ayarlayın.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
[MAIN]
|
||||||
|
is_local = False
|
||||||
|
provider_name = server
|
||||||
|
provider_model = deepseek-r1:70b
|
||||||
|
provider_server_address = http://x.x.x.x:3333
|
||||||
|
```
|
||||||
|
|
||||||
|
Sonraki adım: [Servisleri başlatın ve AgenticSeek'i çalıştırın](#servisleri-başlatın-ve-çalıştırın)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Konuşmadan Metne
|
||||||
|
|
||||||
|
Uyarı: Konuşmadan metne özelliği şu anda yalnızca CLI modunda çalışmaktadır.
|
||||||
|
|
||||||
|
Konuşmadan metne özelliğinin şu anda yalnızca İngilizce dilinde çalıştığını lütfen unutmayın.
|
||||||
|
|
||||||
|
Konuşmadan metne işlevi varsayılan olarak devre dışıdır. Etkinleştirmek için config.ini dosyasında listen seçeneğini True olarak ayarlayın:
|
||||||
|
|
||||||
|
```
|
||||||
|
listen = True
|
||||||
|
```
|
||||||
|
|
||||||
|
Etkinleştirildiğinde, konuşmadan metne özelliği girdinizi işlemeye başlamadan önce bir tetikleyici anahtar kelimeyi (ajanın adı) dinler. Ajanın adını *config.ini* dosyasındaki `agent_name` değerini güncelleyerek özelleştirebilirsiniz:
|
||||||
|
|
||||||
|
```
|
||||||
|
agent_name = Friday
|
||||||
|
```
|
||||||
|
|
||||||
|
En iyi tanıma performansı için ajan adı olarak "John" veya "Emma" gibi yaygın bir İngilizce isim kullanmanızı öneririz.
|
||||||
|
|
||||||
|
Transkript görünmeye başladığında, uyandırmak için ajanın adını yüksek sesle söyleyin (ör: "Friday").
|
||||||
|
|
||||||
|
Sorgunuzu net bir şekilde söyleyin.
|
||||||
|
|
||||||
|
Sisteme devam etmesi gerektiğini bildirmek için isteğinizi bir onay ifadesiyle bitirin. Onay ifadesi örnekleri:
|
||||||
|
```
|
||||||
|
"do it", "go ahead", "execute", "run", "start", "thanks", "would ya", "please", "okay?", "proceed", "continue", "go on", "do that", "go it", "do you understand?"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Yapılandırma
|
||||||
|
|
||||||
|
Örnek yapılandırma:
|
||||||
|
```
|
||||||
|
[MAIN]
|
||||||
|
is_local = True
|
||||||
|
provider_name = ollama
|
||||||
|
provider_model = deepseek-r1:32b
|
||||||
|
provider_server_address = http://127.0.0.1:11434 # Ollama örneği; LM-Studio için http://127.0.0.1:1234 kullanın
|
||||||
|
agent_name = Friday
|
||||||
|
recover_last_session = False
|
||||||
|
save_session = False
|
||||||
|
speak = False
|
||||||
|
listen = False
|
||||||
|
|
||||||
|
jarvis_personality = False
|
||||||
|
languages = en zh # TTS ve potansiyel yönlendirme için dil listesi.
|
||||||
|
[BROWSER]
|
||||||
|
headless_browser = False
|
||||||
|
stealth_mode = False
|
||||||
|
```
|
||||||
|
|
||||||
|
**`config.ini` Ayarlarının Açıklaması**:
|
||||||
|
|
||||||
|
* **`[MAIN]` Bölümü:**
|
||||||
|
* `is_local`: Yerel LLM sağlayıcısı (Ollama, LM-Studio, yerel OpenAI uyumlu sunucu) veya kendi barındırdığınız sunucu seçeneğini kullanıyorsanız `True`. Bulut tabanlı API (OpenAI, Google, vb.) kullanıyorsanız `False`.
|
||||||
|
* `provider_name`: LLM sağlayıcısını belirtir.
|
||||||
|
* Yerel seçenekler: `ollama`, `lm-studio`, `openai` (yerel OpenAI uyumlu sunucular için), `server` (kendi barındırdığınız sunucu kurulumu için).
|
||||||
|
* API seçenekleri: `openai`, `google`, `deepseek`, `huggingface`, `togetherAI`.
|
||||||
|
* `provider_model`: Seçilen sağlayıcı için belirli model adı veya kimliği (ör: Ollama için `deepseekcoder:6.7b`, OpenAI API için `gpt-3.5-turbo`, TogetherAI için `mistralai/Mixtral-8x7B-Instruct-v0.1`).
|
||||||
|
* `provider_server_address`: LLM sağlayıcınızın adresi.
|
||||||
|
* Yerel sağlayıcılar için: ör: Ollama için `http://127.0.0.1:11434`, LM-Studio için `http://127.0.0.1:1234`.
|
||||||
|
* `server` sağlayıcı türü için: Kendi barındırdığınız LLM sunucunuzun adresi (ör: `http://sunucu_ip_adresiniz:3333`).
|
||||||
|
* Bulut API'leri (`is_local = False`) için: Genellikle yok sayılır veya boş bırakılabilir, çünkü API uç noktası genellikle istemci kütüphanesi tarafından işlenir.
|
||||||
|
* `agent_name`: Yapay zeka asistanının adı (ör: Friday). Etkinleştirilmişse konuşmadan metne için tetikleyici kelime olarak kullanılır.
|
||||||
|
* `recover_last_session`: Önceki oturumun durumunu kurtarmaya çalışmak için `True`, yeni başlamak için `False`.
|
||||||
|
* `save_session`: Mevcut oturumun durumunu olası kurtarma için kaydetmek için `True`, aksi takdirde `False`.
|
||||||
|
* `speak`: Metinden sese sesli çıktıyı etkinleştirmek için `True`, devre dışı bırakmak için `False`.
|
||||||
|
* `listen`: Konuşmadan metne sesli girdiyi etkinleştirmek için `True` (yalnızca CLI modu), devre dışı bırakmak için `False`.
|
||||||
|
* `work_dir`: **Kritik:** AgenticSeek'in dosya okuyacağı/yazacağı dizin. **Bu yolun sisteminizde geçerli ve erişilebilir olduğundan emin olun.**
|
||||||
|
* `jarvis_personality`: Daha "Jarvis-benzeri" bir sistem istemi kullanmak için `True` (deneysel), standart istem için `False`.
|
||||||
|
* `languages`: Virgülle ayrılmış dil listesi (ör: `en, zh, fr`). TTS ses seçimi için kullanılır (varsayılan olarak ilki) ve LLM yönlendiricisine yardımcı olabilir. Yönlendirici verimliliği için çok fazla veya çok benzer dil kullanmaktan kaçının.
|
||||||
|
* **`[BROWSER]` Bölümü:**
|
||||||
|
* `headless_browser`: Otomatik tarayıcıyı görünür pencere olmadan çalıştırmak için `True` (web arayüzü veya etkileşimsiz kullanım için önerilir). Tarayıcı penceresini göstermek için `False` (CLI modu veya hata ayıklama için kullanışlıdır).
|
||||||
|
* `stealth_mode`: Tarayıcı otomasyonunun tespit edilmesini zorlaştıran önlemleri etkinleştirmek için `True`. Anticaptcha gibi tarayıcı eklentilerinin manuel olarak yüklenmesini gerektirebilir.
|
||||||
|
|
||||||
|
Bu bölüm desteklenen LLM sağlayıcı türlerini özetler. `config.ini` dosyasında yapılandırın.
|
||||||
|
|
||||||
|
**Yerel Sağlayıcılar (Kendi Donanımınızda Çalışır):**
|
||||||
|
|
||||||
|
| `config.ini`'deki Sağlayıcı Adı | `is_local` | Açıklama | Kurulum Bölümü |
|
||||||
|
|-------------------------------|------------|-----------------------------------------------------------------------------|------------------------------------------------------------------|
|
||||||
|
| `ollama` | `True` | Ollama kullanarak yerel LLM'leri sunun. | [LLM'yi yerel olarak çalıştırma kurulumu](#llmyi-makinenizde-yerel-olarak-çalıştırma-kurulumu) |
|
||||||
|
| `lm-studio` | `True` | LM-Studio kullanarak yerel LLM'leri sunun. | [LLM'yi yerel olarak çalıştırma kurulumu](#llmyi-makinenizde-yerel-olarak-çalıştırma-kurulumu) |
|
||||||
|
| `openai` (yerel sunucu için) | `True` | OpenAI uyumlu API sunan yerel bir sunucuya bağlanın (ör: llama.cpp). | [LLM'yi yerel olarak çalıştırma kurulumu](#llmyi-makinenizde-yerel-olarak-çalıştırma-kurulumu) |
|
||||||
|
| `server` | `False` | Başka bir makinede çalışan AgenticSeek kendi barındırmalı LLM sunucusuna bağlanın. | [LLM'yi kendi sunucunuzda çalıştırma kurulumu](#llmyi-kendi-sunucunuzda-çalıştırma-kurulumu) |
|
||||||
|
|
||||||
|
**API Sağlayıcıları (Bulut Tabanlı):**
|
||||||
|
|
||||||
|
| `config.ini`'deki Sağlayıcı Adı | `is_local` | Açıklama | Kurulum Bölümü |
|
||||||
|
|-------------------------------|------------|--------------------------------------------------|-----------------------------------------------------|
|
||||||
|
| `openai` | `False` | OpenAI'ın resmi API'sini kullanın (ör: GPT-3.5, GPT-4). | [API ile çalıştırma kurulumu](#api-ile-çalıştırma-kurulumu) |
|
||||||
|
| `google` | `False` | Google Gemini modellerini API üzerinden kullanın. | [API ile çalıştırma kurulumu](#api-ile-çalıştırma-kurulumu) |
|
||||||
|
| `deepseek` | `False` | Deepseek'in resmi API'sini kullanın. | [API ile çalıştırma kurulumu](#api-ile-çalıştırma-kurulumu) |
|
||||||
|
| `huggingface` | `False` | Hugging Face Inference API'yi kullanın. | [API ile çalıştırma kurulumu](#api-ile-çalıştırma-kurulumu) |
|
||||||
|
| `togetherAI` | `False` | TogetherAI API'si üzerinden çeşitli açık modelleri kullanın. | [API ile çalıştırma kurulumu](#api-ile-çalıştırma-kurulumu) |
|
||||||
|
|
||||||
|
---
|
||||||
|
## Sorun Giderme
|
||||||
|
|
||||||
|
Sorunlarla karşılaşırsanız bu bölüm rehberlik sağlar.
|
||||||
|
|
||||||
|
# Bilinen Sorunlar
|
||||||
|
|
||||||
|
## ChromeDriver Sorunları
|
||||||
|
|
||||||
|
**Hata Örneği:** `SessionNotCreatedException: Message: session not created: This version of ChromeDriver only supports Chrome version XXX`
|
||||||
|
|
||||||
|
### Temel Neden
|
||||||
|
ChromeDriver sürüm uyumsuzluğu şu durumlarda oluşur:
|
||||||
|
1. Yüklü ChromeDriver sürümünüz Chrome tarayıcı sürümünüzle eşleşmiyor
|
||||||
|
2. Docker ortamlarında `undetected_chromedriver`, bağlanan ikili dosyayı atlayarak kendi ChromeDriver sürümünü indirebilir
|
||||||
|
|
||||||
|
### Çözüm Adımları
|
||||||
|
|
||||||
|
#### 1. Chrome Sürümünüzü Kontrol Edin
|
||||||
|
Google Chrome'u açın → `Ayarlar > Chrome Hakkında` bölümünden sürümünüzü bulun (ör: "Sürüm 134.0.6998.88")
|
||||||
|
|
||||||
|
#### 2. Eşleşen ChromeDriver'ı İndirin
|
||||||
|
|
||||||
|
**Chrome 115 ve sonrası için:** [Chrome for Testing API](https://googlechromelabs.github.io/chrome-for-testing/) kullanın
|
||||||
|
- Chrome for Testing uygunluk panosunu ziyaret edin
|
||||||
|
- Chrome sürümünüzü veya en yakın eşleşmeyi bulun
|
||||||
|
- İşletim sisteminiz için ChromeDriver'ı indirin (Docker ortamları için Linux64)
|
||||||
|
|
||||||
|
**Eski Chrome sürümleri için:** [Eski ChromeDriver indirmeleri](https://chromedriver.chromium.org/downloads) kullanın
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
#### 3. ChromeDriver'ı Yükleyin (Bir Yöntem Seçin)
|
||||||
|
|
||||||
|
**Yöntem A: Proje Kök Dizini (Docker için Önerilir)**
|
||||||
|
```bash
|
||||||
|
# İndirilen chromedriver ikili dosyasını proje kök dizinine yerleştirin
|
||||||
|
cp path/to/downloaded/chromedriver ./chromedriver
|
||||||
|
chmod +x ./chromedriver # Linux/macOS'ta çalıştırılabilir yapın
|
||||||
|
```
|
||||||
|
|
||||||
|
**Yöntem B: Sistem PATH'i**
|
||||||
|
```bash
|
||||||
|
# Linux/macOS
|
||||||
|
sudo mv chromedriver /usr/local/bin/
|
||||||
|
sudo chmod +x /usr/local/bin/chromedriver
|
||||||
|
|
||||||
|
# Windows: chromedriver.exe dosyasını PATH'inizdeki bir klasöre yerleştirin
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 4. Kurulumu Doğrulayın
|
||||||
|
```bash
|
||||||
|
# ChromeDriver sürümünü test edin
|
||||||
|
./chromedriver --version
|
||||||
|
# Veya PATH'teyse:
|
||||||
|
chromedriver --version
|
||||||
|
```
|
||||||
|
|
||||||
|
### Docker'a Özel Notlar
|
||||||
|
|
||||||
|
⚠️ **Docker Kullanıcıları İçin Önemli:**
|
||||||
|
- Docker volume mount yaklaşımı gizli modda (`undetected_chromedriver`) çalışmayabilir
|
||||||
|
- **Çözüm**: ChromeDriver'ı proje kök dizinine `./chromedriver` olarak yerleştirin
|
||||||
|
- Uygulama bu ikili dosyayı otomatik olarak algılayıp kullanacaktır
|
||||||
|
- Loglarda şunu görmelisiniz: `"Using ChromeDriver from project root: ./chromedriver"`
|
||||||
|
|
||||||
|
### Sorun Giderme İpuçları
|
||||||
|
|
||||||
|
1. **Hâlâ sürüm uyumsuzluğu mu var?**
|
||||||
|
- ChromeDriver'ın çalıştırılabilir olduğunu doğrulayın: `ls -la ./chromedriver`
|
||||||
|
- ChromeDriver sürümünü kontrol edin: `./chromedriver --version`
|
||||||
|
- Chrome tarayıcı sürümünüzle eşleştiğinden emin olun
|
||||||
|
|
||||||
|
2. **Docker container sorunları mı var?**
|
||||||
|
- Backend loglarını kontrol edin: `docker logs backend`
|
||||||
|
- Şu mesajı arayın: `"Using ChromeDriver from project root"`
|
||||||
|
- Bulunamazsa dosyanın var olduğunu ve çalıştırılabilir olduğunu doğrulayın
|
||||||
|
|
||||||
|
3. **Chrome for Testing sürümleri**
|
||||||
|
- Mümkün olduğunca tam sürüm eşleşmesi kullanın
|
||||||
|
- 134.0.6998.88 sürümü için ChromeDriver 134.0.6998.165 kullanın (en yakın mevcut sürüm)
|
||||||
|
- Ana sürüm numaraları eşleşmelidir (134 = 134)
|
||||||
|
|
||||||
|
### Sürüm Uyumluluk Matrisi
|
||||||
|
|
||||||
|
| Chrome Sürümü | ChromeDriver Sürümü | Durum |
|
||||||
|
|----------------|---------------------|---------|
|
||||||
|
| 134.0.6998.x | 134.0.6998.165 | ✅ Çalışır |
|
||||||
|
| 133.0.6943.x | 133.0.6943.141 | ✅ Çalışır |
|
||||||
|
| 132.0.6834.x | 132.0.6834.159 | ✅ Çalışır |
|
||||||
|
|
||||||
|
*En güncel uyumluluk için [Chrome for Testing panosunu](https://googlechromelabs.github.io/chrome-for-testing/) kontrol edin*
|
||||||
|
|
||||||
|
`Exception: Failed to initialize browser: Message: session not created: This version of ChromeDriver only supports Chrome version 113
|
||||||
|
Current browser version is 134.0.6998.89 with binary path`
|
||||||
|
|
||||||
|
Bu hata, tarayıcınız ve chromedriver sürümü arasında uyumsuzluk varsa oluşur.
|
||||||
|
|
||||||
|
En son sürümü indirmek için şu adrese gidin:
|
||||||
|
|
||||||
|
https://developer.chrome.com/docs/chromedriver/downloads
|
||||||
|
|
||||||
|
Chrome sürüm 115 veya daha yenisini kullanıyorsanız:
|
||||||
|
|
||||||
|
https://googlechromelabs.github.io/chrome-for-testing/
|
||||||
|
|
||||||
|
adresinden işletim sisteminize uygun chromedriver sürümünü indirin.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Bu bölüm eksikse lütfen bir issue açın.
|
||||||
|
|
||||||
|
## Bağlantı Adaptörü Sorunları
|
||||||
|
|
||||||
|
```
|
||||||
|
Exception: Provider lm-studio failed: HTTP request failed: No connection adapters were found for '127.0.0.1:1234/v1/chat/completions'` (Not: port değişebilir)
|
||||||
|
```
|
||||||
|
|
||||||
|
* **Neden:** `config.ini` dosyasında `lm-studio` (veya benzeri yerel OpenAI uyumlu sunucular) için `provider_server_address` değerinde `http://` öneki eksik veya yanlış porta yönlendiriliyor.
|
||||||
|
* **Çözüm:**
|
||||||
|
* Adresin `http://` içerdiğinden emin olun. LM-Studio varsayılan olarak genellikle `http://127.0.0.1:1234` kullanır.
|
||||||
|
* Doğru `config.ini`: `provider_server_address = http://127.0.0.1:1234` (veya gerçek LM-Studio sunucu portunuz).
|
||||||
|
|
||||||
|
## SearxNG Temel URL'si Belirtilmemiş
|
||||||
|
|
||||||
|
```
|
||||||
|
raise ValueError("SearxNG base URL must be provided either as an argument or via the SEARXNG_BASE_URL environment variable.")
|
||||||
|
ValueError: SearxNG base URL must be provided either as an argument or via the SEARXNG_BASE_URL environment variable.`
|
||||||
|
```
|
||||||
|
|
||||||
|
Bu hata, CLI modunu yanlış SearxNG temel URL'si ile çalıştırdığınızda oluşabilir.
|
||||||
|
|
||||||
|
SEARXNG_BASE_URL, Docker'da mı yoksa ana makinede mi çalıştırdığınıza göre değişmelidir:
|
||||||
|
|
||||||
|
**Ana makinede çalıştırma**: `SEARXNG_BASE_URL="http://localhost:8080"`
|
||||||
|
|
||||||
|
**Tamamen Docker'da çalıştırma (web arayüzü)**: `SEARXNG_BASE_URL="http://searxng:8080"`
|
||||||
|
|
||||||
|
## SSS
|
||||||
|
|
||||||
|
**S: Hangi donanıma ihtiyacım var?**
|
||||||
|
|
||||||
|
| Model Boyutu | GPU | Yorum |
|
||||||
|
|-----------|--------|-----------------------------------------------------------|
|
||||||
|
| 7B | 8GB VRAM | ⚠️ Önerilmez. Düşük performans, sık halüsinasyonlar ve planlayıcı ajanlar büyük olasılıkla başarısız olur. |
|
||||||
|
| 14B | 12 GB VRAM (ör: RTX 3060) | ✅ Basit görevler için kullanılabilir. Web tarama ve planlama görevlerinde zorlanabilir. |
|
||||||
|
| 32B | 24+ GB VRAM (ör: RTX 4090) | 🚀 Çoğu görevde başarılı, görev planlamasında hâlâ zorlanabilir |
|
||||||
|
| 70B+ | 48+ GB VRAM | 💪 Mükemmel. Gelişmiş kullanım senaryoları için önerilir. |
|
||||||
|
|
||||||
|
**S: Hata alıyorum, ne yapmalıyım?**
|
||||||
|
|
||||||
|
Yerel sunucunun çalıştığından (`ollama serve`), `config.ini` dosyanızın sağlayıcınızla eşleştiğinden ve bağımlılıkların yüklü olduğundan emin olun. Hiçbiri işe yaramazsa bir issue açmaktan çekinmeyin.
|
||||||
|
|
||||||
|
**S: Gerçekten %100 yerel çalışabilir mi?**
|
||||||
|
|
||||||
|
Evet, Ollama, lm-studio veya server sağlayıcıları ile tüm konuşmadan metne, LLM ve metinden sese modelleri yerel olarak çalışır. Yerel olmayan seçenekler (OpenAI veya diğer API'ler) isteğe bağlıdır.
|
||||||
|
|
||||||
|
**S: Manus varken neden AgenticSeek kullanmalıyım?**
|
||||||
|
|
||||||
|
Manus'un aksine, AgenticSeek harici sistemlerden bağımsızlığa öncelik verir, size daha fazla kontrol, gizlilik ve API maliyetinden kaçınma imkânı sunar.
|
||||||
|
|
||||||
|
**S: Projenin arkasında kim var?**
|
||||||
|
|
||||||
|
Proje benim tarafımdan, bakımcı olarak görev yapan iki arkadaşım ve GitHub'daki açık kaynak topluluğundan katkıda bulunanlarla birlikte oluşturuldu. Bir startup veya herhangi bir kuruluşla bağlantılı değiliz, sadece tutkulu bireylerden oluşan bir grubuz.
|
||||||
|
|
||||||
|
X'te kişisel hesabım (https://x.com/Martin993886460) dışındaki herhangi bir AgenticSeek hesabı taklittir.
|
||||||
|
|
||||||
|
## Katkıda Bulunma
|
||||||
|
|
||||||
|
AgenticSeek'i geliştirmek için geliştiriciler arıyoruz! Açık issue'lara veya tartışmalara göz atın.
|
||||||
|
|
||||||
|
[Katkıda bulunma rehberi](./docs/CONTRIBUTING.md)
|
||||||
|
|
||||||
|
|
||||||
|
## Sponsorlar:
|
||||||
|
|
||||||
|
AgenticSeek'in yeteneklerini uçuş arama, seyahat planlama veya en iyi alışveriş fırsatlarını yakalama gibi özelliklerle geliştirmek ister misiniz? Daha fazla Jarvis benzeri yetenek açmak için SerpApi ile özel bir araç oluşturmayı düşünün. SerpApi ile, tam kontrolü elinizde tutarken ajanınızı özel görevler için güçlendirebilirsiniz.
|
||||||
|
|
||||||
|
<a href="https://serpapi.com/"><img src="./media/banners/sponsor_banner_serpapi.png" height="350" alt="SerpApi Banner" ></a>
|
||||||
|
|
||||||
|
Özel araçları nasıl entegre edeceğinizi öğrenmek için [Contributing.md](./docs/CONTRIBUTING.md) dosyasına bakın!
|
||||||
|
|
||||||
|
### **Patron Sponsor**:
|
||||||
|
|
||||||
|
- [tatra-labs](https://github.com/tatra-labs)
|
||||||
|
|
||||||
|
## Bakımcılar:
|
||||||
|
|
||||||
|
> [Fosowl](https://github.com/Fosowl) | Paris Saati
|
||||||
|
|
||||||
|
> [antoineVIVIES](https://github.com/antoineVIVIES) | Taipei Saati
|
||||||
|
|
||||||
|
## Özel Teşekkürler:
|
||||||
|
|
||||||
|
> [tcsenpai](https://github.com/tcsenpai) ve [plitc](https://github.com/plitc) Backend dockerizasyonuna yardımları için
|
||||||
|
|
||||||
|
[](https://www.star-history.com/#Fosowl/agenticSeek&Date)
|
||||||
@@ -22,6 +22,26 @@ from sources.utility import pretty_print
|
|||||||
from sources.logger import Logger
|
from sources.logger import Logger
|
||||||
from sources.schemas import QueryRequest, QueryResponse
|
from sources.schemas import QueryRequest, QueryResponse
|
||||||
|
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
|
|
||||||
|
def is_running_in_docker():
|
||||||
|
"""Detect if code is running inside a Docker container."""
|
||||||
|
# Method 1: Check for .dockerenv file
|
||||||
|
if os.path.exists('/.dockerenv'):
|
||||||
|
return True
|
||||||
|
|
||||||
|
# Method 2: Check cgroup
|
||||||
|
try:
|
||||||
|
with open('/proc/1/cgroup', 'r') as f:
|
||||||
|
return 'docker' in f.read()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
from celery import Celery
|
from celery import Celery
|
||||||
|
|
||||||
@@ -34,7 +54,7 @@ config.read('config.ini')
|
|||||||
|
|
||||||
api.add_middleware(
|
api.add_middleware(
|
||||||
CORSMiddleware,
|
CORSMiddleware,
|
||||||
allow_origins=["http://localhost", "http://localhost:3000"],
|
allow_origins=["*"],
|
||||||
allow_credentials=True,
|
allow_credentials=True,
|
||||||
allow_methods=["*"],
|
allow_methods=["*"],
|
||||||
allow_headers=["*"],
|
allow_headers=["*"],
|
||||||
@@ -49,6 +69,24 @@ def initialize_system():
|
|||||||
personality_folder = "jarvis" if config.getboolean('MAIN', 'jarvis_personality') else "base"
|
personality_folder = "jarvis" if config.getboolean('MAIN', 'jarvis_personality') else "base"
|
||||||
languages = config["MAIN"]["languages"].split(' ')
|
languages = config["MAIN"]["languages"].split(' ')
|
||||||
|
|
||||||
|
# Force headless mode in Docker containers
|
||||||
|
headless = config.getboolean('BROWSER', 'headless_browser')
|
||||||
|
if is_running_in_docker() and not headless:
|
||||||
|
# Print prominent warning to console (visible in docker-compose output)
|
||||||
|
print("\n" + "*" * 70)
|
||||||
|
print("*** WARNING: Detected Docker environment - forcing headless_browser=True ***")
|
||||||
|
print("*** INFO: To see the browser, run 'python cli.py' on your host machine ***")
|
||||||
|
print("*" * 70 + "\n")
|
||||||
|
|
||||||
|
# Flush to ensure it's displayed immediately
|
||||||
|
sys.stdout.flush()
|
||||||
|
|
||||||
|
# Also log to file
|
||||||
|
logger.warning("Detected Docker environment - forcing headless_browser=True")
|
||||||
|
logger.info("To see the browser, run 'python cli.py' on your host machine instead")
|
||||||
|
|
||||||
|
headless = True
|
||||||
|
|
||||||
provider = Provider(
|
provider = Provider(
|
||||||
provider_name=config["MAIN"]["provider_name"],
|
provider_name=config["MAIN"]["provider_name"],
|
||||||
model=config["MAIN"]["provider_model"],
|
model=config["MAIN"]["provider_model"],
|
||||||
@@ -58,7 +96,7 @@ def initialize_system():
|
|||||||
logger.info(f"Provider initialized: {provider.provider_name} ({provider.model})")
|
logger.info(f"Provider initialized: {provider.provider_name} ({provider.model})")
|
||||||
|
|
||||||
browser = Browser(
|
browser = Browser(
|
||||||
create_driver(headless=config.getboolean('BROWSER', 'headless_browser'), stealth_mode=stealth_mode, lang=languages[0]),
|
create_driver(headless=headless, stealth_mode=stealth_mode, lang=languages[0]),
|
||||||
anticaptcha_manual_install=stealth_mode
|
anticaptcha_manual_install=stealth_mode
|
||||||
)
|
)
|
||||||
logger.info("Browser initialized")
|
logger.info("Browser initialized")
|
||||||
@@ -240,11 +278,25 @@ async def process_query(request: QueryRequest):
|
|||||||
return JSONResponse(status_code=200, content=query_resp.jsonify())
|
return JSONResponse(status_code=200, content=query_resp.jsonify())
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"An error occurred: {str(e)}")
|
logger.error(f"An error occurred: {str(e)}")
|
||||||
sys.exit(1)
|
query_resp.answer = f"An error occurred: {str(e)}"
|
||||||
|
query_resp.reasoning = f"Error: {str(e)}"
|
||||||
|
return JSONResponse(status_code=500, content=query_resp.jsonify())
|
||||||
finally:
|
finally:
|
||||||
|
is_generating = False
|
||||||
logger.info("Processing finished")
|
logger.info("Processing finished")
|
||||||
if config.getboolean('MAIN', 'save_session'):
|
if config.getboolean('MAIN', 'save_session'):
|
||||||
interaction.save_session()
|
interaction.save_session()
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
uvicorn.run(api, host="0.0.0.0", port=8000)
|
# Print startup info
|
||||||
|
if is_running_in_docker():
|
||||||
|
print("[AgenticSeek] Starting in Docker container...")
|
||||||
|
else:
|
||||||
|
print("[AgenticSeek] Starting on host machine...")
|
||||||
|
|
||||||
|
envport = os.getenv("BACKEND_PORT")
|
||||||
|
if envport:
|
||||||
|
port = int(envport)
|
||||||
|
else:
|
||||||
|
port = 7777
|
||||||
|
uvicorn.run(api, host="0.0.0.0", port=port)
|
||||||
@@ -3,12 +3,11 @@ is_local = True
|
|||||||
provider_name = ollama
|
provider_name = ollama
|
||||||
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
|
||||||
agent_name = Name_of_your_AI
|
agent_name = Jarvis
|
||||||
recover_last_session = False
|
recover_last_session = False
|
||||||
save_session = False
|
save_session = False
|
||||||
speak = False
|
speak = False
|
||||||
listen = False
|
listen = False
|
||||||
work_dir = /Users/mlg/Documents/workspace_for_agenticseek
|
|
||||||
jarvis_personality = False
|
jarvis_personality = False
|
||||||
languages = en
|
languages = en
|
||||||
[BROWSER]
|
[BROWSER]
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
version: '3'
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
redis:
|
redis:
|
||||||
container_name: redis
|
container_name: redis
|
||||||
|
profiles: ["core", "full"]
|
||||||
image: docker.io/valkey/valkey:8-alpine
|
image: docker.io/valkey/valkey:8-alpine
|
||||||
command: valkey-server --save 30 1 --loglevel warning
|
command: valkey-server --save 30 1 --loglevel warning
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
@@ -24,15 +24,16 @@ services:
|
|||||||
|
|
||||||
searxng:
|
searxng:
|
||||||
container_name: searxng
|
container_name: searxng
|
||||||
|
profiles: ["core", "full"]
|
||||||
image: docker.io/searxng/searxng:latest
|
image: docker.io/searxng/searxng:latest
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
ports:
|
ports:
|
||||||
- "8080:8080"
|
- "8080:8080"
|
||||||
volumes:
|
volumes:
|
||||||
- ./searxng:/etc/searxng:rw
|
- ./searxng:/etc/searxng:rw,z
|
||||||
environment:
|
environment:
|
||||||
- SEARXNG_BASE_URL=http://localhost:8080/
|
- SEARXNG_BASE_URL=${SEARXNG_BASE_URL:-http://localhost:8080/}
|
||||||
- SEARXNG_SECRET_KEY=$(openssl rand -hex 32)
|
- SEARXNG_SECRET_KEY=${SEARXNG_SECRET_KEY}
|
||||||
- UWSGI_WORKERS=4
|
- UWSGI_WORKERS=4
|
||||||
- UWSGI_THREADS=4
|
- UWSGI_THREADS=4
|
||||||
cap_add:
|
cap_add:
|
||||||
@@ -51,50 +52,52 @@ services:
|
|||||||
|
|
||||||
frontend:
|
frontend:
|
||||||
container_name: frontend
|
container_name: frontend
|
||||||
|
profiles: ["core", "full"]
|
||||||
build:
|
build:
|
||||||
context: ./frontend
|
context: ./frontend
|
||||||
dockerfile: Dockerfile.frontend
|
dockerfile: Dockerfile.frontend
|
||||||
ports:
|
ports:
|
||||||
- "3000:3000"
|
- "3000:3000"
|
||||||
volumes:
|
volumes:
|
||||||
- ./frontend/agentic-seek-front/src:/app/src
|
- ./frontend/agentic-seek-front/src:/app/src:rw,z
|
||||||
- ./screenshots:/app/screenshots
|
- ./screenshots:/app/screenshots
|
||||||
environment:
|
environment:
|
||||||
- NODE_ENV=development
|
- NODE_ENV=development
|
||||||
- CHOKIDAR_USEPOLLING=true
|
- CHOKIDAR_USEPOLLING=true
|
||||||
- BACKEND_URL=http://backend:8000
|
- REACT_APP_BACKEND_URL=${REACT_APP_BACKEND_URL:-http://localhost:7777}
|
||||||
networks:
|
networks:
|
||||||
- agentic-seek-net
|
- agentic-seek-net
|
||||||
|
|
||||||
# NOTE: backend service is not working yet due to issue with chromedriver on docker.
|
backend:
|
||||||
# Therefore backend is run on host machine.
|
container_name: backend
|
||||||
# Open to pull requests to fix this.
|
profiles: ["backend", "full"]
|
||||||
|
build:
|
||||||
#backend:
|
context: .
|
||||||
# container_name: backend
|
dockerfile: Dockerfile.backend
|
||||||
# build:
|
ports:
|
||||||
# context: ./
|
- ${BACKEND_PORT:-7777}:${BACKEND_PORT:-7777}
|
||||||
# dockerfile: Dockerfile.backend
|
volumes:
|
||||||
# stdin_open: true
|
- ./:/app
|
||||||
# tty: true
|
- ${WORK_DIR:-.}:/opt/workspace
|
||||||
# shm_size: 8g
|
command: python3 api.py
|
||||||
# ports:
|
environment:
|
||||||
# - "8000:8000"
|
- SEARXNG_BASE_URL=${SEARXNG_BASE_URL:-http://searxng:8080}
|
||||||
# volumes:
|
- REDIS_URL=${REDIS_BASE_URL:-redis://redis:6379/0}
|
||||||
# - ./:/app
|
- WORK_DIR=/opt/workspace
|
||||||
# environment:
|
- BACKEND_PORT=${BACKEND_PORT}
|
||||||
# - NODE_ENV=development
|
- DOCKER_INTERNAL_URL=http://host.docker.internal
|
||||||
# - REDIS_URL=redis://redis:6379/0
|
- OPENAI_API_KEY=${OPENAI_API_KEY}
|
||||||
# - SEARXNG_URL=http://searxng:8080
|
- DEEPSEEK_API_KEY=${DEEPSEEK_API_KEY}
|
||||||
# - OLLAMA_URL=http://localhost:11434
|
- OPENROUTER_API_KEY=${OPENROUTER_API_KEY}
|
||||||
# - LM_STUDIO_URL=http://localhost:1234
|
- TOGETHER_API_KEY=${TOGETHER_API_KEY}
|
||||||
# extra_hosts:
|
- GOOGLE_API_KEY=${GOOGLE_API_KEY}
|
||||||
# - "host.docker.internal:host-gateway"
|
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
|
||||||
# depends_on:
|
- HUGGINGFACE_API_KEY=${HUGGINGFACE_API_KEY}
|
||||||
# - redis
|
- DSK_DEEPSEEK_API_KEY=${DSK_DEEPSEEK_API_KEY}
|
||||||
# - searxng
|
networks:
|
||||||
# networks:
|
- agentic-seek-net
|
||||||
# - agentic-seek-net
|
extra_hosts:
|
||||||
|
- "host.docker.internal:host-gateway"
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
redis-data:
|
redis-data:
|
||||||
|
|||||||
@@ -92,11 +92,13 @@ Here are some tasks and areas where we need contributions:
|
|||||||
|
|
||||||
Tools are extensions that enable agents to perform specific actions, such as running Python code, making API calls, or conducting web searches. All tools inherit from the Tools base class, which provides methods for parsing and executing tool instructions.
|
Tools are extensions that enable agents to perform specific actions, such as running Python code, making API calls, or conducting web searches. All tools inherit from the Tools base class, which provides methods for parsing and executing tool instructions.
|
||||||
|
|
||||||
## Tools parsing
|
## Understand Tools parsing
|
||||||
|
|
||||||
Agents invoke tools using a standardized format called a block. A block consists of the tool name followed by the content (e.g., code, query, or parameters) to execute. The format looks like this:
|
Agents invoke tools using a standardized format called a block. A block consists of the tool name followed by the content (e.g., code, query, or parameters) to execute. When creating a prompt for an Agent, you must explicitly tell them to use this format.
|
||||||
|
|
||||||
BECAUSE WE USE MARKDOWN QUOTE FORMAT, READING WILL BE BROKEN ON GITHUB PLEASE START READING THE FILE AS RAW: https://raw.githubusercontent.com/Fosowl/agenticSeek/refs/heads/main/CONTRIBUTING.md
|
The format looks like this:
|
||||||
|
|
||||||
|
BECAUSE WE USE MARKDOWN QUOTE FORMAT, READING WILL BE BROKEN ON GITHUB PLEASE START READING THE FILE AS RAW: https://raw.githubusercontent.com/Fosowl/agenticSeek/refs/heads/dev/docs/CONTRIBUTING.md
|
||||||
|
|
||||||
|
|
||||||
```<tool name>
|
```<tool name>
|
||||||
@@ -117,10 +119,9 @@ How to handle multiple arguments then ?
|
|||||||
|
|
||||||
Good question! Each tool is free to handle argument in it's own way within the block, but we provide a common parsing logic:
|
Good question! Each tool is free to handle argument in it's own way within the block, but we provide a common parsing logic:
|
||||||
|
|
||||||
```flight_search
|
```trip_search
|
||||||
from=Paris
|
from=Paris
|
||||||
to=Taipei
|
to=Toulouse
|
||||||
date=30/04/2026
|
|
||||||
```
|
```
|
||||||
|
|
||||||
To extract these parameters, use the `get_parameter_value` method provided by the Tools class. Each tool can define its own parameter-handling logic, but the Tools class ensures consistent parsing.
|
To extract these parameters, use the `get_parameter_value` method provided by the Tools class. Each tool can define its own parameter-handling logic, but the Tools class ensures consistent parsing.
|
||||||
@@ -135,7 +136,7 @@ print("Hello world")
|
|||||||
|
|
||||||
Will save the code in toto.py file within the work_folder defined in the config.ini
|
Will save the code in toto.py file within the work_folder defined in the config.ini
|
||||||
|
|
||||||
## Execution
|
## Tools Implementation
|
||||||
|
|
||||||
When developing a tool, you must implement three abstract methods defined in the Tools class to handle execution, failure detection, and feedback to the agent. These methods ensure consistent behavior across tools and enable robust interaction with the LLM.
|
When developing a tool, you must implement three abstract methods defined in the Tools class to handle execution, failure detection, and feedback to the agent. These methods ensure consistent behavior across tools and enable robust interaction with the LLM.
|
||||||
|
|
||||||
@@ -171,8 +172,38 @@ Recap:
|
|||||||
- get_parameter_value: Retrieves parameter values from a block's content.
|
- get_parameter_value: Retrieves parameter values from a block's content.
|
||||||
- File handling: Supports saving block content to files when a :path is specified.
|
- File handling: Supports saving block content to files when a :path is specified.
|
||||||
|
|
||||||
# Implementing and using Agents
|
## Prompting an Agent for Tools usage
|
||||||
|
|
||||||
|
Consider an example where you want to add a flight search tool to the casual agent, you will need to modify the prompt file for the CasualAgent (e.g., casual_agent.txt) to instruct the LLM to use the a simple flight_search tool. you could add to the prompt:
|
||||||
|
|
||||||
|
You can search for flights using the flight_search tool. Example:
|
||||||
|
```flight_search
|
||||||
|
RY7481
|
||||||
|
```
|
||||||
|
You simply need to enter the flight number, you will then various informations about the flight if it exist, such as : Airline, Status, Departure time, Arrival Time
|
||||||
|
|
||||||
|
## Add the tool to your agent
|
||||||
|
|
||||||
|
To add a tool to an agent you simply need to:
|
||||||
|
1. Import a tool.
|
||||||
|
2. Add the tool class to the **tools** dictionnary.
|
||||||
|
3. Update the agent prompt.
|
||||||
|
|
||||||
|
```python
|
||||||
|
from sources.tools.flightSearch import FlightSearch
|
||||||
|
|
||||||
|
class CasualAgent(Agent):
|
||||||
|
def __init__(self, name, prompt_path, provider, verbose=False):
|
||||||
|
super().__init__(name, prompt_path, provider, verbose, None)
|
||||||
|
self.tools = {
|
||||||
|
"flight_search": FlightSearch(),
|
||||||
|
}
|
||||||
|
self.role = "en"
|
||||||
|
self.type = "casual_agent"
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
# Implementing and using Agents
|
||||||
|
|
||||||
Agents are classes that define how an LLM interacts with users and processes inputs. They can use tools (e.g., for executing code or querying APIs) and maintain a memory of the conversation to provide context-aware responses. All agents inherit from the base Agent class, which provides core functionality like memory management and LLM communication.
|
Agents are classes that define how an LLM interacts with users and processes inputs. They can use tools (e.g., for executing code or querying APIs) and maintain a memory of the conversation to provide context-aware responses. All agents inherit from the base Agent class, which provides core functionality like memory management and LLM communication.
|
||||||
|
|
||||||
|
|||||||
@@ -2,15 +2,18 @@ FROM node:18
|
|||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
# Install dependencies
|
# Copy package files
|
||||||
COPY agentic-seek-front/package.json agentic-seek-front/package-lock.json ./
|
COPY agentic-seek-front/package.json agentic-seek-front/package-lock.json ./
|
||||||
RUN npm install
|
|
||||||
|
# Install dependencies with explicit bin linking
|
||||||
|
RUN npm ci && npm rebuild
|
||||||
|
|
||||||
# Copy application code
|
# Copy application code
|
||||||
COPY agentic-seek-front/ .
|
COPY agentic-seek-front/ .
|
||||||
|
|
||||||
# Expose port
|
# Verify react-scripts is available (catches install issues early)
|
||||||
|
RUN test -f node_modules/.bin/react-scripts || npm install react-scripts
|
||||||
|
|
||||||
EXPOSE 3000
|
EXPOSE 3000
|
||||||
|
|
||||||
# Run the application
|
|
||||||
CMD ["npm", "start"]
|
CMD ["npm", "start"]
|
||||||
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 148 KiB |
|
Before Width: | Height: | Size: 9.4 KiB |
@@ -1,100 +1,33 @@
|
|||||||
import React, { useState, useEffect, useRef } from 'react';
|
import React, { useState, useEffect, useRef, useCallback } from "react";
|
||||||
import ReactMarkdown from 'react-markdown';
|
import ReactMarkdown from "react-markdown";
|
||||||
import axios from 'axios';
|
import axios from "axios";
|
||||||
import './App.css';
|
import "./App.css";
|
||||||
import { colors } from './colors';
|
import { ThemeToggle } from "./components/ThemeToggle";
|
||||||
|
import { ResizableLayout } from "./components/ResizableLayout";
|
||||||
|
import faviconPng from "./logo.png";
|
||||||
|
|
||||||
|
const BACKEND_URL = process.env.REACT_APP_BACKEND_URL;
|
||||||
|
console.log("Using backend URL:", BACKEND_URL);
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const [query, setQuery] = useState('');
|
const [query, setQuery] = useState("");
|
||||||
const [messages, setMessages] = useState([]);
|
const [messages, setMessages] = useState([]);
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const [error, setError] = useState(null);
|
const [error, setError] = useState(null);
|
||||||
const [currentView, setCurrentView] = useState('blocks');
|
const [currentView, setCurrentView] = useState("blocks");
|
||||||
const [responseData, setResponseData] = useState(null);
|
const [responseData, setResponseData] = useState(null);
|
||||||
const [isOnline, setIsOnline] = useState(false);
|
const [isOnline, setIsOnline] = useState(false);
|
||||||
const [status, setStatus] = useState('Agents ready');
|
const [status, setStatus] = useState("Agents ready");
|
||||||
const [expandedReasoning, setExpandedReasoning] = useState(new Set());
|
const [expandedReasoning, setExpandedReasoning] = useState(new Set());
|
||||||
const messagesEndRef = useRef(null);
|
const messagesEndRef = useRef(null);
|
||||||
|
|
||||||
useEffect(() => {
|
const fetchLatestAnswer = useCallback(async () => {
|
||||||
const intervalId = setInterval(() => {
|
|
||||||
checkHealth();
|
|
||||||
fetchLatestAnswer();
|
|
||||||
fetchScreenshot();
|
|
||||||
}, 3000);
|
|
||||||
return () => clearInterval(intervalId);
|
|
||||||
}, [messages]);
|
|
||||||
|
|
||||||
const checkHealth = async () => {
|
|
||||||
try {
|
try {
|
||||||
await axios.get('http://127.0.0.1:8000/health');
|
const res = await axios.get(`${BACKEND_URL}/latest_answer`);
|
||||||
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://127.0.0.1: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) => {
|
|
||||||
return answer
|
|
||||||
.trim()
|
|
||||||
.toLowerCase()
|
|
||||||
.replace(/\s+/g, ' ')
|
|
||||||
.replace(/[.,!?]/g, '')
|
|
||||||
};
|
|
||||||
|
|
||||||
const scrollToBottom = () => {
|
|
||||||
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
|
|
||||||
};
|
|
||||||
|
|
||||||
const toggleReasoning = (messageIndex) => {
|
|
||||||
setExpandedReasoning(prev => {
|
|
||||||
const newSet = new Set(prev);
|
|
||||||
if (newSet.has(messageIndex)) {
|
|
||||||
newSet.delete(messageIndex);
|
|
||||||
} else {
|
|
||||||
newSet.add(messageIndex);
|
|
||||||
}
|
|
||||||
return newSet;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const fetchLatestAnswer = async () => {
|
|
||||||
try {
|
|
||||||
const res = await axios.get('http://127.0.0.1:8000/latest_answer');
|
|
||||||
const data = res.data;
|
const data = res.data;
|
||||||
|
|
||||||
updateData(data);
|
updateData(data);
|
||||||
if (!data.answer || data.answer.trim() === '') {
|
if (!data.answer || data.answer.trim() === "") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const normalizedNewAnswer = normalizeAnswer(data.answer);
|
const normalizedNewAnswer = normalizeAnswer(data.answer);
|
||||||
@@ -105,7 +38,7 @@ function App() {
|
|||||||
setMessages((prev) => [
|
setMessages((prev) => [
|
||||||
...prev,
|
...prev,
|
||||||
{
|
{
|
||||||
type: 'agent',
|
type: "agent",
|
||||||
content: data.answer,
|
content: data.answer,
|
||||||
reasoning: data.reasoning,
|
reasoning: data.reasoning,
|
||||||
agentName: data.agent_name,
|
agentName: data.agent_name,
|
||||||
@@ -116,11 +49,86 @@ function App() {
|
|||||||
setStatus(data.status);
|
setStatus(data.status);
|
||||||
scrollToBottom();
|
scrollToBottom();
|
||||||
} else {
|
} else {
|
||||||
console.log('Duplicate answer detected, skipping:', data.answer);
|
console.log("Duplicate answer detected, skipping:", data.answer);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error fetching latest answer:', error);
|
console.error("Error fetching latest answer:", error);
|
||||||
}
|
}
|
||||||
|
}, [messages]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const intervalId = setInterval(() => {
|
||||||
|
checkHealth();
|
||||||
|
fetchLatestAnswer();
|
||||||
|
fetchScreenshot();
|
||||||
|
}, 3000);
|
||||||
|
return () => clearInterval(intervalId);
|
||||||
|
}, [fetchLatestAnswer]);
|
||||||
|
|
||||||
|
const checkHealth = async () => {
|
||||||
|
try {
|
||||||
|
await axios.get(`${BACKEND_URL}/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(
|
||||||
|
`${BACKEND_URL}/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) => {
|
||||||
|
return answer
|
||||||
|
.trim()
|
||||||
|
.toLowerCase()
|
||||||
|
.replace(/\s+/g, " ")
|
||||||
|
.replace(/[.,!?]/g, "");
|
||||||
|
};
|
||||||
|
|
||||||
|
const scrollToBottom = () => {
|
||||||
|
messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleReasoning = (messageIndex) => {
|
||||||
|
setExpandedReasoning((prev) => {
|
||||||
|
const newSet = new Set(prev);
|
||||||
|
if (newSet.has(messageIndex)) {
|
||||||
|
newSet.delete(messageIndex);
|
||||||
|
} else {
|
||||||
|
newSet.add(messageIndex);
|
||||||
|
}
|
||||||
|
return newSet;
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateData = (data) => {
|
const updateData = (data) => {
|
||||||
@@ -141,97 +149,139 @@ function App() {
|
|||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
setError(null);
|
setError(null);
|
||||||
try {
|
try {
|
||||||
const res = await axios.get('http://127.0.0.1:8000/stop');
|
await axios.get(`${BACKEND_URL}/stop`);
|
||||||
setStatus("Requesting stop...");
|
setStatus("Requesting stop...");
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Error stopping the agent:', err);
|
console.error("Error stopping the agent:", err);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleSubmit = async (e) => {
|
const handleSubmit = async (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
checkHealth();
|
checkHealth();
|
||||||
if (!query.trim()) {
|
if (!query.trim()) {
|
||||||
console.log('Empty query');
|
console.log("Empty query");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setMessages((prev) => [...prev, { type: 'user', content: query }]);
|
setMessages((prev) => [...prev, { type: "user", content: query }]);
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
setError(null);
|
setError(null);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
console.log('Sending query:', query);
|
console.log("Sending query:", query);
|
||||||
setQuery('waiting for response...');
|
setQuery("waiting for response...");
|
||||||
const res = await axios.post('http://127.0.0.1:8000/query', {
|
const res = await axios.post(`${BACKEND_URL}/query`, {
|
||||||
query,
|
query,
|
||||||
tts_enabled: false
|
tts_enabled: false,
|
||||||
});
|
});
|
||||||
setQuery('Enter your query...');
|
setQuery("Enter your query...");
|
||||||
console.log('Response:', res.data);
|
console.log("Response:", res.data);
|
||||||
const data = res.data;
|
const data = res.data;
|
||||||
updateData(data);
|
updateData(data);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Error:', err);
|
console.error("Error:", err);
|
||||||
setError('Failed to process query.');
|
setError("Failed to process query.");
|
||||||
setMessages((prev) => [
|
setMessages((prev) => [
|
||||||
...prev,
|
...prev,
|
||||||
{ type: 'error', content: 'Error: Unable to get a response.' },
|
{ type: "error", content: "Error: Unable to get a response." },
|
||||||
]);
|
]);
|
||||||
} finally {
|
} finally {
|
||||||
console.log('Query completed');
|
console.log("Query completed");
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
setQuery('');
|
setQuery("");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleGetScreenshot = async () => {
|
const handleGetScreenshot = async () => {
|
||||||
try {
|
try {
|
||||||
setCurrentView('screenshot');
|
setCurrentView("screenshot");
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setError('Browser not in use');
|
setError("Browser not in use");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="app">
|
<div className="app">
|
||||||
<header className="header">
|
<header className="header">
|
||||||
|
<div className="header-brand">
|
||||||
|
<div className="logo-container">
|
||||||
|
<img src={faviconPng} alt="AgenticSeek" className="logo-icon" />
|
||||||
|
</div>
|
||||||
|
<div className="brand-text">
|
||||||
<h1>AgenticSeek</h1>
|
<h1>AgenticSeek</h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="header-status">
|
||||||
|
<div
|
||||||
|
className={`status-indicator ${isOnline ? "online" : "offline"}`}
|
||||||
|
>
|
||||||
|
<div className="status-dot"></div>
|
||||||
|
<span className="status-text">
|
||||||
|
{isOnline ? "Online" : "Offline"}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="header-actions">
|
||||||
|
<a
|
||||||
|
href="https://github.com/Fosowl/agenticSeek"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="action-button github-link"
|
||||||
|
aria-label="View on GitHub"
|
||||||
|
>
|
||||||
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
|
||||||
|
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z" />
|
||||||
|
</svg>
|
||||||
|
<span className="action-text">GitHub</span>
|
||||||
|
</a>
|
||||||
|
<div>
|
||||||
|
<ThemeToggle />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</header>
|
</header>
|
||||||
<main className="main">
|
<main className="main">
|
||||||
<div className="app-sections">
|
<ResizableLayout initialLeftWidth={50}>
|
||||||
<div className="chat-section">
|
<div className="chat-section">
|
||||||
<h2>Chat Interface</h2>
|
<h2>Chat Interface</h2>
|
||||||
<div className="messages">
|
<div className="messages">
|
||||||
{messages.length === 0 ? (
|
{messages.length === 0 ? (
|
||||||
<p className="placeholder">No messages yet. Type below to start!</p>
|
<p className="placeholder">
|
||||||
|
No messages yet. Type below to start!
|
||||||
|
</p>
|
||||||
) : (
|
) : (
|
||||||
messages.map((msg, index) => (
|
messages.map((msg, index) => (
|
||||||
<div
|
<div
|
||||||
key={index}
|
key={index}
|
||||||
className={`message ${
|
className={`message ${
|
||||||
msg.type === 'user'
|
msg.type === "user"
|
||||||
? 'user-message'
|
? "user-message"
|
||||||
: msg.type === 'agent'
|
: msg.type === "agent"
|
||||||
? 'agent-message'
|
? "agent-message"
|
||||||
: 'error-message'
|
: "error-message"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<div className="message-header">
|
<div className="message-header">
|
||||||
{msg.type === 'agent' && (
|
{msg.type === "agent" && (
|
||||||
<span className="agent-name">{msg.agentName}</span>
|
<span className="agent-name">{msg.agentName}</span>
|
||||||
)}
|
)}
|
||||||
{msg.type === 'agent' && msg.reasoning && expandedReasoning.has(index) && (
|
{msg.type === "agent" &&
|
||||||
|
msg.reasoning &&
|
||||||
|
expandedReasoning.has(index) && (
|
||||||
<div className="reasoning-content">
|
<div className="reasoning-content">
|
||||||
<ReactMarkdown>{msg.reasoning}</ReactMarkdown>
|
<ReactMarkdown>{msg.reasoning}</ReactMarkdown>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{msg.type === 'agent' && (
|
{msg.type === "agent" && (
|
||||||
<button
|
<button
|
||||||
className="reasoning-toggle"
|
className="reasoning-toggle"
|
||||||
onClick={() => toggleReasoning(index)}
|
onClick={() => toggleReasoning(index)}
|
||||||
title={expandedReasoning.has(index) ? "Hide reasoning" : "Show reasoning"}
|
title={
|
||||||
|
expandedReasoning.has(index)
|
||||||
|
? "Hide reasoning"
|
||||||
|
: "Show reasoning"
|
||||||
|
}
|
||||||
>
|
>
|
||||||
{expandedReasoning.has(index) ? '▼' : '▶'} Reasoning
|
{expandedReasoning.has(index) ? "▼" : "▶"} Reasoning
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -244,7 +294,11 @@ function App() {
|
|||||||
<div ref={messagesEndRef} />
|
<div ref={messagesEndRef} />
|
||||||
</div>
|
</div>
|
||||||
{isOnline && <div className="loading-animation">{status}</div>}
|
{isOnline && <div className="loading-animation">{status}</div>}
|
||||||
{!isLoading && !isOnline && <p className="loading-animation">System offline. Deploy backend first.</p>}
|
{!isLoading && !isOnline && (
|
||||||
|
<p className="loading-animation">
|
||||||
|
System offline. Deploy backend first.
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
<form onSubmit={handleSubmit} className="input-form">
|
<form onSubmit={handleSubmit} className="input-form">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
@@ -253,12 +307,41 @@ function App() {
|
|||||||
placeholder="Type your query..."
|
placeholder="Type your query..."
|
||||||
disabled={isLoading}
|
disabled={isLoading}
|
||||||
/>
|
/>
|
||||||
<button type="submit" disabled={isLoading}>
|
<div className="action-buttons">
|
||||||
Send
|
<button
|
||||||
|
type="submit"
|
||||||
|
disabled={isLoading}
|
||||||
|
className="icon-button"
|
||||||
|
aria-label="Send message"
|
||||||
|
>
|
||||||
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none">
|
||||||
|
<path
|
||||||
|
d="M22 2L11 13M22 2L15 22L11 13M22 2L2 9L11 13"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="2"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
<button onClick={handleStop}>
|
<button
|
||||||
Stop
|
type="button"
|
||||||
|
onClick={handleStop}
|
||||||
|
className="icon-button stop-button"
|
||||||
|
aria-label="Stop processing"
|
||||||
|
>
|
||||||
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none">
|
||||||
|
<rect
|
||||||
|
x="6"
|
||||||
|
y="6"
|
||||||
|
width="12"
|
||||||
|
height="12"
|
||||||
|
fill="currentColor"
|
||||||
|
rx="2"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -266,28 +349,36 @@ function App() {
|
|||||||
<h2>Computer View</h2>
|
<h2>Computer View</h2>
|
||||||
<div className="view-selector">
|
<div className="view-selector">
|
||||||
<button
|
<button
|
||||||
className={currentView === 'blocks' ? 'active' : ''}
|
className={currentView === "blocks" ? "active" : ""}
|
||||||
onClick={() => setCurrentView('blocks')}
|
onClick={() => setCurrentView("blocks")}
|
||||||
>
|
>
|
||||||
Editor View
|
Editor View
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
className={currentView === 'screenshot' ? 'active' : ''}
|
className={currentView === "screenshot" ? "active" : ""}
|
||||||
onClick={responseData?.screenshot ? () => setCurrentView('screenshot') : handleGetScreenshot}
|
onClick={
|
||||||
|
responseData?.screenshot
|
||||||
|
? () => setCurrentView("screenshot")
|
||||||
|
: handleGetScreenshot
|
||||||
|
}
|
||||||
>
|
>
|
||||||
Browser View
|
Browser View
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div className="content">
|
<div className="content">
|
||||||
{error && <p className="error">{error}</p>}
|
{error && <p className="error">{error}</p>}
|
||||||
{currentView === 'blocks' ? (
|
{currentView === "blocks" ? (
|
||||||
<div className="blocks">
|
<div className="blocks">
|
||||||
{responseData && responseData.blocks && Object.values(responseData.blocks).length > 0 ? (
|
{responseData &&
|
||||||
|
responseData.blocks &&
|
||||||
|
Object.values(responseData.blocks).length > 0 ? (
|
||||||
Object.values(responseData.blocks).map((block, index) => (
|
Object.values(responseData.blocks).map((block, index) => (
|
||||||
<div key={index} className="block">
|
<div key={index} className="block">
|
||||||
<p className="block-tool">Tool: {block.tool_type}</p>
|
<p className="block-tool">Tool: {block.tool_type}</p>
|
||||||
<pre>{block.block}</pre>
|
<pre>{block.block}</pre>
|
||||||
<p className="block-feedback">Feedback: {block.feedback}</p>
|
<p className="block-feedback">
|
||||||
|
Feedback: {block.feedback}
|
||||||
|
</p>
|
||||||
{block.success ? (
|
{block.success ? (
|
||||||
<p className="block-success">Success</p>
|
<p className="block-success">Success</p>
|
||||||
) : (
|
) : (
|
||||||
@@ -305,19 +396,19 @@ function App() {
|
|||||||
) : (
|
) : (
|
||||||
<div className="screenshot">
|
<div className="screenshot">
|
||||||
<img
|
<img
|
||||||
src={responseData?.screenshot || 'placeholder.png'}
|
src={responseData?.screenshot || "placeholder.png"}
|
||||||
alt="Screenshot"
|
alt="Screenshot"
|
||||||
onError={(e) => {
|
onError={(e) => {
|
||||||
e.target.src = 'placeholder.png';
|
e.target.src = "placeholder.png";
|
||||||
console.error('Failed to load screenshot');
|
console.error("Failed to load screenshot");
|
||||||
}}
|
}}
|
||||||
key={responseData?.screenshotTimestamp || 'default'}
|
key={responseData?.screenshotTimestamp || "default"}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</ResizableLayout>
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,63 +1,64 @@
|
|||||||
export const colors = {
|
export const colors = {
|
||||||
// Primary colors
|
// Primary colors - matching the dashboard theme
|
||||||
primary: '#0066cc',
|
primary: "#2563eb",
|
||||||
primaryLight: '#e6f2ff',
|
primaryLight: "#dbeafe",
|
||||||
primaryDark: '#004c99',
|
primaryDark: "#1d4ed8",
|
||||||
|
|
||||||
// Secondary colors
|
// Secondary colors - modern grays
|
||||||
secondary: '#6c757d',
|
secondary: "#64748b",
|
||||||
secondaryLight: '#f8f9fa',
|
secondaryLight: "#f1f5f9",
|
||||||
secondaryDark: '#343a40',
|
secondaryDark: "#1e293b",
|
||||||
|
|
||||||
// Accent colors
|
// Accent colors
|
||||||
accent: '#ff9500',
|
accent: "#f59e0b",
|
||||||
accentLight: '#fff4e6',
|
accentLight: "#fef3c7",
|
||||||
accentDark: '#cc7a00',
|
accentDark: "#d97706",
|
||||||
|
|
||||||
// Status colors
|
// Status colors
|
||||||
success: '#28a745',
|
success: "#10b981",
|
||||||
successLight: '#e8f5e9',
|
successLight: "#d1fae5",
|
||||||
warning: '#ffc107',
|
warning: "#f59e0b",
|
||||||
warningLight: '#fff9e6',
|
warningLight: "#fef3c7",
|
||||||
error: '#dc3545',
|
error: "#ef4444",
|
||||||
errorLight: '#ffebee',
|
errorLight: "#fee2e2",
|
||||||
info: '#17a2b8',
|
info: "#06b6d4",
|
||||||
infoLight: '#e3f2fd',
|
infoLight: "#cffafe",
|
||||||
|
|
||||||
// Neutral colors
|
// Neutral colors - modern palette
|
||||||
white: '#ffffff',
|
white: "#ffffff",
|
||||||
gray100: '#f8f9fa',
|
gray50: "#f8fafc",
|
||||||
gray200: '#e9ecef',
|
gray100: "#f1f5f9",
|
||||||
gray300: '#dee2e6',
|
gray200: "#e2e8f0",
|
||||||
gray400: '#ced4da',
|
gray300: "#cbd5e1",
|
||||||
gray500: '#adb5bd',
|
gray400: "#94a3b8",
|
||||||
gray600: '#6c757d',
|
gray500: "#64748b",
|
||||||
gray700: '#495057',
|
gray600: "#475569",
|
||||||
gray800: '#343a40',
|
gray700: "#334155",
|
||||||
gray900: '#212529',
|
gray800: "#1e293b",
|
||||||
black: '#000000',
|
gray900: "#0f172a",
|
||||||
|
black: "#000000",
|
||||||
|
|
||||||
// Text colors
|
// Text colors
|
||||||
textPrimary: '#212529',
|
textPrimary: "#0f172a",
|
||||||
textSecondary: '#6c757d',
|
textSecondary: "#64748b",
|
||||||
textDisabled: '#adb5bd',
|
textDisabled: "#94a3b8",
|
||||||
|
|
||||||
// Background colors
|
// Background colors
|
||||||
background: '#f8f8f8',
|
background: "#f8fafc",
|
||||||
card: '#ffffff',
|
card: "#ffffff",
|
||||||
|
|
||||||
// Border colors
|
// Border colors
|
||||||
border: '#dee2e6',
|
border: "#e2e8f0",
|
||||||
divider: '#e9ecef',
|
divider: "#f1f5f9",
|
||||||
|
|
||||||
// Transparent colors
|
// Transparent colors
|
||||||
transparent: 'transparent',
|
transparent: "transparent",
|
||||||
semiTransparent: 'rgba(0, 0, 0, 0.5)',
|
semiTransparent: "rgba(15, 23, 42, 0.6)",
|
||||||
|
|
||||||
// Dark theme colors
|
// Dark theme colors
|
||||||
darkBackground: '#0f172a',
|
darkBackground: "#0f172a",
|
||||||
darkCard: '#1e293b',
|
darkCard: "#1e293b",
|
||||||
darkBorder: '#334155',
|
darkBorder: "#334155",
|
||||||
darkText: '#f8fafc',
|
darkText: "#f8fafc",
|
||||||
darkTextSecondary: '#cbd5e1',
|
darkTextSecondary: "#cbd5e1",
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
.resizable-container {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resizable-left,
|
||||||
|
.resizable-right {
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resize-handle {
|
||||||
|
width: 8px;
|
||||||
|
background-color: transparent;
|
||||||
|
cursor: col-resize;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 0 2px;
|
||||||
|
transition: background-color 0.2s ease;
|
||||||
|
position: relative;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resize-handle:hover {
|
||||||
|
background-color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.resize-handle-line {
|
||||||
|
width: 2px;
|
||||||
|
height: 40px;
|
||||||
|
background-color: var(--border);
|
||||||
|
border-radius: 1px;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resize-handle:hover .resize-handle-line {
|
||||||
|
background-color: var(--accent-foreground);
|
||||||
|
height: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resizable-container.dragging .resize-handle {
|
||||||
|
background-color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.resizable-container.dragging .resize-handle-line {
|
||||||
|
background-color: var(--accent-foreground);
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mobile responsiveness */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.resizable-container {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resizable-left,
|
||||||
|
.resizable-right {
|
||||||
|
width: 100% !important;
|
||||||
|
height: 50vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resize-handle {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
import React, { useState, useRef, useCallback } from "react";
|
||||||
|
import "./ResizableLayout.css";
|
||||||
|
|
||||||
|
export const ResizableLayout = ({ children, initialLeftWidth = 50 }) => {
|
||||||
|
const [leftWidth, setLeftWidth] = useState(initialLeftWidth);
|
||||||
|
const [isDragging, setIsDragging] = useState(false);
|
||||||
|
const containerRef = useRef(null);
|
||||||
|
|
||||||
|
const handleMouseDown = useCallback((e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setIsDragging(true);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleMouseMove = useCallback(
|
||||||
|
(e) => {
|
||||||
|
if (!isDragging || !containerRef.current) return;
|
||||||
|
|
||||||
|
const containerRect = containerRef.current.getBoundingClientRect();
|
||||||
|
const newLeftWidth =
|
||||||
|
((e.clientX - containerRect.left) / containerRect.width) * 100;
|
||||||
|
|
||||||
|
// Constrain between 20% and 80%
|
||||||
|
const constrainedWidth = Math.max(20, Math.min(80, newLeftWidth));
|
||||||
|
setLeftWidth(constrainedWidth);
|
||||||
|
},
|
||||||
|
[isDragging]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleMouseUp = useCallback(() => {
|
||||||
|
setIsDragging(false);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (isDragging) {
|
||||||
|
document.addEventListener("mousemove", handleMouseMove);
|
||||||
|
document.addEventListener("mouseup", handleMouseUp);
|
||||||
|
document.body.style.cursor = "col-resize";
|
||||||
|
document.body.style.userSelect = "none";
|
||||||
|
} else {
|
||||||
|
document.removeEventListener("mousemove", handleMouseMove);
|
||||||
|
document.removeEventListener("mouseup", handleMouseUp);
|
||||||
|
document.body.style.cursor = "";
|
||||||
|
document.body.style.userSelect = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener("mousemove", handleMouseMove);
|
||||||
|
document.removeEventListener("mouseup", handleMouseUp);
|
||||||
|
document.body.style.cursor = "";
|
||||||
|
document.body.style.userSelect = "";
|
||||||
|
};
|
||||||
|
}, [isDragging, handleMouseMove, handleMouseUp]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
ref={containerRef}
|
||||||
|
className={`resizable-container ${isDragging ? "dragging" : ""}`}
|
||||||
|
>
|
||||||
|
<div className="resizable-left" style={{ width: `${leftWidth}%` }}>
|
||||||
|
{children[0]}
|
||||||
|
</div>
|
||||||
|
<div className="resize-handle" onMouseDown={handleMouseDown}>
|
||||||
|
<div className="resize-handle-line" />
|
||||||
|
</div>
|
||||||
|
<div className="resizable-right" style={{ width: `${100 - leftWidth}%` }}>
|
||||||
|
{children[1]}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { useTheme } from "../contexts/ThemeContext";
|
||||||
|
|
||||||
|
export const ThemeToggle = () => {
|
||||||
|
const { isDark, toggleTheme } = useTheme();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
onClick={toggleTheme}
|
||||||
|
className="theme-toggle"
|
||||||
|
aria-label="Toggle theme"
|
||||||
|
>
|
||||||
|
{isDark ? (
|
||||||
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none">
|
||||||
|
<circle cx="12" cy="12" r="5" stroke="currentColor" strokeWidth="2" />
|
||||||
|
<path
|
||||||
|
d="M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="2"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
) : (
|
||||||
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none">
|
||||||
|
<path
|
||||||
|
d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="2"
|
||||||
|
fill="currentColor"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import React, { createContext, useContext, useState, useEffect } from "react";
|
||||||
|
|
||||||
|
const ThemeContext = createContext();
|
||||||
|
|
||||||
|
export const ThemeProvider = ({ children }) => {
|
||||||
|
const [isDark, setIsDark] = useState(() => {
|
||||||
|
const saved = localStorage.getItem("theme");
|
||||||
|
return saved ? saved === "dark" : true; // Default to dark
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
localStorage.setItem("theme", isDark ? "dark" : "light");
|
||||||
|
document.documentElement.setAttribute(
|
||||||
|
"data-theme",
|
||||||
|
isDark ? "dark" : "light"
|
||||||
|
);
|
||||||
|
}, [isDark]);
|
||||||
|
|
||||||
|
const toggleTheme = () => setIsDark(!isDark);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ThemeContext.Provider value={{ isDark, toggleTheme }}>
|
||||||
|
{children}
|
||||||
|
</ThemeContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useTheme = () => {
|
||||||
|
const context = useContext(ThemeContext);
|
||||||
|
if (!context) {
|
||||||
|
throw new Error("useTheme must be used within ThemeProvider");
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
};
|
||||||
@@ -1,10 +1,14 @@
|
|||||||
import React from 'react';
|
import React from "react";
|
||||||
import ReactDOM from 'react-dom/client';
|
import ReactDOM from "react-dom/client";
|
||||||
import App from './App';
|
import App from "./App";
|
||||||
|
import { ThemeProvider } from "./contexts/ThemeContext";
|
||||||
|
import "./styles/globals.css";
|
||||||
|
|
||||||
const root = ReactDOM.createRoot(document.getElementById('root'));
|
const root = ReactDOM.createRoot(document.getElementById("root"));
|
||||||
root.render(
|
root.render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
|
<ThemeProvider>
|
||||||
<App />
|
<App />
|
||||||
|
</ThemeProvider>
|
||||||
</React.StrictMode>
|
</React.StrictMode>
|
||||||
);
|
);
|
||||||
|
After Width: | Height: | Size: 148 KiB |
@@ -1 +0,0 @@
|
|||||||
<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>
|
|
||||||
|
Before Width: | Height: | Size: 2.6 KiB |
@@ -0,0 +1,217 @@
|
|||||||
|
:root {
|
||||||
|
--background: hsl(0 0% 100%);
|
||||||
|
--foreground: hsl(222.2 47.4% 11.2%);
|
||||||
|
--muted: hsl(210 40% 96.1%);
|
||||||
|
--muted-foreground: hsl(215.4 16.3% 46.9%);
|
||||||
|
--popover: hsl(0 0% 100%);
|
||||||
|
--popover-foreground: hsl(222.2 47.4% 11.2%);
|
||||||
|
--card: hsl(0 0% 100%);
|
||||||
|
--card-foreground: hsl(222.2 47.4% 11.2%);
|
||||||
|
--border: hsl(214.3 31.8% 91.4%);
|
||||||
|
--input: hsl(214.3 31.8% 91.4%);
|
||||||
|
--primary: hsl(222.2 47.4% 11.2%);
|
||||||
|
--primary-foreground: hsl(210 40% 98%);
|
||||||
|
--secondary: hsl(210 40% 96.1%);
|
||||||
|
--secondary-foreground: hsl(222.2 47.4% 11.2%);
|
||||||
|
--accent: hsl(210 40% 96.1%);
|
||||||
|
--accent-foreground: hsl(222.2 47.4% 11.2%);
|
||||||
|
--destructive: hsl(0 100% 50%);
|
||||||
|
--destructive-foreground: hsl(210 40% 98%);
|
||||||
|
--ring: hsl(215 20.2% 65.1%);
|
||||||
|
--radius: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark {
|
||||||
|
--background: hsl(224 71% 4%);
|
||||||
|
--foreground: hsl(213 31% 91%);
|
||||||
|
--muted: hsl(223 47% 11%);
|
||||||
|
--muted-foreground: hsl(215.4 16.3% 56.9%);
|
||||||
|
--popover: hsl(224 71% 4%);
|
||||||
|
--popover-foreground: hsl(215 20.2% 65.1%);
|
||||||
|
--card: hsl(224 71% 4%);
|
||||||
|
--card-foreground: hsl(213 31% 91%);
|
||||||
|
--border: hsl(216 34% 17%);
|
||||||
|
--input: hsl(216 34% 17%);
|
||||||
|
--primary: hsl(210 40% 98%);
|
||||||
|
--primary-foreground: hsl(222.2 47.4% 1.2%);
|
||||||
|
--secondary: hsl(222.2 47.4% 11.2%);
|
||||||
|
--secondary-foreground: hsl(210 40% 98%);
|
||||||
|
--accent: hsl(216 34% 17%);
|
||||||
|
--accent-foreground: hsl(210 40% 98%);
|
||||||
|
--destructive: hsl(0 63% 31%);
|
||||||
|
--destructive-foreground: hsl(210 40% 98%);
|
||||||
|
--ring: hsl(216 34% 17%);
|
||||||
|
--radius: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] {
|
||||||
|
--background: #0a0a0a;
|
||||||
|
--foreground: #fafafa;
|
||||||
|
--card: #1a1a1a;
|
||||||
|
--card-foreground: #fafafa;
|
||||||
|
--popover: #1a1a1a;
|
||||||
|
--popover-foreground: #fafafa;
|
||||||
|
--primary: #fafafa;
|
||||||
|
--primary-foreground: #0a0a0a;
|
||||||
|
--secondary: #2a2a2a;
|
||||||
|
--secondary-foreground: #fafafa;
|
||||||
|
--muted: #1e1e1e;
|
||||||
|
--muted-foreground: #a1a1aa;
|
||||||
|
--accent: #6b7280;
|
||||||
|
--accent-foreground: #ffffff;
|
||||||
|
--destructive: #ef4444;
|
||||||
|
--destructive-foreground: #ffffff;
|
||||||
|
--border: #333333;
|
||||||
|
--input: #333333;
|
||||||
|
--ring: #6b7280;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="light"] {
|
||||||
|
--background: #ffffff;
|
||||||
|
--foreground: #0a0a0a;
|
||||||
|
--card: #ffffff;
|
||||||
|
--card-foreground: #0a0a0a;
|
||||||
|
--popover: #ffffff;
|
||||||
|
--popover-foreground: #0a0a0a;
|
||||||
|
--primary: #0a0a0a;
|
||||||
|
--primary-foreground: #ffffff;
|
||||||
|
--secondary: #f5f5f5;
|
||||||
|
--secondary-foreground: #0a0a0a;
|
||||||
|
--muted: #f5f5f5;
|
||||||
|
--muted-foreground: #737373;
|
||||||
|
--accent: #6b7280;
|
||||||
|
--accent-foreground: #ffffff;
|
||||||
|
--destructive: #ef4444;
|
||||||
|
--destructive-foreground: #ffffff;
|
||||||
|
--border: #e5e5e5;
|
||||||
|
--input: #e5e5e5;
|
||||||
|
--ring: #6b7280;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background-color: var(--background);
|
||||||
|
color: var(--foreground);
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto",
|
||||||
|
sans-serif;
|
||||||
|
transition: background-color 0.3s ease, color 0.3s ease;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
height: 100vh;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
html,
|
||||||
|
body,
|
||||||
|
#root {
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-toggle {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 8px;
|
||||||
|
min-width: 44px;
|
||||||
|
height: 44px;
|
||||||
|
padding: 0 12px;
|
||||||
|
border-radius: 12px;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
background: var(--card);
|
||||||
|
color: var(--foreground);
|
||||||
|
text-decoration: none;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: 500;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-toggle::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: -100%;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: linear-gradient(
|
||||||
|
90deg,
|
||||||
|
transparent,
|
||||||
|
rgba(255, 255, 255, 0.1),
|
||||||
|
transparent
|
||||||
|
);
|
||||||
|
transition: left 0.5s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-toggle:hover::before {
|
||||||
|
left: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-toggle:hover {
|
||||||
|
background: #24292e;
|
||||||
|
border-color: #24292e;
|
||||||
|
color: white;
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 8px 25px rgba(36, 41, 46, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.github-link {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 8px;
|
||||||
|
min-width: 44px;
|
||||||
|
height: 44px;
|
||||||
|
padding: 0 12px;
|
||||||
|
border-radius: 12px;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
background: var(--card);
|
||||||
|
color: var(--foreground);
|
||||||
|
text-decoration: none;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: 500;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.github-link::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: -100%;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: linear-gradient(
|
||||||
|
90deg,
|
||||||
|
transparent,
|
||||||
|
rgba(255, 255, 255, 0.1),
|
||||||
|
transparent
|
||||||
|
);
|
||||||
|
transition: left 0.5s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.github-link:hover::before {
|
||||||
|
left: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.github-link:hover {
|
||||||
|
background: #24292e;
|
||||||
|
border-color: #24292e;
|
||||||
|
color: white;
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 8px 25px rgba(36, 41, 46, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
@@ -5,7 +5,6 @@ set LLM_ROUTER_DIR=llm_router
|
|||||||
if exist "%SCRIPTS_DIR%\windows_install.bat" (
|
if exist "%SCRIPTS_DIR%\windows_install.bat" (
|
||||||
echo Running Windows installation script...
|
echo Running Windows installation script...
|
||||||
call "%SCRIPTS_DIR%\windows_install.bat"
|
call "%SCRIPTS_DIR%\windows_install.bat"
|
||||||
cd "%LLM_ROUTER_DIR%" && call dl_safetensors.bat
|
|
||||||
) else (
|
) else (
|
||||||
echo Error: %SCRIPTS_DIR%\windows_install.bat not found!
|
echo Error: %SCRIPTS_DIR%\windows_install.bat not found!
|
||||||
exit /b 1
|
exit /b 1
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
##########
|
##########
|
||||||
# Dummy script to download the model
|
# Dummy script to download the model
|
||||||
# Because dowloading with hugging face does not seem to work, maybe I am doing something wrong?
|
# Because downloading with hugging face does not seem to work, maybe I am doing something wrong?
|
||||||
# AdaptiveClassifier.from_pretrained("adaptive-classifier/llm-router") ----> result in config.json not found
|
# AdaptiveClassifier.from_pretrained("adaptive-classifier/llm-router") ----> result in config.json not found
|
||||||
# Therefore, I put all the files in llm_router and download the model file with this script, If you know a better way please raise an issue
|
# Therefore, I put all the files in llm_router and download the model file with this script, If you know a better way please raise an issue
|
||||||
#########
|
#########
|
||||||
|
|||||||
@@ -6966,7 +6966,7 @@
|
|||||||
0.010188529267907143
|
0.010188529267907143
|
||||||
],
|
],
|
||||||
"label": "LOW",
|
"label": "LOW",
|
||||||
"text": "You are AoControl, a helpful and knowledgeable agent. To achieve your goal of answering complex questions correctly, you have access to the following tools:\n AoInnovus: An agent with knowledge of the Cadence Innovus User Guide and corresponding Tcl command usage\n AoTcl: An agent with knowledge of the Tcl language\nTo answer questions, you'll need to go through multiple steps involving step-by-step thinking and selecting appropriate tools and their inputs; tools will respond with observations.\nWhen you are ready for a final answer, respond with the `Final Answer:`\nUse the following format:\nQuestion: The question to be answered\nTHought: Reason if you have the final answer. If yes, anwer the question. If not, find out the missing information needed to answer it.\nTool: Pick one of {AoInnovus, AoTcl}\nTool Input: The input for the tool\nObservation: The tool will respond with the result\n...\nFinal Answer: The final answer to the question, make it short (50-100 words)\nThought, Tool, Tool Input, and Observation steps can be repeated multiple times, but sometimes we can find an answer in the first pass.\n---\nQuestion: How do I identify the impact of metal fill on my timing violations?\nThought: Let's think step-by-step, I first need to"
|
"text": "You are AoControl, a helpful and knowledgeable agent. To achieve your goal of answering complex questions correctly, you have access to the following tools:\n AoInnovus: An agent with knowledge of the Cadence Innovus User Guide and corresponding Tcl command usage\n AoTcl: An agent with knowledge of the Tcl language\nTo answer questions, you'll need to go through multiple steps involving step-by-step thinking and selecting appropriate tools and their inputs; tools will respond with observations.\nWhen you are ready for a final answer, respond with the `Final Answer:`\nUse the following format:\nQuestion: The question to be answered\nTHought: Reason if you have the final answer. If yes, answer the question. If not, find out the missing information needed to answer it.\nTool: Pick one of {AoInnovus, AoTcl}\nTool Input: The input for the tool\nObservation: The tool will respond with the result\n...\nFinal Answer: The final answer to the question, make it short (50-100 words)\nThought, Tool, Tool Input, and Observation steps can be repeated multiple times, but sometimes we can find an answer in the first pass.\n---\nQuestion: How do I identify the impact of metal fill on my timing violations?\nThought: Let's think step-by-step, I first need to"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"embedding": [
|
"embedding": [
|
||||||
|
|||||||
|
After Width: | Height: | Size: 118 KiB |
@@ -20,7 +20,7 @@ toto.py
|
|||||||
You can execute bash command using the bash tag :
|
You can execute bash command using the bash tag :
|
||||||
```bash
|
```bash
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
ls -la # exemple
|
ls -la # example
|
||||||
```
|
```
|
||||||
|
|
||||||
You can execute python using the python tag
|
You can execute python using the python tag
|
||||||
@@ -48,5 +48,5 @@ Some rules:
|
|||||||
- 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.
|
||||||
- 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 laziness, write and rewrite full code every time
|
||||||
- If query is unclear say REQUEST_CLARIFICATION
|
- If query is unclear say REQUEST_CLARIFICATION
|
||||||
@@ -29,7 +29,7 @@ You: Sure, here is the plan:
|
|||||||
|
|
||||||
## Task 3: I will setup the project using the file agent
|
## Task 3: I will setup the project using the file agent
|
||||||
|
|
||||||
## Task 4: I asign the coding agent to make a weather app in python
|
## Task 4: I assign the coding agent to make a weather app in python
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
@@ -74,7 +74,7 @@ Rules:
|
|||||||
- Put your plan in a json with the key "plan".
|
- Put your plan in a json with the key "plan".
|
||||||
- specify work folder name to all coding or file agents.
|
- specify work folder name to all coding or file agents.
|
||||||
- You might use a file agent before code agent to setup a project properly. specify folder name.
|
- You might use a file agent before code agent to setup a project properly. specify folder name.
|
||||||
- Give clear, detailled order to each agent and how their task relate to the previous task (if any).
|
- Give clear, detailed order to each agent and how their task relate to the previous task (if any).
|
||||||
- The file agent can only conduct one action at the time. successive file agent could be needed.
|
- The file agent can only conduct one action at the time. successive file agent could be needed.
|
||||||
- Only use web agent for finding necessary informations.
|
- Only use web agent for finding necessary informations.
|
||||||
- Always tell the coding agent where to save file.
|
- Always tell the coding agent where to save file.
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
[project]
|
||||||
|
name = "agenticseek"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "Add your description here"
|
||||||
|
readme = "README.md"
|
||||||
|
requires-python = ">=3.10"
|
||||||
|
dependencies = [
|
||||||
|
"adaptive-classifier>=0.0.10",
|
||||||
|
"aiofiles>=24.1.0",
|
||||||
|
"anyio>=3.5.0,<5",
|
||||||
|
"celery>=5.5.1",
|
||||||
|
"certifi==2025.4.26",
|
||||||
|
"chromedriver-autoinstaller>=0.6.4",
|
||||||
|
"colorama>=0.4.6",
|
||||||
|
"distro>=1.7.0,<2",
|
||||||
|
"fake-useragent>=2.1.0",
|
||||||
|
"fastapi>=0.115.12",
|
||||||
|
"flask>=3.1.0",
|
||||||
|
"httpx>=0.27,<0.29",
|
||||||
|
"ipython>=8.13.0",
|
||||||
|
"jiter>=0.4.0,<1",
|
||||||
|
"kokoro==0.9.4",
|
||||||
|
"langid>=1.1.6",
|
||||||
|
"librosa>=0.10.2.post1",
|
||||||
|
"markdownify>=1.1.0",
|
||||||
|
"numpy>=1.24.4",
|
||||||
|
"ollama>=0.4.7",
|
||||||
|
"openai>=1.84.0",
|
||||||
|
"ordered-set>=4.1.0",
|
||||||
|
"playsound3>=1.0.0",
|
||||||
|
"protobuf>=3.20.3",
|
||||||
|
"pyaudio>=0.2.14",
|
||||||
|
"pydantic>=2.10.6",
|
||||||
|
"pydantic-core>=2.27.2",
|
||||||
|
"pypdf>=5.4.0",
|
||||||
|
"pypinyin>=0.54.0",
|
||||||
|
"pyreadline3>=3.5.4",
|
||||||
|
"python-dotenv>=1.0.0",
|
||||||
|
"requests>=2.31.0",
|
||||||
|
"sacremoses>=0.0.53",
|
||||||
|
"scipy>=1.9.3",
|
||||||
|
"selenium>=4.27.1",
|
||||||
|
"selenium-stealth>=1.0.6",
|
||||||
|
"sentencepiece>=0.2.0",
|
||||||
|
"setuptools>=75.6.0",
|
||||||
|
"sniffio>=1.3.1",
|
||||||
|
"soundfile>=0.13.1",
|
||||||
|
"termcolor>=2.4.0",
|
||||||
|
"text2emotion>=0.0.5",
|
||||||
|
"together>=1.5.0",
|
||||||
|
"torch>=2.4.1",
|
||||||
|
"tqdm>4",
|
||||||
|
"transformers>=4.46.3",
|
||||||
|
"undetected-chromedriver>=3.5.5",
|
||||||
|
"uvicorn>=0.34.0",
|
||||||
|
]
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
kokoro==0.9.4
|
|
||||||
certifi==2025.4.26
|
certifi==2025.4.26
|
||||||
fastapi>=0.115.12
|
fastapi>=0.115.12
|
||||||
flask>=3.1.0
|
flask>=3.1.0
|
||||||
@@ -13,14 +12,12 @@ requests>=2.31.0
|
|||||||
numpy>=1.24.4
|
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
|
playsound3>=1.0.0
|
||||||
soundfile>=0.13.1
|
soundfile>=0.13.1
|
||||||
transformers>=4.46.3
|
transformers>=4.46.3
|
||||||
torch>=2.4.1
|
torch>=2.4.1
|
||||||
python-dotenv>=1.0.0
|
|
||||||
ollama>=0.4.7
|
ollama>=0.4.7
|
||||||
scipy>=1.9.3
|
scipy>=1.9.3
|
||||||
soundfile>=0.13.1
|
|
||||||
protobuf>=3.20.3
|
protobuf>=3.20.3
|
||||||
termcolor>=2.4.0
|
termcolor>=2.4.0
|
||||||
pypdf>=5.4.0
|
pypdf>=5.4.0
|
||||||
@@ -41,8 +38,12 @@ fake_useragent>=2.1.0
|
|||||||
selenium_stealth>=1.0.6
|
selenium_stealth>=1.0.6
|
||||||
undetected-chromedriver>=3.5.5
|
undetected-chromedriver>=3.5.5
|
||||||
sentencepiece>=0.2.0
|
sentencepiece>=0.2.0
|
||||||
|
together>=1.5.0
|
||||||
tqdm>4
|
tqdm>4
|
||||||
openai
|
openai
|
||||||
sniffio
|
sniffio
|
||||||
ordered_set
|
ordered_set
|
||||||
pypinyin
|
pypinyin
|
||||||
|
|
||||||
|
# Optional: TTS support (requires Python <3.12)
|
||||||
|
# pip install kokoro==0.9.4 soundfile ipython
|
||||||
|
|||||||
@@ -4,25 +4,18 @@ echo "Starting installation for Linux..."
|
|||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
if ! command -v python3.10 &> /dev/null; then
|
# Check if uv is installed
|
||||||
echo "Error: Python 3.10 is not installed. Please install Python 3.10 and try again."
|
if ! command -v uv &> /dev/null; then
|
||||||
echo "You can install it using: sudo apt-get install python3.10 python3.10-dev python3.10-venv"
|
echo "Error: uv is not installed. Please install uv first."
|
||||||
|
echo "You can install it using: curl -LsSf https://astral.sh/uv/install.sh | sh"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check if pip3.10 is available
|
|
||||||
if ! python3.10 -m pip --version &> /dev/null; then
|
|
||||||
echo "Error: pip for Python 3.10 is not installed. Installing python3.10-pip..."
|
|
||||||
sudo apt-get install -y python3.10-pip || { echo "Failed to install python3.10-pip"; exit 1; }
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Update package list
|
# Update package list
|
||||||
sudo apt-get update || { echo "Failed to update package list"; exit 1; }
|
sudo apt-get update || { echo "Failed to update package list"; exit 1; }
|
||||||
# make sure essential tool are installed
|
# make sure essential tool are installed
|
||||||
sudo apt-get install -y \
|
sudo apt-get install -y \
|
||||||
python3-dev \
|
python3-dev \
|
||||||
python3-pip \
|
|
||||||
python3-wheel \
|
|
||||||
build-essential \
|
build-essential \
|
||||||
alsa-utils \
|
alsa-utils \
|
||||||
portaudio19-dev \
|
portaudio19-dev \
|
||||||
@@ -33,15 +26,29 @@ sudo apt-get install -y \
|
|||||||
libnss3 \
|
libnss3 \
|
||||||
libxss1 || { echo "Failed to install packages"; exit 1; }
|
libxss1 || { echo "Failed to install packages"; exit 1; }
|
||||||
|
|
||||||
# Upgrade pip for Python 3.10
|
# Initialize uv project if pyproject.toml doesn't exist
|
||||||
python3.10 -m pip install --upgrade pip || { echo "Failed to upgrade pip"; exit 1; }
|
if [ ! -f "pyproject.toml" ]; then
|
||||||
# Install and upgrade setuptools and wheel
|
echo "Initializing uv project..."
|
||||||
python3.10 -m pip install --upgrade setuptools wheel || { echo "Failed to install setuptools and wheel"; exit 1; }
|
uv init --python 3.10 || { echo "Failed to initialize uv project"; exit 1; }
|
||||||
# Install Selenium for chromedriver
|
fi
|
||||||
python3.10 -m pip install selenium || { echo "Failed to install selenium"; exit 1; }
|
|
||||||
# Install Python dependencies from requirements.txt
|
# Sync the project (creates venv and installs dependencies)
|
||||||
python3.10 -m pip install -r requirements.txt --no-cache-dir || { echo "Failed to install requirements.txt"; exit 1; }
|
echo "Setting up Python environment with uv..."
|
||||||
|
uv sync --python 3.10 || { echo "Failed to sync uv project"; exit 1; }
|
||||||
|
|
||||||
|
# Add specific packages
|
||||||
|
echo "Adding Selenium..."
|
||||||
|
uv add selenium || { echo "Failed to add selenium"; exit 1; }
|
||||||
|
|
||||||
|
# Add dependencies from requirements.txt if it exists
|
||||||
|
if [ -f "requirements.txt" ]; then
|
||||||
|
echo "Adding dependencies from requirements.txt..."
|
||||||
|
uv add -r requirements.txt || { echo "Failed to add requirements from requirements.txt"; exit 1; }
|
||||||
|
fi
|
||||||
|
|
||||||
# install docker compose
|
# install docker compose
|
||||||
sudo apt install -y docker-compose
|
sudo apt install -y docker-compose
|
||||||
|
|
||||||
echo "Installation complete for Linux!"
|
echo "Installation complete for Linux!"
|
||||||
|
echo "To activate the environment, run: source .venv/bin/activate"
|
||||||
|
echo "Or run commands with: uv run <command>"
|
||||||
@@ -4,18 +4,13 @@ echo "Starting installation for macOS..."
|
|||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
if ! command -v python3.10 &> /dev/null; then
|
# Check if uv is installed
|
||||||
echo "Error: Python 3.10 is not installed. Please install Python 3.10 and try again."
|
if ! command -v uv &> /dev/null; then
|
||||||
echo "You can install it using: sudo apt-get install python3.10 python3.10-dev python3.10-venv"
|
echo "Error: uv is not installed. Please install uv first."
|
||||||
|
echo "You can install it using: curl -LsSf https://astral.sh/uv/install.sh | sh"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check if pip3.10 is available
|
|
||||||
if ! python3.10 -m pip --version &> /dev/null; then
|
|
||||||
echo "Error: pip for Python 3.10 is not installed. Installing python3.10-pip..."
|
|
||||||
sudo apt-get install -y python3.10-pip || { echo "Failed to install python3.10-pip"; exit 1; }
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Check if homebrew is installed
|
# Check if homebrew is installed
|
||||||
if ! command -v brew &> /dev/null; then
|
if ! command -v brew &> /dev/null; then
|
||||||
echo "Homebrew not found. Installing Homebrew..."
|
echo "Homebrew not found. Installing Homebrew..."
|
||||||
@@ -31,13 +26,26 @@ brew install --cask chromedriver
|
|||||||
# Install portaudio for pyAudio using Homebrew
|
# Install portaudio for pyAudio using Homebrew
|
||||||
brew install portaudio
|
brew install portaudio
|
||||||
|
|
||||||
# Upgrade pip for Python 3.10
|
# Initialize uv project if pyproject.toml doesn't exist
|
||||||
python3.10 -m pip install --upgrade pip || { echo "Failed to upgrade pip"; exit 1; }
|
if [ ! -f "pyproject.toml" ]; then
|
||||||
# Install and upgrade setuptools and wheel
|
echo "Initializing uv project..."
|
||||||
python3.10 -m pip install --upgrade setuptools wheel || { echo "Failed to install setuptools and wheel"; exit 1; }
|
uv init --python 3.10 || { echo "Failed to initialize uv project"; exit 1; }
|
||||||
# Install Selenium for chromedriver
|
fi
|
||||||
python3.10 -m pip install selenium || { echo "Failed to install selenium"; exit 1; }
|
|
||||||
# Install Python dependencies from requirements.txt
|
# Sync the project (creates venv and installs dependencies)
|
||||||
python3.10 -m pip install -r requirements.txt --no-cache-dir || { echo "Failed to install requirements.txt"; exit 1; }
|
echo "Setting up Python environment with uv..."
|
||||||
|
uv sync --python 3.10 || { echo "Failed to sync uv project"; exit 1; }
|
||||||
|
|
||||||
|
# Add specific packages
|
||||||
|
echo "Adding Selenium..."
|
||||||
|
uv add selenium || { echo "Failed to add selenium"; exit 1; }
|
||||||
|
|
||||||
|
# Add dependencies from requirements.txt if it exists
|
||||||
|
if [ -f "requirements.txt" ]; then
|
||||||
|
echo "Adding dependencies from requirements.txt..."
|
||||||
|
uv add -r requirements.txt || { echo "Failed to add requirements from requirements.txt"; exit 1; }
|
||||||
|
fi
|
||||||
|
|
||||||
echo "Installation complete for macOS!"
|
echo "Installation complete for macOS!"
|
||||||
|
echo "To activate the environment, run: source .venv/bin/activate"
|
||||||
|
echo "Or run commands with: uv run <command>"
|
||||||
@@ -1,17 +1,67 @@
|
|||||||
@echo off
|
@echo off
|
||||||
echo Starting installation for Windows...
|
echo Starting installation for Windows...
|
||||||
|
|
||||||
REM Install Python dependencies from requirements.txt
|
REM Check if uv is installed
|
||||||
pip install pyreadline3
|
uv --version >nul 2>&1
|
||||||
pip install -r requirements.txt
|
if %errorlevel% neq 0 (
|
||||||
|
echo Error: uv is not installed. Please install uv first.
|
||||||
|
echo You can install it using: powershell -c "irm https://astral.sh/uv/install.ps1 | iex"
|
||||||
|
pause
|
||||||
|
exit /b 2
|
||||||
|
)
|
||||||
|
|
||||||
REM Install Selenium
|
REM Initialize uv project if pyproject.toml doesn't exist
|
||||||
pip install selenium
|
if not exist "pyproject.toml" (
|
||||||
|
echo Initializing uv project...
|
||||||
|
uv init --python 3.10
|
||||||
|
if %errorlevel% neq 0 (
|
||||||
|
echo Failed to initialize uv project
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
REM Sync the project (creates venv and installs dependencies)
|
||||||
|
echo Setting up Python environment with uv...
|
||||||
|
uv sync --python 3.10
|
||||||
|
if %errorlevel% neq 0 (
|
||||||
|
echo Failed to sync uv project
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
REM Add specific packages
|
||||||
|
echo Adding pyreadline3...
|
||||||
|
uv add pyreadline3
|
||||||
|
if %errorlevel% neq 0 (
|
||||||
|
echo Failed to add pyreadline3
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
echo Adding Selenium...
|
||||||
|
uv add selenium
|
||||||
|
if %errorlevel% neq 0 (
|
||||||
|
echo Failed to add selenium
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
REM Add dependencies from requirements.txt if it exists
|
||||||
|
if exist "requirements.txt" (
|
||||||
|
echo Adding dependencies from requirements.txt...
|
||||||
|
uv add -r requirements.txt
|
||||||
|
if %errorlevel% neq 0 (
|
||||||
|
echo Warning: Some packages from requirements.txt failed to install.
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
echo Installation complete for Windows!
|
||||||
|
echo To activate the environment, run: .venv\Scripts\activate
|
||||||
|
echo Or run commands with: uv run ^<command^>
|
||||||
|
echo.
|
||||||
echo Note: pyAudio installation may require additional steps on Windows.
|
echo Note: pyAudio installation may require additional steps on Windows.
|
||||||
echo Please install portaudio manually (e.g., via vcpkg or prebuilt binaries) and then run: pip install pyaudio
|
echo If pyAudio fails to install, please install portaudio manually and try again.
|
||||||
echo Also, download and install chromedriver manually from: https://sites.google.com/chromium.org/driver/getting-started
|
echo Also, chromedriver-autoinstaller should handle chromedriver automatically.
|
||||||
echo Place chromedriver in a directory included in your PATH.
|
echo If needed, download chromedriver manually from: https://sites.google.com/chromium.org/driver/getting-started
|
||||||
|
|
||||||
echo Installation partially complete for Windows. Follow manual steps above.
|
|
||||||
pause
|
pause
|
||||||
@@ -31,6 +31,7 @@ services:
|
|||||||
- SEARXNG_BASE_URL=http://localhost:8080/
|
- SEARXNG_BASE_URL=http://localhost:8080/
|
||||||
- UWSGI_WORKERS=1
|
- UWSGI_WORKERS=1
|
||||||
- UWSGI_THREADS=1
|
- UWSGI_THREADS=1
|
||||||
|
user: "1000:1000" # Run as current user to avoid permission issues
|
||||||
cap_add:
|
cap_add:
|
||||||
- CHOWN
|
- CHOWN
|
||||||
- SETGID
|
- SETGID
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
[real_ip]
|
||||||
|
|
||||||
|
# Number of values to trust for X-Forwarded-For.
|
||||||
|
|
||||||
|
x_for = 1
|
||||||
|
|
||||||
|
# The prefix defines the number of leading bits in an address that are compared
|
||||||
|
# to determine whether or not an address is part of a (client) network.
|
||||||
|
|
||||||
|
ipv4_prefix = 32
|
||||||
|
ipv6_prefix = 48
|
||||||
|
|
||||||
|
[botdetection.ip_limit]
|
||||||
|
|
||||||
|
# To get unlimited access in a local network, by default link-local addresses
|
||||||
|
# (networks) are not monitored by the ip_limit
|
||||||
|
filter_link_local = false
|
||||||
|
|
||||||
|
# activate link_token method in the ip_limit method
|
||||||
|
link_token = false
|
||||||
|
|
||||||
|
[botdetection.ip_lists]
|
||||||
|
|
||||||
|
# In the limiter, the ip_lists method has priority over all other methods -> if
|
||||||
|
# an IP is in the pass_ip list, it has unrestricted access and it is also not
|
||||||
|
# checked if e.g. the "user agent" suggests a bot (e.g. curl).
|
||||||
|
|
||||||
|
block_ip = [
|
||||||
|
# '93.184.216.34', # IPv4 of example.org
|
||||||
|
# '257.1.1.1', # invalid IP --> will be ignored, logged in ERROR class
|
||||||
|
]
|
||||||
|
|
||||||
|
pass_ip = [
|
||||||
|
# '192.168.0.0/16', # IPv4 private network
|
||||||
|
# 'fe80::/10' # IPv6 linklocal / wins over botdetection.ip_limit.filter_link_local
|
||||||
|
]
|
||||||
|
|
||||||
|
# Activate passlist of (hardcoded) IPs from the SearXNG organization,
|
||||||
|
# e.g. `check.searx.space`.
|
||||||
|
pass_searxng_org = true
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
[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 = 4
|
|
||||||
|
|
||||||
# Number of threads per worker
|
|
||||||
# default value: 4 (see Dockerfile)
|
|
||||||
threads = 4
|
|
||||||
|
|
||||||
# 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
|
|
||||||
@@ -444,11 +444,11 @@ engines:
|
|||||||
disabled: true
|
disabled: true
|
||||||
|
|
||||||
# Requires Tor
|
# Requires Tor
|
||||||
- name: ahmia
|
#- name: ahmia
|
||||||
engine: ahmia
|
# engine: ahmia
|
||||||
categories: onions
|
# categories: onions
|
||||||
enable_http: true
|
# enable_http: true
|
||||||
shortcut: ah
|
# shortcut: ah
|
||||||
|
|
||||||
- name: anaconda
|
- name: anaconda
|
||||||
engine: xpath
|
engine: xpath
|
||||||
@@ -641,11 +641,6 @@ engines:
|
|||||||
# # get your API key from: https://core.ac.uk/api-keys/register/
|
# # get your API key from: https://core.ac.uk/api-keys/register/
|
||||||
# api_key: 'unset'
|
# api_key: 'unset'
|
||||||
|
|
||||||
- name: cppreference
|
|
||||||
engine: cppreference
|
|
||||||
shortcut: cpp
|
|
||||||
paging: false
|
|
||||||
disabled: true
|
|
||||||
|
|
||||||
- name: crossref
|
- name: crossref
|
||||||
engine: crossref
|
engine: crossref
|
||||||
@@ -1501,31 +1496,12 @@ engines:
|
|||||||
engine: pinterest
|
engine: pinterest
|
||||||
shortcut: pin
|
shortcut: pin
|
||||||
|
|
||||||
- name: piped
|
#- name: piped
|
||||||
engine: piped
|
# engine: piped
|
||||||
shortcut: ppd
|
# shortcut: ppd
|
||||||
categories: videos
|
# categories: videos
|
||||||
piped_filter: videos
|
# piped_filter: videos
|
||||||
timeout: 3.0
|
# timeout: 3.0
|
||||||
|
|
||||||
# URL to use as link and for embeds
|
|
||||||
frontend_url: https://srv.piped.video
|
|
||||||
# Instance will be selected randomly, for more see https://piped-instances.kavin.rocks/
|
|
||||||
backend_url:
|
|
||||||
- https://pipedapi.adminforge.de
|
|
||||||
- https://pipedapi.nosebs.ru
|
|
||||||
- https://pipedapi.ducks.party
|
|
||||||
- https://pipedapi.reallyaweso.me
|
|
||||||
- https://api.piped.private.coffee
|
|
||||||
- https://pipedapi.darkness.services
|
|
||||||
|
|
||||||
- name: piped.music
|
|
||||||
engine: piped
|
|
||||||
network: piped
|
|
||||||
shortcut: ppdm
|
|
||||||
categories: music
|
|
||||||
piped_filter: music_songs
|
|
||||||
timeout: 3.0
|
|
||||||
|
|
||||||
- name: piratebay
|
- name: piratebay
|
||||||
engine: piratebay
|
engine: piratebay
|
||||||
@@ -1942,20 +1918,6 @@ engines:
|
|||||||
shortcut: tm
|
shortcut: tm
|
||||||
disabled: true
|
disabled: true
|
||||||
|
|
||||||
# Requires Tor
|
|
||||||
- name: torch
|
|
||||||
engine: xpath
|
|
||||||
paging: true
|
|
||||||
search_url:
|
|
||||||
http://xmh57jrknzkhv6y3ls3ubitzfqnkrwxhopf5aygthi7d6rplyvk3noyd.onion/cgi-bin/omega/omega?P={query}&DEFAULTOP=and
|
|
||||||
results_xpath: //table//tr
|
|
||||||
url_xpath: ./td[2]/a
|
|
||||||
title_xpath: ./td[2]/b
|
|
||||||
content_xpath: ./td[2]/small
|
|
||||||
categories: onions
|
|
||||||
enable_http: true
|
|
||||||
shortcut: tch
|
|
||||||
|
|
||||||
# torznab engine lets you query any torznab compatible indexer. Using this
|
# torznab engine lets you query any torznab compatible indexer. Using this
|
||||||
# engine in combination with Jackett opens the possibility to query a lot of
|
# engine in combination with Jackett opens the possibility to query a lot of
|
||||||
# public and private indexers directly from SearXNG. More details at:
|
# public and private indexers directly from SearXNG. More details at:
|
||||||
@@ -2155,33 +2117,33 @@ engines:
|
|||||||
website: https://www.wikivoyage.org/
|
website: https://www.wikivoyage.org/
|
||||||
wikidata_id: Q373
|
wikidata_id: Q373
|
||||||
|
|
||||||
- name: wikicommons.images
|
# - name: wikicommons.images
|
||||||
engine: wikicommons
|
# engine: wikicommons
|
||||||
shortcut: wc
|
# shortcut: wc
|
||||||
categories: images
|
# categories: images
|
||||||
search_type: images
|
# search_type: images
|
||||||
number_of_results: 10
|
# number_of_results: 10
|
||||||
|
#
|
||||||
- name: wikicommons.videos
|
# - name: wikicommons.videos
|
||||||
engine: wikicommons
|
# engine: wikicommons
|
||||||
shortcut: wcv
|
# shortcut: wcv
|
||||||
categories: videos
|
# categories: videos
|
||||||
search_type: videos
|
# search_type: videos
|
||||||
number_of_results: 10
|
# number_of_results: 10
|
||||||
|
#
|
||||||
- name: wikicommons.audio
|
# - name: wikicommons.audio
|
||||||
engine: wikicommons
|
# engine: wikicommons
|
||||||
shortcut: wca
|
# shortcut: wca
|
||||||
categories: music
|
# categories: music
|
||||||
search_type: audio
|
# search_type: audio
|
||||||
number_of_results: 10
|
# number_of_results: 10
|
||||||
|
#
|
||||||
- name: wikicommons.files
|
# - name: wikicommons.files
|
||||||
engine: wikicommons
|
# engine: wikicommons
|
||||||
shortcut: wcf
|
# shortcut: wcf
|
||||||
categories: files
|
# categories: files
|
||||||
search_type: files
|
# search_type: files
|
||||||
number_of_results: 10
|
# number_of_results: 10
|
||||||
|
|
||||||
- name: wolframalpha
|
- name: wolframalpha
|
||||||
shortcut: wa
|
shortcut: wa
|
||||||
@@ -2358,16 +2320,6 @@ engines:
|
|||||||
# timeout can be reduced in 'local' search mode
|
# timeout can be reduced in 'local' search mode
|
||||||
timeout: 5.0
|
timeout: 5.0
|
||||||
|
|
||||||
- name: yacy images
|
|
||||||
engine: yacy
|
|
||||||
network: yacy
|
|
||||||
categories: images
|
|
||||||
search_type: image
|
|
||||||
shortcut: yai
|
|
||||||
disabled: true
|
|
||||||
# timeout can be reduced in 'local' search mode
|
|
||||||
timeout: 5.0
|
|
||||||
|
|
||||||
- name: rumble
|
- name: rumble
|
||||||
engine: rumble
|
engine: rumble
|
||||||
shortcut: ru
|
shortcut: ru
|
||||||
@@ -2481,11 +2433,6 @@ engines:
|
|||||||
shortcut: wttr
|
shortcut: wttr
|
||||||
timeout: 9.0
|
timeout: 9.0
|
||||||
|
|
||||||
- name: yummly
|
|
||||||
engine: yummly
|
|
||||||
shortcut: yum
|
|
||||||
disabled: true
|
|
||||||
|
|
||||||
- name: brave
|
- name: brave
|
||||||
engine: brave
|
engine: brave
|
||||||
shortcut: br
|
shortcut: br
|
||||||
|
|||||||
@@ -5,12 +5,12 @@ gid = searxng
|
|||||||
|
|
||||||
# Number of workers (usually CPU count)
|
# Number of workers (usually CPU count)
|
||||||
# default value: %k (= number of CPU core, see Dockerfile)
|
# default value: %k (= number of CPU core, see Dockerfile)
|
||||||
workers = 1
|
workers = 4
|
||||||
|
|
||||||
# Number of threads per worker
|
# Number of threads per worker
|
||||||
# default value: 4 (see Dockerfile)
|
# default value: 4 (see Dockerfile)
|
||||||
enable-threads = true
|
enable-threads = 4
|
||||||
threads = 1
|
threads = 4
|
||||||
|
|
||||||
# The right granted on the created socket
|
# The right granted on the created socket
|
||||||
chmod-socket = 666
|
chmod-socket = 666
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ class BrowserAgent(Agent):
|
|||||||
self.memory = Memory(self.load_prompt(prompt_path),
|
self.memory = Memory(self.load_prompt(prompt_path),
|
||||||
recover_last_session=False, # session recovery in handled by the interaction class
|
recover_last_session=False, # session recovery in handled by the interaction class
|
||||||
memory_compression=False,
|
memory_compression=False,
|
||||||
model_provider=provider.get_model_name())
|
model_provider=provider.get_model_name() if provider else None)
|
||||||
|
|
||||||
def get_today_date(self) -> str:
|
def get_today_date(self) -> str:
|
||||||
"""Get the date"""
|
"""Get the date"""
|
||||||
@@ -77,14 +77,14 @@ class BrowserAgent(Agent):
|
|||||||
def get_unvisited_links(self) -> List[str]:
|
def get_unvisited_links(self) -> List[str]:
|
||||||
return "\n".join([f"[{i}] {link}" for i, link in enumerate(self.navigable_links) if link not in self.search_history])
|
return "\n".join([f"[{i}] {link}" for i, link in enumerate(self.navigable_links) if link not in self.search_history])
|
||||||
|
|
||||||
def make_newsearch_prompt(self, user_prompt: str, search_result: dict) -> str:
|
def make_newsearch_prompt(self, prompt: str, search_result: dict) -> str:
|
||||||
search_choice = self.stringify_search_results(search_result)
|
search_choice = self.stringify_search_results(search_result)
|
||||||
self.logger.info(f"Search results: {search_choice}")
|
self.logger.info(f"Search results: {search_choice}")
|
||||||
return f"""
|
return f"""
|
||||||
Based on the search result:
|
Based on the search result:
|
||||||
{search_choice}
|
{search_choice}
|
||||||
Your goal is to find accurate and complete information to satisfy the user’s request.
|
Your goal is to find accurate and complete information to satisfy the user’s request.
|
||||||
User request: {user_prompt}
|
User request: {prompt}
|
||||||
To proceed, choose a relevant link from the search results. Announce your choice by saying: "I will navigate to <link>"
|
To proceed, choose a relevant link from the search results. Announce your choice by saying: "I will navigate to <link>"
|
||||||
Do not explain your choice.
|
Do not explain your choice.
|
||||||
"""
|
"""
|
||||||
@@ -235,13 +235,17 @@ class BrowserAgent(Agent):
|
|||||||
return links
|
return links
|
||||||
|
|
||||||
def select_link(self, links: List[str]) -> str | None:
|
def select_link(self, links: List[str]) -> str | None:
|
||||||
|
"""
|
||||||
|
Select the first unvisited link that is not the current page.
|
||||||
|
Preference is given to links not in search_history.
|
||||||
|
"""
|
||||||
for lk in links:
|
for lk in links:
|
||||||
if lk == self.current_page:
|
if lk == self.current_page or lk in self.search_history:
|
||||||
self.logger.info(f"Already visited {lk}. Skipping.")
|
self.logger.info(f"Skipping already visited or current link: {lk}")
|
||||||
continue
|
continue
|
||||||
self.logger.info(f"Selected link: {lk}")
|
self.logger.info(f"Selected link: {lk}")
|
||||||
return lk
|
return lk
|
||||||
self.logger.warning("No link selected.")
|
self.logger.warning("No suitable link selected.")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def get_page_text(self, limit_to_model_ctx = False) -> str:
|
def get_page_text(self, limit_to_model_ctx = False) -> str:
|
||||||
@@ -396,7 +400,10 @@ class BrowserAgent(Agent):
|
|||||||
if (link == None and len(extracted_form) < 3) or Action.GO_BACK.value in answer or link in self.search_history:
|
if (link == None and len(extracted_form) < 3) or Action.GO_BACK.value in answer or link in self.search_history:
|
||||||
pretty_print(f"Going back to results. Still {len(unvisited)}", color="status")
|
pretty_print(f"Going back to results. Still {len(unvisited)}", color="status")
|
||||||
self.status_message = "Going back to search results..."
|
self.status_message = "Going back to search results..."
|
||||||
prompt = self.make_newsearch_prompt(user_prompt, unvisited)
|
request_prompt = user_prompt
|
||||||
|
if link is None:
|
||||||
|
request_prompt += f"\nYou previously choosen:\n{self.last_answer} but the website is unavailable. Consider other options."
|
||||||
|
prompt = self.make_newsearch_prompt(request_prompt, unvisited)
|
||||||
self.search_history.append(link)
|
self.search_history.append(link)
|
||||||
self.current_page = link
|
self.current_page = link
|
||||||
continue
|
continue
|
||||||
|
|||||||
@@ -76,7 +76,12 @@ class PlannerAgent(Agent):
|
|||||||
if blocks == None:
|
if blocks == None:
|
||||||
return []
|
return []
|
||||||
for block in blocks:
|
for block in blocks:
|
||||||
|
try:
|
||||||
line_json = json.loads(block)
|
line_json = json.loads(block)
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
self.logger.warning(f"Failed to parse JSON block: {e}")
|
||||||
|
pretty_print(f"JSON parsing error: {e}", color="warning")
|
||||||
|
return []
|
||||||
if 'plan' in line_json:
|
if 'plan' in line_json:
|
||||||
for task in line_json['plan']:
|
for task in line_json['plan']:
|
||||||
if task['agent'].lower() not in [ag_name.lower() for ag_name in self.agents.keys()]:
|
if task['agent'].lower() not in [ag_name.lower() for ag_name in self.agents.keys()]:
|
||||||
@@ -142,17 +147,23 @@ 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")
|
||||||
|
|
||||||
async def make_plan(self, prompt: str) -> str:
|
async def make_plan(self, prompt: str, max_retries: int = 4) -> str:
|
||||||
"""
|
"""
|
||||||
Asks the LLM to make a plan.
|
Asks the LLM to make a plan.
|
||||||
Args:
|
Args:
|
||||||
prompt (str): The prompt to be sent to the LLM.
|
prompt (str): The prompt to be sent to the LLM.
|
||||||
|
max_retries (int): Maximum number of retries before giving up.
|
||||||
Returns:
|
Returns:
|
||||||
str: The plan made by the LLM.
|
str: The plan made by the LLM.
|
||||||
"""
|
"""
|
||||||
ok = False
|
ok = False
|
||||||
answer = None
|
answer = None
|
||||||
|
retries = 0
|
||||||
while not ok:
|
while not ok:
|
||||||
|
if retries >= max_retries:
|
||||||
|
pretty_print(f"Failed to make a plan after {max_retries} attempts. Giving up.", color="failure")
|
||||||
|
self.logger.warning(f"make_plan exceeded max retries ({max_retries}).")
|
||||||
|
return []
|
||||||
animate_thinking("Thinking...", color="status")
|
animate_thinking("Thinking...", color="status")
|
||||||
self.memory.push('user', prompt)
|
self.memory.push('user', prompt)
|
||||||
answer, reasoning = await self.llm_request()
|
answer, reasoning = await self.llm_request()
|
||||||
@@ -163,6 +174,7 @@ class PlannerAgent(Agent):
|
|||||||
self.show_plan(agents_tasks, answer)
|
self.show_plan(agents_tasks, answer)
|
||||||
prompt = f"Failed to parse the tasks. Please write down your task followed by a json plan within ```json. Do not ask for clarification.\n"
|
prompt = f"Failed to parse the tasks. Please write down your task followed by a json plan within ```json. Do not ask for clarification.\n"
|
||||||
pretty_print("Failed to make plan. Retrying...", color="warning")
|
pretty_print("Failed to make plan. Retrying...", color="warning")
|
||||||
|
retries += 1
|
||||||
continue
|
continue
|
||||||
self.show_plan(agents_tasks, answer)
|
self.show_plan(agents_tasks, answer)
|
||||||
ok = True
|
ok = True
|
||||||
@@ -200,7 +212,7 @@ class PlannerAgent(Agent):
|
|||||||
{last_agent_work}
|
{last_agent_work}
|
||||||
Agent {id} work was a {tool_success_str} according to system interpreter.
|
Agent {id} work was a {tool_success_str} according to system interpreter.
|
||||||
{next_task}
|
{next_task}
|
||||||
Is the work done for task {id} leading to sucess or failure ? Did an agent fail with a task?
|
Is the work done for task {id} leading to success or failure ? Did an agent fail with a task?
|
||||||
If agent work was good: answer "NO_UPDATE"
|
If agent work was good: answer "NO_UPDATE"
|
||||||
If agent work is leading to failure: update the plan.
|
If agent work is leading to failure: update the plan.
|
||||||
If a task failed add a task to try again or recover from failure. You might have near identical task twice.
|
If a task failed add a task to try again or recover from failure. You might have near identical task twice.
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ from selenium import webdriver
|
|||||||
from selenium.webdriver.chrome.service import Service
|
from selenium.webdriver.chrome.service import Service
|
||||||
from selenium.webdriver.chrome.options import Options
|
from selenium.webdriver.chrome.options import Options
|
||||||
from selenium.webdriver.common.by import By
|
from selenium.webdriver.common.by import By
|
||||||
from selenium.webdriver.support.ui import WebDriverWait
|
from selenium.webdriver.support.ui import WebDriverWait, Select
|
||||||
from selenium.webdriver.support import expected_conditions as EC
|
from selenium.webdriver.support import expected_conditions as EC
|
||||||
from selenium.common.exceptions import TimeoutException, WebDriverException
|
from selenium.common.exceptions import TimeoutException, WebDriverException
|
||||||
from selenium.webdriver.common.action_chains import ActionChains
|
from selenium.webdriver.common.action_chains import ActionChains
|
||||||
@@ -15,10 +15,13 @@ import undetected_chromedriver as uc
|
|||||||
import chromedriver_autoinstaller
|
import chromedriver_autoinstaller
|
||||||
import certifi
|
import certifi
|
||||||
import ssl
|
import ssl
|
||||||
|
import subprocess
|
||||||
import time
|
import time
|
||||||
import random
|
import random
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
|
import uuid
|
||||||
|
import socket
|
||||||
import tempfile
|
import tempfile
|
||||||
import markdownify
|
import markdownify
|
||||||
import sys
|
import sys
|
||||||
@@ -42,7 +45,14 @@ 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", "/opt/chrome/chrome", "/usr/local/bin/chrome"]
|
paths = ["/usr/bin/google-chrome",
|
||||||
|
"/opt/chrome/chrome",
|
||||||
|
"/usr/bin/chromium-browser",
|
||||||
|
"/usr/bin/chromium",
|
||||||
|
"/usr/local/bin/chrome",
|
||||||
|
"/opt/google/chrome/chrome-headless-shell",
|
||||||
|
#"/app/chrome_bundle/chrome136/chrome-linux64"
|
||||||
|
]
|
||||||
|
|
||||||
for path in paths:
|
for path in paths:
|
||||||
if os.path.exists(path) and os.access(path, os.X_OK):
|
if os.path.exists(path) and os.access(path, os.X_OK):
|
||||||
@@ -68,13 +78,62 @@ def get_random_user_agent() -> str:
|
|||||||
]
|
]
|
||||||
return random.choice(user_agents)
|
return random.choice(user_agents)
|
||||||
|
|
||||||
|
def get_chromedriver_version(chromedriver_path: str) -> str:
|
||||||
|
"""Get the major version of a chromedriver binary. Returns empty string on failure."""
|
||||||
|
try:
|
||||||
|
result = subprocess.run(
|
||||||
|
[chromedriver_path, "--version"],
|
||||||
|
capture_output=True, text=True, timeout=10
|
||||||
|
)
|
||||||
|
# Output format: "ChromeDriver 125.0.6422.78 (...)"
|
||||||
|
return result.stdout.strip().split()[1].split('.')[0]
|
||||||
|
except Exception:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
def is_chromedriver_compatible(chromedriver_path: str) -> bool:
|
||||||
|
"""Check if a chromedriver binary is compatible with the installed Chrome version."""
|
||||||
|
try:
|
||||||
|
chrome_version = chromedriver_autoinstaller.get_chrome_version()
|
||||||
|
if not chrome_version:
|
||||||
|
return True # Can't determine Chrome version, assume compatible
|
||||||
|
chrome_major = chrome_version.split('.')[0]
|
||||||
|
driver_major = get_chromedriver_version(chromedriver_path)
|
||||||
|
if not driver_major:
|
||||||
|
return True # Can't determine driver version, assume compatible
|
||||||
|
return chrome_major == driver_major
|
||||||
|
except Exception:
|
||||||
|
return True # On any error, assume compatible to avoid blocking
|
||||||
|
|
||||||
def install_chromedriver() -> str:
|
def install_chromedriver() -> str:
|
||||||
"""
|
"""
|
||||||
Install the ChromeDriver if not already installed. Return the path.
|
Install the ChromeDriver if not already installed. Return the path.
|
||||||
|
Automatically updates the driver if the version does not match the installed Chrome.
|
||||||
"""
|
"""
|
||||||
|
# First try to use chromedriver in the project root directory (as per README)
|
||||||
|
project_root_chromedriver = "./chromedriver"
|
||||||
|
if os.path.exists(project_root_chromedriver) and os.access(project_root_chromedriver, os.X_OK):
|
||||||
|
if is_chromedriver_compatible(project_root_chromedriver):
|
||||||
|
print(f"Using ChromeDriver from project root: {project_root_chromedriver}")
|
||||||
|
return project_root_chromedriver
|
||||||
|
print("ChromeDriver in project root is outdated, attempting auto-update...")
|
||||||
|
|
||||||
|
# Then try to use the system-installed chromedriver
|
||||||
chromedriver_path = shutil.which("chromedriver")
|
chromedriver_path = shutil.which("chromedriver")
|
||||||
if not chromedriver_path:
|
if chromedriver_path:
|
||||||
|
if is_chromedriver_compatible(chromedriver_path):
|
||||||
|
return chromedriver_path
|
||||||
|
print(f"System ChromeDriver at {chromedriver_path} is outdated, attempting auto-update...")
|
||||||
|
|
||||||
|
# In Docker environment, try the fixed path
|
||||||
|
if os.path.exists('/.dockerenv'):
|
||||||
|
docker_chromedriver_path = "/usr/local/bin/chromedriver"
|
||||||
|
if os.path.exists(docker_chromedriver_path) and os.access(docker_chromedriver_path, os.X_OK):
|
||||||
|
print(f"Using Docker ChromeDriver at {docker_chromedriver_path}")
|
||||||
|
return docker_chromedriver_path
|
||||||
|
|
||||||
|
# Auto-install matching ChromeDriver version
|
||||||
try:
|
try:
|
||||||
|
print("Installing matching ChromeDriver version automatically...")
|
||||||
chromedriver_path = chromedriver_autoinstaller.install()
|
chromedriver_path = chromedriver_autoinstaller.install()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise FileNotFoundError(
|
raise FileNotFoundError(
|
||||||
@@ -83,6 +142,7 @@ def install_chromedriver() -> str:
|
|||||||
"and ensure it's in your PATH or specify the path directly."
|
"and ensure it's in your PATH or specify the path directly."
|
||||||
"See know issues in readme if your chrome version is above 115."
|
"See know issues in readme if your chrome version is above 115."
|
||||||
) from e
|
) from e
|
||||||
|
|
||||||
if not chromedriver_path:
|
if not chromedriver_path:
|
||||||
raise FileNotFoundError("ChromeDriver not found. Please install it or add it to your PATH.")
|
raise FileNotFoundError("ChromeDriver not found. Please install it or add it to your PATH.")
|
||||||
return chromedriver_path
|
return chromedriver_path
|
||||||
@@ -94,24 +154,14 @@ def bypass_ssl() -> str:
|
|||||||
pretty_print("Bypassing SSL verification issues, we strongly advice you update your certifi SSL certificate.", color="warning")
|
pretty_print("Bypassing SSL verification issues, we strongly advice you update your certifi SSL certificate.", color="warning")
|
||||||
ssl._create_default_https_context = ssl._create_unverified_context
|
ssl._create_default_https_context = ssl._create_unverified_context
|
||||||
|
|
||||||
def create_undetected_chromedriver(service, chrome_options) -> webdriver.Chrome:
|
def get_free_port() -> int:
|
||||||
"""Create an undetected ChromeDriver instance."""
|
"""Find and return a free TCP port on the local machine."""
|
||||||
try:
|
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
||||||
driver = uc.Chrome(service=service, options=chrome_options)
|
s.bind(('', 0))
|
||||||
except Exception as e:
|
return s.getsockname()[1]
|
||||||
pretty_print(f"Failed to create Chrome driver: {str(e)}. Trying to bypass SSL...", color="failure")
|
|
||||||
try:
|
|
||||||
bypass_ssl()
|
|
||||||
driver = uc.Chrome(service=service, options=chrome_options)
|
|
||||||
except Exception as e:
|
|
||||||
pretty_print(f"Failed to create Chrome driver, fallback failed:\n{str(e)}.", color="failure")
|
|
||||||
raise e
|
|
||||||
raise e
|
|
||||||
driver.execute_script("Object.defineProperty(navigator, 'webdriver', {get: () => undefined})")
|
|
||||||
return driver
|
|
||||||
|
|
||||||
def create_driver(headless=False, stealth_mode=True, crx_path="./crx/nopecha.crx", lang="en") -> webdriver.Chrome:
|
def create_chrome_options(headless=False, stealth_mode=True, crx_path="./crx/nopecha.crx", lang="en") -> Options:
|
||||||
"""Create a Chrome WebDriver with specified options."""
|
"""Create Chrome options - separated for reusability."""
|
||||||
chrome_options = Options()
|
chrome_options = Options()
|
||||||
chrome_path = get_chrome_path()
|
chrome_path = get_chrome_path()
|
||||||
|
|
||||||
@@ -120,17 +170,28 @@ def create_driver(headless=False, stealth_mode=True, crx_path="./crx/nopecha.crx
|
|||||||
chrome_options.binary_location = chrome_path
|
chrome_options.binary_location = chrome_path
|
||||||
|
|
||||||
if headless:
|
if headless:
|
||||||
chrome_options.add_argument("--headless")
|
chrome_options.add_argument("--headless=new")
|
||||||
chrome_options.add_argument("--disable-gpu")
|
chrome_options.add_argument("--disable-gpu")
|
||||||
chrome_options.add_argument("--disable-webgl")
|
chrome_options.add_argument("--disable-webgl")
|
||||||
user_data_dir = tempfile.mkdtemp()
|
|
||||||
user_agent = get_random_user_agent()
|
user_agent = get_random_user_agent()
|
||||||
width, height = (1920, 1080)
|
width, height = (1920, 1080)
|
||||||
chrome_options.add_argument(f"--user-data-dir={user_data_dir}")
|
profile_dir = f"/tmp/chrome_profile_{uuid.uuid4().hex[:8]}"
|
||||||
chrome_options.add_argument(f"--accept-lang={lang}-{lang.upper()},{lang};q=0.9")
|
|
||||||
chrome_options.add_argument("--timezone=Europe/Paris")
|
# Core options
|
||||||
chrome_options.add_argument("--no-sandbox")
|
chrome_options.add_argument("--no-sandbox")
|
||||||
chrome_options.add_argument("--disable-dev-shm-usage")
|
chrome_options.add_argument('--disable-dev-shm-usage')
|
||||||
|
chrome_options.add_argument(f'--user-data-dir={profile_dir}')
|
||||||
|
chrome_options.add_argument(f"--accept-lang={lang}-{lang.upper()},{lang};q=0.9")
|
||||||
|
chrome_options.add_argument("--disable-extensions")
|
||||||
|
chrome_options.add_argument("--disable-background-timer-throttling")
|
||||||
|
chrome_options.add_argument("--timezone=Europe/Paris")
|
||||||
|
chrome_options.add_argument(f'--remote-debugging-port={get_free_port()}')
|
||||||
|
chrome_options.add_argument('--disable-background-timer-throttling')
|
||||||
|
chrome_options.add_argument('--disable-backgrounding-occluded-windows')
|
||||||
|
chrome_options.add_argument('--disable-renderer-backgrounding')
|
||||||
|
chrome_options.add_argument('--disable-features=TranslateUI')
|
||||||
|
chrome_options.add_argument('--disable-ipc-flooding-protection')
|
||||||
chrome_options.add_argument("--mute-audio")
|
chrome_options.add_argument("--mute-audio")
|
||||||
chrome_options.add_argument("--disable-notifications")
|
chrome_options.add_argument("--disable-notifications")
|
||||||
chrome_options.add_argument("--autoplay-policy=user-gesture-required")
|
chrome_options.add_argument("--autoplay-policy=user-gesture-required")
|
||||||
@@ -139,28 +200,14 @@ def create_driver(headless=False, stealth_mode=True, crx_path="./crx/nopecha.crx
|
|||||||
chrome_options.add_argument("--disable-blink-features=AutomationControlled")
|
chrome_options.add_argument("--disable-blink-features=AutomationControlled")
|
||||||
chrome_options.add_argument(f'user-agent={user_agent["ua"]}')
|
chrome_options.add_argument(f'user-agent={user_agent["ua"]}')
|
||||||
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:
|
||||||
if not os.path.exists(crx_path):
|
if not os.path.exists(crx_path):
|
||||||
pretty_print(f"Anti-captcha CRX not found at {crx_path}.", color="failure")
|
pretty_print(f"Anti-captcha CRX not found at {crx_path}.", color="failure")
|
||||||
else:
|
else:
|
||||||
chrome_options.add_extension(crx_path)
|
chrome_options.add_extension(crx_path)
|
||||||
|
|
||||||
chromedriver_path = install_chromedriver()
|
if not stealth_mode:
|
||||||
|
|
||||||
service = Service(chromedriver_path)
|
|
||||||
if stealth_mode:
|
|
||||||
chrome_options.add_argument("--disable-blink-features=AutomationControlled")
|
|
||||||
driver = create_undetected_chromedriver(service, chrome_options)
|
|
||||||
chrome_version = driver.capabilities['browserVersion']
|
|
||||||
stealth(driver,
|
|
||||||
languages=["en-US", "en"],
|
|
||||||
vendor=user_agent["vendor"],
|
|
||||||
platform="Win64" if "windows" in user_agent["ua"].lower() else "MacIntel" if "mac" in user_agent["ua"].lower() else "Linux x86_64",
|
|
||||||
webgl_vendor="Intel Inc.",
|
|
||||||
renderer="Intel Iris OpenGL Engine",
|
|
||||||
fix_hairline=True,
|
|
||||||
)
|
|
||||||
return driver
|
|
||||||
security_prefs = {
|
security_prefs = {
|
||||||
"profile.default_content_setting_values.geolocation": 0,
|
"profile.default_content_setting_values.geolocation": 0,
|
||||||
"profile.default_content_setting_values.notifications": 0,
|
"profile.default_content_setting_values.notifications": 0,
|
||||||
@@ -184,6 +231,55 @@ def create_driver(headless=False, stealth_mode=True, crx_path="./crx/nopecha.crx
|
|||||||
chrome_options.add_experimental_option("prefs", security_prefs)
|
chrome_options.add_experimental_option("prefs", security_prefs)
|
||||||
chrome_options.add_experimental_option("excludeSwitches", ["enable-automation"])
|
chrome_options.add_experimental_option("excludeSwitches", ["enable-automation"])
|
||||||
chrome_options.add_experimental_option('useAutomationExtension', False)
|
chrome_options.add_experimental_option('useAutomationExtension', False)
|
||||||
|
|
||||||
|
return chrome_options
|
||||||
|
|
||||||
|
def create_undetected_chromedriver(service, chrome_options) -> webdriver.Chrome:
|
||||||
|
"""Create an undetected ChromeDriver instance with proper error handling."""
|
||||||
|
try:
|
||||||
|
driver = uc.Chrome(service=service, options=chrome_options)
|
||||||
|
except Exception as e:
|
||||||
|
pretty_print(f"Failed to create Chrome driver: {str(e)}. Trying to bypass SSL...", color="failure")
|
||||||
|
try:
|
||||||
|
bypass_ssl()
|
||||||
|
# Create NEW options object - this is the key fix
|
||||||
|
fresh_options = create_chrome_options(
|
||||||
|
headless=any("--headless" in arg for arg in chrome_options.arguments),
|
||||||
|
stealth_mode=True, # We're in stealth mode if we reach this point
|
||||||
|
crx_path="./crx/nopecha.crx" # Default path
|
||||||
|
)
|
||||||
|
driver = uc.Chrome(service=service, options=fresh_options)
|
||||||
|
except Exception as e:
|
||||||
|
pretty_print(f"Failed to create Chrome driver, fallback failed:\n{str(e)}.", color="failure")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
driver.execute_script("Object.defineProperty(navigator, 'webdriver', {get: () => undefined})")
|
||||||
|
return driver
|
||||||
|
|
||||||
|
def create_driver(headless=False, stealth_mode=True, crx_path="./crx/nopecha.crx", lang="en") -> webdriver.Chrome:
|
||||||
|
"""Create a Chrome WebDriver with specified options."""
|
||||||
|
# Warn if trying to run non-headless in Docker
|
||||||
|
if not headless and os.path.exists('/.dockerenv'):
|
||||||
|
print("[WARNING] Running non-headless browser in Docker may fail!")
|
||||||
|
print("[WARNING] Consider setting headless=True or headless_browser=True in config.ini")
|
||||||
|
|
||||||
|
chrome_options = create_chrome_options(headless, stealth_mode, crx_path, lang)
|
||||||
|
chromedriver_path = install_chromedriver()
|
||||||
|
service = Service(chromedriver_path)
|
||||||
|
|
||||||
|
if stealth_mode:
|
||||||
|
driver = create_undetected_chromedriver(service, chrome_options)
|
||||||
|
user_agent = get_random_user_agent()
|
||||||
|
stealth(driver,
|
||||||
|
languages=["en-US", "en"],
|
||||||
|
vendor=user_agent["vendor"],
|
||||||
|
platform="Win64" if "windows" in user_agent["ua"].lower() else "MacIntel" if "mac" in user_agent["ua"].lower() else "Linux x86_64",
|
||||||
|
webgl_vendor="Intel Inc.",
|
||||||
|
renderer="Intel Iris OpenGL Engine",
|
||||||
|
fix_hairline=True,
|
||||||
|
)
|
||||||
|
return driver
|
||||||
|
else:
|
||||||
return webdriver.Chrome(service=service, options=chrome_options)
|
return webdriver.Chrome(service=service, options=chrome_options)
|
||||||
|
|
||||||
class Browser:
|
class Browser:
|
||||||
@@ -441,7 +537,16 @@ class Browser:
|
|||||||
if input_type in ["hidden", "submit", "button", "image"] or not element["displayed"]:
|
if input_type in ["hidden", "submit", "button", "image"] or not element["displayed"]:
|
||||||
continue
|
continue
|
||||||
input_name = element.get("text") or element.get("id") or input_type
|
input_name = element.get("text") or element.get("id") or input_type
|
||||||
if input_type == "checkbox" or input_type == "radio":
|
if input_type == "select":
|
||||||
|
options = element.get("options", [])
|
||||||
|
options_str = ", ".join(opt["text"] for opt in options if opt["text"])
|
||||||
|
selected = next((opt["text"] for opt in options if opt.get("selected")), "")
|
||||||
|
form_strings.append(f"[{input_name}](select: {selected}) options: [{options_str}]")
|
||||||
|
elif input_type == "textarea":
|
||||||
|
form_strings.append(f"[{input_name}]("")")
|
||||||
|
elif input_type == "file":
|
||||||
|
form_strings.append(f"[{input_name}](file: )")
|
||||||
|
elif input_type == "checkbox" or input_type == "radio":
|
||||||
try:
|
try:
|
||||||
checked_status = "checked" if element.is_selected() else "unchecked"
|
checked_status = "checked" if element.is_selected() else "unchecked"
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -605,7 +710,29 @@ class Browser:
|
|||||||
self.logger.warning(f"Element '{name}' is not interactable (not displayed or disabled)")
|
self.logger.warning(f"Element '{name}' is not interactable (not displayed or disabled)")
|
||||||
continue
|
continue
|
||||||
input_type = (element.get_attribute("type") or "text").lower()
|
input_type = (element.get_attribute("type") or "text").lower()
|
||||||
if input_type in ["checkbox", "radio"]:
|
tag_name = (element.tag_name or "").lower()
|
||||||
|
if tag_name == "select":
|
||||||
|
try:
|
||||||
|
select = Select(element)
|
||||||
|
select.select_by_visible_text(value)
|
||||||
|
self.logger.info(f"Selected '{value}' for {name}")
|
||||||
|
except Exception:
|
||||||
|
try:
|
||||||
|
select.select_by_value(value)
|
||||||
|
self.logger.info(f"Selected value '{value}' for {name}")
|
||||||
|
except Exception as sel_e:
|
||||||
|
self.logger.warning(f"Could not select '{value}' for {name}: {sel_e}")
|
||||||
|
elif tag_name == "textarea":
|
||||||
|
element.clear()
|
||||||
|
element.send_keys(value)
|
||||||
|
self.logger.info(f"Filled textarea {name}")
|
||||||
|
elif input_type == "file":
|
||||||
|
if os.path.isabs(value) and os.path.exists(value):
|
||||||
|
element.send_keys(value)
|
||||||
|
self.logger.info(f"Uploaded file '{value}' for {name}")
|
||||||
|
else:
|
||||||
|
self.logger.warning(f"File not found: {value}")
|
||||||
|
elif input_type in ["checkbox", "radio"]:
|
||||||
is_checked = element.is_selected()
|
is_checked = element.is_selected()
|
||||||
should_be_checked = value.lower() == "checked"
|
should_be_checked = value.lower() == "checked"
|
||||||
|
|
||||||
@@ -698,8 +825,6 @@ if __name__ == "__main__":
|
|||||||
|
|
||||||
input("press enter to continue")
|
input("press enter to continue")
|
||||||
print("AntiCaptcha / Form Test")
|
print("AntiCaptcha / Form Test")
|
||||||
browser.go_to("https://www.google.com/recaptcha/api2/demo")
|
|
||||||
time.sleep(50)
|
|
||||||
browser.go_to("https://bot.sannysoft.com")
|
browser.go_to("https://bot.sannysoft.com")
|
||||||
time.sleep(5)
|
time.sleep(5)
|
||||||
#txt = browser.get_text()
|
#txt = browser.get_text()
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
from typing import List, Tuple, Type, Dict
|
from typing import List, Tuple, Type, Dict
|
||||||
import re
|
import re
|
||||||
import langid
|
import langid
|
||||||
import nltk
|
|
||||||
from nltk.sentiment.vader import SentimentIntensityAnalyzer
|
|
||||||
from transformers import MarianMTModel, MarianTokenizer
|
from transformers import MarianMTModel, MarianTokenizer
|
||||||
|
|
||||||
from sources.utility import pretty_print, animate_thinking
|
from sources.utility import pretty_print, animate_thinking
|
||||||
@@ -16,7 +14,6 @@ class LanguageUtility:
|
|||||||
args:
|
args:
|
||||||
supported_language: list of languages for translation, determine which Helsinki-NLP model to load
|
supported_language: list of languages for translation, determine which Helsinki-NLP model to load
|
||||||
"""
|
"""
|
||||||
self.sid = None
|
|
||||||
self.translators_tokenizer = None
|
self.translators_tokenizer = None
|
||||||
self.translators_model = None
|
self.translators_model = None
|
||||||
self.logger = Logger("language.log")
|
self.logger = Logger("language.log")
|
||||||
@@ -25,11 +22,6 @@ class LanguageUtility:
|
|||||||
|
|
||||||
def load_model(self) -> None:
|
def load_model(self) -> None:
|
||||||
animate_thinking("Loading language utility...", color="status")
|
animate_thinking("Loading language utility...", color="status")
|
||||||
try:
|
|
||||||
nltk.data.find('vader_lexicon')
|
|
||||||
except LookupError:
|
|
||||||
nltk.download('vader_lexicon')
|
|
||||||
self.sid = SentimentIntensityAnalyzer()
|
|
||||||
self.translators_tokenizer = {lang: MarianTokenizer.from_pretrained(f"Helsinki-NLP/opus-mt-{lang}-en") for lang in self.supported_language if lang != "en"}
|
self.translators_tokenizer = {lang: MarianTokenizer.from_pretrained(f"Helsinki-NLP/opus-mt-{lang}-en") for lang in self.supported_language if lang != "en"}
|
||||||
self.translators_model = {lang: MarianMTModel.from_pretrained(f"Helsinki-NLP/opus-mt-{lang}-en") for lang in self.supported_language if lang != "en"}
|
self.translators_model = {lang: MarianMTModel.from_pretrained(f"Helsinki-NLP/opus-mt-{lang}-en") for lang in self.supported_language if lang != "en"}
|
||||||
|
|
||||||
@@ -65,49 +57,17 @@ class LanguageUtility:
|
|||||||
translation = model.generate(**inputs)
|
translation = model.generate(**inputs)
|
||||||
return tokenizer.decode(translation[0], skip_special_tokens=True)
|
return tokenizer.decode(translation[0], skip_special_tokens=True)
|
||||||
|
|
||||||
def detect_emotion(self, text: str) -> str:
|
|
||||||
"""
|
|
||||||
Detect the dominant emotion in the given text
|
|
||||||
Args:
|
|
||||||
text: string to analyze
|
|
||||||
Returns: string of the dominant emotion
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
scores = self.sid.polarity_scores(text)
|
|
||||||
emotions = {
|
|
||||||
'Happy': max(scores['pos'], 0),
|
|
||||||
'Angry': 0,
|
|
||||||
'Sad': max(scores['neg'], 0),
|
|
||||||
'Fear': 0,
|
|
||||||
'Surprise': 0
|
|
||||||
}
|
|
||||||
if scores['compound'] < -0.5:
|
|
||||||
emotions['Angry'] = abs(scores['compound']) * 0.5
|
|
||||||
emotions['Fear'] = abs(scores['compound']) * 0.5
|
|
||||||
elif scores['compound'] > 0.5:
|
|
||||||
emotions['Happy'] = scores['compound']
|
|
||||||
emotions['Surprise'] = scores['compound'] * 0.5
|
|
||||||
dominant_emotion = max(emotions, key=emotions.get)
|
|
||||||
if emotions[dominant_emotion] == 0:
|
|
||||||
return 'Neutral'
|
|
||||||
self.logger.info(f"Emotion: {dominant_emotion} for text: {text}")
|
|
||||||
return dominant_emotion
|
|
||||||
except Exception as e:
|
|
||||||
raise e
|
|
||||||
|
|
||||||
def analyze(self, text):
|
def analyze(self, text):
|
||||||
"""
|
"""
|
||||||
Combined analysis of language and emotion
|
Combined analysis of language and emotion
|
||||||
Args:
|
Args:
|
||||||
text: string to analyze
|
text: string to analyze
|
||||||
Returns: dictionary with language and emotion results
|
Returns: dictionary with language related information
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
language = self.detect_language(text)
|
language = self.detect_language(text)
|
||||||
emotions = self.detect_emotion(text)
|
|
||||||
return {
|
return {
|
||||||
"language": language,
|
"language": language
|
||||||
"emotions": emotions
|
|
||||||
}
|
}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise e
|
raise e
|
||||||
@@ -125,4 +85,4 @@ if __name__ == "__main__":
|
|||||||
pretty_print(f"Language: {detector.detect_language(text)}", color="status")
|
pretty_print(f"Language: {detector.detect_language(text)}", color="status")
|
||||||
result = detector.analyze(text)
|
result = detector.analyze(text)
|
||||||
trans = detector.translate(text, result['language'])
|
trans = detector.translate(text, result['language'])
|
||||||
pretty_print(f"Translation: {trans} - from: {result['language']} - Emotion: {result['emotions']}")
|
pretty_print(f"Translation: {trans} - from: {result['language']}")
|
||||||
@@ -17,6 +17,9 @@ from sources.utility import pretty_print, animate_thinking
|
|||||||
class Provider:
|
class Provider:
|
||||||
def __init__(self, provider_name, model, server_address="127.0.0.1:5000", is_local=False):
|
def __init__(self, provider_name, model, server_address="127.0.0.1:5000", is_local=False):
|
||||||
self.provider_name = provider_name.lower()
|
self.provider_name = provider_name.lower()
|
||||||
|
# Normalize provider name aliases (e.g. README documents 'togetherAI' but canonical key is 'together')
|
||||||
|
_aliases = {"togetherai": "together"}
|
||||||
|
self.provider_name = _aliases.get(self.provider_name, self.provider_name)
|
||||||
self.model = model
|
self.model = model
|
||||||
self.is_local = is_local
|
self.is_local = is_local
|
||||||
self.server_ip = server_address
|
self.server_ip = server_address
|
||||||
@@ -32,11 +35,14 @@ class Provider:
|
|||||||
"together": self.together_fn,
|
"together": self.together_fn,
|
||||||
"dsk_deepseek": self.dsk_deepseek,
|
"dsk_deepseek": self.dsk_deepseek,
|
||||||
"openrouter": self.openrouter_fn,
|
"openrouter": self.openrouter_fn,
|
||||||
|
"anthropic": self.anthropic_fn,
|
||||||
|
"minimax": self.minimax_fn,
|
||||||
"test": self.test_fn
|
"test": self.test_fn
|
||||||
}
|
}
|
||||||
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", "together", "google", "openrouter"]
|
self.internal_url, self.in_docker = self.get_internal_url()
|
||||||
|
self.unsafe_providers = ["openai", "deepseek", "dsk_deepseek", "together", "google", "openrouter", "anthropic", "minimax"]
|
||||||
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 and self.is_local == False:
|
if self.provider_name in self.unsafe_providers and self.is_local == False:
|
||||||
@@ -57,6 +63,13 @@ class Provider:
|
|||||||
exit(1)
|
exit(1)
|
||||||
return api_key
|
return api_key
|
||||||
|
|
||||||
|
def get_internal_url(self):
|
||||||
|
load_dotenv()
|
||||||
|
url = os.getenv("DOCKER_INTERNAL_URL")
|
||||||
|
if not url: # running on host
|
||||||
|
return "http://localhost", False
|
||||||
|
return url, True
|
||||||
|
|
||||||
def respond(self, history, verbose=True):
|
def respond(self, history, verbose=True):
|
||||||
"""
|
"""
|
||||||
Use the choosen provider to generate text.
|
Use the choosen provider to generate text.
|
||||||
@@ -152,7 +165,11 @@ class Provider:
|
|||||||
Use local or remote Ollama server to generate text.
|
Use local or remote Ollama server to generate text.
|
||||||
"""
|
"""
|
||||||
thought = ""
|
thought = ""
|
||||||
host = "http://localhost:11434" if self.is_local else f"http://{self.server_address}"
|
if self.is_local:
|
||||||
|
server_port = self.server_address.split(":")[-1] if ":" in str(self.server_address) else "11434"
|
||||||
|
host = f"{self.internal_url}:{server_port}"
|
||||||
|
else:
|
||||||
|
host = f"http://{self.server_address}"
|
||||||
client = OllamaClient(host=host)
|
client = OllamaClient(host=host)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -203,7 +220,13 @@ class Provider:
|
|||||||
Use openai to generate text.
|
Use openai to generate text.
|
||||||
"""
|
"""
|
||||||
base_url = self.server_ip
|
base_url = self.server_ip
|
||||||
if self.is_local:
|
if self.is_local and self.in_docker:
|
||||||
|
try:
|
||||||
|
host, port = base_url.split(':')
|
||||||
|
except Exception as e:
|
||||||
|
port = "8000"
|
||||||
|
client = OpenAI(api_key=self.api_key, base_url=f"{self.internal_url}:{port}")
|
||||||
|
elif self.is_local:
|
||||||
client = OpenAI(api_key=self.api_key, base_url=f"http://{base_url}")
|
client = OpenAI(api_key=self.api_key, base_url=f"http://{base_url}")
|
||||||
else:
|
else:
|
||||||
client = OpenAI(api_key=self.api_key)
|
client = OpenAI(api_key=self.api_key)
|
||||||
@@ -323,27 +346,64 @@ class Provider:
|
|||||||
def lm_studio_fn(self, history, verbose=False):
|
def lm_studio_fn(self, history, verbose=False):
|
||||||
"""
|
"""
|
||||||
Use local lm-studio server to generate text.
|
Use local lm-studio server to generate text.
|
||||||
lm studio use endpoint /v1/chat/completions not /chat/completions like openai
|
|
||||||
"""
|
"""
|
||||||
thought = ""
|
if self.in_docker:
|
||||||
route_start = f"{self.server_ip}/v1/chat/completions"
|
# Extract port from server_address, handling both "host:port" and "http://host:port"
|
||||||
|
port = "1234" # default
|
||||||
|
addr = self.server_address
|
||||||
|
if "://" not in addr:
|
||||||
|
addr = f"http://{addr}"
|
||||||
|
parsed_addr = urlparse(addr)
|
||||||
|
if parsed_addr.port:
|
||||||
|
port = str(parsed_addr.port)
|
||||||
|
url = f"{self.internal_url}:{port}"
|
||||||
|
else:
|
||||||
|
# Normalize the address to ensure it has a scheme prefix
|
||||||
|
addr = self.server_ip
|
||||||
|
if "://" not in addr:
|
||||||
|
addr = f"http://{addr}"
|
||||||
|
url = addr
|
||||||
|
route_start = f"{url}/v1/chat/completions"
|
||||||
payload = {
|
payload = {
|
||||||
"messages": history,
|
"messages": history,
|
||||||
"temperature": 0.7,
|
"temperature": 0.7,
|
||||||
"max_tokens": 4096,
|
"max_tokens": 4096,
|
||||||
"model": self.model
|
"model": self.model
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = requests.post(route_start, json=payload, timeout=30)
|
||||||
|
if response.status_code != 200:
|
||||||
|
raise Exception(f"LM Studio returned status {response.status_code}: {response.text}")
|
||||||
|
if not response.text.strip():
|
||||||
|
raise Exception("LM Studio returned empty response")
|
||||||
try:
|
try:
|
||||||
response = requests.post(route_start, json=payload)
|
|
||||||
result = response.json()
|
result = response.json()
|
||||||
|
except ValueError as json_err:
|
||||||
|
raise Exception(f"Invalid JSON from LM Studio: {response.text[:200]}") from json_err
|
||||||
|
|
||||||
if verbose:
|
if verbose:
|
||||||
print("Response from LM Studio:", result)
|
print("Response from LM Studio:", result)
|
||||||
return result.get("choices", [{}])[0].get("message", {}).get("content", "")
|
choices = result.get("choices", [])
|
||||||
|
if not choices:
|
||||||
|
raise Exception(f"No choices in LM Studio response: {result}")
|
||||||
|
|
||||||
|
message = choices[0].get("message", {})
|
||||||
|
content = message.get("content", "")
|
||||||
|
if not content:
|
||||||
|
raise Exception(f"Empty content in LM Studio response: {result}")
|
||||||
|
return content
|
||||||
|
|
||||||
|
except requests.exceptions.Timeout:
|
||||||
|
raise Exception("LM Studio request timed out - check if server is responsive")
|
||||||
|
except requests.exceptions.ConnectionError:
|
||||||
|
raise Exception(f"Cannot connect to LM Studio at {route_start} - check if server is running")
|
||||||
except requests.exceptions.RequestException as e:
|
except requests.exceptions.RequestException as e:
|
||||||
raise Exception(f"HTTP request failed: {str(e)}") from e
|
raise Exception(f"HTTP request failed: {str(e)}") from e
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise Exception(f"An error occurred: {str(e)}") from e
|
if "LM Studio" in str(e):
|
||||||
return thought
|
raise # Re-raise our custom exceptions
|
||||||
|
raise Exception(f"Unexpected error: {str(e)}") from e
|
||||||
|
|
||||||
def openrouter_fn(self, history, verbose=False):
|
def openrouter_fn(self, history, verbose=False):
|
||||||
"""
|
"""
|
||||||
@@ -368,6 +428,39 @@ class Provider:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise Exception(f"OpenRouter API error: {str(e)}") from e
|
raise Exception(f"OpenRouter API error: {str(e)}") from e
|
||||||
|
|
||||||
|
def minimax_fn(self, history, verbose=False):
|
||||||
|
"""
|
||||||
|
Use MiniMax API to generate text via OpenAI-compatible interface.
|
||||||
|
|
||||||
|
Supported models:
|
||||||
|
- MiniMax-M2.7: Latest flagship model with enhanced reasoning and coding
|
||||||
|
- MiniMax-M2.7-highspeed: High-speed version of M2.7 for low-latency scenarios
|
||||||
|
- MiniMax-M2.5: Peak performance model (~60 tps), 204,800 context window
|
||||||
|
- MiniMax-M2.5-highspeed: Same performance, faster (~100 tps)
|
||||||
|
|
||||||
|
Note: temperature must be in range (0.0, 1.0], default is 1.0
|
||||||
|
"""
|
||||||
|
load_dotenv()
|
||||||
|
base_url = os.getenv("MINIMAX_BASE_URL", "https://api.minimax.io/v1")
|
||||||
|
|
||||||
|
client = OpenAI(api_key=self.api_key, base_url=base_url)
|
||||||
|
if self.is_local:
|
||||||
|
raise Exception("MiniMax is not available for local use. Change config.ini")
|
||||||
|
try:
|
||||||
|
response = client.chat.completions.create(
|
||||||
|
model=self.model,
|
||||||
|
messages=history,
|
||||||
|
temperature=1.0,
|
||||||
|
)
|
||||||
|
if response is None:
|
||||||
|
raise Exception("MiniMax response is empty.")
|
||||||
|
thought = response.choices[0].message.content
|
||||||
|
if verbose:
|
||||||
|
print(thought)
|
||||||
|
return thought
|
||||||
|
except Exception as e:
|
||||||
|
raise Exception(f"MiniMax API error: {str(e)}") from e
|
||||||
|
|
||||||
def dsk_deepseek(self, history, verbose=False):
|
def dsk_deepseek(self, history, verbose=False):
|
||||||
"""
|
"""
|
||||||
Use: xtekky/deepseek4free
|
Use: xtekky/deepseek4free
|
||||||
@@ -392,13 +485,13 @@ class Provider:
|
|||||||
if chunk['type'] == 'text':
|
if chunk['type'] == 'text':
|
||||||
thought += chunk['content']
|
thought += chunk['content']
|
||||||
return thought
|
return thought
|
||||||
except AuthenticationError:
|
except AuthenticationError as e:
|
||||||
raise AuthenticationError("Authentication failed. Please check your token.") from e
|
raise AuthenticationError("Authentication failed. Please check your token.") from e
|
||||||
except RateLimitError:
|
except RateLimitError as e:
|
||||||
raise RateLimitError("Rate limit exceeded. Please wait before making more requests.") from e
|
raise RateLimitError("Rate limit exceeded. Please wait before making more requests.") from e
|
||||||
except CloudflareError as e:
|
except CloudflareError as e:
|
||||||
raise CloudflareError(f"Cloudflare protection encountered: {str(e)}") from e
|
raise CloudflareError(f"Cloudflare protection encountered: {str(e)}") from e
|
||||||
except NetworkError:
|
except NetworkError as e:
|
||||||
raise NetworkError("Network error occurred. Check your internet connection.") from e
|
raise NetworkError("Network error occurred. Check your internet connection.") from e
|
||||||
except APIError as e:
|
except APIError as e:
|
||||||
raise APIError(f"API error occurred: {str(e)}") from e
|
raise APIError(f"API error occurred: {str(e)}") from e
|
||||||
|
|||||||
@@ -7,10 +7,14 @@ import json
|
|||||||
from typing import List, Tuple, Type, Dict
|
from typing import List, Tuple, Type, Dict
|
||||||
import torch
|
import torch
|
||||||
from transformers import AutoTokenizer, AutoModelForSeq2SeqLM
|
from transformers import AutoTokenizer, AutoModelForSeq2SeqLM
|
||||||
|
import configparser
|
||||||
|
|
||||||
from sources.utility import timer_decorator, pretty_print, animate_thinking
|
from sources.utility import timer_decorator, pretty_print, animate_thinking
|
||||||
from sources.logger import Logger
|
from sources.logger import Logger
|
||||||
|
|
||||||
|
config = configparser.ConfigParser()
|
||||||
|
config.read('config.ini')
|
||||||
|
|
||||||
class Memory():
|
class Memory():
|
||||||
"""
|
"""
|
||||||
Memory is a class for managing the conversation memory
|
Memory is a class for managing the conversation memory
|
||||||
@@ -162,6 +166,9 @@ class Memory():
|
|||||||
if self.memory[curr_idx-1]['content'] == content:
|
if self.memory[curr_idx-1]['content'] == content:
|
||||||
pretty_print("Warning: same message have been pushed twice to memory", color="error")
|
pretty_print("Warning: same message have been pushed twice to memory", color="error")
|
||||||
time_str = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
time_str = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||||
|
if config["MAIN"]["provider_name"] == "openrouter":
|
||||||
|
self.memory.append({'role': role, 'content': content})
|
||||||
|
else:
|
||||||
self.memory.append({'role': role, 'content': content, 'time': time_str, 'model_used': self.model_provider})
|
self.memory.append({'role': role, 'content': content, 'time': time_str, 'model_used': self.model_provider})
|
||||||
return curr_idx-1
|
return curr_idx-1
|
||||||
|
|
||||||
|
|||||||
@@ -3,11 +3,18 @@ from typing import List, Tuple, Type, Dict
|
|||||||
import queue
|
import queue
|
||||||
import threading
|
import threading
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import torch
|
|
||||||
import time
|
import time
|
||||||
from transformers import AutoModelForSpeechSeq2Seq, AutoProcessor, pipeline
|
|
||||||
import librosa
|
IMPORT_FOUND = True
|
||||||
import pyaudio
|
|
||||||
|
try:
|
||||||
|
import torch
|
||||||
|
import librosa
|
||||||
|
import pyaudio
|
||||||
|
from transformers import AutoModelForSpeechSeq2Seq, AutoProcessor, pipeline
|
||||||
|
except ImportError:
|
||||||
|
print(Fore.RED + "Speech To Text disabled." + Fore.RESET)
|
||||||
|
IMPORT_FOUND = False
|
||||||
|
|
||||||
audio_queue = queue.Queue()
|
audio_queue = queue.Queue()
|
||||||
done = False
|
done = False
|
||||||
@@ -23,6 +30,9 @@ class AudioRecorder:
|
|||||||
self.chunk = chunk
|
self.chunk = chunk
|
||||||
self.record_seconds = record_seconds
|
self.record_seconds = record_seconds
|
||||||
self.verbose = verbose
|
self.verbose = verbose
|
||||||
|
self.thread = None
|
||||||
|
self.audio = None
|
||||||
|
if IMPORT_FOUND:
|
||||||
self.audio = pyaudio.PyAudio()
|
self.audio = pyaudio.PyAudio()
|
||||||
self.thread = threading.Thread(target=self._record, daemon=True)
|
self.thread = threading.Thread(target=self._record, daemon=True)
|
||||||
|
|
||||||
@@ -30,6 +40,8 @@ class AudioRecorder:
|
|||||||
"""
|
"""
|
||||||
Record audio from the microphone and add it to the audio queue.
|
Record audio from the microphone and add it to the audio queue.
|
||||||
"""
|
"""
|
||||||
|
if not IMPORT_FOUND:
|
||||||
|
return
|
||||||
stream = self.audio.open(format=self.format, channels=self.channels, rate=self.rate,
|
stream = self.audio.open(format=self.format, channels=self.channels, rate=self.rate,
|
||||||
input=True, frames_per_buffer=self.chunk)
|
input=True, frames_per_buffer=self.chunk)
|
||||||
if self.verbose:
|
if self.verbose:
|
||||||
@@ -58,10 +70,14 @@ class AudioRecorder:
|
|||||||
|
|
||||||
def start(self) -> None:
|
def start(self) -> None:
|
||||||
"""Start the recording thread."""
|
"""Start the recording thread."""
|
||||||
|
if not IMPORT_FOUND:
|
||||||
|
return
|
||||||
self.thread.start()
|
self.thread.start()
|
||||||
|
|
||||||
def join(self) -> None:
|
def join(self) -> None:
|
||||||
"""Wait for the recording thread to finish."""
|
"""Wait for the recording thread to finish."""
|
||||||
|
if not IMPORT_FOUND:
|
||||||
|
return
|
||||||
self.thread.join()
|
self.thread.join()
|
||||||
|
|
||||||
class Transcript:
|
class Transcript:
|
||||||
@@ -69,6 +85,9 @@ class Transcript:
|
|||||||
Transcript is a class that transcribes audio from the audio queue and adds it to the transcript.
|
Transcript is a class that transcribes audio from the audio queue and adds it to the transcript.
|
||||||
"""
|
"""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
if not IMPORT_FOUND:
|
||||||
|
print(Fore.RED + "Transcript: Speech to Text is disabled." + Fore.RESET)
|
||||||
|
return
|
||||||
self.last_read = None
|
self.last_read = None
|
||||||
device = self.get_device()
|
device = self.get_device()
|
||||||
torch_dtype = torch.float16 if device == "cuda" else torch.float32
|
torch_dtype = torch.float16 if device == "cuda" else torch.float32
|
||||||
@@ -91,6 +110,8 @@ class Transcript:
|
|||||||
)
|
)
|
||||||
|
|
||||||
def get_device(self) -> str:
|
def get_device(self) -> str:
|
||||||
|
if not IMPORT_FOUND:
|
||||||
|
return "cpu"
|
||||||
if torch.backends.mps.is_available():
|
if torch.backends.mps.is_available():
|
||||||
return "mps"
|
return "mps"
|
||||||
if torch.cuda.is_available():
|
if torch.cuda.is_available():
|
||||||
@@ -108,6 +129,8 @@ class Transcript:
|
|||||||
|
|
||||||
def transcript_job(self, audio_data: np.ndarray, sample_rate: int = 16000) -> str:
|
def transcript_job(self, audio_data: np.ndarray, sample_rate: int = 16000) -> str:
|
||||||
"""Transcribe the audio data."""
|
"""Transcribe the audio data."""
|
||||||
|
if not IMPORT_FOUND:
|
||||||
|
return ""
|
||||||
if audio_data.dtype != np.float32:
|
if audio_data.dtype != np.float32:
|
||||||
audio_data = audio_data.astype(np.float32) / np.iinfo(audio_data.dtype).max
|
audio_data = audio_data.astype(np.float32) / np.iinfo(audio_data.dtype).max
|
||||||
if len(audio_data.shape) > 1:
|
if len(audio_data.shape) > 1:
|
||||||
@@ -122,6 +145,9 @@ class AudioTranscriber:
|
|||||||
AudioTranscriber is a class that transcribes audio from the audio queue and adds it to the transcript.
|
AudioTranscriber is a class that transcribes audio from the audio queue and adds it to the transcript.
|
||||||
"""
|
"""
|
||||||
def __init__(self, ai_name: str, verbose: bool = False):
|
def __init__(self, ai_name: str, verbose: bool = False):
|
||||||
|
if not IMPORT_FOUND:
|
||||||
|
print(Fore.RED + "AudioTranscriber: Speech to Text is disabled." + Fore.RESET)
|
||||||
|
return
|
||||||
self.verbose = verbose
|
self.verbose = verbose
|
||||||
self.ai_name = ai_name
|
self.ai_name = ai_name
|
||||||
self.transcriptor = Transcript()
|
self.transcriptor = Transcript()
|
||||||
@@ -152,6 +178,8 @@ class AudioTranscriber:
|
|||||||
"""
|
"""
|
||||||
Transcribe the audio data using AI stt model.
|
Transcribe the audio data using AI stt model.
|
||||||
"""
|
"""
|
||||||
|
if not IMPORT_FOUND:
|
||||||
|
return
|
||||||
global done
|
global done
|
||||||
if self.verbose:
|
if self.verbose:
|
||||||
print(Fore.BLUE + "AudioTranscriber: Started processing..." + Fore.RESET)
|
print(Fore.BLUE + "AudioTranscriber: Started processing..." + Fore.RESET)
|
||||||
@@ -185,9 +213,13 @@ class AudioTranscriber:
|
|||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
"""Start the transcription thread."""
|
"""Start the transcription thread."""
|
||||||
|
if not IMPORT_FOUND:
|
||||||
|
return
|
||||||
self.thread.start()
|
self.thread.start()
|
||||||
|
|
||||||
def join(self):
|
def join(self):
|
||||||
|
if not IMPORT_FOUND:
|
||||||
|
return
|
||||||
"""Wait for the transcription thread to finish."""
|
"""Wait for the transcription thread to finish."""
|
||||||
self.thread.join()
|
self.thread.join()
|
||||||
|
|
||||||
|
|||||||
@@ -5,9 +5,15 @@ import subprocess
|
|||||||
from sys import modules
|
from sys import modules
|
||||||
from typing import List, Tuple, Type, Dict
|
from typing import List, Tuple, Type, Dict
|
||||||
|
|
||||||
from kokoro import KPipeline
|
IMPORT_FOUND = True
|
||||||
from IPython.display import display, Audio
|
try:
|
||||||
import soundfile as sf
|
from kokoro import KPipeline
|
||||||
|
from IPython.display import display, Audio
|
||||||
|
import soundfile as sf
|
||||||
|
except ImportError:
|
||||||
|
print("Speech synthesis disabled. To enable TTS, install: pip install kokoro==0.9.4 soundfile ipython")
|
||||||
|
print("Note: kokoro requires Python <3.12 due to num2words dependency.")
|
||||||
|
IMPORT_FOUND = False
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
from utility import pretty_print, animate_thinking
|
from utility import pretty_print, animate_thinking
|
||||||
@@ -33,7 +39,7 @@ class Speech():
|
|||||||
}
|
}
|
||||||
self.pipeline = None
|
self.pipeline = None
|
||||||
self.language = language
|
self.language = language
|
||||||
if enable:
|
if enable and IMPORT_FOUND:
|
||||||
self.pipeline = KPipeline(lang_code=self.lang_map[language])
|
self.pipeline = KPipeline(lang_code=self.lang_map[language])
|
||||||
self.voice = self.voice_map[language][voice_idx]
|
self.voice = self.voice_map[language][voice_idx]
|
||||||
self.speed = 1.2
|
self.speed = 1.2
|
||||||
@@ -57,7 +63,8 @@ class Speech():
|
|||||||
sentence (str): The text to convert to speech. Will be pre-processed.
|
sentence (str): The text to convert to speech. Will be pre-processed.
|
||||||
voice_idx (int, optional): Index of the voice to use from the voice map.
|
voice_idx (int, optional): Index of the voice to use from the voice map.
|
||||||
"""
|
"""
|
||||||
if not self.pipeline:
|
if not self.pipeline or not IMPORT_FOUND:
|
||||||
|
print("Pipeline disabled.")
|
||||||
return
|
return
|
||||||
if voice_idx >= len(self.voice_map[self.language]):
|
if voice_idx >= len(self.voice_map[self.language]):
|
||||||
pretty_print("Invalid voice number, using default voice", color="error")
|
pretty_print("Invalid voice number, using default voice", color="error")
|
||||||
@@ -109,7 +116,7 @@ class Speech():
|
|||||||
def shorten_paragraph(self, sentence):
|
def shorten_paragraph(self, sentence):
|
||||||
#TODO find a better way, we would like to have the TTS not be annoying, speak only useful informations
|
#TODO find a better way, we would like to have the TTS not be annoying, speak only useful informations
|
||||||
"""
|
"""
|
||||||
Find long paragraph like **explaination**: <long text> by keeping only the first sentence.
|
Find long paragraph like **explanation**: <long text> by keeping only the first sentence.
|
||||||
Args:
|
Args:
|
||||||
sentence (str): The sentence to shorten
|
sentence (str): The sentence to shorten
|
||||||
Returns:
|
Returns:
|
||||||
@@ -159,6 +166,7 @@ class Speech():
|
|||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
# TODO add info message for cn2an, jieba chinese related import
|
# TODO add info message for cn2an, jieba chinese related import
|
||||||
|
IMPORT_FOUND = False
|
||||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||||
speech = Speech()
|
speech = Speech()
|
||||||
tosay_en = """
|
tosay_en = """
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ class FileFinder(Tools):
|
|||||||
else:
|
else:
|
||||||
return {"filename": file_path, "error": "File not found"}
|
return {"filename": file_path, "error": "File not found"}
|
||||||
|
|
||||||
def recursive_search(self, directory_path: str, filename: str) -> str | None:
|
def recursive_search(self, directory_path: str, filename: str) -> str:
|
||||||
"""
|
"""
|
||||||
Recursively searches for files in a directory and its subdirectories.
|
Recursively searches for files in a directory and its subdirectories.
|
||||||
Args:
|
Args:
|
||||||
|
|||||||
@@ -12,61 +12,65 @@ from sources.tools.tools import Tools
|
|||||||
class FlightSearch(Tools):
|
class FlightSearch(Tools):
|
||||||
def __init__(self, api_key: str = None):
|
def __init__(self, api_key: str = None):
|
||||||
"""
|
"""
|
||||||
A tool to search for flight information using a flight number via AviationStack API.
|
A tool to search for flight information using a flight number via SerpApi.
|
||||||
"""
|
"""
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.tag = "flight_search"
|
self.tag = "flight_search"
|
||||||
self.name = "Flight Search"
|
self.name = "Flight Search"
|
||||||
self.description = "Search for flight information using a flight number via AviationStack API."
|
self.description = "Search for flight information using a flight number via SerpApi."
|
||||||
self.api_key = None
|
self.api_key = api_key or os.getenv("SERPAPI_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:
|
||||||
if self.api_key is None:
|
if self.api_key is None:
|
||||||
return "Error: No AviationStack API key provided."
|
return "Error: No SerpApi key provided."
|
||||||
|
|
||||||
for block in blocks:
|
for block in blocks:
|
||||||
flight_number = block.strip().lower().replace('\n', '')
|
flight_number = block.strip().upper().replace('\n', '')
|
||||||
if not flight_number:
|
if not flight_number:
|
||||||
return "Error: No flight number provided."
|
return "Error: No flight number provided."
|
||||||
|
|
||||||
try:
|
try:
|
||||||
url = "http://api.aviationstack.com/v1/flights"
|
url = "https://serpapi.com/search"
|
||||||
params = {
|
params = {
|
||||||
"access_key": self.api_key,
|
"engine": "google_flights",
|
||||||
"flight_iata": flight_number,
|
"api_key": self.api_key,
|
||||||
"limit": 1
|
"q": flight_number,
|
||||||
|
"type": "2" # Flight status search
|
||||||
}
|
}
|
||||||
|
|
||||||
response = requests.get(url, params=params)
|
response = requests.get(url, params=params)
|
||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
|
|
||||||
data = response.json()
|
data = response.json()
|
||||||
if "data" in data and len(data["data"]) > 0:
|
|
||||||
flight = data["data"][0]
|
|
||||||
# Extract key flight information
|
|
||||||
flight_status = flight.get("flight_status", "Unknown")
|
|
||||||
departure = flight.get("departure", {})
|
|
||||||
arrival = flight.get("arrival", {})
|
|
||||||
airline = flight.get("airline", {}).get("name", "Unknown")
|
|
||||||
|
|
||||||
departure_airport = departure.get("airport", "Unknown")
|
if "flights" in data and len(data["flights"]) > 0:
|
||||||
departure_time = departure.get("scheduled", "Unknown")
|
flight = data["flights"][0]
|
||||||
arrival_airport = arrival.get("airport", "Unknown")
|
|
||||||
arrival_time = arrival.get("scheduled", "Unknown")
|
# Extract key information
|
||||||
|
departure = flight.get("departure_airport", {})
|
||||||
|
arrival = flight.get("arrival_airport", {})
|
||||||
|
|
||||||
|
departure_code = departure.get("id", "Unknown")
|
||||||
|
departure_time = flight.get("departure_time", "Unknown")
|
||||||
|
arrival_code = arrival.get("id", "Unknown")
|
||||||
|
arrival_time = flight.get("arrival_time", "Unknown")
|
||||||
|
airline = flight.get("airline", "Unknown")
|
||||||
|
status = flight.get("flight_status", "Unknown")
|
||||||
|
|
||||||
return (
|
return (
|
||||||
f"Flight: {flight_number}\n"
|
f"Flight: {flight_number}\n"
|
||||||
f"Airline: {airline}\n"
|
f"Airline: {airline}\n"
|
||||||
f"Status: {flight_status}\n"
|
f"Status: {status}\n"
|
||||||
f"Departure: {departure_airport} at {departure_time}\n"
|
f"Departure: {departure_code} at {departure_time}\n"
|
||||||
f"Arrival: {arrival_airport} at {arrival_time}"
|
f"Arrival: {arrival_code} at {arrival_time}"
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
return f"No flight information found for {flight_number}"
|
return f"No flight information found for {flight_number}"
|
||||||
|
|
||||||
except requests.RequestException as e:
|
except requests.RequestException as e:
|
||||||
return f"Error during flight search: {str(e)}"
|
return f"Error during flight search: {str(e)}"
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return f"Unexpected error: {str(e)}"
|
return f"Unexpected error: {str(e)}"
|
||||||
|
|
||||||
return "No flight search performed"
|
return "No flight search performed"
|
||||||
|
|
||||||
def execution_failure_check(self, output: str) -> bool:
|
def execution_failure_check(self, output: str) -> bool:
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ class MCP_finder(Tools):
|
|||||||
try:
|
try:
|
||||||
matching_mcp_infos = self.find_mcp_servers(block_clean)
|
matching_mcp_infos = self.find_mcp_servers(block_clean)
|
||||||
except requests.exceptions.RequestException as e:
|
except requests.exceptions.RequestException as e:
|
||||||
output += "Connection failed. Is the API key in environement?\n"
|
output += "Connection failed. Is the API key in environment?\n"
|
||||||
continue
|
continue
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
output += f"Error: {str(e)}\n"
|
output += f"Error: {str(e)}\n"
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import requests
|
import requests
|
||||||
from bs4 import BeautifulSoup
|
from bs4 import BeautifulSoup
|
||||||
|
import sys
|
||||||
import os
|
import os
|
||||||
|
from urllib.parse import urlencode
|
||||||
|
|
||||||
if __name__ == "__main__": # if running as a script for individual testing
|
if __name__ == "__main__": # if running as a script for individual testing
|
||||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
|
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
|
||||||
@@ -77,7 +79,14 @@ class searxSearch(Tools):
|
|||||||
'Upgrade-Insecure-Requests': '1',
|
'Upgrade-Insecure-Requests': '1',
|
||||||
'User-Agent': self.user_agent
|
'User-Agent': self.user_agent
|
||||||
}
|
}
|
||||||
data = f"q={query}&categories=general&language=auto&time_range=&safesearch=0&theme=simple".encode('utf-8')
|
data = urlencode({
|
||||||
|
'q': query,
|
||||||
|
'categories': 'general',
|
||||||
|
'language': 'auto',
|
||||||
|
'time_range': '',
|
||||||
|
'safesearch': '0',
|
||||||
|
'theme': 'simple'
|
||||||
|
}).encode('utf-8')
|
||||||
try:
|
try:
|
||||||
response = requests.post(search_url, headers=headers, data=data, verify=False)
|
response = requests.post(search_url, headers=headers, data=data, verify=False)
|
||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
@@ -101,7 +110,7 @@ class searxSearch(Tools):
|
|||||||
"""
|
"""
|
||||||
Checks if the execution failed based on the output.
|
Checks if the execution failed based on the output.
|
||||||
"""
|
"""
|
||||||
return "Error" in output
|
return "Error" in output or "No search results" in output
|
||||||
|
|
||||||
def interpreter_feedback(self, output: str) -> str:
|
def interpreter_feedback(self, output: str) -> str:
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -41,28 +41,23 @@ class Tools():
|
|||||||
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
|
||||||
self.safe_mode = True
|
self.safe_mode = False
|
||||||
self.allow_language_exec_bash = False
|
self.allow_language_exec_bash = False
|
||||||
|
|
||||||
def get_work_dir(self):
|
def get_work_dir(self):
|
||||||
return self.work_dir
|
return self.work_dir
|
||||||
|
|
||||||
def set_allow_language_exec_bash(value: bool) -> None:
|
def set_allow_language_exec_bash(self, value: bool) -> None:
|
||||||
self.allow_language_exec_bash = value
|
self.allow_language_exec_bash = value
|
||||||
|
|
||||||
def check_config_dir_validity(self):
|
def safe_get_work_dir_path(self):
|
||||||
"""Check if the config directory is valid."""
|
path = None
|
||||||
path = self.config['MAIN']['work_dir']
|
path = os.getenv('WORK_DIR', path)
|
||||||
if path == "":
|
if path is None or path == "":
|
||||||
print("WARNING: Work directory not set in config.ini")
|
path = self.config['MAIN']['work_dir'] if 'MAIN' in self.config and 'work_dir' in self.config['MAIN'] else None
|
||||||
return False
|
if path is None or path == "":
|
||||||
if path.lower() == "none":
|
raise Exception("No work dir specified, please specify a work dir in .env file.")
|
||||||
print("WARNING: Work directory set to none in config.ini")
|
return path
|
||||||
return False
|
|
||||||
if not os.path.exists(path):
|
|
||||||
print(f"WARNING: Work directory {path} does not exist")
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
def config_exists(self):
|
def config_exists(self):
|
||||||
"""Check if the config file exists."""
|
"""Check if the config file exists."""
|
||||||
@@ -73,11 +68,10 @@ class Tools():
|
|||||||
default_path = os.path.dirname(os.getcwd())
|
default_path = os.path.dirname(os.getcwd())
|
||||||
if self.config_exists():
|
if self.config_exists():
|
||||||
self.config.read('./config.ini')
|
self.config.read('./config.ini')
|
||||||
config_path = self.config['MAIN']['work_dir']
|
workdir_path = self.safe_get_work_dir_path()
|
||||||
dir_path = default_path if not self.check_config_dir_validity() else config_path
|
|
||||||
else:
|
else:
|
||||||
dir_path = default_path
|
workdir_path = default_path
|
||||||
return dir_path
|
return workdir_path
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def execute(self, blocks:[str], safety:bool) -> str:
|
def execute(self, blocks:[str], safety:bool) -> str:
|
||||||
@@ -157,7 +151,7 @@ class Tools():
|
|||||||
self.excutable_blocks_found = False
|
self.excutable_blocks_found = False
|
||||||
return tmp
|
return tmp
|
||||||
|
|
||||||
def load_exec_block(self, llm_text: str) -> tuple[list[str], str | None]:
|
def load_exec_block(self, llm_text: str):
|
||||||
"""
|
"""
|
||||||
Extract code/query blocks from LLM-generated text and process them for execution.
|
Extract code/query blocks from LLM-generated text and process them for execution.
|
||||||
This method parses the text looking for code blocks marked with the tool's tag (e.g. ```python).
|
This method parses the text looking for code blocks marked with the tool's tag (e.g. ```python).
|
||||||
|
|||||||
@@ -11,6 +11,37 @@ function findInputs(element, result = []) {
|
|||||||
displayed: isElementDisplayed(input)
|
displayed: isElementDisplayed(input)
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
// Find all <select> elements (dropdowns / multi-choice)
|
||||||
|
const selects = element.querySelectorAll('select');
|
||||||
|
selects.forEach(select => {
|
||||||
|
const options = Array.from(select.options).map(opt => ({
|
||||||
|
value: opt.value,
|
||||||
|
text: opt.textContent.trim(),
|
||||||
|
selected: opt.selected
|
||||||
|
}));
|
||||||
|
result.push({
|
||||||
|
tagName: select.tagName,
|
||||||
|
text: select.name || '',
|
||||||
|
type: 'select',
|
||||||
|
class: select.className || '',
|
||||||
|
xpath: getXPath(select),
|
||||||
|
displayed: isElementDisplayed(select),
|
||||||
|
multiple: select.multiple,
|
||||||
|
options: options
|
||||||
|
});
|
||||||
|
});
|
||||||
|
// Find all <textarea> elements
|
||||||
|
const textareas = element.querySelectorAll('textarea');
|
||||||
|
textareas.forEach(textarea => {
|
||||||
|
result.push({
|
||||||
|
tagName: textarea.tagName,
|
||||||
|
text: textarea.name || '',
|
||||||
|
type: 'textarea',
|
||||||
|
class: textarea.className || '',
|
||||||
|
xpath: getXPath(textarea),
|
||||||
|
displayed: isElementDisplayed(textarea)
|
||||||
|
});
|
||||||
|
});
|
||||||
const allElements = element.querySelectorAll('*');
|
const allElements = element.querySelectorAll('*');
|
||||||
allElements.forEach(el => {
|
allElements.forEach(el => {
|
||||||
if (el.shadowRoot) {
|
if (el.shadowRoot) {
|
||||||
|
|||||||
@@ -1,10 +1,45 @@
|
|||||||
@echo off
|
@echo off
|
||||||
|
|
||||||
docker-compose up
|
if "%1"=="full" (
|
||||||
if %ERRORLEVEL% neq 0 (
|
echo Starting full deployment...
|
||||||
echo Error: Failed to start containers. Check Docker logs with 'docker compose logs'.
|
) else (
|
||||||
echo Possible fixes: Ensure Docker Desktop is running or check if port 8080 is free.
|
set "msg=Starting partial deployment... (backend run on host), use "full" to run all services in containers"
|
||||||
exit /b 1
|
echo !msg!
|
||||||
)
|
)
|
||||||
|
|
||||||
timeout /t 10 /nobreak >nul
|
@echo off
|
||||||
|
openssl rand -hex 32 >nul 2>&1
|
||||||
|
if %ERRORLEVEL% == 0 (
|
||||||
|
for /f %%i in ('openssl rand -hex 32') do set SEARXNG_SECRET_KEY=%%i
|
||||||
|
goto :key_generated
|
||||||
|
)
|
||||||
|
|
||||||
|
python --version >nul 2>&1
|
||||||
|
if %ERRORLEVEL% == 0 (
|
||||||
|
for /f %%i in ('python -c "import secrets; print(secrets.token_hex(32))"') do set SEARXNG_SECRET_KEY=%%i
|
||||||
|
goto :key_generated
|
||||||
|
)
|
||||||
|
|
||||||
|
py --version >nul 2>&1
|
||||||
|
if %ERRORLEVEL% == 0 (
|
||||||
|
for /f %%i in ('py -c "import secrets; print(secrets.token_hex(32))"') do set SEARXNG_SECRET_KEY=%%i
|
||||||
|
goto :key_generated
|
||||||
|
)
|
||||||
|
|
||||||
|
echo Error: Neither openssl nor python is available to generate a secret key.
|
||||||
|
echo Please install Python from https://python.org or OpenSSL
|
||||||
|
exit /b 2
|
||||||
|
|
||||||
|
:key_generated
|
||||||
|
echo Secret key generated successfully
|
||||||
|
|
||||||
|
REM Generate secret key
|
||||||
|
for /f %%i in ('powershell -command "[System.Web.Security.Membership]::GeneratePassword(64,0)"') do set SEARXNG_SECRET_KEY=%%i
|
||||||
|
|
||||||
|
if "%1"=="full" (
|
||||||
|
docker compose up -d backend
|
||||||
|
timeout /t 5 /nobreak >nul
|
||||||
|
docker compose --profile full up
|
||||||
|
) else (
|
||||||
|
docker compose --profile core up
|
||||||
|
)
|
||||||
@@ -1,12 +1,35 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
|
source .env
|
||||||
|
|
||||||
command_exists() {
|
command_exists() {
|
||||||
command -v "$1" &> /dev/null
|
command -v "$1" &> /dev/null
|
||||||
}
|
}
|
||||||
|
if [ -z "$WORK_DIR" ]; then
|
||||||
|
echo "Error: WORK_DIR environment variable is not set. Please set it in your .env file."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
#
|
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||||
# Check if Docker is installed é running
|
dir_size_bytes=$(du -s -b "$WORK_DIR" 2>/dev/null | awk '{print $1}')
|
||||||
#
|
else
|
||||||
|
dir_size_bytes=$(du -s --bytes "$WORK_DIR" 2>/dev/null | awk '{print $1}')
|
||||||
|
fi
|
||||||
|
|
||||||
|
max_size_bytes=$((2 * 1024 * 1024 * 1024 * 10))
|
||||||
|
|
||||||
|
echo "Mounting $WORK_DIR ($dir_size_bytes bytes) to docker."
|
||||||
|
|
||||||
|
if [ -n "$dir_size_bytes" ] && [ "$dir_size_bytes" -gt "$max_size_bytes" ]; then
|
||||||
|
echo "Error: WORK_DIR ($WORK_DIR) contains more than 20GB of data ($(du -sh "$WORK_DIR" 2>/dev/null | awk '{print $1}'))."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$1" = "full" ]; then
|
||||||
|
echo "Starting full deployment with backend and all services..."
|
||||||
|
else
|
||||||
|
echo "Starting core deployment with frontend and search services only... use ./start_services.sh full to start backend as well"
|
||||||
|
fi
|
||||||
|
|
||||||
if ! command_exists docker; then
|
if ! command_exists docker; then
|
||||||
echo "Error: Docker is not installed. Please install Docker first."
|
echo "Error: Docker is not installed. Please install Docker first."
|
||||||
@@ -41,17 +64,18 @@ else
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Check if Docker Compose is installed
|
# Check if Docker Compose is installed
|
||||||
if ! command_exists docker-compose && ! docker compose version >/dev/null 2>&1; then
|
# Prefer the newer 'docker compose' command if available
|
||||||
echo "Error: Docker Compose is not installed. Please install it first."
|
if docker compose version >/dev/null 2>&1; then
|
||||||
echo "On Ubuntu: sudo apt install docker-compose"
|
echo "Using newer docker compose (v2)."
|
||||||
echo "Or via pip: pip install docker-compose"
|
COMPOSE_CMD="docker compose"
|
||||||
exit 1
|
elif command_exists docker-compose; then
|
||||||
fi
|
echo "Using old docker-compose."
|
||||||
|
|
||||||
if command_exists docker-compose; then
|
|
||||||
COMPOSE_CMD="docker-compose"
|
COMPOSE_CMD="docker-compose"
|
||||||
else
|
else
|
||||||
COMPOSE_CMD="docker compose"
|
echo "Error: Docker Compose is not installed. Please install it first."
|
||||||
|
echo "On Ubuntu: sudo apt install docker-compose-plugin"
|
||||||
|
echo "Or install Docker Desktop which includes compose v2"
|
||||||
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check if docker-compose.yml exists
|
# Check if docker-compose.yml exists
|
||||||
@@ -60,15 +84,58 @@ if [ ! -f "docker-compose.yml" ]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# start docker compose for searxng, redis, frontend services
|
# Stop only the backend container if it's running to ensure a clean state
|
||||||
echo "Warning: stopping all docker containers (t-4 seconds)..."
|
if docker ps --format '{{.Names}}' | grep -q '^backend$'; then
|
||||||
sleep 4
|
echo "New start: (re)starting backend container..."
|
||||||
docker stop $(docker ps -a -q)
|
docker stop backend
|
||||||
echo "All containers stopped"
|
echo "Backend container stopped."
|
||||||
|
fi
|
||||||
|
|
||||||
if ! $COMPOSE_CMD up; then
|
# export searxng secret key (cross-platform)
|
||||||
|
if command -v openssl &> /dev/null; then
|
||||||
|
export SEARXNG_SECRET_KEY=$(openssl rand -hex 32)
|
||||||
|
else
|
||||||
|
# Fallback: use Python if openssl is not available
|
||||||
|
if command -v python3 &> /dev/null; then
|
||||||
|
export SEARXNG_SECRET_KEY=$(python3 -c "import secrets; print(secrets.token_hex(32))")
|
||||||
|
else
|
||||||
|
echo "Error: Neither openssl nor python is available to generate a secret key."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$1" = "full" ]; then
|
||||||
|
# First start backend and wait for it to be healthy
|
||||||
|
echo "Full docker deployment. Starting backend service..."
|
||||||
|
if ! $COMPOSE_CMD up -d backend; then
|
||||||
|
echo "Error: Failed to start backend container."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
# Wait for backend to be healthy (check if it's running and not restarting)
|
||||||
|
echo "Waiting for backend to be ready..."
|
||||||
|
for i in {1..30}; do
|
||||||
|
if [ "$(docker inspect -f '{{.State.Running}}' backend)" = "true" ] && \
|
||||||
|
[ "$(docker inspect -f '{{.State.Restarting}}' backend)" = "false" ]; then
|
||||||
|
echo "backend is ready!"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
if [ $i -eq 30 ]; then
|
||||||
|
echo "Error: backend failed to start properly after 30 seconds"
|
||||||
|
$COMPOSE_CMD logs backend
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
if ! $COMPOSE_CMD --profile full up; then
|
||||||
echo "Error: Failed to start containers. Check Docker logs with '$COMPOSE_CMD logs'."
|
echo "Error: Failed to start containers. Check Docker logs with '$COMPOSE_CMD logs'."
|
||||||
echo "Possible fixes: Run with sudo or ensure port 8080 is free."
|
echo "Possible fixes: Run with sudo or ensure port 8080 is free."
|
||||||
exit 1
|
exit 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if ! $COMPOSE_CMD --profile core up; then
|
||||||
|
echo "Error: Failed to start containers. Check Docker logs with '$COMPOSE_CMD logs'."
|
||||||
|
echo "Possible fixes: Run with sudo or ensure port 8080 is free."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
sleep 10
|
sleep 10
|
||||||
@@ -1,20 +1,37 @@
|
|||||||
import unittest
|
import unittest
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) # Add project root to Python path
|
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) # Add project root to Python path
|
||||||
|
|
||||||
|
# Mock heavy dependencies
|
||||||
|
for mod_name in [
|
||||||
|
'torch', 'transformers', 'kokoro', 'adaptive_classifier', 'text2emotion',
|
||||||
|
'ollama', 'openai', 'together', 'IPython', 'IPython.display',
|
||||||
|
'playsound3', 'soundfile', 'pyaudio', 'librosa',
|
||||||
|
'pypdf', 'langid', 'pypinyin', 'fake_useragent',
|
||||||
|
'chromedriver_autoinstaller', 'num2words', 'sentencepiece', 'sacremoses',
|
||||||
|
'scipy', 'numpy', 'selenium_stealth', 'undetected_chromedriver',
|
||||||
|
'markdownify',
|
||||||
|
]:
|
||||||
|
if mod_name not in sys.modules:
|
||||||
|
sys.modules[mod_name] = MagicMock()
|
||||||
|
|
||||||
|
os.environ.setdefault('WORK_DIR', '/tmp')
|
||||||
|
|
||||||
from sources.agents.browser_agent import BrowserAgent
|
from sources.agents.browser_agent import BrowserAgent
|
||||||
|
|
||||||
class TestBrowserAgentParsing(unittest.TestCase):
|
class TestBrowserAgentParsing(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
# Initialize a basic BrowserAgent instance for testing
|
self.agent = BrowserAgent.__new__(BrowserAgent)
|
||||||
self.agent = BrowserAgent(
|
self.agent.notes = []
|
||||||
name="TestAgent",
|
self.agent.navigable_links = []
|
||||||
prompt_path="../prompts/base/browser_agent.txt",
|
self.agent.search_history = []
|
||||||
provider=None
|
self.agent.current_page = ""
|
||||||
)
|
self.agent.logger = MagicMock()
|
||||||
|
|
||||||
def test_extract_links(self):
|
def test_extract_links(self):
|
||||||
# Test various link formats
|
|
||||||
test_text = """
|
test_text = """
|
||||||
Check this out: https://thriveonai.com/15-ai-startups-in-japan-to-take-note-of, and www.google.com!
|
Check this out: https://thriveonai.com/15-ai-startups-in-japan-to-take-note-of, and www.google.com!
|
||||||
Also try https://test.org/about?page=1, hey this one as well bro https://weatherstack.com/documentation/.
|
Also try https://test.org/about?page=1, hey this one as well bro https://weatherstack.com/documentation/.
|
||||||
@@ -23,13 +40,22 @@ class TestBrowserAgentParsing(unittest.TestCase):
|
|||||||
"https://thriveonai.com/15-ai-startups-in-japan-to-take-note-of",
|
"https://thriveonai.com/15-ai-startups-in-japan-to-take-note-of",
|
||||||
"www.google.com",
|
"www.google.com",
|
||||||
"https://test.org/about?page=1",
|
"https://test.org/about?page=1",
|
||||||
"https://weatherstack.com/documentation",
|
"https://weatherstack.com/documentation"
|
||||||
]
|
]
|
||||||
result = self.agent.extract_links(test_text)
|
result = self.agent.extract_links(test_text)
|
||||||
self.assertEqual(result, expected)
|
self.assertEqual(result, expected)
|
||||||
|
|
||||||
|
def test_extract_links_no_links(self):
|
||||||
|
"""Test that text without links returns empty list."""
|
||||||
|
result = self.agent.extract_links("No links here at all.")
|
||||||
|
self.assertEqual(result, [])
|
||||||
|
|
||||||
|
def test_extract_links_single_link(self):
|
||||||
|
"""Test extraction of a single link."""
|
||||||
|
result = self.agent.extract_links("Visit https://example.com for details")
|
||||||
|
self.assertEqual(result, ["https://example.com"])
|
||||||
|
|
||||||
def test_extract_form(self):
|
def test_extract_form(self):
|
||||||
# Test form extraction
|
|
||||||
test_text = """
|
test_text = """
|
||||||
Fill this: [username](john) and [password](secret123)
|
Fill this: [username](john) and [password](secret123)
|
||||||
Not a form: [random]text
|
Not a form: [random]text
|
||||||
@@ -38,8 +64,18 @@ class TestBrowserAgentParsing(unittest.TestCase):
|
|||||||
result = self.agent.extract_form(test_text)
|
result = self.agent.extract_form(test_text)
|
||||||
self.assertEqual(result, expected)
|
self.assertEqual(result, expected)
|
||||||
|
|
||||||
|
def test_extract_form_empty(self):
|
||||||
|
"""Test form extraction with no form inputs."""
|
||||||
|
result = self.agent.extract_form("Just regular text here.")
|
||||||
|
self.assertEqual(result, [])
|
||||||
|
|
||||||
|
def test_extract_form_checkbox(self):
|
||||||
|
"""Test form extraction with checkbox values."""
|
||||||
|
text = "[agree](checked) and [newsletter](unchecked)"
|
||||||
|
result = self.agent.extract_form(text)
|
||||||
|
self.assertEqual(len(result), 2)
|
||||||
|
|
||||||
def test_clean_links(self):
|
def test_clean_links(self):
|
||||||
# Test link cleaning
|
|
||||||
test_links = [
|
test_links = [
|
||||||
"https://example.com.",
|
"https://example.com.",
|
||||||
"www.test.com,",
|
"www.test.com,",
|
||||||
@@ -55,8 +91,13 @@ class TestBrowserAgentParsing(unittest.TestCase):
|
|||||||
result = self.agent.clean_links(test_links)
|
result = self.agent.clean_links(test_links)
|
||||||
self.assertEqual(result, expected)
|
self.assertEqual(result, expected)
|
||||||
|
|
||||||
|
def test_clean_links_with_slash(self):
|
||||||
|
"""Test that trailing slash is stripped since it's not alphanumeric."""
|
||||||
|
links = ["https://example.com/path/"]
|
||||||
|
result = self.agent.clean_links(links)
|
||||||
|
self.assertEqual(result, ["https://example.com/path"])
|
||||||
|
|
||||||
def test_parse_answer(self):
|
def test_parse_answer(self):
|
||||||
# Test parsing answer with notes and links
|
|
||||||
test_text = """
|
test_text = """
|
||||||
Here's some info
|
Here's some info
|
||||||
Note: This is important. We are doing test it's very cool.
|
Note: This is important. We are doing test it's very cool.
|
||||||
@@ -66,5 +107,102 @@ class TestBrowserAgentParsing(unittest.TestCase):
|
|||||||
self.agent.parse_answer(test_text)
|
self.agent.parse_answer(test_text)
|
||||||
self.assertEqual(self.agent.notes[0], "Note: This is important. We are doing test it's very cool.")
|
self.assertEqual(self.agent.notes[0], "Note: This is important. We are doing test it's very cool.")
|
||||||
|
|
||||||
|
def test_parse_answer_extracts_links(self):
|
||||||
|
"""Test that parse_answer returns extracted links."""
|
||||||
|
text = "Navigate to https://example.com and https://test.org"
|
||||||
|
links = self.agent.parse_answer(text)
|
||||||
|
self.assertIn("https://example.com", links)
|
||||||
|
self.assertIn("https://test.org", links)
|
||||||
|
|
||||||
|
def test_parse_answer_no_notes(self):
|
||||||
|
"""Test parse_answer with no notes section."""
|
||||||
|
text = "Go to https://example.com"
|
||||||
|
self.agent.parse_answer(text)
|
||||||
|
# Notes should have an empty entry
|
||||||
|
self.assertEqual(len(self.agent.notes), 1)
|
||||||
|
|
||||||
|
def test_select_link_unvisited(self):
|
||||||
|
"""Test selecting first unvisited link."""
|
||||||
|
self.agent.search_history = ["https://visited.com"]
|
||||||
|
self.agent.current_page = "https://current.com"
|
||||||
|
links = ["https://visited.com", "https://current.com", "https://new.com"]
|
||||||
|
result = self.agent.select_link(links)
|
||||||
|
self.assertEqual(result, "https://new.com")
|
||||||
|
|
||||||
|
def test_select_link_all_visited(self):
|
||||||
|
"""Test that None is returned when all links are visited."""
|
||||||
|
self.agent.search_history = ["https://a.com", "https://b.com"]
|
||||||
|
self.agent.current_page = ""
|
||||||
|
links = ["https://a.com", "https://b.com"]
|
||||||
|
result = self.agent.select_link(links)
|
||||||
|
self.assertIsNone(result)
|
||||||
|
|
||||||
|
def test_select_link_empty(self):
|
||||||
|
"""Test with empty links list."""
|
||||||
|
result = self.agent.select_link([])
|
||||||
|
self.assertIsNone(result)
|
||||||
|
|
||||||
|
def test_jsonify_search_results(self):
|
||||||
|
"""Test parsing search result text into structured data."""
|
||||||
|
text = """Title: Result One
|
||||||
|
Snippet: First result snippet
|
||||||
|
Link: https://one.com
|
||||||
|
|
||||||
|
Title: Result Two
|
||||||
|
Snippet: Second result snippet
|
||||||
|
Link: https://two.com"""
|
||||||
|
results = self.agent.jsonify_search_results(text)
|
||||||
|
self.assertEqual(len(results), 2)
|
||||||
|
self.assertEqual(results[0]["title"], "Result One")
|
||||||
|
self.assertEqual(results[0]["link"], "https://one.com")
|
||||||
|
self.assertEqual(results[1]["snippet"], "Second result snippet")
|
||||||
|
|
||||||
|
def test_jsonify_search_results_empty(self):
|
||||||
|
"""Test with empty search results."""
|
||||||
|
results = self.agent.jsonify_search_results("")
|
||||||
|
self.assertEqual(results, [])
|
||||||
|
|
||||||
|
def test_jsonify_search_results_partial(self):
|
||||||
|
"""Test with partial result (only title and link)."""
|
||||||
|
text = """Title: Partial Result
|
||||||
|
Link: https://partial.com"""
|
||||||
|
results = self.agent.jsonify_search_results(text)
|
||||||
|
self.assertEqual(len(results), 1)
|
||||||
|
self.assertEqual(results[0]["title"], "Partial Result")
|
||||||
|
self.assertNotIn("snippet", results[0])
|
||||||
|
|
||||||
|
def test_stringify_search_results(self):
|
||||||
|
"""Test converting structured results back to string."""
|
||||||
|
results = [
|
||||||
|
{"link": "https://one.com", "snippet": "First snippet"},
|
||||||
|
{"link": "https://two.com", "snippet": "Second snippet"}
|
||||||
|
]
|
||||||
|
output = self.agent.stringify_search_results(results)
|
||||||
|
self.assertIn("https://one.com", output)
|
||||||
|
self.assertIn("First snippet", output)
|
||||||
|
self.assertIn("https://two.com", output)
|
||||||
|
|
||||||
|
def test_select_unvisited(self):
|
||||||
|
"""Test filtering visited results."""
|
||||||
|
self.agent.search_history = ["https://visited.com"]
|
||||||
|
results = [
|
||||||
|
{"link": "https://visited.com", "title": "Old"},
|
||||||
|
{"link": "https://new.com", "title": "New"}
|
||||||
|
]
|
||||||
|
unvisited = self.agent.select_unvisited(results)
|
||||||
|
self.assertEqual(len(unvisited), 1)
|
||||||
|
self.assertEqual(unvisited[0]["link"], "https://new.com")
|
||||||
|
|
||||||
|
def test_select_unvisited_all_new(self):
|
||||||
|
"""Test when no results are visited."""
|
||||||
|
self.agent.search_history = []
|
||||||
|
results = [
|
||||||
|
{"link": "https://a.com", "title": "A"},
|
||||||
|
{"link": "https://b.com", "title": "B"}
|
||||||
|
]
|
||||||
|
unvisited = self.agent.select_unvisited(results)
|
||||||
|
self.assertEqual(len(unvisited), 2)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
import unittest
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
|
||||||
|
|
||||||
|
from unittest.mock import patch, MagicMock
|
||||||
|
|
||||||
|
# Mock heavy dependencies
|
||||||
|
for mod_name in [
|
||||||
|
'torch', 'transformers', 'kokoro', 'adaptive_classifier', 'text2emotion',
|
||||||
|
'ollama', 'openai', 'together', 'IPython', 'IPython.display',
|
||||||
|
'playsound3', 'soundfile', 'pyaudio', 'librosa',
|
||||||
|
'pypdf', 'langid', 'pypinyin', 'fake_useragent',
|
||||||
|
'num2words', 'sentencepiece', 'sacremoses',
|
||||||
|
'scipy', 'numpy', 'selenium_stealth', 'undetected_chromedriver',
|
||||||
|
'markdownify', 'chromedriver_autoinstaller',
|
||||||
|
]:
|
||||||
|
if mod_name not in sys.modules:
|
||||||
|
sys.modules[mod_name] = MagicMock()
|
||||||
|
|
||||||
|
os.environ.setdefault('WORK_DIR', '/tmp')
|
||||||
|
|
||||||
|
from sources.browser import get_chromedriver_version, is_chromedriver_compatible
|
||||||
|
|
||||||
|
|
||||||
|
class TestChromedriverVersionCheck(unittest.TestCase):
|
||||||
|
"""Test suite for ChromeDriver version checking and auto-update logic."""
|
||||||
|
|
||||||
|
@patch('sources.browser.subprocess.run')
|
||||||
|
def test_get_chromedriver_version_success(self, mock_run):
|
||||||
|
"""Test extracting major version from chromedriver --version output."""
|
||||||
|
mock_run.return_value = MagicMock(
|
||||||
|
stdout="ChromeDriver 125.0.6422.78 (abc123)\n"
|
||||||
|
)
|
||||||
|
self.assertEqual(get_chromedriver_version("/usr/bin/chromedriver"), "125")
|
||||||
|
|
||||||
|
@patch('sources.browser.subprocess.run')
|
||||||
|
def test_get_chromedriver_version_failure(self, mock_run):
|
||||||
|
"""Test graceful failure when chromedriver --version fails."""
|
||||||
|
mock_run.side_effect = FileNotFoundError("not found")
|
||||||
|
self.assertEqual(get_chromedriver_version("/nonexistent"), "")
|
||||||
|
|
||||||
|
@patch('sources.browser.subprocess.run')
|
||||||
|
def test_get_chromedriver_version_timeout(self, mock_run):
|
||||||
|
"""Test graceful failure on timeout."""
|
||||||
|
import subprocess
|
||||||
|
mock_run.side_effect = subprocess.TimeoutExpired(cmd="chromedriver", timeout=10)
|
||||||
|
self.assertEqual(get_chromedriver_version("/usr/bin/chromedriver"), "")
|
||||||
|
|
||||||
|
@patch('sources.browser.chromedriver_autoinstaller.get_chrome_version')
|
||||||
|
@patch('sources.browser.get_chromedriver_version')
|
||||||
|
def test_compatible_versions(self, mock_driver_ver, mock_chrome_ver):
|
||||||
|
"""Test that matching major versions are compatible."""
|
||||||
|
mock_chrome_ver.return_value = "125.0.6422.78"
|
||||||
|
mock_driver_ver.return_value = "125"
|
||||||
|
self.assertTrue(is_chromedriver_compatible("/usr/bin/chromedriver"))
|
||||||
|
|
||||||
|
@patch('sources.browser.chromedriver_autoinstaller.get_chrome_version')
|
||||||
|
@patch('sources.browser.get_chromedriver_version')
|
||||||
|
def test_incompatible_versions(self, mock_driver_ver, mock_chrome_ver):
|
||||||
|
"""Test that mismatched major versions are incompatible."""
|
||||||
|
mock_chrome_ver.return_value = "126.0.6478.55"
|
||||||
|
mock_driver_ver.return_value = "125"
|
||||||
|
self.assertFalse(is_chromedriver_compatible("/usr/bin/chromedriver"))
|
||||||
|
|
||||||
|
@patch('sources.browser.chromedriver_autoinstaller.get_chrome_version')
|
||||||
|
def test_no_chrome_version_assumes_compatible(self, mock_chrome_ver):
|
||||||
|
"""Test that missing Chrome version defaults to compatible."""
|
||||||
|
mock_chrome_ver.return_value = None
|
||||||
|
self.assertTrue(is_chromedriver_compatible("/usr/bin/chromedriver"))
|
||||||
|
|
||||||
|
@patch('sources.browser.chromedriver_autoinstaller.get_chrome_version')
|
||||||
|
@patch('sources.browser.get_chromedriver_version')
|
||||||
|
def test_no_driver_version_assumes_compatible(self, mock_driver_ver, mock_chrome_ver):
|
||||||
|
"""Test that missing driver version defaults to compatible."""
|
||||||
|
mock_chrome_ver.return_value = "125.0.6422.78"
|
||||||
|
mock_driver_ver.return_value = ""
|
||||||
|
self.assertTrue(is_chromedriver_compatible("/usr/bin/chromedriver"))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
import unittest
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import shutil
|
||||||
|
import logging
|
||||||
|
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
|
||||||
|
|
||||||
|
from sources.logger import Logger
|
||||||
|
|
||||||
|
|
||||||
|
class TestLogger(unittest.TestCase):
|
||||||
|
"""Test suite for the Logger class."""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.logger = Logger("test_logger.log")
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
if os.path.exists('.logs'):
|
||||||
|
for handler in self.logger.logger.handlers[:]:
|
||||||
|
handler.close()
|
||||||
|
self.logger.logger.removeHandler(handler)
|
||||||
|
log_path = os.path.join('.logs', 'test_logger.log')
|
||||||
|
if os.path.exists(log_path):
|
||||||
|
os.remove(log_path)
|
||||||
|
|
||||||
|
def test_initialization(self):
|
||||||
|
"""Test logger initializes correctly."""
|
||||||
|
self.assertTrue(self.logger.enabled)
|
||||||
|
self.assertIsNotNone(self.logger.logger)
|
||||||
|
self.assertTrue(os.path.exists('.logs'))
|
||||||
|
|
||||||
|
def test_log_creates_file(self):
|
||||||
|
"""Test that logging creates a log file."""
|
||||||
|
self.logger.info("test message")
|
||||||
|
self.assertTrue(os.path.exists(self.logger.log_path))
|
||||||
|
|
||||||
|
def test_log_writes_message(self):
|
||||||
|
"""Test that log messages are written to file."""
|
||||||
|
self.logger.info("hello world")
|
||||||
|
with open(self.logger.log_path, 'r') as f:
|
||||||
|
content = f.read()
|
||||||
|
self.assertIn("hello world", content)
|
||||||
|
|
||||||
|
def test_log_deduplication(self):
|
||||||
|
"""Test that consecutive identical messages are not duplicated."""
|
||||||
|
self.logger.info("duplicate message")
|
||||||
|
self.logger.info("duplicate message")
|
||||||
|
with open(self.logger.log_path, 'r') as f:
|
||||||
|
content = f.read()
|
||||||
|
self.assertEqual(content.count("duplicate message"), 1)
|
||||||
|
|
||||||
|
def test_log_different_messages(self):
|
||||||
|
"""Test that different messages are all written."""
|
||||||
|
self.logger.info("message one")
|
||||||
|
self.logger.info("message two")
|
||||||
|
with open(self.logger.log_path, 'r') as f:
|
||||||
|
content = f.read()
|
||||||
|
self.assertIn("message one", content)
|
||||||
|
self.assertIn("message two", content)
|
||||||
|
|
||||||
|
def test_error_level(self):
|
||||||
|
"""Test error level logging."""
|
||||||
|
self.logger.error("error occurred")
|
||||||
|
with open(self.logger.log_path, 'r') as f:
|
||||||
|
content = f.read()
|
||||||
|
self.assertIn("ERROR", content)
|
||||||
|
self.assertIn("error occurred", content)
|
||||||
|
|
||||||
|
def test_warning_level(self):
|
||||||
|
"""Test warning level logging."""
|
||||||
|
self.logger.warning("warning issued")
|
||||||
|
with open(self.logger.log_path, 'r') as f:
|
||||||
|
content = f.read()
|
||||||
|
self.assertIn("WARNING", content)
|
||||||
|
self.assertIn("warning issued", content)
|
||||||
|
|
||||||
|
def test_create_folder(self):
|
||||||
|
"""Test folder creation."""
|
||||||
|
test_path = ".test_log_folder"
|
||||||
|
result = self.logger.create_folder(test_path)
|
||||||
|
self.assertTrue(result)
|
||||||
|
self.assertTrue(os.path.exists(test_path))
|
||||||
|
os.rmdir(test_path)
|
||||||
|
|
||||||
|
def test_create_folder_already_exists(self):
|
||||||
|
"""Test folder creation when folder already exists."""
|
||||||
|
result = self.logger.create_folder('.logs')
|
||||||
|
self.assertTrue(result)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
||||||
@@ -0,0 +1,232 @@
|
|||||||
|
import unittest
|
||||||
|
from unittest.mock import patch, MagicMock
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
|
||||||
|
|
||||||
|
from sources.llm_provider import Provider
|
||||||
|
|
||||||
|
|
||||||
|
class TestMiniMaxProvider(unittest.TestCase):
|
||||||
|
"""Test cases for MiniMax provider integration."""
|
||||||
|
|
||||||
|
def test_minimax_provider_registered(self):
|
||||||
|
"""Test that minimax provider is registered in available_providers."""
|
||||||
|
with patch.object(Provider, 'get_api_key', return_value='test-key'):
|
||||||
|
provider = Provider("minimax", "MiniMax-M2.5", is_local=False)
|
||||||
|
self.assertIn("minimax", provider.available_providers)
|
||||||
|
|
||||||
|
def test_minimax_in_unsafe_providers(self):
|
||||||
|
"""Test that minimax is in unsafe_providers list."""
|
||||||
|
with patch.object(Provider, 'get_api_key', return_value='test-key'):
|
||||||
|
provider = Provider("minimax", "MiniMax-M2.5", is_local=False)
|
||||||
|
self.assertIn("minimax", provider.unsafe_providers)
|
||||||
|
|
||||||
|
def test_minimax_api_key_required(self):
|
||||||
|
"""Test that API key is fetched for minimax provider."""
|
||||||
|
with patch.object(Provider, 'get_api_key', return_value='test-minimax-key') as mock_get_key:
|
||||||
|
provider = Provider("minimax", "MiniMax-M2.5", is_local=False)
|
||||||
|
mock_get_key.assert_called_with("minimax")
|
||||||
|
self.assertEqual(provider.api_key, 'test-minimax-key')
|
||||||
|
|
||||||
|
@patch('sources.llm_provider.OpenAI')
|
||||||
|
@patch.dict(os.environ, {'MINIMAX_API_KEY': 'test-key'})
|
||||||
|
def test_minimax_local_not_supported(self, mock_openai_class):
|
||||||
|
"""Test that minimax provider raises error when is_local=True."""
|
||||||
|
provider = Provider("minimax", "MiniMax-M2.5", is_local=True)
|
||||||
|
provider.api_key = 'test-key'
|
||||||
|
history = [{"role": "user", "content": "Hello"}]
|
||||||
|
with self.assertRaises(Exception) as context:
|
||||||
|
provider.minimax_fn(history)
|
||||||
|
self.assertIn("not available for local use", str(context.exception))
|
||||||
|
|
||||||
|
@patch('sources.llm_provider.OpenAI')
|
||||||
|
@patch.dict(os.environ, {'MINIMAX_API_KEY': 'test-key'})
|
||||||
|
def test_minimax_uses_correct_base_url(self, mock_openai_class):
|
||||||
|
"""Test that minimax provider uses correct base URL."""
|
||||||
|
mock_client = MagicMock()
|
||||||
|
mock_openai_class.return_value = mock_client
|
||||||
|
mock_response = MagicMock()
|
||||||
|
mock_response.choices = [MagicMock(message=MagicMock(content="Hello!"))]
|
||||||
|
mock_client.chat.completions.create.return_value = mock_response
|
||||||
|
|
||||||
|
with patch.object(Provider, 'get_api_key', return_value='test-key'):
|
||||||
|
provider = Provider("minimax", "MiniMax-M2.5", is_local=False)
|
||||||
|
history = [{"role": "user", "content": "Hello"}]
|
||||||
|
provider.minimax_fn(history)
|
||||||
|
|
||||||
|
mock_openai_class.assert_called_with(
|
||||||
|
api_key='test-key',
|
||||||
|
base_url='https://api.minimax.io/v1'
|
||||||
|
)
|
||||||
|
|
||||||
|
@patch('sources.llm_provider.OpenAI')
|
||||||
|
@patch.dict(os.environ, {
|
||||||
|
'MINIMAX_API_KEY': 'test-key',
|
||||||
|
'MINIMAX_BASE_URL': 'https://api.minimaxi.com/v1'
|
||||||
|
})
|
||||||
|
def test_minimax_custom_base_url(self, mock_openai_class):
|
||||||
|
"""Test that minimax provider uses custom base URL from env."""
|
||||||
|
mock_client = MagicMock()
|
||||||
|
mock_openai_class.return_value = mock_client
|
||||||
|
mock_response = MagicMock()
|
||||||
|
mock_response.choices = [MagicMock(message=MagicMock(content="Hello!"))]
|
||||||
|
mock_client.chat.completions.create.return_value = mock_response
|
||||||
|
|
||||||
|
with patch.object(Provider, 'get_api_key', return_value='test-key'):
|
||||||
|
provider = Provider("minimax", "MiniMax-M2.5", is_local=False)
|
||||||
|
history = [{"role": "user", "content": "Hello"}]
|
||||||
|
provider.minimax_fn(history)
|
||||||
|
|
||||||
|
mock_openai_class.assert_called_with(
|
||||||
|
api_key='test-key',
|
||||||
|
base_url='https://api.minimaxi.com/v1'
|
||||||
|
)
|
||||||
|
|
||||||
|
@patch('sources.llm_provider.OpenAI')
|
||||||
|
@patch.dict(os.environ, {'MINIMAX_API_KEY': 'test-key'})
|
||||||
|
def test_minimax_uses_temperature_one(self, mock_openai_class):
|
||||||
|
"""Test that minimax provider uses temperature=1.0."""
|
||||||
|
mock_client = MagicMock()
|
||||||
|
mock_openai_class.return_value = mock_client
|
||||||
|
mock_response = MagicMock()
|
||||||
|
mock_response.choices = [MagicMock(message=MagicMock(content="Hello!"))]
|
||||||
|
mock_client.chat.completions.create.return_value = mock_response
|
||||||
|
|
||||||
|
with patch.object(Provider, 'get_api_key', return_value='test-key'):
|
||||||
|
provider = Provider("minimax", "MiniMax-M2.5", is_local=False)
|
||||||
|
history = [{"role": "user", "content": "Hello"}]
|
||||||
|
provider.minimax_fn(history)
|
||||||
|
|
||||||
|
call_kwargs = mock_client.chat.completions.create.call_args[1]
|
||||||
|
self.assertEqual(call_kwargs['temperature'], 1.0)
|
||||||
|
|
||||||
|
@patch('sources.llm_provider.OpenAI')
|
||||||
|
@patch.dict(os.environ, {'MINIMAX_API_KEY': 'test-key'})
|
||||||
|
def test_minimax_returns_response_content(self, mock_openai_class):
|
||||||
|
"""Test that minimax provider returns response content."""
|
||||||
|
mock_client = MagicMock()
|
||||||
|
mock_openai_class.return_value = mock_client
|
||||||
|
mock_response = MagicMock()
|
||||||
|
mock_response.choices = [MagicMock(message=MagicMock(content="Test response"))]
|
||||||
|
mock_client.chat.completions.create.return_value = mock_response
|
||||||
|
|
||||||
|
with patch.object(Provider, 'get_api_key', return_value='test-key'):
|
||||||
|
provider = Provider("minimax", "MiniMax-M2.5", is_local=False)
|
||||||
|
history = [{"role": "user", "content": "Hello"}]
|
||||||
|
result = provider.minimax_fn(history)
|
||||||
|
|
||||||
|
self.assertEqual(result, "Test response")
|
||||||
|
|
||||||
|
@patch('sources.llm_provider.OpenAI')
|
||||||
|
@patch.dict(os.environ, {'MINIMAX_API_KEY': 'test-key'})
|
||||||
|
def test_minimax_handles_empty_response(self, mock_openai_class):
|
||||||
|
"""Test that minimax provider handles empty response."""
|
||||||
|
mock_client = MagicMock()
|
||||||
|
mock_openai_class.return_value = mock_client
|
||||||
|
mock_client.chat.completions.create.return_value = None
|
||||||
|
|
||||||
|
with patch.object(Provider, 'get_api_key', return_value='test-key'):
|
||||||
|
provider = Provider("minimax", "MiniMax-M2.5", is_local=False)
|
||||||
|
history = [{"role": "user", "content": "Hello"}]
|
||||||
|
|
||||||
|
with self.assertRaises(Exception) as context:
|
||||||
|
provider.minimax_fn(history)
|
||||||
|
self.assertIn("response is empty", str(context.exception))
|
||||||
|
|
||||||
|
@patch('sources.llm_provider.OpenAI')
|
||||||
|
@patch.dict(os.environ, {'MINIMAX_API_KEY': 'test-key'})
|
||||||
|
def test_minimax_handles_api_error(self, mock_openai_class):
|
||||||
|
"""Test that minimax provider handles API errors."""
|
||||||
|
mock_client = MagicMock()
|
||||||
|
mock_openai_class.return_value = mock_client
|
||||||
|
mock_client.chat.completions.create.side_effect = Exception("API rate limit exceeded")
|
||||||
|
|
||||||
|
with patch.object(Provider, 'get_api_key', return_value='test-key'):
|
||||||
|
provider = Provider("minimax", "MiniMax-M2.5", is_local=False)
|
||||||
|
history = [{"role": "user", "content": "Hello"}]
|
||||||
|
|
||||||
|
with self.assertRaises(Exception) as context:
|
||||||
|
provider.minimax_fn(history)
|
||||||
|
self.assertIn("MiniMax API error", str(context.exception))
|
||||||
|
|
||||||
|
|
||||||
|
class TestMiniMaxProviderModels(unittest.TestCase):
|
||||||
|
"""Test cases for MiniMax provider model configurations."""
|
||||||
|
|
||||||
|
@patch('sources.llm_provider.OpenAI')
|
||||||
|
@patch.dict(os.environ, {'MINIMAX_API_KEY': 'test-key'})
|
||||||
|
def test_minimax_m27_model(self, mock_openai_class):
|
||||||
|
"""Test MiniMax-M2.7 model."""
|
||||||
|
mock_client = MagicMock()
|
||||||
|
mock_openai_class.return_value = mock_client
|
||||||
|
mock_response = MagicMock()
|
||||||
|
mock_response.choices = [MagicMock(message=MagicMock(content="Response"))]
|
||||||
|
mock_client.chat.completions.create.return_value = mock_response
|
||||||
|
|
||||||
|
with patch.object(Provider, 'get_api_key', return_value='test-key'):
|
||||||
|
provider = Provider("minimax", "MiniMax-M2.7", is_local=False)
|
||||||
|
history = [{"role": "user", "content": "Hello"}]
|
||||||
|
provider.minimax_fn(history)
|
||||||
|
|
||||||
|
call_kwargs = mock_client.chat.completions.create.call_args[1]
|
||||||
|
self.assertEqual(call_kwargs['model'], "MiniMax-M2.7")
|
||||||
|
|
||||||
|
@patch('sources.llm_provider.OpenAI')
|
||||||
|
@patch.dict(os.environ, {'MINIMAX_API_KEY': 'test-key'})
|
||||||
|
def test_minimax_m27_highspeed_model(self, mock_openai_class):
|
||||||
|
"""Test MiniMax-M2.7-highspeed model."""
|
||||||
|
mock_client = MagicMock()
|
||||||
|
mock_openai_class.return_value = mock_client
|
||||||
|
mock_response = MagicMock()
|
||||||
|
mock_response.choices = [MagicMock(message=MagicMock(content="Response"))]
|
||||||
|
mock_client.chat.completions.create.return_value = mock_response
|
||||||
|
|
||||||
|
with patch.object(Provider, 'get_api_key', return_value='test-key'):
|
||||||
|
provider = Provider("minimax", "MiniMax-M2.7-highspeed", is_local=False)
|
||||||
|
history = [{"role": "user", "content": "Hello"}]
|
||||||
|
provider.minimax_fn(history)
|
||||||
|
|
||||||
|
call_kwargs = mock_client.chat.completions.create.call_args[1]
|
||||||
|
self.assertEqual(call_kwargs['model'], "MiniMax-M2.7-highspeed")
|
||||||
|
|
||||||
|
@patch('sources.llm_provider.OpenAI')
|
||||||
|
@patch.dict(os.environ, {'MINIMAX_API_KEY': 'test-key'})
|
||||||
|
def test_minimax_m25_model(self, mock_openai_class):
|
||||||
|
"""Test MiniMax-M2.5 model."""
|
||||||
|
mock_client = MagicMock()
|
||||||
|
mock_openai_class.return_value = mock_client
|
||||||
|
mock_response = MagicMock()
|
||||||
|
mock_response.choices = [MagicMock(message=MagicMock(content="Response"))]
|
||||||
|
mock_client.chat.completions.create.return_value = mock_response
|
||||||
|
|
||||||
|
with patch.object(Provider, 'get_api_key', return_value='test-key'):
|
||||||
|
provider = Provider("minimax", "MiniMax-M2.5", is_local=False)
|
||||||
|
history = [{"role": "user", "content": "Hello"}]
|
||||||
|
provider.minimax_fn(history)
|
||||||
|
|
||||||
|
call_kwargs = mock_client.chat.completions.create.call_args[1]
|
||||||
|
self.assertEqual(call_kwargs['model'], "MiniMax-M2.5")
|
||||||
|
|
||||||
|
@patch('sources.llm_provider.OpenAI')
|
||||||
|
@patch.dict(os.environ, {'MINIMAX_API_KEY': 'test-key'})
|
||||||
|
def test_minimax_m25_highspeed_model(self, mock_openai_class):
|
||||||
|
"""Test MiniMax-M2.5-highspeed model."""
|
||||||
|
mock_client = MagicMock()
|
||||||
|
mock_openai_class.return_value = mock_client
|
||||||
|
mock_response = MagicMock()
|
||||||
|
mock_response.choices = [MagicMock(message=MagicMock(content="Response"))]
|
||||||
|
mock_client.chat.completions.create.return_value = mock_response
|
||||||
|
|
||||||
|
with patch.object(Provider, 'get_api_key', return_value='test-key'):
|
||||||
|
provider = Provider("minimax", "MiniMax-M2.5-highspeed", is_local=False)
|
||||||
|
history = [{"role": "user", "content": "Hello"}]
|
||||||
|
provider.minimax_fn(history)
|
||||||
|
|
||||||
|
call_kwargs = mock_client.chat.completions.create.call_args[1]
|
||||||
|
self.assertEqual(call_kwargs['model'], "MiniMax-M2.5-highspeed")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
import unittest
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
|
||||||
|
|
||||||
|
from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
|
# Mock heavy dependencies to allow import without installing them all
|
||||||
|
for mod_name in [
|
||||||
|
'torch', 'transformers', 'kokoro', 'adaptive_classifier', 'text2emotion',
|
||||||
|
'ollama', 'openai', 'together', 'IPython', 'IPython.display',
|
||||||
|
'playsound3', 'soundfile', 'pyaudio', 'librosa',
|
||||||
|
'pypdf', 'langid', 'pypinyin', 'fake_useragent',
|
||||||
|
'chromedriver_autoinstaller', 'num2words', 'sentencepiece', 'sacremoses',
|
||||||
|
'scipy', 'numpy', 'selenium_stealth', 'undetected_chromedriver',
|
||||||
|
'markdownify',
|
||||||
|
]:
|
||||||
|
if mod_name not in sys.modules:
|
||||||
|
sys.modules[mod_name] = MagicMock()
|
||||||
|
|
||||||
|
os.environ.setdefault('WORK_DIR', '/tmp')
|
||||||
|
|
||||||
|
from sources.agents.planner_agent import PlannerAgent
|
||||||
|
|
||||||
|
|
||||||
|
class TestPlannerAgentParsing(unittest.TestCase):
|
||||||
|
"""Test suite for PlannerAgent.parse_agent_tasks JSON parsing robustness."""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.agent = PlannerAgent.__new__(PlannerAgent)
|
||||||
|
self.agent.tools = {"json": MagicMock()}
|
||||||
|
self.agent.tools["json"].tag = "json"
|
||||||
|
self.agent.logger = MagicMock()
|
||||||
|
self.agent.agents = {
|
||||||
|
"coder": MagicMock(),
|
||||||
|
"file": MagicMock(),
|
||||||
|
"web": MagicMock(),
|
||||||
|
"casual": MagicMock()
|
||||||
|
}
|
||||||
|
|
||||||
|
def test_parse_valid_json(self):
|
||||||
|
"""Test that valid JSON plan is parsed correctly."""
|
||||||
|
valid_json = '{"plan": [{"agent": "web", "id": "1", "task": "Search info", "need": []}]}'
|
||||||
|
self.agent.tools["json"].load_exec_block.return_value = ([valid_json], None)
|
||||||
|
|
||||||
|
with patch.object(self.agent, 'get_task_names', return_value=["Task 1: Search info"]):
|
||||||
|
result = self.agent.parse_agent_tasks("dummy text")
|
||||||
|
|
||||||
|
self.assertEqual(len(result), 1)
|
||||||
|
self.assertEqual(result[0][1]['agent'], 'web')
|
||||||
|
|
||||||
|
def test_parse_malformed_json_returns_empty(self):
|
||||||
|
"""Test that malformed JSON returns empty list instead of crashing."""
|
||||||
|
malformed_json = '{"plan": [{"agent": "web", "id": "1" "task": "missing comma"}]}'
|
||||||
|
self.agent.tools["json"].load_exec_block.return_value = ([malformed_json], None)
|
||||||
|
|
||||||
|
with patch.object(self.agent, 'get_task_names', return_value=[]):
|
||||||
|
result = self.agent.parse_agent_tasks("dummy text")
|
||||||
|
|
||||||
|
self.assertEqual(result, [])
|
||||||
|
self.agent.logger.warning.assert_called_once()
|
||||||
|
|
||||||
|
def test_parse_truncated_json_returns_empty(self):
|
||||||
|
"""Test that truncated JSON returns empty list instead of crashing."""
|
||||||
|
truncated_json = '{"plan": [{"agent": "web", "id": "1", "task": "Search'
|
||||||
|
self.agent.tools["json"].load_exec_block.return_value = ([truncated_json], None)
|
||||||
|
|
||||||
|
with patch.object(self.agent, 'get_task_names', return_value=[]):
|
||||||
|
result = self.agent.parse_agent_tasks("dummy text")
|
||||||
|
|
||||||
|
self.assertEqual(result, [])
|
||||||
|
self.agent.logger.warning.assert_called_once()
|
||||||
|
|
||||||
|
def test_parse_no_blocks_returns_empty(self):
|
||||||
|
"""Test that missing blocks returns empty list."""
|
||||||
|
self.agent.tools["json"].load_exec_block.return_value = (None, None)
|
||||||
|
|
||||||
|
with patch.object(self.agent, 'get_task_names', return_value=[]):
|
||||||
|
result = self.agent.parse_agent_tasks("no json here")
|
||||||
|
|
||||||
|
self.assertEqual(result, [])
|
||||||
|
|
||||||
|
def test_parse_invalid_agent_returns_empty(self):
|
||||||
|
"""Test that an unknown agent name returns empty list."""
|
||||||
|
valid_json = '{"plan": [{"agent": "unknown_agent", "id": "1", "task": "Do something", "need": []}]}'
|
||||||
|
self.agent.tools["json"].load_exec_block.return_value = ([valid_json], None)
|
||||||
|
|
||||||
|
with patch.object(self.agent, 'get_task_names', return_value=["Task 1"]):
|
||||||
|
result = self.agent.parse_agent_tasks("dummy text")
|
||||||
|
|
||||||
|
self.assertEqual(result, [])
|
||||||
|
|
||||||
|
def test_parse_multiple_tasks(self):
|
||||||
|
"""Test parsing a plan with multiple tasks."""
|
||||||
|
multi_task_json = '{"plan": [{"agent": "web", "id": "1", "task": "Search", "need": []}, {"agent": "coder", "id": "2", "task": "Code it", "need": ["1"]}]}'
|
||||||
|
self.agent.tools["json"].load_exec_block.return_value = ([multi_task_json], None)
|
||||||
|
|
||||||
|
with patch.object(self.agent, 'get_task_names', return_value=["Task 1: Search", "Task 2: Code it"]):
|
||||||
|
result = self.agent.parse_agent_tasks("dummy text")
|
||||||
|
|
||||||
|
self.assertEqual(len(result), 2)
|
||||||
|
self.assertEqual(result[0][1]['agent'], 'web')
|
||||||
|
self.assertEqual(result[1][1]['agent'], 'coder')
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
import unittest
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
|
||||||
|
|
||||||
|
from sources.utility import get_color_map
|
||||||
|
|
||||||
|
|
||||||
|
class TestUtility(unittest.TestCase):
|
||||||
|
"""Test suite for utility module functions."""
|
||||||
|
|
||||||
|
def test_get_color_map_returns_dict(self):
|
||||||
|
"""Test that get_color_map returns a dictionary."""
|
||||||
|
color_map = get_color_map()
|
||||||
|
self.assertIsInstance(color_map, dict)
|
||||||
|
|
||||||
|
def test_get_color_map_has_required_keys(self):
|
||||||
|
"""Test that color map contains all required color keys."""
|
||||||
|
color_map = get_color_map()
|
||||||
|
required_keys = ["success", "failure", "status", "code", "warning", "output", "info"]
|
||||||
|
for key in required_keys:
|
||||||
|
self.assertIn(key, color_map, f"Missing key: {key}")
|
||||||
|
|
||||||
|
def test_get_color_map_values_are_strings(self):
|
||||||
|
"""Test that all color values are strings."""
|
||||||
|
color_map = get_color_map()
|
||||||
|
for key, value in color_map.items():
|
||||||
|
self.assertIsInstance(value, str, f"Value for '{key}' should be a string")
|
||||||
|
|
||||||
|
def test_success_is_green(self):
|
||||||
|
"""Test that success maps to green."""
|
||||||
|
color_map = get_color_map()
|
||||||
|
self.assertEqual(color_map["success"], "green")
|
||||||
|
|
||||||
|
def test_failure_is_red(self):
|
||||||
|
"""Test that failure maps to red."""
|
||||||
|
color_map = get_color_map()
|
||||||
|
self.assertEqual(color_map["failure"], "red")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
||||||