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