Girish Jeswani commited on
Commit
3ee2842
·
1 Parent(s): 9f5fa59

add gemini routes

Browse files
multi_llm_chatbot_backend/app/api/routes.py CHANGED
@@ -1,6 +1,8 @@
 
1
  from fastapi import APIRouter, Body, HTTPException
2
  import httpx
3
  from app.llm.llm_client import LLMClient
 
4
  from app.models.persona import Persona
5
  from app.core.orchestrator import ChatOrchestrator
6
  from app.core.seamless_orchestrator import SeamlessOrchestrator
@@ -9,7 +11,28 @@ from typing import Optional, List
9
 
10
  router = APIRouter()
11
 
12
- # Improved LLM client with better short response handling
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
  class ShortResponseOllamaClient(LLMClient):
14
  def __init__(self, model_name: str = "llama3.2:1b"):
15
  self.model_name = model_name
@@ -44,7 +67,7 @@ class ShortResponseOllamaClient(LLMClient):
44
  "temperature": 0.7,
45
  "top_p": 0.9,
46
  "top_k": 40,
47
- "num_predict": 200, # Increased from 150
48
  "repeat_penalty": 1.1,
49
  }
50
  }
@@ -70,7 +93,8 @@ class ShortResponseOllamaClient(LLMClient):
70
  except Exception as e:
71
  return f"I apologize, but I'm having trouble generating a response right now. Please try again."
72
 
73
- llm = ShortResponseOllamaClient(model_name="llama3.2:1b")
 
74
  chat_orchestrator = ChatOrchestrator()
75
  seamless_orchestrator = SeamlessOrchestrator(llm=llm)
76
 
@@ -83,7 +107,6 @@ class GlobalSessionContext:
83
  self.full_log.append({"role": role, "content": content})
84
 
85
  def filter_by_persona(self, persona_id: str):
86
- # Return full context but mark the persona for better understanding
87
  return self.full_log
88
 
89
  def clear(self):
@@ -91,28 +114,31 @@ class GlobalSessionContext:
91
 
92
  session_context = GlobalSessionContext()
93
 
94
- # Improved personas with natural, conversational prompts
95
- DEFAULT_PERSONAS = [
96
- Persona(
97
- id="methodist",
98
- name="Methodist Advisor",
99
- system_prompt="You are Dr. Methodist, a structured PhD advisor. Give brief, organized advice in 2-3 clear sentences. Focus on systematic approaches and planning.",
100
- llm=llm
101
- ),
102
- Persona(
103
- id="theorist",
104
- name="Theorist Advisor",
105
- system_prompt="You are Dr. Theorist, a philosophical PhD advisor. Give brief, thoughtful insights in 2-3 sentences. Focus on concepts, frameworks, and deeper understanding.",
106
- llm=llm
107
- ),
108
- Persona(
109
- id="pragmatist",
110
- name="Pragmatist Advisor",
111
- system_prompt="You are Dr. Pragmatist, a practical PhD advisor. Give brief, actionable advice in 2-3 sentences. Focus on concrete steps and real-world solutions.",
112
- llm=llm
113
- )
114
- ]
115
-
 
 
 
116
  for persona in DEFAULT_PERSONAS:
117
  chat_orchestrator.register_persona(persona)
118
 
@@ -134,6 +160,63 @@ class ReplyToAdvisor(BaseModel):
134
  advisor_id: str
135
  original_message_id: Optional[str] = None
136
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
137
  # Sequential advisor responses endpoint
138
  @router.post("/chat-sequential")
139
  async def chat_sequential(message: ChatMessage):
@@ -184,7 +267,7 @@ async def chat_sequential(message: ChatMessage):
184
  responses.append({
185
  "persona": chat_orchestrator.personas[persona_id].name,
186
  "persona_id": persona_id,
187
- "response": "I'm having trouble generating a response right now. Please try asking again.",
188
  "order": i
189
  })
190
 
@@ -204,6 +287,40 @@ async def chat_sequential(message: ChatMessage):
204
  }]
205
  }
