oceansweep commited on
Commit
e406469
1 Parent(s): 3655951

Upload 3 files

Browse files
App_Function_Libraries/Benchmarks_Evaluations/Confabulation_check.py ADDED
@@ -0,0 +1,81 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Confabulation_check.py
2
+ #
3
+ # This file contains the functions that are used to check the confabulation of the user's input.
4
+ #
5
+ #
6
+ # Imports
7
+ #
8
+ # External Imports
9
+ #
10
+ # Local Imports
11
+ #
12
+ #
13
+ ####################################################################################################
14
+ #
15
+ # Functions:
16
+ from App_Function_Libraries.Chat import chat_api_call
17
+ from App_Function_Libraries.Benchmarks_Evaluations.ms_g_eval import validate_inputs, detailed_api_error
18
+
19
+
20
+ def simplified_geval(transcript: str, summary: str, api_name: str, api_key: str, temp: float = 0.7) -> str:
21
+ """
22
+ Perform a simplified version of G-Eval using a single query to evaluate the summary.
23
+
24
+ Args:
25
+ transcript (str): The original transcript
26
+ summary (str): The summary to be evaluated
27
+ api_name (str): The name of the LLM API to use
28
+ api_key (str): The API key for the chosen LLM
29
+ temp (float, optional): The temperature parameter for the API call. Defaults to 0.7.
30
+
31
+ Returns:
32
+ str: The evaluation result
33
+ """
34
+ try:
35
+ validate_inputs(transcript, summary, api_name, api_key)
36
+ except ValueError as e:
37
+ return str(e)
38
+
39
+ prompt = f"""You are an AI assistant tasked with evaluating the quality of a summary. You will be given an original transcript and a summary of that transcript. Your task is to evaluate the summary based on the following criteria:
40
+
41
+ 1. Coherence (1-5): How well-structured and organized is the summary?
42
+ 2. Consistency (1-5): How factually aligned is the summary with the original transcript?
43
+ 3. Fluency (1-3): How well-written is the summary in terms of grammar, spelling, and readability?
44
+ 4. Relevance (1-5): How well does the summary capture the important information from the transcript?
45
+
46
+ Please provide a score for each criterion and a brief explanation for your scoring. Then, give an overall assessment of the summary's quality.
47
+
48
+ Original Transcript:
49
+ {transcript}
50
+
51
+ Summary to Evaluate:
52
+ {summary}
53
+
54
+ Please provide your evaluation in the following format:
55
+ Coherence: [score] - [brief explanation]
56
+ Consistency: [score] - [brief explanation]
57
+ Fluency: [score] - [brief explanation]
58
+ Relevance: [score] - [brief explanation]
59
+
60
+ Overall Assessment: [Your overall assessment of the summary's quality]
61
+ """
62
+
63
+ try:
64
+ result = chat_api_call(
65
+ api_name,
66
+ api_key,
67
+ prompt,
68
+ "",
69
+ temp=temp,
70
+ system_message="You are a helpful AI assistant tasked with evaluating summaries."
71
+ )
72
+ except Exception as e:
73
+ return detailed_api_error(api_name, e)
74
+
75
+ formatted_result = f"""
76
+ Confabulation Check Results:
77
+
78
+ {result}
79
+ """
80
+
81
+ return formatted_result
App_Function_Libraries/Benchmarks_Evaluations/__init__.py ADDED
File without changes
App_Function_Libraries/Benchmarks_Evaluations/ms_g_eval.py ADDED
@@ -0,0 +1,498 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #######################################################################################################################
2
+ #
3
+ # Evaluations_Benchmarks_tab.py
4
+ #
5
+ # Description: This file contains the code to evaluate the generated text using G-Eval metric.
6
+ #
7
+ # Scripts taken from https://github.com/microsoft/promptflow/tree/main/examples/flows/evaluation/eval-summarization and modified.
8
+ #
9
+ import configparser
10
+ import inspect
11
+ import json
12
+ import logging
13
+ import os
14
+ import re
15
+ from typing import Dict, Callable, List, Any
16
+
17
+ import gradio as gr
18
+ from tenacity import (
19
+ RetryError,
20
+ Retrying,
21
+ after_log,
22
+ before_sleep_log,
23
+ stop_after_attempt,
24
+ wait_random_exponential,
25
+ )
26
+
27
+ from App_Function_Libraries.Chat import chat_api_call
28
+
29
+ #
30
+ #######################################################################################################################
31
+ #
32
+ # Start of G-Eval.py
33
+
34
+ logger = logging.getLogger(__name__)
35
+
36
+ current_dir = os.path.dirname(os.path.abspath(__file__))
37
+ # Construct the path to the config file
38
+ config_path = os.path.join(current_dir, 'Config_Files', 'config.txt')
39
+ # Read the config file
40
+ config = configparser.ConfigParser()
41
+ config.read(config_path)
42
+
43
+
44
+ def aggregate(
45
+ fluency_list: List[float],
46
+ consistency_list: List[float],
47
+ relevance_list: List[float],
48
+ coherence_list: List[float],
49
+ ) -> Dict[str, float]:
50
+ """
51
+ Takes list of scores for 4 dims and outputs average for them.
52
+
53
+ Args:
54
+ fluency_list (List(float)): list of fluency scores
55
+ consistency_list (List(float)): list of consistency scores
56
+ relevance_list (List(float)): list of relevance scores
57
+ coherence_list (List(float)): list of coherence scores
58
+
59
+ Returns:
60
+ Dict[str, float]: Returns average scores
61
+ """
62
+ average_fluency = sum(fluency_list) / len(fluency_list)
63
+ average_consistency = sum(consistency_list) / len(consistency_list)
64
+ average_relevance = sum(relevance_list) / len(relevance_list)
65
+ average_coherence = sum(coherence_list) / len(coherence_list)
66
+
67
+ log_metric("average_fluency", average_fluency)
68
+ log_metric("average_consistency", average_consistency)
69
+ log_metric("average_relevance", average_relevance)
70
+ log_metric("average_coherence", average_coherence)
71
+
72
+ return {
73
+ "average_fluency": average_fluency,
74
+ "average_consistency": average_consistency,
75
+ "average_relevance": average_relevance,
76
+ "average_coherence": average_coherence,
77
+ }
78
+
79
+ def run_geval(transcript: str, summary: str, api_key: str, api_name: str = None, save: bool = False):
80
+ try:
81
+ validate_inputs(transcript, summary, api_name, api_key)
82
+ except ValueError as e:
83
+ return str(e)
84
+
85
+ prompts = {
86
+ "coherence": """You will be given one summary written for a source document.
87
+
88
+ Your task is to rate the summary on one metric.
89
+
90
+ Please make sure you read and understand these instructions carefully. Please keep this document open while reviewing, and refer to it as needed.
91
+
92
+ Evaluation Criteria:
93
+
94
+ Coherence (1-5) - the collective quality of all sentences. We align this dimension with the DUC quality question of structure and coherence whereby "the summary should be well-structured and well-organized. The summary should not just be a heap of related information, but should build from sentence to a coherent body of information about a topic."
95
+
96
+ Evaluation Steps:
97
+
98
+ 1. Read the source document carefully and identify the main topic and key points.
99
+ 2. Read the summary and compare it to the source document. Check if the summary covers the main topic and key points of the source document, and if it presents them in a clear and logical order.
100
+ 3. Assign a score for coherence on a scale of 1 to 5, where 1 is the lowest and 5 is the highest based on the Evaluation Criteria.
101
+
102
+
103
+ Example:
104
+
105
+
106
+ Source Document:
107
+
108
+ {{Document}}
109
+
110
+ Summary:
111
+
112
+ {{Summary}}
113
+
114
+
115
+ Evaluation Form (scores ONLY):
116
+
117
+ - Coherence:""",
118
+ "consistency": """You will be given a source document. You will then be given one summary written for this source document.
119
+
120
+ Your task is to rate the summary on one metric.
121
+
122
+ Please make sure you read and understand these instructions carefully. Please keep this document open while reviewing, and refer to it as needed.
123
+
124
+
125
+ Evaluation Criteria:
126
+
127
+ Consistency (1-5) - the factual alignment between the summary and the summarized source. A factually consistent summary contains only statements that are entailed by the source document. Annotators were also asked to penalize summaries that contained hallucinated facts.
128
+
129
+ Evaluation Steps:
130
+
131
+ 1. Read the source document carefully and identify the main facts and details it presents.
132
+ 2. Read the summary and compare it to the source document. Check if the summary contains any factual errors that are not supported by the source document.
133
+ 3. Assign a score for consistency based on the Evaluation Criteria.
134
+
135
+
136
+ Example:
137
+
138
+
139
+ Source Document:
140
+
141
+ {{Document}}
142
+
143
+ Summary:
144
+
145
+ {{Summary}}
146
+
147
+
148
+ Evaluation Form (scores ONLY):
149
+
150
+ - Consistency:""",
151
+ "fluency": """You will be given one summary written for a source document.
152
+
153
+ Your task is to rate the summary on one metric.
154
+
155
+ Please make sure you read and understand these instructions carefully. Please keep this document open while reviewing, and refer to it as needed.
156
+
157
+
158
+ Evaluation Criteria:
159
+
160
+ Fluency (1-3): the quality of the summary in terms of grammar, spelling, punctuation, word choice, and sentence structure.
161
+
162
+ - 1: Poor. The summary has many errors that make it hard to understand or sound unnatural.
163
+ - 2: Fair. The summary has some errors that affect the clarity or smoothness of the text, but the main points are still comprehensible.
164
+ - 3: Good. The summary has few or no errors and is easy to read and follow.
165
+
166
+
167
+ Example:
168
+
169
+ Summary:
170
+
171
+ {{Summary}}
172
+
173
+
174
+ Evaluation Form (scores ONLY):
175
+
176
+ - Fluency (1-3):""",
177
+ "relevance": """You will be given one summary written for a source document.
178
+
179
+ Your task is to rate the summary on one metric.
180
+
181
+ Please make sure you read and understand these instructions carefully. Please keep this document open while reviewing, and refer to it as needed.
182
+
183
+ Evaluation Criteria:
184
+
185
+ Relevance (1-5) - selection of important content from the source. The summary should include only important information from the source document. Annotators were instructed to penalize summaries which contained redundancies and excess information.
186
+
187
+ Evaluation Steps:
188
+
189
+ 1. Read the summary and the source document carefully.
190
+ 2. Compare the summary to the source document and identify the main points of the source document.
191
+ 3. Assess how well the summary covers the main points of the source document, and how much irrelevant or redundant information it contains.
192
+ 4. Assign a relevance score from 1 to 5.
193
+
194
+
195
+ Example:
196
+
197
+
198
+ Source Document:
199
+
200
+ {{Document}}
201
+
202
+ Summary:
203
+
204
+ {{Summary}}
205
+
206
+
207
+ Evaluation Form (scores ONLY):
208
+
209
+ - Relevance:"""
210
+ }
211
+
212
+ scores = {}
213
+ explanations = {}
214
+ for metric, prompt in prompts.items():
215
+ full_prompt = prompt.replace("{{Document}}", transcript).replace("{{Summary}}", summary)
216
+ try:
217
+ score = geval_summarization(full_prompt, 5 if metric != "fluency" else 3, api_name, api_key)
218
+ scores[metric] = score
219
+ explanations[metric] = "Score based on the evaluation criteria."
220
+ except Exception as e:
221
+ error_message = detailed_api_error(api_name, e)
222
+ return error_message
223
+
224
+ avg_scores = aggregate([scores['fluency']], [scores['consistency']],
225
+ [scores['relevance']], [scores['coherence']])
226
+
227
+ results = {
228
+ "scores": scores,
229
+ "average_scores": avg_scores
230
+ }
231
+ logging.debug("Results: %s", results)
232
+
233
+ if save is not None:
234
+ logging.debug("Saving results to geval_results.json")
235
+ save_eval_results(results)
236
+ logging.debug("Results saved to geval_results.json")
237
+
238
+ formatted_result = f"""
239
+ Confabulation Check Results:
240
+
241
+ Coherence: {scores['coherence']:.2f} - {explanations['coherence']}
242
+ Consistency: {scores['consistency']:.2f} - {explanations['consistency']}
243
+ Fluency: {scores['fluency']:.2f} - {explanations['fluency']}
244
+ Relevance: {scores['relevance']:.2f} - {explanations['relevance']}
245
+
246
+ Overall Assessment: The summary has been evaluated on four key metrics.
247
+ The average scores are:
248
+ Fluency: {avg_scores['average_fluency']:.2f}
249
+ Consistency: {avg_scores['average_consistency']:.2f}
250
+ Relevance: {avg_scores['average_relevance']:.2f}
251
+ Coherence: {avg_scores['average_coherence']:.2f}
252
+
253
+ These scores indicate the overall quality of the summary in terms of its
254
+ coherence, consistency with the original text, fluency of language, and
255
+ relevance of content.
256
+ """
257
+
258
+ return formatted_result
259
+
260
+
261
+ def create_geval_tab():
262
+ with gr.Tab("G-Eval"):
263
+ gr.Markdown("# G-Eval Summarization Evaluation")
264
+ with gr.Row():
265
+ with gr.Column():
266
+ document_input = gr.Textbox(label="Source Document", lines=10)
267
+ summary_input = gr.Textbox(label="Summary", lines=5)
268
+ api_name_input = gr.Dropdown(
269
+ choices=["OpenAI", "Anthropic", "Cohere", "Groq", "OpenRouter", "DeepSeek", "HuggingFace", "Mistral", "Llama.cpp", "Kobold", "Ooba", "Tabbyapi", "VLLM", "Local-LLM", "Ollama"],
270
+ label="Select API"
271
+ )
272
+ api_key_input = gr.Textbox(label="API Key (if required)", type="password")
273
+ save_value = gr.Checkbox(label="Save Results to a JSON file(geval_results.json)")
274
+ evaluate_button = gr.Button("Evaluate Summary")
275
+ with gr.Column():
276
+ output = gr.Textbox(label="Evaluation Results", lines=10)
277
+
278
+ evaluate_button.click(
279
+ fn=run_geval,
280
+ inputs=[document_input, summary_input, api_name_input, api_key_input, save_value],
281
+ outputs=output
282
+ )
283
+
284
+ return document_input, summary_input, api_name_input, api_key_input, evaluate_button, output
285
+
286
+
287
+ def parse_output(output: str, max: float) -> float:
288
+ """
289
+ Function that extracts numerical score from the beginning of string
290
+
291
+ Args:
292
+ output (str): String to search
293
+ max (float): Maximum score allowed
294
+
295
+ Returns:
296
+ float: The extracted score
297
+ """
298
+ matched: List[str] = re.findall(r"(?<!\S)\d+(?:\.\d+)?", output)
299
+ if matched:
300
+ if len(matched) == 1:
301
+ score = float(matched[0])
302
+ if score > max:
303
+ raise ValueError(f"Parsed number: {score} was larger than max score: {max}")
304
+ else:
305
+ raise ValueError(f"More than one number detected in input. Input to parser was: {output}")
306
+ else:
307
+ raise ValueError(f'No number detected in input. Input to parser was "{output}". ')
308
+ return score
309
+
310
+ def geval_summarization(
311
+ prompt_with_src_and_gen: str,
312
+ max_score: float,
313
+ api_endpoint: str,
314
+ api_key: str,
315
+ ) -> float:
316
+ model = get_model_from_config(api_endpoint)
317
+
318
+ try:
319
+ for attempt in Retrying(
320
+ reraise=True,
321
+ before_sleep=before_sleep_log(logger, logging.INFO),
322
+ after=after_log(logger, logging.INFO),
323
+ wait=wait_random_exponential(multiplier=1, min=1, max=120),
324
+ stop=stop_after_attempt(10),
325
+ ):
326
+ with attempt:
327
+ system_message="You are a helpful AI assistant"
328
+ # TEMP setting for Confabulation check
329
+ temp = 0.7
330
+ logging.info(f"Debug - geval_summarization Function - API Endpoint: {api_endpoint}")
331
+ try:
332
+ response = chat_api_call(api_endpoint, api_key, prompt_with_src_and_gen, "", temp, system_message)
333
+ except Exception as e:
334
+ raise ValueError(f"Unsupported API endpoint: {api_endpoint}")
335
+ except RetryError:
336
+ logger.exception(f"geval {api_endpoint} call failed\nInput prompt was: {prompt_with_src_and_gen}")
337
+ raise
338
+
339
+ try:
340
+ score = parse_output(response, max_score)
341
+ except ValueError as e:
342
+ logger.warning(f"Error parsing output: {e}")
343
+ score = 0
344
+
345
+ return score
346
+
347
+
348
+ def get_model_from_config(api_name: str) -> str:
349
+ model = config.get('models', api_name)
350
+ if isinstance(model, dict):
351
+ # If the model is a dictionary, return a specific key or a default value
352
+ return model.get('name', str(model)) # Adjust 'name' to the appropriate key if needed
353
+ return str(model) if model is not None else ""
354
+
355
+ def aggregate_llm_scores(llm_responses: List[str], max_score: float) -> float:
356
+ """Parse and average valid scores from the generated responses of
357
+ the G-Eval LLM call.
358
+
359
+ Args:
360
+ llm_responses (List[str]): List of scores from multiple LLMs
361
+ max_score (float): The maximum score allowed.
362
+
363
+ Returns:
364
+ float: The average of all the valid scores
365
+ """
366
+ all_scores = []
367
+ error_count = 0
368
+ for generated in llm_responses:
369
+ try:
370
+ parsed = parse_output(generated, max_score)
371
+ all_scores.append(parsed)
372
+ except ValueError as e:
373
+ logger.warning(e)
374
+ error_count += 1
375
+ if error_count:
376
+ logger.warning(f"{error_count} out of 20 scores were discarded due to corrupt g-eval generation")
377
+ score = sum(all_scores) / len(all_scores)
378
+ return score
379
+
380
+
381
+ def validate_inputs(document: str, summary: str, api_name: str, api_key: str) -> None:
382
+ """
383
+ Validate inputs for the G-Eval function.
384
+
385
+ Args:
386
+ document (str): The source document
387
+ summary (str): The summary to evaluate
388
+ api_name (str): The name of the API to use
389
+ api_key (str): The API key
390
+
391
+ Raises:
392
+ ValueError: If any of the inputs are invalid
393
+ """
394
+ if not document.strip():
395
+ raise ValueError("Source document cannot be empty")
396
+ if not summary.strip():
397
+ raise ValueError("Summary cannot be empty")
398
+ if api_name.lower() not in ["openai", "anthropic", "cohere", "groq", "openrouter", "deepseek", "huggingface",
399
+ "mistral", "llama.cpp", "kobold", "ooba", "tabbyapi", "vllm", "local-llm", "ollama"]:
400
+ raise ValueError(f"Unsupported API: {api_name}")
401
+
402
+
403
+ def detailed_api_error(api_name: str, error: Exception) -> str:
404
+ """
405
+ Generate a detailed error message for API failures.
406
+
407
+ Args:
408
+ api_name (str): The name of the API that failed
409
+ error (Exception): The exception that was raised
410
+
411
+ Returns:
412
+ str: A detailed error message
413
+ """
414
+ error_type = type(error).__name__
415
+ error_message = str(error)
416
+ return f"API Failure: {api_name}\nError Type: {error_type}\nError Message: {error_message}\nPlease check your API key and network connection, and try again."
417
+
418
+
419
+ def save_eval_results(results: Dict[str, Any], filename: str = "geval_results.json") -> None:
420
+ """
421
+ Save evaluation results to a JSON file.
422
+
423
+ Args:
424
+ results (Dict[str, Any]): The evaluation results
425
+ filename (str): The name of the file to save results to
426
+ """
427
+ with open(filename, 'w') as f:
428
+ json.dump(results, f, indent=2)
429
+ print(f"Results saved to {filename}")
430
+
431
+
432
+
433
+
434
+ #
435
+ #
436
+ #######################################################################################################################
437
+ #
438
+ # Taken from: https://github.com/microsoft/promptflow/blob/b5a68f45e4c3818a29e2f79a76f2e73b8ea6be44/src/promptflow-core/promptflow/_core/metric_logger.py
439
+
440
+ class MetricLoggerManager:
441
+ _instance = None
442
+
443
+ def __init__(self):
444
+ self._metric_loggers = []
445
+
446
+ @staticmethod
447
+ def get_instance() -> "MetricLoggerManager":
448
+ if MetricLoggerManager._instance is None:
449
+ MetricLoggerManager._instance = MetricLoggerManager()
450
+ return MetricLoggerManager._instance
451
+
452
+ def log_metric(self, key, value, variant_id=None):
453
+ for logger in self._metric_loggers:
454
+ if len(inspect.signature(logger).parameters) == 2:
455
+ logger(key, value) # If the logger only accepts two parameters, we don't pass variant_id
456
+ else:
457
+ logger(key, value, variant_id)
458
+
459
+ def add_metric_logger(self, logger_func: Callable):
460
+ existing_logger = next((logger for logger in self._metric_loggers if logger is logger_func), None)
461
+ if existing_logger:
462
+ return
463
+ if not callable(logger_func):
464
+ return
465
+ sign = inspect.signature(logger_func)
466
+ # We accept two kinds of metric loggers:
467
+ # def log_metric(k, v)
468
+ # def log_metric(k, v, variant_id)
469
+ if len(sign.parameters) not in [2, 3]:
470
+ return
471
+ self._metric_loggers.append(logger_func)
472
+
473
+ def remove_metric_logger(self, logger_func: Callable):
474
+ self._metric_loggers.remove(logger_func)
475
+
476
+
477
+ def log_metric(key, value, variant_id=None):
478
+ """Log a metric for current promptflow run.
479
+
480
+ :param key: Metric name.
481
+ :type key: str
482
+ :param value: Metric value.
483
+ :type value: float
484
+ :param variant_id: Variant id for the metric.
485
+ :type variant_id: str
486
+ """
487
+ MetricLoggerManager.get_instance().log_metric(key, value, variant_id)
488
+
489
+
490
+ def add_metric_logger(logger_func: Callable):
491
+ MetricLoggerManager.get_instance().add_metric_logger(logger_func)
492
+
493
+
494
+ def remove_metric_logger(logger_func: Callable):
495
+ MetricLoggerManager.get_instance().remove_metric_logger(logger_func)
496
+ #
497
+ # End of G-Eval.py
498
+ #######################################################################################################################