diff --git a/.gitignore b/.gitignore index 3faa417..a2ad79e 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ **.db ai-hub/data/* ai-hub/ai_payloads/* +ai-hub/.env.prod \ No newline at end of file diff --git a/ai-hub/Dockerfile b/ai-hub/Dockerfile index 910cc1f..4d9a211 100644 --- a/ai-hub/Dockerfile +++ b/ai-hub/Dockerfile @@ -20,4 +20,4 @@ # 6. Define the command to run the application # --host 0.0.0.0 makes the server accessible from outside the container -CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"] \ No newline at end of file +CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"] \ No newline at end of file diff --git a/ai-hub/app/api/routes/api.py b/ai-hub/app/api/routes/api.py index 5576e67..5515e75 100644 --- a/ai-hub/app/api/routes/api.py +++ b/ai-hub/app/api/routes/api.py @@ -1,6 +1,6 @@ from fastapi import APIRouter from app.api.dependencies import ServiceContainer - +import os # Import routers from .sessions import create_sessions_router from .documents import create_documents_router @@ -14,7 +14,8 @@ """ Creates and returns a main APIRouter that includes all sub-routers. """ - router = APIRouter() + PATH_PREFIX = os.getenv("PATH_PREFIX", "") + router = APIRouter(prefix=PATH_PREFIX) # Include routers for different functionalities router.include_router(create_general_router(services)) diff --git a/ai-hub/app/app.py b/ai-hub/app/app.py index b326451..597532d 100644 --- a/ai-hub/app/app.py +++ b/ai-hub/app/app.py @@ -2,6 +2,7 @@ from fastapi import FastAPI from contextlib import asynccontextmanager from typing import List +import logging # Import centralized settings and other components from app.config import settings @@ -55,6 +56,7 @@ lifespan=lifespan ) + logging.basicConfig(level=settings.LOG_LEVEL, format='%(asctime)s - %(levelname)s - %(message)s') # --- Initialize Core Services using settings --- # 1. Use the new, more flexible factory function to create the embedder instance diff --git a/ai-hub/app/main.py b/ai-hub/app/main.py index 71b97a6..cfc25bf 100644 --- a/ai-hub/app/main.py +++ b/ai-hub/app/main.py @@ -1,10 +1,6 @@ import uvicorn -import logging from app.app import create_app -# Configure logging (can be moved to a higher level, like app.py, if preferred) -logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') - # Use the application factory to create the FastAPI app instance app = create_app() diff --git a/ai-hub/docker-compose.yml b/ai-hub/docker-compose.yml deleted file mode 100644 index 031b31d..0000000 --- a/ai-hub/docker-compose.yml +++ /dev/null @@ -1,13 +0,0 @@ -# docker-compose.yml - -version: '3.8' - -services: - ai-hub: - build: . - container_name: ai_hub_service - restart: unless-stopped - env_file: - - .env - ports: - - "8000:8000" \ No newline at end of file diff --git a/ai-hub/run_chat.sh b/ai-hub/run_chat.sh index 258c374..695d2bc 100644 --- a/ai-hub/run_chat.sh +++ b/ai-hub/run_chat.sh @@ -1,11 +1,11 @@ #!/bin/bash # A script to automatically start the server and run an interactive chat session. -# It now allows the user to specify a model and a FAISS retriever option for each turn. -# -# REQUIREMENTS: -# - 'jq' must be installed (e.g., sudo apt-get install jq). +# It now tests the PATH_PREFIX functionality. +# --- Configuration --- +# Set the desired path prefix for testing +TEST_PATH_PREFIX="/api/v1" BASE_URL="http://127.0.0.1:8000" DEFAULT_MODEL="deepseek" CURRENT_MODEL="" # The model used in the last turn @@ -17,8 +17,10 @@ exit 1 fi -# --- 2. Start the FastAPI Server in the Background --- -echo "--- Starting AI Hub Server ---" +# --- 2. Start the FastAPI Server in the Background with the PATH_PREFIX --- +echo "--- Starting AI Hub Server with PATH_PREFIX=$TEST_PATH_PREFIX ---" +# Export the PATH_PREFIX environment variable so the server can read it +export PATH_PREFIX="$TEST_PATH_PREFIX" uvicorn app.main:app --host 127.0.0.1 --port 8000 & SERVER_PID=$! @@ -36,8 +38,9 @@ # --- 3. Create a New Conversation Session --- echo "--- Starting a new conversation session... ---" -# FIX: Added a trailing slash to the /sessions endpoint to avoid a 307 redirect -SESSION_DATA=$(curl -s -X POST "$BASE_URL/sessions/" \ +# Use the full URL with the test path prefix +SESSION_URL="$BASE_URL$TEST_PATH_PREFIX/sessions/" +SESSION_DATA=$(curl -s -X POST "$SESSION_URL" \ -H "Content-Type: application/json" \ -d '{"user_id": "local_user", "provider_name": "'"$DEFAULT_MODEL"'"}' \ -w '\n%{http_code}') # Add a new line and the status code @@ -116,7 +119,9 @@ echo "Payload: $json_payload" # Optional: for debugging - ai_response_json=$(curl -s -X POST "$BASE_URL/sessions/$SESSION_ID/chat" \ + # Use the full URL with the test path prefix + CHAT_URL="$BASE_URL$TEST_PATH_PREFIX/sessions/$SESSION_ID/chat" + ai_response_json=$(curl -s -X POST "$CHAT_URL" \ -H "Content-Type: application/json" \ -d "$json_payload") @@ -132,4 +137,4 @@ done -# The 'trap' will automatically call the cleanup function when the loop breaks. +# The 'trap' will automatically call the cleanup function when the loop breaks. \ No newline at end of file diff --git a/ai-hub/tests/core/providers/llm/test_llm_general.py b/ai-hub/tests/core/providers/llm/test_llm_general.py index f331689..bc28acd 100644 --- a/ai-hub/tests/core/providers/llm/test_llm_general.py +++ b/ai-hub/tests/core/providers/llm/test_llm_general.py @@ -50,7 +50,7 @@ messages=[{"role": "user", "content": self.prompt}], api_key=self.api_key, temperature=0.0, # <-- Add this line - max_tokens=10000000 # <-- Add this line + max_tokens=1000, # <-- Add this line ) # Run the async test function diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..3aa3a12 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,41 @@ +version: '3.8' + +services: + # The backend service for your AI Hub + ai-hub: + build: ./ai-hub + container_name: ai_hub_service + restart: unless-stopped + env_file: + - ai-hub/.env.prod + volumes: + # Mount the named volume to the /app/data directory in the container + - ai_hub_data:/app/data:rw + ports: + # Expose the AI Hub's port + - "8002:8000" + + # The frontend service for your React application, served by Nginx + ai-ui: + # Use a build context to find the React app's Dockerfile + # This assumes your React project is in a subdirectory named 'react-app' + build: ./ui/client-app + container_name: ai_frontend_service + restart: unless-stopped + env_file: + - ui/client-app/.env.prod + ports: + # Map host port 8080 to container port 80 (Nginx default) + # This avoids a port conflict with the AI hub + - "8003:443" + +# Define the named volume for the AI hub's data +volumes: + ai_hub_data: + driver: local + driver_opts: + type: "nfs" + # IMPORTANT: Replace the IP address below with your NFS server's actual IP + o: "addr=192.168.68.90,rw" + # IMPORTANT: Replace this path with the correct directory on your NFS server + device: ":/volume1/docker/ai-hub/data" \ No newline at end of file diff --git a/local_deployment.sh b/local_deployment.sh new file mode 100644 index 0000000..41f83f5 --- /dev/null +++ b/local_deployment.sh @@ -0,0 +1,72 @@ +#!/bin/bash + +# --- Deployment Script for AI Hub --- +# This script is designed to automate the deployment of the AI Hub application +# using Docker Compose. It's intended to be run on the production server. +# +# The script performs the following actions: +# 1. Defines project-specific configuration variables. +# 2. **Installs Docker Compose if it's not found on the system.** +# 3. Navigates to the correct project directory. +# 4. Stops and removes any currently running Docker containers for the project. +# 5. Pulls the latest Docker images from a registry (if applicable). +# 6. Builds the new Docker images from the source code. +# 7. Starts the new containers in detached mode, with production settings. +# 8. Performs cleanup of old, unused Docker images. + +# --- Configuration --- +# Set the absolute path to your project directory. +PROJECT_DIR="/home/coder/project/cortex-hub" + +# Set the name of your production environment file (if you are using one). +# If you are not using a separate env file, you can leave this variable empty. +PROD_ENV_FILE="ai-hub/.env" + +# --- Script Execution --- +echo "๐Ÿš€ Starting AI Hub deployment process..." + +# Check and install Docker Compose if not found. +echo "๐Ÿ” Checking for Docker Compose..." +if ! command -v docker-compose &> /dev/null; then + echo "โš ๏ธ Docker Compose not found. Installing..." + sudo curl -L "https://github.com/docker/compose/releases/download/v2.20.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose + sudo chmod +x /usr/local/bin/docker-compose + echo "โœ… Docker Compose installed." +else + echo "โœ… Docker Compose is already installed." +fi + + +# Navigate to the project directory. Exit if the directory doesn't exist. +cd "$PROJECT_DIR" || { echo "Error: Project directory '$PROJECT_DIR' not found. Exiting."; exit 1; } + +# Check for the existence of the production environment file, if specified. +if [ -n "$PROD_ENV_FILE" ] && [ ! -f "$PROD_ENV_FILE" ]; then + echo "Warning: Production environment file '$PROD_ENV_FILE' not found." + echo "The script will proceed, but some services may not have the correct environment variables." +fi + +# Stop and remove any existing containers to ensure a clean deployment. +echo "๐Ÿ›‘ Stopping and removing old Docker containers and networks..." +docker-compose down || true + +# Pull the latest images if they are hosted on a registry. +# This step is optional and can be commented out if you only build from source. +# echo "๐Ÿ“ฅ Pulling latest Docker images..." +# docker-compose pull + +# Build new images and start the services. The `--build` flag ensures +# the images are re-built from their respective Dockerfiles. +echo "๐Ÿ—๏ธ Building and starting new containers..." +sudo docker-compose up -d --build + +echo "โœ… Deployment complete! The AI Hub application is now running." + +# --- Post-Deployment Cleanup --- +echo "๐Ÿงน Cleaning up unused Docker resources..." + +# Remove dangling images (images without a tag). The '|| true' prevents the script +# from exiting if there are no dangling images to remove. +sudo docker system prune -f || true + +echo "โœจ Cleanup finished." \ No newline at end of file diff --git a/ui/client-app/.gitignore b/ui/client-app/.gitignore index 4d29575..6580faa 100644 --- a/ui/client-app/.gitignore +++ b/ui/client-app/.gitignore @@ -13,11 +13,7 @@ # misc .DS_Store -.env.local -.env.development.local -.env.test.local -.env.production.local - +.env.prod npm-debug.log* yarn-debug.log* yarn-error.log* diff --git a/ui/client-app/Dockerfile b/ui/client-app/Dockerfile new file mode 100644 index 0000000..38f34ce --- /dev/null +++ b/ui/client-app/Dockerfile @@ -0,0 +1,24 @@ +# Use an official Node.js image to run the app +FROM node:18-alpine + +# Set the working directory inside the container +WORKDIR /app + +# Copy package.json and package-lock.json (or yarn.lock) +COPY package*.json ./ + +# Install dependencies +RUN npm install + +# Copy the rest of the application source code +COPY . . + +# Set environment variables for the application +ENV HOST=0.0.0.0 +ENV PORT=8000 + +# Expose the application port +EXPOSE 8000 + +# Command to start the application +CMD ["npm", "start"] \ No newline at end of file diff --git a/ui/client-app/src/components/CodeFolderAccess.js b/ui/client-app/src/components/CodeFolderAccess.js index e29686d..07e07e5 100644 --- a/ui/client-app/src/components/CodeFolderAccess.js +++ b/ui/client-app/src/components/CodeFolderAccess.js @@ -11,7 +11,7 @@ const folderPath = directoryHandle.name; // Simplified path, you might get a full path in a real app onSelectFolder(directoryHandle); } catch (err) { - console.error("Failed to select directory:", err); + console.warn("Failed to select directory:", err); // You might want to handle user cancellation here } }; diff --git a/ui/client-app/src/hooks/tempCodeRunnerFile.js b/ui/client-app/src/hooks/tempCodeRunnerFile.js deleted file mode 100644 index a61fcae..0000000 --- a/ui/client-app/src/hooks/tempCodeRunnerFile.js +++ /dev/null @@ -1 +0,0 @@ -createSession \ No newline at end of file diff --git a/ui/client-app/src/hooks/useCodeAssistant.js b/ui/client-app/src/hooks/useCodeAssistant.js index 6a121ab..cb9451b 100644 --- a/ui/client-app/src/hooks/useCodeAssistant.js +++ b/ui/client-app/src/hooks/useCodeAssistant.js @@ -157,12 +157,12 @@ readFiles.push(filepath); } catch (error) { console.warn(`Failed to read file: ${filepath}`, error); - ws.current.send(JSON.stringify({ - type: "error", - content: `Could not read file: ${filepath}`, - request_id, - session_id: sessionIdRef.current, - })); + // ws.current.send(JSON.stringify({ + // type: "error", + // content: `Could not read file: ${filepath}`, + // request_id, + // session_id: sessionIdRef.current, + // })); } } diff --git a/ui/client-app/src/services/apiService.js b/ui/client-app/src/services/apiService.js index c8ea6cd..18859ab 100644 --- a/ui/client-app/src/services/apiService.js +++ b/ui/client-app/src/services/apiService.js @@ -3,14 +3,16 @@ import { convertPcmToFloat32 } from "./audioUtils"; -// Please replace with your actual endpoints -const STT_ENDPOINT = "http://localhost:8001/stt/transcribe"; -const SESSIONS_CREATE_ENDPOINT = "http://localhost:8001/sessions"; -const SESSIONS_CHAT_ENDPOINT = (id) => `http://localhost:8001/sessions/${id}/chat`; -const TTS_ENDPOINT = "http://localhost:8001/speech"; -const USERS_LOGIN_ENDPOINT = "http://localhost:8001/users/login"; -const USERS_LOGOUT_ENDPOINT = "http://localhost:8001/users/logout"; -const USERS_ME_ENDPOINT = "http://localhost:8001/users/me"; +// Read base API URL from environment variables, defaulting to localhost +const API_BASE_URL = process.env.REACT_APP_API_BASE_URL || "http://localhost:8001"; +export { API_BASE_URL }; +const STT_ENDPOINT = `${API_BASE_URL}/stt/transcribe`; +const SESSIONS_CREATE_ENDPOINT = `${API_BASE_URL}/sessions/`; +const SESSIONS_CHAT_ENDPOINT = (id) => `${API_BASE_URL}/sessions/${id}/chat`; +const TTS_ENDPOINT = `${API_BASE_URL}/speech`; +const USERS_LOGIN_ENDPOINT = `${API_BASE_URL}/users/login`; +const USERS_LOGOUT_ENDPOINT = `${API_BASE_URL}/users/logout`; +const USERS_ME_ENDPOINT = `${API_BASE_URL}/users/me`; /** * A central utility function to get the user ID. diff --git a/ui/client-app/src/services/websocket.js b/ui/client-app/src/services/websocket.js index a1285d6..2dd1b1e 100644 --- a/ui/client-app/src/services/websocket.js +++ b/ui/client-app/src/services/websocket.js @@ -1,6 +1,6 @@ // src/services/websocket.js -import { createSession } from "./apiService"; +import { createSession, API_BASE_URL } from "./apiService"; /** * Gets a session ID from localStorage or creates a new one via the API. @@ -37,7 +37,9 @@ ) => { try { let sessionId = localStorage.getItem("sessionId"); - sessionId = null; // Force create new session for testing + + // NOTE: The line `sessionId = null;` has been removed as it was for testing purposes + // and would force a new session on every connection. if (!sessionId) { // No existing session, so create one via API @@ -51,8 +53,13 @@ // You now have a valid sessionId, either reused or newly created console.log("Using session ID:", sessionId); - // Then, use the session ID to open the WebSocket connection - const websocketUrl = `ws://localhost:8001/ws/workspace/${sessionId}`; + // Correct the WebSocket URL based on the API_BASE_URL protocol. + // Use `wss` for `https` and `ws` for `http`. + const url = new URL(API_BASE_URL); + const wsProtocol = url.protocol === "https:" ? "wss" : "ws"; + const websocketUrl = `${wsProtocol}://${url.host}${url.pathname}/ws/workspace/${sessionId}`; + + console.log("Connecting to WebSocket URL:", websocketUrl); const ws = new WebSocket(websocketUrl); ws.onopen = () => {