alperall commited on
Commit
5a82ecf
·
verified ·
1 Parent(s): 134342e

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +921 -80
app.py CHANGED
@@ -1,42 +1,514 @@
1
- # ... (Önceki import ve config kısımları aynı)
 
2
 
3
- alp_logo = os.path.join(os.path.dirname(__file__), "alp.png")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
 
5
- # TÜRKÇE PROMPTLAR
6
  DEFAULT_PROMPTS = [{
7
- "category": "📝 Plan Yap",
 
8
  "prompts": [
9
- "İş kurma planı oluştur",
10
- "Hedeflerim için yol haritası hazırla",
11
- "Mülakat provası için strateji öner"
12
  ]
13
  }, {
14
- "category": "✍️ İçerik Üret",
 
15
  "prompts": [
16
- "Teknoloji blogu için makale taslağı oluştur",
17
- "Şirket vizyon metni yaz",
18
- "Satış e-postası taslağı hazırla"
19
  ]
20
  }]
21
 
22
- # TÜRKÇE ÖNERİLER
23
  DEFAULT_SUGGESTIONS = [{
24
- "label": 'Planlama',
25
- "children": [
26
- {"label": "İş Planı", "value": "İş kurma planı oluştur"},
27
- {"label": "Hedef Planı", "value": "Hedeflerim için yol haritası hazırla"},
28
- {"label": "Mülakat Stratejisi", "value": "Mülakat provası için strateji öner"}
29
- ]
 
 
 
 
 
 
 
 
30
  }, {
31
- "label": 'Yazım',
32
- "children": [
33
- {"label": "Teknoloji Makalesi", "value": "Teknoloji blogu için makale taslağı oluştur"},
34
- {"label": "Vizyon Metni", "value": "Şirket vizyon metni yaz"},
35
- {"label": "Satış E-postası", "value": "Satış e-postası taslağı hazırla"}
36
- ]
 
 
 
 
 
 
 
 
37
  }]
38
 
39
- # TÜRKÇE ARAYÜZ KODU
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
  with gr.Blocks(css=css, fill_width=True) as demo:
41
  state = gr.State({
42
  "conversations_history": {},
@@ -46,60 +518,429 @@ with gr.Blocks(css=css, fill_width=True) as demo:
46
  })
47
 
48
  with ms.Application(), antdx.XProvider(
49
- theme=DEFAULT_THEME, locale='tr_TR'), ms.AutoLoading():
50
-
51
- # SOL PANEL
52
- with antd.Col(md=dict(flex="0 0 260px"), elem_classes="chatbot-conversations"):
53
- with antd.Flex(vertical=True, gap="small"):
54
- # Logo ve Başlık
55
- with antd.Typography.Title(level=1, style={"fontSize": 24, "margin": 0}):
56
- with antd.Flex(align="center", gap="small"):
57
- antd.Image(alp_logo, width=32, height=32, preview=False)
58
- ms.Span("AlpDroid AI")
59
-
60
- # Yeni Sohbet Butonu
61
- with antd.Button("Yeni Sohbet", block=True, icon=antd.Icon("PlusOutlined")) as add_conversation_btn
62
-
63
- # Sohbet Geçmişi
64
- with antdx.Conversations() as conversations:
65
- with ms.Slot('menu.items'):
66
- antd.Menu.Item("Sil", key="delete", danger=True, icon=antd.Icon("DeleteOutlined"))
67
-
68
- # SAĞ PANEL
69
- with antd.Col(flex=1, style={"height": "100%"}):
70
- with antd.Flex(vertical=True, gap="middle"):
71
- # Sohbet Kutusu
72
- with antdx.Bubble.List(items=DEFAULT_CONVERSATIONS_HISTORY) as chatbot:
73
-
74
- # Kullanıcı Mesajı
75
- with antdx.Bubble.List.Role(role="user", placement="end"):
76
- ms.Markdown()
77
- with ms.Slot("footer"):
78
- antd.Button(icon=antd.Icon("EditOutlined"))
79
- antd.Popconfirm("Bu mesajı silmek istiyor musunuz?",
80
- ok_button_props={"danger": True},
81
- children=[antd.Button(icon=antd.Icon("DeleteOutlined"))]
82
- )
83
-
84
- # Asistan Mesajı
85
- with antdx.Bubble.List.Role(role="assistant", placement="start"):
86
- antd.Avatar(alp_logo)
87
- ms.Markdown()
88
- with ms.Slot("footer"):
89
- antd.Button(icon=antd.Icon("SyncOutlined"))
90
- antd.Popconfirm("Mesajı yeniden oluştur?",
91
- children=[antd.Button(icon=antd.Icon("DeleteOutlined"))]
92
- )
93
-
94
- # Giriş Alanı
95
- with antdx.Sender(placeholder="Mesaj yazın veya '/' ile komut seçin"):
96
- with antd.Tooltip("Geçmişi Temizle"):
97
- antd.Button(icon=antd.Icon("ClearOutlined"))
98
- with antdx.Suggestion(items=DEFAULT_SUGGESTIONS):
99
- antd.Input.Textarea()
100
-
101
- # EVENT HANDLERS (Önceki koddaki olay yönetim fonksiyonları aynen kalacak)
102
- # ... (Önceki event handler'lar buraya gelecek)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
103
 
