File size: 11,094 Bytes
c051efa
2142c80
 
04b6f7b
f3ff1c7
2142c80
c211cfa
8b61968
2142c80
 
f3ff1c7
8b61968
10b378e
2343f3f
 
 
 
 
 
 
 
 
 
 
 
 
8b61968
2343f3f
 
04b6f7b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f3ff1c7
2142c80
 
f3ff1c7
2343f3f
8b61968
 
 
 
 
 
 
 
2343f3f
 
8b61968
b07a923
 
 
 
2343f3f
 
 
e781c31
9721f1e
8b61968
 
2343f3f
 
8b61968
 
60d0118
04b6f7b
 
8b61968
04b6f7b
8b61968
 
 
 
04b6f7b
8b61968
 
 
2142c80
2343f3f
f3ff1c7
2343f3f
 
04b6f7b
2343f3f
8b61968
2142c80
 
2343f3f
f3ff1c7
2343f3f
 
 
2142c80
 
2343f3f
8b61968
2142c80
 
 
 
8b61968
2142c80
04b6f7b
2343f3f
 
f3ff1c7
 
2142c80
2343f3f
 
 
 
 
 
f30965b
8b61968
f30965b
8b61968
f30965b
 
8b61968
 
f30965b
 
 
8b61968
 
f30965b
8b61968
f30965b
8b61968
f30965b
8b61968
 
f30965b
8b61968
f30965b
8b61968
f30965b
 
8b61968
 
f30965b
 
 
8b61968
 
f30965b
8b61968
f30965b
8b61968
f30965b
 
 
8b61968
 
f30965b
 
 
8b61968
 
f30965b
8b61968
 
f30965b
 
 
 
 
 
 
 
 
 
 
 
 
8b61968
 
f30965b
8b61968
f30965b
8b61968
 
f30965b
8b61968
 
 
 
 
 
 
f30965b
7baf15a
f30965b
dcb0d67
 
7baf15a
 
04b6f7b
 
 
8b61968
 
 
2343f3f
 
 
 
 
570ab7c
2343f3f
8b61968
04b6f7b
 
 
 
2343f3f
8b61968
 
2343f3f
 
8b61968
 
04b6f7b
8b61968
 
01cefec
365f6e4
04b6f7b
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
import gradio as gr
import os
import json
import uuid
import requests
import xml.etree.ElementTree as ET

# API URL ve API KEY ayarları
API_URL = "https://api.openai.com/v1/chat/completions"
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")

# Ürün verilerini çekme işlemi
url = 'https://www.trekbisiklet.com.tr/output/8582384479'
response = requests.get(url)
root = ET.fromstring(response.content)

products = []
for item in root.findall('item'):
    if item.find('isOptionOfAProduct').text == '1':
        if item.find('stockAmount').text > '0':
            name_words = item.find('rootlabel').text.lower().split()
            name = name_words[0]
            full_name = ' '.join(name_words)
            stockAmount = "stokta"
            price = item.find('priceWithTax').text
            item_info = (stockAmount, price)
            # name: ilk kelime (marka), item_info: (stok durumu, fiyat)
            products.append((name, item_info, full_name))

# ------------------ Session Yönetimi ------------------ #
SESSION_DIR = "chat_sessions"
if not os.path.exists(SESSION_DIR):
    os.makedirs(SESSION_DIR)

def get_session_id():
    return str(uuid.uuid4())

def load_session(session_id):
    filename = os.path.join(SESSION_DIR, f"session_{session_id}.json")
    if os.path.exists(filename):
        with open(filename, "r", encoding="utf-8") as f:
            return json.load(f)
    return []

def save_session(session_id, history):
    filename = os.path.join(SESSION_DIR, f"session_{session_id}.json")
    with open(filename, "w", encoding="utf-8") as f:
        json.dump(history, f)

