mirror of
http://88.130.71.182:3000/BlitTech/contexta_be.git
synced 2026-06-12 23:23:21 +00:00
fixed bugs
This commit is contained in:
@@ -21,9 +21,9 @@ def _get_public_chatbot(chatbot_id: str, supabase) -> dict:
|
|||||||
|
|
||||||
@router.post("/chat/{chatbot_id}", response_model=ChatResponse)
|
@router.post("/chat/{chatbot_id}", response_model=ChatResponse)
|
||||||
async def chat(
|
async def chat(
|
||||||
chatbot_id: str,
|
chatbot_id: str,
|
||||||
message: ChatMessage,
|
message: ChatMessage,
|
||||||
user=Depends(get_optional_user),
|
user=Depends(get_optional_user),
|
||||||
):
|
):
|
||||||
supabase = get_supabase()
|
supabase = get_supabase()
|
||||||
chatbot = _get_public_chatbot(chatbot_id, supabase)
|
chatbot = _get_public_chatbot(chatbot_id, supabase)
|
||||||
@@ -97,9 +97,9 @@ async def chat(
|
|||||||
|
|
||||||
@router.get("/chat/{chatbot_id}/history/{session_id}", response_model=List[MessageResponse])
|
@router.get("/chat/{chatbot_id}/history/{session_id}", response_model=List[MessageResponse])
|
||||||
async def get_chat_history(
|
async def get_chat_history(
|
||||||
chatbot_id: str,
|
chatbot_id: str,
|
||||||
session_id: str,
|
session_id: str,
|
||||||
user=Depends(get_optional_user),
|
user=Depends(get_optional_user),
|
||||||
):
|
):
|
||||||
supabase = get_supabase()
|
supabase = get_supabase()
|
||||||
|
|
||||||
@@ -114,7 +114,7 @@ async def get_chat_history(
|
|||||||
conv_id = conversation.data[0]["id"]
|
conv_id = conversation.data[0]["id"]
|
||||||
messages = supabase.table("messages").select("*") \
|
messages = supabase.table("messages").select("*") \
|
||||||
.eq("conversation_id", conv_id) \
|
.eq("conversation_id", conv_id) \
|
||||||
.order("created_at", asc=True) \
|
.order("created_at", desc=False) \
|
||||||
.execute()
|
.execute()
|
||||||
|
|
||||||
return [
|
return [
|
||||||
@@ -140,7 +140,8 @@ async def get_analytics(chatbot_id: str, user=Depends(get_current_user)):
|
|||||||
if not company.data:
|
if not company.data:
|
||||||
raise HTTPException(status_code=404, detail="Company not found")
|
raise HTTPException(status_code=404, detail="Company not found")
|
||||||
|
|
||||||
chatbot = supabase.table("chatbots").select("id").eq("id", chatbot_id).eq("company_id", company.data[0]["id"]).execute()
|
chatbot = supabase.table("chatbots").select("id").eq("id", chatbot_id).eq("company_id",
|
||||||
|
company.data[0]["id"]).execute()
|
||||||
if not chatbot.data:
|
if not chatbot.data:
|
||||||
raise HTTPException(status_code=404, detail="Chatbot not found")
|
raise HTTPException(status_code=404, detail="Chatbot not found")
|
||||||
|
|
||||||
@@ -159,11 +160,11 @@ async def get_analytics(chatbot_id: str, user=Depends(get_current_user)):
|
|||||||
# ── Helpers ───────────────────────────────────────────────────────────────────
|
# ── Helpers ───────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
def _get_or_create_conversation(
|
def _get_or_create_conversation(
|
||||||
chatbot_id: str,
|
chatbot_id: str,
|
||||||
session_id: str,
|
session_id: str,
|
||||||
user_id: Optional[str],
|
user_id: Optional[str],
|
||||||
language: str,
|
language: str,
|
||||||
supabase,
|
supabase,
|
||||||
) -> dict:
|
) -> dict:
|
||||||
existing = supabase.table("conversations").select("*") \
|
existing = supabase.table("conversations").select("*") \
|
||||||
.eq("chatbot_id", chatbot_id) \
|
.eq("chatbot_id", chatbot_id) \
|
||||||
@@ -186,21 +187,29 @@ def _get_or_create_conversation(
|
|||||||
|
|
||||||
|
|
||||||
def _get_conversation_history(conversation_id: str, supabase) -> List[dict]:
|
def _get_conversation_history(conversation_id: str, supabase) -> List[dict]:
|
||||||
|
"""
|
||||||
|
FIX: Changed from desc=True to desc=False (ascending/chronological order).
|
||||||
|
|
||||||
|
The conversation history MUST be in chronological order (oldest first)
|
||||||
|
for the LLM to correctly understand the conversation flow.
|
||||||
|
Previously, messages were returned newest-first, which reversed the
|
||||||
|
conversation and confused the model.
|
||||||
|
"""
|
||||||
messages = supabase.table("messages").select("role, content") \
|
messages = supabase.table("messages").select("role, content") \
|
||||||
.eq("conversation_id", conversation_id) \
|
.eq("conversation_id", conversation_id) \
|
||||||
.order("created_at", desc=True) \
|
.order("created_at", desc=False) \
|
||||||
.limit(20) \
|
.limit(20) \
|
||||||
.execute()
|
.execute()
|
||||||
return messages.data or []
|
return messages.data or []
|
||||||
|
|
||||||
|
|
||||||
def _save_message(
|
def _save_message(
|
||||||
conversation_id: str,
|
conversation_id: str,
|
||||||
role: str,
|
role: str,
|
||||||
content: str,
|
content: str,
|
||||||
supabase,
|
supabase,
|
||||||
sources: Optional[list] = None,
|
sources: Optional[list] = None,
|
||||||
model: str = "",
|
model: str = "",
|
||||||
):
|
):
|
||||||
supabase.table("messages").insert({
|
supabase.table("messages").insert({
|
||||||
"id": str(uuid.uuid4()),
|
"id": str(uuid.uuid4()),
|
||||||
@@ -209,4 +218,4 @@ def _save_message(
|
|||||||
"content": content,
|
"content": content,
|
||||||
"sources": sources,
|
"sources": sources,
|
||||||
"model": model,
|
"model": model,
|
||||||
}).execute()
|
}).execute()
|
||||||
@@ -11,11 +11,11 @@ RAG_SYSTEM_PROMPT = """You are a helpful AI assistant for {company_name}.
|
|||||||
Your role is to answer questions based on the provided context from company documents.
|
Your role is to answer questions based on the provided context from company documents.
|
||||||
|
|
||||||
IMPORTANT RULES:
|
IMPORTANT RULES:
|
||||||
1. Only answer based on the provided context
|
1. Answer based on the provided context below
|
||||||
2. If information is not in the context, say "I don't have information about that in my knowledge base"
|
2. If the context does not contain enough information, say so, but also try to be helpful with what IS available
|
||||||
3. Be concise and helpful
|
3. Be concise and helpful
|
||||||
4. Always maintain a professional, friendly tone
|
4. Always maintain a professional, friendly tone
|
||||||
5. If asked about topics outside the context, politely redirect to relevant topics
|
5. If asked about topics completely outside the context, politely redirect to relevant topics
|
||||||
|
|
||||||
{custom_instructions}
|
{custom_instructions}
|
||||||
|
|
||||||
@@ -47,8 +47,9 @@ class RAGEngine:
|
|||||||
# Step 1: Embed the query
|
# Step 1: Embed the query
|
||||||
try:
|
try:
|
||||||
query_embedding = self.embedding_svc.embed_text(query)
|
query_embedding = self.embedding_svc.embed_text(query)
|
||||||
|
logger.info(f"[RAG] Query embedded successfully. Vector length: {len(query_embedding)}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Embedding error: {e}")
|
logger.error(f"[RAG] Embedding error: {e}", exc_info=True)
|
||||||
return {
|
return {
|
||||||
"response": "I'm having trouble processing your request. Please try again.",
|
"response": "I'm having trouble processing your request. Please try again.",
|
||||||
"sources": [],
|
"sources": [],
|
||||||
@@ -57,13 +58,22 @@ class RAGEngine:
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Step 2: Retrieve relevant chunks
|
# Step 2: Retrieve relevant chunks
|
||||||
|
# FIX: Lowered score_threshold from 0.3 to 0.1 to avoid filtering out
|
||||||
|
# all results. With cosine similarity, 0.3 can be too aggressive for
|
||||||
|
# many document types and query patterns.
|
||||||
retrieved = self.vector_svc.search(
|
retrieved = self.vector_svc.search(
|
||||||
collection_name=collection_name,
|
collection_name=collection_name,
|
||||||
query_vector=query_embedding,
|
query_vector=query_embedding,
|
||||||
limit=5,
|
limit=5,
|
||||||
score_threshold=0.3,
|
score_threshold=0.1, # FIX: was 0.3, now 0.1 to avoid over-filtering
|
||||||
)
|
)
|
||||||
|
|
||||||
|
logger.info(f"[RAG] Retrieved {len(retrieved)} chunks from collection '{collection_name}'")
|
||||||
|
for i, item in enumerate(retrieved):
|
||||||
|
score = item.get("score", 0)
|
||||||
|
text_preview = item.get("payload", {}).get("text", "")[:80]
|
||||||
|
logger.info(f"[RAG] Chunk {i+1}: score={score:.4f}, preview='{text_preview}...'")
|
||||||
|
|
||||||
# Step 3: Build sources
|
# Step 3: Build sources
|
||||||
sources = []
|
sources = []
|
||||||
context_parts = []
|
context_parts = []
|
||||||
@@ -84,7 +94,12 @@ class RAGEngine:
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
context = "\n\n---\n\n".join(context_parts) if context_parts else "No relevant information found."
|
if context_parts:
|
||||||
|
context = "\n\n---\n\n".join(context_parts)
|
||||||
|
logger.info(f"[RAG] Built context from {len(context_parts)} chunks ({len(context)} chars)")
|
||||||
|
else:
|
||||||
|
context = "No relevant information found in the knowledge base."
|
||||||
|
logger.warning(f"[RAG] No context found for query: '{query}' in collection '{collection_name}'")
|
||||||
|
|
||||||
# Step 4: Build messages
|
# Step 4: Build messages
|
||||||
system_prompt = RAG_SYSTEM_PROMPT.format(
|
system_prompt = RAG_SYSTEM_PROMPT.format(
|
||||||
@@ -95,13 +110,18 @@ class RAGEngine:
|
|||||||
|
|
||||||
messages = [{"role": "system", "content": system_prompt}]
|
messages = [{"role": "system", "content": system_prompt}]
|
||||||
|
|
||||||
# Add conversation history (last 10 messages)
|
# FIX: Conversation history must be in CHRONOLOGICAL order (oldest first).
|
||||||
for msg in conversation_history[-10:]:
|
# The history should already come sorted ascending from the chat router.
|
||||||
|
# We take the last 10 messages for context window management.
|
||||||
|
history_to_use = conversation_history[-10:] if conversation_history else []
|
||||||
|
for msg in history_to_use:
|
||||||
messages.append({"role": msg["role"], "content": msg["content"]})
|
messages.append({"role": msg["role"], "content": msg["content"]})
|
||||||
|
|
||||||
# Add current query
|
# Add current query
|
||||||
messages.append({"role": "user", "content": query})
|
messages.append({"role": "user", "content": query})
|
||||||
|
|
||||||
|
logger.info(f"[RAG] Sending {len(messages)} messages to LLM (model: {chatbot_config.get('model')})")
|
||||||
|
|
||||||
# Step 5: Generate response
|
# Step 5: Generate response
|
||||||
model = chatbot_config.get("model", "accounts/fireworks/models/kimi-k2-instruct-0905")
|
model = chatbot_config.get("model", "accounts/fireworks/models/kimi-k2-instruct-0905")
|
||||||
try:
|
try:
|
||||||
@@ -111,6 +131,7 @@ class RAGEngine:
|
|||||||
max_tokens=chatbot_config.get("max_tokens", 1000),
|
max_tokens=chatbot_config.get("max_tokens", 1000),
|
||||||
temperature=chatbot_config.get("temperature", 0.7),
|
temperature=chatbot_config.get("temperature", 0.7),
|
||||||
)
|
)
|
||||||
|
logger.info(f"[RAG] LLM response generated. Tokens used: {result.get('tokens_used', 0)}")
|
||||||
return {
|
return {
|
||||||
"response": result["content"],
|
"response": result["content"],
|
||||||
"sources": sources,
|
"sources": sources,
|
||||||
@@ -118,7 +139,7 @@ class RAGEngine:
|
|||||||
"model": result.get("model", model),
|
"model": result.get("model", model),
|
||||||
}
|
}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"LLM generation error: {e}")
|
logger.error(f"[RAG] LLM generation error: {e}", exc_info=True)
|
||||||
return {
|
return {
|
||||||
"response": "I'm having trouble generating a response. Please try again later.",
|
"response": "I'm having trouble generating a response. Please try again later.",
|
||||||
"sources": sources,
|
"sources": sources,
|
||||||
@@ -127,4 +148,4 @@ class RAGEngine:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
rag_engine = RAGEngine()
|
rag_engine = RAGEngine()
|
||||||
@@ -10,6 +10,7 @@ dependencies = [
|
|||||||
"qdrant-client>=1.17.0",
|
"qdrant-client>=1.17.0",
|
||||||
"sentry-sdk>=2.53.0",
|
"sentry-sdk>=2.53.0",
|
||||||
"spglib>=2.7.0",
|
"spglib>=2.7.0",
|
||||||
|
"stripe>=14.3.0",
|
||||||
"supabase>=2.28.0",
|
"supabase>=2.28.0",
|
||||||
"uvicorn>=0.41.0",
|
"uvicorn>=0.41.0",
|
||||||
]
|
]
|
||||||
|
|||||||
15
uv.lock
generated
15
uv.lock
generated
@@ -202,6 +202,7 @@ dependencies = [
|
|||||||
{ name = "qdrant-client" },
|
{ name = "qdrant-client" },
|
||||||
{ name = "sentry-sdk" },
|
{ name = "sentry-sdk" },
|
||||||
{ name = "spglib" },
|
{ name = "spglib" },
|
||||||
|
{ name = "stripe" },
|
||||||
{ name = "supabase" },
|
{ name = "supabase" },
|
||||||
{ name = "uvicorn" },
|
{ name = "uvicorn" },
|
||||||
]
|
]
|
||||||
@@ -214,6 +215,7 @@ requires-dist = [
|
|||||||
{ name = "qdrant-client", specifier = ">=1.17.0" },
|
{ name = "qdrant-client", specifier = ">=1.17.0" },
|
||||||
{ name = "sentry-sdk", specifier = ">=2.53.0" },
|
{ name = "sentry-sdk", specifier = ">=2.53.0" },
|
||||||
{ name = "spglib", specifier = ">=2.7.0" },
|
{ name = "spglib", specifier = ">=2.7.0" },
|
||||||
|
{ name = "stripe", specifier = ">=14.3.0" },
|
||||||
{ name = "supabase", specifier = ">=2.28.0" },
|
{ name = "supabase", specifier = ">=2.28.0" },
|
||||||
{ name = "uvicorn", specifier = ">=0.41.0" },
|
{ name = "uvicorn", specifier = ">=0.41.0" },
|
||||||
]
|
]
|
||||||
@@ -1448,6 +1450,19 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/96/7c/a81ef5ef10978dd073a854e0fa93b5d8021d0594b639cc8f6453c3c78a1d/strictyaml-1.7.3-py3-none-any.whl", hash = "sha256:fb5c8a4edb43bebb765959e420f9b3978d7f1af88c80606c03fb420888f5d1c7", size = 123917, upload-time = "2023-03-10T12:50:17.242Z" },
|
{ url = "https://files.pythonhosted.org/packages/96/7c/a81ef5ef10978dd073a854e0fa93b5d8021d0594b639cc8f6453c3c78a1d/strictyaml-1.7.3-py3-none-any.whl", hash = "sha256:fb5c8a4edb43bebb765959e420f9b3978d7f1af88c80606c03fb420888f5d1c7", size = 123917, upload-time = "2023-03-10T12:50:17.242Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "stripe"
|
||||||
|
version = "14.3.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "requests" },
|
||||||
|
{ name = "typing-extensions" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/49/67/8a38222a57fc2ba359c4dcb66528d94c00d803c7fde8f8d8470ad6bdccbb/stripe-14.3.0.tar.gz", hash = "sha256:4c76137d741bd43e8bb433a596c198ca20f4cdf17a8fe04604faf37c74b01978", size = 1463618, upload-time = "2026-01-28T21:20:29.856Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/9e/4b/0b7d5920f2be5e42d72bdfc44a9fae57b422668bfc8dacdf2f74886f6daa/stripe-14.3.0-py3-none-any.whl", hash = "sha256:3e36b68b256c8970e99b703e195d947e2a2919095758788c7074ac4485ac255e", size = 2106980, upload-time = "2026-01-28T21:20:27.566Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "supabase"
|
name = "supabase"
|
||||||
version = "2.28.0"
|
version = "2.28.0"
|
||||||
|
|||||||
Reference in New Issue
Block a user