ZongqianLi commited on
Commit
c2a2581
·
verified ·
1 Parent(s): 78ca0bd

Upload 14 files

Browse files
Files changed (5) hide show
  1. app.py +10 -0
  2. bs_reasoning.py +7 -1
  3. templates/index.html +4 -1
  4. templates/index_cn.html +843 -0
  5. tot_reasoning.py +7 -1
app.py CHANGED
@@ -43,6 +43,16 @@ def index():
43
  """Render the main page"""
44
  return render_template('index.html')
45
 
 
 
 
 
 
 
 
 
 
 
46
  @app.route('/config')
47
  def get_config():
48
  """Get initial configuration"""
 
43
  """Render the main page"""
44
  return render_template('index.html')
45
 
46
+ @app.route('/index.html')
47
+ def index_direct():
48
+ """Directly render the main page when accessed via index.html"""
49
+ return render_template('index.html')
50
+
51
+ @app.route('/index_cn.html')
52
+ def index_cn():
53
+ """Render the Chinese version of the main page"""
54
+ return render_template('index_cn.html')
55
+
56
  @app.route('/config')
57
  def get_config():
58
  """Get initial configuration"""
bs_reasoning.py CHANGED
@@ -175,7 +175,13 @@ def wrap_text(text: str, config: VisualizationConfig) -> str:
175
  wrapped_lines = textwrap.wrap(text, width=config.max_chars_per_line)
176
 
177
  if len(wrapped_lines) > config.max_lines:
 
178
  wrapped_lines = wrapped_lines[:config.max_lines]
179
  wrapped_lines[-1] = wrapped_lines[-1][:config.max_chars_per_line-3] + "..."
180
-
 
 
 
 
 
181
  return "<br>".join(wrapped_lines)
 
175
  wrapped_lines = textwrap.wrap(text, width=config.max_chars_per_line)
176
 
177
  if len(wrapped_lines) > config.max_lines:
178
+ # Option 1: Simply truncate and add ellipsis to the last line
179
  wrapped_lines = wrapped_lines[:config.max_lines]
180
  wrapped_lines[-1] = wrapped_lines[-1][:config.max_chars_per_line-3] + "..."
181
+
182
+ # Option 2 (alternative): Include part of the next line to show continuity
183
+ # original_next_line = wrapped_lines[config.max_lines] if len(wrapped_lines) > config.max_lines else ""
184
+ # wrapped_lines = wrapped_lines[:config.max_lines-1]
185
+ # wrapped_lines.append(original_next_line[:config.max_chars_per_line-3] + "...")
186
+
187
  return "<br>".join(wrapped_lines)
templates/index.html CHANGED
@@ -126,7 +126,7 @@
126
  }
127
 
128
  .search-buttons button:hover {
129
- background-color: rgba(37, 99, 235, 0.9); /* 鼠标悬停时变为深一点的蓝色 */
130
  transform: translateY(-1px);
131
  }
132
 
@@ -354,6 +354,9 @@
354
  <div class="links">
355
  <a href="https://github.com/ZongqianLi/ReasonGraph"><u>Github</u> |&nbsp</a><a href="https://arxiv.org/abs/2503.03979"><u>Paper</u> |&nbsp</a><a href="mailto:[email protected]"><u>Email</u></a>
356
  </div>
 
 
 
357
  </div>
358
  </div>
359
 
 
126
  }
127
 
128
  .search-buttons button:hover {
129
+ background-color: rgba(37, 99, 235, 0.9);
130
  transform: translateY(-1px);
131
  }
132
 
 
354
  <div class="links">
355
  <a href="https://github.com/ZongqianLi/ReasonGraph"><u>Github</u> |&nbsp</a><a href="https://arxiv.org/abs/2503.03979"><u>Paper</u> |&nbsp</a><a href="mailto:[email protected]"><u>Email</u></a>
356
  </div>
357
+ <div class="links">
358
+ <a href="./index.html"><u>English</u> |&nbsp</a><a href="./index_cn.html"><u>中文</u></a>
359
+ </div>
360
  </div>
361
  </div>
362
 
