A modern, full-stack web application that provides interactive Q&A functionality using Large Language Models. Features a sleek chat interface with real-time responses, markdown rendering, and structured prompt engineering for consistent, high-quality answers.
- Interactive Chat Interface: Modern, responsive chat UI with typing indicators and message history
- Real-time LLM Integration: Powered by Google Gemini API for intelligent responses
- Markdown Support: Rich text rendering with syntax highlighting and formatting
- Error Handling: Comprehensive error handling with user-friendly messages
- Structured Responses: Consistent answer formatting using prompt engineering
- Cross-platform: Works on desktop and mobile devices
- FastAPI: Modern, fast web framework for building APIs
- Python 3.8+: Core backend language
- Google Gemini API: LLM provider for intelligent responses
- httpx: Async HTTP client for API calls
- Pydantic: Data validation and serialization
- python-dotenv: Environment variable management
- Next.js: React framework with server-side rendering
- React: Component-based UI library
- Tailwind CSS: Utility-first CSS framework
- react-markdown: Markdown rendering with GitHub Flavored Markdown
- remark-gfm: GitHub Flavored Markdown plugin
interactive-qa-llm-app/
├── backend/ # FastAPI backend application
│ ├── app/
│ │ ├── services/
│ │ │ └── llm_client.py # LLM integration and API calls
│ │ ├── utils/
│ │ │ └── prompt.py # Prompt engineering and templates
│ │ ├── main.py # FastAPI app initialization and CORS
│ │ ├── routers.py # API route definitions
│ │ └── schemas.py # Pydantic models for request/response
│ ├── .env # Environment variables (create from .env.example)
│ └── requirements.txt # Python dependencies
├── frontend/ # Next.js frontend application
│ ├── components/
│ │ └── QABox.js # Main chat interface component
│ ├── pages/
│ │ └── index.js # Home page with chat interface
│ ├── styles/
│ │ └── globals.css # Global styles and Tailwind imports
│ ├── package.json # Node.js dependencies and scripts
│ └── next.config.js # Next.js configuration
├── .env.example # Environment variables template
├── .gitignore # Git ignore rules
└── README.md # Project documentation
backend/app/main.py: FastAPI application entry point with CORS middlewarebackend/app/routers.py: Defines the/api/askendpoint for Q&A functionalitybackend/app/services/llm_client.py: Handles communication with Google Gemini APIbackend/app/utils/prompt.py: Contains prompt templates for structured responsesfrontend/components/QABox.js: React component with chat interface and state managementfrontend/pages/index.js: Next.js page that renders the chat interface
- Python 3.8+ (check with
python3 --version) - Node.js 16+ (check with
node --version) - npm or yarn (check with
npm --version) - Git (for cloning the repository)
- Google Gemini API key (free from Google AI Studio)
# Clone the repository
git clone https://github.com/MauriceOmbewa/interactive-qa-llm-app.git
cd interactive-qa-llm-app- Visit Google AI Studio
- Sign in with your Google account
- Click "Get API key" or "Create API key"
- Copy the generated API key (starts with
AIza...)
# Create environment file for backend
cp .env.example backend/.env
# Edit the backend/.env file and replace 'your_api_key_here' with your actual API key
# The file should look like:
# LLM_PROVIDER=gemini
# GEMINI_API_KEY=AIzDxxQ...
# BACKEND_PORT=8000
# FRONTEND_ORIGIN=http://localhost:3000# Navigate to backend directory
cd backend
# Create and activate virtual environment
python3 -m venv .venv
source .venv/bin/activate # On Windows: .venv\Scripts\activate
# Install Python dependencies
pip install -r requirements.txt
# Start the backend server
uvicorn app.main:app --reload --port 8000✅ Backend should now be running at http://localhost:8000
# Navigate to frontend directory (from project root)
cd frontend
# Install Node.js dependencies
npm install
# Start the development server
npm run dev✅ Frontend should now be running at http://localhost:3000
- Open your browser and go to http://localhost:3000
- Type a question in the chat interface (e.g., "What is artificial intelligence?")
- Press Enter or click "Send Message"
- Watch the AI respond with a structured, markdown-formatted answer
- Frontend Application: http://localhost:3000
- Backend API: http://localhost:8000
Create these helper scripts for easier development:
start-backend.sh:
#!/bin/bash
cd backend
source .venv/bin/activate
uvicorn app.main:app --reload --port 8000start-frontend.sh:
#!/bin/bash
cd frontend
npm run devThe application uses environment variables for configuration. Copy .env.example to backend/.env and configure:
# LLM Configuration
LLM_PROVIDER=gemini # Currently supports 'gemini'
GEMINI_API_KEY=your_api_key_here # Your Google Gemini API key
GOOGLE_API_KEY=your_api_key_here # Alternative key name (same value)
# Server Configuration
BACKEND_PORT=8000 # Backend server port
FRONTEND_ORIGIN=http://localhost:3000 # Frontend URL for CORS- Visit Google AI Studio: https://aistudio.google.com/
- Sign in with your Google account
- Create API Key: Look for "Get API key" button
- Copy the key: It should start with something like
AIzaand be about 39 characters long - Paste into .env: Replace
your_api_key_herewith your actual key
.env file to version control. The .gitignore file already excludes it.
The main endpoint for asking questions to the AI assistant.
Request:
curl -X POST http://localhost:8000/api/ask \
-H "Content-Type: application/json" \
-d '{"question": "What is machine learning?"}'Request Body:
{
"question": "What is machine learning?"
}Response:
{
"question": "What is machine learning?",
"answer": "# Summary\n\nMachine learning is a subset of artificial intelligence...",
"raw": {
"candidates": [...],
"usageMetadata": {...}
}
}Response Fields:
question: The original question askedanswer: Formatted markdown response from the AIraw: Complete response object from the Gemini API
Error Responses:
{
"detail": "API key not found. Please set GEMINI_API_KEY in your .env file"
}The system uses carefully crafted prompts to ensure consistent, high-quality responses. The prompt template is defined in backend/app/utils/prompt.py:
def build_prompt(question: str) -> str:
return f"""You are an expert assistant specializing in providing information on international travel documentation requirements, including passports, visas, and other necessary paperwork for visiting various countries. I cannot assist with questions unrelated to travel documents.
When given a travel-related question, respond in Markdown with headings in this exact order:
1. Summary (1-2 sentences)
2. Required Documents (bulleted list)
3. Passport Requirements (bulleted list)
4. Additional Documents (bulleted list)
5. Travel Advisories / Notes (short)
Keep answers practical and concise. If you are unsure, say so and give resources.
User question: {question}"""Prompt Features:
- Structured Output: Ensures consistent response formatting
- Domain Expertise: Focuses on travel documentation
- Markdown Formatting: Enables rich text rendering in the frontend
- Error Handling: Gracefully handles off-topic questions
- ✅ Modern Chat Interface: Clean, intuitive chat UI with message bubbles
- ✅ Real-time Responses: Instant communication with typing indicators
- ✅ Markdown Rendering: Rich text formatting with syntax highlighting
- ✅ Responsive Design: Works perfectly on desktop and mobile
- ✅ Message History: Persistent conversation within session
- ✅ Loading States: Visual feedback during API calls
- ✅ Error Handling: User-friendly error messages
- ✅ Keyboard Shortcuts: Enter to send, Shift+Enter for new line
- ✅ FastAPI Framework: High-performance, modern API
- ✅ Async Processing: Non-blocking request handling
- ✅ CORS Support: Cross-origin requests enabled
- ✅ Input Validation: Pydantic models for data validation
- ✅ Error Handling: Comprehensive error catching and reporting
- ✅ API Documentation: Auto-generated Swagger/OpenAPI docs
- ✅ Environment Configuration: Secure credential management
- ✅ Google Gemini Integration: Latest LLM technology
- ✅ Structured Prompts: Consistent response formatting
- ✅ Token Optimization: Efficient API usage (700 token limit)
- ✅ Provider Abstraction: Easy to switch LLM providers
Test the backend API directly:
# Basic question
curl -X POST http://localhost:8000/api/ask \
-H "Content-Type: application/json" \
-d '{"question":"What documents do I need to travel from Kenya to Ireland?"}'
# Test error handling
curl -X POST http://localhost:8000/api/ask \
-H "Content-Type: application/json" \
-d '{"question":""}'Test the frontend:
- Open http://localhost:3000
- Try these sample questions:
- "What documents do I need to travel from Kenya to Ireland?"
- "What are the passport requirements for US citizens visiting Japan?"
- "Tell me about visa requirements for Germany"
Backend Health:
- Visit http://localhost:8000/docs for API documentation
- Check server logs for any errors
Frontend Health:
- Check browser console for JavaScript errors
- Verify API calls in Network tab
"API key not found" error:
- Ensure
.envfile exists inbackend/directory - Verify
GEMINI_API_KEYis set correctly - Restart the backend server after changing
.env
CORS errors:
- Ensure backend is running on port 8000
- Check
FRONTEND_ORIGINin.envmatches frontend URL
Connection refused:
- Verify both backend (8000) and frontend (3000) are running
- Check firewall settings
- Gemini API Rate Limits: Free tier has request limits (15 requests per minute)
- Token Limits: Responses limited to 700 tokens for optimal performance
- Timeout: API requests timeout after 30 seconds
- API Keys: Never commit
.envfiles to version control - CORS: Configured for localhost development only
- Input Validation: All inputs are validated before processing
- Adapter Pattern:
llm_client.pymakes it easy to switch LLM providers - Async Design: Backend uses async/await for better performance
- Component Architecture: Frontend uses reusable React components
- Hot Reload: Both frontend and backend support hot reloading
- Debugging: Check browser console and terminal logs for errors
- API Testing: Use the Swagger UI at http://localhost:8000/docs
- Fork the repository
- Create a feature branch:
git checkout -b feature/amazing-feature - Commit changes:
git commit -m 'Add amazing feature' - Push to branch:
git push origin feature/amazing-feature - Open a Pull Request
MIT License - see the LICENSE file for details.
If you encounter any issues:
- Review the server logs for error messages
- Ensure your API key is valid and has sufficient quota
- Open an issue on GitHub with detailed error information