|
14 | 14 | #include "text_decoder_runner.h" |
15 | 15 |
|
16 | 16 | namespace executorch::extension::llm { |
| 17 | +// Supports two PTE contracts, selected per-call from the kHasPLE metadata |
| 18 | +// key (mirrors how kEnableDynamicShape etc. are read — queried on demand, |
| 19 | +// not cached in a member). Callers that need it multiple times in a hot |
| 20 | +// path should snapshot into a local. |
| 21 | +// |
| 22 | +// * Legacy (has_ple == false): |
| 23 | +// token_embedding(ids) -> inputs_embeds |
| 24 | +// text_decoder(inputs_embeds, input_pos) |
| 25 | +// |
| 26 | +// * Gemma-style PLE (has_ple == true): |
| 27 | +// token_embedding(ids) -> (inputs_embeds, ple_tok) |
| 28 | +// text_decoder(inputs_embeds, ple_tok, input_pos) |
| 29 | +// ple_tok carries Gemma4's per-layer PLE signal keyed on input_ids. It's |
| 30 | +// computed once in token_embedding and threaded through every decoder call |
| 31 | +// so PLE fires at every position (including multimodal placeholder slots). |
17 | 32 | class MultimodalDecoderRunner : public TextDecoderRunner { |
18 | 33 | public: |
19 | 34 | explicit MultimodalDecoderRunner(Module &module, IOManager *io_manager, |
20 | 35 | const GenerationConfig &config) |
21 | 36 | : TextDecoderRunner(module, io_manager, config) {} |
22 | 37 |
|
| 38 | + // True iff the loaded PTE uses the Gemma-style PLE contract above. |
| 39 | + // Reads the kHasPLE constant_method every call; cheap, but callers in |
| 40 | + // hot loops should snapshot into a local. |
| 41 | + bool has_ple() const { |
| 42 | + auto r = module_->get(kHasPLE); |
| 43 | + if (r.error() != ::executorch::runtime::Error::Ok) { |
| 44 | + return false; |
| 45 | + } |
| 46 | + return r->toScalar().to<bool>(); |
| 47 | + } |
| 48 | + |
23 | 49 | inline ::executorch::runtime::Result<::executorch::aten::Tensor> |
24 | 50 | step(TensorPtr &tokens, int64_t start_pos) override { |
25 | 51 | auto embed_result = module_->execute(kTokenEmbeddingMethod, tokens); |
26 | 52 | if (!embed_result.ok()) { |
27 | 53 | return embed_result.error(); |
28 | 54 | } |
29 | | - return decode((*embed_result)[0], start_pos); |
| 55 | + auto &embed_outputs = *embed_result; |
| 56 | + if (has_ple()) { |
| 57 | + ET_CHECK_MSG(embed_outputs.size() == 2, |
| 58 | + "Expected 2 outputs (inputs_embeds, ple_tok) from " |
| 59 | + "token_embedding, got %zu", |
| 60 | + embed_outputs.size()); |
| 61 | + return decode(embed_outputs[0], embed_outputs[1], start_pos); |
| 62 | + } |
| 63 | + return decode(embed_outputs[0], start_pos); |
30 | 64 | } |
31 | 65 |
|
32 | 66 | inline ::executorch::runtime::Result<::executorch::aten::Tensor> |
|
0 commit comments