Skip to content

Commit 9f9de1a

Browse files
committed
feat (backend): added conditional llm logic
1 parent c7658f8 commit 9f9de1a

14 files changed

Lines changed: 172 additions & 230 deletions

File tree

src/server/.env.selfhost.template

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,11 @@ CELERY_BROKER_URL=redis://:${REDIS_PASSWORD}@redis:6379/0
2525
CELERY_RESULT_BACKEND=redis://:${REDIS_PASSWORD}@redis:6379/0
2626

2727
# --- LLM Provider Configuration ---
28-
# Set LLM_PROVIDER to "NOVITA" to use the Novita API, or "OLLAMA" to use a local Ollama instance.
29-
# If using OLLAMA, ensure it's running and accessible from Docker.
30-
LLM_PROVIDER=NOVITA
31-
NOVITA_API_KEY=<your-novita-api-key>
32-
NOVITA_MODEL_NAME=qwen/qwen3-4b-fp8 # The model to use from Novita
28+
# Standard OpenAI API compatible endpoint configuration.
29+
# For local Ollama, use the following. The Docker container will run Ollama internally.
30+
OPENAI_API_BASE_URL=http://localhost:11434
31+
OPENAI_MODEL_NAME=qwen2:1.5b
32+
OPENAI_API_KEY=ollama
3333

3434
# --- 3rd Party API Keys (Optional but recommended for full functionality) ---
3535
POSTHOG_API_KEY=<your_posthog_project_api_key>

src/server/.env.template

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,12 @@ CELERY_RESULT_BACKEND="redis://:<url_encoded_password>@<host>:<port>/0"
3131
# --- 3rd Party APIs ---
3232
POSTHOG_API_KEY=<your_posthog_project_api_key>
3333
POSTHOG_HOST=<your_posthog_host>
34-
NOVITA_API_KEY=<your-novita-api-key>
34+
# --- OpenAI Compatible LLM Configuration ---
35+
# You can point this to any OpenAI API compatible service, including OpenAI,
36+
# Groq, Together, or a self-hosted vLLM or Ollama instance.
37+
OPENAI_API_BASE_URL=https://api.openai.com/v1
38+
OPENAI_API_KEY=<your_openai_api_key>
39+
OPENAI_MODEL_NAME=gpt-4o-mini
3540
NEWS_API_KEY=<your-news-api-key>
3641
GOOGLE_CLIENT_ID=
3742
GOOGLE_PROJECT_ID=

src/server/Dockerfile

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
# Use a slim Python base image
22
FROM python:3.11-slim
33

4+
# --- Build Arguments ---
5+
# These arguments are passed from docker-compose.yaml or the `docker build` command.
6+
ARG OPENAI_API_KEY
7+
ARG OPENAI_MODEL_NAME
8+
49
# Set environment variables
510
ENV PYTHONDONTWRITEBYTECODE=1 \
611
PYTHONUNBUFFERED=1 \
@@ -9,6 +14,8 @@ ENV PYTHONDONTWRITEBYTECODE=1 \
914
CHROME_VERSION=138.0.7204.157 \
1015
CHROMEDRIVER_VERSION=138.0.7204.157 \
1116
CHROME_BIN=/opt/chrome/chrome
17+
# Set Ollama to listen on all interfaces within the container
18+
ENV OLLAMA_HOST=0.0.0.0
1219

1320
# Install system dependencies
1421
RUN apt-get update && apt-get install -y --no-install-recommends \
@@ -45,6 +52,18 @@ RUN wget -q -O /tmp/chromedriver.zip "https://storage.googleapis.com/chrome-for-
4552
&& chmod +x /usr/local/bin/chromedriver \
4653
&& rm -f /tmp/chromedriver.zip
4754