# ------------------ Predict Fonksiyonu ------------------ #
def predict(system_msg, inputs, top_p, temperature, chat_counter, chatbot=[], history=[], session_id=""):
    headers = {
        "Content-Type": "application/json",
        "Authorization": f"Bearer {OPENAI_API_KEY}"
    }
    print(f"system message is ^^ {system_msg}")
    initial_message = [{"role": "user", "content": f"{inputs}"}]
    
    multi_turn_message = [
        {"role": "system", "content": "Sen bir Bıke Finder asistanısın. Görevin insanların doğru bisiklet modeli seçimini sağlamak. Kişilere sorucağın bir takım sorularala, kişileri tanıyıp, onlara stoklarımızda bulunan bisikletlerin model, boylarını tavsiye edeceksin."},
        {"role": "system", "content": "Merhaba, ben Trek Stok Danısmanı Asistanıyım! Size en uygun Trek bisiklet modelini belirleyebilmem için birkaç sorum olacak. Verdiğiniz bilgiler doğrultusunda, elimizdeki güncel stoklardan ihtiyaçlarınıza en uygun modeli seçip, satin alma sürecine yonlendirecegim. Unutmayin, tum Trek bisikletlerimiz omur boyu garantilidir! Adim 1: Bisiklet Kategorisi - Hangi tur Trek bisikletiyle ilgileniyorsunuz? Lutfen asagidaki seceneklerden birini belirtiniz: Yol Bisikleti ornek: Trek Emonda, Trek Domane; Dag Bisikleti ornek: Trek Fuel EX, Trek Remedy, Trek Procaliber; Hibrit Sehir Bisikleti; Gravel Bisikleti ornek: Trek Checkpoint; Elektrikli Bisikleti ornek: Trek Powerfly, Trek Allant+."},
        {"role": "system", "content": "Adim 2: Kullanim Amaci - Bisikletinizi hangi amaclarla kullanmayi planliyorsunuz? ornek: gunluk ulasim, spor, uzun mesafe turlari, yaris, offroad maceralari, dag yollari. Adim 3: Beklentiler - Trek bisikletinizde hangi ozellikler sizin icin en onemli? ornek: performans, konfor, dayaniklilik, teknoloji yenilik, estetk, diger. Adim 4: Zemin Kosullari - Bisikletinizi hangi zeminlerde kullanacaksiniz? ornek: sehir ici asfalt, hafif engebeli parkurlar, orman dogal parkurlar, zorlu dag yollari offroad, karisik kullanim. Adim 5: Fiziksel Olculer - Dogru model ve cerceve boyutunu belirleyebilmem icin lutfen boyunuzu ve ic bacak boyunuzu paylasir misiniz? ornek: Boyum 180 cm, ic bacak boyum 85 cm. Adim 6: Ek Tercihler - Ek olarak, bisikletinizde tercih ettiginiz baska ozellikler var mi? ornek: ekstra donanim, ozel renk, aksesuar tercihi veya butce araliginiz. Adim 7: Oneri ve Satisa Yonlendirme - Verdigimiz bilgiler dogrultusunda, stoklarimizda bulunan ve ihtiyaclariniza en uygun olan Onerilen Model modelini oneriyorum. Bu model, kisa teknik ozellikler, kullanim avantajlari ve hedeflenen zemin alan ile beklentilerinize hitap ediyor. Ustelik, tum Trek bisikletlerimiz omur boyu garantilidir! Su an stok durumumuz: Stokta mevcut, Sinirli stok. Urun detaylari ve satin alma islemi icin lutfen su linke tiklayin: Trek Onerilen Model Urun Sayfasi https:orneksite.comtrek-ornek-model. Kargo ve Teslimat - Siparisiniz odeme onayindan sonra en gec 24 saat icinde paketlenip kargoya verilir; Yuritici Kargo, MNG veya Aras Kargo ile gonderim yapilir. Kargo takip numarasi SMS ve eposta ile iletilecek; gonderim sureci 3-5 is gunu surer. Belirli tutar uzerindeki siparislerde ucretsiz kargo kampanyasi uygulanir."},
        {"role": "system", "content": "Stokları ve fiyatları https://www.trekbisiklet.com.tr den bakacaksın, diğer markalarla ilgigi soru gelirse kibarca cevaplayamayacağını ve trek in neden farklı olduğunu anlat. Yol bisikletlerinde Türkiye stoklarımızda, Madone, Domane, Emonda, CheckMate, CheckPoint ve SpeedConcept modelleri bulunuyor. Dağ bisikletlerinde ise Marlin, Procaliber, Supercaliber modeller, full amortisörlülerde ise Fuel Ex modeli bulunmakta. Şehir kullanımı için FX ve DS modelini stoklarda bulunduruyoruz."}
    ]
    
    messages = multi_turn_message
    input_words = [str(word).lower() for word in inputs.split()]

    for product_info in products:
        if product_info[0] in input_words:
            new_msg = f"{product_info[2]} {product_info[1][0]} ve fiyatı EURO {product_info[1][1]}"
            print(new_msg)
            product_msg = {"role": "system", "content": new_msg}
            messages.append(product_msg)
    
    for data in chatbot:
        user = {"role": "user", "content": data[0]}
        assistant = {"role": "assistant", "content": data[1]}
        messages.append(user)
        messages.append(assistant)
        
    messages.append({"role": "user", "content": inputs})
                
    # o3-mini modeli için payload'i güncelledik. 
    # Eğer o3-mini modeli streaming desteklemiyorsa "stream": False yapıyoruz.
    payload = {
        "model": "o3-mini",
        "messages": messages,
        "temperature": 0.7,
        "top_p": 0.9,
        "n": 1,
        "stream": False,
        "presence_penalty": 0,
        "frequency_penalty": 0,
    }

    chat_counter += 1
    history.append(inputs)
    print(f"Logging : payload is - {payload}")

    response = requests.post(API_URL, headers=headers, json=payload, stream=payload["stream"])
    print(f"Logging : response code - {response}")
    
    token_counter = 0
    partial_words = ""
    counter = 0
    for chunk in response.iter_lines():
        if counter == 0:
            counter += 1
            continue
        if chunk.decode():
            chunk = chunk.decode()
            if len(chunk) > 12 and "content" in json.loads(chunk[6:])['choices'][0]['delta']:
                partial_words += json.loads(chunk[6:])['choices'][0]["delta"]["content"]
                if token_counter == 0:
                    history.append(" " + partial_words)
                else:
                    history[-1] = partial_words
                chat = [(history[i], history[i + 1]) for i in range(0, len(history) - 1, 2)]
                token_counter += 1
                save_session(session_id, history)
                yield chat, history, chat_counter, response

