-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathapp.py
359 lines (293 loc) · 14.8 KB
/
app.py
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
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
import re
import os
import json
import random, math
from openai import OpenAI
from dotenv import load_dotenv
import streamlit as st
from streamlit_chat import message
from streamlit_extras.colored_header import colored_header
from langchain_community.vectorstores import Chroma
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain.prompts import ChatPromptTemplate
from datetime import datetime
import webbrowser
from typing import List, Dict
from PIL import Image
load_dotenv()
client = OpenAI(api_key=os.environ['OPENAI_API_KEY'])
# Log folder and path
LOG_DIR = "conversation_logs"
if not os.path.exists(LOG_DIR):
os.makedirs(LOG_DIR)
OPENAI_API_KEY = os.environ['OPENAI_API_KEY']
CHROMA_PATH = "chroma"
PROMPT_TEMPLATE = """
1. Sana kullanıcının sorusunu ve ilgili metin alıntılarını sağlayacağım.
2. Görevin, yalnızca sağlanan metin alıntılarını kullanarak Türk Hava Yolları adına cevap vermektir.
3. Yanıtı oluştururken şu kurallara dikkat et:
- Sağlanan metin alıntısında açıkça yer alan bilgileri kullan.
- Metin alıntısında açıkça bulunmayan cevapları tahmin etmeye veya uydurmaya çalışma.
- Eğer kullanıcı bir terim hakkında soru soruyorsa ve bu terim sözlükte bulunuyorsa, sözlük tanımını kullan.
4. Sohbet oturumu ile ilgili genel sorular (sohbeti özetle, soruları listele gibi) için sağlanan metin alıntısını kullanma. Bu tür sorulara doğrudan cevap ver.
5. Yanıtı, Türkçe dilinde ve anlaşılır bir şekilde ver.
6. Kullanıcıya her zaman yardımcı olmaya çalış, ancak mevcut bilgilere dayanmayan yanıtlardan kaçın.
7. Eğer "Sen kimsin" diye bir soru gelirse "Türk Hava Yolları'nın özel seyahat danışmanı Vecihi'yim. Görevim, yolculara seyahat planlamaları konusunda yardımcı olmak ve onlara Türk Hava Yolları'nın hizmetlerini tanıtmaktır. Eğer seyahat planı yaparken yardıma ihtiyacınız varsa, ben buradayım!" diye cevap ver.
Eğer hazırsan, sana kullanıcının sorusunu ve ilgili metin alıntısını sağlıyorum.
{context}
Kullanıcı Sorusu: {question}
Yanıt:
"""
TRAVEL_AGENT_PROMPT = """
Sen Türk Hava Yolları'nın özel seyahat danışmanı Vecihi'sin. Görevin, yolculara seyahat planlamaları konusunda yardımcı olmak ve onlara Türk Hava Yolları'nın hizmetlerini tanıtmaktır. Kullanıcının sorduğu şehir veya ülke hakkında aşağıdaki bilgileri sağlamalısın:
1. Şehir/ülke hakkında kısa bir genel bilgi
2. En popüler 3 gezilecek yer ve kısa açıklamaları
3. Ortalama konaklama fiyatları (bütçe dostu, orta segment ve lüks seçenekler için)
4. En iyi ziyaret zamanı
5. Ulaşım tavsiyeleri (mümkünse Türk Hava Yolları'nın o destinasyona olan uçuşlarını vurgula)
6. Türk Hava Yolları'nın o destinasyona özel bir hizmeti veya kampanyası varsa bundan bahset
Lütfen bu bilgileri Türkçe olarak, anlaşılır ve özet bir şekilde sağla. Cevabına "Tabii ki Size [şehir/ülke] hakkında bilgi vermekten mutluluk duyarım." diye başla. Eğer bir bilgiye sahip değilsen, o kısmı atla. Cevabını verirken nazik ve yardımsever ol, ama fazla resmi olmamaya çalış.
Kullanıcının sorusu: {question}
Yanıt:
"""
st.set_page_config(page_title="Vecihi", page_icon="images/logo.jpg")
# Custom CSS for the background and message colors
st.markdown(
"""
<style>
.stApp {
background-color: white;
}
.user-message {
background-color: #EEEEEE;
border-radius: 8px;
padding: 10px;
margin: 5px 0;
margin-left: auto;
color: black;
width: fit-content;
max-width: 80%;
}
.bot-message {
background-color: #DC1410;
border-radius: 8px;
padding: 10px;
margin: 5px 0;
color: white;
width: fit-content;
max-width: 80%;
}
.bot-message img {
position: absolute;
bottom: 10px;
left: -60px;
width: 50px;
}
.matching-popup {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: white;
padding: 20px;
border-radius: 10px;
box-shadow: 0 0 10px rgba(0,0,0,0.1);
z-index: 1000;
max-width: 80%;
width: 400px;
}
.matching-popup h2 {
color: #DC1410;
margin-bottom: 20px;
}
.matching-popup button {
margin-top: 20px;
}
</style>
""",
unsafe_allow_html=True
)
# Initialize session state variables
if 'user_responses' not in st.session_state:
st.session_state['user_responses'] = ["Merhaba"]
if 'bot_responses' not in st.session_state:
st.session_state['bot_responses'] = ["Merhaba ben Vecihi, size nasıl yardımcı olabilirim?"]
if 'session_id' not in st.session_state:
st.session_state.session_id = datetime.now().strftime("%Y%m%d_%H%M%S")
if 'show_matching' not in st.session_state:
st.session_state.show_matching = False
if 'matching_answers' not in st.session_state:
st.session_state.matching_answers = {}
MATCHING_QUESTIONS = [
"Yaş aralığınız nedir? (18-25, 26-40, 41-60, 60+)",
"Yolculuk sırasında tercih ettiğiniz aktivite nedir? (Uyumak, Sohbet etmek, Kitap okumak, Film izlemek)",
"Hangi dilleri konuşuyorsunuz? (Türkçe, İngilizce, Almanca, vb.)",
"Seyahat amacınız nedir? (İş, Tatil, Eğitim, Diğer)",
"Yemek tercihiniz nedir? (Vejeteryan, Vegan, Her şey, Özel diyet)"
]
class Passenger:
def __init__(self, name: str, answers: Dict[str, str]):
self.name = name
self.answers = answers
self.seat = None
self.vector = self._create_vector()
def _create_vector(self):
return [hash(self.answers[q]) for q in MATCHING_QUESTIONS]
def cosine_similarity(v1: List[int], v2: List[int]) -> float:
dot_product = sum(a * b for a, b in zip(v1, v2))
magnitude1 = math.sqrt(sum(a * a for a in v1))
magnitude2 = math.sqrt(sum(b * b for b in v2))
return dot_product / (magnitude1 * magnitude2)
def match_passengers(passengers: List[Passenger]) -> List[Passenger]:
unmatched = passengers.copy()
random.shuffle(unmatched)
while len(unmatched) > 1:
p1 = unmatched.pop(0)
best_match = max(unmatched, key=lambda p2: cosine_similarity(p1.vector, p2.vector))
unmatched.remove(best_match)
p1.seat = f"{len(passengers) - len(unmatched)}A"
best_match.seat = f"{len(passengers) - len(unmatched)}B"
if unmatched:
unmatched[0].seat = f"{len(passengers)}C"
return passengers
def run_matching_system(name: str, answers: Dict[str, str]) -> List[Dict[str, str]]:
other_passengers = [
Passenger("Ali", {q: random.choice(["18-25", "Uyumak", "Türkçe", "Tatil", "Her şey"]) for q in MATCHING_QUESTIONS}),
Passenger("Ayşe", {q: random.choice(["26-40", "Sohbet etmek", "İngilizce", "İş", "Vejeteryan"]) for q in MATCHING_QUESTIONS}),
Passenger("Mehmet", {q: random.choice(["41-60", "Kitap okumak", "Almanca", "Eğitim", "Vegan"]) for q in MATCHING_QUESTIONS})
]
all_passengers = [Passenger(name, answers)] + other_passengers
matched_passengers = match_passengers(all_passengers)
return [{"name": p.name, "seat": p.seat} for p in matched_passengers]
def generate_response(query_text):
"""Generate response using Chroma DB and OpenAI."""
embedding_function = OpenAIEmbeddings(openai_api_key=OPENAI_API_KEY)
db = Chroma(persist_directory=CHROMA_PATH, embedding_function=embedding_function)
# Search the DB.
results = db.similarity_search_with_relevance_scores(query_text, k=3)
if len(results) == 0 or results[0][1] < 0.7:
return "Üzgünüm, bu konuda yeterli bilgim yok. Size nasıl yardımcı olabilirim?"
context_text = "\n\n---\n\n".join([doc.page_content for doc, _score in results])
prompt_template = ChatPromptTemplate.from_template(PROMPT_TEMPLATE)
prompt = prompt_template.format(context=context_text, question=query_text)
model = ChatOpenAI(openai_api_key=OPENAI_API_KEY, model_name="gpt-4o-mini")
response_text = model.invoke(prompt)
return response_text.content
def travel_agent_response(query_text):
"""Generate travel agent response using OpenAI."""
prompt_template = ChatPromptTemplate.from_template(TRAVEL_AGENT_PROMPT)
prompt = prompt_template.format(question=query_text)
model = ChatOpenAI(openai_api_key=OPENAI_API_KEY, model_name="gpt-4o-mini")
response_text = model.invoke(prompt)
return response_text.content
def is_travel_query(query):
"""Check if the query is specifically about travel planning."""
travel_keywords = ['gezi planı', 'seyahat planı', 'tatil planı', 'tur planı', 'önerisi', 'öneri', 'önerir misin']
return any(keyword in query.lower() for keyword in travel_keywords)
def save_conversation_log(user_responses, bot_responses):
conversation = []
for user, bot in zip(user_responses, bot_responses):
conversation.append({"user": user, "bot": bot})
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
filename = f"conversation_log_{timestamp}.json"
filepath = os.path.join(LOG_DIR, filename)
with open(filepath, "w", encoding="utf-8") as f:
json.dump(conversation, f, ensure_ascii=False, indent=2)
return filepath
def generate_summary(conversation):
conversation_text = "\n".join([f"User: {entry['user']}\nBot: {entry['bot']}" for entry in conversation])
prompt = f"Aşağıdaki konuşmayı özetle ve kullanıcı bir şikayetten bahsediyorsa onu vurgula:\n\n{conversation_text}\n\nÖzet:"
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": prompt}]
)
return response.choices[0].message.content
def create_history_html():
html_content = """
<!DOCTYPE html>
<html lang="tr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Geçmiş Konuşmalar</title>
<style>
body { font-family: Arial, sans-serif; line-height: 1.6; padding: 20px; }
h1, h2 { color: #16b5ed; }
.conversation { border: 1px solid #ddd; margin-bottom: 20px; padding: 10px; }
.summary { background-color: #f0f0f0; padding: 10px; }
</style>
</head>
<body>
<h1>Geçmiş Konuşmalar</h1>
"""
log_files = [f for f in os.listdir(LOG_DIR) if f.startswith("conversation_log_") and f.endswith(".json")]
for log_file in sorted(log_files, reverse=True):
try:
with open(os.path.join(LOG_DIR, log_file), "r", encoding="utf-8") as f:
conversation = json.load(f)
html_content += f"<div class='conversation'><h2>{log_file}</h2>"
for entry in conversation:
html_content += f"<p><strong>User:</strong> {entry['user']}</p>"
html_content += f"<p><strong>Bot:</strong> {entry['bot']}</p>"
summary = generate_summary(conversation)
html_content += f"<div class='summary'><h3>Konuşma Özeti</h3><p>{summary}</p></div></div>"
except Exception as e:
print(f"Dosya işlenirken hata oluştu {log_file}: {str(e)}")
html_content += "</body></html>"
history_filepath = os.path.join(LOG_DIR, "conversation_history.html")
with open(history_filepath, "w", encoding="utf-8") as f:
f.write(html_content)
return history_filepath
def main():
st.markdown("<h1 style='color: #DC1410; border-bottom: 2px solid #DC1410;'>Vecihi</h1>", unsafe_allow_html=True)
if 'matching_state' not in st.session_state:
st.session_state.matching_state = 0
if 'matching_answers' not in st.session_state:
st.session_state.matching_answers = {}
input_container = st.container()
response_container = st.container()
user_input = st.text_input("Mesaj yazın: ", "", key="input")
with response_container:
if user_input.lower() == "eşleştirme sistemi":
st.session_state.matching_state = 1
st.session_state.bot_responses.append("Eşleştirme sistemine hoş geldiniz. Size birkaç soru soracağım. Hazır mısınız?")
st.session_state.user_responses.append(user_input)
elif st.session_state.matching_state > 0:
if st.session_state.matching_state <= len(MATCHING_QUESTIONS):
question = MATCHING_QUESTIONS[st.session_state.matching_state - 1]
options = question.split("(")[1].rstrip(")").split(", ")
if user_input.lower() in [option.lower() for option in options]:
st.session_state.matching_answers[question] = user_input
st.session_state.matching_state += 1
if st.session_state.matching_state <= len(MATCHING_QUESTIONS):
next_question = MATCHING_QUESTIONS[st.session_state.matching_state - 1]
st.session_state.bot_responses.append(f"Teşekkürler. Bir sonraki sorum: {next_question}")
else:
matches = run_matching_system(st.session_state.get('user_name', 'Kullanıcı'), st.session_state.matching_answers)
match_results = "Eşleştirme sonuçları:\n" + "\n".join([f"{match['name']} - Koltuk: {match['seat']}" for match in matches])
st.session_state.bot_responses.append(f"Eşleştirme tamamlandı. {match_results}")
st.session_state.matching_state = 0
else:
st.session_state.bot_responses.append(f"Lütfen geçerli bir seçenek girin. {question}")
st.session_state.user_responses.append(user_input)
elif user_input:
if is_travel_query(user_input):
response = travel_agent_response(user_input)
else:
response = generate_response(user_input)
st.session_state.user_responses.append(user_input)
st.session_state.bot_responses.append(response)
if st.session_state['bot_responses']:
for i in range(len(st.session_state['bot_responses'])):
st.markdown(f'<div class="user-message">{st.session_state["user_responses"][i]}</div>', unsafe_allow_html=True)
col1, col2 = st.columns([1, 9])
with col1:
st.image("images/logo_cocuk_adam.png", width=50, use_column_width=True, clamp=True, output_format='auto')
with col2:
st.markdown(f'<div class="bot-message">{st.session_state["bot_responses"][i]}</div>', unsafe_allow_html=True)
save_conversation_log(st.session_state.user_responses, st.session_state.bot_responses)
if st.button("Geçmiş Konuşmalar"):
history_filepath = create_history_html()
webbrowser.open(f'file://{os.path.abspath(history_filepath)}')
if __name__ == "__main__":
main()