Training_CPU / config_manager.py
Fedir-Ilina's picture
Upload 7 files
eddad46 verified
Raw
History Blame Contribute Delete
7.99 kB
# config_manager.py
import json
import os
import re
# Caminho para o ficheiro principal de onde vamos ler/escrever as constantes
CONFIG_FILE_PATH = 'app.py'
def _load_constants_from_file():
"""Lê todas as CONSTANTES (ALL_CAPS) do ficheiro Python e retorna um dicionário."""
constants = {}
BOOL_CONSTANTS = ["ALLOW_SLEEP_ON_CHANGE"]
try:
with open(CONFIG_FILE_PATH, 'r', encoding='utf-8') as f:
content = f.read()
except FileNotFoundError:
print(f"ERRO: Ficheiro de configuração não encontrado em {CONFIG_FILE_PATH}")
return constants
matches = re.findall(r'^\s*([A-Z_]+)\s*=\s*(.*)', content, re.MULTILINE)
for name, value_str in matches:
value_str = value_str.strip()
# Remove comentários inline
value_str = re.sub(r'\s*#.*$', '', value_str).strip()
try:
if name in BOOL_CONSTANTS:
constants[name] = value_str.lower() == "true"
elif re.match(r'^-?\d+$', value_str):
constants[name] = int(value_str)
elif re.match(r'^-?\d+\.\d+$', value_str):
constants[name] = float(value_str)
elif re.match(r'^-?\d+e[+-]?\d+$', value_str, re.I):
constants[name] = float(value_str)
else:
constants[name] = value_str.strip("'\"")
except Exception:
constants[name] = value_str.strip("'\"")
return constants
_runtime_globals = {}
def set_runtime_globals(g: dict):
"""Chamado pelo app.py no endpoint /api/config para passar variáveis dinâmicas."""
_runtime_globals.update(g)
def _get_runtime_var(key):
return _runtime_globals.get(key)
def map_backend_to_frontend(constants):
"""Mapeia as constantes do backend para a estrutura JSON do frontend."""
def safe_int(v, d=0):
try: return int(v)
except: return d
def safe_float(v, d=0.0):
try: return float(v)
except: return d
return {
"preset": "custom",
"hardware": {
# Variáveis dinâmicas — lidas dos globals do app em runtime
"cpu_count": safe_int(_get_runtime_var('logical_cpus') or constants.get('LOGICAL_CPUS', 0)),
"omp_threads": safe_int(_get_runtime_var('omp_threads') or constants.get('OMP_THREADS', 0)),
"dataloader_workers": safe_int(_get_runtime_var('dataloader_workers') or constants.get('DATALOADER_WORKERS', 0)),
"total_ram_gb": round(safe_float(_get_runtime_var('total_ram_gb') or constants.get('TOTAL_RAM_GB', 0.0)), 1),
},
"ramcfg": {
"base_batch": safe_int(constants.get('BASE_BATCH_SIZE', 1)),
"accum_min": safe_int(constants.get('INITIAL_ACCUMULATION_MIN_STEPS', 2)),
"max_steps": safe_int(constants.get('INITIAL_ACCUMULATION_MAX_STEPS', constants.get('DYNAMIC_ACCUMULATION_MAX_STEPS', 4))),
"target_utilization": safe_float(constants.get('TARGET_RAM_UTILIZATION', 0.95)),
"estimated_batch_gb": safe_float(constants.get('ESTIMATED_BATCH_GB', 0.30)),
"high_ram_limit_pct": safe_float(constants.get('DYNAMIC_ACCUMULATION_HIGH_RAM_LIMIT', 98.5)),
"low_ram_limit_pct": safe_float(constants.get('DYNAMIC_ACCUMULATION_LOW_RAM_LIMIT', 40.0)),
},
"mlcfg": {
"base_max_len": safe_int(constants.get('BASE_MAX_LEN', 128)),
"ml_target_utilization": safe_float(constants.get('TARGET_MAX_LEN_UTILIZATION', 0.75)),
"increment": safe_int(constants.get('MAX_LEN_INCREMENT', 128)),
"cap": safe_int(constants.get('MAX_LEN_CAP', 128)),
"estimated_base_dataset_ram_gb": safe_float(constants.get('ESTIMATED_BASE_DATASET_RAM_GB', 1.6)),
"cost_per_increment_gb": safe_float(constants.get('COST_PER_INCREMENT_GB', 0.30)),
},
"training": {
"base_batch": safe_int(constants.get('BASE_BATCH_SIZE', 1)),
"base_eval_size": safe_int(constants.get('BASE_EVAL_SIZE', 5)),
"base_lr": safe_float(constants.get('BASE_LEARNING_RATE', 1e-4)),
"loggin_steps": safe_int(constants.get('LOGGIN_STEPS', constants.get('LOGGING_STEPS', 10))),
"optim": constants.get('OPTIM', 'adamw_torch'),
"scheduler": constants.get('LR_SCHEDULER_TYPE', 'constant_with_warmup'),
}
}
def update_python_constants(new_data):
"""Atualiza as constantes no ficheiro Python original com os novos valores."""
# Mapeamento plano: chave JSON → nome da constante Python
# Cobre ramcfg, mlcfg e training
MAPPING = {
# ramcfg
"base_batch": "BASE_BATCH_SIZE",
"max_steps": "DYNAMIC_ACCUMULATION_MAX_STEPS",
"target_utilization": "TARGET_RAM_UTILIZATION",
"estimated_batch_gb": "ESTIMATED_BATCH_GB",
"high_ram_limit_pct": "DYNAMIC_ACCUMULATION_HIGH_RAM_LIMIT",
"low_ram_limit_pct": "DYNAMIC_ACCUMULATION_LOW_RAM_LIMIT",
# mlcfg
"base_max_len": "BASE_MAX_LEN",
"ml_target_utilization": "TARGET_MAX_LEN_UTILIZATION",
"increment": "MAX_LEN_INCREMENT",
"cap": "MAX_LEN_CAP",
"estimated_base_dataset_ram_gb": "ESTIMATED_BASE_DATASET_RAM_GB",
"cost_per_increment_gb": "COST_PER_INCREMENT_GB",
# training
"base_eval_size": "BASE_EVAL_SIZE",
"base_lr": "BASE_LEARNING_RATE",
"scheduler": "LR_SCHEDULER_TYPE",
}
# Achatar o JSON aninhado num dicionário plano
flat = {}
for section in ("ramcfg", "mlcfg", "training"):
flat.update(new_data.get(section, {}))
# base_batch pode vir de ramcfg ou training — usar o de training se presente
if "base_batch" in new_data.get("training", {}):
flat["base_batch"] = new_data["training"]["base_batch"]
# Construir dicionário de actualizações { CONST_NAME: value }
updates = {}
for ui_key, const_name in MAPPING.items():
if ui_key in flat:
updates[const_name] = flat[ui_key]
if not updates:
return False
try:
with open(CONFIG_FILE_PATH, 'r', encoding='utf-8') as f:
lines = f.readlines()
except FileNotFoundError:
return False
new_lines = []
for line in lines:
match = re.match(r'^(\s*)([A-Z_]+)\s*=\s*(.*)', line)
if match:
indent, name, rest = match.groups()
if name in updates:
val = updates[name]
if isinstance(val, str):
formatted = f'"{val}"'
elif isinstance(val, bool):
formatted = str(val)
elif isinstance(val, float):
# Preserva notação científica para learning rate
if abs(val) < 0.001 and val != 0:
formatted = f'{val:.0e}'
else:
formatted = str(val)
else:
formatted = str(val)
# Preserva comentário inline se existia
comment_match = re.search(r'(#.*)$', rest)
comment = f' {comment_match.group(1)}' if comment_match else ''
new_lines.append(f"{indent}{name} = {formatted}{comment}\n")
continue
new_lines.append(line)
try:
with open(CONFIG_FILE_PATH, 'w', encoding='utf-8') as f:
f.writelines(new_lines)
return True
except Exception as e:
print(f"ERRO ao escrever no ficheiro: {e}")
return False
# Fim de config_manager.py