206
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
207
  # Reply to specific advisor endpoint
208
  @router.post("/reply-to-advisor")
209
  async def reply_to_advisor(reply: ReplyToAdvisor):
@@ -242,40 +359,6 @@ async def reply_to_advisor(reply: ReplyToAdvisor):
242
  "response": "I'm having trouble generating a reply right now. Please try again."
243
  }
244
 
245
- # Main chat endpoint (keep for compatibility)
246
- @router.post("/chat")
247
- async def chat_with_orchestrator(message: ChatMessage):
248
- """Redirect to sequential endpoint for better UX"""
249
- return await chat_sequential(message)
250
-
251
- # Individual advisor endpoint with context
252
- @router.post("/chat/{persona_id}")
253
- async def chat_with_specific_advisor(persona_id: str, input: UserInput):
254
- """Chat with a specific advisor"""
255
- try:
256
- if persona_id not in chat_orchestrator.personas:
257
- raise HTTPException(status_code=404, detail=f"Persona '{persona_id}' not found")
258
-
259
- session_context.append("user", input.user_input)
260
- persona = chat_orchestrator.personas[persona_id]
261
- context = session_context.full_log.copy()
262
- reply = await persona.respond(context)
263
- session_context.append(persona_id, reply)
264
-
265
- return {
266
- "persona": persona.name,
267
- "persona_id": persona_id,
268
- "response": reply
269
- }
270
- except HTTPException:
271
- raise
272
- except Exception as e:
273
- print(f"Error in chat_with_specific_advisor: {e}")
274
- return {
275
- "persona": "System",
276
- "response": "I'm having trouble generating a response right now. Please try again."
277
- }
278
-
279
  # Reset session
280
  @router.post("/reset-session")
281
  async def reset_session():
@@ -292,26 +375,23 @@ async def reset_session():
292
  def get_context():
293
  return session_context.full_log
294
 
295
- # Model switching for development
296
  @router.post("/switch-model")
297
  async def switch_model(model_name: str = Body(...)):
298
- global llm
299
- try:
300
- llm = ShortResponseOllamaClient(model_name=model_name)
301
-
302
- # Update all personas with new LLM
303
- for persona_id in chat_orchestrator.personas:
304
- chat_orchestrator.personas[persona_id].llm = llm
305
-
306
- seamless_orchestrator.llm = llm
307
-
308
- return {"message": f"Switched to model: {model_name}"}
309
- except Exception as e:
310
- return {"error": f"Failed to switch model: {str(e)}"}
311
 
312
  @router.get("/current-model")
313
  async def get_current_model():
314
- return {"model": llm.model_name}
 
 
 
 
 
315
 
316
  # Debug endpoint
317
  @router.get("/debug/personas")
@@ -323,5 +403,6 @@ async def debug_personas():
323
  "prompt": persona.system_prompt[:100] + "..."
324
  } for pid, persona in chat_orchestrator.personas.items()
325
  },
326
- "context_length": len(session_context.full_log)
 
327
  }
 
1
+ import os
2
  from fastapi import APIRouter, Body, HTTPException
3
  import httpx
4
  from app.llm.llm_client import LLMClient
5
+ from app.llm.gemini_client import GeminiClient
6
  from app.models.persona import Persona
7
  from app.core.orchestrator import ChatOrchestrator
8
  from app.core.seamless_orchestrator import SeamlessOrchestrator
 
11
 
12
  router = APIRouter()
13
 
14
+ # Provider management
15
+ current_provider = "gemini"
16
+ available_providers = ["ollama", "gemini"]
17
+
18
+ def create_llm_client(provider: str = None) -> LLMClient:
19
+ """Create LLM client based on provider"""
20
+ if provider is None:
21
+ provider = current_provider
22
+
23
+ if provider == "gemini":
24
+ try:
25
+ return GeminiClient(model_name="gemini-1.5-flash")
26
+ except ValueError as e:
27
+ # Fallback to Ollama if Gemini API key is not available
28
+ print(f"Gemini API key not found, falling back to Ollama: {e}")
29
+ return ShortResponseOllamaClient(model_name="llama3.2:1b")
30
+ elif provider == "ollama":
31
+ return ShortResponseOllamaClient(model_name="llama3.2:1b")
32
+ else:
33
+ raise ValueError(f"Unknown provider: {provider}")
34
+
35
+ # Improved LLM client with better short response handling for Ollama
36
  class ShortResponseOllamaClient(LLMClient):