55+
# --- Conditional Ollama Installation ---
56+
# This checks if the API key is set to 'ollama'. If so, install Ollama and pull the specified model.
57+
# This is checked during the 'docker build' process.
58+
RUN if [ "$OPENAI_API_KEY" = "ollama" ]; then \
59+
echo "Ollama API key detected. Installing Ollama..."; \
60+
curl -fsSL https://ollama.com/install.sh | sh; \
61+
echo "Pulling model: ${OPENAI_MODEL_NAME}"; \
62+
/usr/local/bin/ollama pull ${OPENAI_MODEL_NAME}; \
63+
else \
64+
echo "Ollama configuration not detected. Skipping installation."; \
65+
fi
66+
4867
# Create required directories
4968
RUN mkdir -p /etc/supervisor/conf.d /var/log/supervisor
5069

src/server/docker-compose.yaml

Lines changed: 56 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -3,66 +3,68 @@
33
# This file defines how to run your application using Docker Compose.
44
# It now orchestrates the main application, Redis, and the WAHA service.
55

6-
version: "3.8"
7-
86
services:
9-
# 1. The Main Sentient Application (FastAPI, Nginx, Celery, MCPs)
10-
sentient-app:
11-
build:
12-
context: .
13-
dockerfile: Dockerfile
14-
container_name: sentient-app
15-
restart: unless-stopped
16-
env_file:
17-
- ./.env
18-
ports:
19-
# Exposes Nginx (running on port 80 in the container) to port 5000 on the host.
20-
# This is the primary public-facing port for your entire application.
21-
- "5000:80"
22-
depends_on:
23-
- redis
24-
- waha
25-
networks:
26-
- sentient_network
7+
# 1. The Main Sentient Application (FastAPI, Nginx, Celery, MCPs)
8+
sentient-app:
9+
build:
10+
context: .
11+
dockerfile: Dockerfile
12+
# Pass variables from the .env file as build arguments for conditional builds
13+
args:
14+
- OPENAI_API_KEY=${OPENAI_API_KEY}
15+
- OPENAI_MODEL_NAME=${OPENAI_MODEL_NAME}
16+
container_name: sentient-app
17+
restart: unless-stopped
18+
env_file:
19+
- ./.env
20+
ports:
21+
# Exposes Nginx (running on port 80 in the container) to port 5000 on the host.
22+
# This is the primary public-facing port for your entire application.
23+
- "5000:80"
24+
depends_on:
25+
- redis
26+
- waha
27+
networks:
28+
- sentient_network
2729

28-
# 2. Redis for Celery Broker & Backend
29-
redis:
30-
image: redis:7-alpine
31-
container_name: redis-db
32-
restart: unless-stopped
33-
# Use the password from the .env file, ensuring it's quoted
34-
command: redis-server --requirepass "${REDIS_PASSWORD}"
35-
volumes:
36-
# Use a named volume for persistent Redis data
37-
- redis-data:/data
38-
networks:
39-
- sentient_network
40-
env_file:
41-
- ./.env
42-
# No ports are exposed to the host. Communication is internal and secure.
30+
# 2. Redis for Celery Broker & Backend
31+
redis:
32+
image: redis:7-alpine
33+
container_name: redis-db
34+
restart: unless-stopped
35+
# Use the password from the .env file, ensuring it's quoted
36+
command: redis-server --requirepass "${REDIS_PASSWORD}"
37+
volumes:
38+
# Use a named volume for persistent Redis data
39+
- redis-data:/data
40+
networks:
41+
- sentient_network
42+
env_file:
43+
- ./.env
44+
# No ports are exposed to the host. Communication is internal and secure.
4345

44-
# 3. WAHA (WhatsApp HTTP API) Service
45-
waha:
46-
image: devlikeapro/waha:noweb
47-
container_name: waha-notifier
48-
restart: unless-stopped
49-
volumes:
50-
# Use a named volume for persistent WhatsApp session data
51-
- waha-sessions:/app/.sessions
52-
networks:
53-
- sentient_network
54-
env_file:
55-
- ./.env
56-
ports:
57-
- "8000:3000"
58-
# No ports are exposed to the host. The sentient-app communicates with it internally.
46+
# 3. WAHA (WhatsApp HTTP API) Service
47+
waha:
48+
image: devlikeapro/waha:noweb
49+
container_name: waha-notifier
50+
restart: unless-stopped
51+
volumes:
52+
# Use a named volume for persistent WhatsApp session data
53+
- waha-sessions:/app/.sessions
54+
networks:
55+
- sentient_network
56+
env_file:
57+
- ./.env
58+
ports:
59+
- "8000:3000"
60+
# No ports are exposed to the host. The sentient-app communicates with it internally.
5961

