8#include <nlohmann/json.hpp>
91 throw std::runtime_error(
"Shard: Failed to open file (Windows): " +
file_path +
" Error: " + std::to_string(
GetLastError()));
98 throw std::runtime_error(
"Shard: Failed to get file size (Windows): " +
file_path);
104 throw std::runtime_error(
"Shard: File is empty: " +
file_path);
111 throw std::runtime_error(
"Shard: Failed to create file mapping (Windows): " +
file_path +
" Error: " + std::to_string(
GetLastError()));
120 throw std::runtime_error(
"Shard: Failed to map view of file (Windows): " +
file_path +
" Error: " + std::to_string(
GetLastError()));
138 throw std::runtime_error(
"Shard: File is empty: " +
file_path);
146 throw std::runtime_error(
"Shard: Failed to memory map file: " +
file_path +
" Error: " +
strerror(
errno));
152 throw std::runtime_error(
"Shard: File too small (" + std::to_string(
file_size) +
" bytes) to be a valid SafeTensors shard (min 8 bytes for metadata length): " +
file_path);
157 throw std::runtime_error(
"Shard: Metadata size is 0 in file header: " +
file_path);
160 throw std::runtime_error(
"Shard: Declared metadata size (" + std::to_string(
metadata_size) +
") plus header (8 bytes) exceeds file size (" + std::to_string(
file_size) +
") in: " +
file_path);
205 : file_path(std::move(
other.file_path)),
206 mapped_data(
other.mapped_data),
207 file_size(
other.file_size),
208 metadata_size(
other.metadata_size),
209 metadata_ptr(
other.metadata_ptr),
210 tensor_data_block_ptr(
other.tensor_data_block_ptr)
218 other.mapped_data =
nullptr;
220 other.metadata_size = 0;
221 other.metadata_ptr =
nullptr;
222 other.tensor_data_block_ptr =
nullptr;
232 if (
this != &
other) {
234 file_path = std::move(
other.file_path);
235 mapped_data =
other.mapped_data;
236 file_size =
other.file_size;
237 metadata_size =
other.metadata_size;
238 metadata_ptr =
other.metadata_ptr;
239 tensor_data_block_ptr =
other.tensor_data_block_ptr;
246 other.mapped_data =
nullptr;
248 other.metadata_size = 0;
249 other.metadata_ptr =
nullptr;
250 other.tensor_data_block_ptr =
nullptr;
267 throw std::logic_error(
"Shard not properly mapped or initialized to get tensor data: " +
file_path);
273 throw std::out_of_range(
274 "Tensor data (local_offset: " + std::to_string(
local_offset) +
275 ", n_bytes: " + std::to_string(
n_bytes) +
276 ") out of bounds for data block of shard: " +
file_path +
288 if (!std::filesystem::exists(
path_obj)){
289 throw std::runtime_error(
"SafeTensorsLoader: Provided model_load_path does not exist: " +
model_load_path_);
292 if (std::filesystem::is_directory(
path_obj)) {
293 Logger::info(
"SafeTensorsLoader: Path is a directory. Attempting to load from directory.");
295 }
else if (std::filesystem::is_regular_file(
path_obj)) {
296 Logger::info(
"SafeTensorsLoader: Path is a single file. Loading single file.");
301 throw std::runtime_error(
"SafeTensorsLoader: model_load_path is not a valid file or directory: " +
model_load_path_);
305 Logger::warning(
"SafeTensorsLoader: Initialization complete, but no tensors were loaded and no shards mapped. Check model path and format: " +
model_load_path_);
307 Logger::info(
"SafeTensorsLoader: Initialization complete. Total unique tensors mapped: " + std::to_string(
tensors_.size()) +
339 throw std::runtime_error(
"SafeTensorsLoader: Failed to open index file: " +
actual_index_path.string());
344 }
catch (
const nlohmann::json::parse_error&
e) {
346 throw std::runtime_error(
"SafeTensorsLoader: Failed to parse index JSON from " +
actual_index_path.string() +
": " +
e.what());
373 Logger::debug(
"SafeTensorsLoader: Shard '" +
shard_filename +
"' already loaded/parsed (should not happen if unique_shards logic is correct).");
378 throw std::runtime_error(
"SafeTensorsLoader: Index file " +
actual_index_path.string() +
" does not contain a valid 'weight_map'.");
383 for (
const auto&
entry : std::filesystem::directory_iterator(
dir_p)) {
384 if (
entry.is_regular_file() &&
entry.path().extension() ==
".safetensors") {
390 Logger::warning(
"SafeTensorsLoader: No .safetensors files found directly in directory: " +
directory_path_str +
". Checking for model.safetensors as last resort.");
393 Logger::info(
"SafeTensorsLoader: Found 'model.safetensors' in directory, loading it as a single non-sharded model.");
397 Logger::info(
"SafeTensorsLoader: No .safetensors files or index.json found in directory: " +
directory_path_str +
". No model weights will be loaded from this path directly.");
400 Logger::info(
"SafeTensorsLoader: Found single .safetensors file: " +
shard_files[0].
string() +
". Loading as non-sharded.");
404 Logger::info(
"SafeTensorsLoader: Found " + std::to_string(
shard_files.size()) +
" .safetensors files (no index). Loading all as individual shards.");
418 Logger::debug(
"SafeTensorsLoader: Shard/file '" +
key_to_use +
"' (path: " + file_path +
") already processed/loaded.");
423 auto shard = std::make_unique<Shard>(file_path);
426 }
catch (
const std::exception&
e) {
427 throw std::runtime_error(
"SafeTensorsLoader: Error processing file/shard '" + file_path +
"' (key: " +
key_to_use +
"): " +
e.what());
432 Logger::debug(
"SafeTensorsLoader: Parsing metadata for shard: " + shard_key +
" (file: " +
shard.file_path +
")");
433 if (!
shard.metadata_ptr ||
shard.metadata_size == 0) {
434 throw std::runtime_error(
"Shard metadata is not available for parsing (nullptr or zero size): " +
shard.file_path);
439 }
catch (
const std::length_error&
le) {
440 throw std::runtime_error(
"Error constructing metadata string for shard " +
shard.file_path +
": " +
le.what());
446 }
catch (
const nlohmann::json::parse_error&
e) {
447 throw std::runtime_error(
"Failed to parse metadata JSON for shard " +
shard.file_path +
" (key: " + shard_key +
") at offset 8, metadata_size: " +
448 std::to_string(
shard.metadata_size) +
". Error: " +
e.what() +
449 "\nJSON content snippet (first 200 chars): " +
metadata_json_str.substr(0, 200));
461 [](
unsigned char c){ return static_cast<char>(std::toupper(c)); });
468 throw std::runtime_error(
"Tensor '" +
tensor_name_str +
"' 'data_offsets' must be an array of two numbers.");
479 "Previous shard key: '" +
tensors_[
tensor_info.name].shard_key +
"', New shard key: '" + shard_key +
"'. " +
480 "Overwriting with info from current shard being parsed. This can happen with unindexed multi-file loads or inconsistent index files.");
489 }
catch (
const nlohmann::json::exception&
e) {
490 throw std::runtime_error(
"Failed to parse tensor info for '" +
tensor_name_str +
"' in shard " +
491 shard.file_path +
" (key: " + shard_key +
"): " +
e.what());
498 std::vector<std::string>
names;
509 throw std::runtime_error(
"Tensor not found in SafeTensorsLoader metadata: " +
name);
526 throw std::logic_error(
"Internal inconsistency: Could not determine shard key for tensor '" +
tensor_name +
"'.");
531 throw std::logic_error(
"Internal inconsistency: Shard key '" +
determined_shard_key +
"' for tensor '" +
tensor_name +
"' not found in loaded_shards_ map. Tensors map has it, but shard object itself is missing.");
545 std::map<std::string, std::vector<uint8_t>>
result_map;
547 Logger::debug(
"SafeTensorsLoader::load_all_tensors_parallel: No tensors to load.");
551 std::vector<std::future<std::pair<std::string, std::vector<uint8_t>>>>
futures;
552 unsigned int n_threads = std::max(1u, std::thread::hardware_concurrency());
557 Logger::info(
"SafeTensorsLoader: Loading all " + std::to_string(
tensors_.size()) +
" tensors in parallel using " + std::to_string(
n_threads) +
" threads.");
562 std::vector<uint8_t> data = this->get_tensor_bytes(tensor_name);
563 return std::make_pair(tensor_name, std::move(data));
569 std::pair<std::string, std::vector<uint8_t>>
tensor_pair =
fut.get();
571 }
catch (
const std::exception&
e) {
572 Logger::error(
"SafeTensorsLoader: Error loading a tensor in parallel task: " + std::string(
e.what()));
576 Logger::info(
"SafeTensorsLoader: Finished loading all tensors in parallel.");
584 size_t num_elements =
n_bytes / 2;
585 std::vector<float>
f32_vec(num_elements);
587 for (
size_t i = 0;
i < num_elements; ++
i) {
590 std::vector<uint8_t>
bytes_out(num_elements *
sizeof(
float));
594 size_t num_elements =
n_bytes / 2;
595 std::vector<float>
f32_vec(num_elements);
597 for (
size_t i = 0;
i < num_elements; ++
i) {
600 std::vector<uint8_t>
bytes_out(num_elements *
sizeof(
float));
604 throw std::runtime_error(
"SafeTensorsLoader: Unsupported tensor dtype for conversion: " +
dtype_str_upper);
613 }
else if (std::filesystem::is_regular_file(
model_fs_path)) {
628 nlohmann::json data = nlohmann::json::parse(
f);
637 config_to_populate.max_position_embeddings = data.value(
"max_position_embeddings", 2048);
645 if (data.contains(
"architectures") && data[
"architectures"].is_array() && !data[
"architectures"].empty()) {
676 }
catch (
const nlohmann::json::exception&
e) {
695 task = std::move(this->
tasks_.front());
static void debug(const std::string &message)
static void warning(const std::string &message)
static void info(const std::string &message)
static void error(const std::string &message)
std::map< std::string, std::unique_ptr< Shard > > loaded_shards_
std::map< std::string, TensorInfo > tensors_
SafeTensorsLoader(const std::string &model_load_path)
Constructs a SafeTensorsLoader.
const Shard * get_shard_for_tensor(const std::string &tensor_name) const
Get the Shard object for a given tensor name.
void load_from_directory(const std::string &directory_path)
Load tensors from a directory, handling index files and multiple shards.
static bool load_model_config_from_json(const std::string &model_path_or_dir, ModelConfig &config_to_populate)
Loads model configuration from a JSON file corresponding to a .safetensors model path.
std::map< std::string, std::string > tensor_name_to_shard_key_map_
void load_single_file(const std::string &file_path, const std::string &shard_key_override="")
Load a single .safetensors file as a shard.
std::map< std::string, std::vector< uint8_t > > load_all_tensors_parallel() const
Load all tensors in parallel.
std::vector< std::string > tensor_names() const
Get a list of all tensor names available in the loaded model.
const TensorInfo & get_tensor_info(const std::string &name) const
Get information about a specific tensor.
std::vector< uint8_t > convert_tensor_data(const uint8_t *data, size_t size, const std::string &dtype) const
Convert raw tensor data to FP32 if needed.
std::vector< uint8_t > get_tensor_bytes(const std::string &name) const
Get the raw bytes for a tensor, converting to FP32 if needed.
std::string model_load_path_
~SafeTensorsLoader()
Destructor. Cleans up all memory-mapped shards.
void parse_shard_metadata(Shard &shard, const std::string &shard_key)
Parse the metadata of a shard and populate tensor information.
Thread pool for parallel tensor loading operations.
~ThreadPool()
Destructor that ensures proper cleanup of threads.
ThreadPool(size_t num_threads)
Constructs a thread pool with specified number of threads.
std::queue< std::function< void()> > tasks_
std::vector< std::thread > workers_
std::condition_variable condition_
const float * p[kIrDepthSourcesMax]
Logging utilities for the TinyLlama implementation.
float cpu_f16_to_float32(uint16_t f16_raw)
float cpu_bf16_to_float32(uint16_t bf16_raw)
SafeTensors format loader for efficient tensor loading, supporting single and sharded models.
Model configuration structure holding architecture and hyperparameters.
Information about a tensor stored in the SafeTensors file(s)
Represents a memory-mapped SafeTensors file (shard).
~Shard()
Destructor. Cleans up memory mapping and file handles.
uint64_t metadata_size
Size of the metadata block in bytes.
const uint8_t * tensor_data_block_ptr
Pointer to the start of the tensor data block.
void * mapped_data
Pointer to the memory-mapped data.
std::string file_path
Path to the shard file.
const uint8_t * metadata_ptr
Pointer to the start of the metadata block.
Shard & operator=(Shard &&other) noexcept
Move assignment operator.
size_t file_size
Size of the mapped file in bytes.
const uint8_t * get_tensor_raw_data(size_t local_offset, size_t n_bytes) const
Get a pointer to the raw tensor data within this shard.
Shard(const std::string &fp)
Construct and memory-map a shard file.