Skip to content

Commit 80c7011

Browse files
authored
avoid long-form crashes in MOSS and Voxtral (#115)
* avoid long-form OOMs in MOSS and Voxtral * format
1 parent 223c9b0 commit 80c7011

10 files changed

Lines changed: 946 additions & 143 deletions

File tree

src/arch/moss/decoder.cpp

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,102 @@ PrefillBuild build_prefill_graph(ggml_context * ctx,
179179
return pb;
180180
}
181181

182+
PrefillChunkBuild build_prefill_chunk_graph(ggml_context * ctx,
183+
const MossWeights & weights,
184+
const MossHParams & hp,
185+
transcribe::causal_lm::KvCache & kv_cache,
186+
int T_chunk,
187+
int max_n_kv,
188+
bool use_flash,
189+
bool want_logits) {
190+
PrefillChunkBuild pb{};
191+
pb.T_chunk = T_chunk;
192+
pb.max_n_kv = max_n_kv;
193+
if (ctx == nullptr || T_chunk <= 0 || max_n_kv < T_chunk) {
194+
log_msg(TRANSCRIBE_LOG_LEVEL_ERROR, "moss decoder: invalid chunk (T_chunk=%d max_n_kv=%d)", T_chunk, max_n_kv);
195+
return pb;
196+
}
197+
if (kv_cache.self_k == nullptr || kv_cache.self_v == nullptr) {
198+
log_msg(TRANSCRIBE_LOG_LEVEL_ERROR, "moss decoder: kv_cache not initialized");
199+
return pb;
200+
}
201+
if (max_n_kv > kv_cache.n_ctx) {
202+
log_msg(TRANSCRIBE_LOG_LEVEL_ERROR, "moss decoder: max_n_kv=%d exceeds n_ctx=%d", max_n_kv, kv_cache.n_ctx);
203+
return pb;
204+
}
205+
206+
const int64_t hidden = hp.dec_hidden;
207+
const int64_t vocab = hp.dec_vocab_size;
208+
const int n_layer = hp.dec_n_layers;
209+
const float rms_eps = hp.dec_rms_norm_eps;
210+
const auto bp = to_block_params(hp);
211+
212+
pb.input_ids_in = ggml_new_tensor_1d(ctx, GGML_TYPE_I32, T_chunk);
213+
named(pb.input_ids_in, "dec.chunk.input_ids");
214+
ggml_set_input(pb.input_ids_in);
215+
216+
pb.audio_dense_in = ggml_new_tensor_2d(ctx, GGML_TYPE_F32, hidden, T_chunk);
217+
named(pb.audio_dense_in, "dec.chunk.audio_dense");
218+
ggml_set_input(pb.audio_dense_in);
219+
220+
pb.keep_mask_in = ggml_new_tensor_2d(ctx, GGML_TYPE_F32, 1, T_chunk);
221+
named(pb.keep_mask_in, "dec.chunk.keep_mask");
222+
ggml_set_input(pb.keep_mask_in);
223+
224+
pb.positions_in = ggml_new_tensor_1d(ctx, GGML_TYPE_I32, T_chunk);
225+
named(pb.positions_in, "dec.chunk.positions");
226+
ggml_set_input(pb.positions_in);
227+
228+
pb.kv_idx_in = ggml_new_tensor_1d(ctx, GGML_TYPE_I64, T_chunk);
229+
named(pb.kv_idx_in, "dec.chunk.kv_idx");
230+
ggml_set_input(pb.kv_idx_in);
231+
232+
pb.mask_in = ggml_new_tensor_2d(ctx, GGML_TYPE_F16, max_n_kv, T_chunk);
233+
named(pb.mask_in, "dec.chunk.attn_mask");
234+
ggml_set_input(pb.mask_in);
235+
236+
ggml_cgraph * gf = ggml_new_graph_custom(ctx, 16384, false);
237+
if (gf == nullptr) {
238+
log_msg(TRANSCRIBE_LOG_LEVEL_ERROR, "moss decoder: ggml_new_graph_custom failed");
239+
return pb;
240+
}
241+
pb.graph = gf;
242+
243+
ggml_tensor * token_emb = ggml_get_rows(ctx, weights.dec_embed.token_w, pb.input_ids_in);
244+
245+
// Same blend injection as the single-shot path, on this chunk's slice.
246+
ggml_tensor * x = ggml_add(ctx, ggml_mul(ctx, token_emb, pb.keep_mask_in), pb.audio_dense_in);
247+
248+
for (int il = 0; il < n_layer; ++il) {
249+
x = causal_lm::block_step_n(ctx, gf, x, to_block_view(weights.dec_blocks[il]), bp, kv_cache, il, T_chunk,
250+
max_n_kv, pb.mask_in, pb.positions_in, pb.kv_idx_in, use_flash);
251+
}
252+
253+
if (want_logits) {
254+
x = ggml_mul(ctx, ggml_rms_norm(ctx, x, rms_eps), weights.dec_final.norm_w);
255+
256+
// Only the chunk's last position feeds the head; the rest of the
257+
// prompt has already done its job by writing KV.
258+
ggml_tensor * last_x = ggml_view_2d(ctx, x, hidden, 1, ggml_element_size(x) * hidden,
259+
ggml_element_size(x) * hidden * static_cast<size_t>(T_chunk - 1));
260+
last_x = ggml_cont(ctx, last_x);
261+
262+
ggml_tensor * logits = ggml_mul_mat(ctx, weights.dec_embed.token_w, last_x);
263+
logits = ggml_reshape_1d(ctx, logits, vocab);
264+
named(logits, "dec.logits_raw");
265+
transcribe::debug::mark_tensor_for_dump(logits);
266+
267+
pb.out = logits;
268+
ggml_set_output(pb.out);
269+
ggml_build_forward_expand(gf, pb.out);
270+
} else {
271+
// No output tensor to pull the graph through: expand on the last
272+
// block's hidden state so every KV write in the chain is scheduled.
273+
ggml_build_forward_expand(gf, x);
274+
}
275+
return pb;
276+
}
277+
182278
StepBuild build_step_graph(ggml_context * ctx,
183279
const MossWeights & weights,
184280
const MossHParams & hp,

src/arch/moss/decoder.h

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,42 @@ PrefillBuild build_prefill_graph(ggml_context * ctx,
5252
bool use_flash,
5353
bool slice_last);
5454

55+
// ---------- Chunked prefill ----------
56+
//
57+
// One chunk of a long prompt, run against the KV already written by earlier
58+
// chunks. Used instead of build_prefill_graph when T_prompt exceeds
59+
// causal_lm::prefill_chunk_size(); a prompt that fits in one chunk still goes
60+
// through build_prefill_graph unchanged, so every existing golden dump and
61+
// tolerance keeps pinning exactly the graph it was recorded against.
62+
//
63+
// Bounds two things that a single-shot prefill does not: the flash-attention
64+
// query-row count (Metal asserts ne01 < 65536) and the causal mask, which is
65+
// [n_past + T_chunk, T_chunk] here instead of [T_prompt, T_prompt].
66+
struct PrefillChunkBuild {
67+
ggml_tensor * input_ids_in = nullptr; // [T_chunk] i32
68+
ggml_tensor * audio_dense_in = nullptr; // [hidden, T_chunk] f32
69+
ggml_tensor * keep_mask_in = nullptr; // [1, T_chunk] f32
70+
ggml_tensor * positions_in = nullptr; // [T_chunk] i32
71+
ggml_tensor * kv_idx_in = nullptr; // [T_chunk] i64
72+
ggml_tensor * mask_in = nullptr; // [max_n_kv, T_chunk] f16
73+
ggml_tensor * out = nullptr; // [vocab] last-position logits (final chunk only)
74+
ggml_cgraph * graph = nullptr;
75+
int T_chunk = 0;
76+
int max_n_kv = 0;
77+
};
78+
79+
// `want_logits` is true only for the chunk containing the final prompt
80+
// position: earlier chunks exist purely to populate the KV cache, so building
81+
// the tied lm_head for them would cost a [vocab, T_chunk] matmul for nothing.
82+
PrefillChunkBuild build_prefill_chunk_graph(ggml_context * ctx,
83+
const MossWeights & weights,
84+
const MossHParams & hp,
85+
transcribe::causal_lm::KvCache & kv_cache,
86+
int T_chunk,
87+
int max_n_kv,
88+
bool use_flash,
89+
bool want_logits);
90+
5591
struct StepBuild {
5692
ggml_tensor * input_id_in = nullptr; // [1] i32
5793
ggml_tensor * position_in = nullptr; // [1] i32

0 commit comments

Comments
 (0)