6062
# Defines the shared network for all services
6163
networks:
62-
sentient_network:
63-
driver: bridge
64+
sentient_network:
65+
driver: bridge
6466

6567
# Defines the named volumes for data persistence
6668
volumes:
67-
redis-data:
68-
waha-sessions:
69+
redis-data:
70+
waha-sessions:

src/server/main/config.py

Lines changed: 8 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -315,31 +315,19 @@
315315
}
316316
}
317317

318-
# --- Service Provider Configuration ---
319-
# These variables allow for flexible switching between different service providers.
320-
LLM_PROVIDER = os.getenv("LLM_PROVIDER", "OLLAMA") # Options: "OLLAMA", "NOVITA"
321-
# STT_PROVIDER = os.getenv("STT_PROVIDER", "FASTER_WHISPER") # Voice removed
322-
# TTS_PROVIDER = os.getenv("TTS_PROVIDER", "ORPHEUS") # Voice removed
323-
324-
# --- Service-Specific API Keys and Paths ---
325-
# ELEVENLABS_API_KEY = os.getenv("ELEVENLABS_API_KEY") # Voice removed
326-
# ELEVENLABS_VOICE_ID = os.getenv("ELEVENLABS_VOICE_ID", "JBFqnCBsd6RMkjVDRZzb") # Voice removed
327-
328-
# Voice related model paths and settings removed
329-
330-
331318
# LLM Endpoint Configuration
332-
OLLAMA_BASE_URL = os.getenv("OLLAMA_BASE_URL", "http://localhost:11434")
333-
OLLAMA_MODEL_NAME = os.getenv("OLLAMA_MODEL_NAME", "qwen3:4b")
334-
NOVITA_API_KEY = os.getenv("NOVITA_API_KEY")
335-
NOVITA_MODEL_NAME = os.getenv("NOVITA_MODEL_NAME", "qwen/qwen3-4b-fp8")
319+
# --- OpenAI API Standard Configuration ---
320+
# This can point to OpenAI, Ollama, Groq, or any other compatible service.
321+
OPENAI_API_BASE_URL = os.getenv("OPENAI_API_BASE_URL", "http://localhost:11434")
322+
OPENAI_MODEL_NAME = os.getenv("OPENAI_MODEL_NAME", "qwen2:1.5b")
323+
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY", "ollama") # Default key for Ollama
324+
336325
# MCP Server URLs
337326
PROGRESS_UPDATER_MCP_SERVER_URL=os.getenv("PROGRESS_UPDATER_MCP_SERVER_URL", "http://localhost:9011/sse")
338327
CHAT_TOOLS_MCP_SERVER_URL=os.getenv("CHAT_TOOLS_MCP_SERVER_URL", "http://localhost:9013/sse") # For agent action handoff
339328
SUPERMEMORY_MCP_BASE_URL = os.getenv("SUPERMEMORY_MCP_BASE_URL", "https://mcp.supermemory.ai/")
340329
SUPERMEMORY_MCP_ENDPOINT_SUFFIX = os.getenv("SUPERMEMORY_MCP_ENDPOINT_SUFFIX", "/sse")
341330

342331
print(f"[{datetime.datetime.now()}] [MainServer_Config] Configuration loaded. AUTH0_DOMAIN: {'SET' if AUTH0_DOMAIN else 'NOT SET'}")
343-
print(f"[{datetime.datetime.now()}] [MainServer_Config] LLM Provider: {LLM_PROVIDER}")
344-
# print(f"[{datetime.datetime.now()}] [MainServer_Config] STT Provider: {STT_PROVIDER}") # Voice removed
345-
# print(f"[{datetime.datetime.now()}] [MainServer_Config] TTS Provider: {TTS_PROVIDER}") # Voice removed
332+
print(f"[{datetime.datetime.now()}] [MainServer_Config] LLM Endpoint: {OPENAI_API_BASE_URL}")
333+
print(f"[{datetime.datetime.now()}] [MainServer_Config] LLM Model: {OPENAI_MODEL_NAME}")

