Spaces:
Running
Running
Merge branch 'main' of github.com:sohank-17/Neon-AI-Project
Browse files
multi_llm_chatbot_backend/app/api/routes.py
CHANGED
|
@@ -11,6 +11,9 @@ from pydantic import BaseModel
|
|
| 11 |
from typing import Optional, List
|
| 12 |
from fastapi import UploadFile, File
|
| 13 |
from app.utils.document_extractor import extract_text_from_file
|
|
|
|
|
|
|
|
|
|
| 14 |
from app.utils.file_limits import is_within_upload_limit
|
| 15 |
|
| 16 |
router = APIRouter()
|
|
@@ -531,6 +534,22 @@ async def debug_personas():
|
|
| 531 |
"current_provider": current_provider
|
| 532 |
}
|
| 533 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 534 |
@router.get("/uploaded-files")
|
| 535 |
def get_uploaded_filenames():
|
| 536 |
-
return {"files": session_context.uploaded_files}
|
|
|
|
| 11 |
from typing import Optional, List
|
| 12 |
from fastapi import UploadFile, File
|
| 13 |
from app.utils.document_extractor import extract_text_from_file
|
| 14 |
+
from app.core.orchestrator import answer_with_persona_context
|
| 15 |
+
from app.utils.chroma_client import add_persona_doc
|
| 16 |
+
import hashlib
|
| 17 |
from app.utils.file_limits import is_within_upload_limit
|
| 18 |
|
| 19 |
router = APIRouter()
|
|
|
|
| 534 |
"current_provider": current_provider
|
| 535 |
}
|
| 536 |
|
| 537 |
+
class PersonaQuery(BaseModel):
|
| 538 |
+
question: str
|
| 539 |
+
persona: str
|
| 540 |
+
|
| 541 |
+
@router.post("/ask/")
|
| 542 |
+
async def ask_question(query: PersonaQuery):
|
| 543 |
+
response = await answer_with_persona_context(query.question, query.persona)
|
| 544 |
+
|
| 545 |
+
# Store Q&A in vector DB
|
| 546 |
+
combined_text = f"Q: {query.question}\nA: {response}"
|
| 547 |
+
doc_id = hashlib.md5(combined_text.encode()).hexdigest() # Create a unique doc ID
|
| 548 |
+
|
| 549 |
+
add_persona_doc(combined_text, query.persona, doc_id)
|
| 550 |
+
|
| 551 |
+
return {"response": response}
|
| 552 |
+
|
| 553 |
@router.get("/uploaded-files")
|
| 554 |
def get_uploaded_filenames():
|
| 555 |
+
return {"files": session_context.uploaded_files}
|
multi_llm_chatbot_backend/app/core/orchestrator.py
CHANGED
|
@@ -1,5 +1,8 @@
|
|
| 1 |
from app.models.persona import Persona
|
| 2 |
from typing import List
|
|
|
|
|
|
|
|
|
|
| 3 |
|
| 4 |
class ChatOrchestrator:
|
| 5 |
def __init__(self):
|
|
@@ -25,3 +28,16 @@ class ChatOrchestrator:
|
|
| 25 |
context.append({"role": persona.id, "content": reply})
|
| 26 |
|
| 27 |
return responses
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
from app.models.persona import Persona
|
| 2 |
from typing import List
|
| 3 |
+
from app.utils.chroma_client import query_persona_knowledge
|
| 4 |
+
from app.llm.gemini_client import GeminiClient
|
| 5 |
+
from app.llm.llm_client import LLMClient
|
| 6 |
|
| 7 |
class ChatOrchestrator:
|
| 8 |
def __init__(self):
|
|
|
|
| 28 |
context.append({"role": persona.id, "content": reply})
|
| 29 |
|
| 30 |
return responses
|
| 31 |
+
|
| 32 |
+
async def answer_with_persona_context(question: str, persona: str) -> str:
|
| 33 |
+
llm: LLMClient = GeminiClient()
|
| 34 |
+
context_chunks = query_persona_knowledge(question, persona)
|
| 35 |
+
context = "\n".join(context_chunks) if context_chunks else "No relevant info found."
|
| 36 |
+
|
| 37 |
+
system_prompt = f"You are a helpful assistant responding as a {persona}."
|
| 38 |
+
message_context = [
|
| 39 |
+
{"role": "system", "content": system_prompt},
|
| 40 |
+
{"role": "user", "content": f"Context:\n{context}\n\nQuestion:\n{question}"}
|
| 41 |
+
]
|
| 42 |
+
|
| 43 |
+
return await llm.generate(system_prompt, message_context)
|
multi_llm_chatbot_backend/app/llm/embedding_client.py
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sentence_transformers import SentenceTransformer
|
| 2 |
+
import os
|
| 3 |
+
|
| 4 |
+
API_KEY = os.getenv("GEMINI_API_KEY")
|
| 5 |
+
|
| 6 |
+
# Using a compact, fast model good for semantic search
|
| 7 |
+
model = SentenceTransformer("all-MiniLM-L6-v2")
|
| 8 |
+
|
| 9 |
+
def get_embedding(text: str) -> list[float]:
|
| 10 |
+
embedding = model.encode(text, convert_to_numpy=True)
|
| 11 |
+
return embedding.tolist()
|
multi_llm_chatbot_backend/app/utils/chroma_client.py
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import chromadb
|
| 2 |
+
from chromadb.config import Settings
|
| 3 |
+
from app.llm.embedding_client import get_embedding
|
| 4 |
+
|
| 5 |
+
client = chromadb.PersistentClient(path="./chroma_storage")
|
| 6 |
+
|
| 7 |
+
collection = client.get_or_create_collection("persona_knowledge")
|
| 8 |
+
|
| 9 |
+
def add_persona_doc(text: str, persona: str, doc_id: str):
|
| 10 |
+
embedding = get_embedding(text)
|
| 11 |
+
collection.add(
|
| 12 |
+
documents=[text],
|
| 13 |
+
embeddings=[embedding],
|
| 14 |
+
metadatas=[{"persona": persona}],
|
| 15 |
+
ids=[doc_id]
|
| 16 |
+
)
|
| 17 |
+
|
| 18 |
+
def query_persona_knowledge(query: str, persona: str):
|
| 19 |
+
query_embedding = get_embedding(query)
|
| 20 |
+
results = collection.query(
|
| 21 |
+
query_embeddings=[query_embedding],
|
| 22 |
+
n_results=3,
|
| 23 |
+
where={"persona": persona}
|
| 24 |
+
)
|
| 25 |
+
return results['documents'][0] if results['documents'] else []
|