mirror of
https://github.com/Nighthawk42/fulloch.git
synced 2026-08-30 08:02:28 +00:00
usability and docker launch fixes
This commit is contained in:
@@ -59,6 +59,7 @@ data/token.json
|
||||
# Local Data and Models (mount separately)
|
||||
# =============================================================================
|
||||
data/models/
|
||||
data/voices/
|
||||
data/cache/
|
||||
*.gguf
|
||||
|
||||
|
||||
@@ -99,3 +99,4 @@ data/voices/private
|
||||
!data/config.example.yml
|
||||
!.env.example\
|
||||
publish.sh
|
||||
searxng_data/settings.yml.new
|
||||
|
||||
+11
-8
@@ -2,8 +2,6 @@ FROM python:3.12-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Point HuggingFace cache at the mounted data volume so from_pretrained()
|
||||
# finds models downloaded by launch.sh into data/models/hub/
|
||||
ENV HF_HOME=/app/data/models
|
||||
ENV HF_HUB_OFFLINE=1
|
||||
|
||||
@@ -15,20 +13,25 @@ RUN apt-get update && apt-get install -y \
|
||||
ffmpeg \
|
||||
git \
|
||||
libportaudio2 \
|
||||
procps \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install Python dependencies (Force transformers 4.57.3 for offline Qwen3 ASR to work)
|
||||
RUN pip install torch torchaudio --index-url https://download.pytorch.org/whl/cu130
|
||||
RUN pip install torch torchaudio --index-url https://download.pytorch.org/whl/cpu
|
||||
COPY requirements.txt .
|
||||
RUN pip install --no-cache-dir -r requirements.txt && \
|
||||
pip install --no-deps git+https://github.com/rekuenkdr/Qwen3-TTS-streaming.git@97da215
|
||||
|
||||
# Create non-root user
|
||||
RUN useradd -m -u 1000 -s /bin/bash appuser
|
||||
USER appuser
|
||||
|
||||
# Copy application code
|
||||
COPY app.py .
|
||||
COPY core/ core/
|
||||
COPY tools/ tools/
|
||||
COPY utils/ utils/
|
||||
COPY audio/ audio/
|
||||
COPY --chown=appuser:appuser app.py .
|
||||
COPY --chown=appuser:appuser core/ core/
|
||||
COPY --chown=appuser:appuser tools/ tools/
|
||||
COPY --chown=appuser:appuser utils/ utils/
|
||||
COPY --chown=appuser:appuser audio/ audio/
|
||||
|
||||
# Run the app
|
||||
CMD ["python", "app.py"]
|
||||
|
||||
+33
-26
@@ -1,42 +1,49 @@
|
||||
# Use PyTorch image with CUDA support (includes Python 3.11, PyTorch, CUDA)
|
||||
FROM pytorch/pytorch:2.5.1-cuda12.4-cudnn9-devel
|
||||
# Stage 1: Build compiled CUDA extensions
|
||||
FROM pytorch/pytorch:2.8.0-cuda12.8-cudnn9-devel AS builder
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
ENV CMAKE_ARGS="-DGGML_CUDA=on"
|
||||
ENV FORCE_CMAKE=1
|
||||
|
||||
RUN apt-get update && apt-get install -y git && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
COPY requirements.txt /tmp/requirements.txt
|
||||
RUN pip install --no-cache-dir -r /tmp/requirements.txt && \
|
||||
pip install --no-deps git+https://github.com/rekuenkdr/Qwen3-TTS-streaming.git@97da215 && \
|
||||
pip install flash-attn --no-build-isolation
|
||||
|
||||
# Stage 2: Runtime (no CUDA compilers/headers)
|
||||
FROM pytorch/pytorch:2.8.0-cuda12.8-cudnn9-runtime
|
||||
|
||||
# Avoid interactive prompts
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# Install system dependencies
|
||||
WORKDIR /app
|
||||
|
||||
ENV HF_HOME=/app/data/models
|
||||
ENV HF_HUB_OFFLINE=1
|
||||
|
||||
# Install runtime system dependencies
|
||||
RUN apt-get update && apt-get install -y \
|
||||
git \
|
||||
sox \
|
||||
libsox-dev \
|
||||
libsox-fmt-all \
|
||||
ffmpeg \
|
||||
libportaudio2 \
|
||||
procps \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
WORKDIR /app
|
||||
# Copy Python environment with compiled packages from builder
|
||||
COPY --from=builder /opt/conda /opt/conda
|
||||
|
||||
# Point HuggingFace cache at the mounted data volume so from_pretrained()
|
||||
# finds models downloaded by launch.sh into data/models/hub/
|
||||
ENV HF_HOME=/app/data/models
|
||||
ENV HF_HUB_OFFLINE=1
|
||||
|
||||
# Set Environment Variables for llama-cpp-python CUDA build
|
||||
ENV CMAKE_ARGS="-DGGML_CUDA=on"
|
||||
ENV FORCE_CMAKE=1
|
||||
|
||||
# Install Python dependencies (Force transformers 4.57.3 for offline Qwen3 ASR to work)
|
||||
RUN pip install torch torchaudio --index-url https://download.pytorch.org/whl/cu130
|
||||
COPY requirements.txt .
|
||||
RUN pip install --no-cache-dir -r requirements.txt && \
|
||||
pip install --no-deps git+https://github.com/rekuenkdr/Qwen3-TTS-streaming.git@97da215 && \
|
||||
pip install flash-attn --no-build-isolation
|
||||
# Create non-root user
|
||||
RUN useradd -m -u 1000 -s /bin/bash appuser
|
||||
USER appuser
|
||||
|
||||
# Copy application code
|
||||
COPY app.py .
|
||||
COPY core/ core/
|
||||
COPY tools/ tools/
|
||||
COPY utils/ utils/
|
||||
COPY audio/ audio/
|
||||
COPY --chown=appuser:appuser app.py .
|
||||
COPY --chown=appuser:appuser core/ core/
|
||||
COPY --chown=appuser:appuser tools/ tools/
|
||||
COPY --chown=appuser:appuser utils/ utils/
|
||||
COPY --chown=appuser:appuser audio/ audio/
|
||||
|
||||
CMD ["python", "app.py"]
|
||||
|
||||
@@ -11,11 +11,12 @@ Usage:
|
||||
"""
|
||||
|
||||
import os
|
||||
import warnings
|
||||
import logging
|
||||
import yaml
|
||||
from pathlib import Path
|
||||
|
||||
warnings.filterwarnings("ignore", message="Setting `pad_token_id` to `eos_token_id`")
|
||||
# Suppress noisy "Setting pad_token_id to eos_token_id" from transformers
|
||||
logging.getLogger("transformers.generation.utils").setLevel(logging.ERROR)
|
||||
|
||||
# Load configuration
|
||||
with open("./data/config.yml", "r") as f:
|
||||
@@ -32,8 +33,6 @@ os.environ["DO_NOT_TRACK"] = "1"
|
||||
os.environ["ANONYMIZED_TELEMETRY"] = "False"
|
||||
os.environ["VLLM_NO_USAGE_STATS"] = "1"
|
||||
|
||||
import logging
|
||||
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
|
||||
|
||||
+12
-5
@@ -2,17 +2,28 @@ services:
|
||||
# Fulloch AI
|
||||
app:
|
||||
build:
|
||||
context: .
|
||||
context: .
|
||||
env_file:
|
||||
- .env
|
||||
container_name: fulloch-ai
|
||||
network_mode: host
|
||||
restart: unless-stopped
|
||||
devices:
|
||||
- /dev/snd:/dev/snd
|
||||
group_add:
|
||||
- audio
|
||||
environment:
|
||||
- PULSE_SERVER=unix:${XDG_RUNTIME_DIR}/pulse/native
|
||||
volumes:
|
||||
- ./data:/app/data:rw
|
||||
- ${XDG_RUNTIME_DIR}/pulse:${XDG_RUNTIME_DIR}/pulse:ro
|
||||
- ${HOME}/.config/pulse/cookie:/home/appuser/.config/pulse/cookie:ro
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pgrep -f 'python app.py' || exit 1"]
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
start_period: 60s
|
||||
|
||||
# Web Search - Searxng
|
||||
searxng:
|
||||
@@ -30,7 +41,3 @@ services:
|
||||
- SETGID
|
||||
- SETUID
|
||||
- DAC_OVERRIDE
|
||||
|
||||
volumes:
|
||||
searxng_data: {}
|
||||
app_data: {}
|
||||
+11
-4
@@ -8,12 +8,23 @@ services:
|
||||
- .env
|
||||
container_name: fulloch-ai
|
||||
network_mode: host
|
||||
restart: unless-stopped
|
||||
devices:
|
||||
- /dev/snd:/dev/snd
|
||||
group_add:
|
||||
- audio
|
||||
environment:
|
||||
- PULSE_SERVER=unix:${XDG_RUNTIME_DIR}/pulse/native
|
||||
volumes:
|
||||
- ./data:/app/data:rw
|
||||
- ${XDG_RUNTIME_DIR}/pulse:${XDG_RUNTIME_DIR}/pulse:ro
|
||||
- ${HOME}/.config/pulse/cookie:/home/appuser/.config/pulse/cookie:ro
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pgrep -f 'python app.py' || exit 1"]
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
start_period: 60s
|
||||
deploy:
|
||||
resources:
|
||||
reservations:
|
||||
@@ -38,7 +49,3 @@ services:
|
||||
- SETGID
|
||||
- SETUID
|
||||
- DAC_OVERRIDE
|
||||
|
||||
volumes:
|
||||
searxng_data: {}
|
||||
app_data: {}
|
||||
@@ -74,40 +74,44 @@ echo "✅ All dependencies found."
|
||||
echo "📂 Checking directory structure..."
|
||||
mkdir -p "$HUB_DIR" "$GRAMMAR_DIR"
|
||||
|
||||
# 2a. Check for config.yml
|
||||
# 2a. Check for config.yml and .env, create from templates if missing
|
||||
CONFIG_FILE="$(pwd)/data/config.yml"
|
||||
CONFIG_EXAMPLE="$(pwd)/data/config.example.yml"
|
||||
ENV_FILE="$(pwd)/.env"
|
||||
ENV_EXAMPLE="$(pwd)/.env.example"
|
||||
|
||||
CREATED_FILES=()
|
||||
|
||||
if [ ! -f "$CONFIG_FILE" ]; then
|
||||
echo "📝 config.yml not found. Creating from template..."
|
||||
cp "$CONFIG_EXAMPLE" "$CONFIG_FILE"
|
||||
echo ""
|
||||
echo "⚠️ Please edit data/config.yml with your settings before continuing."
|
||||
echo " See data/config.example.yml for documentation on each option."
|
||||
echo ""
|
||||
echo " Run ./launch.sh again when ready."
|
||||
exit 0
|
||||
CREATED_FILES+=("data/config.yml")
|
||||
else
|
||||
echo "✅ config.yml exists."
|
||||
fi
|
||||
|
||||
# 2b. Check for .env
|
||||
ENV_FILE="$(pwd)/.env"
|
||||
ENV_EXAMPLE="$(pwd)/.env.example"
|
||||
|
||||
if [ ! -f "$ENV_FILE" ]; then
|
||||
echo "📝 .env not found. Creating from template..."
|
||||
cp "$ENV_EXAMPLE" "$ENV_FILE"
|
||||
echo ""
|
||||
echo "⚠️ Please edit .env with your credentials before continuing."
|
||||
echo " See .env.example for documentation on each variable."
|
||||
echo ""
|
||||
echo " Run ./launch.sh again when ready."
|
||||
exit 0
|
||||
CREATED_FILES+=(".env")
|
||||
else
|
||||
echo "✅ .env exists."
|
||||
fi
|
||||
|
||||
if [ ${#CREATED_FILES[@]} -gt 0 ]; then
|
||||
echo ""
|
||||
echo "📄 Created: ${CREATED_FILES[*]}"
|
||||
echo ""
|
||||
read -p "Continue with defaults or exit to edit these files first? (c)ontinue / (e)xit: " response
|
||||
response=${response,,}
|
||||
if [[ "$response" == "e" || "$response" == "exit" ]]; then
|
||||
echo ""
|
||||
echo " Edit the files and run ./launch.sh again when ready."
|
||||
exit 0
|
||||
fi
|
||||
echo "✅ Continuing with defaults."
|
||||
fi
|
||||
|
||||
# 3. Check and Download json.gbnf
|
||||
if [ ! -f "$GRAMMAR_DIR/json.gbnf" ]; then
|
||||
if ask_download "json.gbnf (grammar file)"; then
|
||||
@@ -187,7 +191,7 @@ else
|
||||
fi
|
||||
|
||||
# 7. Prompt the user
|
||||
read -p "Are you using a GPU? (y/n): " response
|
||||
read -p "Are you using an NVIDIA GPU? (y/n): " response
|
||||
response=${response,,}
|
||||
if [[ "$response" == "y" || "$response" == "yes" ]]; then
|
||||
COMPOSE_FILE="compose_gpu.yml"
|
||||
@@ -197,6 +201,14 @@ else
|
||||
echo "✅ Using default containers"
|
||||
fi
|
||||
|
||||
# 8. Launch Docker Compose
|
||||
# 8. Prepare runtime environment
|
||||
# Ensure XDG_RUNTIME_DIR is set with the correct UID (compose files reference it)
|
||||
export XDG_RUNTIME_DIR="${XDG_RUNTIME_DIR:-/run/user/$(id -u)}"
|
||||
|
||||
# Ensure PulseAudio cookie exists (PipeWire systems may not create one)
|
||||
mkdir -p "${HOME}/.config/pulse"
|
||||
[ -f "${HOME}/.config/pulse/cookie" ] || touch "${HOME}/.config/pulse/cookie"
|
||||
|
||||
# 9. Launch Docker Compose
|
||||
echo "🚀 All files checked. Starting services..."
|
||||
docker compose -f "$COMPOSE_FILE" up -d
|
||||
|
||||
@@ -58,6 +58,7 @@ A privacy-focused voice assistant that runs speech recognition, text-to-speech,
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Linux-based OS
|
||||
- Python 3.10+
|
||||
- CUDA-capable GPU (recommended) or CPU
|
||||
- ~4GB disk space for models
|
||||
|
||||
+8
-8
@@ -96,15 +96,15 @@ def external_information(query: str = "get me the latest news stories") -> str:
|
||||
|
||||
today = datetime.now().strftime("%B %d, %Y")
|
||||
|
||||
prompt = f"""
|
||||
Today is {today}.
|
||||
lines = [f"Today is {today}.", ""]
|
||||
if website_snippets:
|
||||
lines.append("A web search has retrieved the following information:")
|
||||
lines.extend(website_snippets)
|
||||
lines.append("")
|
||||
lines.append("User question:")
|
||||
lines.append(query)
|
||||
|
||||
{f"A web search has retrieved the following information:\n{chr(10).join(website_snippets)}" if len(website_snippets) > 0 else ""}
|
||||
|
||||
User question:
|
||||
{query}
|
||||
"""
|
||||
return prompt.strip()
|
||||
return "\n".join(lines)
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("Web Search")
|
||||
|
||||
Reference in New Issue
Block a user