src/server/main/llm.py

Lines changed: 12 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,8 @@
44
from qwen_agent.agents import Assistant
55
from qwen_agent.llm import get_chat_model
66

7-
from main.config import (
8-
LLM_PROVIDER,
9-
OLLAMA_MODEL_NAME, OLLAMA_BASE_URL,
10-
NOVITA_MODEL_NAME, NOVITA_API_KEY
11-
)
7+
from main.config import (OPENAI_API_BASE_URL, OPENAI_API_KEY,
8+
OPENAI_MODEL_NAME)
129

1310
logger = logging.getLogger(__name__)
1411

@@ -18,42 +15,20 @@ def get_qwen_assistant(system_message: str = DEFAULT_SYSTEM_PROMPT, function_lis
1815
"""
1916
Initializes and returns a Qwen Assistant agent configured for the current environment.
2017
"""
21-
llm_cfg = {}
22-
if LLM_PROVIDER == "OLLAMA":
23-
# Ollama configuration for Qwen Agent (expects OpenAI-compatible v1 endpoint)
24-
# Ensure OLLAMA_BASE_URL is like "http://localhost:11434"
25-
ollama_v1_url = f"{OLLAMA_BASE_URL.rstrip('/')}/v1"
26-
llm_cfg = {
27-
'model': OLLAMA_MODEL_NAME, # e.g., "llama3.2:3b"
28-
'model_server': ollama_v1_url,
29-
'api_key': 'ollama', # Placeholder, Ollama doesn't typically require a key via API
30-
}
31-
logger.info(f"Qwen Agent configured for LLM_PROVIDER='OLLAMA': model={OLLAMA_MODEL_NAME}, server={ollama_v1_url}")
32-
elif LLM_PROVIDER == "NOVITA":
33-
# Novita configuration for Qwen Agent
34-
novita_v1_url = "https://api.novita.ai/v3/openai"
35-
llm_cfg = {
36-
'model': NOVITA_MODEL_NAME,
37-
'model_server': novita_v1_url,
38-
'api_key': NOVITA_API_KEY,
39-
}
40-
# For Novita, some models might need specific routing prefixes if not handled by model_server directly
41-
# e.g. 'openrouter/meta-llama/llama-3.1-8b-instruct'
42-
# However, Qwen's OpenAI client usually takes the model name as passed.
43-
# The 'model_server' determines the endpoint.
44-
logger.info(f"Qwen Agent configured for LLM_PROVIDER='NOVITA': model={NOVITA_MODEL_NAME}, server={novita_v1_url}")
45-
else:
46-
logger.error(f"Invalid LLM_PROVIDER: '{LLM_PROVIDER}'. Must be 'OLLAMA' or 'NOVITA'.")
47-
raise ValueError(f"Invalid LLM_PROVIDER configured: {LLM_PROVIDER}")
48-
49-
if not llm_cfg.get('model'):
18+
# Qwen-agent's `Assistant` uses an OpenAI-compatible interface.
19+
# We map our standard environment variables to its expected config format.
20+
# Note: `model_server` for qwen-agent is the `base_url`.
21+
llm_cfg = {
22+
'model': OPENAI_MODEL_NAME,
23+
'model_server': f"{OPENAI_API_BASE_URL.rstrip('/')}/v1",
24+
'api_key': OPENAI_API_KEY,
25+
}
26+
logger.info(f"Qwen Agent configured with model='{OPENAI_MODEL_NAME}' and server='{llm_cfg['model_server']}'")
27+
if not OPENAI_MODEL_NAME:
5028
logger.error("LLM model name is not configured. Qwen Agent cannot be initialized.")
5129
raise ValueError("LLM model configuration error.")
5230

5331
try:
54-
# Initialize the LLM part of the agent
55-
# llm_instance = get_chat_model(llm_cfg) # Can also pass llm_cfg directly to Assistant
56-
5732
# Initialize the Assistant agent
5833
bot = Assistant(
5934
llm=llm_cfg,

src/server/supervisord.conf

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,17 @@
33
[supervisord]
44
nodaemon=true
55
user=root
6+
environment=DISPLAY=":99"
7+
8+
[program:xvfb]
9+
command=/usr/bin/Xvfb :99 -screen 0 1024x768x24
10+
priority=1
11+
autostart=true
12+
autorestart=true
13+
stdout_logfile=/dev/stdout
14+
stdout_logfile_maxbytes=0
15+
stderr_logfile=/dev/stderr
16+
stderr_logfile_maxbytes=0
617

718
[program:nginx]
819
command=/usr/sbin/nginx -g "daemon off;"
@@ -14,6 +25,17 @@ stdout_logfile_maxbytes=0
1425
stderr_logfile=/dev/stderr
1526
stderr_logfile_maxbytes=0
1627

28+
[program:ollama]
29+
command=/bin/sh -c "if [ -f /usr/local/bin/ollama ]; then /usr/local/bin/ollama serve; else echo Ollama not installed, skipping... && tail -f /dev/null; fi"
30+
directory=/app
31+
autostart=true
32+
autorestart=true
33+
priority=5
34+
stdout_logfile=/dev/stdout
35+
stdout_logfile_maxbytes=0
36+
stderr_logfile=/dev/stderr
37+
stderr_logfile_maxbytes=0
38+
1739
[program:main-server]
1840
command=uvicorn main.app:app --host 0.0.0.0 --port 5000 --lifespan on
1941
directory=/app
@@ -235,18 +257,3 @@ stdout_logfile=/dev/stdout
235257
stdout_logfile_maxbytes=0
236258
stderr_logfile=/dev/stderr
237259
stderr_logfile_maxbytes=0
238-
239-
[supervisord]
240-
nodaemon=true
241-
user=root
242-
environment=DISPLAY=":99"
243-
244-
[program:xvfb]
245-
command=/usr/bin/Xvfb :99 -screen 0 1024x768x24
246-
priority=1
247-
autostart=true
248-
autorestart=true
249-
stdout_logfile=/dev/stdout
250-
stdout_logfile_maxbytes=0
251-
stderr_logfile=/dev/stderr
252-
stderr_logfile_maxbytes=0

src/server/workers/executor/config.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,10 @@
1313
MONGO_URI = os.getenv("MONGO_URI", "mongodb://localhost:27017/")
1414
MONGO_DB_NAME = os.getenv("MONGO_DB_NAME", "sentient_dev_db")
1515

16-
LLM_PROVIDER = os.getenv("LLM_PROVIDER", "OLLAMA")
17-
OLLAMA_BASE_URL = os.getenv("OLLAMA_BASE_URL", "http://localhost:11434")
18-
OLLAMA_MODEL_NAME = os.getenv("OLLAMA_MODEL_NAME", "qwen3:4b")
19-
NOVITA_API_KEY = os.getenv("NOVITA_API_KEY")
20-
NOVITA_MODEL_NAME = os.getenv("NOVITA_MODEL_NAME", "qwen/qwen3-4b-fp8")
21-
16+
# OpenAI API Standard Configuration
17+
OPENAI_API_BASE_URL = os.getenv("OPENAI_API_BASE_URL", "http://localhost:11434")
18+
OPENAI_MODEL_NAME = os.getenv("OPENAI_MODEL_NAME", "qwen2:1.5b")
19+
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY", "ollama")
2220
SUPERMEMORY_MCP_BASE_URL = os.getenv("SUPERMEMORY_MCP_BASE_URL", "https://mcp.supermemory.ai/")
2321
SUPERMEMORY_MCP_ENDPOINT_SUFFIX = os.getenv("SUPERMEMORY_MCP_ENDPOINT_SUFFIX", "/sse")
2422

0 commit comments

Comments
 (0)