88#include < executorch/examples/models/muse-glimmer/runtime/mlx_executor.h>
99
1010#include < executorch/backends/mlx/runtime/backend_options.h>
11- #include < executorch/examples/models/muse-glimmer/runtime/embedding_materializer.h>
1211#include < executorch/examples/models/muse-glimmer/runtime/engine/sampling.h>
12+ #include < executorch/examples/models/muse-glimmer/runtime/model_embeddings.h>
1313#include < executorch/extension/llm/batching/executor_utils.h>
1414#include < executorch/extension/llm/runner/model_metadata.h>
1515#include < executorch/extension/llm/sampler/sampler.h>
2525
2626namespace executorch ::extension::llm {
2727namespace {
28+ using muse_glimmer_abi::check_constant_method;
29+ using muse_glimmer_abi::dense_tensor;
30+ using muse_glimmer_abi::tensor_abi;
2831using runtime::Error;
2932constexpr const char * kDecoder = " forward_from_embeddings" ;
3033constexpr const char * kBackend = " MLXBackend" ;
3134
32- bool tensor_abi (
33- const runtime::TensorInfo& info,
34- aten::ScalarType dtype,
35- std::initializer_list<int64_t > shape) {
36- if (info.scalar_type () != dtype || info.sizes ().size () != shape.size () ||
37- info.dim_order ().size () != shape.size ()) {
38- return false ;
39- }
40- size_t bytes = runtime::elementSize (dtype);
41- size_t dim = 0 ;
42- for (int64_t size : shape) {
43- if (size <= 0 || size != info.sizes ()[dim] ||
44- info.dim_order ()[dim] != dim ||
45- static_cast <uint64_t >(size) >
46- std::numeric_limits<size_t >::max () / bytes) {
47- return false ;
48- }
49- bytes *= static_cast <size_t >(size);
50- ++dim;
51- }
52- return info.nbytes () == bytes;
53- }
54-
55- bool dense_tensor (const aten::Tensor& tensor) {
56- int64_t stride = 1 ;
57- for (int dim = tensor.dim () - 1 ; dim >= 0 ; --dim) {
58- if (tensor.size (dim) <= 0 ||
59- (tensor.size (dim) != 1 && tensor.strides ()[dim] != stride) ||
60- stride > std::numeric_limits<int64_t >::max () / tensor.size (dim)) {
61- return false ;
62- }
63- stride *= tensor.size (dim);
64- }
65- return tensor.device ().is_cpu () && tensor.const_data_ptr () != nullptr ;
66- }
67-
6835Error check_mlx_backends (const runtime::MethodMeta& meta) {
69- if (meta.num_backends () == 0 ) {
70- return Error::InvalidProgram;
71- }
72- for (size_t i = 0 ; i < meta.num_backends (); ++i) {
73- ET_ASSIGN_OR_RETURN (name, meta.get_backend_name (i));
74- if (std::string (name) != kBackend ) {
75- return Error::NotSupported;
76- }
77- }
78- return Error::Ok;
79- }
80-
81- // Metadata can be read during create(), but no backend may load off-engine.
82- Error check_constant_method (Module& module , const char * name) {
83- ET_ASSIGN_OR_RETURN (meta, module .method_meta (name));
84- if (meta.num_inputs () != 0 || meta.num_outputs () != 1 ||
85- meta.num_backends () != 0 || meta.num_instructions () != 0 ) {
86- ET_LOG (Error, " Muse Glimmer metadata %s must be constant" , name);
87- return Error::InvalidProgram;
88- }
89- return Error::Ok;
90- }
91-
92- runtime::Result<int64_t > check_vision_abi (
93- Module& module ,
94- int32_t hidden,
95- aten::ScalarType dtype,
96- int max_patches) {
97- ET_CHECK_OK_OR_RETURN_ERROR (
98- check_constant_method (module , " get_max_vision_patches" ));
99- ET_ASSIGN_OR_RETURN (
100- published_patches,
101- detail::read_required_positive_int (module , " get_max_vision_patches" ));
102- if (published_patches < 4 ||
103- published_patches > std::numeric_limits<int32_t >::max ()) {
104- return Error::InvalidProgram;
105- }
106- // Export bounds patches in complete 2x2 downsampling groups, but publishes
107- // the original limit (which need not be divisible by four).
108- const int64_t patches = (published_patches / 4 ) * 4 ;
109- ET_ASSIGN_OR_RETURN (meta, module .method_meta (" vision_encoder" ));
110- if (meta.num_inputs () != 9 || meta.num_outputs () != 1 ) {
111- return Error::InvalidProgram;
112- }
113- ET_CHECK_OK_OR_RETURN_ERROR (check_mlx_backends (meta));
114- // Host preprocessing's nine-input ABI; inspect it without loading vision.
115- for (size_t i = 0 ; i < 9 ; ++i) {
116- ET_ASSIGN_OR_RETURN (input, meta.input_tensor_meta (i));
117- bool valid = false ;
118- if (i == 0 ) {
119- valid = tensor_abi (input, aten::ScalarType::Float, {1 , patches, 1176 });
120- } else if (i == 1 ) {
121- // Positional interpolation is always BF16, even for an FP16 decoder.
122- valid = tensor_abi (input, aten::ScalarType::BFloat16, {1 , patches, 1536 });
123- } else if (i == 2 || i == 3 ) {
124- valid = tensor_abi (input, aten::ScalarType::Float, {patches, 48 });
125- } else if (i == 6 || i == 7 ) {
126- valid =
127- tensor_abi (input, aten::ScalarType::Bool, {1 , 1 , patches, patches});
128- } else {
129- valid = tensor_abi (input, aten::ScalarType::Long, {patches});
130- }
131- if (!valid) {
132- return Error::InvalidProgram;
133- }
134- }
135- ET_ASSIGN_OR_RETURN (output, meta.output_tensor_meta (0 ));
136- if (!tensor_abi (output, dtype, {1 , patches / 4 , hidden})) {
137- return Error::InvalidProgram;
138- }
139- return std::min<int64_t >(patches, max_patches) / 4 ;
36+ return muse_glimmer_abi::check_backends (meta, kBackend );
14037}
14138
14239uint64_t prediction_seed (uint64_t seed, batching::Position position) {
@@ -172,67 +69,6 @@ struct MuseGlimmerMLXExecutor::DFlash {
17269 }
17370};
17471
175- struct MuseGlimmerMLXExecutor ::EngineEmbeddings {
176- std::shared_ptr<const MuseGlimmerPreparationSpec> spec;
177- MuseGlimmerMaterializer materializer;
178- std::string pos_embed_path;
179- std::mutex vision_mutex;
180- std::unique_ptr<MuseGlimmerVisionRuntime> vision;
181-
182- EngineEmbeddings (
183- std::shared_ptr<const MuseGlimmerPreparationSpec> spec,
184- std::string path)
185- : spec(std::move(spec)),
186- materializer (this ->spec),
187- pos_embed_path(std::move(path)) {}
188-
189- Error initialize (Module& module ) {
190- ET_CHECK_OK_OR_RETURN_ERROR (module .load_method (" embed_text" ));
191- if (spec->has_vision ) {
192- ET_CHECK_OK_OR_RETURN_ERROR (module .load_method (" vision_encoder" ));
193- MuseGlimmerVisionRuntimeConfig config;
194- config.module = &module ;
195- config.execution_mutex = &vision_mutex;
196- config.pos_embed_path = pos_embed_path;
197- config.activation_dtype = spec->activation_dtype ;
198- config.expected_hidden_dim = spec->hidden_dim ;
199- config.max_image_tokens = spec->max_soft_tokens ;
200- config.max_encoded_bytes = spec->image_limits .max_encoded_bytes ;
201- config.max_image_dimension = spec->image_limits .max_image_dimension ;
202- config.max_image_pixels = spec->image_limits .max_image_pixels ;
203- vision = std::make_unique<MuseGlimmerVisionRuntime>(std::move (config));
204- }
205- return Error::Ok;
206- }
207-
208- runtime::Result<TensorPtr> materialize (
209- Module& module ,
210- const std::vector<batching::Input>& inputs) {
211- return materializer.materialize (
212- inputs,
213- [&](const MuseGlimmerRGBImage& image)
214- -> runtime::Result<PreparedMuseGlimmerImage> {
215- return vision->prepare_decoded_image (
216- image.rgb .data (), image.width , image.height );
217- },
218- [&](const std::vector<int64_t >& tokens) -> runtime::Result<TensorPtr> {
219- auto input = make_tensor_ptr (
220- {1 , static_cast <aten::SizesType>(tokens.size ())}, tokens);
221- ET_ASSIGN_OR_RETURN (
222- outputs,
223- module .execute (
224- " embed_text" , std::vector<runtime::EValue>{input}));
225- if (outputs.size () != 1 || !outputs[0 ].isTensor () ||
226- !dense_tensor (outputs[0 ].toTensor ())) {
227- return Error::InvalidProgram;
228- }
229- // The materializer copies these rows before another method can reuse
230- // the module's shared output arena.
231- return make_tensor_ptr (outputs[0 ].toTensor ());
232- });
233- }
234- };
235-
23672MuseGlimmerMLXExecutor::MuseGlimmerMLXExecutor (
23773 std::unique_ptr<Module> module ,
23874 std::shared_ptr<cache::Cache> cache,
@@ -243,8 +79,9 @@ MuseGlimmerMLXExecutor::MuseGlimmerMLXExecutor(
24379 module_ (std::move(module )),
24480 ctl_(cache->as<cache::BatchControl>()),
24581 spec_(std::move(spec)),
246- embeddings_(
247- std::make_unique<EngineEmbeddings>(spec_, std::move(pos_embed_path))),
82+ embeddings_(std::make_unique<MuseGlimmerModelEmbeddings>(
83+ spec_,
84+ std::move (pos_embed_path))),
24885 max_sessions_(max_sessions) {}
24986
25087MuseGlimmerMLXExecutor::~MuseGlimmerMLXExecutor () = default ;
@@ -364,7 +201,8 @@ runtime::Result<MuseGlimmerBackend> MuseGlimmerMLXExecutor::create(
364201 if (has_vision) {
365202 ET_ASSIGN_OR_RETURN (
366203 soft_tokens,
367- check_vision_abi (*module , hidden, dtype, config.max_vision_patches ));
204+ muse_glimmer_abi::check_vision_abi (
205+ *module , hidden, dtype, config.max_vision_patches , kBackend ));
368206 max_soft_tokens = soft_tokens;
369207 }
370208 const int session_capacity = config.max_session_tokens
@@ -711,7 +549,7 @@ void MuseGlimmerMLXExecutor::set_sampling(
711549
712550bool MuseGlimmerMLXExecutor::accepts (
713551 const batching::PreparedInput& input) const {
714- return embeddings_->materializer . accepts (input);
552+ return embeddings_->accepts (input);
715553}
716554
717555runtime::Result<MuseGlimmerMLXExecutor::Rewinds>
0 commit comments