Spaces:
Running
Running
Girish Jeswani commited on
Commit ·
b19def4
1
Parent(s): b812a6c
updated gemini context management
Browse files
multi_llm_chatbot_backend/app/core/context_manager.py
CHANGED
|
@@ -141,24 +141,46 @@ class ContextManager:
|
|
| 141 |
def _format_for_gemini(self, messages: List[dict], system_prompt: str) -> List[dict]:
|
| 142 |
"""
|
| 143 |
Format messages for Gemini API (uses user/model roles with parts structure)
|
|
|
|
| 144 |
"""
|
| 145 |
formatted = []
|
| 146 |
|
| 147 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
| 148 |
if system_prompt:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 149 |
formatted.extend([
|
| 150 |
{
|
| 151 |
"role": "user",
|
| 152 |
-
"parts": [{"text":
|
| 153 |
},
|
| 154 |
{
|
| 155 |
"role": "model",
|
| 156 |
-
"parts": [{"text": "I understand. I'll follow these instructions."}]
|
| 157 |
}
|
| 158 |
])
|
| 159 |
|
| 160 |
-
# Convert messages to Gemini format
|
| 161 |
-
for message in
|
| 162 |
role = message['role']
|
| 163 |
content = message['content']
|
| 164 |
|
|
@@ -178,7 +200,7 @@ class ContextManager:
|
|
| 178 |
"role": "user",
|
| 179 |
"parts": [{"text": f"[Context Document] {content}"}]
|
| 180 |
})
|
| 181 |
-
|
| 182 |
return formatted
|
| 183 |
|
| 184 |
def _format_for_ollama(self, messages: List[dict], system_prompt: str) -> str:
|
|
|
|
| 141 |
def _format_for_gemini(self, messages: List[dict], system_prompt: str) -> List[dict]:
|
| 142 |
"""
|
| 143 |
Format messages for Gemini API (uses user/model roles with parts structure)
|
| 144 |
+
FIXED VERSION - Better handles system messages and document context
|
| 145 |
"""
|
| 146 |
formatted = []
|
| 147 |
|
| 148 |
+
# Process all messages, handling system messages properly
|
| 149 |
+
system_message_content = ""
|
| 150 |
+
conversation_messages = []
|
| 151 |
+
|
| 152 |
+
# Add initial system prompt if provided
|
| 153 |
if system_prompt:
|
| 154 |
+
system_message_content = system_prompt
|
| 155 |
+
|
| 156 |
+
for message in messages:
|
| 157 |
+
role = message['role']
|
| 158 |
+
content = message['content']
|
| 159 |
+
|
| 160 |
+
if role == 'system':
|
| 161 |
+
# Accumulate system messages
|
| 162 |
+
if system_message_content:
|
| 163 |
+
system_message_content += "\n\n" + content
|
| 164 |
+
else:
|
| 165 |
+
system_message_content = content
|
| 166 |
+
else:
|
| 167 |
+
conversation_messages.append(message)
|
| 168 |
+
|
| 169 |
+
# Add accumulated system content as initial exchange
|
| 170 |
+
if system_message_content:
|
| 171 |
formatted.extend([
|
| 172 |
{
|
| 173 |
"role": "user",
|
| 174 |
+
"parts": [{"text": system_message_content}]
|
| 175 |
},
|
| 176 |
{
|
| 177 |
"role": "model",
|
| 178 |
+
"parts": [{"text": "I understand. I'll follow these instructions and use the document context you've provided."}]
|
| 179 |
}
|
| 180 |
])
|
| 181 |
|
| 182 |
+
# Convert conversation messages to Gemini format
|
| 183 |
+
for message in conversation_messages:
|
| 184 |
role = message['role']
|
| 185 |
content = message['content']
|
| 186 |
|
|
|
|
| 200 |
"role": "user",
|
| 201 |
"parts": [{"text": f"[Context Document] {content}"}]
|
| 202 |
})
|
| 203 |
+
|
| 204 |
return formatted
|
| 205 |
|
| 206 |
def _format_for_ollama(self, messages: List[dict], system_prompt: str) -> str:
|
multi_llm_chatbot_backend/app/core/improved_orchestrator.py
CHANGED
|
@@ -446,10 +446,10 @@ When analyzing the document context:
|
|
| 446 |
async def _build_enhanced_context_for_persona(self, session, persona, user_message: str, document_context: str) -> List[Dict[str, str]]:
|
| 447 |
"""
|
| 448 |
Build enhanced context that properly integrates document information with conversation history
|
| 449 |
-
FIXED VERSION -
|
| 450 |
"""
|
| 451 |
enhanced_context = []
|
| 452 |
-
|
| 453 |
# Get recent conversation history (last 6 messages for efficiency)
|
| 454 |
recent_messages = session.messages[-6:] if len(session.messages) > 6 else session.messages
|
| 455 |
|
|
@@ -467,6 +467,7 @@ When analyzing the document context:
|
|
| 467 |
CURRENT SESSION CONTEXT:
|
| 468 |
The student has uploaded the following documents: {doc_list}
|
| 469 |
|
|
|
|
| 470 |
{document_context}
|
| 471 |
|
| 472 |
IMPORTANT: When the student refers to "my document," "my dissertation," "my proposal," etc., they are referring to one of their uploaded documents. Use the document context above to understand which specific document they mean and reference it by name in your response.
|
|
@@ -495,21 +496,15 @@ When analyzing the document context:
|
|
| 495 |
"role": "system",
|
| 496 |
"content": system_message
|
| 497 |
})
|
| 498 |
-
|
| 499 |
-
# Add recent conversation
|
| 500 |
for message in recent_messages:
|
| 501 |
-
if message.get('role')
|
| 502 |
enhanced_context.append({
|
| 503 |
"role": message['role'],
|
| 504 |
"content": message['content']
|
| 505 |
})
|
| 506 |
-
|
| 507 |
-
# Add current user message
|
| 508 |
-
enhanced_context.append({
|
| 509 |
-
"role": "user",
|
| 510 |
-
"content": user_message
|
| 511 |
-
})
|
| 512 |
-
|
| 513 |
return enhanced_context
|
| 514 |
|
| 515 |
def _is_valid_response(self, response: str, persona_id: str) -> bool:
|
|
|
|
| 446 |
async def _build_enhanced_context_for_persona(self, session, persona, user_message: str, document_context: str) -> List[Dict[str, str]]:
|
| 447 |
"""
|
| 448 |
Build enhanced context that properly integrates document information with conversation history
|
| 449 |
+
FIXED VERSION - Ensures document context is properly preserved for both providers
|
| 450 |
"""
|
| 451 |
enhanced_context = []
|
| 452 |
+
|
| 453 |
# Get recent conversation history (last 6 messages for efficiency)
|
| 454 |
recent_messages = session.messages[-6:] if len(session.messages) > 6 else session.messages
|
| 455 |
|
|
|
|
| 467 |
CURRENT SESSION CONTEXT:
|
| 468 |
The student has uploaded the following documents: {doc_list}
|
| 469 |
|
| 470 |
+
DOCUMENT CONTENT:
|
| 471 |
{document_context}
|
| 472 |
|
| 473 |
IMPORTANT: When the student refers to "my document," "my dissertation," "my proposal," etc., they are referring to one of their uploaded documents. Use the document context above to understand which specific document they mean and reference it by name in your response.
|
|
|
|
| 496 |
"role": "system",
|
| 497 |
"content": system_message
|
| 498 |
})
|
| 499 |
+
|
| 500 |
+
# Add recent conversation messages (excluding system messages to avoid duplication)
|
| 501 |
for message in recent_messages:
|
| 502 |
+
if message.get('role') != 'system':
|
| 503 |
enhanced_context.append({
|
| 504 |
"role": message['role'],
|
| 505 |
"content": message['content']
|
| 506 |
})
|
| 507 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 508 |
return enhanced_context
|
| 509 |
|
| 510 |
def _is_valid_response(self, response: str, persona_id: str) -> bool:
|
multi_llm_chatbot_backend/app/llm/improved_gemini_client.py
CHANGED
|
@@ -23,6 +23,7 @@ class ImprovedGeminiClient(LLMClient):
|
|
| 23 |
async def generate(self, system_prompt: str, context: List[dict], temperature: float, max_tokens: int) -> str:
|
| 24 |
"""
|
| 25 |
Generate response using improved context management
|
|
|
|
| 26 |
"""
|
| 27 |
try:
|
| 28 |
# Use context manager to prepare optimal context window
|
|
@@ -35,6 +36,9 @@ class ImprovedGeminiClient(LLMClient):
|
|
| 35 |
logger.debug(f"Context prepared: {len(context_window.messages)} messages, "
|
| 36 |
f"~{context_window.total_tokens} tokens, truncated={context_window.truncated}")
|
| 37 |
|
|
|
|
|
|
|
|
|
|
| 38 |
payload = {
|
| 39 |
"contents": context_window.messages,
|
| 40 |
"generationConfig": {
|
|
@@ -50,7 +54,7 @@ class ImprovedGeminiClient(LLMClient):
|
|
| 50 |
"threshold": "BLOCK_MEDIUM_AND_ABOVE"
|
| 51 |
},
|
| 52 |
{
|
| 53 |
-
"category": "HARM_CATEGORY_HATE_SPEECH",
|
| 54 |
"threshold": "BLOCK_MEDIUM_AND_ABOVE"
|
| 55 |
},
|
| 56 |
{
|
|
@@ -64,33 +68,41 @@ class ImprovedGeminiClient(LLMClient):
|
|
| 64 |
]
|
| 65 |
}
|
| 66 |
|
| 67 |
-
url = f"{self.base_url}/{self.model_name}:generateContent?key={self.api_key}"
|
| 68 |
-
|
| 69 |
async with httpx.AsyncClient(timeout=30.0) as client:
|
| 70 |
-
response = await client.post(
|
|
|
|
|
|
|
|
|
|
|
|
|
| 71 |
response.raise_for_status()
|
| 72 |
|
| 73 |
result = response.json()
|
| 74 |
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
return self._clean_response(text)
|
| 80 |
|
| 81 |
-
|
| 82 |
-
if "promptFeedback" in result:
|
| 83 |
-
feedback = result["promptFeedback"]
|
| 84 |
-
logger.warning(f"Gemini prompt feedback: {feedback}")
|
| 85 |
|
| 86 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 87 |
|
| 88 |
-
except httpx.TimeoutException:
|
| 89 |
-
logger.error("Gemini API timeout")
|
| 90 |
-
return "I'm experiencing a delay in processing. Please try again."
|
| 91 |
except httpx.HTTPStatusError as e:
|
| 92 |
logger.error(f"Gemini API HTTP error: {e.response.status_code} - {e.response.text}")
|
| 93 |
-
return "I'm
|
|
|
|
|
|
|
|
|
|
| 94 |
except Exception as e:
|
| 95 |
logger.error(f"Unexpected error in Gemini client: {str(e)}")
|
| 96 |
return "I encountered an unexpected error. Please try again."
|
|
|
|
| 23 |
async def generate(self, system_prompt: str, context: List[dict], temperature: float, max_tokens: int) -> str:
|
| 24 |
"""
|
| 25 |
Generate response using improved context management
|
| 26 |
+
FIXED VERSION - Better debugging and context handling
|
| 27 |
"""
|
| 28 |
try:
|
| 29 |
# Use context manager to prepare optimal context window
|
|
|
|
| 36 |
logger.debug(f"Context prepared: {len(context_window.messages)} messages, "
|
| 37 |
f"~{context_window.total_tokens} tokens, truncated={context_window.truncated}")
|
| 38 |
|
| 39 |
+
# DEBUG: Log the actual content being sent to Gemini
|
| 40 |
+
logger.debug(f"Gemini payload preview: {str(context_window.messages)[:500]}...")
|
| 41 |
+
|
| 42 |
payload = {
|
| 43 |
"contents": context_window.messages,
|
| 44 |
"generationConfig": {
|
|
|
|
| 54 |
"threshold": "BLOCK_MEDIUM_AND_ABOVE"
|
| 55 |
},
|
| 56 |
{
|
| 57 |
+
"category": "HARM_CATEGORY_HATE_SPEECH",
|
| 58 |
"threshold": "BLOCK_MEDIUM_AND_ABOVE"
|
| 59 |
},
|
| 60 |
{
|
|
|
|
| 68 |
]
|
| 69 |
}
|
| 70 |
|
|
|
|
|
|
|
| 71 |
async with httpx.AsyncClient(timeout=30.0) as client:
|
| 72 |
+
response = await client.post(
|
| 73 |
+
f"{self.base_url}/{self.model_name}:generateContent",
|
| 74 |
+
json=payload,
|
| 75 |
+
headers={"x-goog-api-key": self.api_key}
|
| 76 |
+
)
|
| 77 |
response.raise_for_status()
|
| 78 |
|
| 79 |
result = response.json()
|
| 80 |
|
| 81 |
+
# Better error handling
|
| 82 |
+
if "candidates" not in result or not result["candidates"]:
|
| 83 |
+
logger.error(f"No candidates in Gemini response: {result}")
|
| 84 |
+
return "I apologize, but I'm unable to generate a response right now. Please try again."
|
|
|
|
| 85 |
|
| 86 |
+
candidate = result["candidates"][0]
|
|
|
|
|
|
|
|
|
|
| 87 |
|
| 88 |
+
if "content" not in candidate or "parts" not in candidate["content"]:
|
| 89 |
+
logger.error(f"Invalid candidate structure: {candidate}")
|
| 90 |
+
return "I apologize, but I received an unexpected response format. Please try again."
|
| 91 |
+
|
| 92 |
+
text = candidate["content"]["parts"][0].get("text", "").strip()
|
| 93 |
+
|
| 94 |
+
if not text:
|
| 95 |
+
logger.warning("Empty response from Gemini")
|
| 96 |
+
return "I apologize, but I couldn't generate a meaningful response. Please try rephrasing your question."
|
| 97 |
+
|
| 98 |
+
return self._clean_response(text)
|
| 99 |
|
|
|
|
|
|
|
|
|
|
| 100 |
except httpx.HTTPStatusError as e:
|
| 101 |
logger.error(f"Gemini API HTTP error: {e.response.status_code} - {e.response.text}")
|
| 102 |
+
return "I'm experiencing issues connecting to the AI service. Please try again."
|
| 103 |
+
except httpx.TimeoutException:
|
| 104 |
+
logger.error("Gemini API timeout")
|
| 105 |
+
return "The AI service is taking too long to respond. Please try again."
|
| 106 |
except Exception as e:
|
| 107 |
logger.error(f"Unexpected error in Gemini client: {str(e)}")
|
| 108 |
return "I encountered an unexpected error. Please try again."
|