| | |
| | """ |
| | Sistema de execução massiva de testes com paralelismo |
| | """ |
| | import asyncio |
| | import logging |
| | import time |
| | import threading |
| | from concurrent.futures import ThreadPoolExecutor, as_completed |
| | from typing import Dict, List, Any, Optional |
| | from datetime import datetime |
| | import uuid |
| | import sys |
| | import os |
| |
|
| | |
| | sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) |
| |
|
| | from graphs.main_graph import AgentGraphManager |
| | from testes.test_validator import TestValidator |
| | from utils.config import AVAILABLE_MODELS |
| |
|
| | class MassiveTestRunner: |
| | """ |
| | Executor de testes massivos com paralelismo otimizado |
| | """ |
| | |
| | def __init__(self, max_workers: int = 5): |
| | """ |
| | Inicializa o test runner |
| | |
| | Args: |
| | max_workers: Número máximo de workers paralelos |
| | """ |
| | self.max_workers = max_workers |
| | logging.info(f"🔧 MassiveTestRunner inicializado com {max_workers} workers paralelos") |
| | self.validator = TestValidator() |
| | self.results = { |
| | 'session_info': {}, |
| | 'group_results': [], |
| | 'individual_results': [], |
| | 'summary': {} |
| | } |
| | self.status = { |
| | 'current_status': 'idle', |
| | 'progress': 0, |
| | 'current_group': None, |
| | 'completed_tests': 0, |
| | 'total_tests': 0, |
| | 'start_time': None, |
| | 'estimated_remaining': None, |
| | 'errors': [], |
| | 'current_test': None, |
| | 'running_tests': {}, |
| | 'cancelled_tests': set(), |
| | 'timeout_tests': set() |
| | } |
| | self._lock = threading.Lock() |
| | self._cancel_event = threading.Event() |
| | self._test_timeout = 360 |
| | self._active_futures = {} |
| | |
| | async def run_test_session(self, session: Dict[str, Any], validation_method: str = 'llm', expected_content: str = None) -> Dict[str, Any]: |
| | """ |
| | Executa sessão completa de testes |
| | |
| | Args: |
| | session: Dados da sessão de teste |
| | validation_method: Método de validação ('llm' ou 'keyword') |
| | expected_content: Conteúdo esperado (para validação keyword) |
| | |
| | Returns: |
| | Resultados completos dos testes |
| | """ |
| | try: |
| | print(f"\n🔥 MASSIVE TEST RUNNER INICIADO") |
| | print(f"📋 Sessão: {session['id']}") |
| | print(f"❓ Pergunta: {session['question']}") |
| | print(f"👥 Grupos: {len(session['groups'])}") |
| |
|
| | total_tests = sum(group['iterations'] for group in session['groups']) |
| | print(f"🔢 Total de testes: {total_tests}") |
| | print(f"⚡ Workers paralelos: {self.max_workers}") |
| | print("-" * 60) |
| |
|
| | logging.info(f"🚀 Iniciando sessão de testes: {session['id']}") |
| |
|
| | |
| | with self._lock: |
| | self.status.update({ |
| | 'current_status': 'initializing', |
| | 'start_time': time.time(), |
| | 'total_tests': total_tests |
| | }) |
| | |
| | |
| | self.results['session_info'] = { |
| | 'id': session['id'], |
| | 'question': session['question'], |
| | 'validation_method': validation_method, |
| | 'expected_content': expected_content, |
| | 'total_groups': len(session['groups']), |
| | 'total_tests': self.status['total_tests'], |
| | 'started_at': datetime.now().isoformat() |
| | } |
| | |
| | |
| | group_results = [] |
| | |
| | for group_idx, group in enumerate(session['groups']): |
| | print(f"\n📊 EXECUTANDO GRUPO {group_idx + 1}/{len(session['groups'])}") |
| | print(f"🤖 Modelo SQL: {group['sql_model_name']}") |
| | print(f"🔄 Processing Agent: {'✅ ' + group['processing_model_name'] if group['processing_enabled'] else '❌ Desativado'}") |
| | print(f"🔢 Iterações: {group['iterations']}") |
| | print(f"⏰ {datetime.now().strftime('%H:%M:%S')}") |
| |
|
| | logging.info(f"📊 Executando grupo {group_idx + 1}/{len(session['groups'])}: {group['sql_model_name']}") |
| |
|
| | with self._lock: |
| | self.status['current_group'] = group_idx + 1 |
| | self.status['current_status'] = 'running_group' |
| | |
| | |
| | group_result = await self._run_group_tests( |
| | session['question'], |
| | group, |
| | validation_method, |
| | expected_content |
| | ) |
| | |
| | group_results.append(group_result) |
| | self.results['group_results'] = group_results |
| | |
| | |
| | completed_so_far = sum(len(gr['individual_results']) for gr in group_results) |
| | with self._lock: |
| | self.status['completed_tests'] = completed_so_far |
| | self.status['progress'] = (completed_so_far / self.status['total_tests']) * 100 |
| | |
| | |
| | if self.status['start_time']: |
| | elapsed = time.time() - self.status['start_time'] |
| | if completed_so_far > 0: |
| | avg_time_per_test = elapsed / completed_so_far |
| | remaining_tests = self.status['total_tests'] - completed_so_far |
| | self.status['estimated_remaining'] = avg_time_per_test * remaining_tests |
| | |
| | |
| | self._generate_summary() |
| |
|
| | with self._lock: |
| | self.status['current_status'] = 'completed' |
| | self.status['progress'] = 100 |
| | self.status['end_time'] = time.time() |
| | total_time = self.status['end_time'] - self.status['start_time'] |
| | self.status['total_execution_time'] = total_time |
| |
|
| | logging.info(f"✅ Sessão de testes concluída: {session['id']}") |
| | logging.info(f"📊 Resumo final: {self.status['total_tests']} testes em {total_time:.2f}s") |
| | logging.info(f"🎯 Taxa geral de sucesso: {self.results['summary'].get('overall_success_rate', 0)}%") |
| |
|
| | return self.results |
| | |
| | except Exception as e: |
| | logging.error(f"❌ Erro na sessão de testes: {e}") |
| | with self._lock: |
| | self.status['current_status'] = 'error' |
| | self.status['errors'].append(str(e)) |
| | raise |
| | |
| | async def _run_group_tests(self, question: str, group: Dict[str, Any], validation_method: str, expected_content: str) -> Dict[str, Any]: |
| | """ |
| | Executa testes de um grupo específico com paralelismo REAL |
| | |
| | Args: |
| | question: Pergunta do teste |
| | group: Configuração do grupo |
| | validation_method: Método de validação |
| | expected_content: Conteúdo esperado |
| | |
| | Returns: |
| | Resultados do grupo |
| | """ |
| | print(f"🔄 Executando {group['iterations']} testes em paralelo (máx {self.max_workers} simultâneos)") |
| | logging.info(f"🔄 Executando {group['iterations']} testes para grupo {group['id']}") |
| |
|
| | |
| | semaphore = asyncio.Semaphore(self.max_workers) |
| |
|
| | print(f"⚡ Iniciando {group['iterations']} testes com paralelismo REAL...") |
| | start_time = time.time() |
| |
|
| | |
| | print(f"🚀 Executando {group['iterations']} testes em paralelo (máx {self.max_workers} simultâneos)") |
| |
|
| | |
| | semaphore = asyncio.Semaphore(self.max_workers) |
| | tasks = [] |
| |
|
| | print(f"⚡ Criando {group['iterations']} tasks paralelas...") |
| | for iteration in range(group['iterations']): |
| | task = self._run_single_test( |
| | semaphore, |
| | question, |
| | group, |
| | iteration + 1, |
| | validation_method, |
| | expected_content |
| | ) |
| | tasks.append(task) |
| |
|
| | print(f"🚀 Executando {len(tasks)} testes em paralelo...") |
| | start_time = time.time() |
| |
|
| | |
| | individual_results = await asyncio.gather(*tasks, return_exceptions=True) |
| |
|
| | execution_time = time.time() - start_time |
| | print(f"✅ Grupo {group['id']} concluído em {execution_time:.2f}s") |
| |
|
| | execution_time = time.time() - start_time |
| | print(f"✅ Grupo {group['id']} concluído em {execution_time:.2f}s ({group['iterations']} testes)") |
| | |
| | |
| | valid_results = [] |
| | errors = [] |
| | |
| | for result in individual_results: |
| | if isinstance(result, Exception): |
| | errors.append(str(result)) |
| | logging.error(f"Erro em teste individual: {result}") |
| | else: |
| | valid_results.append(result) |
| | self.results['individual_results'].append(result) |
| | |
| | |
| | group_stats = self._calculate_group_stats(valid_results, group) |
| | group_stats['errors'] = errors |
| | group_stats['error_count'] = len(errors) |
| | |
| | logging.info(f"✅ Grupo {group['id']} concluído: {len(valid_results)} sucessos, {len(errors)} erros") |
| | |
| | return group_stats |
| | |
| | async def _run_single_test(self, semaphore: asyncio.Semaphore, question: str, group: Dict[str, Any], |
| | iteration: int, validation_method: str, expected_content: str) -> Dict[str, Any]: |
| | """ |
| | Executa um teste individual com paralelismo real |
| | |
| | Args: |
| | semaphore: Semáforo para controle de concorrência |
| | question: Pergunta do teste |
| | group: Configuração do grupo |
| | iteration: Número da iteração |
| | validation_method: Método de validação |
| | expected_content: Conteúdo esperado |
| | |
| | Returns: |
| | Resultado do teste individual |
| | """ |
| | async with semaphore: |
| | try: |
| | start_time = time.time() |
| |
|
| | |
| | thread_id = f"test_{group['id']}_{iteration}_{uuid.uuid4().hex[:8]}" |
| |
|
| | |
| | with self._lock: |
| | self.status['running_tests'][thread_id] = { |
| | 'start_time': start_time, |
| | 'group_id': group['id'], |
| | 'iteration': iteration, |
| | 'question': question[:50] + '...' if len(question) > 50 else question |
| | } |
| | self.status['current_test'] = thread_id |
| |
|
| | print(f"🔄 [{datetime.now().strftime('%H:%M:%S')}] 🚀 INICIANDO {thread_id} (Worker {asyncio.current_task().get_name() if asyncio.current_task() else 'unknown'})") |
| | logging.info(f"🔄 Iniciando teste {thread_id} - Grupo {group['id']}, Iteração {iteration}") |
| |
|
| | |
| | if thread_id in self.status['cancelled_tests']: |
| | print(f"🚫 Teste {thread_id} cancelado antes de iniciar") |
| | return self._create_cancelled_result(thread_id, group, iteration, start_time) |
| |
|
| | |
| | current_task = asyncio.current_task() |
| | with self._lock: |
| | self._active_futures[thread_id] = current_task |
| |
|
| | |
| | loop = asyncio.get_event_loop() |
| |
|
| | def run_sync_test(): |
| | """Executa teste de forma síncrona em thread separada""" |
| | try: |
| | |
| | if thread_id in self.status['cancelled_tests']: |
| | return {'cancelled': True, 'reason': 'cancelled_before_start'} |
| |
|
| | |
| | new_loop = asyncio.new_event_loop() |
| | asyncio.set_event_loop(new_loop) |
| |
|
| | |
| | graph_manager = AgentGraphManager() |
| |
|
| | |
| | result = new_loop.run_until_complete( |
| | graph_manager.process_query( |
| | user_input=question, |
| | selected_model=group['sql_model_name'], |
| | processing_enabled=group['processing_enabled'], |
| | processing_model=group['processing_model_name'] if group['processing_enabled'] else None, |
| | thread_id=thread_id |
| | ) |
| | ) |
| |
|
| | new_loop.close() |
| |
|
| | |
| | if thread_id in self.status['cancelled_tests']: |
| | return {'cancelled': True, 'reason': 'cancelled_after_execution'} |
| |
|
| | return result |
| |
|
| | except Exception as e: |
| | logging.error(f"Erro em thread separada para {thread_id}: {e}") |
| | return {'error': str(e)} |
| |
|
| | |
| | with ThreadPoolExecutor(max_workers=1) as executor: |
| | future = loop.run_in_executor(executor, run_sync_test) |
| |
|
| | |
| | while not future.done(): |
| | await asyncio.sleep(0.1) |
| | if thread_id in self.status['cancelled_tests']: |
| | future.cancel() |
| | print(f"🚫 Cancelando future do teste {thread_id}") |
| | try: |
| | await future |
| | except: |
| | pass |
| | return self._create_cancelled_result(thread_id, group, iteration, start_time, 'user_cancelled') |
| |
|
| | result = await future |
| |
|
| | execution_time = time.time() - start_time |
| |
|
| | |
| | with self._lock: |
| | if thread_id in self.status['running_tests']: |
| | del self.status['running_tests'][thread_id] |
| | if self.status['current_test'] == thread_id: |
| | self.status['current_test'] = None |
| | if thread_id in self._active_futures: |
| | del self._active_futures[thread_id] |
| |
|
| | |
| | if isinstance(result, dict): |
| | if result.get('cancelled'): |
| | print(f"🚫 [{datetime.now().strftime('%H:%M:%S')}] CANCELADO {thread_id} - {result.get('reason', 'unknown')}") |
| | logging.info(f"🚫 Teste {thread_id} cancelado") |
| | return self._create_cancelled_result(thread_id, group, iteration, start_time, result.get('reason')) |
| | elif result.get('timeout'): |
| | print(f"⏰ [{datetime.now().strftime('%H:%M:%S')}] TIMEOUT {thread_id} após {result.get('duration')}s") |
| | logging.warning(f"⏰ Teste {thread_id} timeout") |
| | return self._create_timeout_result(thread_id, group, iteration, start_time, result.get('duration')) |
| | elif result.get('error'): |
| | print(f"❌ [{datetime.now().strftime('%H:%M:%S')}] ERRO {thread_id}: {result['error']}") |
| | logging.error(f"❌ Teste {thread_id} erro: {result['error']}") |
| |
|
| | print(f"✅ [{datetime.now().strftime('%H:%M:%S')}] 🎉 CONCLUÍDO {thread_id} em {execution_time:.2f}s") |
| | logging.info(f"✅ Teste {thread_id} concluído em {execution_time:.2f}s") |
| |
|
| | |
| | validation_result = await self.validator.validate_result( |
| | question=question, |
| | sql_query=result.get('sql_query_extracted', ''), |
| | response=result.get('response', ''), |
| | method=validation_method, |
| | expected_content=expected_content |
| | ) |
| |
|
| | |
| | individual_result = { |
| | 'group_id': group['id'], |
| | 'iteration': iteration, |
| | 'thread_id': thread_id, |
| | 'timestamp': datetime.now().isoformat(), |
| | 'execution_time': round(execution_time, 2), |
| | 'question': question, |
| | 'sql_model': group['sql_model_name'], |
| | 'processing_enabled': group['processing_enabled'], |
| | 'processing_model': group['processing_model_name'], |
| | 'sql_query': result.get('sql_query_extracted', ''), |
| | 'response': result.get('response', ''), |
| | 'error': result.get('error'), |
| | 'success': not bool(result.get('error')), |
| | 'validation': validation_result |
| | } |
| |
|
| | |
| | with self._lock: |
| | self.status['completed_tests'] += 1 |
| | progress = (self.status['completed_tests'] / self.status['total_tests']) * 100 |
| | self.status['progress'] = progress |
| |
|
| | |
| | if self.status['start_time']: |
| | elapsed = time.time() - self.status['start_time'] |
| | if self.status['completed_tests'] > 0: |
| | avg_time_per_test = elapsed / self.status['completed_tests'] |
| | remaining_tests = self.status['total_tests'] - self.status['completed_tests'] |
| | self.status['estimated_remaining'] = avg_time_per_test * remaining_tests |
| |
|
| | |
| | remaining_min = int(self.status['estimated_remaining'] // 60) |
| | remaining_sec = int(self.status['estimated_remaining'] % 60) |
| |
|
| | print(f"📊 [{datetime.now().strftime('%H:%M:%S')}] Progresso: {self.status['completed_tests']}/{self.status['total_tests']} ({progress:.1f}%) - Restam ~{remaining_min}m{remaining_sec}s") |
| |
|
| | logging.info(f"📊 Progresso: {self.status['completed_tests']}/{self.status['total_tests']} ({progress:.1f}%)") |
| |
|
| | return individual_result |
| |
|
| | except Exception as e: |
| | logging.error(f"❌ Erro em teste individual (grupo {group['id']}, iteração {iteration}): {e}") |
| |
|
| | |
| | with self._lock: |
| | self.status['completed_tests'] += 1 |
| | self.status['errors'].append(f"Grupo {group['id']}, Iteração {iteration}: {e}") |
| |
|
| | return { |
| | 'group_id': group['id'], |
| | 'iteration': iteration, |
| | 'thread_id': f"error_{group['id']}_{iteration}", |
| | 'timestamp': datetime.now().isoformat(), |
| | 'execution_time': time.time() - start_time, |
| | 'question': question, |
| | 'sql_model': group['sql_model_name'], |
| | 'processing_enabled': group['processing_enabled'], |
| | 'processing_model': group['processing_model_name'], |
| | 'sql_query': '', |
| | 'response': '', |
| | 'error': str(e), |
| | 'success': False, |
| | 'validation': {'valid': False, 'score': 0, 'reason': f'Erro de execução: {e}'} |
| | } |
| | |
| | def _calculate_group_stats(self, results: List[Dict[str, Any]], group: Dict[str, Any]) -> Dict[str, Any]: |
| | """ |
| | Calcula estatísticas de um grupo |
| | |
| | Args: |
| | results: Resultados individuais do grupo |
| | group: Configuração do grupo |
| | |
| | Returns: |
| | Estatísticas do grupo |
| | """ |
| | if not results: |
| | return { |
| | 'group_id': group['id'], |
| | 'group_config': group, |
| | 'total_tests': 0, |
| | 'success_rate': 0, |
| | 'validation_rate': 0, |
| | 'consistency_rate': 0, |
| | 'avg_execution_time': 0, |
| | 'individual_results': [] |
| | } |
| | |
| | total_tests = len(results) |
| | successful_tests = sum(1 for r in results if r.get('success', False)) |
| | valid_responses = sum(1 for r in results if r.get('validation', {}).get('valid', False)) |
| | |
| | |
| | responses = [r.get('response', '') for r in results if r.get('success', False)] |
| | sql_queries = [r.get('sql_query', '') for r in results if r.get('success', False)] |
| | |
| | response_consistency = self._calculate_consistency(responses) |
| | sql_consistency = self._calculate_consistency(sql_queries) |
| | |
| | avg_execution_time = sum(r.get('execution_time', 0) for r in results) / total_tests |
| | |
| | return { |
| | 'group_id': group['id'], |
| | 'group_config': group, |
| | 'total_tests': total_tests, |
| | 'successful_tests': successful_tests, |
| | 'valid_responses': valid_responses, |
| | 'success_rate': round((successful_tests / total_tests) * 100, 2), |
| | 'validation_rate': round((valid_responses / total_tests) * 100, 2), |
| | 'response_consistency': round(response_consistency * 100, 2), |
| | 'sql_consistency': round(sql_consistency * 100, 2), |
| | 'avg_execution_time': round(avg_execution_time, 2), |
| | 'individual_results': results |
| | } |
| | |
| | def _calculate_consistency(self, items: List[str]) -> float: |
| | """ |
| | Calcula taxa de consistência entre itens |
| | |
| | Args: |
| | items: Lista de strings para comparar |
| | |
| | Returns: |
| | Taxa de consistência (0-1) |
| | """ |
| | if len(items) <= 1: |
| | return 1.0 |
| | |
| | |
| | unique_items = set(items) |
| | most_common_count = max(items.count(item) for item in unique_items) |
| | |
| | return most_common_count / len(items) |
| | |
| | def _generate_summary(self): |
| | """Gera resumo geral dos testes""" |
| | group_results = self.results.get('group_results', []) |
| | |
| | if not group_results: |
| | self.results['summary'] = {} |
| | return |
| | |
| | total_tests = sum(gr['total_tests'] for gr in group_results) |
| | total_successful = sum(gr['successful_tests'] for gr in group_results) |
| | total_valid = sum(gr['valid_responses'] for gr in group_results) |
| | |
| | avg_success_rate = sum(gr['success_rate'] for gr in group_results) / len(group_results) |
| | avg_validation_rate = sum(gr['validation_rate'] for gr in group_results) / len(group_results) |
| | avg_response_consistency = sum(gr['response_consistency'] for gr in group_results) / len(group_results) |
| | avg_sql_consistency = sum(gr['sql_consistency'] for gr in group_results) / len(group_results) |
| | |
| | self.results['summary'] = { |
| | 'total_groups': len(group_results), |
| | 'total_tests': total_tests, |
| | 'total_successful': total_successful, |
| | 'total_valid': total_valid, |
| | 'overall_success_rate': round((total_successful / total_tests) * 100, 2), |
| | 'overall_validation_rate': round((total_valid / total_tests) * 100, 2), |
| | 'avg_response_consistency': round(avg_response_consistency, 2), |
| | 'avg_sql_consistency': round(avg_sql_consistency, 2), |
| | 'best_performing_group': max(group_results, key=lambda x: x['validation_rate']), |
| | 'most_consistent_group': max(group_results, key=lambda x: x['response_consistency']) |
| | } |
| | |
| | def get_status(self) -> Dict[str, Any]: |
| | """Retorna status atual dos testes""" |
| | with self._lock: |
| | status = self.status.copy() |
| | |
| | status['running_tests_count'] = len(self.status['running_tests']) |
| | status['running_tests_details'] = list(self.status['running_tests'].values()) |
| | return status |
| |
|
| | def cancel_current_test(self, thread_id: str = None) -> bool: |
| | """ |
| | Cancela teste específico ou o mais antigo em execução |
| | |
| | Args: |
| | thread_id: ID do teste específico para cancelar (opcional) |
| | |
| | Returns: |
| | True se cancelou algum teste |
| | """ |
| | with self._lock: |
| | if thread_id: |
| | if thread_id in self.status['running_tests']: |
| | self.status['cancelled_tests'].add(thread_id) |
| | print(f"🚫 Teste {thread_id} marcado para cancelamento") |
| | logging.info(f"Teste {thread_id} cancelado pelo usuário") |
| | return True |
| | else: |
| | |
| | if self.status['running_tests']: |
| | oldest_test = min( |
| | self.status['running_tests'].items(), |
| | key=lambda x: x[1]['start_time'] |
| | ) |
| | thread_id = oldest_test[0] |
| | self.status['cancelled_tests'].add(thread_id) |
| | print(f"🚫 Teste mais antigo {thread_id} marcado para cancelamento") |
| | logging.info(f"Teste mais antigo {thread_id} cancelado pelo usuário") |
| | return True |
| | return False |
| |
|
| | def cancel_all_tests(self) -> int: |
| | """ |
| | Cancela todos os testes em execução |
| | |
| | Returns: |
| | Número de testes cancelados |
| | """ |
| | with self._lock: |
| | running_count = len(self.status['running_tests']) |
| | for thread_id in self.status['running_tests'].keys(): |
| | self.status['cancelled_tests'].add(thread_id) |
| |
|
| | print(f"🚫 {running_count} testes marcados para cancelamento") |
| | logging.info(f"{running_count} testes cancelados pelo usuário") |
| | return running_count |
| |
|
| | def skip_stuck_tests(self, max_duration: int = 120) -> int: |
| | """ |
| | Marca testes travados (que excedem tempo limite) para cancelamento |
| | |
| | Args: |
| | max_duration: Tempo máximo em segundos |
| | |
| | Returns: |
| | Número de testes marcados como travados |
| | """ |
| | current_time = time.time() |
| | stuck_count = 0 |
| |
|
| | with self._lock: |
| | for thread_id, test_info in self.status['running_tests'].items(): |
| | if current_time - test_info['start_time'] > max_duration: |
| | if thread_id not in self.status['cancelled_tests']: |
| | self.status['timeout_tests'].add(thread_id) |
| | self.status['cancelled_tests'].add(thread_id) |
| | stuck_count += 1 |
| | print(f"⏰ Teste {thread_id} marcado como travado (>{max_duration}s)") |
| | logging.warning(f"Teste {thread_id} travado - timeout após {max_duration}s") |
| |
|
| | return stuck_count |
| |
|
| | def _create_cancelled_result(self, thread_id: str, group: Dict[str, Any], iteration: int, start_time: float, reason: str = 'user_cancelled') -> Dict[str, Any]: |
| | """Cria resultado para teste cancelado""" |
| | execution_time = time.time() - start_time |
| | return { |
| | 'thread_id': thread_id, |
| | 'group_id': group['id'], |
| | 'iteration': iteration, |
| | 'success': False, |
| | 'cancelled': True, |
| | 'cancel_reason': reason, |
| | 'execution_time': execution_time, |
| | 'sql_query': None, |
| | 'final_response': f"Teste cancelado: {reason}", |
| | 'validation_valid': False, |
| | 'validation_score': 0, |
| | 'error': None, |
| | 'timestamp': datetime.now().isoformat() |
| | } |
| |
|
| | def _create_timeout_result(self, thread_id: str, group: Dict[str, Any], iteration: int, start_time: float, duration: int) -> Dict[str, Any]: |
| | """Cria resultado para teste com timeout""" |
| | execution_time = time.time() - start_time |
| | return { |
| | 'thread_id': thread_id, |
| | 'group_id': group['id'], |
| | 'iteration': iteration, |
| | 'success': False, |
| | 'timeout': True, |
| | 'timeout_duration': duration, |
| | 'execution_time': execution_time, |
| | 'sql_query': None, |
| | 'final_response': f"Teste travado - timeout após {duration}s", |
| | 'validation_valid': False, |
| | 'validation_score': 0, |
| | 'error': f"Timeout após {duration}s", |
| | 'timestamp': datetime.now().isoformat() |
| | } |
| | |
| | def get_results(self) -> Dict[str, Any]: |
| | """Retorna resultados dos testes""" |
| | return self.results |
| |
|