104
  if __name__ == "__main__":
105
- demo.launch()
 
1
+ import os
2
+ import uuid
3
 
4
+ import gradio as gr
5
+ import modelscope_studio.components.antd as antd
6
+ import modelscope_studio.components.antdx as antdx
7
+ import modelscope_studio.components.base as ms
8
+ from openai import OpenAI
9
+
10
+ # Qwen/QwQ-32B
11
+
12
+ # =========== Configuration
13
+ # API KEY
14
+ client = OpenAI(
15
+ base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
16
+ api_key=os.getenv("DASHSCOPE_API_KEY"),
17
+ )
18
+
19
+ model = "qwq-32b"
20
+
21
+ save_history = True
22
+
23
+ # =========== Configuration
24
+
25
+ is_modelscope_studio = os.getenv('MODELSCOPE_ENVIRONMENT') == 'studio'
26
+
27
+
28
+ def get_text(text: str, cn_text: str):
29
+ if is_modelscope_studio:
30
+ return cn_text
31
+ return text
32
+
33
+
34
+ qwen_logo = os.path.join(os.path.dirname(__file__), "qwen.png")
35
 
 
36
  DEFAULT_PROMPTS = [{
37
+ "category":
38
+ "🖋 Make a plan",
39
  "prompts": [
40
+ "Help me with a plan to start a business",
41
+ "Help me with a plan to achieve my goals",
42
+ "Help me with a plan for a successful interview"
43
  ]
44
  }, {
45
+ "category":
46
+ "📅 Help me write",
47
  "prompts": [
48
+ "Help me write a story with a twist ending",
49
+ "Help me write a blog post on mental health",
50
+ "Help me write a letter to my future self"
51
  ]
52
  }]
53
 
 
54
  DEFAULT_SUGGESTIONS = [{
55
+ "label":
56
+ 'Make a plan',
57
+ "value":
58
+ "Make a plan",
59
+ "children": [{
60
+ "label": "Start a business",
61
+ "value": "Help me with a plan to start a business"
62
+ }, {
63
+ "label": "Achieve my goals",
64
+ "value": "Help me with a plan to achieve my goals"
65
+ }, {
66
+ "label": "Successful interview",
67
+ "value": "Help me with a plan for a successful interview"
68
+ }]
69
  }, {
70
+ "label":
71
+ 'Help me write',
72
+ "value":
73
+ "Help me write",
74
+ "children": [{
75
+ "label": "Story with a twist ending",
76
+ "value": "Help me write a story with a twist ending"
77
+ }, {
78
+ "label": "Blog post on mental health",
79
+ "value": "Help me write a blog post on mental health"
80
+ }, {
81
+ "label": "Letter to my future self",
82
+ "value": "Help me write a letter to my future self"
83
+ }]
84
  }]
85
 
