-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathapp.py
More file actions
215 lines (177 loc) · 11.2 KB
/
app.py
File metadata and controls
215 lines (177 loc) · 11.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
from flask import Flask, request, jsonify, render_template
import requests
import time
import random
from openai import OpenAI
#from rag_core import retrieve_documents, FAQ_DATA, RAG_ENABLED
from chroma_core import retrieve_documents, FAQ_DATA, RAG_ENABLED
from utils import detect_category
from config import GITHUB_TOKEN, GITHUB_ENDPOINT, GITHUB_MODEL
app = Flask(__name__)
# --- Topic Filter Options ---
TOPICS = ['শিক্ষা', 'স্বাস্থ্য', 'ভ্রমণ', 'প্রযুক্তি', 'খেলাধুলা', 'সব']
# --- 1. Core Utility Functions ---
def call_github_model_api(prompt, system_instruction_text, max_retries=5, delay=1):
# Validate GitHub token
if not GITHUB_TOKEN or GITHUB_TOKEN == "your-github-token-here":
print("--- [ERROR] GitHub token not configured properly ---")
return {'type': 'fallback', 'text': 'দুঃখিত! এআই ইঞ্জিন উত্তর তৈরি করতে পারেনি। অনুগ্রহ করে অন্যভাবে প্রশ্নটি করুন।'}
# Initialize OpenAI client
client = OpenAI(
base_url=GITHUB_ENDPOINT,
api_key=GITHUB_TOKEN
)
for i in range(max_retries):
try:
print(f"--- [GitHub Model] Attempt {i+1}/{max_retries} - Calling {GITHUB_ENDPOINT} ---")
response = client.chat.completions.create(
model=GITHUB_MODEL,
messages=[
{"role": "system", "content": system_instruction_text},
{"role": "user", "content": prompt}
],
max_tokens=1000,
temperature=0.7
)
if response.choices and len(response.choices) > 0:
content = response.choices[0].message.content
print(f"--- [GitHub Model] Success! Response length: {len(content)} ---") # type: ignore
return {'type': 'success', 'text': content}
else:
print(f"--- [GitHub Model] No choices in response ---")
return {'type': 'fallback', 'text': 'দুঃখিত! এআই ইঞ্জিন উত্তর তৈরি করতে পারেনি। অনুগ্রহ করে অন্যভাবে প্রশ্নটি করুন।'}
except Exception as e:
print(f"--- [GitHub Model Error] {e} ---")
# Check if it's a rate limit error
if "rate limit" in str(e).lower() or "429" in str(e):
print(f"--- [GitHub Model Rate Limit] Retrying in {delay}s... ---")
if i < max_retries - 1:
time.sleep(delay)
delay *= 2
continue
else:
return {'type': 'fallback', 'text': 'দুঃখিত! এআই ইঞ্জিন উত্তর তৈরি করতে পারেনি। অনুগ্রহ করে অন্যভাবে প্রশ্নটি করুন।'}
# For other errors, retry with exponential backoff
if i < max_retries - 1:
print(f"--- [GitHub Model] Retrying in {delay}s... ---")
time.sleep(delay)
delay *= 2
else:
return {'type': 'fallback', 'text': 'দুঃখিত! এআই ইঞ্জিন উত্তর তৈরি করতে পারেনি। অনুগ্রহ করে অন্যভাবে প্রশ্নটি করুন।'}
return {'type': 'fallback', 'text': 'দুঃখিত! এআই ইঞ্জিন উত্তর তৈরি করতে পারেনি। অনুগ্রহ করে অন্যভাবে প্রশ্নটি করুন।'}
# --- 2. RAG Logic Implementation ---
def get_rag_response(query, topic='সব'):
print(f"--- [RAG] Filtering by Topic: {topic} ---")
print(f"--- [RAG] RAG_ENABLED: {RAG_ENABLED} ---")
if not RAG_ENABLED:
print("--- [RAG ERROR] RAG system not enabled, falling back to LLM ---")
return {'type': 'fallback', 'text': 'দুঃখিত! ভেক্টর ডেটাবেস লোড হতে পারেনি। RAG নিষ্ক্রিয়।', 'top_docs': []}
# 1. Retrieval using FAISS with Metadata Filtering
print(f"--- [RAG] Retrieval using FAISS with Metadata Filtering ---")
print(f"--- [RAG] Query: '{query}' | Topic: '{topic}' ---")
top_docs = retrieve_documents(query, topic, k=3)
print(f"--- [RAG] Retrieved {len(top_docs)} documents ---")
# Display retrieved documents for debugging
for i, doc in enumerate(top_docs):
print(f"--- [RAG] Document {i+1}: {doc.page_content[:100]}... ---")
print(f"--- [RAG] Metadata: {doc.metadata} ---")
if not top_docs:
all_questions = [
f"({f['question']}"
for f in FAQ_DATA
if (topic == 'সব' or f['topic'] == topic)
]
system_prompt = "আপনি একজন বাংলা ভাষাভাষী সহযোগী চ্যাটবট। যদি কোনো প্রশ্নের সরাসরি উত্তর না পান, তাহলে আপনাকে অবশ্যই ব্যবহারকারীকে জানাতে হবে যে আপনি উত্তর খুঁজে পাননি এবং তারপর একটি সহায়ক বাংলা ফলব্যাক বার্তা দিতে হবে। বাংলাতেই উত্তর দিন। আপনাকে অবশ্যই নিম্নলিখিত প্রশ্নের একটি তালিকা দিয়ে ফলব্যাক বার্তাটি শেষ করতে হবে। শুধুমাত্র বাংলাতেই উত্তর দিন।"
fallback_prompt = (
f"ব্যবহারকারী একটি প্রশ্ন করেছেন কিন্তু আমি আমার FAQ ডেটাসেটে কোনো সরাসরি উত্তর পাইনি। ব্যবহারকারীর প্রশ্ন: \"{query}\"। "
f"আপনি কি আমার FAQ-এর {topic} টপিকের নিম্নলিখিত প্রশ্নগুলির উপর ভিত্তি করে কোনো সহায়ক বা প্রাসঙ্গিক উত্তর দিতে পারেন? "
f"যদি না পারেন, তবে স্পষ্ট করে বলুন: \"দুঃখিত, আমি সরাসরি উত্তর খুঁজে পাইনি। আপনি নিম্নলিখিত প্রশ্নগুলি থেকে বেছে নিতে পারেন:\" \n"
f"তালিকা: {'; '.join(all_questions)}"
)
result = call_github_model_api(fallback_prompt, system_prompt)
result['top_docs'] = [] # type: ignore
return result
else:
# 2. Augmented Generation (RAG)
context = '\n---\n'.join([
f"ডকুমেন্ট কনটেক্সট: {doc.page_content}"
for doc in top_docs
])
system_prompt = "আপনি একজন বিশেষজ্ঞ বাংলা FAQ চ্যাটবট। আপনার কাজ হল প্রদত্ত কনটেক্সট (FAQ প্রশ্ন-উত্তর) ব্যবহার করে ব্যবহারকারীর প্রশ্নের সবচেয়ে উপযুক্ত ও সঠিক উত্তর বাংলায় দেওয়া। আপনাকে শুধু কনটেক্সটের উপর ভিত্তি করে উত্তর দিতে হবে, অন্য কোনো তথ্য যোগ করা যাবে না। বাংলাতেই উত্তর দিন।"
rag_prompt = f"প্রদত্ত কনটেক্সট: \n\n{context}\n\n--- \n\nব্যবহারকারীর প্রশ্ন: {query}"
result = call_github_model_api(rag_prompt, system_prompt)
result['top_docs'] = top_docs # type: ignore
return result
# --- 3. Flask Routes ---
@app.route('/')
def index():
"""Renders the main chat interface."""
return render_template(
'index.html',
topics=TOPICS
)
@app.route('/random-questions', methods=['GET'])
def get_random_questions():
"""Returns 4 random questions from FAQ_DATA."""
try:
# Get all questions from FAQ_DATA
all_questions = [faq for faq in FAQ_DATA]
# Shuffle and take 4 random questions
random_questions = random.sample(all_questions, min(4, len(all_questions)))
return jsonify({
'status': 'success',
'questions': random_questions
})
except Exception as e:
return jsonify({
'status': 'error',
'message': f'Error fetching random questions: {str(e)}'
}), 500
@app.route('/filter', methods=['POST'])
def update_filter():
"""Updates the topic filter."""
data = request.json
if 'topic' in data and data['topic'] in TOPICS: # type: ignore
return jsonify({
'status': 'success',
'topic': data['topic'] # type: ignore
})
else:
return jsonify({
'status': 'error',
'message': 'Invalid topic'
}), 400
@app.route('/chat', methods=['POST'])
def chat():
"""Handles the user query and returns the RAG response."""
data = request.json
query = data.get('query', '').strip() # type: ignore
if not query:
return jsonify({'response': 'অনুগ্রহ করে একটি প্রশ্ন লিখুন।'})
try:
# Detect topic automatically using utils.py
detected_topic = detect_category(query)
# Get response using RAG logic
rag_result = get_rag_response(query, detected_topic)
response_text = rag_result['text']
# Audio generation logic removed
# Get retrieved documents data
retrieved_docs = []
if RAG_ENABLED and 'top_docs' in rag_result:
for i, doc in enumerate(rag_result['top_docs']):
retrieved_docs.append({
'content': doc.page_content, # type: ignore
'topic': doc.metadata.get('topic', 'Unknown'), # type: ignore
'index': i + 1
})
return jsonify({
'response': response_text,
'rag_status': 'active' if RAG_ENABLED else 'inactive',
'topic': detected_topic,
'retrieved_docs': retrieved_docs # Show top k most similar documents
})
except Exception as e:
print(f"Chat processing error: {e}")
return jsonify({'response': f'একটি অভ্যন্তরীণ ত্রুটি ঘটেছে: {e}'}), 500
if __name__ == '__main__':
app.run(debug=True)