ai-edge-litert-nightly 2.2.0.dev20260102__cp312-cp312-manylinux_2_27_x86_64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ai_edge_litert/__init__.py +1 -0
- ai_edge_litert/_pywrap_analyzer_wrapper.so +0 -0
- ai_edge_litert/_pywrap_litert_compiled_model_wrapper.so +0 -0
- ai_edge_litert/_pywrap_litert_interpreter_wrapper.so +0 -0
- ai_edge_litert/_pywrap_litert_tensor_buffer_wrapper.so +0 -0
- ai_edge_litert/_pywrap_modify_model_interface.so +0 -0
- ai_edge_litert/_pywrap_string_util.so +0 -0
- ai_edge_litert/_pywrap_tensorflow_lite_calibration_wrapper.so +0 -0
- ai_edge_litert/_pywrap_tensorflow_lite_metrics_wrapper.so +0 -0
- ai_edge_litert/any_pb2.py +37 -0
- ai_edge_litert/aot/__init__.py +0 -0
- ai_edge_litert/aot/ai_pack/__init__.py +0 -0
- ai_edge_litert/aot/ai_pack/export_lib.py +300 -0
- ai_edge_litert/aot/aot_compile.py +153 -0
- ai_edge_litert/aot/core/__init__.py +0 -0
- ai_edge_litert/aot/core/apply_plugin.py +148 -0
- ai_edge_litert/aot/core/common.py +97 -0
- ai_edge_litert/aot/core/components.py +93 -0
- ai_edge_litert/aot/core/mlir_transforms.py +36 -0
- ai_edge_litert/aot/core/tflxx_util.py +30 -0
- ai_edge_litert/aot/core/types.py +374 -0
- ai_edge_litert/aot/prepare_for_npu.py +152 -0
- ai_edge_litert/aot/vendors/__init__.py +22 -0
- ai_edge_litert/aot/vendors/example/__init__.py +0 -0
- ai_edge_litert/aot/vendors/example/example_backend.py +157 -0
- ai_edge_litert/aot/vendors/fallback_backend.py +128 -0
- ai_edge_litert/aot/vendors/google_tensor/__init__.py +0 -0
- ai_edge_litert/aot/vendors/google_tensor/google_tensor_backend.py +168 -0
- ai_edge_litert/aot/vendors/google_tensor/target.py +84 -0
- ai_edge_litert/aot/vendors/import_vendor.py +132 -0
- ai_edge_litert/aot/vendors/mediatek/__init__.py +0 -0
- ai_edge_litert/aot/vendors/mediatek/mediatek_backend.py +196 -0
- ai_edge_litert/aot/vendors/mediatek/target.py +94 -0
- ai_edge_litert/aot/vendors/qualcomm/__init__.py +0 -0
- ai_edge_litert/aot/vendors/qualcomm/qualcomm_backend.py +161 -0
- ai_edge_litert/aot/vendors/qualcomm/target.py +75 -0
- ai_edge_litert/api_pb2.py +43 -0
- ai_edge_litert/compiled_model.py +250 -0
- ai_edge_litert/descriptor_pb2.py +3361 -0
- ai_edge_litert/duration_pb2.py +37 -0
- ai_edge_litert/empty_pb2.py +37 -0
- ai_edge_litert/field_mask_pb2.py +37 -0
- ai_edge_litert/format_converter_wrapper_pybind11.so +0 -0
- ai_edge_litert/hardware_accelerator.py +22 -0
- ai_edge_litert/internal/__init__.py +0 -0
- ai_edge_litert/internal/litertlm_builder.py +584 -0
- ai_edge_litert/internal/litertlm_core.py +58 -0
- ai_edge_litert/internal/litertlm_header_schema_py_generated.py +1596 -0
- ai_edge_litert/internal/llm_metadata_pb2.py +45 -0
- ai_edge_litert/internal/llm_model_type_pb2.py +51 -0
- ai_edge_litert/internal/sampler_params_pb2.py +39 -0
- ai_edge_litert/internal/token_pb2.py +38 -0
- ai_edge_litert/interpreter.py +1039 -0
- ai_edge_litert/libLiteRt.so +0 -0
- ai_edge_litert/libpywrap_litert_common.so +0 -0
- ai_edge_litert/metrics_interface.py +48 -0
- ai_edge_litert/metrics_portable.py +70 -0
- ai_edge_litert/model_runtime_info_pb2.py +66 -0
- ai_edge_litert/plugin_pb2.py +46 -0
- ai_edge_litert/profiling_info_pb2.py +47 -0
- ai_edge_litert/pywrap_genai_ops.so +0 -0
- ai_edge_litert/schema_py_generated.py +19640 -0
- ai_edge_litert/source_context_pb2.py +37 -0
- ai_edge_litert/struct_pb2.py +47 -0
- ai_edge_litert/tensor_buffer.py +167 -0
- ai_edge_litert/timestamp_pb2.py +37 -0
- ai_edge_litert/tools/__init__.py +0 -0
- ai_edge_litert/tools/apply_plugin_main +0 -0
- ai_edge_litert/tools/flatbuffer_utils.py +534 -0
- ai_edge_litert/type_pb2.py +53 -0
- ai_edge_litert/vendors/google_tensor/compiler/libLiteRtCompilerPlugin_google_tensor.so +0 -0
- ai_edge_litert/vendors/mediatek/compiler/libLiteRtCompilerPlugin_MediaTek.so +0 -0
- ai_edge_litert/vendors/qualcomm/compiler/libLiteRtCompilerPlugin_Qualcomm.so +0 -0
- ai_edge_litert/wrappers_pb2.py +53 -0
- ai_edge_litert_nightly-2.2.0.dev20260102.dist-info/METADATA +52 -0
- ai_edge_litert_nightly-2.2.0.dev20260102.dist-info/RECORD +78 -0
- ai_edge_litert_nightly-2.2.0.dev20260102.dist-info/WHEEL +5 -0
- ai_edge_litert_nightly-2.2.0.dev20260102.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,584 @@
|
|
|
1
|
+
# Copyright 2025 The ODML Authors.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
"""Builder class for LiteRT-LM files.
|
|
17
|
+
|
|
18
|
+
Example usage:
|
|
19
|
+
```
|
|
20
|
+
builder = litertlm_builder.LitertLmFileBuilder()
|
|
21
|
+
builder.add_system_metadata(
|
|
22
|
+
litertlm_builder.Metadata(
|
|
23
|
+
key="Authors",
|
|
24
|
+
value="The ODML Authors",
|
|
25
|
+
dtype=litertlm_builder.DType.STRING,
|
|
26
|
+
)
|
|
27
|
+
)
|
|
28
|
+
builder.add_tflite_model(
|
|
29
|
+
model_path,
|
|
30
|
+
litertlm_builder.TfLiteModelType.PREFILL_DECODE,
|
|
31
|
+
)
|
|
32
|
+
builder.add_sentencepiece_tokenizer(tokenizer_path)
|
|
33
|
+
builder.add_llm_metadata(llm_metadata_path)
|
|
34
|
+
with litertlm_core.open_file(output_path, "wb") as f:
|
|
35
|
+
builder.build(f)
|
|
36
|
+
```
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
# TODO(b/445163709): Remove this module once litert_lm publishes a pypi package.
|
|
40
|
+
|
|
41
|
+
import dataclasses
|
|
42
|
+
import enum
|
|
43
|
+
import os # pylint: disable=unused-import
|
|
44
|
+
from typing import Any, BinaryIO, Callable, IO, Optional, TypeVar, Union
|
|
45
|
+
import zlib
|
|
46
|
+
import flatbuffers
|
|
47
|
+
from google.protobuf import message
|
|
48
|
+
from google.protobuf import text_format
|
|
49
|
+
from ai_edge_litert.internal import litertlm_core
|
|
50
|
+
from ai_edge_litert.internal import litertlm_header_schema_py_generated as schema
|
|
51
|
+
from ai_edge_litert.internal import llm_metadata_pb2
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@enum.unique
|
|
55
|
+
class DType(enum.Enum):
|
|
56
|
+
"""DType enum.
|
|
57
|
+
|
|
58
|
+
This enum maps to the data types defined in the LiteRT-LM flatbuffers schema.
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
INT8 = "int8"
|
|
62
|
+
INT16 = "int16"
|
|
63
|
+
INT32 = "int32"
|
|
64
|
+
INT64 = "int64"
|
|
65
|
+
UINT8 = "uint8"
|
|
66
|
+
UINT16 = "uint16"
|
|
67
|
+
UINT32 = "uint32"
|
|
68
|
+
UINT64 = "uint64"
|
|
69
|
+
FLOAT32 = "float32"
|
|
70
|
+
DOUBLE = "double"
|
|
71
|
+
BOOL = "bool"
|
|
72
|
+
STRING = "string"
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
@dataclasses.dataclass
|
|
76
|
+
class Metadata:
|
|
77
|
+
"""Metadata class."""
|
|
78
|
+
|
|
79
|
+
key: str
|
|
80
|
+
value: Any
|
|
81
|
+
dtype: DType
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
@enum.unique
|
|
85
|
+
class TfLiteModelType(enum.Enum):
|
|
86
|
+
"""TfLiteModelType enum.
|
|
87
|
+
|
|
88
|
+
This enum maps to the model types defined in the LiteRT-LM flatbuffers schema.
|
|
89
|
+
"""
|
|
90
|
+
|
|
91
|
+
PREFILL_DECODE = "tf_lite_prefill_decode"
|
|
92
|
+
|
|
93
|
+
EMBEDDER = "tf_lite_embedder"
|
|
94
|
+
PER_LAYER_EMBEDDER = "tf_lite_per_layer_embedder"
|
|
95
|
+
|
|
96
|
+
AUX = "tf_lite_aux"
|
|
97
|
+
|
|
98
|
+
AUDIO_FRONTEND = "tf_lite_audio_frontend"
|
|
99
|
+
AUDIO_ENCODER_HW = "tf_lite_audio_encoder_hw"
|
|
100
|
+
AUDIO_ADAPTER = "tf_lite_audio_adapter"
|
|
101
|
+
END_OF_AUDIO = "tf_lite_end_of_audio"
|
|
102
|
+
|
|
103
|
+
VISION_ENCODER = "tf_lite_vision_encoder"
|
|
104
|
+
VISION_ADAPTER = "tf_lite_vision_adapter"
|
|
105
|
+
|
|
106
|
+
@classmethod
|
|
107
|
+
def get_enum_from_tf_free_value(cls, tf_free_value: str) -> "TfLiteModelType":
|
|
108
|
+
"""A helper method to get the enum value from the TF-free value."""
|
|
109
|
+
value = "tf_lite_" + tf_free_value.lower()
|
|
110
|
+
return cls(value)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
@dataclasses.dataclass
|
|
114
|
+
class _SectionObject:
|
|
115
|
+
# Metadata for the section.
|
|
116
|
+
metadata: list[Metadata]
|
|
117
|
+
# The data type of the section.
|
|
118
|
+
data_type: schema.AnySectionDataType | int
|
|
119
|
+
# The data reader for the section. This should return the data as a byte
|
|
120
|
+
# string.
|
|
121
|
+
data_reader: Callable[[], bytes]
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
@enum.unique
|
|
125
|
+
class LlmModelType(enum.StrEnum):
|
|
126
|
+
"""LLM model type for the LiteRT LM model."""
|
|
127
|
+
|
|
128
|
+
GENERIC = "generic"
|
|
129
|
+
GEMMA3N = "gemma3n"
|
|
130
|
+
GEMMA3 = "gemma3"
|
|
131
|
+
QWEN3 = "qwen3"
|
|
132
|
+
QWEN2P5 = "qwen2p5"
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
LitertLmFileBuilderT = TypeVar(
|
|
136
|
+
"LitertLmFileBuilderT", bound="LitertLmFileBuilder"
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
class LitertLmFileBuilder:
|
|
141
|
+
"""LitertLmFileBuilder class.
|
|
142
|
+
|
|
143
|
+
This is the primary entry point for building a LiteRT-LM file. It provides
|
|
144
|
+
methods to add system metadata, sections, and llm metadata to the file.
|
|
145
|
+
|
|
146
|
+
Example usage:
|
|
147
|
+
```
|
|
148
|
+
builder = litertlm_builder.LitertLmFileBuilder()
|
|
149
|
+
builder.add_system_metadata(
|
|
150
|
+
litertlm_builder.Metadata(
|
|
151
|
+
key="Authors",
|
|
152
|
+
value="The ODML Authors",
|
|
153
|
+
dtype=litertlm_builder.DType.STRING,
|
|
154
|
+
)
|
|
155
|
+
)
|
|
156
|
+
builder.add_tflite_model(
|
|
157
|
+
model_path,
|
|
158
|
+
litertlm_builder.TfLiteModelType.PREFILL_DECODE,
|
|
159
|
+
)
|
|
160
|
+
builder.add_sentencepiece_tokenizer(tokenizer_path)
|
|
161
|
+
builder.add_llm_metadata(llm_metadata_path)
|
|
162
|
+
with litertlm_core.open_file(output_path, "wb") as f:
|
|
163
|
+
builder.build(f)
|
|
164
|
+
```
|
|
165
|
+
"""
|
|
166
|
+
|
|
167
|
+
def __init__(self):
|
|
168
|
+
self._system_metadata: list[Metadata] = []
|
|
169
|
+
self._sections: list[_SectionObject] = []
|
|
170
|
+
self._has_llm_metadata = False
|
|
171
|
+
self._has_tokenizer = False
|
|
172
|
+
|
|
173
|
+
def add_system_metadata(
|
|
174
|
+
self,
|
|
175
|
+
metadata: Metadata,
|
|
176
|
+
) -> LitertLmFileBuilderT:
|
|
177
|
+
"""Adds system level metadata to the litertlm file."""
|
|
178
|
+
for existing_metadata in self._system_metadata:
|
|
179
|
+
if existing_metadata.key == metadata.key:
|
|
180
|
+
raise ValueError(
|
|
181
|
+
f"System metadata already exists for key: {metadata.key}"
|
|
182
|
+
)
|
|
183
|
+
self._system_metadata.append(metadata)
|
|
184
|
+
return self
|
|
185
|
+
|
|
186
|
+
def add_llm_metadata(
|
|
187
|
+
self,
|
|
188
|
+
llm_metadata_path: str,
|
|
189
|
+
additional_metadata: Optional[list[Metadata]] = None,
|
|
190
|
+
) -> LitertLmFileBuilderT:
|
|
191
|
+
"""Adds llm metadata to the litertlm file.
|
|
192
|
+
|
|
193
|
+
Args:
|
|
194
|
+
llm_metadata_path: The path to the llm metadata file. Can be binary or
|
|
195
|
+
textproto format.
|
|
196
|
+
additional_metadata: Additional metadata to add to the llm metadata.
|
|
197
|
+
|
|
198
|
+
Returns:
|
|
199
|
+
The currentLitertLmFileBuilder object.
|
|
200
|
+
|
|
201
|
+
Raises:
|
|
202
|
+
FileNotFoundError: If the llm metadata file is not found.
|
|
203
|
+
"""
|
|
204
|
+
assert not self._has_llm_metadata, "Llm metadata already added."
|
|
205
|
+
self._has_llm_metadata = True
|
|
206
|
+
if not litertlm_core.path_exists(llm_metadata_path):
|
|
207
|
+
raise FileNotFoundError(
|
|
208
|
+
f"Llm metadata file not found: {llm_metadata_path}"
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
if _is_binary_proto(llm_metadata_path):
|
|
212
|
+
|
|
213
|
+
def data_reader():
|
|
214
|
+
with litertlm_core.open_file(llm_metadata_path, "rb") as f:
|
|
215
|
+
return f.read()
|
|
216
|
+
|
|
217
|
+
else:
|
|
218
|
+
|
|
219
|
+
def data_reader():
|
|
220
|
+
with litertlm_core.open_file(llm_metadata_path, "r") as f:
|
|
221
|
+
return text_format.Parse(
|
|
222
|
+
f.read(), llm_metadata_pb2.LlmMetadata()
|
|
223
|
+
).SerializeToString()
|
|
224
|
+
|
|
225
|
+
section_object = _SectionObject(
|
|
226
|
+
metadata=additional_metadata if additional_metadata else [],
|
|
227
|
+
data_type=schema.AnySectionDataType.LlmMetadataProto,
|
|
228
|
+
data_reader=data_reader,
|
|
229
|
+
)
|
|
230
|
+
self._sections.append(section_object)
|
|
231
|
+
return self
|
|
232
|
+
|
|
233
|
+
def add_tflite_model(
|
|
234
|
+
self,
|
|
235
|
+
tflite_model_path: str,
|
|
236
|
+
model_type: TfLiteModelType,
|
|
237
|
+
additional_metadata: Optional[list[Metadata]] = None,
|
|
238
|
+
) -> LitertLmFileBuilderT:
|
|
239
|
+
"""Adds a tflite model to the litertlm file.
|
|
240
|
+
|
|
241
|
+
Args:
|
|
242
|
+
tflite_model_path: The path to the tflite model file.
|
|
243
|
+
model_type: The type of the tflite model.
|
|
244
|
+
additional_metadata: Additional metadata to add to the tflite model.
|
|
245
|
+
|
|
246
|
+
Returns:
|
|
247
|
+
The current LitertLmFileBuilder object.
|
|
248
|
+
|
|
249
|
+
Raises:
|
|
250
|
+
FileNotFoundError: If the tflite model file is not found.
|
|
251
|
+
ValueError: If the model type metadata is overridden.
|
|
252
|
+
"""
|
|
253
|
+
if not litertlm_core.path_exists(tflite_model_path):
|
|
254
|
+
raise FileNotFoundError(
|
|
255
|
+
f"Tflite model file not found: {tflite_model_path}"
|
|
256
|
+
)
|
|
257
|
+
metadata = [
|
|
258
|
+
Metadata(key="model_type", value=model_type.value, dtype=DType.STRING)
|
|
259
|
+
]
|
|
260
|
+
if additional_metadata:
|
|
261
|
+
for metadata_item in additional_metadata:
|
|
262
|
+
if metadata_item.key == "model_type":
|
|
263
|
+
raise ValueError("Model type metadata cannot be overridden.")
|
|
264
|
+
metadata.extend(additional_metadata)
|
|
265
|
+
|
|
266
|
+
def data_reader():
|
|
267
|
+
with litertlm_core.open_file(tflite_model_path, "rb") as f:
|
|
268
|
+
return f.read()
|
|
269
|
+
|
|
270
|
+
section_object = _SectionObject(
|
|
271
|
+
metadata=metadata,
|
|
272
|
+
data_type=schema.AnySectionDataType.TFLiteModel,
|
|
273
|
+
data_reader=data_reader,
|
|
274
|
+
)
|
|
275
|
+
self._sections.append(section_object)
|
|
276
|
+
return self
|
|
277
|
+
|
|
278
|
+
def add_sentencepiece_tokenizer(
|
|
279
|
+
self,
|
|
280
|
+
sp_tokenizer_path: str,
|
|
281
|
+
additional_metadata: Optional[list[Metadata]] = None,
|
|
282
|
+
) -> LitertLmFileBuilderT:
|
|
283
|
+
"""Adds a sentencepiece tokenizer to the litertlm file.
|
|
284
|
+
|
|
285
|
+
Args:
|
|
286
|
+
sp_tokenizer_path: The path to the sentencepiece tokenizer file.
|
|
287
|
+
additional_metadata: Additional metadata to add to the sentencepiece
|
|
288
|
+
tokenizer.
|
|
289
|
+
|
|
290
|
+
Returns:
|
|
291
|
+
The current LitertLmFileBuilder object.
|
|
292
|
+
|
|
293
|
+
Raises:
|
|
294
|
+
FileNotFoundError: If the sentencepiece tokenizer file is not found.
|
|
295
|
+
"""
|
|
296
|
+
assert not self._has_tokenizer, "Tokenizer already added."
|
|
297
|
+
self._has_tokenizer = True
|
|
298
|
+
if not litertlm_core.path_exists(sp_tokenizer_path):
|
|
299
|
+
raise FileNotFoundError(
|
|
300
|
+
f"Sentencepiece tokenizer file not found: {sp_tokenizer_path}"
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
def data_reader():
|
|
304
|
+
with litertlm_core.open_file(sp_tokenizer_path, "rb") as f:
|
|
305
|
+
return f.read()
|
|
306
|
+
|
|
307
|
+
section_object = _SectionObject(
|
|
308
|
+
metadata=additional_metadata if additional_metadata else [],
|
|
309
|
+
data_type=schema.AnySectionDataType.SP_Tokenizer,
|
|
310
|
+
data_reader=data_reader,
|
|
311
|
+
)
|
|
312
|
+
self._sections.append(section_object)
|
|
313
|
+
return self
|
|
314
|
+
|
|
315
|
+
def add_hf_tokenizer(
|
|
316
|
+
self,
|
|
317
|
+
hf_tokenizer_path: str,
|
|
318
|
+
additional_metadata: Optional[list[Metadata]] = None,
|
|
319
|
+
) -> LitertLmFileBuilderT:
|
|
320
|
+
"""Adds a hf tokenizer to the litertlm file.
|
|
321
|
+
|
|
322
|
+
Args:
|
|
323
|
+
hf_tokenizer_path: The path to the hf tokenizer `tokenizer.json` file.
|
|
324
|
+
additional_metadata: Additional metadata to add to the hf tokenizer.
|
|
325
|
+
|
|
326
|
+
Returns:
|
|
327
|
+
The current LitertLmFileBuilder object.
|
|
328
|
+
|
|
329
|
+
Raises:
|
|
330
|
+
FileNotFoundError: If the hf tokenizer file is not found.
|
|
331
|
+
"""
|
|
332
|
+
assert not self._has_tokenizer, "Tokenizer already added."
|
|
333
|
+
self._has_tokenizer = True
|
|
334
|
+
if not litertlm_core.path_exists(hf_tokenizer_path):
|
|
335
|
+
raise FileNotFoundError(
|
|
336
|
+
f"HF tokenizer file not found: {hf_tokenizer_path}"
|
|
337
|
+
)
|
|
338
|
+
|
|
339
|
+
def read_and_compress(path: str) -> bytes:
|
|
340
|
+
with litertlm_core.open_file(path, "rb") as f:
|
|
341
|
+
content = f.read()
|
|
342
|
+
uncompressed_size = len(content)
|
|
343
|
+
compressed_content = zlib.compress(content)
|
|
344
|
+
return uncompressed_size.to_bytes(8, "little") + compressed_content
|
|
345
|
+
|
|
346
|
+
section_object = _SectionObject(
|
|
347
|
+
metadata=additional_metadata if additional_metadata else [],
|
|
348
|
+
data_type=schema.AnySectionDataType.HF_Tokenizer_Zlib,
|
|
349
|
+
data_reader=lambda: read_and_compress(hf_tokenizer_path),
|
|
350
|
+
)
|
|
351
|
+
self._sections.append(section_object)
|
|
352
|
+
return self
|
|
353
|
+
|
|
354
|
+
def build(
|
|
355
|
+
self,
|
|
356
|
+
stream: BinaryIO | IO[Union[bytes, str]],
|
|
357
|
+
) -> None:
|
|
358
|
+
"""Builds the litertlm into the given stream."""
|
|
359
|
+
stream.seek(0)
|
|
360
|
+
# To simplify the build logic, we reserved the first block for the header.
|
|
361
|
+
# This translates to the first block will be padded to `BLOCK_SIZE`.
|
|
362
|
+
# TODO(b/413978412): support headers > 16KB.
|
|
363
|
+
stream.write(b"\0" * litertlm_core.BLOCK_SIZE)
|
|
364
|
+
|
|
365
|
+
# Write sections
|
|
366
|
+
offsets = []
|
|
367
|
+
for section in self._sections:
|
|
368
|
+
start_offset = stream.tell()
|
|
369
|
+
stream.write(section.data_reader())
|
|
370
|
+
end_offset = stream.tell()
|
|
371
|
+
offsets.append((start_offset, end_offset))
|
|
372
|
+
_write_padding(stream, litertlm_core.BLOCK_SIZE)
|
|
373
|
+
|
|
374
|
+
# write header
|
|
375
|
+
self._write_header(stream, offsets)
|
|
376
|
+
|
|
377
|
+
def _write_header(
|
|
378
|
+
self, stream: BinaryIO, offsets: list[tuple[int, int]]
|
|
379
|
+
) -> None:
|
|
380
|
+
"""Writes the header to the stream."""
|
|
381
|
+
assert self._system_metadata, "System metadata is empty."
|
|
382
|
+
|
|
383
|
+
stream.seek(0)
|
|
384
|
+
stream.write(b"LITERTLM")
|
|
385
|
+
stream.write(litertlm_core.LITERTLM_MAJOR_VERSION.to_bytes(4, "little"))
|
|
386
|
+
stream.write(litertlm_core.LITERTLM_MINOR_VERSION.to_bytes(4, "little"))
|
|
387
|
+
stream.write(litertlm_core.LITERTLM_PATCH_VERSION.to_bytes(4, "little"))
|
|
388
|
+
_write_padding(stream, litertlm_core.HEADER_BEGIN_BYTE_OFFSET)
|
|
389
|
+
stream.write(self._get_header_data(offsets))
|
|
390
|
+
header_end_offset = stream.tell()
|
|
391
|
+
if header_end_offset > litertlm_core.BLOCK_SIZE:
|
|
392
|
+
raise ValueError("Header size exceeds 16KB limit.")
|
|
393
|
+
stream.seek(litertlm_core.HEADER_END_LOCATION_BYTE_OFFSET)
|
|
394
|
+
stream.write(header_end_offset.to_bytes(8, "little"))
|
|
395
|
+
|
|
396
|
+
def _get_header_data(self, offsets: list[tuple[int, int]]) -> bytearray:
|
|
397
|
+
builder = flatbuffers.Builder(1024)
|
|
398
|
+
system_metadata_offset = self._write_system_metadata(builder)
|
|
399
|
+
section_metadata_offset = self._write_section_metadata(builder, offsets)
|
|
400
|
+
schema.LiteRTLMMetaDataStart(builder)
|
|
401
|
+
schema.LiteRTLMMetaDataAddSystemMetadata(builder, system_metadata_offset)
|
|
402
|
+
schema.LiteRTLMMetaDataAddSectionMetadata(builder, section_metadata_offset)
|
|
403
|
+
root = schema.LiteRTLMMetaDataEnd(builder)
|
|
404
|
+
builder.Finish(root)
|
|
405
|
+
return builder.Output()
|
|
406
|
+
|
|
407
|
+
def _write_system_metadata(self, builder: flatbuffers.Builder) -> int:
|
|
408
|
+
"""Writes the system metadata to the builder."""
|
|
409
|
+
system_metadata_offsets = [
|
|
410
|
+
_write_metadata(builder, m) for m in self._system_metadata
|
|
411
|
+
]
|
|
412
|
+
schema.SystemMetadataStartEntriesVector(
|
|
413
|
+
builder, len(system_metadata_offsets)
|
|
414
|
+
)
|
|
415
|
+
for offsets in reversed(system_metadata_offsets):
|
|
416
|
+
builder.PrependUOffsetTRelative(offsets)
|
|
417
|
+
entries_vec = builder.EndVector()
|
|
418
|
+
schema.SystemMetadataStart(builder)
|
|
419
|
+
schema.SystemMetadataAddEntries(builder, entries_vec)
|
|
420
|
+
return schema.SystemMetadataEnd(builder)
|
|
421
|
+
|
|
422
|
+
def _write_section_metadata(
|
|
423
|
+
self, builder: flatbuffers.Builder, offsets: list[tuple[int, int]]
|
|
424
|
+
) -> int:
|
|
425
|
+
"""Writes the section metadata to the builder."""
|
|
426
|
+
assert len(self._sections) == len(offsets)
|
|
427
|
+
|
|
428
|
+
section_objects_offsets = []
|
|
429
|
+
for section, offset in zip(self._sections, offsets):
|
|
430
|
+
section_objects_offsets.append(
|
|
431
|
+
_write_section_object(
|
|
432
|
+
builder, section.metadata, offset, section.data_type
|
|
433
|
+
)
|
|
434
|
+
)
|
|
435
|
+
|
|
436
|
+
schema.SectionMetadataStartObjectsVector(
|
|
437
|
+
builder, len(section_objects_offsets)
|
|
438
|
+
)
|
|
439
|
+
for obj in reversed(section_objects_offsets):
|
|
440
|
+
builder.PrependUOffsetTRelative(obj)
|
|
441
|
+
objects_vec = builder.EndVector()
|
|
442
|
+
|
|
443
|
+
schema.SectionMetadataStart(builder)
|
|
444
|
+
schema.SectionMetadataAddObjects(builder, objects_vec)
|
|
445
|
+
return schema.SectionMetadataEnd(builder)
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
def _is_binary_proto(filepath: str) -> bool:
|
|
449
|
+
"""Checks if a file is a binary protobuf or a textproto version of LlmMetadata.
|
|
450
|
+
|
|
451
|
+
Args:
|
|
452
|
+
filepath (str): The path to the file.
|
|
453
|
+
|
|
454
|
+
Returns:
|
|
455
|
+
bool: True if the file is a binary protobuf, False if it's a textproto.
|
|
456
|
+
TextProto.
|
|
457
|
+
"""
|
|
458
|
+
assert litertlm_core.path_exists(filepath), f"File {filepath} does not exist."
|
|
459
|
+
|
|
460
|
+
try:
|
|
461
|
+
with litertlm_core.open_file(filepath, "rb") as f:
|
|
462
|
+
content = f.read()
|
|
463
|
+
msg = llm_metadata_pb2.LlmMetadata()
|
|
464
|
+
msg.ParseFromString(content)
|
|
465
|
+
if msg.IsInitialized():
|
|
466
|
+
return True
|
|
467
|
+
except message.DecodeError:
|
|
468
|
+
# This is expected if the file is in text format. We'll just pass and try
|
|
469
|
+
# the next format.
|
|
470
|
+
pass
|
|
471
|
+
|
|
472
|
+
try:
|
|
473
|
+
with litertlm_core.open_file(filepath, "r") as f:
|
|
474
|
+
content = f.read()
|
|
475
|
+
msg = text_format.Parse(content, llm_metadata_pb2.LlmMetadata())
|
|
476
|
+
if msg.IsInitialized():
|
|
477
|
+
return False
|
|
478
|
+
except (text_format.ParseError, UnicodeDecodeError) as e:
|
|
479
|
+
raise ValueError(f"Failed to parse LlmMetadata from {filepath}.") from e
|
|
480
|
+
|
|
481
|
+
|
|
482
|
+
def _write_padding(stream: BinaryIO, block_size: int) -> None:
|
|
483
|
+
"""Writes zero padding to align to the next block size."""
|
|
484
|
+
current_pos = stream.tell()
|
|
485
|
+
padding_needed = (block_size - (current_pos % block_size)) % block_size
|
|
486
|
+
if padding_needed > 0:
|
|
487
|
+
stream.write(b"\0" * padding_needed)
|
|
488
|
+
|
|
489
|
+
|
|
490
|
+
def _write_metadata(builder: flatbuffers.Builder, metadata: Metadata) -> int:
|
|
491
|
+
"""Writes a FlatBuffers KeyValuePair."""
|
|
492
|
+
key_offset = builder.CreateString(metadata.key)
|
|
493
|
+
|
|
494
|
+
if metadata.dtype == DType.BOOL:
|
|
495
|
+
schema.BoolStart(builder)
|
|
496
|
+
schema.BoolAddValue(builder, metadata.value)
|
|
497
|
+
value_offset = schema.BoolEnd(builder)
|
|
498
|
+
value_type = schema.VData.Bool
|
|
499
|
+
elif metadata.dtype == DType.INT8:
|
|
500
|
+
schema.Int8Start(builder)
|
|
501
|
+
schema.Int8AddValue(builder, metadata.value)
|
|
502
|
+
value_offset = schema.Int8End(builder)
|
|
503
|
+
value_type = schema.VData.Int8
|
|
504
|
+
elif metadata.dtype == DType.INT16:
|
|
505
|
+
schema.Int16Start(builder)
|
|
506
|
+
schema.Int16AddValue(builder, metadata.value)
|
|
507
|
+
value_offset = schema.Int16End(builder)
|
|
508
|
+
value_type = schema.VData.Int16
|
|
509
|
+
elif metadata.dtype == DType.INT32:
|
|
510
|
+
schema.Int32Start(builder)
|
|
511
|
+
schema.Int32AddValue(builder, metadata.value)
|
|
512
|
+
value_offset = schema.Int32End(builder)
|
|
513
|
+
value_type = schema.VData.Int32
|
|
514
|
+
elif metadata.dtype == DType.INT64:
|
|
515
|
+
schema.Int64Start(builder)
|
|
516
|
+
schema.Int64AddValue(builder, metadata.value)
|
|
517
|
+
value_offset = schema.Int64End(builder)
|
|
518
|
+
value_type = schema.VData.Int64
|
|
519
|
+
elif metadata.dtype == DType.UINT8:
|
|
520
|
+
schema.UInt8Start(builder)
|
|
521
|
+
schema.UInt8AddValue(builder, metadata.value)
|
|
522
|
+
value_offset = schema.UInt8End(builder)
|
|
523
|
+
value_type = schema.VData.UInt8
|
|
524
|
+
elif metadata.dtype == DType.UINT16:
|
|
525
|
+
schema.UInt16Start(builder)
|
|
526
|
+
schema.UInt16AddValue(builder, metadata.value)
|
|
527
|
+
value_offset = schema.UInt16End(builder)
|
|
528
|
+
value_type = schema.VData.UInt16
|
|
529
|
+
elif metadata.dtype == DType.UINT32:
|
|
530
|
+
schema.UInt32Start(builder)
|
|
531
|
+
schema.UInt32AddValue(builder, metadata.value)
|
|
532
|
+
value_offset = schema.UInt32End(builder)
|
|
533
|
+
value_type = schema.VData.UInt32
|
|
534
|
+
elif metadata.dtype == DType.UINT64:
|
|
535
|
+
schema.UInt64Start(builder)
|
|
536
|
+
schema.UInt64AddValue(builder, metadata.value)
|
|
537
|
+
value_offset = schema.UInt64End(builder)
|
|
538
|
+
value_type = schema.VData.UInt64
|
|
539
|
+
elif metadata.dtype == DType.FLOAT32:
|
|
540
|
+
schema.Float32Start(builder)
|
|
541
|
+
schema.Float32AddValue(builder, metadata.value)
|
|
542
|
+
value_offset = schema.Float32End(builder)
|
|
543
|
+
value_type = schema.VData.Float32
|
|
544
|
+
elif metadata.dtype == DType.DOUBLE:
|
|
545
|
+
schema.DoubleStart(builder)
|
|
546
|
+
schema.DoubleAddValue(builder, metadata.value)
|
|
547
|
+
value_offset = schema.DoubleEnd(builder)
|
|
548
|
+
value_type = schema.VData.Double
|
|
549
|
+
elif metadata.dtype == DType.STRING:
|
|
550
|
+
value_offset_str = builder.CreateString(str(metadata.value))
|
|
551
|
+
schema.StringValueStart(builder)
|
|
552
|
+
schema.StringValueAddValue(builder, value_offset_str)
|
|
553
|
+
value_offset = schema.StringValueEnd(builder)
|
|
554
|
+
value_type = schema.VData.StringValue
|
|
555
|
+
else:
|
|
556
|
+
raise ValueError(f"Unsupported dtype: {metadata.dtype}")
|
|
557
|
+
|
|
558
|
+
schema.KeyValuePairStart(builder)
|
|
559
|
+
schema.KeyValuePairAddKey(builder, key_offset)
|
|
560
|
+
schema.KeyValuePairAddValueType(builder, value_type)
|
|
561
|
+
schema.KeyValuePairAddValue(builder, value_offset)
|
|
562
|
+
return schema.KeyValuePairEnd(builder)
|
|
563
|
+
|
|
564
|
+
|
|
565
|
+
def _write_section_object(
|
|
566
|
+
builder: flatbuffers.Builder,
|
|
567
|
+
section_metadata: list[Metadata],
|
|
568
|
+
section_offset: tuple[int, int],
|
|
569
|
+
section_type: schema.AnySectionDataType,
|
|
570
|
+
) -> int:
|
|
571
|
+
"""Writes a FlatBuffers SectionObject."""
|
|
572
|
+
section_metadata_offsets = [
|
|
573
|
+
_write_metadata(builder, m) for m in section_metadata
|
|
574
|
+
]
|
|
575
|
+
schema.SectionObjectStartItemsVector(builder, len(section_metadata_offsets))
|
|
576
|
+
for offsets in reversed(section_metadata_offsets):
|
|
577
|
+
builder.PrependUOffsetTRelative(offsets)
|
|
578
|
+
items_vec = builder.EndVector()
|
|
579
|
+
schema.SectionObjectStart(builder)
|
|
580
|
+
schema.SectionObjectAddItems(builder, items_vec)
|
|
581
|
+
schema.SectionObjectAddBeginOffset(builder, section_offset[0])
|
|
582
|
+
schema.SectionObjectAddEndOffset(builder, section_offset[1])
|
|
583
|
+
schema.SectionObjectAddDataType(builder, section_type)
|
|
584
|
+
return schema.SectionObjectEnd(builder)
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Copyright 2025 The ODML Authors.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
"""Core library with shared constants and utilities for LiteRT-LM tools."""
|
|
16
|
+
|
|
17
|
+
# TODO(b/445163709): Remove this module once litert_lm publishes a pypi package.
|
|
18
|
+
|
|
19
|
+
import os # pylint: disable=unused-import
|
|
20
|
+
|
|
21
|
+
from ai_edge_litert.internal import litertlm_header_schema_py_generated as schema
|
|
22
|
+
|
|
23
|
+
# --- File Format Constants ---
|
|
24
|
+
# LINT.IfChange(litertlm_version_constants) # copybara:comment
|
|
25
|
+
LITERTLM_MAJOR_VERSION = 1
|
|
26
|
+
LITERTLM_MINOR_VERSION = 5
|
|
27
|
+
LITERTLM_PATCH_VERSION = 0
|
|
28
|
+
# copybara:comment_begin(google-only)
|
|
29
|
+
# LINT.ThenChange(
|
|
30
|
+
# litert_lm/schema/core/litertlm_header.h:litertlm_version_constants,
|
|
31
|
+
# litert_lm/schema/py/litertlm_core.py:litertlm_version_constants
|
|
32
|
+
# )
|
|
33
|
+
# copybara:comment_end(google-only)
|
|
34
|
+
BLOCK_SIZE = 16 * 1024
|
|
35
|
+
HEADER_BEGIN_BYTE_OFFSET = 32
|
|
36
|
+
HEADER_END_LOCATION_BYTE_OFFSET = 24
|
|
37
|
+
|
|
38
|
+
SECTION_DATA_TYPE_TO_STRING_MAP = {
|
|
39
|
+
v: k for k, v in schema.AnySectionDataType.__dict__.items()
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def any_section_data_type_to_string(data_type: int):
|
|
44
|
+
"""Converts AnySectionDataType enum to its string representation."""
|
|
45
|
+
if data_type in SECTION_DATA_TYPE_TO_STRING_MAP:
|
|
46
|
+
return SECTION_DATA_TYPE_TO_STRING_MAP[data_type]
|
|
47
|
+
else:
|
|
48
|
+
raise ValueError(f"Unknown AnySectionDataType value: {data_type}")
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def path_exists(file_path: str) -> bool:
|
|
52
|
+
"""Checks if a file exists."""
|
|
53
|
+
return os.path.exists(file_path)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def open_file(file_path: str, mode: str = "rb"):
|
|
57
|
+
"""Opens a file using the given mode."""
|
|
58
|
+
return open(file_path, mode)
|