86
+ DEFAULT_CONVERSATIONS_HISTORY = [{"role": "placeholder"}]
87
+
88
+ DEFAULT_LOCALE = 'zh_CN' if is_modelscope_studio else 'en_US'
89
+
90
+ DEFAULT_THEME = {
91
+ "token": {
92
+ "colorPrimary": "#6A57FF",
93
+ }
94
+ }
95
+
96
+
97
+ def format_history(history):
98
+ messages = [{
99
+ "role": "system",
100
+ "content": "You are a helpful and harmless assistant.",
101
+ }]
102
+ for item in history:
103
+ if item["role"] == "user":
104
+ messages.append({"role": "user", "content": item["content"]})
105
+ elif item["role"] == "assistant":
106
+ messages.append({"role": "assistant", "content": item["content"]})
107
+ return messages
108
+
109
+
110
+ class Gradio_Events:
111
+
112
+ @staticmethod
113
+ def _submit(state_value):
114
+ history = state_value["conversations_history"][
115
+ state_value["conversation_id"]]
116
+ # submit
117
+ history_messages = format_history(history)
118
+
119
+ history.append({
120
+ "role": "assistant",
121
+ "content": "",
122
+ "key": str(uuid.uuid4()),
123
+ "meta": {
124
+ "reason_content": ""
125
+ },
126
+ "loading": True,
127
+ })
128
+
129
+ yield {
130
+ chatbot: gr.update(items=history),
131
+ state: gr.update(value=state_value),
132
+ }
133
+ try:
134
+ response = client.chat.completions.create(
135
+ model=model, # ModelScope Model-Id
136
+ messages=history_messages,
137
+ stream=True)
138
+ thought_done = False
139
+ for chunk in response:
140
+ reasoning_content = chunk.choices[0].delta.reasoning_content
141
+ content = chunk.choices[0].delta.content
142
+ history[-1]["loading"] = False
143
+
144
+ if content and not thought_done:
145
+ thought_done = True
146
+ history[-1]["meta"]["reason_content"] = history[-1][
147
+ "content"]
148
+
149
+ print("Reason: ",history[-1]["meta"]["reason_content"])
150
+
151
+ history[-1]["content"] = ""
152
+ history[-1]["meta"]["thought_end_message"] = get_text(
153
+ "End of Thought", "已深度思考")
154
+ if not thought_done:
155
+ history[-1]["content"] += reasoning_content or ""
156
+ else:
157
+ history[-1]["content"] += content or ""
158
+
159
+ yield {
160
+ chatbot: gr.update(items=history),
161
+ state: gr.update(value=state_value)
162
+ }
163
+ history[-1]["meta"]["end"] = True
164
+
165
+ print("Answer: ",history[-1]["content"])
166
+
167
+ yield {
168
+ chatbot: gr.update(items=history),
169
+ state: gr.update(value=state_value),
170
+ }
171
+ except Exception as e:
172
+ history[-1]["loading"] = False
173
+ history[-1]["meta"]["end"] = True
174
+ history[-1]["meta"]["error"] = True
175
+ history[-1]["content"] = "Failed to respond, please try again."
176
+ yield {
177
+ chatbot: gr.update(items=history),
178
+ state: gr.update(value=state_value)
179
+ }
180
+ print('Error: ',e)
181
+ raise e
182
+
183
+
184
+ @staticmethod
185
+ def submit(sender_value, state_value):
186
+ if not state_value["conversation_id"]:
187
+ random_id = str(uuid.uuid4())
188
+ history = []
189
+ state_value["conversation_id"] = random_id
190
+ state_value["conversations_history"][random_id] = history
191
+ state_value["conversations"].append({
192
+ "label": sender_value,
193
+ "key": random_id
194
+ })
195
+
196
+ history = state_value["conversations_history"][
197
+ state_value["conversation_id"]]
198
+ history.append({
199
+ "role": "user",
200
+ "meta": {},
201
+ "key": str(uuid.uuid4()),
202
+ "content": sender_value
203
+ })
204
+
205
+ # preprocess submit
206
+ yield Gradio_Events.preprocess_submit()(state_value)
207
+ try:
208
+ for chunk in Gradio_Events._submit(state_value):
209
+ yield chunk
210
+ except Exception as e:
211
+ raise e
212
+ finally:
213
+ # postprocess submit
214
+ yield Gradio_Events.postprocess_submit(state_value)
215
+
216
+ @staticmethod
217
+ def regenerate_message(state_value, e: gr.EventData):
218
+ conversation_key = e._data["component"]["conversationKey"]
219
+ history = state_value["conversations_history"][
220
+ state_value["conversation_id"]]
221
+ index = -1
222
+ for i, conversation in enumerate(history):
223
+ if conversation["key"] == conversation_key:
224
+ index = i
225
+ break
226
+ if index == -1:
227
+ yield gr.skip()
228
+ history = history[:index]
229
+ state_value["conversations_history"][
230
+ state_value["conversation_id"]] = history
231
+
232
+ yield {
233
+ chatbot:gr.update(items=history),
234
+ state: gr.update(value=state_value)
235
+ }
236
+
237
+ # preprocess submit
238
+ yield Gradio_Events.preprocess_submit(clear_input=False)(state_value)
239
+ try:
240
+ for chunk in Gradio_Events._submit(state_value):
241
+ yield chunk
242
+ except Exception as e:
243
+ raise e
244
+ finally:
245
+ # postprocess submit
246
+ yield Gradio_Events.postprocess_submit(state_value)
247
+
248
+
249
+ @staticmethod
250
+ def preprocess_submit(clear_input=True):
251
+
252
+ def preprocess_submit_handler(state_value):
253
+ history = state_value["conversations_history"][
254
+ state_value["conversation_id"]]
255
+ for conversation in history:
256
+ if "meta" in conversation:
257
+ conversation["meta"]["disabled"] = True
258
+ return {
259
+ sender: gr.update(value=None, loading=True) if clear_input else gr.update(loading=True),
260
+ conversations:
261
+ gr.update(active_key=state_value["conversation_id"],
262
+ items=list(
263
+ map(
264
+ lambda item: {
265
+ **item,
266
+ "disabled":
267
+ True if item["key"] != state_value[
268
+ "conversation_id"] else False,
269
+ }, state_value["conversations"]))),
270
+ add_conversation_btn:
271
+ gr.update(disabled=True),
272
+ clear_btn:
273
+ gr.update(disabled=True),
274
+ conversation_delete_menu_item:
275
+ gr.update(disabled=True),
276
+ chatbot:
277
+ gr.update(items=history),
278
+ state:
279
+ gr.update(value=state_value),
280
+ }
281
+
282
+ return preprocess_submit_handler
283
+
284
+ @staticmethod
285
+ def postprocess_submit(state_value):
286
+ history = state_value["conversations_history"][
287
+ state_value["conversation_id"]]
288
+ for conversation in history:
289
+ if "meta" in conversation:
290
+ conversation["meta"]["disabled"] = False
291
+ return {
292
+ sender: gr.update(loading=False),
293
+ conversation_delete_menu_item: gr.update(disabled=False),
294
+ clear_btn: gr.update(disabled=False),
295
+ conversations: gr.update(items=state_value["conversations"]),
296
+ add_conversation_btn: gr.update(disabled=False),
297
+ chatbot: gr.update(items=history),
298
+ state: gr.update(value=state_value),
299
+ }
300
+
301
+ @staticmethod
302
+ def cancel(state_value):
303
+ history = state_value["conversations_history"][
304
+ state_value["conversation_id"]]
305
+ history[-1]["loading"] = False
306
+ history[-1]["meta"]["end"] = True
307
+ history[-1]["meta"]["canceled"] = True
308
+ return Gradio_Events.postprocess_submit(state_value)
309
+
310
+ @staticmethod
311
+ def delete_message(state_value, e: gr.EventData):
312
+ conversation_key = e._data["component"]["conversationKey"]
313
+ history = state_value["conversations_history"][
314
+ state_value["conversation_id"]]
315
+ history = [item for item in history if item["key"] != conversation_key]
316
+ state_value["conversations_history"][
317
+ state_value["conversation_id"]] = history
318
+
319
+ return gr.update(items=history if len(history) >
320
+ 0 else DEFAULT_CONVERSATIONS_HISTORY), gr.update(
321
+ value=state_value)
322
+
323
+
324
+
325
+ @staticmethod
326
+ def edit_message(state_value, e: gr.EventData):
327
+ conversation_key = e._data["component"]["conversationKey"]
328
+ history = state_value["conversations_history"][
329
+ state_value["conversation_id"]]
330
+ index = -1
331
+ for i, conversation in enumerate(history):
332
+ if conversation["key"] == conversation_key:
333
+ index = i
334
+ break
335
+ if index == -1:
336
+ return gr.skip()
337
+ state_value["editing_message_index"] = index
338
+ text = ''
339
+ if isinstance(history[index]["content"], str):
340
+ text = history[index]["content"]
341
+ else:
342
+ text = history[index]["content"]["text"]
343
+ return gr.update(value=text), gr.update(value=state_value)
344
+
345
+ @staticmethod
346
+ def confirm_edit_message(edit_textarea_value, state_value):
347
+ history = state_value["conversations_history"][
348
+ state_value["conversation_id"]]
349
+ message = history[state_value["editing_message_index"]]
350
+ if isinstance(message["content"], str):
351
+ message["content"] = edit_textarea_value
352
+ else:
353
+ message["content"]["text"] = edit_textarea_value
354
+ return gr.update(items=history), gr.update(value=state_value)
355
+
356
+ @staticmethod
357
+ def select_suggestion(sender_value, e: gr.EventData):
358
+ return gr.update(value=sender_value[:-1] + e._data["payload"][0])
359
+
360
+ @staticmethod
361
+ def apply_prompt(e: gr.EventData):
362
+ return gr.update(value=e._data["payload"][0]["data"]["description"])
363
+
364
+ @staticmethod
365
+ def new_chat(state_value):
366
+ if not state_value["conversation_id"]:
367
+ return gr.skip()
368
+ state_value["conversation_id"] = ""
369
+ return gr.update(active_key=state_value["conversation_id"]), gr.update(
370
+ items=DEFAULT_CONVERSATIONS_HISTORY), gr.update(value=state_value)
371
+
372
+ @staticmethod
373
+ def select_conversation(state_value, e: gr.EventData):
374
+ active_key = e._data["payload"][0]
375
+ if state_value["conversation_id"] == active_key or (
376
+ active_key not in state_value["conversations_history"]):
377
+ return gr.skip()
378
+ state_value["conversation_id"] = active_key
379
+ return gr.update(active_key=active_key), gr.update(
380
+ items=state_value["conversations_history"][active_key]), gr.update(
381
+ value=state_value)
382
+
383
+ @staticmethod
384
+ def click_conversation_menu(state_value, e: gr.EventData):
385
+ conversation_id = e._data["payload"][0]["key"]
386
+ operation = e._data["payload"][1]["key"]
387
+ if operation == "delete":
388
+ del state_value["conversations_history"][conversation_id]
389
+
390
+ state_value["conversations"] = [
391
+ item for item in state_value["conversations"]
392
+ if item["key"] != conversation_id
393
+ ]
394
+
395
+ if state_value["conversation_id"] == conversation_id:
396
+ state_value["conversation_id"] = ""
397
+ return gr.update(
398
+ items=state_value["conversations"],
399
+ active_key=state_value["conversation_id"]), gr.update(
400
+ items=DEFAULT_CONVERSATIONS_HISTORY), gr.update(
401
+ value=state_value)
402
+ else:
403
+ return gr.update(
404
+ items=state_value["conversations"]), gr.skip(), gr.update(
405
+ value=state_value)
406
+ return gr.skip()
407
+
408
+ @staticmethod
409
+ def clear_conversation_history(state_value):
410
+ if not state_value["conversation_id"]:
411
+ return gr.skip()
412
+ state_value["conversations_history"][
413
+ state_value["conversation_id"]] = []
414
+ return gr.update(items=DEFAULT_CONVERSATIONS_HISTORY), gr.update(
415
+ value=state_value)
416
+
417
+ @staticmethod
418
+ def close_modal():
419
+ return gr.update(open=False)
420
+
421
+ @staticmethod
422
+ def open_modal():
423
+ return gr.update(open=True)
424
+
425
+ @staticmethod
426
+ def update_browser_state(state_value):
427
+
428
+ return gr.update(value=dict(
429
+ conversations=state_value["conversations"],
430
+ conversations_history=state_value["conversations_history"]))
431
+
432
+ @staticmethod
433
+ def apply_browser_state(browser_state_value, state_value):
434
+ state_value["conversations"] = browser_state_value["conversations"]
435
+ state_value["conversations_history"] = browser_state_value[
436
+ "conversations_history"]
437
+ return gr.update(
438
+ items=browser_state_value["conversations"]), gr.update(
439
+ value=state_value)
440
+
441
+
442
+ css = """
443
+ .gradio-container {
444
+ padding: 0 !important;
445
+ }
446
+
447
+ .gradio-container > main.fillable {
448
+ padding: 0 !important;
449
+ }
450
+
451
+ #chatbot {
452
+ height: calc(100vh - 21px - 16px);
453
+ }
454
+
455
+ #chatbot .chatbot-conversations {
456
+ height: 100%;
457
+ background-color: var(--ms-gr-ant-color-bg-layout);
458
+ }
459
+
460
+ #chatbot .chatbot-conversations .chatbot-conversations-list {
461
+ padding-left: 0;
462
+ padding-right: 0;
463
+ }
464
+
465
+ #chatbot .chatbot-chat {
466
+ padding: 32px;
467
+ height: 100%;
468
+ }
469
+
470
+ @media (max-width: 768px) {
471
+ #chatbot .chatbot-chat {
472
+ padding: 0;
473
+ }
474
+ }
475
+
476
+ #chatbot .chatbot-chat .chatbot-chat-messages {
477
+ flex: 1;
478
+ }
479
+
480
+ #chatbot .chatbot-chat .chatbot-chat-messages .chatbot-chat-message .chatbot-chat-message-footer {
481
+ visibility: hidden;
482
+ opacity: 0;
483
+ transition: opacity 0.2s;
484
+ }
485
+
486
+ #chatbot .chatbot-chat .chatbot-chat-messages .chatbot-chat-message:last-child .chatbot-chat-message-footer {
487
+ visibility: visible;
488
+ opacity: 1;
489
+ }
490
+
491
+ #chatbot .chatbot-chat .chatbot-chat-messages .chatbot-chat-message:hover .chatbot-chat-message-footer {
492
+ visibility: visible;
493
+ opacity: 1;
494
+ }
495
+ """
496
+
497
+
498
+ def logo():
499
+ with antd.Typography.Title(level=1,
500
+ elem_style=dict(fontSize=24,
501
+ padding=8,
502
+ margin=0)):
503
+ with antd.Flex(align="center", gap="small", justify="center"):
504
+ antd.Image(qwen_logo,
505
+ preview=False,
506
+ alt="logo",
507
+ width=24,
508
+ height=24)
509
+ ms.Span("QwQ-32B")
510
+
511
+
512
  with gr.Blocks(css=css, fill_width=True) as demo:
