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
- # Add system prompt as initial exchange
 
 
 
 
148
  if system_prompt:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
149
  formatted.extend([
150
  {
151
  "role": "user",
152
- "parts": [{"text": system_prompt}]
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 messages:
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 - No document hallucination when no documents exist
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 history
500
  for message in recent_messages:
501
- if message.get('role') in ['user', 'assistant']:
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(url, json=payload)
 
 
 
 
71
  response.raise_for_status()
72
 
73
  result = response.json()
74
 
75
- if "candidates" in result and result["candidates"]:
76
- candidate = result["candidates"][0]
77
- if "content" in candidate and "parts" in candidate["content"]:
78
- text = candidate["content"]["parts"][0].get("text", "")
79
- return self._clean_response(text)
80
 
81
- # Handle safety filter or other issues
82
- if "promptFeedback" in result:
83
- feedback = result["promptFeedback"]
84
- logger.warning(f"Gemini prompt feedback: {feedback}")
85
 
86
- return "I apologize, but I'm unable to provide a response to that query."
 
 
 
 
 
 
 
 
 
 
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 having trouble accessing the AI service. Please try again."
 
 
 
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."