Spaces:
Sleeping
Sleeping
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 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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,
|
| 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 |
-
|
|
|
|
| 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 |
-
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
| 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
|
| 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 |
-
#
|
| 296 |
@router.post("/switch-model")
|
| 297 |
async def switch_model(model_name: str = Body(...)):
|
| 298 |
-
|
| 299 |
-
|
| 300 |
-
|
| 301 |
-
|
| 302 |
-
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
}
|