37
  def __init__(self, model_name: str = "llama3.2:1b"):
38
  self.model_name = model_name
 
67
  "temperature": 0.7,
68
  "top_p": 0.9,
69
  "top_k": 40,
70
+ "num_predict": 200,
71
  "repeat_penalty": 1.1,
72
  }
73
  }
 
93
  except Exception as e:
94
  return f"I apologize, but I'm having trouble generating a response right now. Please try again."
95
 
96
+ # Initialize with default provider
97
+ llm = create_llm_client()
98
  chat_orchestrator = ChatOrchestrator()
99
  seamless_orchestrator = SeamlessOrchestrator(llm=llm)
100
 
 
107
  self.full_log.append({"role": role, "content": content})
108
 
109
  def filter_by_persona(self, persona_id: str):
 
110
  return self.full_log
111
 
112
  def clear(self):
 
114
 
115
  session_context = GlobalSessionContext()
116
 
117
+ def create_default_personas(llm_client: LLMClient):
118
+ """Create default personas with given LLM client"""
119
+ return [
120
+ Persona(
121
+ id="methodist",
122
+ name="Methodist Advisor",
123
+ system_prompt="You are Dr. Methodist, a structured PhD advisor. Give brief, organized advice in 2-3 clear sentences. Focus on systematic approaches and planning.",
124
+ llm=llm_client
125
+ ),
126
+ Persona(
127
+ id="theorist",
128
+ name="Theorist Advisor",
129
+ system_prompt="You are Dr. Theorist, a philosophical PhD advisor. Give brief, thoughtful insights in 2-3 sentences. Focus on concepts, frameworks, and deeper understanding.",
130
+ llm=llm_client
131
+ ),
132
+ Persona(
133
+ id="pragmatist",
134
+ name="Pragmatist Advisor",
135
+ system_prompt="You are Dr. Pragmatist, a practical PhD advisor. Give brief, actionable advice in 2-3 sentences. Focus on concrete steps and real-world solutions.",
136
+ llm=llm_client
137
+ )
138
+ ]
139
+
140
+ # Initialize personas
141
+ DEFAULT_PERSONAS = create_default_personas(llm)
142
  for persona in DEFAULT_PERSONAS:
143
  chat_orchestrator.register_persona(persona)
144
 
 
160
  advisor_id: str
161
  original_message_id: Optional[str] = None
162
 
163
+ class ProviderSwitch(BaseModel):
164
+ provider: str
165
+
166
+ # Provider management endpoints
167
+ @router.get("/current-provider")
168
+ async def get_current_provider():
169
+ return {
170
+ "current_provider": current_provider,
171
+ "available_providers": available_providers,
172
+ "model_info": {
173
+ "name": llm.model_name if hasattr(llm, 'model_name') else "gemini-1.5-flash",
174
+ "provider": current_provider
175
+ }
176
+ }
177
+
178
+ @router.post("/switch-provider")
179
+ async def switch_provider(provider_data: ProviderSwitch):
180
+ global current_provider, llm
181
+
182
+ if provider_data.provider not in available_providers:
183
+ raise HTTPException(
184
+ status_code=400,
185
+ detail=f"Unknown provider: {provider_data.provider}. Available: {available_providers}"
186
+ )
187
+
188
+ try:
189
+ # Update current provider
190
+ current_provider = provider_data.provider
191
+
192
+ # Create new LLM client
193
+ new_llm = create_llm_client(current_provider)
194
+ llm = new_llm
195
+
196
+ # Update all personas with new LLM
197
+ new_personas = create_default_personas(new_llm)
198
+ chat_orchestrator.personas.clear()
199
+ for persona in new_personas:
200
+ chat_orchestrator.register_persona(persona)
201
+
202
+ # Update seamless orchestrator
203
+ seamless_orchestrator.llm = new_llm
204
+
205
+ return {
206
+ "message": f"Successfully switched to {current_provider}",
207
+ "current_provider": current_provider,
208
+ "model_info": {
209
+ "name": new_llm.model_name if hasattr(new_llm, 'model_name') else "gemini-1.5-flash",
210
+ "provider": current_provider
211
+ }
212
+ }
213
+
214
+ except Exception as e:
215
+ raise HTTPException(
216
+ status_code=500,
217
+ detail=f"Failed to switch to {provider_data.provider}: {str(e)}"
218
+ )
219
+
220
  # Sequential advisor responses endpoint
