jiangjiechen commited on
Commit
54f08eb
·
1 Parent(s): b6d36be

update layout

Browse files
Files changed (1) hide show
  1. app.py +367 -41
app.py CHANGED
@@ -1,58 +1,384 @@
1
  import gradio as gr
2
  import tiktoken
3
  import json
 
4
 
5
- def count_tokens(text):
6
- """
7
- 计算输入文本中的 token 数量,并根据用户选择格式化文本。
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
 
9
- Args:
10
- text (str): 输入文本。
11
- use_markdown (bool): 是否使用 Markdown/LaTeX 格式输出。
 
 
12
 
13
  Returns:
14
- tuple: 返回 token 数量和格式化后的文本。
15
  """
 
 
 
16
  encoding = tiktoken.encoding_for_model("gpt-4")
17
  tokens = encoding.encode(text)
18
 
 
 
 
 
19
  try:
20
  parsed_json = json.loads(text)
21
- text = json.dumps(parsed_json, indent=4, ensure_ascii=False)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
  except json.JSONDecodeError:
23
- pass
24
-
25
- text = text.replace("\\n", "\n")
26
-
27
- formatted_text = text
28
-
29
- return len(tokens), gr.update(value=formatted_text)
30
-
31
- # 定义 Gradio 接口
32
- iface = gr.Interface(
33
- fn=count_tokens,
34
- inputs=[
35
- gr.Textbox(
36
- lines=10,
37
- max_lines=1000000,
38
- placeholder="Enter your text here..."
39
- ),
40
- # gr.Checkbox(label="使用 Markdown/LaTeX 格式输出", value=True) # 格��选择开关
41
- ],
42
- outputs=[
43
- "number",
44
- gr.Markdown(label="Beautified Text")
45
- ],
46
- title="Token Counter with tiktoken",
47
- description="Enter text below to calculate the number of tokens using the tiktoken library. Supports LaTeX formulas using $ for inline and $$ for block formulas.",
48
- examples=[
49
- ["这是一个行内公式示例:$E=mc^2$"],
50
- ["这是一个块级公式示例:$$\\sum_{i=1}^n i = \\frac{n(n+1)}{2}$$"],
51
- ["这是混合示例:\n行内公式:$\\alpha + \\beta$\n块级公式:$$\\int_0^\\infty e^{-x^2} dx = \\frac{\\sqrt{\\pi}}{2}$$"],
52
- ["普通文本示例:Hello, how are you doing today?"],
53
- ],
54
- theme="default"
55
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56
 
57
  # 启动应用
58
  if __name__ == "__main__":
 
1
  import gradio as gr
2
  import tiktoken
3
  import json
4
+ import re
5
 
6
+ def highlight_json_keys(json_str):
7
+ """为JSON字符串中的键添加高亮显示"""
8
+ def replace_key(match):
9
+ key = match.group(1)
10
+ return f'<span style="color: #0066cc; font-weight: bold;">"{key}"</span>:'
11
+
12
+ # 匹配JSON键的正则表达式
13
+ key_pattern = r'"([^"]+)"(?=\s*:)'
14
+ highlighted = re.sub(key_pattern, replace_key, json_str)
15
+ return highlighted
16
+
17
+ def normalize_latex(text):
18
+ """标准化LaTeX公式格式,支持多种标记形式"""
19
+ # 将 \( \) 转换为 $ $ (行内公式,不跨行)
20
+ # text = re.sub(r'\\\((.*?)\\\)', r'$\1$', text)
21
+
22
+ # # 将 \[ \] 转换为 $$ $$ (块级公式,允许跨行)
23
+ # text = re.sub(r'\\\[(.*?)\\\]', r'$$\1$$', text, flags=re.DOTALL)
24
+
25
+ return text
26
+
27
+ def format_json_with_syntax_highlighting(json_obj, indent=0):
28
+ """格式化JSON并添加语法高亮"""
29
+ def format_value(value, current_indent):
30
+ if isinstance(value, dict):
31
+ if not value:
32
+ return "{}"
33
+ items = []
34
+ for k, v in value.items():
35
+ key_str = f'<br><span style="color: #0066cc; font-weight: bold; background-color: #f0f8ff; padding: 2px 4px; border-radius: 3px; margin: 2px 0; display: inline-block;">"{k}"</span>'
36
+ value_str = format_value(v, current_indent + 2)
37
+ items.append(f"{key_str}: {value_str}")
38
+ return "{" + "".join(items) + "<br>}"
39
+ elif isinstance(value, list):
40
+ if not value:
41
+ return "[]"
42
+ items = []
43
+ for item in value:
44
+ items.append(f"<br>{format_value(item, current_indent + 2)}")
45
+ return "[" + "".join(items) + "<br>]"
46
+ elif isinstance(value, str):
47
+ return f'<span style="color: #008000;">"{value}"</span>'
48
+ elif isinstance(value, bool):
49
+ return f'<span style="color: #0000ff;">{str(value).lower()}</span>'
50
+ elif isinstance(value, (int, float)):
51
+ return f'<span style="color: #ff0000;">{value}</span>'
52
+ elif value is None:
53
+ return '<span style="color: #808080;">null</span>'
54
+ else:
55
+ return str(value)
56
 
57
+ return format_value(json_obj, indent)
58
+
59
+ def count_tokens_and_format(text):
60
+ """
61
+ 计算输入文本中的 token 数量,并返回不同格式的输出。
62
 
63
  Returns:
64
+ tuple: (token_count, html_output, markdown_output)
65
  """
66
+ if not text.strip():
67
+ return 0, "", ""
68
+
69
  encoding = tiktoken.encoding_for_model("gpt-4")
70
  tokens = encoding.encode(text)
71
 
72
+ # 先尝试解析JSON (原始文本)
73
+ is_json = False
74
+ json_text = text
75
+ text = text.replace('\\\\', '\\')
76
  try:
77
  parsed_json = json.loads(text)
78
+ # JSON使用HTML格式以保持语法高亮
79
+ json_formatted = format_json_with_syntax_highlighting(parsed_json)
80
+ html_output = f'<div class="json-container">{json_formatted}</div>'
81
+ # JSON的markdown版本(无高亮)
82
+ markdown_output = f"```json\n{json.dumps(parsed_json, indent=2, ensure_ascii=False)}\n```"
83
+ is_json = True
84
+ except json.JSONDecodeError:
85
+ # 普通文本处理LaTeX - 只对非JSON文本处理换行符
86
+ processed_text = text.replace("\\n", "\n")
87
+ formatted_text = normalize_latex(processed_text)
88
+
89
+ if len(text) > 1000 or text.count('\n') > 20:
90
+ html_output = f'<div class="long-text-container">{formatted_text}</div>'
91
+ else:
92
+ html_output = f'<div class="text-container">{formatted_text}</div>'
93
+
94
+ # Markdown输出保持LaTeX格式
95
+ markdown_output = formatted_text
96
+
97
+ return len(tokens), html_output, markdown_output
98
+
99
+ def count_tokens(text):
100
+ """主函数:返回token数量和格式化输出"""
101
+ tokens, html_out, markdown_out = count_tokens_and_format(text)
102
+
103
+ # 检查是否包含LaTeX或是JSON
104
+ try:
105
+ json.loads(text.replace("\\n", "\n"))
106
+ # 是JSON,返回HTML版本
107
+ return tokens, html_out
108
  except json.JSONDecodeError:
109
+ # 检查是否包含LaTeX
110
+ if '$' in text or '\\(' in text or '\\[' in text:
111
+ # 包含LaTeX,返回markdown版本
112
+ return tokens, markdown_out
113
+ else:
114
+ # 普通文本,返回HTML版本获得更好的样式
115
+ return tokens, html_out
116
+
117
+ def create_custom_css():
118
+ """创建自定义CSS样式"""
119
+ return """
120
+ .gradio-container {
121
+ max-width: 100% !important;
122
+ width: 100% !important;
123
+ margin: 0 auto !important;
124
+ padding: 20px !important;
125
+ font-family: Arial, sans-serif !important;
126
+ }
127
+
128
+ @media (min-width: 768px) {
129
+ .gradio-container {
130
+ max-width: 1200px !important;
131
+ }
132
+ }
133
+
134
+ .input-container textarea {
135
+ resize: vertical !important;
136
+ min-height: 200px !important;
137
+ max-height: 500px !important;
138
+ }
139
+
140
+ .output-container {
141
+ min-height: 200px !important;
142
+ max-height: 600px !important;
143
+ overflow-y: auto !important;
144
+ }
145
+
146
+ .gradio-row {
147
+ display: flex !important;
148
+ flex-wrap: wrap !important;
149
+ gap: 20px !important;
150
+ align-items: flex-start !important;
151
+ }
152
+
153
+ .gradio-column {
154
+ flex: 1 !important;
155
+ min-width: 300px !important;
156
+ }
157
+
158
+ /* 响应式设计 */
159
+ @media (max-width: 768px) {
160
+ .gradio-column {
161
+ min-width: 100% !important;
162
+ }
163
+ .gradio-container {
164
+ padding: 10px !important;
165
+ }
166
+ }
167
+
168
+ /* 长文本容器样式优化 */
169
+ .long-text-container {
170
+ max-height: 500px !important;
171
+ overflow-y: auto !important;
172
+ padding: 15px !important;
173
+ border: 1px solid #ddd !important;
174
+ border-radius: 8px !important;
175
+ background-color: #f8f9fa !important;
176
+ font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace !important;
177
+ font-size: 14px !important;
178
+ line-height: 1.5 !important;
179
+ white-space: pre-wrap !important;
180
+ word-wrap: break-word !important;
181
+ box-sizing: border-box !important;
182
+ }
183
+
184
+ .long-text-container::-webkit-scrollbar {
185
+ width: 8px !important;
186
+ }
187
+
188
+ .long-text-container::-webkit-scrollbar-track {
189
+ background: #f1f1f1 !important;
190
+ border-radius: 4px !important;
191
+ }
192
+
193
+ .long-text-container::-webkit-scrollbar-thumb {
194
+ background: #888 !important;
195
+ border-radius: 4px !important;
196
+ }
197
+
198
+ .long-text-container::-webkit-scrollbar-thumb:hover {
199
+ background: #555 !important;
200
+ }
201
+
202
+ /* 普通文本容器样式 */
203
+ .text-container {
204
+ padding: 15px !important;
205
+ border: 1px solid #ddd !important;
206
+ border-radius: 8px !important;
207
+ background-color: #fff !important;
208
+ font-family: Arial, sans-serif !important;
209
+ font-size: 14px !important;
210
+ line-height: 1.6 !important;
211
+ white-space: pre-wrap !important;
212
+ word-wrap: break-word !important;
213
+ box-sizing: border-box !important;
214
+ }
215
+
216
+ /* JSON容器样式 */
217
+ .json-container {
218
+ max-height: 500px !important;
219
+ overflow-y: auto !important;
220
+ padding: 15px !important;
221
+ border: 1px solid #ddd !important;
222
+ border-radius: 8px !important;
223
+ background-color: #f8f9fa !important;
224
+ font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace !important;
225
+ font-size: 14px !important;
226
+ line-height: 1.8 !important;
227
+ white-space: normal !important;
228
+ word-wrap: break-word !important;
229
+ box-sizing: border-box !important;
230
+ }
231
+
232
+ .json-container::-webkit-scrollbar {
233
+ width: 8px !important;
234
+ }
235
+
236
+ .json-container::-webkit-scrollbar-track {
237
+ background: #f1f1f1 !important;
238
+ border-radius: 4px !important;
239
+ }
240
+
241
+ .json-container::-webkit-scrollbar-thumb {
242
+ background: #888 !important;
243
+ border-radius: 4px !important;
244
+ }
245
+
246
+ .json-container::-webkit-scrollbar-thumb:hover {
247
+ background: #555 !important;
248
+ }
249
+ """
250
+
251
+ # 创建处理函数
252
+ def process_text(text):
253
+ """处理文本并返回适当的输出格式"""
254
+ if not text.strip():
255
+ return 0, "", ""
256
+
257
+ tokens, html_out, markdown_out = count_tokens_and_format(text)
258
+
259
+ # 检查内容类型
260
+ try:
261
+ json.loads(text)
262
+ # JSON: 显示HTML,隐藏Markdown
263
+ return tokens, html_out, ""
264
+ except json.JSONDecodeError:
265
+ # 检查是否包含LaTeX
266
+ if '$' in text or '\\(' in text or '\\[' in text:
267
+ # LaTeX: 显示Markdown,隐藏HTML
268
+ return tokens, "", markdown_out
269
+ else:
270
+ # 普通文本: 显示HTML,隐藏Markdown
271
+ return tokens, html_out, ""
272
+
273
+ # 使用 Blocks 创建更灵活的布局
274
+ with gr.Blocks(
275
+ theme="soft",
276
+ css=create_custom_css(),
277
+ title="🔢 智能 Token 计数器"
278
+ ) as iface:
279
+
280
+ gr.HTML("""
281
+ <div style="text-align: center; margin: 10px 0;">
282
+ <h1 style="color: #333; margin-bottom: 10px;">🔢 智能 Token 计数器</h1>
283
+ <p style="font-size: 16px; color: #666; margin-bottom: 20px;">
284
+ 使用 tiktoken 库计算文本的 token 数量,支持:
285
+ </p>
286
+ </div>
287
+ """)
288
+
289
+ with gr.Row():
290
+ # 左列:输入和token计数
291
+ with gr.Column(scale=1):
292
+ input_text = gr.Textbox(
293
+ lines=15,
294
+ max_lines=50,
295
+ placeholder="""输入您的文本,支持JSON格式和LaTeX公式...
296
+
297
+ 示例:
298
+ • JSON: {"key": "value"}
299
+ • LaTeX行内公式: \\(E=mc^2\\) 或 $E=mc^2$
300
+ • LaTeX块级公式: \\[\\sum_{i=1}^n i\\] 或 $$\\sum_{i=1}^n i$$""",
301
+ label="输入文本",
302
+ show_label=True,
303
+ container=True,
304
+ elem_classes=["input-container"]
305
+ )
306
+
307
+ token_count = gr.Number(
308
+ label="Token 数量",
309
+ precision=0,
310
+ show_label=True,
311
+ interactive=False
312
+ )
313
+
314
+ # 右列:渲染输出
315
+ with gr.Column(scale=1):
316
+ # HTML 输出(用于JSON和普通文本)
317
+ html_output = gr.HTML(
318
+ label="格式化文本",
319
+ show_label=True,
320
+ elem_classes=["output-container"],
321
+ visible=True
322
+ )
323
+
324
+ # Markdown 输出(用于LaTeX)
325
+ markdown_output = gr.Markdown(
326
+ label="格式化文本",
327
+ show_label=True,
328
+ elem_classes=["output-container"],
329
+ visible=False,
330
+ latex_delimiters=[
331
+ {"left": "$", "right": "$", "display": False},
332
+ {"left": "$$", "right": "$$", "display": True},
333
+ {"left": "\\(", "right": "\\)", "display": False},
334
+ {"left": "\\[", "right": "\\]", "display": True}
335
+ ]
336
+ )
337
+
338
+ # 示例区域
339
+ gr.Examples(
340
+ examples=[
341
+ ['{"name": "Claude", "version": "3.5", "features": ["JSON解析", "LaTeX支持", "智能格式化"]}'],
342
+ ["这是一个行内公式示例:\\(E=mc^2\\) 和另一个 $F=ma$"],
343
+ ["这是一个块级公式示例:\\[\\sum_{i=1}^n i = \\frac{n(n+1)}{2}\\]"],
344
+ ["混合示例包含文本、JSON和LaTeX:\n这里是普通文本。\n\n这里是JSON:{\"message\": \"Hello World\", \"count\": 42}\n\n这里是公式:\\(\\alpha + \\beta = \\gamma\\)\n\n块级公式:\\[\\int_0^\\infty e^{-x^2} dx = \\frac{\\sqrt{\\pi}}{2}\\]"],
345
+ ],
346
+ inputs=input_text,
347
+ label="点击示例快速体验"
348
+ )
349
+
350
+ # 动态更新函数
351
+ def update_outputs(text):
352
+ tokens, html_content, markdown_content = process_text(text)
353
+
354
+ if html_content and not markdown_content:
355
+ # 显示HTML,隐藏Markdown
356
+ return (
357
+ tokens,
358
+ gr.update(value=html_content, visible=True),
359
+ gr.update(value="", visible=False)
360
+ )
361
+ elif markdown_content and not html_content:
362
+ # 显示Markdown,隐藏HTML
363
+ return (
364
+ tokens,
365
+ gr.update(value="", visible=False),
366
+ gr.update(value=markdown_content, visible=True)
367
+ )
368
+ else:
369
+ # 默认显示HTML
370
+ return (
371
+ tokens,
372
+ gr.update(value="", visible=True),
373
+ gr.update(value="", visible=False)
374
+ )
375
+
376
+ # 绑定事件
377
+ input_text.change(
378
+ fn=update_outputs,
379
+ inputs=input_text,
380
+ outputs=[token_count, html_output, markdown_output]
381
+ )
382
 
383
  # 启动应用
384
  if __name__ == "__main__":