push updated backend changes and auto start buiding
Browse files- .env +0 -0
- Dockerfile +15 -8
- __pycache__/agents.cpython-312.pyc +0 -0
- __pycache__/app.cpython-312.pyc +0 -0
- __pycache__/config.cpython-312.pyc +0 -0
- __pycache__/rag.cpython-312.pyc +0 -0
- agents.py +163 -67
- app.py +32 -7
- untitled +0 -0
.env
ADDED
File without changes
|
Dockerfile
CHANGED
@@ -3,23 +3,30 @@ FROM python:3.10-slim
|
|
3 |
# Install system dependencies
|
4 |
RUN apt-get update && apt-get install -y git && rm -rf /var/lib/apt/lists/*
|
5 |
|
6 |
-
# Set Hugging Face cache to a writable directory
|
7 |
-
ENV HF_HOME=/
|
8 |
-
ENV TRANSFORMERS_CACHE=/
|
9 |
-
ENV
|
|
|
|
|
|
|
|
|
10 |
|
11 |
# Set working directory
|
12 |
WORKDIR /app
|
13 |
|
14 |
-
# Copy
|
15 |
-
COPY .
|
16 |
|
17 |
# Install Python dependencies
|
18 |
RUN pip install --no-cache-dir --upgrade pip \
|
19 |
&& pip install --no-cache-dir -r requirements.txt
|
20 |
|
|
|
|
|
|
|
21 |
# Expose the port Hugging Face Spaces expects
|
22 |
EXPOSE 7860
|
23 |
|
24 |
-
# Run FastAPI with Uvicorn
|
25 |
-
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]
|
|
|
3 |
# Install system dependencies
|
4 |
RUN apt-get update && apt-get install -y git && rm -rf /var/lib/apt/lists/*
|
5 |
|
6 |
+
# Set Hugging Face cache to a writable directory in /tmp
|
7 |
+
ENV HF_HOME=/tmp/huggingface
|
8 |
+
ENV TRANSFORMERS_CACHE=/tmp/huggingface
|
9 |
+
ENV HUGGINGFACE_HUB_CACHE=/tmp/huggingface
|
10 |
+
ENV HF_HUB_CACHE=/tmp/huggingface
|
11 |
+
|
12 |
+
# Create cache directory with proper permissions
|
13 |
+
RUN mkdir -p /tmp/huggingface && chmod 777 /tmp/huggingface
|
14 |
|
15 |
# Set working directory
|
16 |
WORKDIR /app
|
17 |
|
18 |
+
# Copy requirements first for better caching
|
19 |
+
COPY requirements.txt .
|
20 |
|
21 |
# Install Python dependencies
|
22 |
RUN pip install --no-cache-dir --upgrade pip \
|
23 |
&& pip install --no-cache-dir -r requirements.txt
|
24 |
|
25 |
+
# Copy project files
|
26 |
+
COPY . /app
|
27 |
+
|
28 |
# Expose the port Hugging Face Spaces expects
|
29 |
EXPOSE 7860
|
30 |
|
31 |
+
# Run FastAPI with Uvicorn (with reload disabled for production)
|
32 |
+
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860", "--workers", "1"]
|
__pycache__/agents.cpython-312.pyc
ADDED
Binary file (6.32 kB). View file
|
|
__pycache__/app.cpython-312.pyc
ADDED
Binary file (1.49 kB). View file
|
|
__pycache__/config.cpython-312.pyc
ADDED
Binary file (1.47 kB). View file
|
|
__pycache__/rag.cpython-312.pyc
ADDED
Binary file (8.72 kB). View file
|
|
agents.py
CHANGED
@@ -1,99 +1,195 @@
|
|
1 |
-
|
2 |
import torch
|
3 |
-
from transformers import AutoModelForCausalLM, AutoTokenizer
|
4 |
-
from autogen import AssistantAgent, UserProxyAgent
|
5 |
from config import LLM_MODEL, CONFIDENCE_THRESHOLD, VECTORSTORE_DIR
|
6 |
-
from rag import RAGAgent
|
7 |
import os
|
8 |
import sys
|
|
|
|
|
|
|
|
|
|
|
9 |
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
os.environ["HF_HUB_CACHE"] = os.path.join(hf_cache, "hub")
|
16 |
|
17 |
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
|
18 |
if BASE_DIR not in sys.path:
|
19 |
sys.path.insert(0, BASE_DIR)
|
20 |
|
21 |
-
|
22 |
# Load BioMistral once
|
23 |
class BioMistralModel:
|
24 |
def __init__(self, model_name=LLM_MODEL, device=None):
|
25 |
-
|
26 |
self.device = device or ("cuda" if torch.cuda.is_available() else "cpu")
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
)
|
33 |
-
|
34 |
-
def generate_answer(self, query: str) -> str:
|
35 |
-
prompt = f"You are a helpful bioinformatics tutor. Answer clearly:\n\nQuestion: {query}\nAnswer:"
|
36 |
-
inputs = self.tokenizer(prompt, return_tensors="pt").to(self.device)
|
37 |
-
|
38 |
-
with torch.no_grad():
|
39 |
-
outputs = self.model.generate(
|
40 |
-
**inputs,
|
41 |
-
max_new_tokens=512,
|
42 |
-
do_sample=True,
|
43 |
-
top_p=0.95,
|
44 |
-
temperature=0.7,
|
45 |
-
pad_token_id=self.tokenizer.eos_token_id
|
46 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
47 |
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
# Formatting Agent
|
53 |
-
class FormattingAgent(AssistantAgent):
|
54 |
-
def __init__(self, name="FormattingAgent", **kwargs):
|
55 |
-
super().__init__(name=name, **kwargs)
|
56 |
|
57 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
58 |
cleaned = " ".join(text.split())
|
59 |
if cleaned:
|
60 |
cleaned = cleaned[0].upper() + cleaned[1:]
|
|
|
|
|
|
|
61 |
return cleaned
|
62 |
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
super().__init__(name=name, **kwargs)
|
68 |
self.model = BioMistralModel()
|
69 |
-
self.
|
70 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
71 |
|
72 |
def process_query(self, query: str) -> str:
|
73 |
-
|
|
|
|
|
|
|
74 |
|
|
|
75 |
answer = self.model.generate_answer(query)
|
76 |
confidence = self.estimate_confidence(answer)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
77 |
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
83 |
|
84 |
def estimate_confidence(self, answer: str) -> float:
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
|
|
|
|
|
|
|
|
|
|
91 |
return 0.5
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
class
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
|
|
|
|
|
1 |
+
|
2 |
import torch
|
3 |
+
from transformers import AutoModelForCausalLM, AutoTokenizer, pipeline
|
|
|
4 |
from config import LLM_MODEL, CONFIDENCE_THRESHOLD, VECTORSTORE_DIR
|
|
|
5 |
import os
|
6 |
import sys
|
7 |
+
import logging
|
8 |
+
|
9 |
+
# Set up logging
|
10 |
+
logging.basicConfig(level=logging.INFO)
|
11 |
+
logger = logging.getLogger(__name__)
|
12 |
|
13 |
+
hf_cache = "/tmp/huggingface"
|
14 |
+
os.environ["HF_HOME"] = hf_cache
|
15 |
+
os.environ["TRANSFORMERS_CACHE"] = hf_cache
|
16 |
+
os.environ["HUGGINGFACE_HUB_CACHE"] = hf_cache
|
17 |
+
os.makedirs(hf_cache, exist_ok=True)
|
|
|
18 |
|
19 |
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
|
20 |
if BASE_DIR not in sys.path:
|
21 |
sys.path.insert(0, BASE_DIR)
|
22 |
|
|
|
23 |
# Load BioMistral once
|
24 |
class BioMistralModel:
|
25 |
def __init__(self, model_name=LLM_MODEL, device=None):
|
26 |
+
logger.info(f"Loading model: {model_name}")
|
27 |
self.device = device or ("cuda" if torch.cuda.is_available() else "cpu")
|
28 |
+
|
29 |
+
try:
|
30 |
+
self.tokenizer = AutoTokenizer.from_pretrained(
|
31 |
+
model_name,
|
32 |
+
cache_dir=hf_cache
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
33 |
)
|
34 |
+
self.model = AutoModelForCausalLM.from_pretrained(
|
35 |
+
model_name,
|
36 |
+
torch_dtype=torch.float16 if self.device == "cuda" else torch.float32,
|
37 |
+
device_map="auto" if self.device == "cuda" else None,
|
38 |
+
cache_dir=hf_cache
|
39 |
+
)
|
40 |
+
logger.info("Model loaded successfully")
|
41 |
+
except Exception as e:
|
42 |
+
logger.error(f"Error loading model: {e}")
|
43 |
+
# Fallback to pipeline
|
44 |
+
self.pipeline = pipeline(
|
45 |
+
"text-generation",
|
46 |
+
model=model_name,
|
47 |
+
device=0 if self.device == "cuda" else -1,
|
48 |
+
torch_dtype=torch.float16 if self.device == "cuda" else torch.float32
|
49 |
+
)
|
50 |
+
self.use_pipeline = True
|
51 |
+
else:
|
52 |
+
self.use_pipeline = False
|
53 |
|
54 |
+
def generate_answer(self, query: str) -> str:
|
55 |
+
prompt = f"""You are a helpful bioinformatics tutor. Answer clearly and concisely.
|
|
|
|
|
|
|
|
|
|
|
|
|
56 |
|
57 |
+
Question: {query}
|
58 |
+
Answer:"""
|
59 |
+
|
60 |
+
try:
|
61 |
+
if hasattr(self, 'use_pipeline') and self.use_pipeline:
|
62 |
+
# Use pipeline fallback
|
63 |
+
result = self.pipeline(
|
64 |
+
prompt,
|
65 |
+
max_new_tokens=256,
|
66 |
+
do_sample=True,
|
67 |
+
top_p=0.9,
|
68 |
+
temperature=0.7,
|
69 |
+
pad_token_id=self.pipeline.tokenizer.eos_token_id
|
70 |
+
)
|
71 |
+
full_text = result[0]['generated_text']
|
72 |
+
else:
|
73 |
+
# Use model directly
|
74 |
+
inputs = self.tokenizer(prompt, return_tensors="pt").to(self.device)
|
75 |
+
|
76 |
+
with torch.no_grad():
|
77 |
+
outputs = self.model.generate(
|
78 |
+
**inputs,
|
79 |
+
max_new_tokens=256,
|
80 |
+
do_sample=True,
|
81 |
+
top_p=0.9,
|
82 |
+
temperature=0.7,
|
83 |
+
pad_token_id=self.tokenizer.eos_token_id
|
84 |
+
)
|
85 |
+
|
86 |
+
full_text = self.tokenizer.decode(outputs[0], skip_special_tokens=True)
|
87 |
+
|
88 |
+
# Extract only the answer part
|
89 |
+
if "Answer:" in full_text:
|
90 |
+
return full_text.split("Answer:", 1)[-1].strip()
|
91 |
+
else:
|
92 |
+
return full_text.replace(prompt, "").strip()
|
93 |
+
|
94 |
+
except Exception as e:
|
95 |
+
logger.error(f"Error generating answer: {e}")
|
96 |
+
return f"I apologize, but I encountered an error while processing your question: {str(e)}"
|
97 |
+
|
98 |
+
# Formatting utility
|
99 |
+
class TextFormatter:
|
100 |
+
@staticmethod
|
101 |
+
def format_text(text: str) -> str:
|
102 |
+
"""Clean and format text output"""
|
103 |
+
if not text:
|
104 |
+
return "I don't have an answer for that question. Could you please rephrase or ask something else?"
|
105 |
+
|
106 |
+
# Basic cleaning
|
107 |
cleaned = " ".join(text.split())
|
108 |
if cleaned:
|
109 |
cleaned = cleaned[0].upper() + cleaned[1:]
|
110 |
+
# Ensure it ends with punctuation
|
111 |
+
if not cleaned[-1] in {'.', '!', '?'}:
|
112 |
+
cleaned += '.'
|
113 |
return cleaned
|
114 |
|
115 |
+
# Tutor Agent
|
116 |
+
class TutorAgent:
|
117 |
+
def __init__(self):
|
118 |
+
logger.info("Initializing TutorAgent")
|
|
|
119 |
self.model = BioMistralModel()
|
120 |
+
self.formatter = TextFormatter()
|
121 |
+
|
122 |
+
# Initialize RAG
|
123 |
+
self.rag_agent = None
|
124 |
+
try:
|
125 |
+
from rag import RAGAgent
|
126 |
+
self.rag_agent = RAGAgent(vectorstore_dir=str(VECTORSTORE_DIR))
|
127 |
+
logger.info("RAG agent initialized")
|
128 |
+
except ImportError as e:
|
129 |
+
logger.warning(f"RAG not available: {e}")
|
130 |
+
except Exception as e:
|
131 |
+
logger.warning(f"Failed to initialize RAG: {e}")
|
132 |
|
133 |
def process_query(self, query: str) -> str:
|
134 |
+
logger.info(f"Processing query: {query}")
|
135 |
+
|
136 |
+
if not query or len(query.strip()) < 2:
|
137 |
+
return "Please ask a meaningful question about bioinformatics."
|
138 |
|
139 |
+
# Generate answer
|
140 |
answer = self.model.generate_answer(query)
|
141 |
confidence = self.estimate_confidence(answer)
|
142 |
+
|
143 |
+
logger.info(f"Confidence: {confidence:.2f}")
|
144 |
+
|
145 |
+
# If confidence is low and RAG is available, try to enhance
|
146 |
+
if confidence < CONFIDENCE_THRESHOLD and self.rag_agent:
|
147 |
+
logger.info("Low confidence, attempting RAG enhancement")
|
148 |
+
try:
|
149 |
+
rag_answer = self._enhance_with_rag(query)
|
150 |
+
if rag_answer and len(rag_answer) > len(answer):
|
151 |
+
answer = rag_answer
|
152 |
+
except Exception as e:
|
153 |
+
logger.warning(f"RAG enhancement failed: {e}")
|
154 |
+
|
155 |
+
return self.formatter.format_text(answer)
|
156 |
|
157 |
+
def _enhance_with_rag(self, query: str) -> str:
|
158 |
+
"""Enhance answer using RAG if available"""
|
159 |
+
if not self.rag_agent:
|
160 |
+
return ""
|
161 |
+
|
162 |
+
try:
|
163 |
+
# Assuming RAGAgent has an answer method
|
164 |
+
if hasattr(self.rag_agent, 'answer'):
|
165 |
+
result = self.rag_agent.answer(query)
|
166 |
+
return result.get('answer', '') if isinstance(result, dict) else str(result)
|
167 |
+
else:
|
168 |
+
return ""
|
169 |
+
except Exception as e:
|
170 |
+
logger.error(f"RAG error: {e}")
|
171 |
+
return ""
|
172 |
|
173 |
def estimate_confidence(self, answer: str) -> float:
|
174 |
+
"""Simple confidence estimation"""
|
175 |
+
answer = answer.strip()
|
176 |
+
if not answer:
|
177 |
+
return 0.0
|
178 |
+
|
179 |
+
length = len(answer)
|
180 |
+
if length > 150:
|
181 |
+
return 0.85
|
182 |
+
elif length > 80:
|
183 |
+
return 0.7
|
184 |
+
elif length > 30:
|
185 |
return 0.5
|
186 |
+
else:
|
187 |
+
return 0.3
|
188 |
+
|
189 |
+
# User class (
|
190 |
+
class BioUser:
|
191 |
+
def __init__(self, name="BioUser"):
|
192 |
+
self.name = name
|
193 |
+
|
194 |
+
def ask_question(self, question: str, tutor: TutorAgent) -> str:
|
195 |
+
return tutor.process_query(question)
|
app.py
CHANGED
@@ -1,15 +1,31 @@
|
|
1 |
-
#
|
|
|
2 |
import uvicorn
|
3 |
from fastapi import FastAPI
|
4 |
from pydantic import BaseModel
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5 |
from agents import TutorAgent, BioUser
|
6 |
|
7 |
# Initialize FastAPI
|
8 |
app = FastAPI(title="Bioinformatics Tutor API")
|
9 |
|
10 |
-
# Initialize agents
|
11 |
-
|
12 |
-
|
|
|
|
|
|
|
|
|
|
|
13 |
|
14 |
# Request model
|
15 |
class QueryRequest(BaseModel):
|
@@ -24,10 +40,19 @@ def ask_tutor(request: QueryRequest):
|
|
24 |
"""
|
25 |
Ask the Bioinformatics Tutor a question.
|
26 |
"""
|
27 |
-
|
28 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
29 |
|
30 |
@app.get("/")
|
31 |
def root():
|
32 |
-
return {"message": "Bioinformatics Tutor API is running."}
|
33 |
|
|
|
|
|
|
|
|
1 |
+
# app.py
|
2 |
+
import os
|
3 |
import uvicorn
|
4 |
from fastapi import FastAPI
|
5 |
from pydantic import BaseModel
|
6 |
+
|
7 |
+
|
8 |
+
os.environ['HF_HOME'] = '/tmp/huggingface'
|
9 |
+
os.environ['TRANSFORMERS_CACHE'] = '/tmp/huggingface'
|
10 |
+
os.environ['HUGGINGFACE_HUB_CACHE'] = '/tmp/huggingface'
|
11 |
+
|
12 |
+
# Create cache directory
|
13 |
+
os.makedirs('/tmp/huggingface', exist_ok=True)
|
14 |
+
|
15 |
+
|
16 |
from agents import TutorAgent, BioUser
|
17 |
|
18 |
# Initialize FastAPI
|
19 |
app = FastAPI(title="Bioinformatics Tutor API")
|
20 |
|
21 |
+
# Initialize agents
|
22 |
+
try:
|
23 |
+
user_agent = BioUser()
|
24 |
+
tutor_agent = TutorAgent()
|
25 |
+
agents_loaded = True
|
26 |
+
except Exception as e:
|
27 |
+
print(f"Error loading agents: {e}")
|
28 |
+
agents_loaded = False
|
29 |
|
30 |
# Request model
|
31 |
class QueryRequest(BaseModel):
|
|
|
40 |
"""
|
41 |
Ask the Bioinformatics Tutor a question.
|
42 |
"""
|
43 |
+
if not agents_loaded:
|
44 |
+
return QueryResponse(answer="Error: Agents not loaded. Please check the server logs.")
|
45 |
+
|
46 |
+
try:
|
47 |
+
answer = tutor_agent.process_query(request.question)
|
48 |
+
return QueryResponse(answer=answer)
|
49 |
+
except Exception as e:
|
50 |
+
return QueryResponse(answer=f"Error processing query: {str(e)}")
|
51 |
|
52 |
@app.get("/")
|
53 |
def root():
|
54 |
+
return {"message": "Bioinformatics Tutor API is running.", "agents_loaded": agents_loaded}
|
55 |
|
56 |
+
@app.get("/health")
|
57 |
+
def health_check():
|
58 |
+
return {"status": "healthy", "agents_loaded": agents_loaded}
|
untitled
ADDED
File without changes
|