221
  @router.post("/chat-sequential")
222
  async def chat_sequential(message: ChatMessage):
 
267
  responses.append({
268
  "persona": chat_orchestrator.personas[persona_id].name,
269
  "persona_id": persona_id,
270
+ "response": "I'm having trouble generating a response right now. Please try again.",
271
  "order": i
272
  })
273
 
 
287
  }]
288
  }
289
 
290
+ # Main chat endpoint (keep for compatibility)
291
+ @router.post("/chat")
292
+ async def chat_with_orchestrator(message: ChatMessage):
293
+ """Redirect to sequential endpoint for better UX"""
294
+ return await chat_sequential(message)
295
+
296
+ # Individual advisor endpoint with context
297
+ @router.post("/chat/{persona_id}")
298
+ async def chat_with_specific_advisor(persona_id: str, input: UserInput):
299
+ """Chat with a specific advisor"""
300
+ try:
301
+ if persona_id not in chat_orchestrator.personas:
302
+ raise HTTPException(status_code=404, detail=f"Persona '{persona_id}' not found")
303
+
304
+ session_context.append("user", input.user_input)
305
+ persona = chat_orchestrator.personas[persona_id]
306
+ context = session_context.full_log.copy()
307
+ reply = await persona.respond(context)
308
+ session_context.append(persona_id, reply)
309
+
310
+ return {
311
+ "persona": persona.name,
312
+ "persona_id": persona_id,
313
+ "response": reply
314
+ }
315
+ except HTTPException:
316
+ raise
317
+ except Exception as e:
318
+ print(f"Error in chat_with_specific_advisor: {e}")
319
+ return {
320
+ "persona": "System",
321
+ "response": "I'm having trouble generating a response right now. Please try again."
322
+ }
323
+
324
  # Reply to specific advisor endpoint
325
  @router.post("/reply-to-advisor")
326
  async def reply_to_advisor(reply: ReplyToAdvisor):
 
359
  "response": "I'm having trouble generating a reply right now. Please try again."
360
  }
361
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
362
  # Reset session
363
  @router.post("/reset-session")
364
  async def reset_session():
 
375
  def get_context():
376
  return session_context.full_log
377
 
378
+ # Legacy model switching endpoint (now redirects to provider switching)
379
  @router.post("/switch-model")
380
  async def switch_model(model_name: str = Body(...)):
381
+ # For backward compatibility, try to map model names to providers
382
+ if "gemini" in model_name.lower():
383
+ return await switch_provider(ProviderSwitch(provider="gemini"))
384
+ else:
385
+ return await switch_provider(ProviderSwitch(provider="ollama"))
 
 
 
 
 
 
 
 
386
 
387
  @router.get("/current-model")
388
  async def get_current_model():
389
+ # For backward compatibility
390
+ model_name = llm.model_name if hasattr(llm, 'model_name') else "gemini-1.5-flash"
391
+ return {
392
+ "model": model_name,
393
+ "provider": current_provider
394
+ }
395
 
396
  # Debug endpoint
397
  @router.get("/debug/personas")
 
403
  "prompt": persona.system_prompt[:100] + "..."
404
  } for pid, persona in chat_orchestrator.personas.items()
405
  },
406
+ "context_length": len(session_context.full_log),
407
+ "current_provider": current_provider
408
  }