nexusbert commited on
Commit
a04ac86
·
1 Parent(s): c5a6c93
Files changed (6) hide show
  1. Dockerfile +55 -0
  2. README.md +124 -10
  3. app.py +69 -0
  4. model/__init__.py +0 -0
  5. model/biomistral_service.py +96 -0
  6. requirements.txt +7 -0
Dockerfile ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Use a lightweight Python base
2
+ FROM python:3.10-slim
3
+
4
+ # Prevent interactive prompts & speed up Python
5
+ ENV DEBIAN_FRONTEND=noninteractive \
6
+ PYTHONUNBUFFERED=1 \
7
+ PYTHONDONTWRITEBYTECODE=1 \
8
+ PIP_NO_CACHE_DIR=1 \
9
+ TOKENIZERS_PARALLELISM=false
10
+
11
+ # Set work directory
12
+ WORKDIR /code
13
+
14
+ # Install system dependencies
15
+ RUN apt-get update && apt-get install -y --no-install-recommends \
16
+ build-essential \
17
+ git \
18
+ curl \
19
+ libopenblas-dev \
20
+ libomp-dev \
21
+ && rm -rf /var/lib/apt/lists/*
22
+
23
+ # Copy requirements first (for Docker caching)
24
+ COPY requirements.txt .
25
+
26
+ # Install Python dependencies
27
+ RUN pip install --no-cache-dir -r requirements.txt
28
+
29
+ # Hugging Face tools
30
+ RUN pip install --no-cache-dir huggingface-hub accelerate
31
+
32
+ # Set Hugging Face cache inside container (persistent, not /tmp)
33
+ ENV HF_HOME=/models/huggingface
34
+ ENV TRANSFORMERS_CACHE=/models/huggingface
35
+ ENV HUGGINGFACE_HUB_CACHE=/models/huggingface
36
+ ENV HF_HUB_CACHE=/models/huggingface
37
+
38
+ # Create cache dir
39
+ RUN mkdir -p /models/huggingface
40
+
41
+ # Pre-download model at build time (BioMistral-7B model)
42
+ RUN python -c "from huggingface_hub import snapshot_download; snapshot_download(repo_id='BioMistral/BioMistral-7B')"
43
+
44
+ # Preload tokenizer (avoid runtime delays)
45
+ RUN python -c "from transformers import AutoTokenizer; AutoTokenizer.from_pretrained('BioMistral/BioMistral-7B', use_fast=True)"
46
+
47
+ # Copy project files
48
+ COPY . .
49
+
50
+ # Expose FastAPI port (Hugging Face Spaces uses 7860)
51
+ EXPOSE 7860
52
+
53
+ # Run FastAPI app with uvicorn (single worker)
54
+ CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]
55
+
README.md CHANGED
@@ -1,10 +1,124 @@
1
- ---
2
- title: QuickCare Text
3
- emoji: 🚀
4
- colorFrom: green
5
- colorTo: indigo
6
- sdk: docker
7
- pinned: false
8
- ---
9
-
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # QuickCare Text - MediScope AI
2
+
3
+ A FastAPI service for medical chat using BioMistral-7B model with **conversational AI support** and **session management**.
4
+
5
+ ## 🚀 Quick Start
6
+
7
+ ### 1. Install Dependencies
8
+
9
+ ```bash
10
+ pip install -r requirements.txt
11
+ ```
12
+
13
+ ### 2. Run the Server
14
+
15
+ ```bash
16
+ uvicorn app:app --host 0.0.0.0 --port 8000 --reload
17
+ ```
18
+
19
+ The server will start on `http://127.0.0.1:8000`
20
+
21
+ **Note:** On first run, the BioMistral-7B model (~14GB) will be downloaded from Hugging Face. This may take several minutes.
22
+
23
+ ### 3. Test the API
24
+
25
+ #### Start a new conversation:
26
+ ```bash
27
+ curl -X POST "http://127.0.0.1:8000/chat" \
28
+ -H "Content-Type: application/json" \
29
+ -d '{"prompt": "I have a rash on my arm that itches for 3 days"}'
30
+ ```
31
+
32
+ **Response:**
33
+ ```json
34
+ {
35
+ "response": "It sounds like you may have a mild skin irritation...",
36
+ "session_id": "550e8400-e29b-41d4-a716-446655440000"
37
+ }
38
+ ```
39
+
40
+ #### Continue the conversation (use the session_id from previous response):
41
+ ```bash
42
+ curl -X POST "http://127.0.0.1:8000/chat" \
43
+ -H "Content-Type: application/json" \
44
+ -d '{
45
+ "prompt": "What should I do about it?",
46
+ "session_id": "550e8400-e29b-41d4-a716-446655440000"
47
+ }'
48
+ ```
49
+
50
+ #### Clear a session:
51
+ ```bash
52
+ curl -X DELETE "http://127.0.0.1:8000/chat/550e8400-e29b-41d4-a716-446655440000"
53
+ ```
54
+
55
+ ## 📁 Project Structure
56
+
57
+ ```
58
+ .
59
+ ├── app.py # FastAPI application with session management
60
+ ├── model/
61
+ │ ├── __init__.py
62
+ │ └── biomistral_service.py # BioMistral model with conversation history
63
+ ├── requirements.txt # Python dependencies
64
+ └── README.md # This file
65
+ ```
66
+
67
+ ## 🔧 API Endpoints
68
+
69
+ ### `GET /`
70
+ Health check and API information.
71
+
72
+ ### `GET /health`
73
+ Health check endpoint.
74
+
75
+ ### `POST /chat`
76
+ Chat endpoint with conversation support.
77
+
78
+ **Request Body:**
79
+ ```json
80
+ {
81
+ "prompt": "Your medical question or symptom description",
82
+ "session_id": "optional-session-id" // If omitted, a new session is created
83
+ }
84
+ ```
85
+
86
+ **Response:**
87
+ ```json
88
+ {
89
+ "response": "AI-generated medical advice/explanation",
90
+ "session_id": "session-id-for-continuing-conversation"
91
+ }
92
+ ```
93
+
94
+ ### `DELETE /chat/{session_id}`
95
+ Clear conversation history for a specific session.
96
+
97
+ ## 💬 Conversation Features
98
+
99
+ - **Session Management**: Each conversation has a unique `session_id`
100
+ - **Multi-turn Conversations**: Maintain context across multiple messages
101
+ - **Automatic Session Creation**: New sessions are created automatically if `session_id` is not provided
102
+ - **Conversation History**: Full conversation history is maintained per session
103
+
104
+ ## 🧠 Model Information
105
+
106
+ - **Model:** BioMistral/BioMistral-7B
107
+ - **Source:** Hugging Face
108
+ - **Purpose:** Medical chat, reasoning, and education
109
+ - **Capabilities:** Multi-turn medical conversations, symptom analysis, medical education
110
+
111
+ ## ⚠️ Important Notes
112
+
113
+ - This is an **educational tool** and should not replace professional medical consultation
114
+ - Always encourage users to consult healthcare professionals for serious conditions
115
+ - The model is loaded into memory on startup, which may take time and require significant RAM/VRAM
116
+ - Sessions are stored in memory (not persisted). Restarting the server will clear all sessions
117
+ - For production use, consider implementing persistent storage, caching, rate limiting, and proper error handling
118
+
119
+ ## 🔜 Next Steps
120
+
121
+ - Add `/analyze-image` endpoint (BiomedCLIP)
122
+ - Add `/analyze-text` endpoint (ClinicalBERT)
123
+ - Fuse all endpoints into `/triage` endpoint
124
+ - Add persistent session storage (Redis/Database)
app.py ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI, HTTPException
2
+ from pydantic import BaseModel
3
+ from typing import Optional
4
+ from model.biomistral_service import chat_with_biomistral, clear_session
5
+
6
+ app = FastAPI(
7
+ title="Quickcare",
8
+ description="AI medical education and symptom assistant with conversation support",
9
+ version="1.0.0"
10
+ )
11
+
12
+ class ChatRequest(BaseModel):
13
+ prompt: str
14
+ session_id: Optional[str] = None
15
+
16
+ class ChatResponse(BaseModel):
17
+ response: str
18
+ session_id: str
19
+
20
+ @app.post("/chat", response_model=ChatResponse)
21
+ async def chat_endpoint(request: ChatRequest):
22
+ """
23
+ Chat endpoint that maintains conversation context using session_id.
24
+
25
+ If session_id is not provided, a new session will be created.
26
+ The same session_id can be used to continue a conversation.
27
+ """
28
+ try:
29
+ user_prompt = request.prompt.strip()
30
+ if not user_prompt:
31
+ raise HTTPException(status_code=400, detail="Prompt cannot be empty")
32
+
33
+ response, session_id = chat_with_biomistral(
34
+ user_prompt=user_prompt,
35
+ session_id=request.session_id
36
+ )
37
+
38
+ return ChatResponse(response=response, session_id=session_id)
39
+ except Exception as e:
40
+ raise HTTPException(status_code=500, detail=str(e))
41
+
42
+ @app.delete("/chat/{session_id}")
43
+ async def clear_chat_session(session_id: str):
44
+ """Clear conversation history for a specific session."""
45
+ try:
46
+ cleared = clear_session(session_id)
47
+ if cleared:
48
+ return {"message": f"Session {session_id} cleared successfully"}
49
+ else:
50
+ raise HTTPException(status_code=404, detail="Session not found")
51
+ except Exception as e:
52
+ raise HTTPException(status_code=500, detail=str(e))
53
+
54
+ @app.get("/")
55
+ def home():
56
+ return {
57
+ "message": "BioMistral AI Chat API is running 🚀",
58
+ "features": [
59
+ "Conversational AI with session management",
60
+ "Multi-turn medical conversations",
61
+ "Session-based conversation history"
62
+ ]
63
+ }
64
+
65
+ @app.get("/health")
66
+ def health():
67
+ """Health check endpoint."""
68
+ return {"status": "healthy"}
69
+
model/__init__.py ADDED
File without changes
model/biomistral_service.py ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from transformers import AutoModelForCausalLM, AutoTokenizer, pipeline
2
+ from typing import Dict, List, Tuple
3
+ import uuid
4
+
5
+ MODEL_NAME = "BioMistral/BioMistral-7B"
6
+
7
+ print("🔹 Loading BioMistral model... This may take a while on first run.")
8
+
9
+ tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)
10
+ model = AutoModelForCausalLM.from_pretrained(
11
+ MODEL_NAME,
12
+ device_map="auto",
13
+ torch_dtype="auto"
14
+ )
15
+
16
+ chat_pipeline = pipeline(
17
+ "text-generation",
18
+ model=model,
19
+ tokenizer=tokenizer,
20
+ max_new_tokens=512,
21
+ temperature=0.7,
22
+ top_p=0.9
23
+ )
24
+
25
+ # Store conversation history per session
26
+ conversation_sessions: Dict[str, List[Dict[str, str]]] = {}
27
+
28
+ SYSTEM_PROMPT = (
29
+ "You are MediScope AI, a medical assistant that helps patients understand "
30
+ "their symptoms in simple, safe, and educational language. "
31
+ "Always encourage professional consultation for serious conditions."
32
+ )
33
+
34
+ def get_or_create_session(session_id: str) -> List[Dict[str, str]]:
35
+ """Get existing session or create a new one."""
36
+ if session_id not in conversation_sessions:
37
+ conversation_sessions[session_id] = []
38
+ return conversation_sessions[session_id]
39
+
40
+ def build_conversation_prompt(history: List[Dict[str, str]], user_prompt: str) -> str:
41
+ """Build the full conversation prompt from history and new user message."""
42
+ prompt_parts = [SYSTEM_PROMPT]
43
+
44
+ # Add conversation history
45
+ for msg in history:
46
+ if msg["role"] == "user":
47
+ prompt_parts.append(f"User: {msg['content']}")
48
+ elif msg["role"] == "assistant":
49
+ prompt_parts.append(f"Assistant: {msg['content']}")
50
+
51
+ # Add current user message
52
+ prompt_parts.append(f"User: {user_prompt}")
53
+ prompt_parts.append("Assistant:")
54
+
55
+ return "\n\n".join(prompt_parts)
56
+
57
+ def chat_with_biomistral(user_prompt: str, session_id: str = None) -> Tuple[str, str]:
58
+ """
59
+ Chat with BioMistral model, maintaining conversation history.
60
+
61
+ Args:
62
+ user_prompt: The user's message
63
+ session_id: Optional session ID. If None, a new session is created.
64
+
65
+ Returns:
66
+ tuple: (response_text, session_id)
67
+ """
68
+ # Generate or use provided session ID
69
+ if session_id is None:
70
+ session_id = str(uuid.uuid4())
71
+
72
+ # Get conversation history for this session
73
+ history = get_or_create_session(session_id)
74
+
75
+ # Build the full conversation prompt
76
+ full_prompt = build_conversation_prompt(history, user_prompt)
77
+
78
+ # Generate response
79
+ response = chat_pipeline(full_prompt)[0]["generated_text"]
80
+
81
+ # Extract only the assistant's reply (everything after the last "Assistant:")
82
+ reply = response.split("Assistant:")[-1].strip()
83
+
84
+ # Update conversation history
85
+ history.append({"role": "user", "content": user_prompt})
86
+ history.append({"role": "assistant", "content": reply})
87
+
88
+ return reply, session_id
89
+
90
+ def clear_session(session_id: str) -> bool:
91
+ """Clear conversation history for a session."""
92
+ if session_id in conversation_sessions:
93
+ conversation_sessions[session_id] = []
94
+ return True
95
+ return False
96
+
requirements.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ fastapi
2
+ uvicorn[standard]
3
+ transformers
4
+ torch
5
+ accelerate
6
+ bitsandbytes
7
+