Spaces:
Running
Running
Refactor project for Lung Cancer AI Advisor: update app and API descriptions, modify .gitignore to exclude Jupyter notebooks, and remove outdated deployment documentation. Delete unused files and enhance logging for better traceability.
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- .gitignore +2 -2
- 0.0.18 +0 -2
- DEPLOYMENT.md +0 -206
- DEPLOYMENT_SUMMARY.md +0 -296
- Medical_AI_System_Prompts.docx +0 -0
- QUICK_DEPLOY.md +0 -100
- README.md +21 -86
- api/__pycache__/__init__.cpython-311.pyc +0 -0
- api/__pycache__/__init__.cpython-313.pyc +0 -0
- api/__pycache__/app.cpython-311.pyc +0 -0
- api/__pycache__/app.cpython-313.pyc +0 -0
- api/__pycache__/exceptions.cpython-313.pyc +0 -0
- api/__pycache__/middleware.cpython-313.pyc +0 -0
- api/__pycache__/models.cpython-313.pyc +0 -0
- api/app.py +22 -6
- api/exceptions.py +1 -1
- api/middleware.py +6 -5
- api/models.py +1 -1
- api/routers/__pycache__/__init__.cpython-311.pyc +0 -0
- api/routers/__pycache__/__init__.cpython-313.pyc +0 -0
- api/routers/__pycache__/health.cpython-313.pyc +0 -0
- api/routers/__pycache__/medical.cpython-311.pyc +0 -0
- api/routers/__pycache__/medical.cpython-313.pyc +0 -0
- api/routers/auth.py +25 -11
- api/routers/health.py +39 -14
- api/routers/medical.py +120 -15
- app.py +1 -1
- backup/backup_20251022_110950/chunks.pkl +0 -3
- backup/backup_20251022_110950/vector_store/index.faiss +0 -3
- backup/backup_20251022_110950/vector_store/index.pkl +0 -3
- backup/backup_20251022_111044/chunks.pkl +0 -3
- backup/backup_20251022_111044/vector_store/index.faiss +0 -3
- backup/backup_20251022_111044/vector_store/index.pkl +0 -3
- core/__pycache__/__init__.cpython-311.pyc +0 -0
- core/__pycache__/__init__.cpython-313.pyc +0 -0
- core/__pycache__/agent.cpython-311.pyc +0 -0
- core/__pycache__/agent.cpython-313.pyc +0 -0
- core/__pycache__/background_init.cpython-313.pyc +0 -0
- core/__pycache__/config.cpython-311.pyc +0 -0
- core/__pycache__/config.cpython-313.pyc +0 -0
- core/__pycache__/data_loaders.cpython-313.pyc +0 -0
- core/__pycache__/github_storage.cpython-313.pyc +0 -0
- core/__pycache__/retrievers.cpython-313.pyc +0 -0
- core/__pycache__/text_processors.cpython-313.pyc +0 -0
- core/__pycache__/tools.cpython-313.pyc +0 -0
- core/__pycache__/tracing.cpython-313.pyc +0 -0
- core/__pycache__/utils.cpython-313.pyc +0 -0
- core/__pycache__/validation.cpython-313.pyc +0 -0
- core/agent.py +41 -23
- core/context_enrichment.py +8 -2
.gitignore
CHANGED
|
@@ -207,6 +207,6 @@ marimo/_static/
|
|
| 207 |
marimo/_lsp/
|
| 208 |
__marimo__/
|
| 209 |
|
| 210 |
-
|
| 211 |
Lung Cancer Guidelines/
|
| 212 |
-
|
|
|
|
| 207 |
marimo/_lsp/
|
| 208 |
__marimo__/
|
| 209 |
|
| 210 |
+
*.ipynb
|
| 211 |
Lung Cancer Guidelines/
|
| 212 |
+
|
0.0.18
DELETED
|
@@ -1,2 +0,0 @@
|
|
| 1 |
-
Defaulting to user installation because normal site-packages is not writeable
|
| 2 |
-
Requirement already satisfied: python-multipart in c:\users\moaze\appdata\roaming\python\python313\site-packages (0.0.9)
|
|
|
|
|
|
|
|
|
DEPLOYMENT.md
DELETED
|
@@ -1,206 +0,0 @@
|
|
| 1 |
-
# Hugging Face Deployment Guide
|
| 2 |
-
|
| 3 |
-
## Overview
|
| 4 |
-
This guide explains how to deploy the Lung Cancer Clinical Decision Support System to Hugging Face Spaces.
|
| 5 |
-
|
| 6 |
-
## Prerequisites
|
| 7 |
-
- Hugging Face account
|
| 8 |
-
- Git installed locally
|
| 9 |
-
- OpenAI API key (for the agent)
|
| 10 |
-
- GitHub Personal Access Token (for side effects storage)
|
| 11 |
-
|
| 12 |
-
## Deployment Steps
|
| 13 |
-
|
| 14 |
-
### 1. Create a New Hugging Face Space
|
| 15 |
-
|
| 16 |
-
1. Go to [Hugging Face Spaces](https://huggingface.co/spaces)
|
| 17 |
-
2. Click "Create new Space"
|
| 18 |
-
3. Configure:
|
| 19 |
-
- **Space name**: `moazx-api` (or your preferred name)
|
| 20 |
-
- **License**: Choose appropriate license
|
| 21 |
-
- **SDK**: Docker
|
| 22 |
-
- **Hardware**: CPU Basic (or upgrade as needed)
|
| 23 |
-
|
| 24 |
-
### 2. Configure Environment Variables
|
| 25 |
-
|
| 26 |
-
In your Hugging Face Space settings, add these secrets:
|
| 27 |
-
|
| 28 |
-
```bash
|
| 29 |
-
OPENAI_API_KEY=your_openai_api_key_here
|
| 30 |
-
GITHUB_TOKEN=your_github_token_here
|
| 31 |
-
GITHUB_REPO=your_username/your_repo_name
|
| 32 |
-
GITHUB_BRANCH=main
|
| 33 |
-
PORT=7860
|
| 34 |
-
```
|
| 35 |
-
|
| 36 |
-
### 3. Deploy the Application
|
| 37 |
-
|
| 38 |
-
#### Option A: Direct Push to Hugging Face
|
| 39 |
-
|
| 40 |
-
```bash
|
| 41 |
-
# Clone your Hugging Face Space repository
|
| 42 |
-
git clone https://huggingface.co/spaces/YOUR_USERNAME/moazx-api
|
| 43 |
-
cd moazx-api
|
| 44 |
-
|
| 45 |
-
# Copy all backend files
|
| 46 |
-
cp -r /path/to/backend/* .
|
| 47 |
-
|
| 48 |
-
# Add and commit
|
| 49 |
-
git add .
|
| 50 |
-
git commit -m "Initial deployment"
|
| 51 |
-
git push
|
| 52 |
-
```
|
| 53 |
-
|
| 54 |
-
#### Option B: Using Hugging Face CLI
|
| 55 |
-
|
| 56 |
-
```bash
|
| 57 |
-
# Install Hugging Face CLI
|
| 58 |
-
pip install huggingface_hub
|
| 59 |
-
|
| 60 |
-
# Login
|
| 61 |
-
huggingface-cli login
|
| 62 |
-
|
| 63 |
-
# Push to Space
|
| 64 |
-
huggingface-cli upload YOUR_USERNAME/moazx-api . --repo-type=space
|
| 65 |
-
```
|
| 66 |
-
|
| 67 |
-
### 4. Verify Deployment
|
| 68 |
-
|
| 69 |
-
1. Wait for the Space to build (check the logs)
|
| 70 |
-
2. Once running, test the API:
|
| 71 |
-
- Visit: `https://YOUR_USERNAME-moazx-api.hf.space`
|
| 72 |
-
- Check health: `https://YOUR_USERNAME-moazx-api.hf.space/health`
|
| 73 |
-
- View docs: `https://YOUR_USERNAME-moazx-api.hf.space/docs`
|
| 74 |
-
|
| 75 |
-
### 5. Deploy Frontend
|
| 76 |
-
|
| 77 |
-
The frontend is configured to use the API at `https://moazx-api.hf.space`.
|
| 78 |
-
|
| 79 |
-
#### Option A: Serve from the same Space
|
| 80 |
-
The frontend files are already in the `/frontend` directory and will be served automatically.
|
| 81 |
-
|
| 82 |
-
#### Option B: Deploy to separate hosting
|
| 83 |
-
Deploy the frontend folder to:
|
| 84 |
-
- Netlify
|
| 85 |
-
- Vercel
|
| 86 |
-
- GitHub Pages
|
| 87 |
-
- Any static hosting service
|
| 88 |
-
|
| 89 |
-
## API Endpoints
|
| 90 |
-
|
| 91 |
-
Once deployed, your API will be available at:
|
| 92 |
-
|
| 93 |
-
```
|
| 94 |
-
Base URL: https://moazx-api.hf.space
|
| 95 |
-
|
| 96 |
-
Endpoints:
|
| 97 |
-
- GET / - API information
|
| 98 |
-
- GET /health - Health check
|
| 99 |
-
- GET /health/initialization - Initialization status
|
| 100 |
-
- POST /auth/login - User login
|
| 101 |
-
- POST /auth/logout - User logout
|
| 102 |
-
- GET /auth/status - Authentication status
|
| 103 |
-
- GET /ask - Ask a question (non-streaming)
|
| 104 |
-
- GET /ask/stream - Ask a question (streaming)
|
| 105 |
-
- GET /export/{format} - Export conversation
|
| 106 |
-
```
|
| 107 |
-
|
| 108 |
-
## Frontend Configuration
|
| 109 |
-
|
| 110 |
-
The frontend is already configured to use the Hugging Face API:
|
| 111 |
-
|
| 112 |
-
```javascript
|
| 113 |
-
// In frontend/script.js
|
| 114 |
-
this.apiBase = 'https://moazx-api.hf.space';
|
| 115 |
-
```
|
| 116 |
-
|
| 117 |
-
## Authentication
|
| 118 |
-
|
| 119 |
-
The system uses session-based authentication:
|
| 120 |
-
|
| 121 |
-
1. Default credentials (change in production):
|
| 122 |
-
- Username: `admin`
|
| 123 |
-
- Password: `admin123`
|
| 124 |
-
|
| 125 |
-
2. To change credentials, update `api/routers/auth.py`
|
| 126 |
-
|
| 127 |
-
## Monitoring
|
| 128 |
-
|
| 129 |
-
Monitor your deployment:
|
| 130 |
-
|
| 131 |
-
1. **Hugging Face Space Logs**: Check the logs tab in your Space
|
| 132 |
-
2. **API Health**: Monitor `/health` endpoint
|
| 133 |
-
3. **Initialization Status**: Check `/health/initialization`
|
| 134 |
-
|
| 135 |
-
## Troubleshooting
|
| 136 |
-
|
| 137 |
-
### Issue: Space fails to build
|
| 138 |
-
- Check Dockerfile syntax
|
| 139 |
-
- Verify all dependencies in requirements.txt
|
| 140 |
-
- Check Space logs for specific errors
|
| 141 |
-
|
| 142 |
-
### Issue: API returns 500 errors
|
| 143 |
-
- Verify environment variables are set correctly
|
| 144 |
-
- Check that OPENAI_API_KEY is valid
|
| 145 |
-
- Review application logs
|
| 146 |
-
|
| 147 |
-
### Issue: CORS errors in frontend
|
| 148 |
-
- Verify CORS middleware configuration in `api/middleware.py`
|
| 149 |
-
- Ensure frontend URL is in allowed origins
|
| 150 |
-
|
| 151 |
-
### Issue: Slow initialization
|
| 152 |
-
- The system loads models in the background
|
| 153 |
-
- Check `/health/initialization` for status
|
| 154 |
-
- Consider upgrading to better hardware tier
|
| 155 |
-
|
| 156 |
-
## Performance Optimization
|
| 157 |
-
|
| 158 |
-
### For Better Performance:
|
| 159 |
-
1. Upgrade to GPU hardware tier (for faster embeddings)
|
| 160 |
-
2. Use persistent storage for cached data
|
| 161 |
-
3. Enable CDN for frontend assets
|
| 162 |
-
|
| 163 |
-
### Memory Management:
|
| 164 |
-
- Current setup uses CPU-optimized models
|
| 165 |
-
- Faiss-cpu for vector search
|
| 166 |
-
- Sentence-transformers for embeddings
|
| 167 |
-
|
| 168 |
-
## Security Considerations
|
| 169 |
-
|
| 170 |
-
1. **Change default credentials** in production
|
| 171 |
-
2. **Rotate API keys** regularly
|
| 172 |
-
3. **Enable rate limiting** (already configured)
|
| 173 |
-
4. **Use HTTPS** (automatic on Hugging Face)
|
| 174 |
-
5. **Review CORS settings** for production
|
| 175 |
-
|
| 176 |
-
## Updating the Deployment
|
| 177 |
-
|
| 178 |
-
To update your deployment:
|
| 179 |
-
|
| 180 |
-
```bash
|
| 181 |
-
# Make changes locally
|
| 182 |
-
git add .
|
| 183 |
-
git commit -m "Update description"
|
| 184 |
-
git push
|
| 185 |
-
|
| 186 |
-
# Hugging Face will automatically rebuild
|
| 187 |
-
```
|
| 188 |
-
|
| 189 |
-
## Cost Considerations
|
| 190 |
-
|
| 191 |
-
- **Free tier**: CPU Basic (limited resources)
|
| 192 |
-
- **Paid tiers**: Better performance and reliability
|
| 193 |
-
- **API costs**: OpenAI API usage (pay per token)
|
| 194 |
-
|
| 195 |
-
## Support
|
| 196 |
-
|
| 197 |
-
For issues:
|
| 198 |
-
1. Check Hugging Face Space logs
|
| 199 |
-
2. Review application logs at `/logs/app.log`
|
| 200 |
-
3. Test endpoints using `/docs` (Swagger UI)
|
| 201 |
-
|
| 202 |
-
## Additional Resources
|
| 203 |
-
|
| 204 |
-
- [Hugging Face Spaces Documentation](https://huggingface.co/docs/hub/spaces)
|
| 205 |
-
- [FastAPI Documentation](https://fastapi.tiangolo.com/)
|
| 206 |
-
- [Docker Documentation](https://docs.docker.com/)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
DEPLOYMENT_SUMMARY.md
DELETED
|
@@ -1,296 +0,0 @@
|
|
| 1 |
-
# Deployment Summary - Hugging Face Integration
|
| 2 |
-
|
| 3 |
-
## Changes Made for Hugging Face Deployment
|
| 4 |
-
|
| 5 |
-
### 1. Frontend Configuration (`frontend/script.js`)
|
| 6 |
-
**Changed:**
|
| 7 |
-
- Updated API base URL from `http://127.0.0.1:8000` to `https://moazx-api.hf.space`
|
| 8 |
-
|
| 9 |
-
**Impact:**
|
| 10 |
-
- Frontend now connects to the deployed Hugging Face Space API
|
| 11 |
-
- Works seamlessly with the production backend
|
| 12 |
-
|
| 13 |
-
### 2. Backend Configuration (`app.py`)
|
| 14 |
-
**Changed:**
|
| 15 |
-
- Updated host from `127.0.0.1` to `0.0.0.0` (bind to all interfaces)
|
| 16 |
-
- Updated port to use environment variable `PORT` (default: 7860)
|
| 17 |
-
- Disabled reload for production
|
| 18 |
-
- Configured for single worker deployment
|
| 19 |
-
|
| 20 |
-
**Impact:**
|
| 21 |
-
- Backend now accepts connections from external sources
|
| 22 |
-
- Compatible with Hugging Face Spaces port configuration
|
| 23 |
-
- Optimized for production deployment
|
| 24 |
-
|
| 25 |
-
### 3. CORS Middleware (`api/middleware.py`)
|
| 26 |
-
**Already Configured:**
|
| 27 |
-
- CORS middleware already includes `https://moazx-api.hf.space`
|
| 28 |
-
- Supports multiple origins for development and production
|
| 29 |
-
- Allows credentials for authentication
|
| 30 |
-
|
| 31 |
-
**No changes needed** - already production-ready!
|
| 32 |
-
|
| 33 |
-
### 4. Docker Configuration (`Dockerfile`)
|
| 34 |
-
**Already Configured:**
|
| 35 |
-
- Multi-stage build for optimized image size
|
| 36 |
-
- Exposes port 7860 (Hugging Face standard)
|
| 37 |
-
- Runs as non-root user for security
|
| 38 |
-
- Uses Python 3.11-slim for minimal footprint
|
| 39 |
-
|
| 40 |
-
**No changes needed** - already production-ready!
|
| 41 |
-
|
| 42 |
-
### 5. Environment Variables (`.env.example`)
|
| 43 |
-
**Updated:**
|
| 44 |
-
- Added comprehensive documentation for all environment variables
|
| 45 |
-
- Included GitHub storage configuration
|
| 46 |
-
- Added server configuration (PORT, HOST)
|
| 47 |
-
- Added CORS configuration
|
| 48 |
-
- Documented authentication credentials
|
| 49 |
-
|
| 50 |
-
**Action Required:**
|
| 51 |
-
- Copy `.env.example` to `.env` and fill in your actual values
|
| 52 |
-
- Set these as secrets in Hugging Face Space settings
|
| 53 |
-
|
| 54 |
-
### 6. Documentation
|
| 55 |
-
**Created/Updated:**
|
| 56 |
-
- `DEPLOYMENT.md` - Comprehensive deployment guide
|
| 57 |
-
- `README.md` - Updated with full feature list and usage instructions
|
| 58 |
-
- `.env.example` - Complete environment variable documentation
|
| 59 |
-
|
| 60 |
-
## Deployment Checklist
|
| 61 |
-
|
| 62 |
-
### ✅ Code Changes Complete
|
| 63 |
-
- [x] Frontend API endpoint updated
|
| 64 |
-
- [x] Backend configured for production
|
| 65 |
-
- [x] CORS properly configured
|
| 66 |
-
- [x] Docker configuration verified
|
| 67 |
-
- [x] Environment variables documented
|
| 68 |
-
|
| 69 |
-
### 📋 Next Steps for Deployment
|
| 70 |
-
|
| 71 |
-
1. **Prepare Hugging Face Space**
|
| 72 |
-
```bash
|
| 73 |
-
# Create a new Space on Hugging Face
|
| 74 |
-
# Name: moazx-api
|
| 75 |
-
# SDK: Docker
|
| 76 |
-
# Hardware: CPU Basic (or better)
|
| 77 |
-
```
|
| 78 |
-
|
| 79 |
-
2. **Set Environment Variables in Hugging Face**
|
| 80 |
-
Go to Space Settings → Variables and Secrets:
|
| 81 |
-
```
|
| 82 |
-
OPENAI_API_KEY=your_actual_key
|
| 83 |
-
GITHUB_TOKEN=your_github_token
|
| 84 |
-
GITHUB_REPO=username/repo
|
| 85 |
-
GITHUB_BRANCH=main
|
| 86 |
-
PORT=7860
|
| 87 |
-
```
|
| 88 |
-
|
| 89 |
-
3. **Deploy Code to Hugging Face**
|
| 90 |
-
```bash
|
| 91 |
-
# Clone your HF Space
|
| 92 |
-
git clone https://huggingface.co/spaces/YOUR_USERNAME/moazx-api
|
| 93 |
-
cd moazx-api
|
| 94 |
-
|
| 95 |
-
# Copy all backend files
|
| 96 |
-
cp -r /path/to/backend/* .
|
| 97 |
-
|
| 98 |
-
# Commit and push
|
| 99 |
-
git add .
|
| 100 |
-
git commit -m "Initial deployment"
|
| 101 |
-
git push
|
| 102 |
-
```
|
| 103 |
-
|
| 104 |
-
4. **Verify Deployment**
|
| 105 |
-
- Wait for build to complete (check logs)
|
| 106 |
-
- Test health endpoint: `https://moazx-api.hf.space/health`
|
| 107 |
-
- Test API docs: `https://moazx-api.hf.space/docs`
|
| 108 |
-
- Test frontend by opening `frontend/index.html`
|
| 109 |
-
|
| 110 |
-
5. **Test Functionality**
|
| 111 |
-
- Login with credentials (admin/admin123)
|
| 112 |
-
- Ask a test question
|
| 113 |
-
- Verify citations are working
|
| 114 |
-
- Test export functionality
|
| 115 |
-
- Check streaming responses
|
| 116 |
-
|
| 117 |
-
## File Structure for Deployment
|
| 118 |
-
|
| 119 |
-
```
|
| 120 |
-
backend/
|
| 121 |
-
├── api/
|
| 122 |
-
│ ├── __init__.py
|
| 123 |
-
│ ├── app.py # Main FastAPI application
|
| 124 |
-
│ ├── middleware.py # CORS, auth, rate limiting
|
| 125 |
-
│ ├── exceptions.py
|
| 126 |
-
│ ├── models.py
|
| 127 |
-
│ └── routers/
|
| 128 |
-
│ ├── medical.py # Medical query endpoints
|
| 129 |
-
│ ├── health.py # Health check endpoints
|
| 130 |
-
│ ├── export.py # Export endpoints
|
| 131 |
-
│ └── auth.py # Authentication endpoints
|
| 132 |
-
├── core/
|
| 133 |
-
│ ├── agent.py # LangChain agent configuration ⭐
|
| 134 |
-
│ ├── tools.py # Agent tools
|
| 135 |
-
│ ├── retrievers.py # Hybrid search
|
| 136 |
-
│ ├── context_enrichment.py # Context page enrichment
|
| 137 |
-
│ ├── vector_store.py # FAISS vector store
|
| 138 |
-
│ └── ...
|
| 139 |
-
├── frontend/
|
| 140 |
-
│ ├── index.html # Main UI
|
| 141 |
-
│ ├── script.js # Frontend logic ⭐ (updated)
|
| 142 |
-
│ ├── styles.css # Styling
|
| 143 |
-
│ └── login.html # Login page
|
| 144 |
-
├── data/
|
| 145 |
-
│ ├── chunks.pkl # Preprocessed document chunks
|
| 146 |
-
│ └── medical_terms_cache.json
|
| 147 |
-
├── Dockerfile # Docker configuration
|
| 148 |
-
├── requirements.txt # Python dependencies
|
| 149 |
-
├── app.py # Entry point ⭐ (updated)
|
| 150 |
-
├── README.md # Documentation ⭐ (updated)
|
| 151 |
-
├── DEPLOYMENT.md # Deployment guide ⭐ (new)
|
| 152 |
-
├── .env.example # Environment variables ⭐ (updated)
|
| 153 |
-
└── .gitignore
|
| 154 |
-
|
| 155 |
-
⭐ = Files modified/created for deployment
|
| 156 |
-
```
|
| 157 |
-
|
| 158 |
-
## Configuration Summary
|
| 159 |
-
|
| 160 |
-
### API Endpoint
|
| 161 |
-
- **Production**: `https://moazx-api.hf.space`
|
| 162 |
-
- **Local Dev**: `http://localhost:7860`
|
| 163 |
-
|
| 164 |
-
### Authentication
|
| 165 |
-
- **Default Username**: `admin`
|
| 166 |
-
- **Default Password**: `admin123`
|
| 167 |
-
- **⚠️ Change in production!**
|
| 168 |
-
|
| 169 |
-
### Required Environment Variables
|
| 170 |
-
```bash
|
| 171 |
-
OPENAI_API_KEY=required
|
| 172 |
-
GITHUB_TOKEN=optional (for side effects)
|
| 173 |
-
GITHUB_REPO=optional
|
| 174 |
-
PORT=7860
|
| 175 |
-
```
|
| 176 |
-
|
| 177 |
-
### Optional Environment Variables
|
| 178 |
-
```bash
|
| 179 |
-
LANGSMITH_API_KEY=optional (for tracing)
|
| 180 |
-
ALLOWED_ORIGINS=optional (auto-configured)
|
| 181 |
-
AUTH_USERNAME=optional (defaults to admin)
|
| 182 |
-
AUTH_PASSWORD=optional (defaults to admin123)
|
| 183 |
-
```
|
| 184 |
-
|
| 185 |
-
## Testing the Deployment
|
| 186 |
-
|
| 187 |
-
### 1. Health Check
|
| 188 |
-
```bash
|
| 189 |
-
curl https://moazx-api.hf.space/health
|
| 190 |
-
```
|
| 191 |
-
|
| 192 |
-
Expected response:
|
| 193 |
-
```json
|
| 194 |
-
{
|
| 195 |
-
"status": "healthy",
|
| 196 |
-
"timestamp": "2025-01-22T...",
|
| 197 |
-
"version": "1.0.0"
|
| 198 |
-
}
|
| 199 |
-
```
|
| 200 |
-
|
| 201 |
-
### 2. API Documentation
|
| 202 |
-
Visit: `https://moazx-api.hf.space/docs`
|
| 203 |
-
|
| 204 |
-
### 3. Test Query (with authentication)
|
| 205 |
-
```bash
|
| 206 |
-
# Login first
|
| 207 |
-
curl -X POST https://moazx-api.hf.space/auth/login \
|
| 208 |
-
-H "Content-Type: application/json" \
|
| 209 |
-
-d '{"username":"admin","password":"admin123"}' \
|
| 210 |
-
-c cookies.txt
|
| 211 |
-
|
| 212 |
-
# Ask a question
|
| 213 |
-
curl -X GET "https://moazx-api.hf.space/ask?query=What%20is%20EGFR%20mutation&session_id=test123" \
|
| 214 |
-
-b cookies.txt
|
| 215 |
-
```
|
| 216 |
-
|
| 217 |
-
## Troubleshooting
|
| 218 |
-
|
| 219 |
-
### Issue: Build fails on Hugging Face
|
| 220 |
-
- Check Dockerfile syntax
|
| 221 |
-
- Verify requirements.txt has all dependencies
|
| 222 |
-
- Check Space logs for specific errors
|
| 223 |
-
|
| 224 |
-
### Issue: API returns 500 errors
|
| 225 |
-
- Verify OPENAI_API_KEY is set correctly
|
| 226 |
-
- Check application logs in Space
|
| 227 |
-
- Verify data files (chunks.pkl) are present
|
| 228 |
-
|
| 229 |
-
### Issue: Frontend can't connect
|
| 230 |
-
- Verify CORS settings in middleware.py
|
| 231 |
-
- Check that frontend is using correct API URL
|
| 232 |
-
- Test API endpoint directly first
|
| 233 |
-
|
| 234 |
-
### Issue: Authentication fails
|
| 235 |
-
- Verify credentials in auth.py
|
| 236 |
-
- Check cookie settings
|
| 237 |
-
- Ensure HTTPS is being used
|
| 238 |
-
|
| 239 |
-
## Performance Considerations
|
| 240 |
-
|
| 241 |
-
### Current Setup
|
| 242 |
-
- **CPU-optimized**: Uses faiss-cpu and CPU-only PyTorch
|
| 243 |
-
- **Memory**: ~2-4GB RAM usage
|
| 244 |
-
- **Startup time**: 30-60 seconds (background initialization)
|
| 245 |
-
|
| 246 |
-
### Optimization Options
|
| 247 |
-
1. **Upgrade to GPU tier** - Faster embeddings and inference
|
| 248 |
-
2. **Enable caching** - Cache frequently accessed documents
|
| 249 |
-
3. **Optimize chunk size** - Reduce memory footprint
|
| 250 |
-
4. **Use persistent storage** - Store vector index on disk
|
| 251 |
-
|
| 252 |
-
## Security Checklist
|
| 253 |
-
|
| 254 |
-
- [x] HTTPS enabled (automatic on Hugging Face)
|
| 255 |
-
- [x] Session-based authentication implemented
|
| 256 |
-
- [x] Rate limiting configured (100 req/min)
|
| 257 |
-
- [x] CORS properly configured
|
| 258 |
-
- [x] Input validation in place
|
| 259 |
-
- [ ] Change default credentials (TODO in production)
|
| 260 |
-
- [ ] Rotate API keys regularly (TODO)
|
| 261 |
-
- [ ] Enable monitoring/logging (TODO)
|
| 262 |
-
|
| 263 |
-
## Monitoring
|
| 264 |
-
|
| 265 |
-
### Key Metrics to Monitor
|
| 266 |
-
1. **API Response Time**: Check X-Process-Time header
|
| 267 |
-
2. **Error Rate**: Monitor 500 errors in logs
|
| 268 |
-
3. **Initialization Status**: `/health/initialization` endpoint
|
| 269 |
-
4. **OpenAI API Usage**: Monitor token consumption
|
| 270 |
-
|
| 271 |
-
### Logs Location
|
| 272 |
-
- Hugging Face Space logs tab
|
| 273 |
-
- Application logs: `/logs/app.log`
|
| 274 |
-
|
| 275 |
-
## Next Steps After Deployment
|
| 276 |
-
|
| 277 |
-
1. **Test thoroughly** with real clinical questions
|
| 278 |
-
2. **Monitor performance** and optimize as needed
|
| 279 |
-
3. **Update documentation** with actual deployment URL
|
| 280 |
-
4. **Set up monitoring** and alerts
|
| 281 |
-
5. **Plan for scaling** if usage increases
|
| 282 |
-
6. **Regular updates** to medical guidelines
|
| 283 |
-
7. **Security audit** and credential rotation
|
| 284 |
-
|
| 285 |
-
## Support Resources
|
| 286 |
-
|
| 287 |
-
- **Deployment Guide**: See `DEPLOYMENT.md`
|
| 288 |
-
- **API Documentation**: Visit `/docs` on deployed Space
|
| 289 |
-
- **Hugging Face Docs**: https://huggingface.co/docs/hub/spaces
|
| 290 |
-
- **FastAPI Docs**: https://fastapi.tiangolo.com/
|
| 291 |
-
|
| 292 |
-
---
|
| 293 |
-
|
| 294 |
-
**Deployment Status**: ✅ Ready for Deployment
|
| 295 |
-
|
| 296 |
-
All code changes are complete. Follow the deployment checklist to deploy to Hugging Face Spaces.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Medical_AI_System_Prompts.docx
ADDED
|
Binary file (44.4 kB). View file
|
|
|
QUICK_DEPLOY.md
DELETED
|
@@ -1,100 +0,0 @@
|
|
| 1 |
-
# Quick Deployment Guide - Hugging Face
|
| 2 |
-
|
| 3 |
-
## 🚀 Deploy in 5 Steps
|
| 4 |
-
|
| 5 |
-
### Step 1: Create Hugging Face Space
|
| 6 |
-
1. Go to https://huggingface.co/spaces
|
| 7 |
-
2. Click "Create new Space"
|
| 8 |
-
3. Settings:
|
| 9 |
-
- Name: `moazx-api`
|
| 10 |
-
- SDK: **Docker**
|
| 11 |
-
- Hardware: CPU Basic (minimum)
|
| 12 |
-
|
| 13 |
-
### Step 2: Set Environment Variables
|
| 14 |
-
In Space Settings → Secrets, add:
|
| 15 |
-
```
|
| 16 |
-
OPENAI_API_KEY=sk-...your-key...
|
| 17 |
-
GITHUB_TOKEN=ghp_...your-token...
|
| 18 |
-
GITHUB_REPO=username/repo-name
|
| 19 |
-
GITHUB_BRANCH=main
|
| 20 |
-
PORT=7860
|
| 21 |
-
```
|
| 22 |
-
|
| 23 |
-
### Step 3: Push Code
|
| 24 |
-
```bash
|
| 25 |
-
# Clone your Space
|
| 26 |
-
git clone https://huggingface.co/spaces/YOUR_USERNAME/moazx-api
|
| 27 |
-
cd moazx-api
|
| 28 |
-
|
| 29 |
-
# Copy all files from backend folder
|
| 30 |
-
cp -r /path/to/backend/* .
|
| 31 |
-
|
| 32 |
-
# Commit and push
|
| 33 |
-
git add .
|
| 34 |
-
git commit -m "Deploy Lung Cancer Clinical Decision Support System"
|
| 35 |
-
git push
|
| 36 |
-
```
|
| 37 |
-
|
| 38 |
-
### Step 4: Wait for Build
|
| 39 |
-
- Watch the build logs in your Space
|
| 40 |
-
- Wait for "Running" status (30-60 seconds)
|
| 41 |
-
|
| 42 |
-
### Step 5: Test
|
| 43 |
-
```bash
|
| 44 |
-
# Test health endpoint
|
| 45 |
-
curl https://YOUR_USERNAME-moazx-api.hf.space/health
|
| 46 |
-
|
| 47 |
-
# Visit API docs
|
| 48 |
-
open https://YOUR_USERNAME-moazx-api.hf.space/docs
|
| 49 |
-
```
|
| 50 |
-
|
| 51 |
-
## ✅ Verification Checklist
|
| 52 |
-
|
| 53 |
-
- [ ] Space is running (green status)
|
| 54 |
-
- [ ] `/health` returns `{"status": "healthy"}`
|
| 55 |
-
- [ ] `/docs` shows API documentation
|
| 56 |
-
- [ ] Can login with admin/admin123
|
| 57 |
-
- [ ] Can ask a test question
|
| 58 |
-
- [ ] Streaming responses work
|
| 59 |
-
- [ ] Citations appear in answers
|
| 60 |
-
|
| 61 |
-
## 🔧 Quick Fixes
|
| 62 |
-
|
| 63 |
-
### Build Failed?
|
| 64 |
-
- Check Dockerfile syntax
|
| 65 |
-
- Verify all files are committed
|
| 66 |
-
- Check Space logs for errors
|
| 67 |
-
|
| 68 |
-
### API Not Responding?
|
| 69 |
-
- Verify OPENAI_API_KEY is set
|
| 70 |
-
- Check Space logs
|
| 71 |
-
- Restart the Space
|
| 72 |
-
|
| 73 |
-
### Frontend Can't Connect?
|
| 74 |
-
- Update `frontend/script.js` with your Space URL:
|
| 75 |
-
```javascript
|
| 76 |
-
this.apiBase = 'https://YOUR_USERNAME-moazx-api.hf.space';
|
| 77 |
-
```
|
| 78 |
-
|
| 79 |
-
## 📱 Access Your Deployment
|
| 80 |
-
|
| 81 |
-
- **API**: `https://YOUR_USERNAME-moazx-api.hf.space`
|
| 82 |
-
- **Docs**: `https://YOUR_USERNAME-moazx-api.hf.space/docs`
|
| 83 |
-
- **Health**: `https://YOUR_USERNAME-moazx-api.hf.space/health`
|
| 84 |
-
|
| 85 |
-
## 🔐 Default Credentials
|
| 86 |
-
|
| 87 |
-
- Username: `admin`
|
| 88 |
-
- Password: `admin123`
|
| 89 |
-
|
| 90 |
-
**⚠️ Change these in production!**
|
| 91 |
-
|
| 92 |
-
## 📚 Full Documentation
|
| 93 |
-
|
| 94 |
-
- Detailed guide: `DEPLOYMENT.md`
|
| 95 |
-
- Complete summary: `DEPLOYMENT_SUMMARY.md`
|
| 96 |
-
- README: `README.md`
|
| 97 |
-
|
| 98 |
-
---
|
| 99 |
-
|
| 100 |
-
**Need Help?** Check the full deployment guide in `DEPLOYMENT.md`
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
README.md
CHANGED
|
@@ -33,22 +33,16 @@ A specialized AI-powered clinical decision support system for thoracic oncologis
|
|
| 33 |
## 🚀 Deployment
|
| 34 |
|
| 35 |
### Live API
|
| 36 |
-
The API is deployed at: **https://moazx-
|
| 37 |
|
| 38 |
### Quick Start
|
| 39 |
|
| 40 |
1. **Access the API**:
|
| 41 |
-
- API Docs: https://moazx-
|
| 42 |
-
- Health Check: https://moazx-
|
| 43 |
|
| 44 |
-
2. **Use the Frontend**:
|
| 45 |
-
- Open `frontend/index.html` in a browser
|
| 46 |
-
- Login with credentials (default: admin/admin123)
|
| 47 |
-
- Start asking clinical questions
|
| 48 |
|
| 49 |
-
### Deploy Your Own Instance
|
| 50 |
|
| 51 |
-
See [DEPLOYMENT.md](DEPLOYMENT.md) for detailed deployment instructions.
|
| 52 |
|
| 53 |
## 📚 API Endpoints
|
| 54 |
|
|
@@ -61,10 +55,22 @@ See [DEPLOYMENT.md](DEPLOYMENT.md) for detailed deployment instructions.
|
|
| 61 |
- `POST /auth/login` - User login
|
| 62 |
- `POST /auth/logout` - User logout
|
| 63 |
- `GET /auth/status` - Check authentication status
|
| 64 |
-
|
| 65 |
### Medical Queries
|
| 66 |
-
- `
|
| 67 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 68 |
|
| 69 |
### Export
|
| 70 |
- `GET /export/{format}?session_id={id}` - Export conversation (format: pdf, docx, txt)
|
|
@@ -116,46 +122,6 @@ See `.env.example` for all configuration options:
|
|
| 116 |
- `PORT`: Server port (default: 7860)
|
| 117 |
- `ALLOWED_ORIGINS`: CORS allowed origins
|
| 118 |
|
| 119 |
-
### Authentication
|
| 120 |
-
|
| 121 |
-
Default credentials (change in production):
|
| 122 |
-
- Username: `admin`
|
| 123 |
-
- Password: `admin123`
|
| 124 |
-
|
| 125 |
-
Update in `api/routers/auth.py` or via environment variables.
|
| 126 |
-
|
| 127 |
-
## 📖 Usage Examples
|
| 128 |
-
|
| 129 |
-
### Using the API
|
| 130 |
-
|
| 131 |
-
```python
|
| 132 |
-
import requests
|
| 133 |
-
|
| 134 |
-
# Login
|
| 135 |
-
response = requests.post(
|
| 136 |
-
"https://moazx-api.hf.space/auth/login",
|
| 137 |
-
json={"username": "admin", "password": "admin123"}
|
| 138 |
-
)
|
| 139 |
-
cookies = response.cookies
|
| 140 |
-
|
| 141 |
-
# Ask a question
|
| 142 |
-
response = requests.get(
|
| 143 |
-
"https://moazx-api.hf.space/ask",
|
| 144 |
-
params={
|
| 145 |
-
"query": "What is the first-line treatment for EGFR-mutated NSCLC?",
|
| 146 |
-
"session_id": "my-session-123"
|
| 147 |
-
},
|
| 148 |
-
cookies=cookies
|
| 149 |
-
)
|
| 150 |
-
print(response.json()["response"])
|
| 151 |
-
```
|
| 152 |
-
|
| 153 |
-
### Using the Frontend
|
| 154 |
-
|
| 155 |
-
1. Open `frontend/index.html`
|
| 156 |
-
2. Login with credentials
|
| 157 |
-
3. Type your clinical question
|
| 158 |
-
4. Receive evidence-based answers with citations
|
| 159 |
|
| 160 |
## 🏗️ Architecture
|
| 161 |
|
|
@@ -178,10 +144,11 @@ print(response.json()["response"])
|
|
| 178 |
## 📊 Response Format
|
| 179 |
|
| 180 |
The agent provides:
|
| 181 |
-
- **Concise,
|
| 182 |
- **Inline citations** after each statement
|
| 183 |
- **Comprehensive reference list** at the end
|
| 184 |
-
- **Structured formatting** for easy scanning
|
|
|
|
| 185 |
|
| 186 |
Example:
|
| 187 |
```
|
|
@@ -194,35 +161,3 @@ Example:
|
|
| 194 |
**References:**
|
| 195 |
(Source: NCCN.pdf, Pages: 45, 46, Provider: NCCN, Location: NSCLC Treatment Algorithm)
|
| 196 |
```
|
| 197 |
-
|
| 198 |
-
## 🔒 Security
|
| 199 |
-
|
| 200 |
-
- Session-based authentication
|
| 201 |
-
- Rate limiting (100 requests/minute)
|
| 202 |
-
- CORS protection
|
| 203 |
-
- Input validation
|
| 204 |
-
- Secure cookie handling
|
| 205 |
-
|
| 206 |
-
## 📝 License
|
| 207 |
-
|
| 208 |
-
[Add your license here]
|
| 209 |
-
|
| 210 |
-
## 🤝 Contributing
|
| 211 |
-
|
| 212 |
-
Contributions are welcome! Please read the contributing guidelines first.
|
| 213 |
-
|
| 214 |
-
## 📧 Support
|
| 215 |
-
|
| 216 |
-
For issues or questions:
|
| 217 |
-
- Check the [DEPLOYMENT.md](DEPLOYMENT.md) guide
|
| 218 |
-
- Review API docs at `/docs`
|
| 219 |
-
- Open an issue on GitHub
|
| 220 |
-
|
| 221 |
-
## 🙏 Acknowledgments
|
| 222 |
-
|
| 223 |
-
Built with:
|
| 224 |
-
- FastAPI
|
| 225 |
-
- LangChain
|
| 226 |
-
- OpenAI
|
| 227 |
-
- FAISS
|
| 228 |
-
- Sentence Transformers
|
|
|
|
| 33 |
## 🚀 Deployment
|
| 34 |
|
| 35 |
### Live API
|
| 36 |
+
The API is deployed at: **https://moazx-lung-cancer-ai-advisor.hf.space**
|
| 37 |
|
| 38 |
### Quick Start
|
| 39 |
|
| 40 |
1. **Access the API**:
|
| 41 |
+
- API Docs: https://moazx-lung-cancer-ai-advisor.hf.space/docs
|
| 42 |
+
- Health Check: https://moazx-lung-cancer-ai-advisor.hf.space/health
|
| 43 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 44 |
|
|
|
|
| 45 |
|
|
|
|
| 46 |
|
| 47 |
## 📚 API Endpoints
|
| 48 |
|
|
|
|
| 55 |
- `POST /auth/login` - User login
|
| 56 |
- `POST /auth/logout` - User logout
|
| 57 |
- `GET /auth/status` - Check authentication status
|
| 58 |
+
up
|
| 59 |
### Medical Queries
|
| 60 |
+
- `POST /ask` - Ask a question (complete response)
|
| 61 |
+
```json
|
| 62 |
+
{
|
| 63 |
+
"query": "What are the early symptoms of lung cancer?",
|
| 64 |
+
"session_id": "user_123_session_1699612345"
|
| 65 |
+
}
|
| 66 |
+
```
|
| 67 |
+
- `POST /ask/stream` - Ask a question (streaming response)
|
| 68 |
+
```json
|
| 69 |
+
{
|
| 70 |
+
"query": "What are the treatment options?",
|
| 71 |
+
"session_id": "user_123_session_1699612345"
|
| 72 |
+
}
|
| 73 |
+
```
|
| 74 |
|
| 75 |
### Export
|
| 76 |
- `GET /export/{format}?session_id={id}` - Export conversation (format: pdf, docx, txt)
|
|
|
|
| 122 |
- `PORT`: Server port (default: 7860)
|
| 123 |
- `ALLOWED_ORIGINS`: CORS allowed origins
|
| 124 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 125 |
|
| 126 |
## 🏗️ Architecture
|
| 127 |
|
|
|
|
| 144 |
## 📊 Response Format
|
| 145 |
|
| 146 |
The agent provides:
|
| 147 |
+
- **Concise, evidence-based answers** for busy clinicians
|
| 148 |
- **Inline citations** after each statement
|
| 149 |
- **Comprehensive reference list** at the end
|
| 150 |
+
- **Structured markdown formatting** for easy scanning
|
| 151 |
+
- **Real-time streaming** for immediate feedback
|
| 152 |
|
| 153 |
Example:
|
| 154 |
```
|
|
|
|
| 161 |
**References:**
|
| 162 |
(Source: NCCN.pdf, Pages: 45, 46, Provider: NCCN, Location: NSCLC Treatment Algorithm)
|
| 163 |
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
api/__pycache__/__init__.cpython-311.pyc
CHANGED
|
Binary files a/api/__pycache__/__init__.cpython-311.pyc and b/api/__pycache__/__init__.cpython-311.pyc differ
|
|
|
api/__pycache__/__init__.cpython-313.pyc
CHANGED
|
Binary files a/api/__pycache__/__init__.cpython-313.pyc and b/api/__pycache__/__init__.cpython-313.pyc differ
|
|
|
api/__pycache__/app.cpython-311.pyc
CHANGED
|
Binary files a/api/__pycache__/app.cpython-311.pyc and b/api/__pycache__/app.cpython-311.pyc differ
|
|
|
api/__pycache__/app.cpython-313.pyc
CHANGED
|
Binary files a/api/__pycache__/app.cpython-313.pyc and b/api/__pycache__/app.cpython-313.pyc differ
|
|
|
api/__pycache__/exceptions.cpython-313.pyc
CHANGED
|
Binary files a/api/__pycache__/exceptions.cpython-313.pyc and b/api/__pycache__/exceptions.cpython-313.pyc differ
|
|
|
api/__pycache__/middleware.cpython-313.pyc
CHANGED
|
Binary files a/api/__pycache__/middleware.cpython-313.pyc and b/api/__pycache__/middleware.cpython-313.pyc differ
|
|
|
api/__pycache__/models.cpython-313.pyc
CHANGED
|
Binary files a/api/__pycache__/models.cpython-313.pyc and b/api/__pycache__/models.cpython-313.pyc differ
|
|
|
api/app.py
CHANGED
|
@@ -31,7 +31,7 @@ logger = logging.getLogger(__name__)
|
|
| 31 |
async def lifespan(app: FastAPI):
|
| 32 |
"""Application lifespan management with background initialization"""
|
| 33 |
# Startup
|
| 34 |
-
logger.info("Starting
|
| 35 |
|
| 36 |
# Start background initialization of heavy components
|
| 37 |
try:
|
|
@@ -46,13 +46,29 @@ async def lifespan(app: FastAPI):
|
|
| 46 |
yield
|
| 47 |
|
| 48 |
# Shutdown
|
| 49 |
-
logger.info("Shutting down
|
| 50 |
|
| 51 |
|
| 52 |
# Create FastAPI application
|
| 53 |
app = FastAPI(
|
| 54 |
-
title="
|
| 55 |
-
description="
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 56 |
version="1.0.0",
|
| 57 |
docs_url="/docs",
|
| 58 |
redoc_url="/redoc",
|
|
@@ -83,9 +99,9 @@ app.include_router(export.router)
|
|
| 83 |
async def root():
|
| 84 |
"""Root endpoint with API information"""
|
| 85 |
return {
|
| 86 |
-
"name": "
|
| 87 |
"version": "1.0.0",
|
| 88 |
-
"description": "
|
| 89 |
"docs": "/docs",
|
| 90 |
"health": "/health",
|
| 91 |
"endpoints": {
|
|
|
|
| 31 |
async def lifespan(app: FastAPI):
|
| 32 |
"""Application lifespan management with background initialization"""
|
| 33 |
# Startup
|
| 34 |
+
logger.info("Starting Lung Cancer AI Advisor API...")
|
| 35 |
|
| 36 |
# Start background initialization of heavy components
|
| 37 |
try:
|
|
|
|
| 46 |
yield
|
| 47 |
|
| 48 |
# Shutdown
|
| 49 |
+
logger.info("Shutting down Lung Cancer AI Advisor API...")
|
| 50 |
|
| 51 |
|
| 52 |
# Create FastAPI application
|
| 53 |
app = FastAPI(
|
| 54 |
+
title="Lung Cancer AI Advisor API",
|
| 55 |
+
description="""AI-Powered Lung Cancer Information & Support API
|
| 56 |
+
|
| 57 |
+
This API provides intelligent responses to lung cancer-related queries using advanced AI and medical knowledge retrieval.
|
| 58 |
+
|
| 59 |
+
**Key Features:**
|
| 60 |
+
- Intelligent Query Processing: AI agent automatically selects appropriate tools and data sources
|
| 61 |
+
- Session Management: Maintains conversation context across multiple queries
|
| 62 |
+
- Streaming Support: Real-time response streaming for better UX
|
| 63 |
+
- Medical Knowledge Base: Access to comprehensive lung cancer information
|
| 64 |
+
|
| 65 |
+
**Main Endpoints:**
|
| 66 |
+
- POST /ask - Get complete AI response for a query
|
| 67 |
+
- POST /ask/stream - Stream AI response in real-time (recommended for better UX)
|
| 68 |
+
- GET /health - Check API health and initialization status
|
| 69 |
+
- POST /export/{format} - Export conversation history
|
| 70 |
+
|
| 71 |
+
""",
|
| 72 |
version="1.0.0",
|
| 73 |
docs_url="/docs",
|
| 74 |
redoc_url="/redoc",
|
|
|
|
| 99 |
async def root():
|
| 100 |
"""Root endpoint with API information"""
|
| 101 |
return {
|
| 102 |
+
"name": "Lung Cancer AI Advisor API",
|
| 103 |
"version": "1.0.0",
|
| 104 |
+
"description": "AI-powered advisor for lung cancer information and support",
|
| 105 |
"docs": "/docs",
|
| 106 |
"health": "/health",
|
| 107 |
"endpoints": {
|
api/exceptions.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
| 1 |
"""
|
| 2 |
-
Exception handlers for
|
| 3 |
"""
|
| 4 |
import logging
|
| 5 |
from datetime import datetime
|
|
|
|
| 1 |
"""
|
| 2 |
+
Exception handlers for Lung Cancer AI Advisor API
|
| 3 |
"""
|
| 4 |
import logging
|
| 5 |
from datetime import datetime
|
api/middleware.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
| 1 |
"""
|
| 2 |
-
Middleware for
|
| 3 |
"""
|
| 4 |
import time
|
| 5 |
import logging
|
|
@@ -147,7 +147,8 @@ def get_cors_middleware_config():
|
|
| 147 |
# Default to allowing Hugging Face Space and localhost
|
| 148 |
# Include null for file:// protocol and common local development origins
|
| 149 |
allowed_origins = [
|
| 150 |
-
"
|
|
|
|
| 151 |
"http://localhost:8000",
|
| 152 |
"http://127.0.0.1:8000",
|
| 153 |
"http://localhost:5500", # Live Server default port
|
|
@@ -160,7 +161,7 @@ def get_cors_middleware_config():
|
|
| 160 |
return {
|
| 161 |
"allow_origins": allowed_origins,
|
| 162 |
"allow_credentials": True,
|
| 163 |
-
"allow_methods": ["
|
| 164 |
-
"allow_headers": ["
|
| 165 |
-
"expose_headers": ["
|
| 166 |
}
|
|
|
|
| 1 |
"""
|
| 2 |
+
Middleware for Lung Cancer AI Advisor API
|
| 3 |
"""
|
| 4 |
import time
|
| 5 |
import logging
|
|
|
|
| 147 |
# Default to allowing Hugging Face Space and localhost
|
| 148 |
# Include null for file:// protocol and common local development origins
|
| 149 |
allowed_origins = [
|
| 150 |
+
"http://127.0.0.1:7860",
|
| 151 |
+
"https://huggingface.co",
|
| 152 |
"http://localhost:8000",
|
| 153 |
"http://127.0.0.1:8000",
|
| 154 |
"http://localhost:5500", # Live Server default port
|
|
|
|
| 161 |
return {
|
| 162 |
"allow_origins": allowed_origins,
|
| 163 |
"allow_credentials": True,
|
| 164 |
+
"allow_methods": ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
|
| 165 |
+
"allow_headers": ["Content-Type", "Authorization", "Accept", "Origin", "X-Requested-With", "Cookie"],
|
| 166 |
+
"expose_headers": ["Set-Cookie"],
|
| 167 |
}
|
api/models.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
| 1 |
"""
|
| 2 |
-
API Models and Schemas for
|
| 3 |
"""
|
| 4 |
from pydantic import BaseModel, Field
|
| 5 |
from typing import Optional, List, Dict, Any
|
|
|
|
| 1 |
"""
|
| 2 |
+
API Models and Schemas for Lung Cancer AI Advisor
|
| 3 |
"""
|
| 4 |
from pydantic import BaseModel, Field
|
| 5 |
from typing import Optional, List, Dict, Any
|
api/routers/__pycache__/__init__.cpython-311.pyc
CHANGED
|
Binary files a/api/routers/__pycache__/__init__.cpython-311.pyc and b/api/routers/__pycache__/__init__.cpython-311.pyc differ
|
|
|
api/routers/__pycache__/__init__.cpython-313.pyc
CHANGED
|
Binary files a/api/routers/__pycache__/__init__.cpython-313.pyc and b/api/routers/__pycache__/__init__.cpython-313.pyc differ
|
|
|
api/routers/__pycache__/health.cpython-313.pyc
CHANGED
|
Binary files a/api/routers/__pycache__/health.cpython-313.pyc and b/api/routers/__pycache__/health.cpython-313.pyc differ
|
|
|
api/routers/__pycache__/medical.cpython-311.pyc
CHANGED
|
Binary files a/api/routers/__pycache__/medical.cpython-311.pyc and b/api/routers/__pycache__/medical.cpython-311.pyc differ
|
|
|
api/routers/__pycache__/medical.cpython-313.pyc
CHANGED
|
Binary files a/api/routers/__pycache__/medical.cpython-313.pyc and b/api/routers/__pycache__/medical.cpython-313.pyc differ
|
|
|
api/routers/auth.py
CHANGED
|
@@ -100,6 +100,9 @@ async def login(
|
|
| 100 |
"""
|
| 101 |
Login endpoint - validates credentials and creates session
|
| 102 |
"""
|
|
|
|
|
|
|
|
|
|
| 103 |
# Verify credentials
|
| 104 |
if not verify_credentials(username, password):
|
| 105 |
logger.warning(f"Failed login attempt for username: {username}")
|
|
@@ -107,18 +110,22 @@ async def login(
|
|
| 107 |
|
| 108 |
# Create session
|
| 109 |
token = create_session(username)
|
|
|
|
| 110 |
|
| 111 |
# Set secure cookie
|
| 112 |
-
#
|
| 113 |
-
|
| 114 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 115 |
|
| 116 |
-
|
| 117 |
-
parsed_origin = urlparse(origin) if origin else None
|
| 118 |
-
is_cross_site = bool(parsed_origin and parsed_origin.hostname and parsed_origin.hostname != request.url.hostname)
|
| 119 |
-
is_https = request.url.scheme == "https"
|
| 120 |
-
samesite = "none" if (is_https and (is_production or is_cross_site)) else "lax"
|
| 121 |
-
secure = True if samesite == "none" else is_production
|
| 122 |
|
| 123 |
response.set_cookie(
|
| 124 |
key="session_token",
|
|
@@ -126,7 +133,8 @@ async def login(
|
|
| 126 |
httponly=True,
|
| 127 |
max_age=SESSION_MAX_AGE,
|
| 128 |
samesite=samesite,
|
| 129 |
-
secure=secure
|
|
|
|
| 130 |
)
|
| 131 |
|
| 132 |
logger.info(f"Successful login for user: {username}")
|
|
@@ -175,12 +183,18 @@ async def verify(session_token: Optional[str] = Cookie(None)):
|
|
| 175 |
|
| 176 |
|
| 177 |
@router.get("/status")
|
| 178 |
-
async def status(session_token: Optional[str] = Cookie(None)):
|
| 179 |
"""
|
| 180 |
Check authentication status without raising exception
|
| 181 |
"""
|
|
|
|
| 182 |
session_data = verify_session(session_token)
|
| 183 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 184 |
return {
|
| 185 |
"authenticated": session_data is not None,
|
| 186 |
"username": session_data.get("username") if session_data else None
|
|
|
|
| 100 |
"""
|
| 101 |
Login endpoint - validates credentials and creates session
|
| 102 |
"""
|
| 103 |
+
# Log login attempt
|
| 104 |
+
logger.info(f"Login attempt for username: {username}, Origin: {request.headers.get('origin')}")
|
| 105 |
+
|
| 106 |
# Verify credentials
|
| 107 |
if not verify_credentials(username, password):
|
| 108 |
logger.warning(f"Failed login attempt for username: {username}")
|
|
|
|
| 110 |
|
| 111 |
# Create session
|
| 112 |
token = create_session(username)
|
| 113 |
+
logger.info(f"Session created for user: {username}")
|
| 114 |
|
| 115 |
# Set secure cookie
|
| 116 |
+
# Detect if we're running on HTTPS (Hugging Face Spaces use HTTPS)
|
| 117 |
+
is_https = request.url.scheme == "https" or request.headers.get("x-forwarded-proto") == "https"
|
| 118 |
+
|
| 119 |
+
# For HTTPS (production/HF Spaces), use SameSite=None with Secure=True for cross-origin
|
| 120 |
+
# For HTTP (local dev), use SameSite=Lax with Secure=False
|
| 121 |
+
if is_https:
|
| 122 |
+
samesite = "none"
|
| 123 |
+
secure = True
|
| 124 |
+
else:
|
| 125 |
+
samesite = "lax"
|
| 126 |
+
secure = False
|
| 127 |
|
| 128 |
+
logger.info(f"Setting cookie with samesite={samesite}, secure={secure}, is_https={is_https}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 129 |
|
| 130 |
response.set_cookie(
|
| 131 |
key="session_token",
|
|
|
|
| 133 |
httponly=True,
|
| 134 |
max_age=SESSION_MAX_AGE,
|
| 135 |
samesite=samesite,
|
| 136 |
+
secure=secure,
|
| 137 |
+
path="/"
|
| 138 |
)
|
| 139 |
|
| 140 |
logger.info(f"Successful login for user: {username}")
|
|
|
|
| 183 |
|
| 184 |
|
| 185 |
@router.get("/status")
|
| 186 |
+
async def status(request: Request, session_token: Optional[str] = Cookie(None)):
|
| 187 |
"""
|
| 188 |
Check authentication status without raising exception
|
| 189 |
"""
|
| 190 |
+
logger.info(f"Status check - Cookie present: {session_token is not None}, Origin: {request.headers.get('origin')}")
|
| 191 |
session_data = verify_session(session_token)
|
| 192 |
|
| 193 |
+
if session_data:
|
| 194 |
+
logger.info(f"Status check - Authenticated as: {session_data.get('username')}")
|
| 195 |
+
else:
|
| 196 |
+
logger.info("Status check - Not authenticated")
|
| 197 |
+
|
| 198 |
return {
|
| 199 |
"authenticated": session_data is not None,
|
| 200 |
"username": session_data.get("username") if session_data else None
|
api/routers/health.py
CHANGED
|
@@ -3,11 +3,6 @@ Health Check and System Status Router
|
|
| 3 |
"""
|
| 4 |
from datetime import datetime
|
| 5 |
from fastapi import APIRouter
|
| 6 |
-
import sys
|
| 7 |
-
import os
|
| 8 |
-
|
| 9 |
-
# Add src to path for imports
|
| 10 |
-
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(__file__))))
|
| 11 |
|
| 12 |
from api.models import HealthStatus, InitializationStatus
|
| 13 |
|
|
@@ -23,28 +18,58 @@ async def health_check():
|
|
| 23 |
|
| 24 |
# Check agent availability
|
| 25 |
try:
|
| 26 |
-
from agent import safe_run_agent
|
| 27 |
components["agent"] = "healthy"
|
| 28 |
except Exception:
|
| 29 |
components["agent"] = "unhealthy"
|
| 30 |
|
| 31 |
# Check vector store
|
| 32 |
try:
|
| 33 |
-
from vector_store import
|
| 34 |
-
|
| 35 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 36 |
components["vector_store"] = "unhealthy"
|
| 37 |
|
| 38 |
# Check data loaders
|
| 39 |
try:
|
| 40 |
-
from data_loaders import load_pdf_documents
|
| 41 |
components["data_loaders"] = "healthy"
|
| 42 |
except Exception:
|
| 43 |
components["data_loaders"] = "unhealthy"
|
| 44 |
|
| 45 |
# Check tools
|
| 46 |
try:
|
| 47 |
-
from tools import medical_guidelines_knowledge_tool
|
| 48 |
components["tools"] = "healthy"
|
| 49 |
except Exception:
|
| 50 |
components["tools"] = "unhealthy"
|
|
@@ -52,7 +77,7 @@ async def health_check():
|
|
| 52 |
# Check initialization status
|
| 53 |
initialization_status = None
|
| 54 |
try:
|
| 55 |
-
from background_init import (
|
| 56 |
is_initialization_complete,
|
| 57 |
get_initialization_status,
|
| 58 |
is_initialization_successful,
|
|
@@ -130,8 +155,8 @@ async def get_version():
|
|
| 130 |
"""
|
| 131 |
return {
|
| 132 |
"version": "1.0.0",
|
| 133 |
-
"name": "
|
| 134 |
-
"description": "
|
| 135 |
"build_date": "2024-01-01"
|
| 136 |
}
|
| 137 |
|
|
|
|
| 3 |
"""
|
| 4 |
from datetime import datetime
|
| 5 |
from fastapi import APIRouter
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6 |
|
| 7 |
from api.models import HealthStatus, InitializationStatus
|
| 8 |
|
|
|
|
| 18 |
|
| 19 |
# Check agent availability
|
| 20 |
try:
|
| 21 |
+
from core.agent import safe_run_agent
|
| 22 |
components["agent"] = "healthy"
|
| 23 |
except Exception:
|
| 24 |
components["agent"] = "unhealthy"
|
| 25 |
|
| 26 |
# Check vector store
|
| 27 |
try:
|
| 28 |
+
from core.vector_store import get_vector_store, load_vector_store
|
| 29 |
+
from core.background_init import is_initialization_complete
|
| 30 |
+
|
| 31 |
+
# If initialization is complete, check the vector store directly
|
| 32 |
+
if is_initialization_complete():
|
| 33 |
+
vector_store = get_vector_store()
|
| 34 |
+
if vector_store is not None:
|
| 35 |
+
components["vector_store"] = "healthy"
|
| 36 |
+
else:
|
| 37 |
+
# If vector store is None, try to load it
|
| 38 |
+
try:
|
| 39 |
+
loaded_store = load_vector_store()
|
| 40 |
+
if loaded_store is not None:
|
| 41 |
+
components["vector_store"] = "healthy"
|
| 42 |
+
else:
|
| 43 |
+
components["vector_store"] = "unhealthy"
|
| 44 |
+
except Exception as e:
|
| 45 |
+
logger.error(f"Failed to load vector store: {e}")
|
| 46 |
+
components["vector_store"] = "unhealthy"
|
| 47 |
+
else:
|
| 48 |
+
# If initialization is not complete, the vector store might not be ready yet
|
| 49 |
+
# but we'll check if it exists and can be loaded
|
| 50 |
+
try:
|
| 51 |
+
loaded_store = load_vector_store()
|
| 52 |
+
if loaded_store is not None:
|
| 53 |
+
components["vector_store"] = "healthy"
|
| 54 |
+
else:
|
| 55 |
+
components["vector_store"] = "initializing"
|
| 56 |
+
except Exception as e:
|
| 57 |
+
logger.warning(f"Vector store not ready yet: {e}")
|
| 58 |
+
components["vector_store"] = "initializing"
|
| 59 |
+
except Exception as e:
|
| 60 |
+
logger.error(f"Error checking vector store health: {e}")
|
| 61 |
components["vector_store"] = "unhealthy"
|
| 62 |
|
| 63 |
# Check data loaders
|
| 64 |
try:
|
| 65 |
+
from core.data_loaders import load_pdf_documents
|
| 66 |
components["data_loaders"] = "healthy"
|
| 67 |
except Exception:
|
| 68 |
components["data_loaders"] = "unhealthy"
|
| 69 |
|
| 70 |
# Check tools
|
| 71 |
try:
|
| 72 |
+
from core.tools import medical_guidelines_knowledge_tool
|
| 73 |
components["tools"] = "healthy"
|
| 74 |
except Exception:
|
| 75 |
components["tools"] = "unhealthy"
|
|
|
|
| 77 |
# Check initialization status
|
| 78 |
initialization_status = None
|
| 79 |
try:
|
| 80 |
+
from core.background_init import (
|
| 81 |
is_initialization_complete,
|
| 82 |
get_initialization_status,
|
| 83 |
is_initialization_successful,
|
|
|
|
| 155 |
"""
|
| 156 |
return {
|
| 157 |
"version": "1.0.0",
|
| 158 |
+
"name": "Lung Cancer AI Advisor API",
|
| 159 |
+
"description": "AI-powered advisor for lung cancer information and support",
|
| 160 |
"build_date": "2024-01-01"
|
| 161 |
}
|
| 162 |
|
api/routers/medical.py
CHANGED
|
@@ -1,9 +1,11 @@
|
|
| 1 |
"""
|
| 2 |
-
Medical Query Router for
|
| 3 |
"""
|
| 4 |
import asyncio
|
|
|
|
| 5 |
from fastapi import APIRouter, HTTPException
|
| 6 |
from fastapi.responses import StreamingResponse
|
|
|
|
| 7 |
import sys
|
| 8 |
import os
|
| 9 |
|
|
@@ -15,18 +17,78 @@ from core.agent import safe_run_agent, safe_run_agent_streaming
|
|
| 15 |
router = APIRouter(tags=["medical"])
|
| 16 |
|
| 17 |
|
| 18 |
-
|
| 19 |
-
async def ask(query: str, session_id: str = "default"):
|
| 20 |
"""
|
| 21 |
-
|
| 22 |
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 26 |
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 27 |
try:
|
| 28 |
-
response = await safe_run_agent(user_input=query, session_id=session_id)
|
| 29 |
-
return {"response": response, "session_id": session_id}
|
| 30 |
|
| 31 |
except Exception as e:
|
| 32 |
raise HTTPException(
|
|
@@ -34,20 +96,60 @@ async def ask(query: str, session_id: str = "default"):
|
|
| 34 |
detail=f"Error processing medical query: {str(e)}"
|
| 35 |
)
|
| 36 |
|
|
|
|
|
|
|
|
|
|
| 37 |
|
| 38 |
-
|
| 39 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 40 |
"""
|
| 41 |
-
Process a medical query with streaming response
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 42 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 43 |
Args:
|
| 44 |
-
|
| 45 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 46 |
"""
|
| 47 |
async def event_stream():
|
| 48 |
try:
|
| 49 |
chunk_buffer = ""
|
| 50 |
-
async for chunk in safe_run_agent_streaming(user_input=query, session_id=session_id):
|
| 51 |
chunk_buffer += chunk
|
| 52 |
|
| 53 |
# Send chunks in reasonable sizes for smoother streaming
|
|
@@ -64,3 +166,6 @@ async def ask_stream(query: str, session_id: str = "default"):
|
|
| 64 |
yield f"Error: {str(e)}"
|
| 65 |
|
| 66 |
return StreamingResponse(event_stream(), media_type="text/markdown")
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
"""
|
| 2 |
+
Medical Query Router for Lung Cancer AI Advisor
|
| 3 |
"""
|
| 4 |
import asyncio
|
| 5 |
+
import inspect
|
| 6 |
from fastapi import APIRouter, HTTPException
|
| 7 |
from fastapi.responses import StreamingResponse
|
| 8 |
+
from pydantic import BaseModel, Field
|
| 9 |
import sys
|
| 10 |
import os
|
| 11 |
|
|
|
|
| 17 |
router = APIRouter(tags=["medical"])
|
| 18 |
|
| 19 |
|
| 20 |
+
class QueryRequest(BaseModel):
|
|
|
|
| 21 |
"""
|
| 22 |
+
Request model for medical queries
|
| 23 |
|
| 24 |
+
Example:
|
| 25 |
+
{
|
| 26 |
+
"query": "What are the early symptoms of lung cancer?",
|
| 27 |
+
"session_id": "user_123_session_456"
|
| 28 |
+
}
|
| 29 |
+
"""
|
| 30 |
+
query: str = Field(
|
| 31 |
+
...,
|
| 32 |
+
description="The medical question or query about lung cancer",
|
| 33 |
+
example="Give me the options for first line treatment for NSCLC?"
|
| 34 |
+
)
|
| 35 |
+
session_id: str = Field(
|
| 36 |
+
...,
|
| 37 |
+
description="Unique session identifier for conversation continuity. Use the same `session_id` to maintain context across multiple queries. Format: `user_{user_id}_session_{timestamp}`",
|
| 38 |
+
example="user_123_session_1699612345"
|
| 39 |
+
)
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
@router.post(
|
| 43 |
+
"/ask",
|
| 44 |
+
summary="Ask a lung cancer question",
|
| 45 |
+
)
|
| 46 |
+
async def ask(request: QueryRequest):
|
| 47 |
"""
|
| 48 |
+
Process a lung cancer-related medical query and return a complete response.
|
| 49 |
+
|
| 50 |
+
The AI agent intelligently selects appropriate tools and data sources to provide
|
| 51 |
+
accurate, evidence-based information about lung cancer.
|
| 52 |
+
|
| 53 |
+
Request Body:
|
| 54 |
+
- `query` (required): Your medical question about lung cancer
|
| 55 |
+
- `session_id` (required): Unique identifier to maintain conversation context
|
| 56 |
+
|
| 57 |
+
Response:
|
| 58 |
+
- `response`: Complete AI-generated answer in markdown format
|
| 59 |
+
- `session_id`: Echo of the session identifier used
|
| 60 |
+
|
| 61 |
+
Example Request:
|
| 62 |
+
{
|
| 63 |
+
"query": "What are the early symptoms of lung cancer?",
|
| 64 |
+
"session_id": "user_123_session_1699612345"
|
| 65 |
+
}
|
| 66 |
+
|
| 67 |
+
Example Response:
|
| 68 |
+
{
|
| 69 |
+
"response": "Early symptoms of lung cancer may include...\n\n**Common Early Signs:**\n- Persistent cough...",
|
| 70 |
+
"session_id": "user_123_session_1699612345"
|
| 71 |
+
}
|
| 72 |
+
|
| 73 |
+
Frontend Integration Tips:
|
| 74 |
+
- Use the same `session_id` for follow-up questions to maintain context
|
| 75 |
+
- Display response in markdown renderer for better formatting
|
| 76 |
+
- Show loading state while waiting for response
|
| 77 |
+
- Handle 500 errors gracefully with user-friendly messages
|
| 78 |
+
|
| 79 |
+
Args:
|
| 80 |
+
request: QueryRequest containing query and session_id
|
| 81 |
+
|
| 82 |
+
Returns:
|
| 83 |
+
Dictionary with response text and session_id
|
| 84 |
+
|
| 85 |
+
Raises:
|
| 86 |
+
HTTPException: 500 if query processing fails
|
| 87 |
+
"""
|
| 88 |
+
|
| 89 |
try:
|
| 90 |
+
response = await safe_run_agent(user_input=request.query, session_id=request.session_id)
|
| 91 |
+
return {"response": response, "session_id": request.session_id}
|
| 92 |
|
| 93 |
except Exception as e:
|
| 94 |
raise HTTPException(
|
|
|
|
| 96 |
detail=f"Error processing medical query: {str(e)}"
|
| 97 |
)
|
| 98 |
|
| 99 |
+
# Dedent the docstring so OpenAPI/Redoc renderers don't treat the
|
| 100 |
+
# indented lines as a markdown code block (leading 4-space indentation).
|
| 101 |
+
ask.__doc__ = inspect.cleandoc(ask.__doc__ or "")
|
| 102 |
|
| 103 |
+
|
| 104 |
+
@router.post(
|
| 105 |
+
"/ask/stream",
|
| 106 |
+
summary="Ask a lung cancer question with streaming response",
|
| 107 |
+
|
| 108 |
+
)
|
| 109 |
+
async def ask_stream(request: QueryRequest):
|
| 110 |
"""
|
| 111 |
+
Process a lung cancer-related medical query with real-time streaming response.
|
| 112 |
+
|
| 113 |
+
Recommended for frontend use - Provides better user experience by streaming
|
| 114 |
+
the response as it's generated, similar to ChatGPT.
|
| 115 |
+
|
| 116 |
+
Request Body:
|
| 117 |
+
- `query` (required): Your medical question about lung cancer
|
| 118 |
+
- `session_id` (required): Unique identifier to maintain conversation context
|
| 119 |
+
|
| 120 |
+
Response:
|
| 121 |
+
- Streaming text/markdown content
|
| 122 |
+
- Response is sent in chunks as it's generated
|
| 123 |
+
- Connection stays open until response is complete
|
| 124 |
|
| 125 |
+
|
| 126 |
+
Example Request:
|
| 127 |
+
{
|
| 128 |
+
"query": "Explain the difference between small cell and non-small cell lung cancer",
|
| 129 |
+
"session_id": "user_123_session_1699612345"
|
| 130 |
+
}
|
| 131 |
+
|
| 132 |
+
Frontend Integration Tips:
|
| 133 |
+
- Use the same `session_id` for follow-up questions to maintain context
|
| 134 |
+
- Display response in markdown renderer for better formatting
|
| 135 |
+
- Show loading state while waiting for response
|
| 136 |
+
- Render markdown progressively as chunks arrive
|
| 137 |
+
- Show typing indicator while streaming
|
| 138 |
+
- Handle 500 errors gracefully with user-friendly messages
|
| 139 |
+
|
| 140 |
Args:
|
| 141 |
+
request: QueryRequest containing query and session_id
|
| 142 |
+
|
| 143 |
+
Returns:
|
| 144 |
+
StreamingResponse with text/markdown content
|
| 145 |
+
|
| 146 |
+
Raises:
|
| 147 |
+
HTTPException: 500 if query processing fails
|
| 148 |
"""
|
| 149 |
async def event_stream():
|
| 150 |
try:
|
| 151 |
chunk_buffer = ""
|
| 152 |
+
async for chunk in safe_run_agent_streaming(user_input=request.query, session_id=request.session_id):
|
| 153 |
chunk_buffer += chunk
|
| 154 |
|
| 155 |
# Send chunks in reasonable sizes for smoother streaming
|
|
|
|
| 166 |
yield f"Error: {str(e)}"
|
| 167 |
|
| 168 |
return StreamingResponse(event_stream(), media_type="text/markdown")
|
| 169 |
+
|
| 170 |
+
# Dedent streaming endpoint docstring for proper Markdown rendering in docs
|
| 171 |
+
ask_stream.__doc__ = inspect.cleandoc(ask_stream.__doc__ or "")
|
app.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
| 1 |
"""
|
| 2 |
-
Startup script for
|
| 3 |
"""
|
| 4 |
import sys
|
| 5 |
import os
|
|
|
|
| 1 |
"""
|
| 2 |
+
Startup script for Lung Cancer AI Advisor API
|
| 3 |
"""
|
| 4 |
import sys
|
| 5 |
import os
|
backup/backup_20251022_110950/chunks.pkl
DELETED
|
@@ -1,3 +0,0 @@
|
|
| 1 |
-
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:b038845d797cac35024d39df8c7a861d741a1f7c2edc1a54286e17de1806b38e
|
| 3 |
-
size 3878660
|
|
|
|
|
|
|
|
|
|
|
|
backup/backup_20251022_110950/vector_store/index.faiss
DELETED
|
@@ -1,3 +0,0 @@
|
|
| 1 |
-
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:824156db2ada7613098cc7c9a8c27d66b33553885146fb6f66ab450ddc5d95cb
|
| 3 |
-
size 8248365
|
|
|
|
|
|
|
|
|
|
|
|
backup/backup_20251022_110950/vector_store/index.pkl
DELETED
|
@@ -1,3 +0,0 @@
|
|
| 1 |
-
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:2655709226a3c13f4dae2efc131aaee81f68ac696a9b9a7aa8daeabc026d40d4
|
| 3 |
-
size 4020637
|
|
|
|
|
|
|
|
|
|
|
|
backup/backup_20251022_111044/chunks.pkl
DELETED
|
@@ -1,3 +0,0 @@
|
|
| 1 |
-
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:b038845d797cac35024d39df8c7a861d741a1f7c2edc1a54286e17de1806b38e
|
| 3 |
-
size 3878660
|
|
|
|
|
|
|
|
|
|
|
|
backup/backup_20251022_111044/vector_store/index.faiss
DELETED
|
@@ -1,3 +0,0 @@
|
|
| 1 |
-
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:824156db2ada7613098cc7c9a8c27d66b33553885146fb6f66ab450ddc5d95cb
|
| 3 |
-
size 8248365
|
|
|
|
|
|
|
|
|
|
|
|
backup/backup_20251022_111044/vector_store/index.pkl
DELETED
|
@@ -1,3 +0,0 @@
|
|
| 1 |
-
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:2655709226a3c13f4dae2efc131aaee81f68ac696a9b9a7aa8daeabc026d40d4
|
| 3 |
-
size 4020637
|
|
|
|
|
|
|
|
|
|
|
|
core/__pycache__/__init__.cpython-311.pyc
CHANGED
|
Binary files a/core/__pycache__/__init__.cpython-311.pyc and b/core/__pycache__/__init__.cpython-311.pyc differ
|
|
|
core/__pycache__/__init__.cpython-313.pyc
CHANGED
|
Binary files a/core/__pycache__/__init__.cpython-313.pyc and b/core/__pycache__/__init__.cpython-313.pyc differ
|
|
|
core/__pycache__/agent.cpython-311.pyc
CHANGED
|
Binary files a/core/__pycache__/agent.cpython-311.pyc and b/core/__pycache__/agent.cpython-311.pyc differ
|
|
|
core/__pycache__/agent.cpython-313.pyc
CHANGED
|
Binary files a/core/__pycache__/agent.cpython-313.pyc and b/core/__pycache__/agent.cpython-313.pyc differ
|
|
|
core/__pycache__/background_init.cpython-313.pyc
CHANGED
|
Binary files a/core/__pycache__/background_init.cpython-313.pyc and b/core/__pycache__/background_init.cpython-313.pyc differ
|
|
|
core/__pycache__/config.cpython-311.pyc
CHANGED
|
Binary files a/core/__pycache__/config.cpython-311.pyc and b/core/__pycache__/config.cpython-311.pyc differ
|
|
|
core/__pycache__/config.cpython-313.pyc
CHANGED
|
Binary files a/core/__pycache__/config.cpython-313.pyc and b/core/__pycache__/config.cpython-313.pyc differ
|
|
|
core/__pycache__/data_loaders.cpython-313.pyc
CHANGED
|
Binary files a/core/__pycache__/data_loaders.cpython-313.pyc and b/core/__pycache__/data_loaders.cpython-313.pyc differ
|
|
|
core/__pycache__/github_storage.cpython-313.pyc
CHANGED
|
Binary files a/core/__pycache__/github_storage.cpython-313.pyc and b/core/__pycache__/github_storage.cpython-313.pyc differ
|
|
|
core/__pycache__/retrievers.cpython-313.pyc
CHANGED
|
Binary files a/core/__pycache__/retrievers.cpython-313.pyc and b/core/__pycache__/retrievers.cpython-313.pyc differ
|
|
|
core/__pycache__/text_processors.cpython-313.pyc
CHANGED
|
Binary files a/core/__pycache__/text_processors.cpython-313.pyc and b/core/__pycache__/text_processors.cpython-313.pyc differ
|
|
|
core/__pycache__/tools.cpython-313.pyc
CHANGED
|
Binary files a/core/__pycache__/tools.cpython-313.pyc and b/core/__pycache__/tools.cpython-313.pyc differ
|
|
|
core/__pycache__/tracing.cpython-313.pyc
CHANGED
|
Binary files a/core/__pycache__/tracing.cpython-313.pyc and b/core/__pycache__/tracing.cpython-313.pyc differ
|
|
|
core/__pycache__/utils.cpython-313.pyc
CHANGED
|
Binary files a/core/__pycache__/utils.cpython-313.pyc and b/core/__pycache__/utils.cpython-313.pyc differ
|
|
|
core/__pycache__/validation.cpython-313.pyc
CHANGED
|
Binary files a/core/__pycache__/validation.cpython-313.pyc and b/core/__pycache__/validation.cpython-313.pyc differ
|
|
|
core/agent.py
CHANGED
|
@@ -4,6 +4,7 @@ from typing import Any, AsyncGenerator
|
|
| 4 |
import asyncio
|
| 5 |
import requests
|
| 6 |
import os
|
|
|
|
| 7 |
from langchain.agents import create_openai_tools_agent, AgentExecutor
|
| 8 |
from langchain.memory import ConversationBufferWindowMemory
|
| 9 |
from langchain.prompts import ChatPromptTemplate, MessagesPlaceholder
|
|
@@ -101,22 +102,30 @@ Your primary purpose is to provide evidence-based clinical guidance on lung canc
|
|
| 101 |
|
| 102 |
**AUDIENCE**: Your responses are for thoracic oncologists, pulmonologists, and medical experts managing lung cancer. Use appropriate medical terminology, clinical precision, and expert-level detail specific to lung cancer management.
|
| 103 |
|
| 104 |
-
**RESPONSE STYLE - CRITICAL: CONCISE,
|
| 105 |
- **IMMEDIATE DIRECT ANSWERS**: Start immediately with the answer - NO introductory phrases like "I will retrieve...", "Let me search...", "Please hold on...", or status updates
|
| 106 |
- **NO PREAMBLES**: Never announce what you're about to do - just do it and present the results directly
|
| 107 |
- **ZERO PROCEDURAL STATEMENTS**: Do NOT write "I will retrieve", "I will search", "I will gather", "Please wait", "Hold on", or any similar phrases - START DIRECTLY WITH THE CLINICAL ANSWER
|
| 108 |
- **FIRST WORD RULE**: Your response must begin with the actual answer content (e.g., a heading, clinical information, or direct statement) - never with a procedural announcement
|
| 109 |
-
|
| 110 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 111 |
- **CLINICAL EFFICIENCY**: Respect physicians' time by delivering key information first, then supporting details
|
| 112 |
-
- **STRUCTURED
|
| 113 |
-
- **ESSENTIAL DETAILS
|
| 114 |
- **PRIORITIZED INFORMATION**: Lead with the most critical clinical decision points, contraindications, and evidence-based recommendations
|
| 115 |
- **LUNG CANCER FOCUS**: Prioritize lung cancer-specific information including histology, molecular markers, staging, and treatment selection
|
| 116 |
-
- Use precise medical terminology
|
| 117 |
-
- Reference specific guideline sources (tables, figures, algorithms) with
|
| 118 |
-
- Highlight critical nuances, contraindications, and special populations only when clinically significant
|
| 119 |
-
- When multiple approaches exist, prioritize by evidence level and clinical context
|
| 120 |
- **CONTEXT AWARENESS**: Use context pages to ensure accuracy, but synthesize information concisely
|
| 121 |
- **DIRECT ANSWERS**: Answer the specific question asked without providing tangential information
|
| 122 |
|
|
@@ -129,6 +138,13 @@ Your primary purpose is to provide evidence-based clinical guidance on lung canc
|
|
| 129 |
- Even for basic lung cancer concepts (e.g., "what is EGFR mutation", "ALK rearrangement", "PD-L1 expression"), you MUST retrieve information from the guidelines first
|
| 130 |
- Only after retrieving guideline information should you formulate your answer based on what was retrieved
|
| 131 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 132 |
**TOOL USAGE REQUIREMENTS:**
|
| 133 |
1. **MEDICAL QUESTIONS** (definitions, treatments, guidelines, etc.):
|
| 134 |
- MANDATORY: Use "medical_guidelines_knowledge_tool" FIRST
|
|
@@ -250,16 +266,19 @@ class SessionMemoryManager:
|
|
| 250 |
|
| 251 |
def __init__(self):
|
| 252 |
self._sessions = {}
|
| 253 |
-
self._default_window_size = 10
|
| 254 |
|
| 255 |
def get_memory(self, session_id: str = "default") -> ConversationBufferWindowMemory:
|
| 256 |
"""Get or create memory for a specific session."""
|
| 257 |
if session_id not in self._sessions:
|
| 258 |
-
|
| 259 |
-
|
| 260 |
-
|
| 261 |
-
|
| 262 |
-
|
|
|
|
|
|
|
|
|
|
| 263 |
return self._sessions[session_id]
|
| 264 |
|
| 265 |
def clear_session(self, session_id: str) -> bool:
|
|
@@ -359,7 +378,7 @@ def _perform_automatic_validation(user_input: str, response: str) -> None:
|
|
| 359 |
"""
|
| 360 |
try:
|
| 361 |
# Import here to avoid circular imports
|
| 362 |
-
from .tools import _last_question, _last_documents
|
| 363 |
|
| 364 |
# Check if we have the necessary context for validation
|
| 365 |
if not _last_question or not _last_documents:
|
|
@@ -414,10 +433,6 @@ async def run_agent_streaming(user_input: str, session_id: str = "default", max_
|
|
| 414 |
yield "Sorry, I didn't receive any questions. Please enter your question or request."
|
| 415 |
return
|
| 416 |
|
| 417 |
-
# Store the original user question for validation
|
| 418 |
-
from .tools import store_user_question
|
| 419 |
-
store_user_question(user_input.strip())
|
| 420 |
-
|
| 421 |
retry_count = 0
|
| 422 |
last_error = None
|
| 423 |
current_run_id = None
|
|
@@ -545,13 +560,16 @@ async def run_agent_streaming(user_input: str, session_id: str = "default", max_
|
|
| 545 |
yield "Sorry, the system is currently busy. Please try again in a little while."
|
| 546 |
return
|
| 547 |
|
| 548 |
-
except APIError as e:
|
| 549 |
retry_count += 1
|
| 550 |
last_error = e
|
| 551 |
-
|
|
|
|
| 552 |
|
| 553 |
if retry_count <= max_retries:
|
| 554 |
-
|
|
|
|
|
|
|
| 555 |
continue
|
| 556 |
else:
|
| 557 |
yield "Sorry, there was an error connecting to the service. Please try again later."
|
|
|
|
| 4 |
import asyncio
|
| 5 |
import requests
|
| 6 |
import os
|
| 7 |
+
import httpx
|
| 8 |
from langchain.agents import create_openai_tools_agent, AgentExecutor
|
| 9 |
from langchain.memory import ConversationBufferWindowMemory
|
| 10 |
from langchain.prompts import ChatPromptTemplate, MessagesPlaceholder
|
|
|
|
| 102 |
|
| 103 |
**AUDIENCE**: Your responses are for thoracic oncologists, pulmonologists, and medical experts managing lung cancer. Use appropriate medical terminology, clinical precision, and expert-level detail specific to lung cancer management.
|
| 104 |
|
| 105 |
+
**RESPONSE STYLE - CRITICAL: CONCISE, ACCURATE, MEDIUM-LENGTH ANSWERS FOR MEDICAL EXPERTS**:
|
| 106 |
- **IMMEDIATE DIRECT ANSWERS**: Start immediately with the answer - NO introductory phrases like "I will retrieve...", "Let me search...", "Please hold on...", or status updates
|
| 107 |
- **NO PREAMBLES**: Never announce what you're about to do - just do it and present the results directly
|
| 108 |
- **ZERO PROCEDURAL STATEMENTS**: Do NOT write "I will retrieve", "I will search", "I will gather", "Please wait", "Hold on", or any similar phrases - START DIRECTLY WITH THE CLINICAL ANSWER
|
| 109 |
- **FIRST WORD RULE**: Your response must begin with the actual answer content (e.g., a heading, clinical information, or direct statement) - never with a procedural announcement
|
| 110 |
+
|
| 111 |
+
**RESPONSE LENGTH - MEDIUM AND BALANCED**:
|
| 112 |
+
- **NOT TOO LONG**: Avoid excessive detail, lengthy explanations, or exhaustive lists that overwhelm busy clinicians
|
| 113 |
+
- **NOT TOO SHORT**: Provide sufficient clinical context, key recommendations, and essential details for informed decision-making
|
| 114 |
+
- **MEDIUM LENGTH TARGET**: Aim for 200-400 words for standard queries; 400-600 words for complex multi-part questions
|
| 115 |
+
- **QUALITY OVER QUANTITY**: Every sentence must add clinical value - eliminate redundancy and filler content
|
| 116 |
+
- **CONCISE & COMPLETE**: Cover all essential aspects of the query without unnecessary elaboration
|
| 117 |
+
|
| 118 |
+
**EXPERT-TAILORED CONTENT**:
|
| 119 |
+
- **PRECISION & ACCURACY**: Provide exact, evidence-based information from guidelines - no speculation or general knowledge
|
| 120 |
- **CLINICAL EFFICIENCY**: Respect physicians' time by delivering key information first, then supporting details
|
| 121 |
+
- **STRUCTURED CLARITY**: Use clear hierarchical formatting (headers, bullet points) to enable rapid information scanning
|
| 122 |
+
- **ESSENTIAL DETAILS**: Include specific clinical parameters, dosing, biomarkers, and monitoring when directly relevant to the query
|
| 123 |
- **PRIORITIZED INFORMATION**: Lead with the most critical clinical decision points, contraindications, and evidence-based recommendations
|
| 124 |
- **LUNG CANCER FOCUS**: Prioritize lung cancer-specific information including histology, molecular markers, staging, and treatment selection
|
| 125 |
+
- **MEDICAL TERMINOLOGY**: Use precise medical terminology appropriate for thoracic oncologists and pulmonologists
|
| 126 |
+
- **CONCISE CITATIONS**: Reference specific guideline sources (tables, figures, algorithms) with brief inline citations
|
| 127 |
+
- **CLINICALLY SIGNIFICANT NUANCES**: Highlight critical nuances, contraindications, and special populations only when clinically significant
|
| 128 |
+
- **EVIDENCE-BASED PRIORITIZATION**: When multiple approaches exist, prioritize by evidence level and clinical context
|
| 129 |
- **CONTEXT AWARENESS**: Use context pages to ensure accuracy, but synthesize information concisely
|
| 130 |
- **DIRECT ANSWERS**: Answer the specific question asked without providing tangential information
|
| 131 |
|
|
|
|
| 138 |
- Even for basic lung cancer concepts (e.g., "what is EGFR mutation", "ALK rearrangement", "PD-L1 expression"), you MUST retrieve information from the guidelines first
|
| 139 |
- Only after retrieving guideline information should you formulate your answer based on what was retrieved
|
| 140 |
|
| 141 |
+
**STRICT QUERY ADHERENCE - ALL PROVIDERS REQUIREMENT:**
|
| 142 |
+
- When the user explicitly requests information from "all guidelines", "all providers", "according to all guidelines", or similar phrasing, you MUST retrieve and present information from ALL available guideline providers (NCCN, ASCO, ESMO, NICE, Manus)
|
| 143 |
+
- Do NOT selectively omit providers - if the user asks for "all", you must query each provider separately and include ALL results
|
| 144 |
+
- Call the medical_guidelines_knowledge_tool multiple times (once per provider) when "all providers" is requested
|
| 145 |
+
- Present a comprehensive answer that includes recommendations from every available provider
|
| 146 |
+
- If a specific provider has no information on the topic, explicitly state that in your response
|
| 147 |
+
|
| 148 |
**TOOL USAGE REQUIREMENTS:**
|
| 149 |
1. **MEDICAL QUESTIONS** (definitions, treatments, guidelines, etc.):
|
| 150 |
- MANDATORY: Use "medical_guidelines_knowledge_tool" FIRST
|
|
|
|
| 266 |
|
| 267 |
def __init__(self):
|
| 268 |
self._sessions = {}
|
| 269 |
+
self._default_window_size = 20 # Increased from 10 to maintain better context
|
| 270 |
|
| 271 |
def get_memory(self, session_id: str = "default") -> ConversationBufferWindowMemory:
|
| 272 |
"""Get or create memory for a specific session."""
|
| 273 |
if session_id not in self._sessions:
|
| 274 |
+
import warnings
|
| 275 |
+
with warnings.catch_warnings():
|
| 276 |
+
warnings.filterwarnings("ignore", category=DeprecationWarning)
|
| 277 |
+
self._sessions[session_id] = ConversationBufferWindowMemory(
|
| 278 |
+
memory_key="chat_history",
|
| 279 |
+
return_messages=True,
|
| 280 |
+
max_window_size=self._default_window_size
|
| 281 |
+
)
|
| 282 |
return self._sessions[session_id]
|
| 283 |
|
| 284 |
def clear_session(self, session_id: str) -> bool:
|
|
|
|
| 378 |
"""
|
| 379 |
try:
|
| 380 |
# Import here to avoid circular imports
|
| 381 |
+
from .tools import _last_question, _last_documents
|
| 382 |
|
| 383 |
# Check if we have the necessary context for validation
|
| 384 |
if not _last_question or not _last_documents:
|
|
|
|
| 433 |
yield "Sorry, I didn't receive any questions. Please enter your question or request."
|
| 434 |
return
|
| 435 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 436 |
retry_count = 0
|
| 437 |
last_error = None
|
| 438 |
current_run_id = None
|
|
|
|
| 560 |
yield "Sorry, the system is currently busy. Please try again in a little while."
|
| 561 |
return
|
| 562 |
|
| 563 |
+
except (APIError, httpx.RemoteProtocolError, httpx.ReadError, httpx.ConnectError) as e:
|
| 564 |
retry_count += 1
|
| 565 |
last_error = e
|
| 566 |
+
error_type = type(e).__name__
|
| 567 |
+
logger.error(f"OpenAI API/Connection error ({error_type}): {str(e)}")
|
| 568 |
|
| 569 |
if retry_count <= max_retries:
|
| 570 |
+
wait_time = min(2 ** retry_count, 10) # Exponential backoff, max 10 seconds
|
| 571 |
+
logger.info(f"Retrying after {wait_time} seconds... (Attempt {retry_count}/{max_retries})")
|
| 572 |
+
await asyncio.sleep(wait_time)
|
| 573 |
continue
|
| 574 |
else:
|
| 575 |
yield "Sorry, there was an error connecting to the service. Please try again later."
|
core/context_enrichment.py
CHANGED
|
@@ -25,6 +25,7 @@ class ContextEnricher:
|
|
| 25 |
"""
|
| 26 |
self._document_cache: Dict[str, List[Document]] = {}
|
| 27 |
self._cache_size = cache_size
|
|
|
|
| 28 |
|
| 29 |
def enrich_documents(
|
| 30 |
self,
|
|
@@ -187,8 +188,13 @@ class ContextEnricher:
|
|
| 187 |
try:
|
| 188 |
from . import utils
|
| 189 |
|
| 190 |
-
# Load all chunks
|
| 191 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 192 |
if not all_chunks:
|
| 193 |
logger.debug(f"No chunks available for enrichment")
|
| 194 |
return None
|
|
|
|
| 25 |
"""
|
| 26 |
self._document_cache: Dict[str, List[Document]] = {}
|
| 27 |
self._cache_size = cache_size
|
| 28 |
+
self._all_chunks_cache: Optional[List[Document]] = None # Cache all chunks to avoid reloading
|
| 29 |
|
| 30 |
def enrich_documents(
|
| 31 |
self,
|
|
|
|
| 188 |
try:
|
| 189 |
from . import utils
|
| 190 |
|
| 191 |
+
# Load all chunks (use cached version to avoid redundant loading)
|
| 192 |
+
if self._all_chunks_cache is None:
|
| 193 |
+
self._all_chunks_cache = utils.load_chunks()
|
| 194 |
+
if self._all_chunks_cache:
|
| 195 |
+
logger.debug(f"Loaded {len(self._all_chunks_cache)} chunks into enricher cache")
|
| 196 |
+
|
| 197 |
+
all_chunks = self._all_chunks_cache
|
| 198 |
if not all_chunks:
|
| 199 |
logger.debug(f"No chunks available for enrichment")
|
| 200 |
return None
|