513
  state = gr.State({
514
  "conversations_history": {},
 
518
  })
519
 
520
  with ms.Application(), antdx.XProvider(
521
+ theme=DEFAULT_THEME, locale=DEFAULT_LOCALE), ms.AutoLoading():
522
+ with antd.Row(gutter=[20, 20], wrap=False, elem_id="chatbot"):
523
+ # Left Column
524
+ with antd.Col(md=dict(flex="0 0 260px", span=24, order=0),
525
+ span=0,
526
+ order=1,
527
+ elem_classes="chatbot-conversations"):
528
+ with antd.Flex(vertical=True,
529
+ gap="small",
530
+ elem_style=dict(height="100%")):
531
+ # Logo
532
+ logo()
533
+
534
+ # New Conversation Button
535
+ with antd.Button(value=None,
536
+ color="primary",
537
+ variant="filled",
538
+ block=True) as add_conversation_btn:
539
+ ms.Text(get_text("New Conversation", "新建对话"))
540
+ with ms.Slot("icon"):
541
+ antd.Icon("PlusOutlined")
542
+
543
+ # Conversations List
544
+ with antdx.Conversations(
545
+ elem_classes="chatbot-conversations-list",
546
+ ) as conversations:
547
+ with ms.Slot('menu.items'):
548
+ with antd.Menu.Item(
549
+ label="Delete", key="delete", danger=True
550
+ ) as conversation_delete_menu_item:
551
+ with ms.Slot("icon"):
552
+ antd.Icon("DeleteOutlined")
553
+ # Right Column
554
+ with antd.Col(flex=1, elem_style=dict(height="100%")):
555
+ with antd.Flex(vertical=True,
556
+ gap="middle",
557
+ elem_classes="chatbot-chat"):
558
+ # Chatbot
559
+ with antdx.Bubble.List(
560
+ items=DEFAULT_CONVERSATIONS_HISTORY,
561
+ elem_classes="chatbot-chat-messages") as chatbot:
562
+ # Define Chatbot Roles
563
+ with ms.Slot("roles"):
564
+ # Placeholder Role
565
+ with antdx.Bubble.List.Role(
566
+ role="placeholder",
567
+ styles=dict(content=dict(width="100%")),
568
+ variant="borderless"):
569
+ with ms.Slot("messageRender"):
570
+ with antd.Space(
571
+ direction="vertical",
572
+ size=16,
573
+ elem_style=dict(width="100%")):
574
+ with antdx.Welcome(
575
+ styles=dict(icon=dict(
576
+ flexShrink=0)),
577
+ variant="borderless",
578
+ title=get_text(
579
+ "Hello, I'm QwQ-32B",
580
+ "你好,我是 QwQ-32B"),
581
+ description=get_text(
582
+ "You can type text to get started.",
583
+ "你可以输入文本开始对话。"),
584
+ ):
585
+ with ms.Slot("icon"):
586
+ antd.Image(qwen_logo,
587
+ preview=False)
588
+ with antdx.Prompts(title=get_text(
589
+ "How can I help you today?",
590
+ "有什么我能帮助你的吗?"),
591
+ styles={
592
+ "list": {
593
+ "width":
594
+ '100%',
595
+ },
596
+ "item": {
597
+ "flex": 1,
598
+ },
599
+ }) as prompts:
600
+ for item in DEFAULT_PROMPTS:
601
+ with antdx.Prompts.Item(
602
+ label=item["category"]
603
+ ):
604
+ for prompt in item[
605
+ "prompts"]:
606
+ antdx.Prompts.Item(
607
+ description=prompt,
608
+ )
609
+
610
+ # User Role
611
+ with antdx.Bubble.List.Role(
612
+ role="user",
613
+ placement="end",
614
+ elem_classes="chatbot-chat-message",
615
+ class_names=dict(
616
+ footer="chatbot-chat-message-footer"),
617
+ styles=dict(content=dict(
618
+ maxWidth="100%",
619
+ overflow='auto',
620
+ ))):
621
+ with ms.Slot(
622
+ "messageRender",
623
+ params_mapping="(content) => content"):
624
+
625
+ ms.Markdown()
626
+ with ms.Slot("footer",
627
+ params_mapping="""(bubble) => {
628
+ return {
629
+ copy_btn: {
630
+ copyable: { text: typeof bubble.content === 'string' ? bubble.content : bubble.content?.text, tooltips: false },
631
+ },
632
+ edit_btn: { conversationKey: bubble.key, disabled: bubble.meta.disabled },
633
+ delete_btn: { conversationKey: bubble.key, disabled: bubble.meta.disabled },
634
+ };
635
+ }"""):
636
+ with antd.Typography.Text(
637
+ copyable=dict(tooltips=False),
638
+ as_item="copy_btn"):
639
+ with ms.Slot("copyable.icon"):
640
+ with antd.Button(value=None,
641
+ size="small",
642
+ color="default",
643
+ variant="text"):
644
+ with ms.Slot("icon"):
645
+ antd.Icon("CopyOutlined")
646
+ with antd.Button(value=None,
647
+ size="small",
648
+ color="default",
649
+ variant="text"):
650
+ with ms.Slot("icon"):
651
+ antd.Icon("CheckOutlined")
652
+ with antd.Button(value=None,
653
+ size="small",
654
+ color="default",
655
+ variant="text",
656
+ as_item="edit_btn"
657
+ ) as user_edit_btn:
658
+ with ms.Slot("icon"):
659
+ antd.Icon("EditOutlined")
660
+ with antd.Popconfirm(
661
+ title="Delete the message",
662
+ description=
663
+ "Are you sure to delete this message?",
664
+ ok_button_props=dict(danger=True),
665
+ as_item="delete_btn"
666
+ ) as user_delete_popconfirm:
667
+ with antd.Button(value=None,
668
+ size="small",
669
+ color="default",
670
+ variant="text",
671
+ as_item="delete_btn"):
672
+ with ms.Slot("icon"):
673
+ antd.Icon("DeleteOutlined")
674
+
675
+ # Chatbot Role
676
+ with antdx.Bubble.List.Role(
677
+ role="assistant",
678
+ placement="start",
679
+ elem_classes="chatbot-chat-message",
680
+ class_names=dict(
681
+ footer="chatbot-chat-message-footer"),
682
+ styles=dict(content=dict(
683
+ maxWidth="100%", overflow='auto'))):
684
+ with ms.Slot("avatar"):
685
+ antd.Avatar(
686
+ os.path.join(os.path.dirname(__file__),
687
+ "qwen.png"))
688
+ with ms.Slot(
689
+ "messageRender",
690
+ params_mapping="""(content, bubble) => {
691
+ const reason_content = bubble?.meta?.reason_content
692
+ const has_error = bubble?.meta?.error
693
+ return {
694
+ reasoning: reason_content || content,
695
+ reasoning_container: has_error ? { style: { display: 'none' } } : undefined,
696
+ answer: {
697
+ value: reason_content || has_error ? content : undefined
698
+ },
699
+ collapse_label: bubble.meta?.thought_end_message,
700
+ collapse_progress: bubble.meta?.thought_end_message ? { style: { display: 'none' } } : undefined,
701
+ canceled: bubble.meta?.canceled ? undefined : { style: { display: 'none' } }
702
+ }
703
+ }"""):
704
+ with antd.Flex(vertical=True,
705
+ gap="middle"):
706
+ with antd.Collapse(
707
+ default_active_key=[
708
+ "reasoning"
709
+ ],
710
+ as_item="reasoning_container"):
711
+ with antd.Collapse.Item(
712
+ key="reasoning"):
713
+ with ms.Slot("label"):
714
+ with antd.Space(
715
+ size="middle"):
716
+ ms.Span(
717
+ get_text(
718
+ "Thinking...",
719
+ "思考中..."),
720
+ as_item=
721
+ "collapse_label")
722
+ antd.Progress(
723
+ percent="100",
724
+ status="active",
725
+ elem_style=dict(
726
+ display="flex",
727
+ alignItems=
728
+ "center",
729
+ ),
730
+ show_info=False,
731
+ size=[110, 5],
732
+ as_item=
733
+ "collapse_progress"
734
+ )
735
+ with antd.Alert(
736
+ type="warning"):
737
+ with ms.Slot(
738
+ "description"):
739
+ ms.Markdown(
740
+ as_item="reasoning"
741
+ )
742
+ ms.Markdown(
743
+ as_item="answer",
744
+ elem_classes="answer-content")
745
+
746
+ antd.Divider(as_item="canceled")
747
+ antd.Typography.Text(get_text(
748
+ "Chat completion paused.", "聊天已暂停。"),
749
+ as_item="canceled",
750
+ type="warning")
751
+
752
+ with ms.Slot("footer",
753
+ params_mapping="""(bubble) => {
754
+ if (bubble?.meta?.end) {
755
+ return {
756
+ copy_btn: {
757
+ copyable: { text: bubble.content, tooltips: false },
758
+ },
759
+ regenerate_btn: { conversationKey: bubble.key, disabled: bubble.meta.disabled },
760
+ delete_btn: { conversationKey: bubble.key, disabled: bubble.meta.disabled },
761
+ edit_btn: { conversationKey: bubble.key, disabled: bubble.meta.disabled },
762
+ };
763
+ }
764
+ return { actions_container: { style: { display: 'none' } } };
765
+ }"""):
766
+ with ms.Div(as_item="actions_container"):
767
+ with antd.Typography.Text(
768
+ copyable=dict(tooltips=False),
769
+ as_item="copy_btn"):
770
+ with ms.Slot("copyable.icon"):
771
+ with antd.Button(
772
+ value=None,
773
+ size="small",
774
+ color="default",
775
+ variant="text"):
776
+ with ms.Slot("icon"):
777
+ antd.Icon(
778
+ "CopyOutlined")
779
+ with antd.Button(
780
+ value=None,
781
+ size="small",
782
+ color="default",
783
+ variant="text"):
784
+ with ms.Slot("icon"):
785
+ antd.Icon(
786
+ "CheckOutlined")
787
+
788
+ with antd.Popconfirm(
789
+ title=get_text(
790
+ "Regenerate the message",
791
+ "重新生成消息"),
792
+ description=get_text(
793
+ "Regenerate the message will also delete all subsequent messages.",
794
+ "重新生成消息将会删除所有的后续消息。"),
795
+ ok_button_props=dict(
796
+ danger=True),
797
+ as_item="regenerate_btn"
798
+ ) as chatbot_regenerate_popconfirm:
799
+ with antd.Button(
800
+ value=None,
801
+ size="small",
802
+ color="default",
803
+ variant="text",
804
+ as_item="regenerate_btn",
805
+ ):
806
+ with ms.Slot("icon"):
807
+ antd.Icon("SyncOutlined")
808
+ with antd.Button(value=None,
809
+ size="small",
810
+ color="default",
811
+ variant="text",
812
+ as_item="edit_btn"
813
+ ) as chatbot_edit_btn:
814
+ with ms.Slot("icon"):
815
+ antd.Icon("EditOutlined")
816
+ with antd.Popconfirm(
817
+ title=get_text("Delete the message", "删除消息"),
818
+ description=get_text(
819
+ "Are you sure to delete this message?",
820
+ "确定要删除这条消息吗?"),
821
+ ok_button_props=dict(
822
+ danger=True),
823
+ as_item="delete_btn"
824
+ ) as chatbot_delete_popconfirm:
825
+ with antd.Button(
826
+ value=None,
827
+ size="small",
828
+ color="default",
829
+ variant="text",
830
+ as_item="delete_btn"):
831
+ with ms.Slot("icon"):
832
+ antd.Icon("DeleteOutlined")
833
+
834
+ # Sender
835
+ with antdx.Suggestion(
836
+ items=DEFAULT_SUGGESTIONS,
837
+ # onKeyDown Handler in Javascript
838
+ should_trigger="""(e, { onTrigger, onKeyDown }) => {
839
+ switch(e.key) {
840
+ case '/':
841
+ onTrigger()
842
+ break
843
+ case 'ArrowRight':
844
+ case 'ArrowLeft':
845
+ case 'ArrowUp':
846
+ case 'ArrowDown':
847
+ break;
848
+ default:
849
+ onTrigger(false)
850
+ }
851
+ onKeyDown(e)
852
+ }""") as suggestion:
853
+ with ms.Slot("children"):
854
+ with antdx.Sender(placeholder=get_text(
855
+ "Enter / to get suggestions",
856
+ "输入 / 获取建议"), ) as sender:
857
+ with ms.Slot("prefix"):
858
+ # Clear Button
859
+ with antd.Tooltip(title=get_text(
860
+ "Clear Conversation History",
861
+ "清空对话历史"), ):
862
+ with antd.Button(
863
+ value=None,
864
+ type="text") as clear_btn:
865
+ with ms.Slot("icon"):
866
+ antd.Icon("ClearOutlined")
867
+
868
+ # Modals
869
+ with antd.Modal(title=get_text("Edit Message", "编辑消息"),
870
+ open=False,
871
+ centered=True,
872
+ width="60%") as edit_modal:
873
+ edit_textarea = antd.Input.Textarea(auto_size=dict(minRows=2,
874
+ maxRows=6),
875
+ elem_style=dict(width="100%"))
876
+ # Events Handler
877
+ if save_history:
878
+ browser_state = gr.BrowserState(
879
+ {
880
+ "conversations_history": {},
881
+ "conversations": [],
882
+ },
883
+ storage_key="qwen_qwq_chatbot_storage")
884
+ state.change(fn=Gradio_Events.update_browser_state,
885
+ inputs=[state],
886
+ outputs=[browser_state])
887
+
888
+ demo.load(fn=Gradio_Events.apply_browser_state,
889
+ inputs=[browser_state, state],
890
+ outputs=[conversations, state])
891
+
892
+ add_conversation_btn.click(fn=Gradio_Events.new_chat,
893
+ inputs=[state],
894
+ outputs=[conversations, chatbot, state])
895
+ conversations.active_change(fn=Gradio_Events.select_conversation,
896
+ inputs=[state],
897
+ outputs=[conversations, chatbot, state])
898
+ conversations.menu_click(fn=Gradio_Events.click_conversation_menu,
899
+ inputs=[state],
900
+ outputs=[conversations, chatbot, state])
901
+ prompts.item_click(fn=Gradio_Events.apply_prompt, outputs=[sender])
902
+
903
+ clear_btn.click(fn=Gradio_Events.clear_conversation_history,
904
+ inputs=[state],
905
+ outputs=[chatbot, state])
906
+
907
+ suggestion.select(fn=Gradio_Events.select_suggestion,
908
+ inputs=[sender],
909
+ outputs=[sender])
910
+
911
+ gr.on(triggers=[user_edit_btn.click, chatbot_edit_btn.click],
912
+ fn=Gradio_Events.edit_message,
913
+ inputs=[state],
914
+ outputs=[edit_textarea, state]).then(fn=Gradio_Events.open_modal,
915
+ outputs=[edit_modal])
916
+ edit_modal.ok(fn=Gradio_Events.confirm_edit_message,
917
+ inputs=[edit_textarea, state],
918
+ outputs=[chatbot, state]).then(fn=Gradio_Events.close_modal,
919
+ outputs=[edit_modal])
920
+ edit_modal.cancel(fn=Gradio_Events.close_modal, outputs=[edit_modal])
921
+ gr.on(triggers=[
922
+ chatbot_delete_popconfirm.confirm, user_delete_popconfirm.confirm
923
+ ],
924
+ fn=Gradio_Events.delete_message,
925
+ inputs=[state],
926
+ outputs=[chatbot, state])
927
+
928
+ regenerating_event = chatbot_regenerate_popconfirm.confirm(
929
+ fn=Gradio_Events.regenerate_message,
930
+ inputs=[state],
931
+ outputs=[sender, clear_btn, conversation_delete_menu_item, add_conversation_btn, conversations, chatbot, state])
932
+
933
+ submit_event = sender.submit(fn=Gradio_Events.submit,
934
+ inputs=[sender, state],
935
+ outputs=[sender, clear_btn, conversation_delete_menu_item,
936
+ add_conversation_btn, conversations,chatbot, state])
937
+ sender.cancel(fn=None, cancels=[submit_event, regenerating_event])
938
+ sender.cancel(fn=Gradio_Events.cancel,
939
+ inputs=[state],
940
+ outputs=[
941
+ sender, conversation_delete_menu_item, clear_btn,
942
+ conversations, add_conversation_btn, chatbot, state
943
+ ])
944
 
945
  if __name__ == "__main__":
946
+ demo.queue(default_concurrency_limit=200).launch(ssr_mode=False, max_threads=200)