PeterKruger commited on
Commit
22c6123
·
verified ·
1 Parent(s): ea1baa7

Delete app_old.py

Browse files
Files changed (1) hide show
  1. app_old.py +0 -1104
app_old.py DELETED
@@ -1,1104 +0,0 @@
1
- import streamlit as st
2
- import os
3
- from huggingface_hub import InferenceClient
4
- import numpy as np
5
- import pandas as pd
6
- import concurrent.futures
7
- import re
8
- import time
9
- import random
10
- import functools
11
- import sys
12
- import io
13
- from contextlib import redirect_stdout, redirect_stderr
14
-
15
- # Initialize session state variables properly at the very beginning
16
- if 'main_output' not in st.session_state:
17
- st.session_state['main_output'] = []
18
- if 'debug_output' not in st.session_state:
19
- st.session_state['debug_output'] = []
20
- if 'progress' not in st.session_state:
21
- st.session_state['progress'] = 0
22
- if 'results_df' not in st.session_state:
23
- st.session_state['results_df'] = pd.DataFrame()
24
- if 'is_running' not in st.session_state:
25
- st.session_state['is_running'] = False
26
-
27
- # FILES
28
- iteration_output_file = "llm_benchmark_iteration_results.csv" # File to store iteration results, defined as global
29
- results_file = "llm_benchmark_results.csv" # all data
30
-
31
- # GLOBAL PARAMETERS
32
- time_sleep = 0.2 # take time before making a new request
33
- base_temp = 0.2 # base temperature for models
34
-
35
- # QUESTION GLOBAL PARAMETERS
36
- question_temp = 0.7 # question generation temperature
37
- question_max_tokens = 256 # question generation max tokens
38
- question_treshold = 4.3 # min average rank for questions to be accepted
39
- reject_rank = 3 # all question ranks must be above
40
-
41
- # ANSWER GLOBAL PARAMETERS
42
- answer_temp = 0.5 # base answering temperature
43
- long_temp = 1.0 # answering temperature for creative questions
44
- answer_max_tokens = 1048 # max tokens per answer
45
- long_max_tokens = 2048 # max tokens per creative answer
46
-
47
- # --- Difficulty probabilities ---
48
- difficulty_probabilities = {
49
- "a very simple": 0.0,
50
- "a simple": 0.0,
51
- "a": 0.1, # average
52
- "a difficult": 0.3,
53
- "a very difficult": 0.6
54
- }
55
-
56
- # Custom print function to capture output
57
- def custom_print(*args, **kwargs):
58
- # Convert args to string and join with spaces
59
- output = ' '.join(map(str, args))
60
-
61
- # Add to main output list
62
- st.session_state['main_output'].append(output)
63
-
64
- # Also print to standard output for console logging
65
- print(*args, **kwargs)
66
-
67
- # Custom function to capture warnings and errors
68
- def log_debug(message):
69
- st.session_state['debug_output'].append(message)
70
- print(f"DEBUG: {message}", file=sys.stderr)
71
-
72
- def retry_api_request(max_retries=3, wait_time=10):
73
- """Decorator for retrying API requests with rate limit handling."""
74
- def decorator(func):
75
- @functools.wraps(func)
76
- def wrapper(*args, **kwargs):
77
- retries = 0
78
- while retries <= max_retries:
79
- try:
80
- return func(*args, **kwargs)
81
- except Exception as e:
82
- error_msg = f"API error: {e}"
83
- log_debug(error_msg)
84
- if retries < max_retries:
85
- retry_msg = f"Waiting for {wait_time} seconds before retrying... (Retry {retries + 1}/{max_retries})"
86
- log_debug(retry_msg)
87
- time.sleep(wait_time)
88
- retries += 1
89
- else:
90
- failure_msg = f"Max retries reached. Request failed."
91
- log_debug(failure_msg)
92
- return None
93
-
94
- return None
95
-
96
- return wrapper
97
- return decorator
98
-
99
- # --- Single model request function for Hugging Face ---
100
-
101
- @retry_api_request()
102
- def make_hf_request(model_name, messages, temperature, max_tokens, token=None):
103
- """
104
- Send request to a Hugging Face model using InferenceClient
105
-
106
- Args:
107
- model_name: Name of the Hugging Face model
108
- messages: Messages in the format [{"role": "user", "content": "..."}]
109
- temperature: Temperature parameter for generation
110
- max_tokens: Maximum tokens to generate
111
- token: Hugging Face API token
112
-
113
- Returns:
114
- Generated text or None if request fails
115
- """
116
- client = InferenceClient(model=model_name, token=token)
117
-
118
- # Convert messages to a prompt string
119
- prompt = ""
120
- for msg in messages:
121
- if msg["role"] == "user":
122
- prompt += f"User: {msg['content']}\n\n"
123
- elif msg["role"] == "assistant":
124
- prompt += f"Assistant: {msg['content']}\n\n"
125
-
126
- if not prompt.endswith("Assistant: "):
127
- prompt += "Assistant: "
128
-
129
- try:
130
- response = client.text_generation(
131
- prompt,
132
- max_new_tokens=max_tokens,
133
- temperature=temperature,
134
- do_sample=True
135
- )
136
- return response
137
- except Exception as e:
138
- error_msg = f"Hugging Face Inference API error: {e}"
139
- log_debug(error_msg)
140
- return None
141
-
142
- # --- Prompting Functions ---
143
- def generate_question_prompt(topic, difficulty):
144
- # 1. Base Instructions with Difficulty and Topic Clarity
145
- base_instructions = [
146
- f"Generate {difficulty} question on the following topic: {topic}.",
147
- f"Formulate {difficulty} question regarding the following topic: {topic}.",
148
- f"Create {difficulty} question about the following topic: {topic}.",
149
- f"Compose {difficulty} question on the following topic: {topic}.",
150
- f"Develop {difficulty} question that explores the following topic: {topic}."
151
- ]
152
-
153
- # 2. Difficulty Options and Instructions
154
- difficulty_instructions = {
155
- "a very simple": [
156
- "The question should test basic, widely known facts.",
157
- "It should be answerable with common knowledge.",
158
- "Focus on simple recall and recognition.",
159
- "The answer is immediately obvious to someone with basic knowledge."
160
- ],
161
- "a simple": [
162
- "The question should require recall of specific information.",
163
- "It should test knowledge of fundamental concepts.",
164
- "The answer can be found in introductory materials.",
165
- "No complex reasoning or deep analysis is needed."
166
- ],
167
- "a": [ # For "average" difficulty - no specific instructions needed beyond base
168
- "The question should be moderately challenging.",
169
- "It should require some basic reasoning or inference.",
170
- "The answer may require connecting two or three pieces of information.",
171
- "It should test understanding beyond simple memorization."
172
- ],
173
- "a difficult": [
174
- "The question should require analytical thinking and application of knowledge.",
175
- "It should go beyond simple facts and require interpretation.",
176
- "The answer may involve multiple steps or perspectives.",
177
- "It should test deeper comprehension and problem-solving skills."
178
- ],
179
- "a very difficult": [
180
- "The question should require expert-level knowledge and critical analysis.",
181
- "It should involve complex reasoning and nuanced understanding.",
182
- "The answer may require synthesis of information from various sources.",
183
- "It should be challenging even for someone knowledgeable in the field."
184
- ],
185
- }
186
-
187
- difficulty_instructions_creative_writing = {
188
- "a very simple": [
189
- "The task should be very easy to complete, requiring minimal creativity or effort.",
190
- "Focus on simple, straightforward writing."
191
- ],
192
- "a simple": [
193
- "The task should require some imagination, but remain relatively easy.",
194
- "Focus on basic storytelling or poetic elements."
195
- ],
196
- "a": [
197
- "The task should be moderately challenging, requiring a good balance of creativity and execution.",
198
- "Explore more complex ideas or writing styles."
199
- ],
200
- "a difficult": [
201
- "The task should be quite challenging, pushing the boundaries of creativity and writing skill.",
202
- "Incorporate complex themes, metaphors, or unusual narrative structures."
203
- ],
204
- "a very difficult": [
205
- "The task should be extremely challenging, requiring a high level of originality and mastery of language.",
206
- "Experiment with unconventional forms, complex symbolism, or profound philosophical concepts."
207
- ],
208
- }
209
-
210
- # --- Topic-Specific Instructions ---
211
- topic_instructions = {
212
- "math": [
213
- "The question should be a mathematical problem.",
214
- "It should involve calculations or mathematical reasoning.",
215
- "Formulate a math word problem.",
216
- "Create a mathematical problem related to a specic field of math study"
217
- ],
218
- "logics": [
219
- "The question should be a logic puzzle or riddle.",
220
- "It should require deductive or inductive reasoning.",
221
- "Formulate a logical reasoning problem.",
222
- "Create a logic puzzle that requires careful analysis."
223
- ],
224
- "history": [
225
- "The question should relate to a specific historical event, period, or figure.",
226
- "It should require analyzing historical causes and consequences.",
227
- "Formulate a question about historical interpretation or analysis.",
228
- "Create a question that requires understanding of historical context."
229
- ],
230
- "current news": [
231
- "The question should pertain to a recent, significant news event.",
232
- "It should require understanding of current affairs.",
233
- "Formulate a question about the implications of a current news event.",
234
- "Create a question that requires analysis of a recent development."
235
- ],
236
- "general culture": [
237
- "The question should relate to general knowledge and cultural awareness.",
238
- "It should test understanding of common cultural references.",
239
- "Formulate a question about a well-known cultural phenomenon.",
240
- "Create a general knowledge question."
241
- ],
242
- "science": [
243
- "Generate a question regarding a scientific concept.",
244
- "It should test the comprehension of a scientific fact or principle.",
245
- "Form a question that assesses knowledge in a scientific domain."
246
- ],
247
- "technology":[
248
- "Generate a question regarding a technological concept.",
249
- "It should test the comprehension of a technological fact or principle.",
250
- "Form a question that assesses knowledge in a technological domain."
251
- ],
252
- "grammar":[
253
- "Generate a question regarding a gramatical or linguistic concept.",
254
- "It should test the comprehension of a gramatical or linguistic fact or principle.",
255
- "Form a question that assesses knowledge in a gramatical or linguistic domain.",
256
- "Create a question testing the understanding of gramar and linguistic rules."
257
- ],
258
- "coding":[
259
- "Generate a question about a coding concept or algorithm. Suggest also one or more programming languages to address the question.",
260
- "The question should test understanding of programming principles. If required, suggest also one or more programming languages to address the question.",
261
- "Formulate a coding problem or question. You may want to suggest also one or more programming languages to address the question.",
262
- "Create a question that requires knowledge of programming logic. If needed, suggest also one or more programming languages to address the question.",
263
- "The question should be related to software development or computer science. If required, suggest also one or more programming languages to address the question."
264
- "The question should be about Python programming.",
265
- "Formulate a coding problem solvable in Java.",
266
- "Create a question related to JavaScript concepts."
267
- "The question should involve algorithm design. Ssuggest also one or more programming languages to address the question.",
268
- "Formulate a question about data structures. Suggest also one or more programming languages to address the question.",
269
- "Create a question testing debugging skills.",
270
- "The question should assess code optimization techniques."
271
- ],
272
- "creative writing": [
273
- "Write a short story (under 3000 characters) that begins with the sentence: 'The old lighthouse keeper saw a light that wasn't his own.'",
274
- "Compose a poem (under 3000 characters) in the style of haiku, about the feeling of a summer rain.",
275
- "Write a short story (under 3000 characters), no more than five sentences, about a robot who discovers the meaning of friendship.",
276
- "Create a humorous anecdote (under 3000 characters) about a cat and a laser pointer.",
277
- "Write a short story (under 3000 characters) that ends with the phrase: '...and that's how the world changed forever.'",
278
- "Compose a free verse poem (under 3000 characters) about the loneliness of space travel.",
279
- "Write a short, poignant story (under 3000 characters) about a lost object found again.",
280
- "Tell a joke (under 3000 characters) about a programmer and a bug.",
281
- "Respond to the philosophical question (under 3000 characters): 'If a tree falls in a forest and no one is around to hear it, does it make a sound?' in a creative and thought-provoking way.",
282
- "Write a very short story (under 3000 characters) about a talking animal.",
283
- "Imagine you are a grain of sand. Describe your life (under 3000 characters).",
284
- "Write a short story (under 3000 characters) set in a world where colors don't exist.",
285
- "Write a poem (under 3000 characters) about the feeling of nostalgia.",
286
- "Create a short, funny dialogue (under 3000 characters) between two inanimate objects.",
287
- "Write a flash fiction piece (under 3000 characters) inspired by a random word (e.g., 'serendipity', 'obfuscate', 'ephemeral').",
288
- "Respond to the following prompt (under 3000 characters) with a creative story: 'You wake up one morning to find you can fly.'",
289
- "Compose a short story(under 3000 characters), inspired by a piece of classical music",
290
- "Tell a joke (under 3000 characters) based on a pun.",
291
- "Write a short description (under 3000 characters) of a dream you had.",
292
- "Craft a short, suspenseful story (under 3000 characters) that begins: 'The phone rang, but the screen was blank...'",
293
- ],
294
- }
295
-
296
- #add the creative writing specific prompts to the difficulty prompt,
297
- #if the topic is creative writing
298
- if topic == "creative writing":
299
- difficulty_instructions.update(difficulty_instructions_creative_writing)
300
-
301
- # 4. Guiding Sentence for Question Types
302
- question_type_intro = "As an example for you, it could be in the form of:"
303
- question_types = [
304
- "a comparison question (asking to compare and contrast...).",
305
- "an analysis question (asking to analyze the relationship between...).",
306
- "an explanation question (asking to explain the causes of...).",
307
- "a discussion question (asking to discuss the implications of...).",
308
- "a significance question (asking about the significance of...).",
309
- "a cause-and-effect question (like 'How does ... affect ...?').",
310
- "a difference question (like 'What are the key differences between ... and ...?').",
311
- "a hypothetical question (like 'What would be the consequences of ...?').", # Counterfactual
312
- "a scenario-based question (like 'Develop a scenario where...').", #Scenario based
313
- "a pros and cons question (Provide arguments for and against...')." #pro and cons
314
- ]
315
-
316
- # --- Combine Prompts using Random Choices ---
317
- prompt = random.choice(base_instructions) + "\n"
318
- prompt += random.choice(difficulty_instructions[difficulty]) + "\n"
319
-
320
- # Add topic-specific instruction, handling cases where topic might not be defined.
321
- if topic in topic_instructions:
322
- prompt += random.choice(topic_instructions[topic]) + "\n"
323
- else:
324
- log_debug(f"Warning: No topic_instructions defined for topic '{topic}'")
325
-
326
- # 5. Conditional Question Types (Not for math, logics, grammar)
327
- if topic not in ["math", "logics", "grammar", "coding", "creative writing"]:
328
- prompt += question_type_intro + "\n"
329
- prompt += random.choice(question_types)
330
-
331
- prompt += "\n\nIn generating your question, do not show your internal thought process. Make sure to provide as an output only the final complete and consistent formulation of your question\n"
332
- return prompt
333
-
334
- def answer_question_prompt(question):
335
- return f"Answer the question below. Ensure your answer is clear and insightful, relevant to the topic discussed, logical and grammatically sound, and contains only correct information. In generating your answer, do not show your internal thought process. Provide only your final, complete, and supported answer.\n\nQuestion: {question}\n\nAnswer:"
336
-
337
- def rank_answer_prompt(question, answer, topic):
338
- prompt = f"""You are an expert evaluator. Rank the following answer to the given question on a scale of 1 to 5, where:
339
- 1: Not good answer - unclear, irrelevant to the topic, poorly formulated, or with evidently incorrect statements. For creative writing, this also includes being unoriginal, unimaginative, or failing to adhere to the prompt's constraints (including the 3000-character limit).
340
- 2: Quite good answer - quite clear, reasonably adherent to the topic, reasonably well-formulated, with no incorrect statements. For creative writing, some originality and imagination are present, but it may be somewhat predictable or have minor flaws. Adheres to the 3000-character limit.
341
- 3: Good answer - clear, relevant to the topic, well-formulated, with correct statements. For creative writing, this includes demonstrating good originality, imagination, and adherence to the prompt, including the 3000-character limit.
342
- 4: Very good answer - very clear, very relevant to the topic, expertly formulated, with highly correct statements. For creative writing, shows strong originality, a compelling narrative or poetic voice, and excellent adherence to the prompt, including the 3000-character limit.
343
- 5: Exceptionally good answer - only appliable to exceptional answers that match all the criteria of the previous "4: Very good answer", but also bring additional unique insights, perfectly sound original arguments, or other exceptional unexpected contributions to the topic. For creative writing, this indicates a truly outstanding piece of writing with exceptional creativity, emotional resonance, and masterful execution, while adhering to the 3000-character limit.
344
- Consider these criteria in your ranking:
345
- - Clarity: Is the answer easy to understand? Is it ambiguous or confusing?
346
- - Relevance: Is the answer relevant to the specified topic?
347
- - Formulation: Is the answer well-structured and grammatically correct? Is it logically sound? Is it in a form that proovs expert knowledge?
348
- - Correctness: Are the statements in the answer correct? (this is extremely relevant for topics such as math, grammar, logics, coding, science, technology)
349
- - Interest/Engagement: Is the answer likely to be engaging or thought-provoking? (minor consideration)
350
- """
351
-
352
- if topic == "creative writing": # More robust topic check
353
- prompt += """
354
- - (For Creative Writing ONLY): Originality: Is the writing original and imaginative? Does it avoid clichés?
355
- - (For Creative Writing ONLY): Emotional Resonance: Does the writing evoke emotion or connect with the reader on an emotional level?
356
- - (For Creative Writing ONLY): Adherence to Prompt: Does the writing fully address the specific requirements of the creative writing prompt?
357
- - (For Creative Writing ONLY): Character Limit: Does the writing adhere to the 3000-character limit?
358
- """
359
-
360
- prompt += f"""
361
- Just return a single number (the rank from 1 to 5), do not add any other text.
362
- Question: {question}
363
- Answer: {answer}
364
- Rank:"""
365
- return prompt
366
-
367
- def rank_question_prompt(question, topic, difficulty):
368
- difficulty_mapping_rank_prompt = {
369
- "a very simple": "very simple",
370
- "a simple": "simple",
371
- "a": "average",
372
- "a difficult": "difficult",
373
- "a very difficult": "very difficult"
374
- }
375
- difficulty_for_prompt = difficulty_mapping_rank_prompt[difficulty]
376
-
377
- prompt = f"""You are an expert evaluator of questions. Rank the quality of the following question on a scale of 1 to 5, where:
378
- 1: Very poor question - unclear, irrelevant to the topic, not appropriate for the difficulty level, or poorly formulated. For creative writing prompts, this also means the prompt is uninspired, lacks clear instructions, or sets an unreasonable character limit.
379
- 2: Poor question - somewhat unclear, loosely related to the topic, slightly inappropriate for the difficulty level, or with minor formulation issues. For creative writing, the prompt may be somewhat unimaginative or have minor clarity issues.
380
- 3: Good question - clear, relevant to the topic, generally appropriate for the difficulty level, and reasonably well-formulated. For creative writing, the prompt is clear, provides a reasonable starting point for creative work, and sets a clear 3000-character limit.
381
- 4: Very good question - clear, highly relevant to the topic, appropriate for the difficulty level, and well-formulated. For creative writing, the prompt is engaging, sparks imagination, and offers a good balance of direction and freedom, with a clear 3000-character limit.
382
- 5: Excellent question - exceptionally clear, insightful, highly relevant to the topic, perfectly matched to the difficulty level, and expertly formulated. For creative writing, the prompt is exceptionally creative, thought-provoking, and likely to inspire high-quality writing, with a clear 3000-character limit.
383
- Consider these criteria in your ranking:
384
- - Clarity: Is the question easy to understand? Is it ambiguous or confusing?
385
- - Relevance: Is the question relevant to the specified topic ({topic})?
386
- - Difficulty: Is the difficulty of the question appropriate for the indicated level ({difficulty_for_prompt})?
387
- - Formulation: Is the question well-structured and grammatically correct? Is it logically sound?
388
- - Interest/Engagement: Is the question likely to be engaging or thought-provoking? (minor consideration)
389
- """
390
- if topic == "creative writing":
391
- prompt += f"""
392
- - **(For Creative Writing ONLY): Creativity:** Does the prompt encourage original and imaginative responses?
393
- - **(For Creative Writing ONLY): Clarity of Constraints:** Are the creative constraints (e.g., story, poem, joke) and the 3000-character limit clearly stated?
394
- - **(For Creative Writing ONLY): Inspiration Potential:** Is the prompt likely to inspire high-quality, creative writing?
395
- """
396
- prompt += f"""
397
- Just return a single number (the rank from 1 to 5), do not add any other text.
398
- Question: {question}
399
- Rank:"""
400
- return prompt
401
-
402
- # --- Helper function to parse rank strings ---
403
- def parse_rank_string(rank_str, ranking_model_id):
404
- match = re.search(r'^\D*(\d+)', rank_str) # Regex to find the first integer
405
- if match:
406
- rank_str = match.group(1) # Extract the first captured group (the integer)
407
- try:
408
- rank_val = int(rank_str) # Convert to integer *after* regex extraction
409
- if not 1 <= rank_val <= 5: # Check if rank is within valid range
410
- log_debug(f"Warning: Model {ranking_model_id} returned rank outside of valid range [1-5]: {rank_val}. Rank set to None.")
411
- return None
412
- return rank_val
413
- except ValueError:
414
- log_debug(f"Warning: Model {ranking_model_id} returned non-integer rank after regex extraction: '{rank_str}'. Rank set to None.")
415
- return None
416
- else:
417
- log_debug(f"Warning: Model {ranking_model_id} returned non-numeric rank: '{rank_str}'. Rank set to None.")
418
- return None
419
-
420
- # --- Helper Function for Parallel Ranking ---
421
- def get_rank_from_model(ranking_model_id, question, answer, consecutive_failures, failure_threshold, unresponsive_models, model_config, topic, token=None, timeout=60):
422
- start_time = time.time()
423
- rank = None # Initialize rank to None, indicating potential failure
424
-
425
- rank_prompt = rank_answer_prompt(question, answer, topic)
426
-
427
- try:
428
- response = make_hf_request(model_config[ranking_model_id]["name"], [{"role": "user", "content": rank_prompt}], base_temp, 5, token=token)
429
- if response:
430
- try:
431
- rank_str = response.strip()
432
- rank = parse_rank_string(rank_str, ranking_model_id)
433
- except ValueError:
434
- log_debug(f"Warning: Model {ranking_model_id} returned non-integer rank: '{rank_str}'. Rank set to None.")
435
- rank = None
436
- else:
437
- log_debug(f"Warning: Model {ranking_model_id} failed to provide rank. Rank set to None.")
438
- except Exception as e:
439
- duration = time.time() - start_time
440
- log_debug(f"Warning: Model {ranking_model_id} ranking timed out or failed after {duration:.2f}s: {e}")
441
- rank = None
442
-
443
- duration = time.time() - start_time # Calculate total duration of ranking attempt
444
- if duration > timeout:
445
- log_debug(f"Warning: Ranking by model {ranking_model_id} exceeded timeout of {timeout:.2f}s and took {duration:.2f}s.")
446
- rank = None # Ensure rank is None if timeout occurs
447
-
448
- time.sleep(time_sleep) # Keep a small delay to avoid overwhelming APIs even in parallel
449
- return ranking_model_id, rank
450
-
451
- # --- Helper Function for Parallel Ranking of questions ---
452
- def get_question_rank_from_model(ranking_model_id, question, topic, difficulty, consecutive_failures, failure_threshold, unresponsive_models, model_config, token=None, timeout=60):
453
- start_time = time.time()
454
- rank = None # Initialize rank to None, indicating potential failure
455
-
456
- rank_prompt = rank_question_prompt(question, topic, difficulty) # Use question rank prompt
457
-
458
- try:
459
- response = make_hf_request(model_config[ranking_model_id]["name"], [{"role": "user", "content": rank_prompt}], base_temp, 5, token=token)
460
- if response:
461
- try:
462
- rank_str = response.strip()
463
- rank = parse_rank_string(rank_str, ranking_model_id)
464
- except ValueError:
465
- log_debug(f"Warning: Model {ranking_model_id} returned non-integer rank for question: '{rank_str}'. Rank set to None.")
466
- rank = None
467
- else:
468
- log_debug(f"Warning: Model {ranking_model_id} failed to provide rank for question. Rank set to None.")
469
- except Exception as e:
470
- duration = time.time() - start_time
471
- log_debug(f"Warning: Model {ranking_model_id} ranking question timed out or failed after {duration:.2f}s: {e}")
472
- rank = None
473
-
474
- duration = time.time() - start_time # Calculate total duration of ranking attempt
475
- if duration > timeout:
476
- log_debug(f"Warning: Ranking question by model {ranking_model_id} exceeded timeout of {timeout:.2f}s and took {duration:.2f}s.")
477
- rank = None # Ensure rank is None if timeout occurs
478
-
479
- time.sleep(time_sleep) # Keep a small delay to avoid overwhelming APIs even in parallel
480
- return ranking_model_id, rank
481
-
482
- # --- Helper Function for Parallel Answering ---
483
- def get_answer_from_model(model_id, question, consecutive_failures, failure_threshold, unresponsive_models, model_config, topic, token=None, timeout=60):
484
- start_time = time.time() # Start timer
485
- answer_prompt = answer_question_prompt(question)
486
- answer = "Error answering" # Default answer
487
-
488
- temp = answer_temp
489
- max_tok = answer_max_tokens
490
- if topic == "math" or topic == "coding" or topic == "grammar" or topic == "logics":
491
- temp = long_temp
492
- max_tok = long_max_tokens
493
-
494
- try:
495
- response = make_hf_request(model_config[model_id]["name"], [{"role": "user", "content": answer_prompt}], temp, max_tok, token=token)
496
- if response:
497
- answer = response.strip()
498
- except Exception as e:
499
- duration = time.time() - start_time
500
- log_debug(f"Warning: Model {model_id} answering timed out or failed after {duration:.2f}s: {e}")
501
- answer = "Error answering - Timeout" # Or a specific timeout error message
502
- return answer, duration # Return error answer and duration
503
-
504
- time.sleep(time_sleep) # Small delay
505
- duration = time.time() - start_time # Calculate duration
506
- custom_print(f"Answer generation by \"{model_id}\": {duration:.2f}s") # Print answer generation duration separately as requested - as requested
507
-
508
- return answer, duration # Return answer and duration
509
-
510
- # --- Core Logic ---
511
- def run_benchmark(hf_models, topics, difficulties, t, model_config, token=None):
512
- st.session_state['is_running'] = True
513
-
514
- results = {
515
- "model_name": [],
516
- "topic": [],
517
- "difficulty": [],
518
- "question_prompt": [],
519
- "question": [],
520
- "answer": [],
521
- "answer_generation_duration": [],
522
- "average_rank": [],
523
- "ranks":[],
524
- "question_rank_average": [],
525
- "question_ranks": [],
526
- "question_rank_duration": []
527
- }
528
-
529
- cumulative_model_ranks = {} # To store cumulative ranks for each model
530
-
531
- # Check if iteration output file exists and remove it if it does to start fresh
532
- if os.path.exists(iteration_output_file):
533
- os.remove(iteration_output_file)
534
-
535
- consecutive_failures = {} # Track failures per model ID
536
- failure_threshold = 5
537
- unresponsive_models = set()
538
-
539
- # Updated model lists with more informative labels
540
- active_models = hf_models.copy()
541
- model_weights = {}
542
-
543
- for model_id in active_models:
544
- cumulative_model_ranks[model_id] = []
545
- consecutive_failures[model_id] = 0
546
- model_weights[model_id] = 1.0 / len(active_models) # Initial equal weights
547
-
548
- difficulty_choices = list(difficulty_probabilities.keys())
549
- probability_values = list(difficulty_probabilities.values())
550
-
551
- # --- Difficulty mapping for output labels ---
552
- difficulty_mapping = {
553
- "a very simple": "1",
554
- "a simple": "2",
555
- "a": "3",
556
- "a difficult": "4",
557
- "a very difficult": "5"
558
- }
559
-
560
- s_t = 0 #count succesful iterations
561
-
562
- for iteration in range(t): # Added iteration counter
563
- # Update progress in the Streamlit app
564
- st.session_state['progress'] = (iteration + 1) / t
565
-
566
- if len(active_models) < 2:
567
- custom_print("Fewer than 2 active models remaining. Exiting benchmark.")
568
- break
569
-
570
- topic = random.choice(topics)
571
- # --- Select difficulty with probabilities ---
572
- difficulty = random.choices(difficulty_choices, weights=probability_values, k=1)[0] # Weighted random choice
573
- custom_print(f"--- Iteration {s_t + 1}/{t}: {difficulty} question ({difficulty_mapping[difficulty]}) on {topic} ---") # Print iteration number
574
-
575
- # --- Question Generation ---
576
- question = None
577
- question_prompt = generate_question_prompt(topic, difficulty)
578
-
579
- question_accepted = False # Flag to track if question is accepted
580
- question_ranks_all = []
581
- question_avg_rank = np.nan
582
- question_ranking_duration_total = 0
583
-
584
- cumulative_avg_rank = {} # To store cumulative average ranks for each model
585
-
586
- max_attempts = 3 * len(active_models)
587
- for attempt in range(max_attempts):
588
- # --- Filter for question generation roles ("answer" or "both") ---
589
- question_gen_candidates = [
590
- model_id for model_id in active_models
591
- if model_config[model_id].get("role", "both") in ["answer", "both"]
592
- ]
593
- if not question_gen_candidates: # No suitable models left
594
- custom_print("No models available for question generation with 'answer' or 'both' role. Skipping iteration.")
595
- continue # Skip to next iteration
596
-
597
- question_generator_model_id = random.choice(question_gen_candidates)
598
-
599
- # --- Question Generation ---
600
- response = make_hf_request(model_config[question_generator_model_id]["name"],
601
- [{"role": "user", "content": question_prompt}],
602
- question_temp,
603
- question_max_tokens,
604
- token=token)
605
-
606
- if response:
607
- question = response.strip()
608
- consecutive_failures[question_generator_model_id] = 0 # Reset on success
609
- break
610
- else:
611
- custom_print(f"Skipping due to request failure for model {question_generator_model_id}.")
612
- consecutive_failures[question_generator_model_id] += 1
613
-
614
- if consecutive_failures[question_generator_model_id] >= failure_threshold:
615
- custom_print(f"Model {question_generator_model_id} is unresponsive (question gen). Removing from active models.")
616
- if question_generator_model_id in active_models:
617
- active_models.remove(question_generator_model_id)
618
- unresponsive_models.add(question_generator_model_id)
619
- time.sleep(time_sleep)
620
-
621
- if question is None:
622
- custom_print(f"Failed to generate a question after {max_attempts} attempts. Skipping this round.")
623
- continue
624
-
625
- # --- Parallel Question Ranking ---
626
- question_ranks = {}
627
- question_ranking_futures = []
628
- question_ranking_start_time = time.time()
629
-
630
- with concurrent.futures.ThreadPoolExecutor(max_workers=len(active_models) or 1) as executor:
631
- for ranking_model_id in active_models:
632
- # --- Filter for ranking roles ("rank" or "both") ---
633
- if model_config[ranking_model_id].get("role", "both") in ["rank", "both"]:
634
- future = executor.submit(
635
- get_question_rank_from_model,
636
- ranking_model_id,
637
- question,
638
- topic,
639
- difficulty,
640
- consecutive_failures,
641
- failure_threshold,
642
- unresponsive_models,
643
- model_config,
644
- token,
645
- timeout=60
646
- )
647
- question_ranking_futures.append(future)
648
-
649
- for future in concurrent.futures.as_completed(question_ranking_futures): # Collect ranks as they become available
650
- try:
651
- ranking_model_id, rank = future.result() # Get model_id and rank
652
- question_ranks[ranking_model_id] = rank # Store rank with model_id as key
653
- except Exception as e:
654
- log_debug(f"Error getting question rank result: {e}")
655
-
656
- question_ranking_end_time = time.time()
657
- question_ranking_duration_total = question_ranking_end_time - question_ranking_start_time
658
-
659
- # Filter out None values (failed ranks) and calculate weighted average
660
- valid_question_ranks_values = [r for r in question_ranks.values() if r is not None] # Get rank values
661
- question_avg_rank = np.nan # Default to NaN
662
-
663
- if valid_question_ranks_values:
664
- # Create a list of weights corresponding to the valid ranks
665
- weights_for_valid_question_ranks = [model_weights[model_id]
666
- for model_id, rank in question_ranks.items()
667
- if rank is not None]
668
-
669
- #check that the length is correct
670
- if len(weights_for_valid_question_ranks) != len(valid_question_ranks_values):
671
- log_debug("Warning: Mismatch length of weights and valid question ranks")
672
- log_debug(f'weights_for_valid_question_ranks {weights_for_valid_question_ranks}')
673
- log_debug(f'valid_question_ranks_values: {valid_question_ranks_values}')
674
-
675
- question_avg_rank = np.average(valid_question_ranks_values, weights=weights_for_valid_question_ranks)
676
- min_question_rank = min(valid_question_ranks_values) if valid_question_ranks_values else 0 # To avoid error if no valid rank
677
-
678
- if question_avg_rank >= question_treshold and all(rank > reject_rank for rank in valid_question_ranks_values): # Question acceptance criteria
679
- question_accepted = True
680
- custom_print(f"Question accepted. Avg Question Rank: {question_avg_rank:.2f}, Min Rank: {min_question_rank}, Ranks: {[question_ranks.get(m, None) for m in active_models if m in question_ranks]}")
681
- s_t += 1
682
- else:
683
- question_accepted = False
684
- custom_print(f"Question rejected. Avg Question Rank: {question_avg_rank:.2f}, Min Rank: {min_question_rank}, Ranks: {[question_ranks.get(m, None) for m in active_models if m in question_ranks]}")
685
-
686
- if not question_accepted:
687
- custom_print("Generated question was not accepted. Regenerating question.")
688
- continue
689
-
690
- if len(active_models) < 2:
691
- custom_print("Fewer than 2 active models remaining. Exiting benchmark.")
692
- break
693
-
694
- # --- Parallel Answer Generation ---
695
- answers = {}
696
- answer_futures = []
697
- answer_durations = {}
698
- with concurrent.futures.ThreadPoolExecutor(max_workers=len(active_models)) as executor:
699
- for model_id in active_models:
700
- # --- Filter for answer generation roles ("answer" or "both") ---
701
- if model_config[model_id].get("role", "both") in ["answer", "both"]:
702
- try:
703
- future = executor.submit(
704
- get_answer_from_model,
705
- model_id,
706
- question,
707
- consecutive_failures,
708
- failure_threshold,
709
- unresponsive_models,
710
- model_config,
711
- topic,
712
- token,
713
- timeout=60
714
- )
715
- answer_futures.append((model_id, future))
716
- except Exception as e:
717
- log_debug(f"Error submitting answer task for {model_id}: {e}")
718
- answer = "Error answering - Task submission failed"
719
- duration = 0
720
- answers[model_id] = answer
721
- answer_durations[model_id] = duration
722
-
723
- for model_id, future in answer_futures:
724
- try:
725
- answer, duration = future.result() # Get both answer and duration
726
- answers[model_id] = answer
727
- answer_durations[model_id] = duration
728
- except Exception as e:
729
- log_debug(f"Error getting answer from {model_id}: {e}")
730
- answers[model_id] = "Error answering - Future result failed"
731
- answer_durations[model_id] = 0
732
-
733
- # --- Ranking Process ---
734
-
735
- # Prepare to write to file (open in append mode outside the model loop but inside iteration loop)
736
- try:
737
- iteration_results_file_opened = open(iteration_output_file, 'a')
738
- if iteration == 0: # Write header only for the first iteration
739
- iteration_results_file_opened.write("Iteration, Topic, Difficulty, Question Rank, QR Duration, Model,Cumulative Avg Rank,Iteration Avg Rank,Ranks,Ranking Duration (sec)\n") # Added Ranking Duration to header
740
- except Exception as e:
741
- log_debug(f"Error opening results file: {e}")
742
- iteration_results_file_opened = None
743
-
744
-
745
- for model_id in active_models:
746
- if model_id not in answers:
747
- log_debug(f"No answer found for model {model_id}. Skipping ranking.")
748
- continue
749
-
750
- answer = answers[model_id]
751
- if answer == "Error answering" or answer.startswith("Error answering -"): # Handle answer generation errors
752
- consecutive_failures[model_id] += 1
753
- if consecutive_failures[model_id] >= failure_threshold:
754
- custom_print(f"Model {model_id} is consistently failing to answer. Removing from active models.")
755
- if model_id in active_models: # double check before removing, might have been removed in another thread
756
- active_models.remove(model_id)
757
- unresponsive_models.add(model_id)
758
- continue # Skip ranking if answer generation failed for this model
759
-
760
-
761
- if len(active_models) < 2: # Re-check active models before ranking
762
- custom_print("Fewer than 2 active models remaining. Exiting benchmark.")
763
- break
764
-
765
- ranks = {}
766
- ranking_futures = []
767
-
768
- ranking_start_time = time.time()
769
- with concurrent.futures.ThreadPoolExecutor(max_workers=len(active_models) or 1) as executor:
770
- for ranking_model_id in active_models:
771
- # --- Filter for ranking roles ("rank" or "both") ---
772
- if model_config[ranking_model_id].get("role", "both") in ["rank", "both"]:
773
- try:
774
- future = executor.submit(
775
- get_rank_from_model,
776
- ranking_model_id,
777
- question,
778
- answer,
779
- consecutive_failures,
780
- failure_threshold,
781
- unresponsive_models,
782
- model_config,
783
- topic,
784
- token,
785
- timeout=60
786
- )
787
- ranking_futures.append(future)
788
- except Exception as e:
789
- log_debug(f"Error submitting ranking task for {ranking_model_id}: {e}")
790
-
791
- for future in concurrent.futures.as_completed(ranking_futures): # Collect ranks as they become available
792
- try:
793
- ranking_model_id, rank = future.result() # Get model_id and rank
794
- ranks[ranking_model_id] = rank # Store rank with model_id as key
795
- except Exception as e:
796
- log_debug(f"Error getting rank result: {e}")
797
-
798
- ranking_end_time = time.time() # Record end time of ranking
799
- ranking_duration = ranking_end_time - ranking_start_time # Calculate duration
800
-
801
- # Filter out None values (failed ranks) and calculate weighted average
802
- valid_ranks_values = [r for r in ranks.values() if r is not None] # Get rank values
803
- average_rank = np.nan # Default to NaN
804
-
805
- if valid_ranks_values:
806
- #Create a list of weights corresponding to the valid ranks
807
- weights_for_valid_ranks = [model_weights[model_id]
808
- for model_id, rank in ranks.items()
809
- if rank is not None]
810
-
811
-
812
- if len(weights_for_valid_ranks) != len(valid_ranks_values):
813
- log_debug("Warning: Mismatch length of weights and valid answer ranks")
814
- log_debug(f'weights_for_valid_ranks {weights_for_valid_ranks}')
815
- log_debug(f'valid_ranks_values: {valid_ranks_values}')
816
-
817
- average_rank = np.average(valid_ranks_values, weights=weights_for_valid_ranks)
818
-
819
- results["model_name"].append(model_id)
820
- results["topic"].append(topic)
821
- results["difficulty"].append(difficulty)
822
- results["question_prompt"].append(question_prompt)
823
- results["question"].append(question)
824
- results["answer"].append(answer)
825
- results["answer_generation_duration"].append(answer_durations.get(model_id, 0))
826
- results["average_rank"].append(average_rank)
827
- results["ranks"].append([ranks.get(m, None) for m in active_models if m in ranks]) # Store raw ranks including Nones, ensure order
828
- results["question_rank_average"].append(question_avg_rank) # Store question rank average
829
- results["question_ranks"].append([question_ranks.get(m, None) for m in active_models if m in question_ranks]) # Store question ranks
830
- results["question_rank_duration"].append(question_ranking_duration_total) # Store question ranking duration
831
-
832
- if model_id in cumulative_model_ranks:
833
- cumulative_model_ranks[model_id].append(average_rank) # Append current iteration's average rank
834
-
835
- if model_id in cumulative_model_ranks and cumulative_model_ranks[model_id]:
836
- cumulative_avg_rank[model_id] = np.nanmean([r for r in cumulative_model_ranks[model_id] if not np.isnan(r)])
837
- else:
838
- cumulative_avg_rank[model_id] = np.nan
839
-
840
- # --- Print and store iteration results IMMEDIATELY after ranking for this model ---
841
- ranks_str = "[" + ", ".join(map(str, [ranks.get(m, None) for m in active_models if m in ranks])) + "]" if ranks else "[]" # Format ranks for CSV, ensure order
842
- custom_print(f"{topic}, {difficulty_mapping[difficulty]}, {model_id}, {cumulative_avg_rank.get(model_id, np.nan):.2f}, {average_rank:.5f}, {ranks_str}, {ranking_duration:.2f} sec")
843
-
844
- # Write iteration results to file (append mode) - write for each model right after ranking
845
- if iteration_results_file_opened:
846
- try:
847
- iteration_results_file_opened.write(f"{iteration+1},{topic}, {difficulty_mapping[difficulty]},{question_avg_rank:.2f},{question_ranking_duration_total:.2f},{model_id},{cumulative_avg_rank.get(model_id, np.nan):.2f},{average_rank:.2f},{ranks_str},{ranking_duration:.2f}\n")
848
- except Exception as e:
849
- log_debug(f"Error writing to results file: {e}")
850
-
851
- # Update model weights based on cumulative average ranks, handling NaNs
852
- temp_weights = {}
853
- total_valid_rank = 0 # Keep track of the sum of valid (non-NaN) ranks
854
-
855
- for m_id in active_models:
856
- if m_id in cumulative_avg_rank and not np.isnan(cumulative_avg_rank[m_id]):
857
- temp_weights[m_id] = cumulative_avg_rank[m_id]
858
- total_valid_rank += cumulative_avg_rank[m_id]
859
- else: # if cumulative is empty, keep original
860
- temp_weights[m_id] = model_weights.get(m_id, 1.0 / len(active_models))
861
-
862
- # Normalize the weights so they sum to 1, handling cases where total_valid_rank might be zero
863
- if total_valid_rank > 0:
864
- for m_id in temp_weights:
865
- model_weights[m_id] = temp_weights[m_id] / total_valid_rank
866
- else:
867
- # If total_valid_rank is 0 (all models have NaN ranks), revert to equal weights
868
- for m_id in active_models:
869
- model_weights[m_id] = 1.0 / len(active_models)
870
-
871
- if iteration_results_file_opened:
872
- iteration_results_file_opened.close()
873
-
874
- custom_print(f"Unresponsive models during this run: {unresponsive_models}")
875
- st.session_state['is_running'] = False
876
- return results, cumulative_avg_rank, s_t
877
-
878
- def check_model_availability(models, token):
879
- """Test if models are available with the provided token"""
880
- availability_results = {}
881
-
882
- for model_name in models:
883
- st.write(f"Testing availability of {model_name}...")
884
- try:
885
- # Create a simple test prompt
886
- test_prompt = "Hello, are you available?"
887
-
888
- # Use a short timeout to quickly test connectivity
889
- client = InferenceClient(model=model_name, token=token)
890
- response = client.text_generation(
891
- test_prompt,
892
- max_new_tokens=10,
893
- temperature=0.7,
894
- do_sample=True
895
- )
896
-
897
- availability_results[model_name] = {
898
- "available": True,
899
- "response": response[:50] + "..." if len(response) > 50 else response
900
- }
901
- st.success(f"✅ {model_name} is available")
902
-
903
- except Exception as e:
904
- error_msg = str(e)
905
- availability_results[model_name] = {
906
- "available": False,
907
- "error": error_msg
908
- }
909
-
910
- if "401" in error_msg or "unauthorized" in error_msg.lower():
911
- st.error(f"❌ {model_name}: Authentication error. Check your API token.")
912
- elif "404" in error_msg or "not found" in error_msg.lower():
913
- st.error(f"❌ {model_name}: Model not found. It may not exist or you may not have access.")
914
- elif "429" in error_msg or "rate limit" in error_msg.lower():
915
- st.error(f"❌ {model_name}: Rate limit exceeded. Try again later.")
916
- else:
917
- st.error(f"❌ {model_name}: Unknown error: {error_msg}")
918
-
919
- time.sleep(1) # Add delay between checks
920
-
921
- return availability_results
922
-
923
- # Streamlit UI
924
- st.title("LLM Benchmark")
925
-
926
- # Setup sidebar for configuration
927
- st.sidebar.header("Configuration")
928
-
929
- # Add a field for the Hugging Face token
930
- hf_token = st.sidebar.text_input("Hugging Face API Token", type="password",
931
- help="Your Hugging Face API token to access models")
932
-
933
- # Model selection
934
- st.sidebar.subheader("Models")
935
- available_models = [
936
- "meta-llama/Llama-3.1-8B-Instruct",
937
- "meta-llama/Meta-Llama-3-8B-Instruct",
938
- "google/gemma-2-2b-it",
939
- "mistralai/Mistral-7B-Instruct-v0.2",
940
- "microsoft/Phi-3-mini-4k-instruct",
941
- # Add more models as needed
942
- ]
943
-
944
- selected_models = st.sidebar.multiselect(
945
- "Select models to benchmark",
946
- available_models,
947
- default=available_models[:3] # Default to first 3 models
948
- )
949
-
950
- # Topic selection
951
- available_topics = ["math", "logics", "grammar", "coding", "history", "current news",
952
- "general culture", "science", "technology", "creative writing"]
953
- selected_topics = st.sidebar.multiselect(
954
- "Select topics",
955
- available_topics,
956
- default=available_topics
957
- )
958
-
959
- # Number of iterations
960
- num_iterations = st.sidebar.slider("Number of iterations", 1, 20, 5)
961
-
962
- # Create model_config dictionary from selected models
963
- model_config = {}
964
- for model in selected_models:
965
- model_config[model] = {"name": model, "role": "both"}
966
-
967
- # Create tabs for different views
968
- tab1, tab2, tab3 = st.tabs(["Benchmark", "Progress Log", "Debug Log"])
969
-
970
- with tab1:
971
- if st.sidebar.button("Test Selected Models"):
972
- if not hf_token:
973
- st.error("Please enter your Hugging Face API token")
974
- elif not selected_models:
975
- st.error("Please select at least one model")
976
- else:
977
- with st.spinner("Testing model availability..."):
978
- availability = check_model_availability(selected_models, hf_token)
979
-
980
- # Show results in a table
981
- availability_df = pd.DataFrame([
982
- {
983
- "Model": model,
984
- "Available": info["available"],
985
- "Status": "Available" if info["available"] else "Error",
986
- "Details": info.get("response", "") if info["available"] else info.get("error", "")
987
- }
988
- for model, info in availability.items()
989
- ])
990
-
991
- st.dataframe(availability_df)
992
-
993
- # Check if we have enough models to run the benchmark
994
- available_models = [m for m, info in availability.items() if info["available"]]
995
- if len(available_models) >= 2:
996
- st.success(f"{len(available_models)} models are available for benchmarking")
997
- else:
998
- st.error("You need at least 2 available models to run the benchmark")
999
-
1000
- # Progress bar
1001
- progress_bar = st.progress(st.session_state['progress'])
1002
-
1003
- # Start benchmark button
1004
- if st.sidebar.button("Start Benchmark"):
1005
- # Clear previous outputs
1006
- st.session_state['main_output'] = []
1007
- st.session_state['debug_output'] = []
1008
- st.session_state['progress'] = 0
1009
-
1010
- if not hf_token:
1011
- st.error("Please enter your Hugging Face API token")
1012
- elif not selected_models:
1013
- st.error("Please select at least two models")
1014
- elif not selected_topics:
1015
- st.error("Please select at least one topic")
1016
- else:
1017
- # Setup to capture results for display
1018
- results_container = st.container()
1019
-
1020
- # Run the benchmark
1021
- try:
1022
- # Run benchmark and get results
1023
- results, cumulative_avg_rank, total_successful = run_benchmark(
1024
- selected_models, selected_topics,
1025
- ["a very simple", "a simple", "a", "a difficult", "a very difficult"],
1026
- num_iterations, model_config, hf_token
1027
- )
1028
-
1029
- # Update progress to complete
1030
- st.session_state['progress'] = 1.0
1031
- progress_bar.progress(1.0)
1032
-
1033
- # Display results
1034
- if total_successful > 0:
1035
- results_df = pd.DataFrame(results)
1036
- st.session_state['results_df'] = results_df
1037
-
1038
- # Show model rankings
1039
- st.subheader("Model Rankings")
1040
- ranking_df = pd.DataFrame({
1041
- "Model": list(cumulative_avg_rank.keys()),
1042
- "Average Rank": [round(r, 2) if not np.isnan(r) else 'N/A' for r in cumulative_avg_rank.values()]
1043
- })
1044
- ranking_df = ranking_df.sort_values("Average Rank", ascending=False)
1045
- st.dataframe(ranking_df)
1046
-
1047
- # Show detailed results
1048
- st.subheader("Detailed Results")
1049
- st.dataframe(results_df)
1050
-
1051
- # Option to download results
1052
- csv = results_df.to_csv(index=False)
1053
- st.download_button(
1054
- label="Download Results CSV",
1055
- data=csv,
1056
- file_name="llm_benchmark_results.csv",
1057
- mime="text/csv",
1058
- )
1059
- else:
1060
- st.warning("The benchmark did not complete any successful iterations.")
1061
- except Exception as e:
1062
- st.error(f"An error occurred: {e}")
1063
- st.exception(e)
1064
-
1065
- # Show previous results if available
1066
- elif 'results_df' in st.session_state and not st.session_state['results_df'].empty:
1067
- st.subheader("Previous Results")
1068
- st.dataframe(st.session_state['results_df'])
1069
-
1070
- with tab2:
1071
- # Display main output log
1072
- st.subheader("Execution Log")
1073
-
1074
- # Display logs
1075
- if 'main_output' in st.session_state:
1076
- log_text = "\n".join(st.session_state['main_output'])
1077
- st.text_area("Progress Log", log_text, height=400)
1078
- else:
1079
- st.text_area("Progress Log", "No progress logs yet.", height=400)
1080
-
1081
- # Add a refresh button for the log
1082
- if st.button("Refresh Progress Log"):
1083
- st.experimental_rerun()
1084
-
1085
- with tab3:
1086
- # Display debug output
1087
- st.subheader("Debug Log")
1088
-
1089
- # Display debug logs
1090
- if 'debug_output' in st.session_state:
1091
- debug_text = "\n".join(st.session_state['debug_output'])
1092
- st.text_area("Debug Information", debug_text, height=400)
1093
- else:
1094
- st.text_area("Debug Information", "No debug logs yet.", height=400)
1095
-
1096
- # Add a refresh button for the debug log
1097
- if st.button("Refresh Debug Log"):
1098
- st.experimental_rerun()
1099
-
1100
- # Auto-update while benchmark is running
1101
- if st.session_state.get('is_running', False):
1102
- st.empty()
1103
- time.sleep(5) # Update every 5 seconds while running
1104
- st.rerun()