From 16ee228c8cfafbbcb22850d51ba726fec5bba6d4 Mon Sep 17 00:00:00 2001 From: "cto-new[bot]" <140088366+cto-new[bot]@users.noreply.github.com> Date: Fri, 30 Jan 2026 02:56:26 +0000 Subject: [PATCH] Complete Setup & Verification System for NeuraX Next.js + Backend Integration --- backend/api/main.py | 100 +++++ docker/.env.example | 21 + docker/Dockerfile.backend | 22 ++ docker/Dockerfile.frontend | 18 + docker/docker-compose.yml | 59 +++ frontend/package.json | 28 ++ frontend/pages/api/proxy.js | 31 ++ frontend/pages/index.tsx | 165 ++++++++ frontend/styles/globals.css | 26 ++ frontend/tailwind.config.js | 15 + scripts/health_check.sh | 94 +++++ scripts/reset_database.sh | 36 ++ scripts/run_tests.sh | 37 ++ scripts/start_all.sh | 35 ++ scripts/stop_all.sh | 21 + setup/download_models.sh | 38 ++ setup/init_database.sh | 30 ++ setup/setup.sh | 464 +++++++++++++++++++++++ test_setup.sh | 72 ++++ tests/integration/test_api_connection.py | 29 ++ tests/integration/test_file_upload.py | 30 ++ 21 files changed, 1371 insertions(+) create mode 100644 backend/api/main.py create mode 100644 docker/.env.example create mode 100644 docker/Dockerfile.backend create mode 100644 docker/Dockerfile.frontend create mode 100644 docker/docker-compose.yml create mode 100644 frontend/package.json create mode 100644 frontend/pages/api/proxy.js create mode 100644 frontend/pages/index.tsx create mode 100644 frontend/styles/globals.css create mode 100644 frontend/tailwind.config.js create mode 100755 scripts/health_check.sh create mode 100755 scripts/reset_database.sh create mode 100755 scripts/run_tests.sh create mode 100755 scripts/start_all.sh create mode 100755 scripts/stop_all.sh create mode 100755 setup/download_models.sh create mode 100755 setup/init_database.sh create mode 100755 setup/setup.sh create mode 100755 test_setup.sh create mode 100644 tests/integration/test_api_connection.py create mode 100644 tests/integration/test_file_upload.py diff --git a/backend/api/main.py b/backend/api/main.py new file mode 100644 index 0000000..cd2e90c --- /dev/null +++ b/backend/api/main.py @@ -0,0 +1,100 @@ +from fastapi import FastAPI, UploadFile, File, HTTPException +from fastapi.middleware.cors import CORSMiddleware +from fastapi.responses import JSONResponse +from typing import List, Optional +import sys +import os + +# Add parent directory to path +sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + +from ingestion.ingestion_manager import IngestionManager +from retrieval.query_processor import QueryProcessor +from generation.lmstudio_generator import LMStudioGenerator +from indexing.vector_store import VectorStore + +app = FastAPI(title="NeuraX API", version="1.0.0") + +# CORS middleware +app.add_middleware( + CORSMiddleware, + allow_origins=["http://localhost:3000"], + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) + +# Initialize components +ingestion_manager = IngestionManager() +query_processor = QueryProcessor() +vector_store = VectorStore() + +@app.get("/") +async def root(): + return {"message": "NeuraX API is running", "version": "1.0.0"} + +@app.get("/health") +async def health_check(): + """Health check endpoint""" + return { + "status": "healthy", + "components": { + "database": "connected", + "lm_studio": "checking..." + } + } + +@app.post("/api/upload") +async def upload_files(files: List[UploadFile] = File(...)): + """Upload and process files""" + try: + results = [] + for file in files: + # Save file + file_path = f"./data/uploads/{file.filename}" + with open(file_path, "wb") as f: + content = await file.read() + f.write(content) + + # Process file + result = ingestion_manager.process_file(file_path) + results.append({ + "filename": file.filename, + "status": "processed", + "result": result + }) + + return JSONResponse({"status": "success", "files": results}) + except Exception as e: + raise HTTPException(status_code=500, detail=str(e)) + +@app.post("/api/query") +async def process_query(query: dict): + """Process multimodal query""" + try: + query_text = query.get("text", "") + query_type = query.get("type", "text") + + # Process query + results = query_processor.process(query_text) + + return JSONResponse({ + "status": "success", + "results": results + }) + except Exception as e: + raise HTTPException(status_code=500, detail=str(e)) + +@app.get("/api/files") +async def list_files(): + """List uploaded files""" + try: + upload_dir = "./data/uploads" + files = os.listdir(upload_dir) if os.path.exists(upload_dir) else [] + return JSONResponse({"status": "success", "files": files}) + except Exception as e: + raise HTTPException(status_code=500, detail=str(e)) + +if __name__ == "__main__": + import uvicorn + uvicorn.run(app, host="0.0.0.0", port=8000) \ No newline at end of file diff --git a/docker/.env.example b/docker/.env.example new file mode 100644 index 0000000..e419a93 --- /dev/null +++ b/docker/.env.example @@ -0,0 +1,21 @@ +# Docker Environment Configuration + +# Backend Configuration +BACKEND_ENV=development +BACKEND_PORT=8000 + +# Frontend Configuration +FRONTEND_PORT=3000 +NEXT_PUBLIC_API_URL=http://localhost:8000 + +# LM Studio Configuration +LM_STUDIO_URL=http://host.docker.internal:1234 + +# Database Configuration +CHROMA_DB_HOST=chromadb +CHROMA_DB_PORT=8000 +VECTOR_DB_PATH=/app/vector_db + +# Performance Configuration +GPU_ENABLED=true +BATCH_SIZE=32 \ No newline at end of file diff --git a/docker/Dockerfile.backend b/docker/Dockerfile.backend new file mode 100644 index 0000000..e817e84 --- /dev/null +++ b/docker/Dockerfile.backend @@ -0,0 +1,22 @@ +FROM python:3.9-slim + +WORKDIR /app + +# Install system dependencies +RUN apt-get update && apt-get install -y \ + tesseract-ocr \ + ffmpeg \ + && rm -rf /var/lib/apt/lists/* + +# Copy requirements +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +# Copy application +COPY . . + +# Expose port +EXPOSE 8000 + +# Run API +CMD ["uvicorn", "api.main:app", "--host", "0.0.0.0", "--port", "8000"] \ No newline at end of file diff --git a/docker/Dockerfile.frontend b/docker/Dockerfile.frontend new file mode 100644 index 0000000..84d0145 --- /dev/null +++ b/docker/Dockerfile.frontend @@ -0,0 +1,18 @@ +FROM node:18-alpine + +WORKDIR /app + +# Copy package files +COPY package*.json ./ + +# Install dependencies +RUN npm install + +# Copy application +COPY . . + +# Expose port +EXPOSE 3000 + +# Run development server +CMD ["npm", "run", "dev"] \ No newline at end of file diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml new file mode 100644 index 0000000..234fcf9 --- /dev/null +++ b/docker/docker-compose.yml @@ -0,0 +1,59 @@ +version: '3.8' + +services: + backend: + build: + context: ./backend + dockerfile: ../docker/Dockerfile.backend + ports: + - "8000:8000" + volumes: + - ./backend:/app + - ./data:/app/data + - ./vector_db:/app/vector_db + - ./models:/app/models + environment: + - ENVIRONMENT=development + - API_HOST=0.0.0.0 + - API_PORT=8000 + - LM_STUDIO_URL=http://host.docker.internal:1234 + depends_on: + - chromadb + networks: + - neurax-network + + frontend: + build: + context: ./frontend + dockerfile: ../docker/Dockerfile.frontend + ports: + - "3000:3000" + volumes: + - ./frontend:/app + - /app/node_modules + environment: + - NEXT_PUBLIC_API_URL=http://localhost:8000 + - NEXT_PUBLIC_LM_STUDIO_URL=http://localhost:1234 + depends_on: + - backend + networks: + - neurax-network + + chromadb: + image: chromadb/chroma:latest + ports: + - "8001:8000" + volumes: + - ./vector_db:/chroma/chroma + environment: + - IS_PERSISTENT=TRUE + networks: + - neurax-network + +networks: + neurax-network: + driver: bridge + +volumes: + vector_db_data: + models_data: \ No newline at end of file diff --git a/frontend/package.json b/frontend/package.json new file mode 100644 index 0000000..6bc6b3e --- /dev/null +++ b/frontend/package.json @@ -0,0 +1,28 @@ +{ + "name": "neurax-frontend", + "version": "1.0.0", + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "next lint" + }, + "dependencies": { + "next": "^14.0.0", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "axios": "^1.6.0", + "tailwindcss": "^3.3.0", + "postcss": "^8.4.0", + "autoprefixer": "^10.4.0" + }, + "devDependencies": { + "typescript": "^5.0.0", + "@types/node": "^20.0.0", + "@types/react": "^18.2.0", + "@types/react-dom": "^18.2.0", + "eslint": "^8.0.0", + "eslint-config-next": "^14.0.0" + } +} \ No newline at end of file diff --git a/frontend/pages/api/proxy.js b/frontend/pages/api/proxy.js new file mode 100644 index 0000000..ae8a703 --- /dev/null +++ b/frontend/pages/api/proxy.js @@ -0,0 +1,31 @@ +// Next.js API route for proxying requests to backend +export default function handler(req, res) { + const { method, body, query } = req + + // Proxy to backend API + const backendUrl = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000' + const targetUrl = `${backendUrl}${req.url.replace('/api/proxy', '')}` + + // Forward request to backend + fetch(targetUrl, { + method: method, + headers: { + 'Content-Type': 'application/json', + ...req.headers + }, + body: method !== 'GET' ? JSON.stringify(body) : undefined + }) + .then(response => { + // Forward response status and headers + const headers = {} + response.headers.forEach((value, name) => { + headers[name] = value + }) + + res.status(response.status).json(headers) + }) + .catch(error => { + console.error('Proxy error:', error) + res.status(500).json({ error: 'Proxy request failed' }) + }) +} \ No newline at end of file diff --git a/frontend/pages/index.tsx b/frontend/pages/index.tsx new file mode 100644 index 0000000..478e6d8 --- /dev/null +++ b/frontend/pages/index.tsx @@ -0,0 +1,165 @@ +import Head from 'next/head' +import { useState } from 'react' + +export default function Home() { + const [files, setFiles] = useState([]) + const [query, setQuery] = useState('') + const [results, setResults] = useState([]) + const [isLoading, setIsLoading] = useState(false) + const [error, setError] = useState('') + + const handleFileUpload = async (e: React.ChangeEvent) => { + if (e.target.files) { + setFiles(Array.from(e.target.files)) + } + } + + const handleUpload = async () => { + if (files.length === 0) return + + setIsLoading(true) + setError('') + + try { + const formData = new FormData() + files.forEach(file => { + formData.append('files', file) + }) + + const response = await fetch('http://localhost:8000/api/upload', { + method: 'POST', + body: formData + }) + + if (!response.ok) { + throw new Error('Upload failed') + } + + const data = await response.json() + alert(`Upload successful! ${data.files.length} files processed.`) + } catch (err) { + setError(err instanceof Error ? err.message : 'Upload failed') + console.error('Upload error:', err) + } finally { + setIsLoading(false) + } + } + + const handleQuery = async () => { + if (!query.trim()) return + + setIsLoading(true) + setError('') + + try { + const response = await fetch('http://localhost:8000/api/query', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ text: query }) + }) + + if (!response.ok) { + throw new Error('Query failed') + } + + const data = await response.json() + setResults(data.results || []) + } catch (err) { + setError(err instanceof Error ? err.message : 'Query failed') + console.error('Query error:', err) + } finally { + setIsLoading(false) + } + } + + return ( +
+ + NeuraX - Multimodal RAG System + + + +
+

NeuraX

+

Offline Multimodal RAG System

+ + {error && ( +
+ Error: {error} +
+ )} + +
+

File Upload

+
+ +
+ + + {files.length > 0 && ( +
+

Selected Files:

+
    + {files.map((file, index) => ( +
  • {file.name} ({Math.round(file.size / 1024)} KB)
  • + ))} +
+
+ )} +
+ +
+

Query

+