def reset_textbox():
    return gr.update(value='')

def set_visible_false():
    return gr.update(visible=False)

def set_visible_true():
    return gr.update(visible=False)

# Yeni jenerasyon, okunaklı ve modern tasarım için özel CSS
css = """
/* Chat Başlığı: Temiz ve modern görünüm */
.chat-header {
    background-color: #ffffff;
    color: #333;
    padding: 15px;
    text-align: center;
    font-size: 22px;
    font-weight: 600;
    border-bottom: 1px solid #e0e0e0;
}

/* Chat penceresi: Açık ve ferah arka plan */
#chatbot {
    background-color: #fafafa;
    border: none;
    padding: 15px;
}

/* Kullanıcı mesajı: Açık mavi ton, modern görünüm */
.chat-bubble.user {
    background-color: #e1f5fe;
    align-self: flex-end;
    border-radius: 16px;
    padding: 12px 18px;
    margin: 8px 0;
    max-width: 70%;
    box-shadow: 0px 1px 3px rgba(0,0,0,0.1);
    font-size: 16px;
    color: #333;
}

/* Bot mesajı: Beyaz, hafif gri kenarlık, modern görünüm */
.chat-bubble.bot {
    background-color: #ffffff;
    align-self: flex-start;
    border: 1px solid #e0e0e0;
    border-radius: 16px;
    padding: 12px 18px;
    margin: 8px 0;
    max-width: 70%;
    box-shadow: 0px 1px 3px rgba(0,0,0,0.1);
    font-size: 16px;
    color: #333;
}

/* Mesaj gönderme alanı: Temiz ve modern */
.chat-input-container {
    display: flex;
    padding: 15px;
    border-top: 1px solid #e0e0e0;
    background-color: #ffffff;
}

/* Chat input alanı */
.chat-input {
    flex: 1;
    padding: 10px 15px;
    border: 1px solid #ddd;
    border-radius: 20px;
    font-size: 16px;
    outline: none;
}

/* Gönder butonu: Modern mavi ton */
.send-button {
    background-color: #1976d2;
    border: none;
    color: #fff;
    padding: 10px 20px;
    margin-left: 10px;
    border-radius: 20px;
    font-size: 16px;
    cursor: pointer;
}
"""

# Tema ayarını güncelliyoruz: modern ve temiz görünüm için neutral_hue "blue"
theme = gr.themes.Base(
    neutral_hue="blue",
    text_size="sm",
    spacing_size="sm",
)

# Yeni oturum için benzersiz session ID oluşturuyoruz
session_id_value = get_session_id()

with gr.Blocks(theme=theme, css=css) as demo:
    gr.Markdown("<div class='chat-header'>Trek Bike Finder Chatbot</div>")
    
    with gr.Column(elem_id="col_container"):
        with gr.Accordion("", open=False, visible=False):
            system_msg = gr.Textbox(value="")
            new_msg = gr.Textbox(value="")
            accordion_msg = gr.HTML(value="", visible=False)
        chatbot = gr.Chatbot(label='Trek Bike Finder', elem_id="chatbot")
        inputs = gr.Textbox(
            placeholder="Buraya yazın, istediğiniz modeli beraber bulalım.", show_label=False)
        # Oturum geçmişini session_id üzerinden yüklüyoruz.
        state = gr.State(load_session(session_id_value))
        # Gizli session ID
        session_id = gr.Textbox(value=session_id_value, visible=False, label="Session ID")
        with gr.Accordion("", open=False, visible=False):
            top_p = gr.Slider(minimum=0, maximum=1.0, value=0.5, step=0.05, interactive=False, visible=False)
            temperature = gr.Slider(minimum=0, maximum=5.0, value=0.1, step=0.1, interactive=False, visible=False)
            chat_counter = gr.Number(value=0, visible=False, precision=0)

    inputs.submit(
        predict,
        [system_msg, inputs, top_p, temperature, chat_counter, chatbot, state, session_id],
        [chatbot, state, chat_counter]
    )
    inputs.submit(reset_textbox, [], [inputs])

demo.queue(max_size=10).launch(debug=True)