saveModel static method

void saveModel(
  1. Map<String, GPUTensor> modelTensors,
  2. String outputPath
)

Exports the VRAM tensors to a standard Hugging Face .safetensors file

Implementation

static void saveModel(Map<String, GPUTensor> modelTensors, String outputPath) {
  Logger.log('Starting export to $outputPath...');

  Map<String, dynamic> jsonHeader = <String, dynamic>{};
  BytesBuilder rawBuffer = BytesBuilder();
  int currentOffset = 0;

  // 1. Iterate through all mapped parameters (e.g., 'bert.encoder.layer.0...')
  List<String> keys = modelTensors.keys.toList();

  for (int i = 0; i < keys.length; i = i + 1) {
    String tensorName = keys[i];
    GPUTensor tensor = modelTensors[tensorName]!;

    // Pull the latest weights from the GPU back to the Dart CPU Heap
    tensor.toCpu();

    // Flatten the multidimensional array
    List<double> flatData = _flattenTensor(tensor.value);

    // Convert Dart 64-bit doubles to 32-bit floats for standard HF F32 format
    Float32List f32List = Float32List.fromList(flatData);
    Uint8List byteData = f32List.buffer.asUint8List();

    int byteLength = byteData.length;

    // 2. Build the JSON Metadata for this specific tensor
    jsonHeader[tensorName] = <String, dynamic>{
      "dtype": "F32",
      "shape": tensor.shape,
      "data_offsets": <int>[currentOffset, currentOffset + byteLength]
    };

    // 3. Append to the giant binary buffer
    rawBuffer.add(byteData);
    currentOffset = currentOffset + byteLength;

    Logger.log('Processed: $tensorName (Shape: ${tensor.shape})');
  }

  // Safetensors requires an "__metadata__" block (often used for format info)
  jsonHeader["__metadata__"] = <String, String>{
    "format": "pt"
  };

  // 4. Encode JSON
  String jsonString = jsonEncode(jsonHeader);
  Uint8List jsonBytes = utf8.encode(jsonString);

  // 5. Create the 8-byte little-endian header length prefix
  ByteData headerLengthBytes = ByteData(8);
  headerLengthBytes.setUint64(0, jsonBytes.length, Endian.little);

  // 6. Write everything to disk!
  File file = File(outputPath);
  BytesBuilder fileBuilder = BytesBuilder();

  fileBuilder.add(headerLengthBytes.buffer.asUint8List()); // 8 bytes
  fileBuilder.add(jsonBytes);                              // JSON String
  fileBuilder.add(rawBuffer.takeBytes());                  // Raw F32 Tensor Data

  file.writeAsBytesSync(fileBuilder.takeBytes());

  Logger.log('Export Complete! Saved successfully to $outputPath');
}