From 938cb31f1ad6264c01a880bda0e55225a633b584 Mon Sep 17 00:00:00 2001 From: obxium Date: Tue, 14 Oct 2025 14:19:44 -0400 Subject: [PATCH 1/4] Update logo --- dev/nanochat.png | Bin 19811 -> 1305 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/dev/nanochat.png b/dev/nanochat.png index 84e1b5fc9fda7b2739f3db39f23ee19dd58234cb..2313d271f8a7db292f8d5c80307b807e9a055ba3 100644 GIT binary patch literal 1305 zcmeAS@N?(olHy`uVBq!ia0y~yV41_fz$n1N3>0DaDt-;5bOU@sT;=5C&YU>|Bq2av zULMAjla-Z)aDgO{4HRD$kh>75k-sF!FZloe-`~G{`uz9L*AH)Ay?U>EG$<2jNsp(C zV@L(#n@fg_38M_E$y0sq1_i!Em*&n}ENm6~|2!}x_HTApFUi`h$naBI?ou?D zkLA7^*<Uv(RNEvYWQwf>fb$PUM+Eth$(Xuk4m zJU!*v?#utY&)eQT1k`#t0cs)EP=lKYvKJoQF#90E4GB?nhjPx%^ZljySN(^dTTW); zv2UMtE{`sFC8E`^G>!S9^P8ey)0nnT>#@IU+WhKDeN%qQk!f-v-=?3pyL%{@7Z?oA zczlTJ4xnOCjH0;?RTQ2yTKrba{3(rCUgG@y%;Uoyx>uXdd}>_2d)b34y!bP0l+XkKM=x21 literal 19811 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 From 2b58e2dd2ae134078c610665bf0811196c62830c Mon Sep 17 00:00:00 2001 From: obxium Date: Sat, 18 Oct 2025 09:31:11 -0400 Subject: [PATCH 2/4] Update logo in code as well --- nanochat/common.py | 91 +++++++++++++++++++++++++++++----------------- 1 file changed, 58 insertions(+), 33 deletions(-) diff --git a/nanochat/common.py b/nanochat/common.py index 8b10df9..bb825ff 100644 --- a/nanochat/common.py +++ b/nanochat/common.py @@ -8,43 +8,58 @@ import logging import torch import torch.distributed as dist + class ColoredFormatter(logging.Formatter): """Custom formatter that adds colors to log messages.""" + # ANSI color codes COLORS = { - 'DEBUG': '\033[36m', # Cyan - 'INFO': '\033[32m', # Green - 'WARNING': '\033[33m', # Yellow - 'ERROR': '\033[31m', # Red - 'CRITICAL': '\033[35m', # Magenta + "DEBUG": "\033[36m", # Cyan + "INFO": "\033[32m", # Green + "WARNING": "\033[33m", # Yellow + "ERROR": "\033[31m", # Red + "CRITICAL": "\033[35m", # Magenta } - RESET = '\033[0m' - BOLD = '\033[1m' + RESET = "\033[0m" + BOLD = "\033[1m" + def format(self, record): # Add color to the level name levelname = record.levelname if levelname in self.COLORS: - record.levelname = f"{self.COLORS[levelname]}{self.BOLD}{levelname}{self.RESET}" + record.levelname = ( + f"{self.COLORS[levelname]}{self.BOLD}{levelname}{self.RESET}" + ) # Format the message message = super().format(record) # Add color to specific parts of the message - if levelname == 'INFO': + if levelname == "INFO": # Highlight numbers and percentages - message = re.sub(r'(\d+\.?\d*\s*(?:GB|MB|%|docs))', rf'{self.BOLD}\1{self.RESET}', message) - message = re.sub(r'(Shard \d+)', rf'{self.COLORS["INFO"]}{self.BOLD}\1{self.RESET}', message) + message = re.sub( + r"(\d+\.?\d*\s*(?:GB|MB|%|docs))", + rf"{self.BOLD}\1{self.RESET}", + message, + ) + message = re.sub( + r"(Shard \d+)", + rf"{self.COLORS['INFO']}{self.BOLD}\1{self.RESET}", + message, + ) return message + def setup_default_logging(): handler = logging.StreamHandler() - handler.setFormatter(ColoredFormatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')) - logging.basicConfig( - level=logging.INFO, - handlers=[handler] + handler.setFormatter( + ColoredFormatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s") ) + logging.basicConfig(level=logging.INFO, handlers=[handler]) + setup_default_logging() logger = logging.getLogger(__name__) + def get_base_dir(): # co-locate nanochat intermediates with other cached data in ~/.cache (by default) if os.environ.get("NANOCHAT_BASE_DIR"): @@ -56,39 +71,44 @@ def get_base_dir(): os.makedirs(nanochat_dir, exist_ok=True) return nanochat_dir -def print0(s="",**kwargs): - ddp_rank = int(os.environ.get('RANK', 0)) + +def print0(s="", **kwargs): + ddp_rank = int(os.environ.get("RANK", 0)) if ddp_rank == 0: print(s, **kwargs) + def print_banner(): # Cool DOS Rebel font ASCII banner made with https://manytools.org/hacker-tools/ascii-banner/ banner = """ - █████ █████ - ░░███ ░░███ - ████████ ██████ ████████ ██████ ██████ ░███████ ██████ ███████ -░░███░░███ ░░░░░███ ░░███░░███ ███░░███ ███░░███ ░███░░███ ░░░░░███ ░░░███░ - ░███ ░███ ███████ ░███ ░███ ░███ ░███░███ ░░░ ░███ ░███ ███████ ░███ - ░███ ░███ ███░░███ ░███ ░███ ░███ ░███░███ ███ ░███ ░███ ███░░███ ░███ ███ - ████ █████░░████████ ████ █████░░██████ ░░██████ ████ █████░░████████ ░░█████ -░░░░ ░░░░░ ░░░░░░░░ ░░░░ ░░░░░ ░░░░░░ ░░░░░░ ░░░░ ░░░░░ ░░░░░░░░ ░░░░░ -""" + █████ █████ + ░░███ ░░███ + ████████ ██████ ████████ ██████ ██████ ░███████ ██████ ███████ + ░░███░░███ ░░░░░███ ░░███░░███ ███░░███ ███░░███ ░███░░███ ░░░░░███░░░███░ + ░███ ░███ ███████ ░███ ░███ ░███ ░███░███ ░░░ ░███ ░███ ███████ ░███ + ░███ ░███ ███░░███ ░███ ░███ ░███ ░███░███ ███ ░███ ░███ ███░░███ ░███ ███ + ████ █████░░████████ ████ █████░░██████ ░░██████ ████ █████░░███████ ░░█████ + ░░░░ ░░░░░ ░░░░░░░░ ░░░░ ░░░░░ ░░░░░░ ░░░░░░ ░░░░ ░░░░░ ░░░░░░░░ ░░░░░ + """ print0(banner) + def is_ddp(): # TODO is there a proper way - return int(os.environ.get('RANK', -1)) != -1 + return int(os.environ.get("RANK", -1)) != -1 + def get_dist_info(): if is_ddp(): - assert all(var in os.environ for var in ['RANK', 'LOCAL_RANK', 'WORLD_SIZE']) - ddp_rank = int(os.environ['RANK']) - ddp_local_rank = int(os.environ['LOCAL_RANK']) - ddp_world_size = int(os.environ['WORLD_SIZE']) + assert all(var in os.environ for var in ["RANK", "LOCAL_RANK", "WORLD_SIZE"]) + ddp_rank = int(os.environ["RANK"]) + ddp_local_rank = int(os.environ["LOCAL_RANK"]) + ddp_world_size = int(os.environ["WORLD_SIZE"]) return True, ddp_rank, ddp_local_rank, ddp_world_size else: return False, 0, 0, 1 + def compute_init(): """Basic initialization that we keep doing over and over, so make common.""" @@ -104,13 +124,13 @@ def compute_init(): # torch.backends.cudnn.benchmark = False # Precision - torch.set_float32_matmul_precision("high") # uses tf32 instead of fp32 for matmuls + torch.set_float32_matmul_precision("high") # uses tf32 instead of fp32 for matmuls # Distributed setup: Distributed Data Parallel (DDP), optional ddp, ddp_rank, ddp_local_rank, ddp_world_size = get_dist_info() if ddp: device = torch.device("cuda", ddp_local_rank) - torch.cuda.set_device(device) # make "cuda" default to this device + torch.cuda.set_device(device) # make "cuda" default to this device dist.init_process_group(backend="nccl", device_id=device) dist.barrier() else: @@ -121,16 +141,21 @@ def compute_init(): return ddp, ddp_rank, ddp_local_rank, ddp_world_size, device + def compute_cleanup(): """Companion function to compute_init, to clean things up before script exit""" if is_ddp(): dist.destroy_process_group() + class DummyWandb: """Useful if we wish to not use wandb but have all the same signatures""" + def __init__(self): pass + def log(self, *args, **kwargs): pass + def finish(self): pass From cbd560a83d93a8de8ebb238608ee571e7952e2ac Mon Sep 17 00:00:00 2001 From: svlandeg Date: Wed, 29 Oct 2025 11:42:56 +0100 Subject: [PATCH 3/4] revert formatting changes to minimize diff and merge conflicts --- nanochat/common.py | 67 +++++++++++++++------------------------------- 1 file changed, 22 insertions(+), 45 deletions(-) diff --git a/nanochat/common.py b/nanochat/common.py index bb825ff..a0867b0 100644 --- a/nanochat/common.py +++ b/nanochat/common.py @@ -8,58 +8,45 @@ import logging import torch import torch.distributed as dist - class ColoredFormatter(logging.Formatter): """Custom formatter that adds colors to log messages.""" - # ANSI color codes COLORS = { - "DEBUG": "\033[36m", # Cyan - "INFO": "\033[32m", # Green - "WARNING": "\033[33m", # Yellow - "ERROR": "\033[31m", # Red - "CRITICAL": "\033[35m", # Magenta + 'DEBUG': '\033[36m', # Cyan + 'INFO': '\033[32m', # Green + 'WARNING': '\033[33m', # Yellow + 'ERROR': '\033[31m', # Red + 'CRITICAL': '\033[35m', # Magenta } - RESET = "\033[0m" - BOLD = "\033[1m" + RESET = '\033[0m' + BOLD = '\033[1m' def format(self, record): # Add color to the level name levelname = record.levelname if levelname in self.COLORS: - record.levelname = ( - f"{self.COLORS[levelname]}{self.BOLD}{levelname}{self.RESET}" - ) + record.levelname = f"{self.COLORS[levelname]}{self.BOLD}{levelname}{self.RESET}" # Format the message message = super().format(record) # Add color to specific parts of the message - if levelname == "INFO": + if levelname == 'INFO': # Highlight numbers and percentages - message = re.sub( - r"(\d+\.?\d*\s*(?:GB|MB|%|docs))", - rf"{self.BOLD}\1{self.RESET}", - message, - ) - message = re.sub( - r"(Shard \d+)", - rf"{self.COLORS['INFO']}{self.BOLD}\1{self.RESET}", - message, - ) + message = re.sub(r'(\d+\.?\d*\s*(?:GB|MB|%|docs))', rf'{self.BOLD}\1{self.RESET}', message) + message = re.sub(r'(Shard \d+)', rf'{self.COLORS["INFO"]}{self.BOLD}\1{self.RESET}', message) return message def setup_default_logging(): handler = logging.StreamHandler() - handler.setFormatter( - ColoredFormatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s") + handler.setFormatter(ColoredFormatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')) + logging.basicConfig( + level=logging.INFO, + handlers=[handler] ) - logging.basicConfig(level=logging.INFO, handlers=[handler]) - setup_default_logging() logger = logging.getLogger(__name__) - def get_base_dir(): # co-locate nanochat intermediates with other cached data in ~/.cache (by default) if os.environ.get("NANOCHAT_BASE_DIR"): @@ -71,13 +58,11 @@ def get_base_dir(): os.makedirs(nanochat_dir, exist_ok=True) return nanochat_dir - def print0(s="", **kwargs): - ddp_rank = int(os.environ.get("RANK", 0)) + ddp_rank = int(os.environ.get('RANK', 0)) if ddp_rank == 0: print(s, **kwargs) - def print_banner(): # Cool DOS Rebel font ASCII banner made with https://manytools.org/hacker-tools/ascii-banner/ banner = """ @@ -92,23 +77,20 @@ def print_banner(): """ print0(banner) - def is_ddp(): # TODO is there a proper way - return int(os.environ.get("RANK", -1)) != -1 - + return int(os.environ.get('RANK', -1)) != -1 def get_dist_info(): if is_ddp(): - assert all(var in os.environ for var in ["RANK", "LOCAL_RANK", "WORLD_SIZE"]) - ddp_rank = int(os.environ["RANK"]) - ddp_local_rank = int(os.environ["LOCAL_RANK"]) - ddp_world_size = int(os.environ["WORLD_SIZE"]) + assert all(var in os.environ for var in ['RANK', 'LOCAL_RANK', 'WORLD_SIZE']) + ddp_rank = int(os.environ['RANK']) + ddp_local_rank = int(os.environ['LOCAL_RANK']) + ddp_world_size = int(os.environ['WORLD_SIZE']) return True, ddp_rank, ddp_local_rank, ddp_world_size else: return False, 0, 0, 1 - def compute_init(): """Basic initialization that we keep doing over and over, so make common.""" @@ -124,7 +106,7 @@ def compute_init(): # torch.backends.cudnn.benchmark = False # Precision - torch.set_float32_matmul_precision("high") # uses tf32 instead of fp32 for matmuls + torch.set_float32_matmul_precision("high") # uses tf32 instead of fp32 for matmuls # Distributed setup: Distributed Data Parallel (DDP), optional ddp, ddp_rank, ddp_local_rank, ddp_world_size = get_dist_info() @@ -141,21 +123,16 @@ def compute_init(): return ddp, ddp_rank, ddp_local_rank, ddp_world_size, device - def compute_cleanup(): """Companion function to compute_init, to clean things up before script exit""" if is_ddp(): dist.destroy_process_group() - class DummyWandb: """Useful if we wish to not use wandb but have all the same signatures""" - def __init__(self): pass - def log(self, *args, **kwargs): pass - def finish(self): pass From 3fa974f93c1f94c3dc0fe4f57915a0d1aa73feaf Mon Sep 17 00:00:00 2001 From: svlandeg Date: Wed, 29 Oct 2025 11:45:02 +0100 Subject: [PATCH 4/4] few more reverts --- nanochat/common.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/nanochat/common.py b/nanochat/common.py index a0867b0..d80d4ba 100644 --- a/nanochat/common.py +++ b/nanochat/common.py @@ -12,15 +12,14 @@ class ColoredFormatter(logging.Formatter): """Custom formatter that adds colors to log messages.""" # ANSI color codes COLORS = { - 'DEBUG': '\033[36m', # Cyan - 'INFO': '\033[32m', # Green + 'DEBUG': '\033[36m', # Cyan + 'INFO': '\033[32m', # Green 'WARNING': '\033[33m', # Yellow - 'ERROR': '\033[31m', # Red - 'CRITICAL': '\033[35m', # Magenta + 'ERROR': '\033[31m', # Red + 'CRITICAL': '\033[35m', # Magenta } RESET = '\033[0m' BOLD = '\033[1m' - def format(self, record): # Add color to the level name levelname = record.levelname @@ -35,7 +34,6 @@ class ColoredFormatter(logging.Formatter): message = re.sub(r'(Shard \d+)', rf'{self.COLORS["INFO"]}{self.BOLD}\1{self.RESET}', message) return message - def setup_default_logging(): handler = logging.StreamHandler() handler.setFormatter(ColoredFormatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')) @@ -58,7 +56,7 @@ def get_base_dir(): os.makedirs(nanochat_dir, exist_ok=True) return nanochat_dir -def print0(s="", **kwargs): +def print0(s="",**kwargs): ddp_rank = int(os.environ.get('RANK', 0)) if ddp_rank == 0: print(s, **kwargs)