A production-ready Retrieval-Augmented Generation (RAG) application built with .NET 10, featuring advanced document processing, streaming chat responses, and comprehensive infrastructure orchestration using .NET Aspire.
- π Multi-Document Processing - Batch upload and processing with parallel execution
- π¬ Context-Aware Chat - Conversation history with session management
- π Streaming Responses - Real-time chat responses using Server-Sent Events (SSE)
- π Document Management - Full CRUD operations with metadata persistence
- π Semantic Search - Vector-based document retrieval with relevance scoring
- π Source Citations - Automatic citation extraction with relevance tracking
- ποΈ PostgreSQL Integration - Persistent document metadata with EF Core
- β‘ Redis Distributed Caching - Session management for horizontal scaling
- π Structured Logging - Serilog with console and file outputs
- π₯ Health Checks - Comprehensive monitoring for all dependencies
- π³ Container Orchestration - Full .NET Aspire infrastructure management
| Component | Technology |
|---|---|
| RAG Orchestration | Microsoft Kernel Memory |
| LLM & Embeddings | Ollama (phi3, nomic-embed-text) |
| Vector Database | Qdrant |
| Relational Database | PostgreSQL |
| Distributed Cache | Redis |
| Service Orchestration | .NET Aspire |
| API Framework | ASP.NET Core 10.0 |
| Logging | Serilog |
graph TB
Client[Client] --> API[Enarro API]
API --> PG[(PostgreSQL)]
API --> Redis[(Redis Cache)]
API --> KM[Kernel Memory]
KM --> Qdrant[(Qdrant Vector DB)]
KM --> Ollama[Ollama LLM]
API --> HC[Health Checks]
HC -.-> PG
HC -.-> Redis
HC -.-> Qdrant
HC -.-> Ollama
- .NET 10 SDK
- Docker Desktop
- Ollama (for local LLM)
git clone https://github.com/yourusername/enarro.git
cd enarroollama pull phi3
ollama pull nomic-embed-textdotnet run --project Enarro.AppHostThis single command will:
- Start PostgreSQL container
- Start Redis container
- Start Qdrant container
- Start Ollama container
- Run database migrations
- Launch the API
- Open the Aspire dashboard
- API: https://localhost:7001
- Swagger UI: https://localhost:7001/swagger
- Aspire Dashboard: https://localhost:15888
- Health Checks: https://localhost:7001/health
POST /api/v1/ingest
Content-Type: multipart/form-data
file: <file>
tags: {"category": "technical", "department": "engineering"}POST /api/v1/ingest/batch
Content-Type: multipart/form-data
files: <file1>, <file2>, <file3>GET /api/v1/documents?page=1&pageSize=20&tag=technicalGET /api/v1/documents/{id}DELETE /api/v1/documents/{id}POST /api/v1/chat
Content-Type: application/json
{
"message": "What is machine learning?",
"sessionId": "optional-session-id",
"userId": "optional-user-id",
"minRelevance": 0.3,
"filters": {
"category": "technical"
}
}POST /api/v1/chat/stream
Content-Type: application/json
{
"message": "Explain neural networks",
"sessionId": "session-123"
}Response format (Server-Sent Events):
data: Neural
data: networks
data: are...
data: [DONE]
{
"RAGConfigs": {
"IndexName": "rag-test",
"ChatModel": "phi3",
"EmbeddingModel": "nomic-embed-text",
"Retrieval": {
"MinRelevance": 0.3,
"MaxResults": 5,
"AnswerTokens": 4096
},
"Conversation": {
"MaxHistoryMessages": 10,
"SessionTimeoutMinutes": 60
},
"DocumentProcessing": {
"MaxConcurrentUploads": 5,
"MaxFileSizeMB": 50,
"AllowedExtensions": [".pdf", ".docx", ".txt", ".md", ".xlsx", ".pptx", ".json"]
}
}
}CREATE TABLE documents (
id UUID PRIMARY KEY,
file_name VARCHAR(500) NOT NULL,
content_type VARCHAR(100) NOT NULL,
size_bytes BIGINT NOT NULL,
uploaded_at TIMESTAMP NOT NULL,
uploaded_by VARCHAR(100),
status VARCHAR(50) NOT NULL,
chunk_count INTEGER DEFAULT 0,
error_message TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);CREATE TABLE document_tags (
id SERIAL PRIMARY KEY,
document_id UUID REFERENCES documents(id) ON DELETE CASCADE,
tag_key VARCHAR(100) NOT NULL,
tag_value VARCHAR(500) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);Access the health endpoint to check all service statuses:
GET /healthResponse:
{
"status": "Healthy",
"timestamp": "2026-02-07T06:00:00Z",
"checks": [
{
"name": "postgresql",
"status": "Healthy",
"duration": 12.5,
"tags": ["db", "sql"]
},
{
"name": "redis",
"status": "Healthy",
"duration": 8.3,
"tags": ["cache"]
},
{
"name": "ollama",
"status": "Healthy",
"duration": 45.2,
"tags": ["llm"]
},
{
"name": "qdrant",
"status": "Healthy",
"duration": 15.7,
"tags": ["vector-db"]
}
],
"totalDuration": 81.7
}Logs are written to:
- Console: Colored output with timestamps
- Files:
logs/enarro-[date].log(30-day retention)
Log levels can be configured per namespace in appsettings.json:
{
"Serilog": {
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft.AspNetCore": "Warning",
"Microsoft.EntityFrameworkCore": "Warning",
"Enarro": "Debug"
}
}
}
}cd Enarro
dotnet ef migrations add MigrationName
dotnet ef database updatedotnet builddotnet testThe application uses .NET Aspire for container orchestration. All containers are managed automatically:
- PostgreSQL: Port 5432 (persistent volume)
- Redis: Port 6379 (persistent volume)
- Qdrant: Port 6333 (HTTP), 6334 (gRPC) (persistent volume)
- Ollama: Port 11434 (persistent volume)
- Concurrent Uploads: Up to 5 simultaneous document uploads
- Session Timeout: 60 minutes (sliding), 24 hours (absolute)
- Max File Size: 50 MB
- Chunk Size: 512 tokens with 50-token overlap
- Max History: 10 messages per session
- JWT Authentication & Authorization
- User Management System
- API Rate Limiting
- Advanced Query Rewriting
- Document Versioning
- Multi-tenancy Support
- Hybrid Search (Semantic + Keyword)
- Re-ranking Pipeline
- Caching Strategies
- Performance Optimization
- Load Testing
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/AmazingFeature) - Commit your changes (
git commit -m 'Add some AmazingFeature') - Push to the branch (
git push origin feature/AmazingFeature) - Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
For questions or support, please open an issue on GitHub.
Built with β€οΈ using .NET 10 and .NET Aspire