8.9 KiB
CodeRED-Astra Architecture
Overview
CodeRED-Astra is a Retrieval-Augmented Generation (RAG) system for querying ISS technical documentation using vector search, MySQL metadata storage, and Gemini AI for analysis and response generation.
System Components
1. Rust Backend (rust-engine/)
High-performance Rust backend using Warp for HTTP, SQLx for MySQL, and Reqwest for external API calls.
Modules
main.rs - Entry point
- Initializes tracing, database, storage
- Spawns FileWorker and QueryWorker background tasks
- Serves API routes on port 8000
db.rs - Database initialization
- Connects to MySQL
- Creates
filestable (id, filename, path, description, pending_analysis, analysis_status) - Creates
queriestable (id, status, payload, result, timestamps)
api.rs - HTTP endpoints
POST /api/files- Upload file (multipart/form-data)POST /api/files/import-demo- Bulk import from demo-data directoryGET /api/files/list- List all files with statusGET /api/files/delete?id=- Delete file and remove from QdrantPOST /api/query/create- Create new query (returns query ID)GET /api/query/status?id=- Check query statusGET /api/query/result?id=- Get query resultGET /api/query/cancel?id=- Cancel in-progress query
file_worker.rs - File analysis pipeline
- Background worker that processes files with
pending_analysis = TRUE - Claims stale/queued files (requeues if stuck >10 min)
- Stage 1: Call Gemini 1.5 Flash for initial description
- Stage 2: Call Gemini 1.5 Pro for deep vector graph data (keywords, relationships)
- Stage 3: Generate embedding and upsert to Qdrant
- Stage 4: Mark file as ready (
pending_analysis = FALSE,analysis_status = 'Completed') - Resumable: Can recover from crashes/restarts
worker.rs - Query processing pipeline
- Background worker that processes queries with
status = 'Queued' - Requeues stale InProgress jobs (>10 min)
- Stage 1: Embed query text
- Stage 2: Search top-K similar vectors in Qdrant
- Stage 3: Fetch file metadata from MySQL (only completed files)
- Stage 4: Call Gemini to analyze relationships between files
- Stage 5: Call Gemini for final answer synthesis (strict: no speculation)
- Stage 6: Save results to database
- Supports cancellation checks between stages
gemini_client.rs - Gemini API integration
generate_text(prompt)- Text generation with model switching via GEMINI_MODEL env vardemo_text_embedding(text)- Demo 64-dim embeddings (replace with real Gemini embeddings)- Falls back to demo responses if GEMINI_API_KEY not set
vector_db.rs - Qdrant client
ensure_files_collection(dim)- Create 'files' collection with Cosine distanceupsert_point(id, vector)- Store file embeddingsearch_top_k(vector, k)- Find k nearest neighborsdelete_point(id)- Remove file from index
storage.rs - File storage utilities
storage_dir()- Get storage path from ASTRA_STORAGE env or default/app/storageensure_storage_dir()- Create storage directory if missingsave_file(filename, contents)- Save file to storagedelete_file(path)- Remove file from storage
models.rs - Data structures
FileRecord- File metadata (mirrors files table)QueryRecord- Query metadata (mirrors queries table)QueryStatusenum - Queued, InProgress, Completed, Cancelled, Failed
2. Web App (web-app/)
React + Vite frontend with Express backend for API proxying.
Backend (server.mjs)
- Express server that proxies API calls to rust-engine:8000
- Serves React static build from
/dist - Why needed: Docker networking - React can't call rust-engine directly from browser
Frontend (src/)
App.jsx- Main chat interface componentcomponents/ui/chat/chat-header.jsx- Header with debug-only "Seed Demo Data" button (visible with?debug=1)- Calls
/api/files/import-demoendpoint to bulk-load ISS PDFs
3. MySQL Database
Two tables for metadata storage:
files table
id VARCHAR(36) PRIMARY KEY
filename TEXT NOT NULL
path TEXT NOT NULL
description TEXT
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
pending_analysis BOOLEAN DEFAULT TRUE
analysis_status VARCHAR(32) DEFAULT 'Queued'
queries table
id VARCHAR(36) PRIMARY KEY
status VARCHAR(32) NOT NULL
payload JSON
result JSON
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
4. Qdrant Vector Database
- Collection:
files - Dimension: 64 (demo) - replace with real Gemini embedding dimension
- Distance: Cosine similarity
- Stores file embeddings for semantic search
5. Demo Data (rust-engine/demo-data/)
~20 ISS technical PDFs organized by subsystem:
- Electrical Power System (EPS)
- Environmental Control & Life Support (ECLSS)
- Command & Data Handling (C&DH)
- Structures & Mechanisms
Data Flow
File Upload & Analysis
1. User uploads PDF → POST /api/files
2. API saves file to storage, inserts DB record (pending_analysis=true)
3. FileWorker claims pending file
4. Gemini 1.5 Flash generates description
5. Gemini 1.5 Pro generates vector graph data
6. Embed text → upsert to Qdrant
7. Mark file as ready (pending_analysis=false)
Query Processing
1. User submits query → POST /api/query/create
2. API inserts query record (status='Queued')
3. QueryWorker claims queued query
4. Embed query text
5. Search Qdrant for top-K similar files
6. Fetch file metadata from MySQL
7. Gemini analyzes relationships between files
8. Gemini synthesizes final answer (no speculation)
9. Save results to database
Deployment
Development (docker-compose.yml)
- Local testing with hot-reload
- Bind mounts for code
Production (docker-compose.prod.yml)
- Used by GitHub Actions for deployment
- Runs rust-engine as user "1004" (github-actions)
- Docker volume:
rust-storage→/app/storage - Bind mount:
/var/www/codered-astra/rust-engine/demo-data→/app/demo-data:ro - Environment variables:
ASTRA_STORAGE=/app/storageDEMO_DATA_DIR=/app/demo-dataQDRANT_URL=http://qdrant:6333GEMINI_API_KEY=<secret>DATABASE_URL=mysql://astraadmin:password@mysql:3306/astra
Key Design Decisions
1. Two-Stage Analysis (Flash → Pro)
- Flash is faster/cheaper for initial description
- Pro is better for deep analysis and relationship extraction
- Enables cost-effective scaling
2. Resumable Workers
- Workers requeue stale jobs (>10 min in InProgress)
- Survives container restarts without data loss
- Atomic state transitions via SQL
3. Separation of Concerns
- FileWorker: Makes files searchable
- QueryWorker: Answers user queries
- Independent scaling and failure isolation
4. Strict Answer Generation
- Gemini prompted to not speculate
- Must state uncertainty when info is insufficient
- Prevents hallucination in critical ISS documentation
5. Demo Embeddings
- Current: 64-dim deterministic embeddings from text hash
- Production: Replace with real Gemini text embeddings API
- Allows development/testing without embedding API credits
API Usage Examples
Upload File
curl -F "file=@document.pdf" http://localhost:3001/api/files
Import Demo Data
curl -X POST http://localhost:3001/api/files/import-demo
Create Query
curl -X POST http://localhost:3001/api/query/create \
-H "Content-Type: application/json" \
-d '{"q": "What is the voltage of the ISS main bus?", "top_k": 5}'
Check Status
curl http://localhost:3001/api/query/status?id=<query-id>
Get Result
curl http://localhost:3001/api/query/result?id=<query-id>
Future Enhancements
High Priority
- Real Gemini text embeddings (replace demo embeddings)
- File status UI panel (show processing progress)
- Health check endpoint (
/health) - Data purge endpoint (clear all files/queries)
Medium Priority
- Streaming query responses (SSE/WebSocket)
- Query result caching
- File chunking for large PDFs
- User authentication
Low Priority
- Multi-collection support (different document types)
- Query history UI
- File preview in chat
- Export results to PDF
Troubleshooting
Storage Permission Errors
- Ensure
/app/storageis owned by container user - Docker volume must be writable by user 1004 in production
SQL Syntax Errors
- MySQL requires separate
CREATE TABLEstatements - Cannot combine multiple DDL statements in one
sqlx::query()
Qdrant Connection Issues
- Check QDRANT_URL environment variable
- Ensure qdrant service is running and healthy
- Verify network connectivity between containers
Worker Not Processing
- Check logs:
docker logs rust-engine - Verify database connectivity
- Look for stale InProgress jobs in queries/files tables
Demo Presentation (3 minutes)
See rust-engine/DEMODETAILS.md for curated demo script with example queries.