Files
fn_registry/python/functions/ml/safetensors_inspect.py
T
egutierrez e3c8979e8d chore: auto-commit (95 archivos)
- cmd/fn/doctor.go
- cmd/fn/main.go
- cpp/apps/primitives_gallery/playground/tables/CMakeLists.txt
- cpp/apps/primitives_gallery/playground/tables/data_table.cpp
- cpp/apps/primitives_gallery/playground/tables/data_table_logic.cpp
- cpp/apps/primitives_gallery/playground/tables/data_table_logic.h
- cpp/apps/primitives_gallery/playground/tables/self_test.cpp
- cpp/apps/primitives_gallery/playground/tables/tql.cpp
- cpp/apps/primitives_gallery/playground/tables/viz.cpp
- cpp/apps/primitives_gallery/playground/tables/viz.h
- ...

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 00:50:34 +02:00

101 lines
3.4 KiB
Python

"""Lee solo el header de un archivo safetensors sin cargar los tensores en RAM."""
from __future__ import annotations
import json
import struct
def safetensors_inspect(path: str) -> dict:
"""Lee el header de un archivo safetensors sin cargar los tensores.
El formato safetensors almacena al inicio del archivo:
- 8 bytes: uint64 little-endian con la longitud del header JSON (N).
- N bytes: JSON con metadata y descriptores de tensores.
Este enfoque evita cargar gigabytes de pesos en RAM para inspeccionar
un checkpoint: solo se leen los primeros 8 + N bytes.
Spec: https://github.com/huggingface/safetensors#format
Args:
path: ruta al archivo .safetensors (absoluta o relativa).
Returns:
dict con claves:
path (str): ruta absoluta del archivo.
metadata (dict): metadatos del modelo (campo "__metadata__" del header).
tensors (list[dict]): lista de tensores, cada uno con:
name (str): nombre del tensor.
dtype (str): tipo de dato (F32, BF16, F16, I32, etc.).
shape (list[int]): dimensiones del tensor.
data_offsets (list[int]): [inicio, fin] en bytes dentro del
bloque de datos (para acceso lazy si se necesita).
total_size_bytes (int): tamano total del archivo en bytes.
n_tensors (int): numero de tensores en el archivo.
Raises:
FileNotFoundError: si el archivo no existe.
ValueError: si el archivo no es un safetensors valido.
OSError: si no se puede leer el archivo.
"""
import os
abs_path = os.path.abspath(path)
if not os.path.isfile(abs_path):
raise FileNotFoundError(f"safetensors_inspect: archivo no encontrado: {abs_path}")
total_size = os.path.getsize(abs_path)
with open(abs_path, "rb") as f:
# Leer los 8 bytes del tamano del header
raw_len = f.read(8)
if len(raw_len) < 8:
raise ValueError(
f"safetensors_inspect: archivo demasiado corto para ser safetensors: {abs_path}"
)
header_len = struct.unpack("<Q", raw_len)[0] # uint64 little-endian
if header_len == 0 or header_len > total_size:
raise ValueError(
f"safetensors_inspect: header_len invalido ({header_len}) en {abs_path}"
)
raw_header = f.read(header_len)
if len(raw_header) < header_len:
raise ValueError(
f"safetensors_inspect: no se pudo leer el header completo en {abs_path}"
)
try:
header = json.loads(raw_header.decode("utf-8"))
except (json.JSONDecodeError, UnicodeDecodeError) as exc:
raise ValueError(
f"safetensors_inspect: header JSON invalido en {abs_path}: {exc}"
) from exc
metadata = header.get("__metadata__", {})
tensors = []
for name, desc in header.items():
if name == "__metadata__":
continue
tensors.append(
{
"name": name,
"dtype": desc.get("dtype", ""),
"shape": desc.get("shape", []),
"data_offsets": desc.get("data_offsets", []),
}
)
return {
"path": abs_path,
"metadata": metadata,
"tensors": tensors,
"total_size_bytes": total_size,
"n_tensors": len(tensors),
}