templates/index_cn.html ADDED
@@ -0,0 +1,843 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>ReasonGraph</title>
5
+ <link rel="icon" href="static/assets/idea.png" type="image/x-icon">
6
+ <link rel="shortcut icon" href="favicon.ico" type="image/x-icon">
7
+ <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/mermaid.min.js"></script>
8
+ <style>
9
+ body {
10
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
11
+ margin: 0;
12
+ padding: 20px;
13
+ background-color: #f5f5f5;
14
+ }
15
+
16
+ .banner {
17
+ margin: 3px 20px;
18
+ border-radius: 8px;
19
+ background-image: url("{{ url_for('static', filename='assets/banner-bg.jpg') }}");
20
+ background-size: cover;
21
+ background-position: center;
22
+ box-shadow: 0 1px 3px rgba(0,0,0,0.1);
23
+ height: 280px;
24
+ display: flex;
25
+ align-items: center;
26
+ justify-content: center;
27
+ position: relative;
28
+ }
29
+
30
+ .banner::before {
31
+ content: '';
32
+ position: absolute;
33
+ top: 0;
34
+ left: 0;
35
+ right: 0;
36
+ bottom: 0;
37
+ background: rgba(0, 0, 0, 0.4);
38
+ border-radius: 8px;
39
+ }
40
+
41
+ .banner-content {
42
+ position: relative;
43
+ z-index: 1;
44
+ text-align: center;
45
+ display: flex;
46
+ flex-direction: column;
47
+ gap: 24px;
48
+ width: 100%;
49
+ max-width: 800px;
50
+ padding: 0 20px;
51
+ }
52
+
53
+ .banner h1 {
54
+ color: white;
55
+ font-size: 32px;
56
+ font-weight: 600;
57
+ margin: 0;
58
+ text-shadow: 0 2px 4px rgba(0,0,0,0.2);
59
+ }
60
+
61
+ .search-area {
62
+ display: flex;
63
+ flex-direction: column;
64
+ gap: 16px;
65
+ width: 100%;
66
+ }
67
+
68
+ .search-input-container {
69
+ position: relative;
70
+ width: 100%;
71
+ }
72
+
73
+ .search-input {
74
+ width: 100%;
75
+ padding: 12px;
76
+ border: 2px solid rgba(255, 255, 255, 0.2);
77
+ border-radius: 8px;
78
+ background: rgba(255, 255, 255, 0.9);
79
+ font-size: 16px;
80
+ resize: none;
81
+ outline: none;
82
+ transition: border-color 0.2s;
83
+ }
84
+
85
+ .search-input:focus {
86
+ border-color: rgba(255, 255, 255, 0.5);
87
+ }
88
+
89
+ .search-buttons {
90
+ display: flex;
91
+ gap: 10px;
92
+ justify-content: center;
93
+ align-items: center;
94
+ margin: 0 auto;
95
+ max-width: 800px; /* Match search input max-width */
96
+ width: 100%;
97
+ padding: 0 20px;
98
+ }
99
+
100
+ .search-buttons .param-input {
101
+ width: 200px !important; /* Override the default param-input width */
102
+ padding: 8px 16px;
103
+ background-color: rgba(255, 255, 255, 0.9);
104
+ border: none;
105
+ border-radius: 6px;
106
+ font-size: 14px;
107
+ font-weight: 500;
108
+ height: 35px; /* Match button height */
109
+ flex: none; /* Override flex property */
110
+ }
111
+
112
+ .search-buttons button {
113
+ width: auto;
114
+ min-width: 120px;
115
+ padding: 8px 16px;
116
+ background-color: rgba(37, 99, 235, 0.8); /* Semi-transparent gray */
117
+ color: white; /* White text */
118
+ border: none;
119
+ border-radius: 6px;
120
+ font-size: 14px;
121
+ font-weight: 500;
122
+ cursor: pointer;
123
+ transition: all 0.2s;
124
+ height: 35px; /* Fixed height */
125
+ line-height: 1; /* Ensure text vertical centering */
126
+ }
127
+
128
+ .search-buttons button:hover {
129
+ background-color: rgba(37, 99, 235, 0.9);
130
+ transform: translateY(-1px);
131
+ }
132
+
133
+ .links {
134
+ display: flex;
135
+ justify-content: center;
136
+ gap: 0;
137
+ white-space: nowrap;
138
+ }
139
+
140
+ .links a {
141
+ color: white;
142
+ text-decoration: none;
143
+ font-size: 16px;
144
+ opacity: 0.9;
145
+ transition: opacity 0.2s;
146
+ }
147
+
148
+ .links a:hover {
149
+ opacity: 1;
150
+ text-decoration: underline;
151
+ }
152
+
153
+ .container {
154
+ display: flex;
155
+ min-height: 100vh;
156
+ gap: 20px;
157
+ padding: 20px;
158
+ }
159
+
160
+ .column {
161
+ flex: 1;
162
+ background: white;
163
+ border-radius: 8px;
164
+ box-shadow: 0 1px 3px rgba(0,0,0,0.1);
165
+ padding: 20px;
166
+ }
167
+
168
+ h2 {
169
+ margin-top: 0;
170
+ margin-bottom: 20px;
171
+ color: #1f2937;
172
+ font-size: 18px;
173
+ font-weight: 600;
174
+ }
175
+
176
+ .param-group {
177
+ display: flex;
178
+ margin-bottom: 15px;
179
+ border: 1px solid #e5e7eb;
180
+ border-radius: 4px;
181
+ overflow: hidden;
182
+ }
183
+
184
+ .param-label {
185
+ width: 180px;
186
+ padding: 10px 15px;
187
+ background-color: #f8f9fa;
188
+ border-right: 1px solid #e5e7eb;
189
+ font-size: 14px;
190
+ line-height: 1.5;
191
+ color: #374151;
192
+ }
193
+
194
+ .param-input {
195
+ flex: 1;
196
+ padding: 10px 15px;
197
+ border: none;
198
+ font-size: 14px;
199
+ line-height: 1.5;
200
+ outline: none;
201
+ background: white;
202
+ }
203
+
204
+ select.param-input {
205
+ cursor: pointer;
206
+ padding-right: 30px;
207
+ appearance: none;
208
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='%236b7280' viewBox='0 0 16 16'%3E%3Cpath d='M8 10l4-4H4l4 4z'/%3E%3C/svg%3E");
209
+ background-repeat: no-repeat;
210
+ background-position: right 10px center;
211
+ }
212
+
213
+ textarea.param-input {
214
+ resize: vertical;
215
+ min-height: 80px;
216
+ }
217
+
218
+ .error-message {
219
+ color: #dc2626;
220
+ font-size: 14px;
221
+ display: none;
222
+ position: absolute;
223
+ right: 0;
224
+ top: 50%;
225
+ transform: translateY(-50%);
226
+ background: white;
227
+ padding: 2px 8px;
228
+ border-radius: 4px;
229
+ box-shadow: 0 1px 2px rgba(0,0,0,0.1);
230
+ margin-right: 10px;
231
+ }
232
+
233
+ .search-input-container, .param-group {
234
+ position: relative;
235
+ }
236
+
237
+ .output-section {
238
+ margin-top: 20px;
239
+ padding: 0;
240
+ background: white;
241
+ }
242
+
243
+ .output-section h3 {
244
+ margin: 0 0 15px 0;
245
+ color: #1f2937;
246
+ font-size: 18px;
247
+ font-weight: 600;
248
+ }
249
+
250
+ .output-wrapper {
251
+ overflow: auto;
252
+ height: 100px;
253
+ min-height: 100px;
254
+ max-height: 1000px;
255
+ border: 1px solid #e5e7eb;
256
+ border-radius: 4px;
257
+ padding: 15px;
258
+ resize: vertical;
259
+ background-color: #f8f9fa;
260
+ }
261
+
262
+ #raw-output {
263
+ white-space: pre-wrap;
264
+ word-break: break-word;
265
+ margin: 0;
266
+ font-family: 'Menlo', 'Monaco', 'Courier New', monospace;
267
+ font-size: 13px;
268
+ line-height: 1.5;
269
+ color: #1f2937;
270
+ }
271
+
272
+ button:disabled {
273
+ background-color: #9ca3af;
274
+ cursor: not-allowed;
275
+ }
276
+
277
+ .zoom-controls {
278
+ display: flex;
279
+ align-items: center;
280
+ gap: 10px;
281
+ margin-bottom: 10px;
282
+ }
283
+
284
+ .zoom-button {
285
+ padding: 5px 10px;
286
+ background-color: #f3f4f6;
287
+ border: 1px solid #e5e7eb;
288
+ border-radius: 4px;
289
+ cursor: pointer;
290
+ font-size: 14px;
291
+ color: #374151;
292
+ width: auto;
293
+ }
294
+
295
+ .zoom-button:hover {
296
+ background-color: #e5e7eb;
297
+ }
298
+
299
+ .zoom-level {
300
+ font-size: 14px;
301
+ color: #374151;
302
+ min-width: 50px;
303
+ text-align: center;
304
+ }
305
+
306
+ #mermaid-container {
307
+ transform-origin: top left; /* Changed from 'top center' */
308
+ transition: transform 0.2s ease;
309
+ width: 100%;
310
+ display: block; /* Changed from 'flex' */
311
+ justify-content: flex-start; /* Changed from 'center' */
312
+ }
313
+
314
+
315
+ .visualization-wrapper {
316
+ overflow: auto;
317
+ height: 370px;
318
+ min-height: 100px;
319
+ max-height: 1000px;
320
+ border: 1px solid #e5e7eb;
321
+ border-radius: 4px;
322
+ padding: 0;
323
+ resize: vertical;
324
+ background-color: #f8f9fa;
325
+ }
326
+
327
+ .mermaid {
328
+ padding: 0;
329
+ border-radius: 4px;
330
+ }
331
+
332
+ .has-visualization .placeholder-visualization {
333
+ display: none;
334
+ }
335
+ </style>
336
+ </head>
337
+ <body>
338
+ <div class="banner">
339
+ <div class="banner-content">
340
+ <h1>ReasonGraph: Visualisation of Reasoning Paths</h1>
341
+ <div class="search-area">
342
+ <div class="search-input-container">
343
+ <textarea id="question" class="search-input" placeholder="在此输入您的问题..." rows="1"></textarea>
344
+ <div class="error-message" id="question-error">请输入问题</div>
345
+ </div>
346
+ <div class="search-buttons">
347
+ <select class="param-input" id="reasoning-method">
348
+ <!-- Populated dynamically -->
349
+ </select>
350
+ <button onclick="metaReasoning()" id="meta-btn">元推理</button>
351
+ <button onclick="processQuestion()" id="process-btn">���始推理</button>
352
+ </div>
353
+ </div>
354
+ <div class="links">
355
+ <a href="https://github.com/ZongqianLi/ReasonGraph"><u>Github</u> |&nbsp</a><a href="https://arxiv.org/abs/2503.03979"><u>论文</u> |&nbsp</a><a href="mailto:[email protected]"><u>邮箱</u></a>
356
+ </div>
357
+ <div class="links">
358
+ <a href="./index.html"><u>English</u> |&nbsp</a><a href="./index_cn.html"><u>中文</u></a>
359
+ </div>
360
+ </div>
361
+ </div>
362
+
363
+ <div class="container">
364
+ <div class="column">
365
+ <h2>推理设置</h2>
366
+
367
+ <div class="param-group">
368
+ <div class="param-label">API提供商</div>
369
+ <select class="param-input" id="api-provider" onchange="handleProviderChange(this.value)">
370
+ <!-- Populated dynamically -->
371
+ </select>
372
+ </div>
373
+
374
+ <div class="param-group">
375
+ <div class="param-label">模型</div>
376
+ <select class="param-input" id="model">
377
+ <!-- Populated dynamically -->
378
+ </select>
379
+ </div>
380
+
381
+ <div class="param-group">
382
+ <div class="param-label">最大输出令牌数</div>
383
+ <input type="number" class="param-input" id="max-tokens">
384
+ </div>
385
+
386
+ <div class="param-group">
387
+ <div class="param-label">API Key</div>
388
+ <input type="password" class="param-input" id="api-key">
389
+ <div class="error-message" id="api-key-error">请输入有效的API密钥</div>
390
+ </div>
391
+
392
+ <div class="param-group">
393
+ <div class="param-label">自定义提示格式</div>
394
+ <textarea class="param-input" id="prompt-format" rows="6"></textarea>
395
+ </div>
396
+
397
+ <div class="output-section">
398
+ <h3>模型原始输出</h3>
399
+ <div class="output-wrapper">
400
+ <pre id="raw-output">输出将显示在这里...</pre>
401
+ </div>
402
+ </div>
403
+ </div>
404
+
405
+ <div class="column">
406
+ <h2>可视化设置</h2>
407
+
408
+ <div class="param-group">
409
+ <div class="param-label">每行字符数</div>
410
+ <input type="number" class="param-input" id="chars-per-line">
411
+ </div>
412
+
413
+ <div class="param-group">
414
+ <div class="param-label">最大行数</div>
415
+ <input type="number" class="param-input" id="max-lines">
416
+ </div>
417
+
418
+ <div class="output-section">
419
+ <h3>可视化结果</h3>
420
+ <div class="zoom-controls">
421
+ <button class="zoom-button" onclick="adjustZoom(-0.1)">-</button>
422
+ <div class="zoom-level" id="zoom-level">100%</div>
423
+ <button class="zoom-button" onclick="adjustZoom(0.1)">+</button>
424
+ <button class="zoom-button" onclick="resetZoom()">重置</button>
425
+ <button class="zoom-button" onclick="downloadDiagram()">下载</button>
426
+ </div>
427
+ <div class="visualization-wrapper">
428
+ <div id="mermaid-container">
429
+ <div id="mermaid-diagram"></div>
430
+ </div>
431
+ </div>
432
+ </div>
433
+ </div>
434
+ </div>
435
+
436
+ <script>
437
+ // Initialize Mermaid
438
+ mermaid.initialize({
439
+ startOnLoad: true,
440
+ theme: 'default',
441
+ securityLevel: 'loose',
442
+ flowchart: {
443
+ curve: 'basis',
444
+ padding: 15
445
+ }
446
+ });
447
+
448
+ // Store current configuration
449
+ let currentConfig = null;
450
+
451
+ // Zoom control variables
452
+ let currentZoom = 1;
453
+ const MIN_ZOOM = 0.1;
454
+ const MAX_ZOOM = 5;
455
+
456
+ // Initialize zoom lock flag
457
+ window.isZoomLocked = false;
458
+
459
+ // Handle API Provider change
460
+ async function handleProviderChange(provider) {
461
+ try {
462
+ // Update model list
463
+ updateModelList();
464
+
465
+ // Get and update API key
466
+ const response = await fetch(`/provider-api-key/${provider}`);
467
+ const result = await response.json();
468
+
469
+ if (result.success) {
470
+ document.getElementById('api-key').value = result.api_key;
471
+ } else {
472
+ console.error('Failed to get API key:', result.error);
473
+ }
474
+ } catch (error) {
475
+ console.error('Error updating provider settings:', error);
476
+ }
477
+ }
478
+
479
+ // Load initial configuration
480
+ async function loadConfig() {
481
+ try {
482
+ const response = await fetch('/config');
483
+ currentConfig = await response.json();
484
+
485
+ // Populate API providers
486
+ const providerSelect = document.getElementById('api-provider');
487
+ currentConfig.general.providers.forEach(provider => {
488
+ const option = document.createElement('option');
489
+ option.value = provider;
490
+ option.textContent = provider.charAt(0).toUpperCase() + provider.slice(1);
491
+ providerSelect.appendChild(option);
492
+ });
493
+
494
+ // Populate reasoning methods
495
+ const methodSelect = document.getElementById('reasoning-method');
496
+ Object.entries(currentConfig.methods).forEach(([id, methodConfig]) => {
497
+ const option = document.createElement('option');
498
+ option.value = id;
499
+ option.textContent = methodConfig.name;
500
+ methodSelect.appendChild(option);
501
+ });
502
+
503
+ // Initial provider setup
504
+ await handleProviderChange(currentConfig.general.providers[0]);
505
+
506
+ // Set other initial values
507
+ document.getElementById('max-tokens').value = currentConfig.general.max_tokens;
508
+ document.getElementById('chars-per-line').value = currentConfig.general.visualization.chars_per_line;
509
+ document.getElementById('max-lines').value = currentConfig.general.visualization.max_lines;
510
+
511
+ // Set initial prompt format and example question
512
+ const defaultMethod = methodSelect.value;
513
+ const methodConfig = currentConfig.methods[defaultMethod];
514
+ updatePromptFormat(methodConfig.prompt_format);
515
+ updateExampleQuestion(methodConfig.example_question);
516
+ } catch (error) {
517
+ console.error('Failed to load configuration:', error);
518
+ showError('Failed to load configuration. Please refresh the page.');
519
+ }
520
+ }
521
+
522
+ // Update model list based on selected provider
523
+ function updateModelList() {
524
+ const provider = document.getElementById('api-provider').value;
525
+ const modelSelect = document.getElementById('model');
526
+ modelSelect.innerHTML = ''; // Clear current options
527
+
528
+ const models = currentConfig.general.available_models;
529
+ const providers = currentConfig.general.model_providers;
530
+
531
+ models.forEach(model => {
532
+ if (providers[model] === provider) {
533
+ const option = document.createElement('option');
534
+ option.value = model;
535
+ option.textContent = model;
536
+ modelSelect.appendChild(option);
537
+ }
538
+ });
539
+ }
540
+
541
+ // Update prompt format when method changes
542
+ document.getElementById('reasoning-method').addEventListener('change', async (event) => {
543
+ try {
544
+ const response = await fetch(`/method-config/${event.target.value}`);
545
+ const methodConfig = await response.json();
546
+ updatePromptFormat(methodConfig.prompt_format);
547
+ updateExampleQuestion(methodConfig.example_question);
548
+ } catch (error) {
549
+ console.error('Failed to load method configuration:', error);
550
+ showError('Failed to update method configuration.');
551
+ }
552
+ });
553
+
554
+ function updatePromptFormat(format) {
555
+ document.getElementById('prompt-format').value = format;
556
+ }
557
+
558
+ function updateExampleQuestion(question) {
559
+ document.getElementById('question').value = question;
560
+ }
561
+
562
+ function adjustZoom(delta) {
563
+ // Do nothing if zooming is locked
564
+ if (window.isZoomLocked) return;
565
+
566
+ const newZoom = Math.min(Math.max(currentZoom + delta, MIN_ZOOM), MAX_ZOOM);
567
+ if (newZoom !== currentZoom) {
568
+ currentZoom = newZoom;
569
+ applyZoom();
570
+ }
571
+ }
572
+
573
+ function resetZoom() {
574
+ // Do nothing if zooming is locked
575
+ if (window.isZoomLocked) return;
576
+
577
+ currentZoom = 1;
578
+ applyZoom();
579
+ }
580
+
581
+ function applyZoom() {
582
+ const container = document.getElementById('mermaid-container');
583
+ container.style.transform = `scale(${currentZoom})`;
584
+
585
+ // Update zoom level display
586
+ const percentage = Math.round(currentZoom * 100);
587
+ document.getElementById('zoom-level').textContent = `${percentage}%`;
588
+ }
589
+
590
+ function lockVisualization() {
591
+ window.isZoomLocked = true;
592
+ const zoomButtons = document.querySelectorAll('.zoom-button');
593
+ zoomButtons.forEach(button => button.disabled = true);
594
+ document.querySelector('.visualization-wrapper').style.pointerEvents = 'none';
595
+ }
596
+
597
+ function unlockVisualization() {
598
+ window.isZoomLocked = false;
599
+ const zoomButtons = document.querySelectorAll('.zoom-button');
600
+ zoomButtons.forEach(button => button.disabled = false);
601
+ document.querySelector('.visualization-wrapper').style.pointerEvents = 'auto';
602
+ }
603
+
604
+ async function downloadDiagram() {
605
+ // Do nothing if zooming is locked
606
+ if (window.isZoomLocked) return;
607
+
608
+ const diagramContainer = document.getElementById('mermaid-diagram');
609
+ if (!diagramContainer || !diagramContainer.querySelector('svg')) {
610
+ alert('没有可供下载的图表');
611
+ return;
612
+ }
613
+
614
+ try {
615
+ // Get the SVG element
616
+ const svg = diagramContainer.querySelector('svg');
617
+
618
+ // Create a copy of the SVG to modify
619
+ const svgCopy = svg.cloneNode(true);
620
+
621
+ // Ensure the SVG has proper dimensions
622
+ const bbox = svg.getBBox();
623
+ svgCopy.setAttribute('width', bbox.width);
624
+ svgCopy.setAttribute('height', bbox.height);
625
+ svgCopy.setAttribute('viewBox', `${bbox.x} ${bbox.y} ${bbox.width} ${bbox.height}`);
626
+
627
+ // Convert SVG to string
628
+ const serializer = new XMLSerializer();
629
+ const svgString = serializer.serializeToString(svgCopy);
630
+
631
+ // Create blob and download link
632
+ const blob = new Blob([svgString], {type: 'image/svg+xml'});
633
+ const url = URL.createObjectURL(blob);
634
+
635
+ // Create temporary link and trigger download
636
+ const link = document.createElement('a');
637
+ link.href = url;
638
+ link.download = 'reasoning_diagram.svg';
639
+ document.body.appendChild(link);
640
+ link.click();
641
+
642
+ // Cleanup
643
+ document.body.removeChild(link);
644
+ URL.revokeObjectURL(url);
645
+ } catch (error) {
646
+ console.error('Error downloading diagram:', error);
647
+ alert('下载图表失败');
648
+ }
649
+ }
650
+
651
+ function validateInputs() {
652
+ const apiKey = document.getElementById('api-key').value.trim();
653
+ const question = document.getElementById('question').value.trim();
654
+
655
+ let isValid = true;
656
+
657
+ // Validate API Key
658
+ if (!apiKey) {
659
+ document.getElementById('api-key-error').style.display = 'block';
660
+ isValid = false;
661
+ } else {
662
+ document.getElementById('api-key-error').style.display = 'none';
663
+ }
664
+
665
+ // Validate Question
666
+ if (!question) {
667
+ document.getElementById('question-error').style.display = 'block';
668
+ isValid = false;
669
+ } else {
670
+ document.getElementById('question-error').style.display = 'none';
671
+ }
672
+
673
+ return isValid;
674
+ }
675
+
676
+ function showError(message) {
677
+ const rawOutput = document.getElementById('raw-output');
678
+ rawOutput.textContent = `错误: ${message}`;
679
+ rawOutput.style.color = '#dc2626';
680
+ }
681
+
682
+ // Process question
683
+ async function processQuestion(isMetaReasoning = false) {
684
+ if (!validateInputs()) {
685
+ return;
686
+ }
687
+
688
+ // Reset Zoom before processing question
689
+ resetZoom();
690
+ const processButton = document.getElementById('process-btn');
691
+ const metaButton = document.getElementById('meta-btn');
692
+ const rawOutput = document.getElementById('raw-output');
693
+
694
+ processButton.disabled = true;
695
+ metaButton.disabled = true;
696
+ processButton.textContent = '处理中...';
697
+ rawOutput.textContent = '加载中...';
698
+ rawOutput.style.color = '#1f2937';
699
+
700
+ // Lock visualization
701
+ lockVisualization();
702
+
703
+ const data = {
704
+ provider: document.getElementById('api-provider').value,
705
+ api_key: document.getElementById('api-key').value,
706
+ model: document.getElementById('model').value,
707
+ max_tokens: parseInt(document.getElementById('max-tokens').value),
708
+ question: document.getElementById('question').value,
709
+ prompt_format: document.getElementById('prompt-format').value,
710
+ reasoning_method: document.getElementById('reasoning-method').value,
711
+ chars_per_line: parseInt(document.getElementById('chars-per-line').value),
712
+ max_lines: parseInt(document.getElementById('max-lines').value)
713
+ };
714
+
715
+ try {
716
+ const response = await fetch('/process', {
717
+ method: 'POST',
718
+ headers: {
719
+ 'Content-Type': 'application/json'
720
+ },
721
+ body: JSON.stringify(data)
722
+ });
723
+
724
+ const result = await response.json();
725
+
726
+ if (result.success) {
727
+ rawOutput.textContent = result.raw_output;
728
+ rawOutput.style.color = '#1f2937';
729
+
730
+ if (result.visualization) {
731
+ const container = document.getElementById('mermaid-diagram');
732
+ container.innerHTML = result.visualization;
733
+ document.getElementById('mermaid-container').classList.add('has-visualization');
734
+ resetZoom();
735
+ mermaid.init();
736
+ }
737
+ } else {
738
+ showError(result.error || '发生未知错误');
739
+ }
740
+ } catch (error) {
741
+ showError('处理请求失败: ' + error.message);
742
+ } finally {
743
+ // Unlock visualization
744
+ unlockVisualization();
745
+
746
+ processButton.disabled = false;
747
+ metaButton.disabled = false;
748
+ processButton.textContent = '开始推理';
749
+ if (isMetaReasoning) {
750
+ metaButton.textContent = '元推理';
751
+ }
752
+ }
753
+ }
754
+
755
+ // Meta Reasoning function
756
+ async function metaReasoning() {
757
+ const metaButton = document.getElementById('meta-btn');
758
+ const rawOutput = document.getElementById('raw-output');
759
+
760
+ try {
761
+ metaButton.disabled = true;
762
+ metaButton.textContent = '选择方法中...';
763
+ rawOutput.textContent = '正在分析问题以选择最佳方法...';
764
+
765
+ // Get current parameters
766
+ const data = {
767
+ provider: document.getElementById('api-provider').value,
768
+ api_key: document.getElementById('api-key').value,
769
+ model: document.getElementById('model').value,
770
+ question: document.getElementById('question').value
771
+ };
772
+
773
+ // Call the method selection endpoint
774
+ const response = await fetch('/select-method', {
775
+ method: 'POST',
776
+ headers: {
777
+ 'Content-Type': 'application/json'
778
+ },
779
+ body: JSON.stringify(data)
780
+ });
781
+
782
+ const result = await response.json();
783
+
784
+ if (result.success) {
785
+ // Set the selected method
786
+ const methodSelect = document.getElementById('reasoning-method');
787
+ methodSelect.value = result.selected_method;
788
+
789
+ // Fetch and update the corresponding method configuration
790
+ const methodResponse = await fetch(`/method-config/${result.selected_method}`);
791
+ const methodConfig = await methodResponse.json();
792
+
793
+ if (methodConfig) {
794
+ // Update the prompt format
795
+ updatePromptFormat(methodConfig.prompt_format);
796
+
797
+ // Update example question if needed
798
+ if (document.getElementById('question').value === '') {
799
+ updateExampleQuestion(methodConfig.example_question);
800
+ }
801
+
802
+ console.log(`Selected reasoning method: ${methodConfig.name}`);
803
+
804
+ // Update button to show method was selected
805
+ metaButton.textContent = '方法已选择';
806
+ // Process the question with the selected method
807
+ await processQuestion(true);
808
+ } else {
809
+ showError('加载方法配置失败');
810
+ metaButton.textContent = '元推理';
811
+ }
812
+ } else {
813
+ showError(result.error || '选择方法失败');
814
+ metaButton.textContent = '元推理';
815
+ }
816
+ } catch (error) {
817
+ console.error('Meta reasoning error:', error);
818
+ showError('执行元推理失败');
819
+ metaButton.disabled = false;
820
+ metaButton.textContent = '元推理';
821
+ }
822
+ }
823
+
824
+ // Add event listener for mouse wheel zoom
825
+ document.querySelector('.visualization-wrapper').addEventListener('wheel', function(e) {
826
+ // Do nothing if zooming is locked
827
+ if (window.isZoomLocked) {
828
+ e.preventDefault();
829
+ return;
830
+ }
831
+
832
+ if (e.ctrlKey) {
833
+ e.preventDefault(); // Prevent default zoom
834
+ const delta = e.deltaY > 0 ? -0.1 : 0.1;
835
+ adjustZoom(delta);
836
+ }
837
+ });
838
+
839
+ // Load configuration when page loads
840
+ document.addEventListener('DOMContentLoaded', loadConfig);
841
+ </script>
842
+ </body>
843
+ </html>
tot_reasoning.py CHANGED
@@ -125,7 +125,13 @@ def wrap_text(text: str, config: VisualizationConfig) -> str:
125
  wrapped_lines = textwrap.wrap(text, width=config.max_chars_per_line)
126
 
127
  if len(wrapped_lines) > config.max_lines:
 
128
  wrapped_lines = wrapped_lines[:config.max_lines]
129
  wrapped_lines[-1] = wrapped_lines[-1][:config.max_chars_per_line-3] + "..."
130
-
 
 
 
 
 
131
  return "<br>".join(wrapped_lines)
 
125
  wrapped_lines = textwrap.wrap(text, width=config.max_chars_per_line)
126
 
127
  if len(wrapped_lines) > config.max_lines:
128
+ # Option 1: Simply truncate and add ellipsis to the last line
129
  wrapped_lines = wrapped_lines[:config.max_lines]
130
  wrapped_lines[-1] = wrapped_lines[-1][:config.max_chars_per_line-3] + "..."
131
+
132
+ # Option 2 (alternative): Include part of the next line to show continuity
133
+ # original_next_line = wrapped_lines[config.max_lines] if len(wrapped_lines) > config.max_lines else ""
134
+ # wrapped_lines = wrapped_lines[:config.max_lines-1]
135
+ # wrapped_lines.append(original_next_line[:config.max_chars_per_line-3] + "...")
136
+
137
  return "<br>".join(wrapped_lines)