project-baize commited on
Commit
f5164b3
1 Parent(s): 6bea299

Upload 14 files

Browse files
app_modules/__pycache__/chat_func.cpython-38.pyc ADDED
Binary file (605 Bytes). View file
 
app_modules/__pycache__/llama_func.cpython-38.pyc ADDED
Binary file (4.62 kB). View file
 
app_modules/__pycache__/openai_func.cpython-38.pyc ADDED
Binary file (1.8 kB). View file
 
app_modules/__pycache__/overwrites.cpython-38.pyc ADDED
Binary file (2.6 kB). View file
 
app_modules/__pycache__/presets.cpython-38.pyc ADDED
Binary file (2.26 kB). View file
 
app_modules/__pycache__/shared.cpython-38.pyc ADDED
Binary file (1.08 kB). View file
 
app_modules/__pycache__/utils.cpython-38.pyc ADDED
Binary file (9.99 kB). View file
 
app_modules/overwrites.py ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+ import logging
3
+
4
+ from llama_index import Prompt
5
+ from typing import List, Tuple
6
+ import mdtex2html
7
+
8
+ from app_modules.presets import *
9
+ from app_modules.utils import *
10
+
11
+ def compact_text_chunks(self, prompt: Prompt, text_chunks: List[str]) -> List[str]:
12
+ logging.debug("Compacting text chunks...🚀🚀🚀")
13
+ combined_str = [c.strip() for c in text_chunks if c.strip()]
14
+ combined_str = [f"[{index+1}] {c}" for index, c in enumerate(combined_str)]
15
+ combined_str = "\n\n".join(combined_str)
16
+ # resplit based on self.max_chunk_overlap
17
+ text_splitter = self.get_text_splitter_given_prompt(prompt, 1, padding=1)
18
+ return text_splitter.split_text(combined_str)
19
+
20
+
21
+ def postprocess(
22
+ self, y: List[Tuple[str | None, str | None]]
23
+ ) -> List[Tuple[str | None, str | None]]:
24
+ """
25
+ Parameters:
26
+ y: List of tuples representing the message and response pairs. Each message and response should be a string, which may be in Markdown format.
27
+ Returns:
28
+ List of tuples representing the message and response. Each message and response will be a string of HTML.
29
+ """
30
+ if y is None or y == []:
31
+ return []
32
+ temp = []
33
+ for x in y:
34
+ user, bot = x
35
+ if not detect_converted_mark(user):
36
+ user = convert_asis(user)
37
+ if not detect_converted_mark(bot):
38
+ bot = convert_mdtext(bot)
39
+ temp.append((user, bot))
40
+ return temp
41
+
42
+ with open("./assets/custom.js", "r", encoding="utf-8") as f, open("./assets/Kelpy-Codos.js", "r", encoding="utf-8") as f2:
43
+ customJS = f.read()
44
+ kelpyCodos = f2.read()
45
+
46
+ def reload_javascript():
47
+ print("Reloading javascript...")
48
+ js = f'<script>{customJS}</script><script>{kelpyCodos}</script>'
49
+ def template_response(*args, **kwargs):
50
+ res = GradioTemplateResponseOriginal(*args, **kwargs)
51
+ res.body = res.body.replace(b'</html>', f'{js}</html>'.encode("utf8"))
52
+ res.init_headers()
53
+ return res
54
+
55
+ gr.routes.templates.TemplateResponse = template_response
56
+
57
+ GradioTemplateResponseOriginal = gr.routes.templates.TemplateResponse
app_modules/presets.py ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding:utf-8 -*-
2
+ import gradio as gr
3
+
4
+
5
+ title = """<h1 align="left" style="min-width:200px; margin-top:0;"> <img src="https://raw.githubusercontent.com/twitter/twemoji/master/assets/svg/1f432.svg" width="32px"> Baize </h1>"""
6
+ description_top = """\
7
+ <div align="left" style="margin:16px 0">
8
+ Disclaimer: The LLaMA model is a third-party version available on Hugging Face model hub. This demo should be used for research purposes only. Commercial use is strictly prohibited. The model output is not censored and the authors do not endorse the opinions in the generated content. Use at your own risk.
9
+ </div>
10
+ """
11
+ description = """\
12
+ <div align="center" style="margin:16px 0">
13
+ The demo is built on <a href="https://github.com/GaiZhenbiao/ChuanhuChatGPT">ChuanhuChatGPT</a>.
14
+ </div>
15
+ """
16
+ CONCURRENT_COUNT = 100
17
+
18
+
19
+ ALREADY_CONVERTED_MARK = "<!-- ALREADY CONVERTED BY PARSER. -->"
20
+
21
+ small_and_beautiful_theme = gr.themes.Soft(
22
+ primary_hue=gr.themes.Color(
23
+ c50="#02C160",
24
+ c100="rgba(2, 193, 96, 0.2)",
25
+ c200="#02C160",
26
+ c300="rgba(2, 193, 96, 0.32)",
27
+ c400="rgba(2, 193, 96, 0.32)",
28
+ c500="rgba(2, 193, 96, 1.0)",
29
+ c600="rgba(2, 193, 96, 1.0)",
30
+ c700="rgba(2, 193, 96, 0.32)",
31
+ c800="rgba(2, 193, 96, 0.32)",
32
+ c900="#02C160",
33
+ c950="#02C160",
34
+ ),
35
+ secondary_hue=gr.themes.Color(
36
+ c50="#576b95",
37
+ c100="#576b95",
38
+ c200="#576b95",
39
+ c300="#576b95",
40
+ c400="#576b95",
41
+ c500="#576b95",
42
+ c600="#576b95",
43
+ c700="#576b95",
44
+ c800="#576b95",
45
+ c900="#576b95",
46
+ c950="#576b95",
47
+ ),
48
+ neutral_hue=gr.themes.Color(
49
+ name="gray",
50
+ c50="#f9fafb",
51
+ c100="#f3f4f6",
52
+ c200="#e5e7eb",
53
+ c300="#d1d5db",
54
+ c400="#B2B2B2",
55
+ c500="#808080",
56
+ c600="#636363",
57
+ c700="#515151",
58
+ c800="#393939",
59
+ c900="#272727",
60
+ c950="#171717",
61
+ ),
62
+ radius_size=gr.themes.sizes.radius_sm,
63
+ ).set(
64
+ button_primary_background_fill="#06AE56",
65
+ button_primary_background_fill_dark="#06AE56",
66
+ button_primary_background_fill_hover="#07C863",
67
+ button_primary_border_color="#06AE56",
68
+ button_primary_border_color_dark="#06AE56",
69
+ button_primary_text_color="#FFFFFF",
70
+ button_primary_text_color_dark="#FFFFFF",
71
+ button_secondary_background_fill="#F2F2F2",
72
+ button_secondary_background_fill_dark="#2B2B2B",
73
+ button_secondary_text_color="#393939",
74
+ button_secondary_text_color_dark="#FFFFFF",
75
+ # background_fill_primary="#F7F7F7",
76
+ # background_fill_primary_dark="#1F1F1F",
77
+ block_title_text_color="*primary_500",
78
+ block_title_background_fill="*primary_100",
79
+ input_background_fill="#F6F6F6",
80
+ )
app_modules/utils.py ADDED
@@ -0,0 +1,366 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding:utf-8 -*-
2
+ from __future__ import annotations
3
+ from typing import TYPE_CHECKING, Any, Callable, Dict, List, Tuple, Type
4
+ import logging
5
+ import json
6
+ import os
7
+ import datetime
8
+ import hashlib
9
+ import csv
10
+ import requests
11
+ import re
12
+ import html
13
+ import markdown2
14
+ import torch
15
+ import sys
16
+ from pygments.lexers import guess_lexer, ClassNotFound
17
+
18
+ import gradio as gr
19
+ from pypinyin import lazy_pinyin
20
+ import tiktoken
21
+ import mdtex2html
22
+ from markdown import markdown
23
+ from pygments import highlight
24
+ from pygments.lexers import guess_lexer,get_lexer_by_name
25
+ from pygments.formatters import HtmlFormatter
26
+ import transformers
27
+ from peft import PeftModel
28
+ from transformers import GenerationConfig, LlamaForCausalLM, LlamaTokenizer
29
+
30
+ from app_modules.presets import *
31
+
32
+ logging.basicConfig(
33
+ level=logging.INFO,
34
+ format="%(asctime)s [%(levelname)s] [%(filename)s:%(lineno)d] %(message)s",
35
+ )
36
+
37
+
38
+ def markdown_to_html_with_syntax_highlight(md_str):
39
+ def replacer(match):
40
+ lang = match.group(1) or "text"
41
+ code = match.group(2)
42
+ lang = lang.strip()
43
+ #print(1,lang)
44
+ if lang=="text":
45
+ lexer = guess_lexer(code)
46
+ lang = lexer.name
47
+ #print(2,lang)
48
+ try:
49
+ lexer = get_lexer_by_name(lang, stripall=True)
50
+ except ValueError:
51
+ lexer = get_lexer_by_name("python", stripall=True)
52
+ formatter = HtmlFormatter()
53
+ #print(3,lexer.name)
54
+ highlighted_code = highlight(code, lexer, formatter)
55
+
56
+ return f'<pre><code class="{lang}">{highlighted_code}</code></pre>'
57
+
58
+ code_block_pattern = r"```(\w+)?\n([\s\S]+?)\n```"
59
+ md_str = re.sub(code_block_pattern, replacer, md_str, flags=re.MULTILINE)
60
+
61
+ html_str = markdown(md_str)
62
+ return html_str
63
+
64
+
65
+ def normalize_markdown(md_text: str) -> str:
66
+ lines = md_text.split("\n")
67
+ normalized_lines = []
68
+ inside_list = False
69
+
70
+ for i, line in enumerate(lines):
71
+ if re.match(r"^(\d+\.|-|\*|\+)\s", line.strip()):
72
+ if not inside_list and i > 0 and lines[i - 1].strip() != "":
73
+ normalized_lines.append("")
74
+ inside_list = True
75
+ normalized_lines.append(line)
76
+ elif inside_list and line.strip() == "":
77
+ if i < len(lines) - 1 and not re.match(
78
+ r"^(\d+\.|-|\*|\+)\s", lines[i + 1].strip()
79
+ ):
80
+ normalized_lines.append(line)
81
+ continue
82
+ else:
83
+ inside_list = False
84
+ normalized_lines.append(line)
85
+
86
+ return "\n".join(normalized_lines)
87
+
88
+
89
+ def convert_mdtext(md_text):
90
+ code_block_pattern = re.compile(r"```(.*?)(?:```|$)", re.DOTALL)
91
+ inline_code_pattern = re.compile(r"`(.*?)`", re.DOTALL)
92
+ code_blocks = code_block_pattern.findall(md_text)
93
+ non_code_parts = code_block_pattern.split(md_text)[::2]
94
+
95
+ result = []
96
+ for non_code, code in zip(non_code_parts, code_blocks + [""]):
97
+ if non_code.strip():
98
+ non_code = normalize_markdown(non_code)
99
+ if inline_code_pattern.search(non_code):
100
+ result.append(markdown(non_code, extensions=["tables"]))
101
+ else:
102
+ result.append(mdtex2html.convert(non_code, extensions=["tables"]))
103
+ if code.strip():
104
+ # _, code = detect_language(code) # 暂时去除代码高亮功能,因为在大段代码的情况下会出现问题
105
+ # code = code.replace("\n\n", "\n") # 暂时去除代码中的空行,因为在大段代码的情况下会出现问题
106
+ code = f"\n```{code}\n\n```"
107
+ code = markdown_to_html_with_syntax_highlight(code)
108
+ result.append(code)
109
+ result = "".join(result)
110
+ result += ALREADY_CONVERTED_MARK
111
+ return result
112
+
113
+ def convert_asis(userinput):
114
+ return f"<p style=\"white-space:pre-wrap;\">{html.escape(userinput)}</p>"+ALREADY_CONVERTED_MARK
115
+
116
+ def detect_converted_mark(userinput):
117
+ if userinput.endswith(ALREADY_CONVERTED_MARK):
118
+ return True
119
+ else:
120
+ return False
121
+
122
+
123
+
124
+ def detect_language(code):
125
+ if code.startswith("\n"):
126
+ first_line = ""
127
+ else:
128
+ first_line = code.strip().split("\n", 1)[0]
129
+ language = first_line.lower() if first_line else ""
130
+ code_without_language = code[len(first_line) :].lstrip() if first_line else code
131
+ return language, code_without_language
132
+
133
+ def convert_to_markdown(text):
134
+ def replace_leading_tabs_and_spaces(line):
135
+ new_line = []
136
+
137
+ for char in line:
138
+ if char == "\t":
139
+ new_line.append("&#9;")
140
+ elif char == " ":
141
+ new_line.append("&nbsp;")
142
+ else:
143
+ break
144
+ return "".join(new_line) + line[len(new_line):]
145
+
146
+ markdown_text = ""
147
+ lines = text.split("\n")
148
+ in_code_block = False
149
+
150
+ for line in lines:
151
+ if in_code_block is False and line.startswith("```"):
152
+ in_code_block = True
153
+ markdown_text += "```\n"
154
+ elif in_code_block is True and line.startswith("```"):
155
+ in_code_block = False
156
+ markdown_text += "```\n"
157
+ elif in_code_block:
158
+ markdown_text += f"{line}\n"
159
+ else:
160
+ line = replace_leading_tabs_and_spaces(line)
161
+ line = re.sub(r"^(#)", r"\\\1", line)
162
+ markdown_text += f"{line} \n"
163
+
164
+ return markdown_text
165
+
166
+ def add_language_tag(text):
167
+ def detect_language(code_block):
168
+ try:
169
+ lexer = guess_lexer(code_block)
170
+ return lexer.name.lower()
171
+ except ClassNotFound:
172
+ return ""
173
+
174
+ code_block_pattern = re.compile(r"(```)(\w*\n[^`]+```)", re.MULTILINE)
175
+
176
+ def replacement(match):
177
+ code_block = match.group(2)
178
+ if match.group(2).startswith("\n"):
179
+ language = detect_language(code_block)
180
+ if language:
181
+ return f"```{language}{code_block}```"
182
+ else:
183
+ return f"```\n{code_block}```"
184
+ else:
185
+ return match.group(1) + code_block + "```"
186
+
187
+ text2 = code_block_pattern.sub(replacement, text)
188
+ return text2
189
+
190
+ def delete_last_conversation(chatbot, history):
191
+ if len(chatbot) > 0:
192
+ chatbot.pop()
193
+
194
+ if len(history) > 0:
195
+ history.pop()
196
+
197
+ return (
198
+ chatbot,
199
+ history,
200
+ "Delete Done",
201
+ )
202
+
203
+ def reset_state():
204
+ return [], [], "Reset Done"
205
+
206
+ def reset_textbox():
207
+ return gr.update(value=""),""
208
+
209
+ def cancel_outputing():
210
+ shared_state.interrupt()
211
+ textbox = reset_textbox()
212
+ return "Stop Done"
213
+
214
+ def transfer_input(inputs):
215
+ # 一次性返回,降低延迟
216
+ textbox = reset_textbox()
217
+ return (
218
+ inputs,
219
+ gr.update(value=""),
220
+ gr.Button.update(visible=True),
221
+ gr.Button.update(visible=True),
222
+ )
223
+
224
+
225
+ class State:
226
+ interrupted = False
227
+
228
+ def interrupt(self):
229
+ self.interrupted = True
230
+
231
+ def recover(self):
232
+ self.interrupted = False
233
+ shared_state = State()
234
+
235
+
236
+
237
+
238
+
239
+ # Greedy Search
240
+ def greedy_search(input_ids: torch.Tensor,
241
+ model: torch.nn.Module,
242
+ tokenizer: transformers.PreTrainedTokenizer,
243
+ stop_words: list,
244
+ max_length: int,
245
+ temperature: float = 1.0,
246
+ top_p: float = 1.0,
247
+ top_k: int = 25) -> Iterator[str]:
248
+ generated_tokens = []
249
+ past_key_values = None
250
+ current_length = 1
251
+ for i in range(max_length):
252
+ with torch.no_grad():
253
+ if past_key_values is None:
254
+ outputs = model(input_ids)
255
+ else:
256
+ outputs = model(input_ids[:, -1:], past_key_values=past_key_values)
257
+ logits = outputs.logits[:, -1, :]
258
+ past_key_values = outputs.past_key_values
259
+
260
+ # apply temperature
261
+ logits /= temperature
262
+
263
+ probs = torch.softmax(logits, dim=-1)
264
+ # apply top_p
265
+ probs_sort, probs_idx = torch.sort(probs, dim=-1, descending=True)
266
+ probs_sum = torch.cumsum(probs_sort, dim=-1)
267
+ mask = probs_sum - probs_sort > top_p
268
+ probs_sort[mask] = 0.0
269
+
270
+ # apply top_k
271
+ #if top_k is not None:
272
+ # probs_sort1, _ = torch.topk(probs_sort, top_k)
273
+ # min_top_probs_sort = torch.min(probs_sort1, dim=-1, keepdim=True).values
274
+ # probs_sort = torch.where(probs_sort < min_top_probs_sort, torch.full_like(probs_sort, float(0.0)), probs_sort)
275
+
276
+ probs_sort.div_(probs_sort.sum(dim=-1, keepdim=True))
277
+ next_token = torch.multinomial(probs_sort, num_samples=1)
278
+ next_token = torch.gather(probs_idx, -1, next_token)
279
+
280
+ input_ids = torch.cat((input_ids, next_token), dim=-1)
281
+
282
+ generated_tokens.append(next_token[0].item())
283
+ text = tokenizer.decode(generated_tokens)
284
+
285
+ yield text
286
+ if any([x in text for x in stop_words]):
287
+ return
288
+
289
+ def generate_prompt_with_history(text,history,tokenizer,max_length=2048):
290
+ prompt = "The following is a conversation between a human and an AI assistant named Baize (named after a mythical creature in Chinese folklore). Baize is an open-source AI assistant developed by UCSD and Sun Yat-Sen University. The human and the AI assistant take turns chatting. Human statements start with [|Human|] and AI assistant statements start with [|AI|]. The AI assistant always replies in Markdown format. The AI assistant (Baize) always declines to engage with topics, questions, or requests related to unethical, controversial, or sensitive issues. Complete the transcript in exactly that format.\n[|Human|]Hello!\n[|AI|]Hi! How can I help you?"
291
+ history = ["\n[|Human|]{}\n[|AI|]{}".format(x[0],x[1]) for x in history]
292
+ history.append("\n[|Human|]{}\n[|AI|]".format(text))
293
+ history_text = ""
294
+
295
+ for x in history[::-1]:
296
+ if tokenizer(prompt+history_text+x, return_tensors="pt")['input_ids'].size(-1) <= max_length:
297
+ history_text = x + history_text
298
+ flag = True
299
+ if flag:
300
+ return prompt+history_text,tokenizer(prompt+history_text, return_tensors="pt")
301
+ else:
302
+ return False
303
+
304
+
305
+ def is_stop_word_or_prefix(s: str, stop_words: list) -> bool:
306
+ for stop_word in stop_words:
307
+ if s.endswith(stop_word):
308
+ return True
309
+ for i in range(1, len(stop_word)):
310
+ if s.endswith(stop_word[:i]):
311
+ return True
312
+ return False
313
+
314
+
315
+
316
+ def load_tokenizer_and_model(base_model,adapter_model,load_8bit=False):
317
+ if torch.cuda.is_available():
318
+ device = "cuda"
319
+ else:
320
+ device = "cpu"
321
+
322
+ try:
323
+ if torch.backends.mps.is_available():
324
+ device = "mps"
325
+ except: # noqa: E722
326
+ pass
327
+ tokenizer = LlamaTokenizer.from_pretrained(base_model)
328
+ if device == "cuda":
329
+ model = LlamaForCausalLM.from_pretrained(
330
+ base_model,
331
+ load_in_8bit=load_8bit,
332
+ torch_dtype=torch.float16,
333
+ device_map="auto",
334
+ )
335
+ model = PeftModel.from_pretrained(
336
+ model,
337
+ adapter_model,
338
+ torch_dtype=torch.float16,
339
+ )
340
+ elif device == "mps":
341
+ model = LlamaForCausalLM.from_pretrained(
342
+ base_model,
343
+ device_map={"": device},
344
+ torch_dtype=torch.float16,
345
+ )
346
+ model = PeftModel.from_pretrained(
347
+ model,
348
+ adapter_model,
349
+ device_map={"": device},
350
+ torch_dtype=torch.float16,
351
+ )
352
+ else:
353
+ model = LlamaForCausalLM.from_pretrained(
354
+ base_model, device_map={"": device}, low_cpu_mem_usage=True
355
+ )
356
+ model = PeftModel.from_pretrained(
357
+ model,
358
+ adapter_model,
359
+ device_map={"": device},
360
+ )
361
+
362
+ if not load_8bit:
363
+ model.half() # seems to fix bugs for some users.
364
+
365
+ model.eval()
366
+ return tokenizer,model,device
assets/Kelpy-Codos.js ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // ==UserScript==
2
+ // @name Kelpy Codos
3
+ // @namespace https://github.com/Keldos-Li/Kelpy-Codos
4
+ // @version 1.0.5
5
+ // @author Keldos; https://keldos.me/
6
+ // @description Add copy button to PRE tags before CODE tag, for Chuanhu ChatGPT especially.
7
+ // Based on Chuanhu ChatGPT version: ac04408 (2023-3-22)
8
+ // @license GPL-3.0
9
+ // @grant none
10
+ // ==/UserScript==
11
+
12
+ (function () {
13
+ 'use strict';
14
+
15
+ function addCopyButton(pre) {
16
+ var code = pre.querySelector('code');
17
+ if (!code) {
18
+ return; // 如果没有找到 <code> 元素,则不添加按钮
19
+ }
20
+ var firstChild = code.firstChild;
21
+ if (!firstChild) {
22
+ return; // 如果 <code> 元素没有子节点,则不添加按钮
23
+ }
24
+ var button = document.createElement('button');
25
+ button.textContent = '\uD83D\uDCCE'; // 使用 📎 符号作为“复制”按钮的文本
26
+ button.style.position = 'relative';
27
+ button.style.float = 'right';
28
+ button.style.fontSize = '1em'; // 可选:调整按钮大小
29
+ button.style.background = 'none'; // 可选:去掉背景颜色
30
+ button.style.border = 'none'; // 可选:去掉边框
31
+ button.style.cursor = 'pointer'; // 可选:显示指针样式
32
+ button.addEventListener('click', function () {
33
+ var range = document.createRange();
34
+ range.selectNodeContents(code);
35
+ range.setStartBefore(firstChild); // 将范围设置为第一个子节点之前
36
+ var selection = window.getSelection();
37
+ selection.removeAllRanges();
38
+ selection.addRange(range);
39
+
40
+ try {
41
+ var success = document.execCommand('copy');
42
+ if (success) {
43
+ button.textContent = '\u2714';
44
+ setTimeout(function () {
45
+ button.textContent = '\uD83D\uDCCE'; // 恢复按钮为“复制”
46
+ }, 2000);
47
+ } else {
48
+ button.textContent = '\u2716';
49
+ }
50
+ } catch (e) {
51
+ console.error(e);
52
+ button.textContent = '\u2716';
53
+ }
54
+
55
+ selection.removeAllRanges();
56
+ });
57
+ code.insertBefore(button, firstChild); // 将按钮插入到第一个子元素之前
58
+ }
59
+
60
+ function handleNewElements(mutationsList, observer) {
61
+ for (var mutation of mutationsList) {
62
+ if (mutation.type === 'childList') {
63
+ for (var node of mutation.addedNodes) {
64
+ if (node.nodeName === 'PRE') {
65
+ addCopyButton(node);
66
+ }
67
+ }
68
+ }
69
+ }
70
+ }
71
+
72
+ var observer = new MutationObserver(handleNewElements);
73
+ observer.observe(document.documentElement, { childList: true, subtree: true });
74
+
75
+ document.querySelectorAll('pre').forEach(addCopyButton);
76
+ })();
assets/custom.css ADDED
@@ -0,0 +1,208 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ :root {
2
+ --chatbot-color-light: #F3F3F3;
3
+ --chatbot-color-dark: #121111;
4
+ }
5
+
6
+ /* status_display */
7
+ #status_display {
8
+ display: flex;
9
+ min-height: 2.5em;
10
+ align-items: flex-end;
11
+ justify-content: flex-end;
12
+ }
13
+ #status_display p {
14
+ font-size: .85em;
15
+ font-family: monospace;
16
+ color: var(--body-text-color-subdued);
17
+ }
18
+
19
+ #chuanhu_chatbot, #status_display {
20
+ transition: all 0.6s;
21
+ }
22
+
23
+ /* usage_display */
24
+ #usage_display {
25
+ height: 1em;
26
+ }
27
+ #usage_display p{
28
+ padding: 0 1em;
29
+ font-size: .85em;
30
+ font-family: monospace;
31
+ color: var(--body-text-color-subdued);
32
+ }
33
+ /* list */
34
+ ol:not(.options), ul:not(.options) {
35
+ padding-inline-start: 2em !important;
36
+ }
37
+
38
+ /* 亮色 */
39
+ @media (prefers-color-scheme: light) {
40
+ #chuanhu_chatbot {
41
+ background-color: var(--chatbot-color-light) !important;
42
+ }
43
+ [data-testid = "bot"] {
44
+ background-color: #FFFFFF !important;
45
+ }
46
+ [data-testid = "user"] {
47
+ background-color: #95EC69 !important;
48
+ }
49
+ }
50
+ /* 暗色 */
51
+ @media (prefers-color-scheme: dark) {
52
+ #chuanhu_chatbot {
53
+ background-color: var(--chatbot-color-dark) !important;
54
+ }
55
+ [data-testid = "bot"] {
56
+ background-color: #2C2C2C !important;
57
+ }
58
+ [data-testid = "user"] {
59
+ background-color: #26B561 !important;
60
+ }
61
+ body {
62
+ background-color: var(--neutral-950) !important;
63
+ }
64
+ }
65
+ /* 屏幕宽度大于等于500px的设备 */
66
+ @media screen and (min-width: 500px) {
67
+ #chuanhu_chatbot {
68
+ height: calc(100vh - 280px);
69
+ }
70
+ #chuanhu_chatbot .wrap {
71
+ max-height: calc(100vh - 280px - var(--line-sm)*1rem - 2*var(--block-label-margin) );
72
+ }
73
+ }
74
+ /* 屏幕宽度小于500px的设备 */
75
+ @media screen and (max-width: 499px) {
76
+ #chuanhu_chatbot {
77
+ height: calc(100vh - 220px);
78
+ }
79
+ #chuanhu_chatbot .wrap {
80
+ max-height: calc(100vh - 220px - var(--line-sm)*1rem - 2*var(--block-label-margin) );
81
+ }
82
+ }
83
+ /* 对话气泡 */
84
+ [class *= "message"] {
85
+ border-radius: var(--radius-xl) !important;
86
+ border: none;
87
+ padding: var(--spacing-xl) !important;
88
+ font-size: var(--text-md) !important;
89
+ line-height: var(--line-md) !important;
90
+ min-height: calc(var(--text-md)*var(--line-md) + 2*var(--spacing-xl));
91
+ min-width: calc(var(--text-md)*var(--line-md) + 2*var(--spacing-xl));
92
+ }
93
+ [data-testid = "bot"] {
94
+ max-width: 85%;
95
+ border-bottom-left-radius: 0 !important;
96
+ }
97
+ [data-testid = "user"] {
98
+ max-width: 85%;
99
+ width: auto !important;
100
+ border-bottom-right-radius: 0 !important;
101
+ }
102
+ /* 表格 */
103
+ table {
104
+ margin: 1em 0;
105
+ border-collapse: collapse;
106
+ empty-cells: show;
107
+ }
108
+ td,th {
109
+ border: 1.2px solid var(--border-color-primary) !important;
110
+ padding: 0.2em;
111
+ }
112
+ thead {
113
+ background-color: rgba(175,184,193,0.2);
114
+ }
115
+ thead th {
116
+ padding: .5em .2em;
117
+ }
118
+ /* 行内代码 */
119
+ code {
120
+ display: inline;
121
+ white-space: break-spaces;
122
+ border-radius: 6px;
123
+ margin: 0 2px 0 2px;
124
+ padding: .2em .4em .1em .4em;
125
+ background-color: rgba(175,184,193,0.2);
126
+ }
127
+ /* 代码块 */
128
+ pre code {
129
+ display: block;
130
+ overflow: auto;
131
+ white-space: pre;
132
+ background-color: hsla(0, 0%, 0%, 80%)!important;
133
+ border-radius: 10px;
134
+ padding: 1.4em 1.2em 0em 1.4em;
135
+ margin: 1.2em 2em 1.2em 0.5em;
136
+ color: #FFF;
137
+ box-shadow: 6px 6px 16px hsla(0, 0%, 0%, 0.2);
138
+ }
139
+ /* 代码高亮样式 */
140
+ .highlight .hll { background-color: #49483e }
141
+ .highlight .c { color: #75715e } /* Comment */
142
+ .highlight .err { color: #960050; background-color: #1e0010 } /* Error */
143
+ .highlight .k { color: #66d9ef } /* Keyword */
144
+ .highlight .l { color: #ae81ff } /* Literal */
145
+ .highlight .n { color: #f8f8f2 } /* Name */
146
+ .highlight .o { color: #f92672 } /* Operator */
147
+ .highlight .p { color: #f8f8f2 } /* Punctuation */
148
+ .highlight .ch { color: #75715e } /* Comment.Hashbang */
149
+ .highlight .cm { color: #75715e } /* Comment.Multiline */
150
+ .highlight .cp { color: #75715e } /* Comment.Preproc */
151
+ .highlight .cpf { color: #75715e } /* Comment.PreprocFile */
152
+ .highlight .c1 { color: #75715e } /* Comment.Single */
153
+ .highlight .cs { color: #75715e } /* Comment.Special */
154
+ .highlight .gd { color: #f92672 } /* Generic.Deleted */
155
+ .highlight .ge { font-style: italic } /* Generic.Emph */
156
+ .highlight .gi { color: #a6e22e } /* Generic.Inserted */
157
+ .highlight .gs { font-weight: bold } /* Generic.Strong */
158
+ .highlight .gu { color: #75715e } /* Generic.Subheading */
159
+ .highlight .kc { color: #66d9ef } /* Keyword.Constant */
160
+ .highlight .kd { color: #66d9ef } /* Keyword.Declaration */
161
+ .highlight .kn { color: #f92672 } /* Keyword.Namespace */
162
+ .highlight .kp { color: #66d9ef } /* Keyword.Pseudo */
163
+ .highlight .kr { color: #66d9ef } /* Keyword.Reserved */
164
+ .highlight .kt { color: #66d9ef } /* Keyword.Type */
165
+ .highlight .ld { color: #e6db74 } /* Literal.Date */
166
+ .highlight .m { color: #ae81ff } /* Literal.Number */
167
+ .highlight .s { color: #e6db74 } /* Literal.String */
168
+ .highlight .na { color: #a6e22e } /* Name.Attribute */
169
+ .highlight .nb { color: #f8f8f2 } /* Name.Builtin */
170
+ .highlight .nc { color: #a6e22e } /* Name.Class */
171
+ .highlight .no { color: #66d9ef } /* Name.Constant */
172
+ .highlight .nd { color: #a6e22e } /* Name.Decorator */
173
+ .highlight .ni { color: #f8f8f2 } /* Name.Entity */
174
+ .highlight .ne { color: #a6e22e } /* Name.Exception */
175
+ .highlight .nf { color: #a6e22e } /* Name.Function */
176
+ .highlight .nl { color: #f8f8f2 } /* Name.Label */
177
+ .highlight .nn { color: #f8f8f2 } /* Name.Namespace */
178
+ .highlight .nx { color: #a6e22e } /* Name.Other */
179
+ .highlight .py { color: #f8f8f2 } /* Name.Property */
180
+ .highlight .nt { color: #f92672 } /* Name.Tag */
181
+ .highlight .nv { color: #f8f8f2 } /* Name.Variable */
182
+ .highlight .ow { color: #f92672 } /* Operator.Word */
183
+ .highlight .w { color: #f8f8f2 } /* Text.Whitespace */
184
+ .highlight .mb { color: #ae81ff } /* Literal.Number.Bin */
185
+ .highlight .mf { color: #ae81ff } /* Literal.Number.Float */
186
+ .highlight .mh { color: #ae81ff } /* Literal.Number.Hex */
187
+ .highlight .mi { color: #ae81ff } /* Literal.Number.Integer */
188
+ .highlight .mo { color: #ae81ff } /* Literal.Number.Oct */
189
+ .highlight .sa { color: #e6db74 } /* Literal.String.Affix */
190
+ .highlight .sb { color: #e6db74 } /* Literal.String.Backtick */
191
+ .highlight .sc { color: #e6db74 } /* Literal.String.Char */
192
+ .highlight .dl { color: #e6db74 } /* Literal.String.Delimiter */
193
+ .highlight .sd { color: #e6db74 } /* Literal.String.Doc */
194
+ .highlight .s2 { color: #e6db74 } /* Literal.String.Double */
195
+ .highlight .se { color: #ae81ff } /* Literal.String.Escape */
196
+ .highlight .sh { color: #e6db74 } /* Literal.String.Heredoc */
197
+ .highlight .si { color: #e6db74 } /* Literal.String.Interpol */
198
+ .highlight .sx { color: #e6db74 } /* Literal.String.Other */
199
+ .highlight .sr { color: #e6db74 } /* Literal.String.Regex */
200
+ .highlight .s1 { color: #e6db74 } /* Literal.String.Single */
201
+ .highlight .ss { color: #e6db74 } /* Literal.String.Symbol */
202
+ .highlight .bp { color: #f8f8f2 } /* Name.Builtin.Pseudo */
203
+ .highlight .fm { color: #a6e22e } /* Name.Function.Magic */
204
+ .highlight .vc { color: #f8f8f2 } /* Name.Variable.Class */
205
+ .highlight .vg { color: #f8f8f2 } /* Name.Variable.Global */
206
+ .highlight .vi { color: #f8f8f2 } /* Name.Variable.Instance */
207
+ .highlight .vm { color: #f8f8f2 } /* Name.Variable.Magic */
208
+ .highlight .il { color: #ae81ff } /* Literal.Number.Integer.Long */
assets/custom.js ADDED
@@ -0,0 +1 @@
 
 
1
+ // custom javascript here
assets/favicon.ico ADDED