resolveNativeModelAsset function

Future<ModelAsset> resolveNativeModelAsset(
  1. ModelAsset asset
)

Resolves URI-backed model assets before crossing the native ABI.

Implementation

Future<ModelAsset> resolveNativeModelAsset(ModelAsset asset) async {
  if (asset case ModelAssetUri(:final uri, sha256: final expectedSha256)) {
    if (uri.scheme == 'file') {
      return ModelAsset.path(uri.toFilePath());
    }
    if (uri.scheme != 'https') {
      throw const MpException(
        MpStatus.invalidArgument,
        'Native model URIs must use HTTPS or the file scheme.',
      );
    }

    final HttpClient client = HttpClient()..connectionTimeout = const Duration(seconds: 30);
    try {
      final HttpClientRequest request = await client.getUrl(uri);
      final HttpClientResponse response = await request.close();
      if (response.statusCode < 200 || response.statusCode >= 300) {
        throw MpException(
          MpStatus.unavailable,
          'Downloading the model failed with HTTP ${response.statusCode}.',
        );
      }
      final BytesBuilder bytes = BytesBuilder(copy: false);
      var length = 0;
      await for (final List<int> chunk in response) {
        length += chunk.length;
        if (length > _maximumRemoteModelBytes) {
          throw const MpException(
            MpStatus.resourceExhausted,
            'The remote model exceeds the 512 MiB safety limit.',
          );
        }
        bytes.add(chunk);
      }
      final Uint8List result = bytes.takeBytes();
      if (expectedSha256 != null && sha256.convert(result).toString() != expectedSha256) {
        throw const MpException(
          MpStatus.dataLoss,
          'The downloaded model does not match its SHA-256 digest.',
        );
      }
      return ModelAsset.bytes(result, name: uri.pathSegments.lastOrNull);
    } finally {
      client.close(force: true);
    }
  }
  return asset;
}