1use super::{
2 canonical_fingerprint, invalid_plan, BTreeMap, BTreeSet, Deserialize, Deserializer,
3 DynamicBackingPoolId, Serialize, VNextError,
4};
5
6pub const MAX_REUSABLE_EXECUTION_BUCKETS: usize = 64;
7pub const MAX_REUSABLE_EXECUTION_PROGRAM_SHAPES: usize = 256;
8
9#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)]
10#[serde(transparent)]
11pub struct ReusableExecutionClassId(String);
12
13impl ReusableExecutionClassId {
14 pub fn new(value: impl Into<String>) -> Result<Self, VNextError> {
15 let value = value.into();
16 if value.is_empty()
17 || value.len() > 160
18 || !value.bytes().all(|byte| {
19 byte.is_ascii_alphanumeric() || matches!(byte, b'.' | b'_' | b'-' | b':' | b'/')
20 })
21 {
22 return Err(invalid_plan(
23 "reusable execution class id is empty, too long, or non-portable",
24 ));
25 }
26 Ok(Self(value))
27 }
28
29 pub fn as_str(&self) -> &str {
30 &self.0
31 }
32}
33
34impl<'de> Deserialize<'de> for ReusableExecutionClassId {
35 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
36 where
37 D: Deserializer<'de>,
38 {
39 Self::new(String::deserialize(deserializer)?).map_err(serde::de::Error::custom)
40 }
41}
42
43#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)]
44#[serde(transparent)]
45pub struct ReusableExecutionBucketId(String);
46
47impl ReusableExecutionBucketId {
48 fn derive(
49 class_id: &ReusableExecutionClassId,
50 capacity: &ReusableExecutionCapacity,
51 ) -> Result<Self, VNextError> {
52 #[derive(Serialize)]
53 struct BucketIdentity<'a> {
54 domain: &'static str,
55 class_id: &'a ReusableExecutionClassId,
56 capacity: &'a ReusableExecutionCapacity,
57 }
58
59 Ok(Self(format!(
60 "reusable-bucket/sha256/{}",
61 canonical_fingerprint(
62 &BucketIdentity {
63 domain: "ferrum.runtime-vnext.reusable-execution-bucket.v1",
64 class_id,
65 capacity,
66 },
67 "fingerprint reusable execution bucket",
68 )?
69 )))
70 }
71
72 fn validate_for(
73 &self,
74 class_id: &ReusableExecutionClassId,
75 capacity: &ReusableExecutionCapacity,
76 ) -> Result<(), VNextError> {
77 if self != &Self::derive(class_id, capacity)? {
78 return Err(invalid_plan(
79 "reusable execution bucket id is not derived from its class and capacity",
80 ));
81 }
82 Ok(())
83 }
84
85 pub fn as_str(&self) -> &str {
86 &self.0
87 }
88}
89
90#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)]
91#[serde(deny_unknown_fields)]
92pub struct ReusableExecutionCapacity {
93 maximum_sequences: u32,
94 maximum_tokens: u64,
95 maximum_pages: u64,
96}
97
98#[derive(Deserialize)]
99#[serde(deny_unknown_fields)]
100struct ReusableExecutionCapacityWire {
101 maximum_sequences: u32,
102 maximum_tokens: u64,
103 maximum_pages: u64,
104}
105
106impl ReusableExecutionCapacity {
107 pub fn new(
108 maximum_sequences: u32,
109 maximum_tokens: u64,
110 maximum_pages: u64,
111 ) -> Result<Self, VNextError> {
112 if maximum_sequences == 0 || maximum_tokens == 0 || maximum_pages == 0 {
113 return Err(invalid_plan(
114 "reusable execution capacity dimensions must be non-zero",
115 ));
116 }
117 Ok(Self {
118 maximum_sequences,
119 maximum_tokens,
120 maximum_pages,
121 })
122 }
123
124 pub const fn maximum_sequences(self) -> u32 {
125 self.maximum_sequences
126 }
127
128 pub const fn maximum_tokens(self) -> u64 {
129 self.maximum_tokens
130 }
131
132 pub const fn maximum_pages(self) -> u64 {
133 self.maximum_pages
134 }
135
136 pub const fn covers(self, sequences: u32, tokens: u64, pages: u64) -> bool {
137 sequences > 0
138 && tokens > 0
139 && sequences <= self.maximum_sequences
140 && tokens <= self.maximum_tokens
141 && pages <= self.maximum_pages
142 }
143
144 fn strictly_extends(self, previous: Self) -> bool {
145 self.maximum_sequences >= previous.maximum_sequences
146 && self.maximum_tokens >= previous.maximum_tokens
147 && self.maximum_pages >= previous.maximum_pages
148 && self != previous
149 }
150}
151
152impl<'de> Deserialize<'de> for ReusableExecutionCapacity {
153 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
154 where
155 D: Deserializer<'de>,
156 {
157 let wire = ReusableExecutionCapacityWire::deserialize(deserializer)?;
158 Self::new(
159 wire.maximum_sequences,
160 wire.maximum_tokens,
161 wire.maximum_pages,
162 )
163 .map_err(serde::de::Error::custom)
164 }
165}
166
167#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
168#[serde(deny_unknown_fields)]
169pub struct ReusableExecutionBucketSpec {
170 bucket_id: ReusableExecutionBucketId,
171 class_id: ReusableExecutionClassId,
172 capacity: ReusableExecutionCapacity,
173}
174
175#[derive(Deserialize)]
176#[serde(deny_unknown_fields)]
177struct ReusableExecutionBucketSpecWire {
178 bucket_id: String,
179 class_id: ReusableExecutionClassId,
180 capacity: ReusableExecutionCapacity,
181}
182
183impl ReusableExecutionBucketSpec {
184 pub fn new(
185 class_id: ReusableExecutionClassId,
186 capacity: ReusableExecutionCapacity,
187 ) -> Result<Self, VNextError> {
188 let bucket_id = ReusableExecutionBucketId::derive(&class_id, &capacity)?;
189 Ok(Self {
190 bucket_id,
191 class_id,
192 capacity,
193 })
194 }
195
196 fn validate(&self) -> Result<(), VNextError> {
197 self.bucket_id.validate_for(&self.class_id, &self.capacity)
198 }
199
200 pub fn bucket_id(&self) -> &ReusableExecutionBucketId {
201 &self.bucket_id
202 }
203
204 pub fn class_id(&self) -> &ReusableExecutionClassId {
205 &self.class_id
206 }
207
208 pub const fn capacity(&self) -> ReusableExecutionCapacity {
209 self.capacity
210 }
211}
212
213impl<'de> Deserialize<'de> for ReusableExecutionBucketSpec {
214 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
215 where
216 D: Deserializer<'de>,
217 {
218 let wire = ReusableExecutionBucketSpecWire::deserialize(deserializer)?;
219 let spec = Self {
220 bucket_id: ReusableExecutionBucketId(wire.bucket_id),
221 class_id: wire.class_id,
222 capacity: wire.capacity,
223 };
224 spec.validate().map_err(serde::de::Error::custom)?;
225 Ok(spec)
226 }
227}
228
229#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)]
238#[serde(tag = "topology", rename_all = "snake_case", deny_unknown_fields)]
239pub enum ReusableExecutionProgramShape {
240 UniformDecode {
241 request_capacity: u32,
242 token_capacity: u64,
243 query_tokens_per_sequence: u32,
244 },
245 Prefill {
246 tokens_processed: u64,
247 token_capacity: u64,
248 total_prompt_tokens: u64,
249 },
250}
251
252impl ReusableExecutionProgramShape {
253 pub fn uniform_decode(
254 request_capacity: u32,
255 query_tokens_per_sequence: u32,
256 ) -> Result<Self, VNextError> {
257 let token_capacity = u64::from(request_capacity)
258 .checked_mul(u64::from(query_tokens_per_sequence))
259 .ok_or_else(|| invalid_plan("reusable decode program token capacity overflows u64"))?;
260 let shape = Self::UniformDecode {
261 request_capacity,
262 token_capacity,
263 query_tokens_per_sequence,
264 };
265 shape.validate()?;
266 Ok(shape)
267 }
268
269 pub fn prefill(
270 tokens_processed: u64,
271 token_capacity: u64,
272 total_prompt_tokens: u64,
273 ) -> Result<Self, VNextError> {
274 let shape = Self::Prefill {
275 tokens_processed,
276 token_capacity,
277 total_prompt_tokens,
278 };
279 shape.validate()?;
280 Ok(shape)
281 }
282
283 fn validate(self) -> Result<(), VNextError> {
284 match self {
285 Self::UniformDecode {
286 request_capacity,
287 token_capacity,
288 query_tokens_per_sequence,
289 } => {
290 let expected_tokens =
291 u64::from(request_capacity).checked_mul(u64::from(query_tokens_per_sequence));
292 if request_capacity == 0
293 || query_tokens_per_sequence == 0
294 || expected_tokens != Some(token_capacity)
295 {
296 return Err(invalid_plan(
297 "reusable decode program shape is empty or internally inconsistent",
298 ));
299 }
300 }
301 Self::Prefill {
302 tokens_processed,
303 token_capacity,
304 total_prompt_tokens,
305 } => {
306 if token_capacity == 0
307 || total_prompt_tokens == 0
308 || tokens_processed
309 .checked_add(token_capacity)
310 .is_none_or(|end| end > total_prompt_tokens)
311 {
312 return Err(invalid_plan(
313 "reusable prefill program shape is empty or exceeds its prompt frontier",
314 ));
315 }
316 }
317 }
318 Ok(())
319 }
320
321 pub const fn request_capacity(self) -> u32 {
322 match self {
323 Self::UniformDecode {
324 request_capacity, ..
325 } => request_capacity,
326 Self::Prefill { .. } => 1,
327 }
328 }
329
330 pub const fn token_capacity(self) -> u64 {
331 match self {
332 Self::UniformDecode { token_capacity, .. } | Self::Prefill { token_capacity, .. } => {
333 token_capacity
334 }
335 }
336 }
337}
338
339impl<'de> Deserialize<'de> for ReusableExecutionProgramShape {
340 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
341 where
342 D: Deserializer<'de>,
343 {
344 #[derive(Deserialize)]
345 #[serde(tag = "topology", rename_all = "snake_case", deny_unknown_fields)]
346 enum Wire {
347 UniformDecode {
348 request_capacity: u32,
349 token_capacity: u64,
350 query_tokens_per_sequence: u32,
351 },
352 Prefill {
353 tokens_processed: u64,
354 token_capacity: u64,
355 total_prompt_tokens: u64,
356 },
357 }
358
359 let shape = match Wire::deserialize(deserializer)? {
360 Wire::UniformDecode {
361 request_capacity,
362 token_capacity,
363 query_tokens_per_sequence,
364 } => Self::UniformDecode {
365 request_capacity,
366 token_capacity,
367 query_tokens_per_sequence,
368 },
369 Wire::Prefill {
370 tokens_processed,
371 token_capacity,
372 total_prompt_tokens,
373 } => Self::Prefill {
374 tokens_processed,
375 token_capacity,
376 total_prompt_tokens,
377 },
378 };
379 shape.validate().map_err(serde::de::Error::custom)?;
380 Ok(shape)
381 }
382}
383
384#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
393#[serde(deny_unknown_fields)]
394pub struct ReusableExecutionProgramSpec {
395 class_id: ReusableExecutionClassId,
396 shape: ReusableExecutionProgramShape,
397}
398
399impl ReusableExecutionProgramSpec {
400 pub fn new(
401 class_id: ReusableExecutionClassId,
402 shape: ReusableExecutionProgramShape,
403 ) -> Result<Self, VNextError> {
404 shape.validate()?;
405 Ok(Self { class_id, shape })
406 }
407
408 pub fn class_id(&self) -> &ReusableExecutionClassId {
409 &self.class_id
410 }
411
412 pub const fn shape(&self) -> ReusableExecutionProgramShape {
413 self.shape
414 }
415}
416
417#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
418#[serde(rename_all = "snake_case")]
419pub enum ReusableExecutionProgramShapeSemantics {
420 Exact,
421}
422
423#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
424#[serde(rename_all = "snake_case")]
425pub enum ReusableExecutionCatalogMissPolicy {
426 EagerFallback,
427}
428
429#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
430#[serde(rename_all = "snake_case")]
431pub enum ReusableExecutionCatalogLifetime {
432 StartupSealed,
433 OnDemandBounded,
436}
437
438impl ReusableExecutionCatalogLifetime {
439 pub const fn is_startup_sealed(&self) -> bool {
440 matches!(self, Self::StartupSealed)
441 }
442}
443
444#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
452#[serde(deny_unknown_fields)]
453pub struct ReusableExecutionProgramPolicy {
454 shape_semantics: ReusableExecutionProgramShapeSemantics,
455 catalog_miss_policy: ReusableExecutionCatalogMissPolicy,
456 catalog_lifetime: ReusableExecutionCatalogLifetime,
457 warmup_passes: u32,
458 capture_passes: u32,
459 replay_validation_passes: u32,
460 programs: Vec<ReusableExecutionProgramSpec>,
461}
462
463#[derive(Deserialize)]
464#[serde(deny_unknown_fields)]
465struct ReusableExecutionProgramPolicyWire {
466 shape_semantics: ReusableExecutionProgramShapeSemantics,
467 catalog_miss_policy: ReusableExecutionCatalogMissPolicy,
468 catalog_lifetime: ReusableExecutionCatalogLifetime,
469 warmup_passes: u32,
470 capture_passes: u32,
471 replay_validation_passes: u32,
472 programs: Vec<ReusableExecutionProgramSpec>,
473}
474
475impl ReusableExecutionProgramPolicy {
476 pub fn exact_on_demand(
480 programs: Vec<ReusableExecutionProgramSpec>,
481 ) -> Result<Self, VNextError> {
482 let mut policy = Self::exact_startup_sealed(1, 1, 1, programs)?;
483 policy.catalog_lifetime = ReusableExecutionCatalogLifetime::OnDemandBounded;
484 policy.replay_validation_passes = 0;
485 policy.validate()?;
486 Ok(policy)
487 }
488
489 pub fn exact_startup_sealed(
490 warmup_passes: u32,
491 capture_passes: u32,
492 replay_validation_passes: u32,
493 mut programs: Vec<ReusableExecutionProgramSpec>,
494 ) -> Result<Self, VNextError> {
495 let prerequisite_prefill_programs = programs
496 .iter()
497 .filter_map(|program| match program.shape() {
498 ReusableExecutionProgramShape::Prefill {
499 tokens_processed,
500 total_prompt_tokens,
501 ..
502 } if tokens_processed > 0 => Some(ReusableExecutionProgramSpec {
503 class_id: program.class_id().clone(),
504 shape: ReusableExecutionProgramShape::Prefill {
505 tokens_processed: 0,
506 token_capacity: tokens_processed,
507 total_prompt_tokens,
508 },
509 }),
510 _ => None,
511 })
512 .collect::<Vec<_>>();
513 programs.extend(prerequisite_prefill_programs);
514 programs.sort_unstable();
515 programs.dedup();
516 let policy = Self {
517 shape_semantics: ReusableExecutionProgramShapeSemantics::Exact,
518 catalog_miss_policy: ReusableExecutionCatalogMissPolicy::EagerFallback,
519 catalog_lifetime: ReusableExecutionCatalogLifetime::StartupSealed,
520 warmup_passes,
521 capture_passes,
522 replay_validation_passes,
523 programs,
524 };
525 policy.validate()?;
526 Ok(policy)
527 }
528
529 fn validate(&self) -> Result<(), VNextError> {
530 if self.shape_semantics != ReusableExecutionProgramShapeSemantics::Exact
531 || self.catalog_miss_policy != ReusableExecutionCatalogMissPolicy::EagerFallback
532 || self.warmup_passes == 0
533 || self.capture_passes == 0
534 || match self.catalog_lifetime {
535 ReusableExecutionCatalogLifetime::StartupSealed => {
536 self.replay_validation_passes == 0
537 }
538 ReusableExecutionCatalogLifetime::OnDemandBounded => {
539 self.warmup_passes != 1
540 || self.capture_passes != 1
541 || self.replay_validation_passes != 0
542 }
543 }
544 || self.programs.is_empty()
545 || self.programs.len() > MAX_REUSABLE_EXECUTION_PROGRAM_SHAPES
546 {
547 return Err(invalid_plan(
548 "reusable execution program policy has invalid semantics, passes, or shape count",
549 ));
550 }
551 if self.programs.windows(2).any(|pair| pair[0] >= pair[1]) {
552 return Err(invalid_plan(
553 "reusable execution program shapes are duplicate or non-canonical",
554 ));
555 }
556 for program in &self.programs {
557 program.shape().validate()?;
558 }
559 let mut shape_classes = BTreeMap::new();
560 for program in &self.programs {
561 if shape_classes
562 .insert(program.shape(), program.class_id())
563 .is_some()
564 {
565 return Err(invalid_plan(
566 "reusable execution logical shape is assigned to multiple workspace classes",
567 ));
568 }
569 }
570 if self.programs.iter().any(|program| match program.shape() {
571 ReusableExecutionProgramShape::Prefill {
572 tokens_processed,
573 total_prompt_tokens,
574 ..
575 } if tokens_processed > 0 => !self.programs.contains(&ReusableExecutionProgramSpec {
576 class_id: program.class_id().clone(),
577 shape: ReusableExecutionProgramShape::Prefill {
578 tokens_processed: 0,
579 token_capacity: tokens_processed,
580 total_prompt_tokens,
581 },
582 }),
583 _ => false,
584 }) {
585 return Err(invalid_plan(
586 "reusable prefill program policy omits a prerequisite prefix shape",
587 ));
588 }
589 Ok(())
590 }
591
592 pub const fn shape_semantics(&self) -> ReusableExecutionProgramShapeSemantics {
593 self.shape_semantics
594 }
595
596 pub const fn catalog_miss_policy(&self) -> ReusableExecutionCatalogMissPolicy {
597 self.catalog_miss_policy
598 }
599
600 pub const fn catalog_lifetime(&self) -> ReusableExecutionCatalogLifetime {
601 self.catalog_lifetime
602 }
603
604 pub const fn warmup_passes(&self) -> u32 {
605 self.warmup_passes
606 }
607
608 pub const fn capture_passes(&self) -> u32 {
609 self.capture_passes
610 }
611
612 pub const fn replay_validation_passes(&self) -> u32 {
613 self.replay_validation_passes
614 }
615
616 pub fn programs(&self) -> &[ReusableExecutionProgramSpec] {
617 &self.programs
618 }
619}
620
621impl<'de> Deserialize<'de> for ReusableExecutionProgramPolicy {
622 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
623 where
624 D: Deserializer<'de>,
625 {
626 let wire = ReusableExecutionProgramPolicyWire::deserialize(deserializer)?;
627 let policy = Self {
628 shape_semantics: wire.shape_semantics,
629 catalog_miss_policy: wire.catalog_miss_policy,
630 catalog_lifetime: wire.catalog_lifetime,
631 warmup_passes: wire.warmup_passes,
632 capture_passes: wire.capture_passes,
633 replay_validation_passes: wire.replay_validation_passes,
634 programs: wire.programs,
635 };
636 policy.validate().map_err(serde::de::Error::custom)?;
637 Ok(policy)
638 }
639}
640
641#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
642#[serde(deny_unknown_fields)]
643pub struct ReusableExecutionPolicy {
644 maximum_reusable_lanes: u32,
645 buckets: Vec<ReusableExecutionBucketSpec>,
646 #[serde(default, skip_serializing_if = "Option::is_none")]
647 program_policy: Option<ReusableExecutionProgramPolicy>,
648}
649
650#[derive(Deserialize)]
651#[serde(deny_unknown_fields)]
652struct ReusableExecutionPolicyWire {
653 maximum_reusable_lanes: u32,
654 buckets: Vec<ReusableExecutionBucketSpec>,
655 #[serde(default)]
656 program_policy: Option<ReusableExecutionProgramPolicy>,
657}
658
659impl ReusableExecutionPolicy {
660 pub fn new(
661 maximum_reusable_lanes: u32,
662 mut buckets: Vec<ReusableExecutionBucketSpec>,
663 ) -> Result<Self, VNextError> {
664 buckets.sort_by(|left, right| {
665 (left.class_id(), left.capacity()).cmp(&(right.class_id(), right.capacity()))
666 });
667 let policy = Self {
668 maximum_reusable_lanes,
669 buckets,
670 program_policy: None,
671 };
672 policy.validate()?;
673 Ok(policy)
674 }
675
676 pub fn with_program_policy(
677 mut self,
678 program_policy: ReusableExecutionProgramPolicy,
679 ) -> Result<Self, VNextError> {
680 self.program_policy = Some(program_policy);
681 self.validate()?;
682 Ok(self)
683 }
684
685 pub(crate) fn validate(&self) -> Result<(), VNextError> {
686 if self.maximum_reusable_lanes == 0
687 || self.buckets.is_empty()
688 || self.buckets.len() > MAX_REUSABLE_EXECUTION_BUCKETS
689 {
690 return Err(invalid_plan(
691 "reusable execution policy has an invalid lane or bucket count",
692 ));
693 }
694 for bucket in &self.buckets {
695 bucket.validate()?;
696 }
697 if let Some(program_policy) = &self.program_policy {
698 program_policy.validate()?;
699 if program_policy.programs().iter().any(|program| {
700 let shape = program.shape();
701 self.smallest_covering_bucket(
702 program.class_id(),
703 shape.request_capacity(),
704 shape.token_capacity(),
705 0,
706 )
707 .is_none()
708 }) {
709 return Err(invalid_plan(
710 "reusable execution program shape has no covering workspace bucket in its class",
711 ));
712 }
713 }
714 if self.buckets.windows(2).any(|pair| {
715 let left = &pair[0];
716 let right = &pair[1];
717 left.class_id() > right.class_id()
718 || (left.class_id() == right.class_id()
719 && !right.capacity().strictly_extends(left.capacity()))
720 }) {
721 return Err(invalid_plan(
722 "reusable execution buckets are not canonical monotonic class chains",
723 ));
724 }
725 Ok(())
726 }
727
728 pub const fn maximum_reusable_lanes(&self) -> u32 {
729 self.maximum_reusable_lanes
730 }
731
732 pub fn buckets(&self) -> &[ReusableExecutionBucketSpec] {
733 &self.buckets
734 }
735
736 pub fn program_policy(&self) -> Option<&ReusableExecutionProgramPolicy> {
737 self.program_policy.as_ref()
738 }
739
740 pub fn startup_capture_case_count(&self) -> usize {
741 self.program_policy
742 .as_ref()
743 .map_or(self.buckets.len(), |policy| policy.programs().len())
744 }
745
746 pub fn bucket(
747 &self,
748 bucket_id: &ReusableExecutionBucketId,
749 ) -> Option<&ReusableExecutionBucketSpec> {
750 self.buckets
751 .iter()
752 .find(|bucket| bucket.bucket_id() == bucket_id)
753 }
754
755 pub fn smallest_covering_bucket(
756 &self,
757 class_id: &ReusableExecutionClassId,
758 sequences: u32,
759 tokens: u64,
760 pages: u64,
761 ) -> Option<&ReusableExecutionBucketSpec> {
762 self.buckets.iter().find(|bucket| {
763 bucket.class_id() == class_id && bucket.capacity().covers(sequences, tokens, pages)
764 })
765 }
766}
767
768impl<'de> Deserialize<'de> for ReusableExecutionPolicy {
769 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
770 where
771 D: Deserializer<'de>,
772 {
773 let wire = ReusableExecutionPolicyWire::deserialize(deserializer)?;
774 let policy = Self {
775 maximum_reusable_lanes: wire.maximum_reusable_lanes,
776 buckets: wire.buckets,
777 program_policy: wire.program_policy,
778 };
779 policy.validate().map_err(serde::de::Error::custom)?;
780 Ok(policy)
781 }
782}
783
784#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
785#[serde(deny_unknown_fields)]
786pub struct ReusablePoolWorkspaceBudget {
787 pool_id: DynamicBackingPoolId,
788 step_bytes: u64,
789 invocation_bytes: u64,
790}
791
792#[derive(Deserialize)]
793#[serde(deny_unknown_fields)]
794struct ReusablePoolWorkspaceBudgetWire {
795 pool_id: DynamicBackingPoolId,
796 step_bytes: u64,
797 invocation_bytes: u64,
798}
799
800impl ReusablePoolWorkspaceBudget {
801 pub(crate) fn new(
802 pool_id: DynamicBackingPoolId,
803 step_bytes: u64,
804 invocation_bytes: u64,
805 ) -> Result<Self, VNextError> {
806 let budget = Self {
807 pool_id,
808 step_bytes,
809 invocation_bytes,
810 };
811 budget.validate()?;
812 Ok(budget)
813 }
814
815 fn validate(&self) -> Result<(), VNextError> {
816 self.step_bytes
817 .checked_add(self.invocation_bytes)
818 .filter(|total| *total > 0)
819 .ok_or_else(|| {
820 invalid_plan("reusable pool workspace budget is empty or overflows u64")
821 })?;
822 Ok(())
823 }
824
825 pub fn pool_id(&self) -> &DynamicBackingPoolId {
826 &self.pool_id
827 }
828
829 pub const fn step_bytes(&self) -> u64 {
830 self.step_bytes
831 }
832
833 pub const fn invocation_bytes(&self) -> u64 {
834 self.invocation_bytes
835 }
836
837 pub fn total_bytes(&self) -> Result<u64, VNextError> {
838 self.step_bytes
839 .checked_add(self.invocation_bytes)
840 .ok_or_else(|| invalid_plan("reusable pool workspace budget overflows u64"))
841 }
842}
843
844impl<'de> Deserialize<'de> for ReusablePoolWorkspaceBudget {
845 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
846 where
847 D: Deserializer<'de>,
848 {
849 let wire = ReusablePoolWorkspaceBudgetWire::deserialize(deserializer)?;
850 Self::new(wire.pool_id, wire.step_bytes, wire.invocation_bytes)
851 .map_err(serde::de::Error::custom)
852 }
853}
854
855#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
856#[serde(deny_unknown_fields)]
857pub struct ResolvedReusableExecutionBucket {
858 bucket: ReusableExecutionBucketSpec,
859 pool_budgets: Vec<ReusablePoolWorkspaceBudget>,
860}
861
862#[derive(Deserialize)]
863#[serde(deny_unknown_fields)]
864struct ResolvedReusableExecutionBucketWire {
865 bucket: ReusableExecutionBucketSpec,
866 pool_budgets: Vec<ReusablePoolWorkspaceBudget>,
867}
868
869impl ResolvedReusableExecutionBucket {
870 pub(crate) fn new(
871 bucket: ReusableExecutionBucketSpec,
872 pool_budgets: Vec<ReusablePoolWorkspaceBudget>,
873 ) -> Result<Self, VNextError> {
874 let resolved = Self {
875 bucket,
876 pool_budgets,
877 };
878 resolved.validate()?;
879 Ok(resolved)
880 }
881
882 fn validate(&self) -> Result<(), VNextError> {
883 self.bucket.validate()?;
884 if self
885 .pool_budgets
886 .windows(2)
887 .any(|pair| pair[0].pool_id() >= pair[1].pool_id())
888 {
889 return Err(invalid_plan(
890 "resolved reusable bucket pool budgets are duplicate or non-canonical",
891 ));
892 }
893 for budget in &self.pool_budgets {
894 budget.validate()?;
895 }
896 Ok(())
897 }
898
899 pub fn bucket(&self) -> &ReusableExecutionBucketSpec {
900 &self.bucket
901 }
902
903 pub fn pool_budgets(&self) -> &[ReusablePoolWorkspaceBudget] {
904 &self.pool_budgets
905 }
906}
907
908impl<'de> Deserialize<'de> for ResolvedReusableExecutionBucket {
909 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
910 where
911 D: Deserializer<'de>,
912 {
913 let wire = ResolvedReusableExecutionBucketWire::deserialize(deserializer)?;
914 Self::new(wire.bucket, wire.pool_budgets).map_err(serde::de::Error::custom)
915 }
916}
917
918#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
919#[serde(deny_unknown_fields)]
920pub struct ReusableExecutionMemoryPlan {
921 maximum_reusable_lanes: u32,
922 maximum_device_executables: u64,
923 buckets: Vec<ResolvedReusableExecutionBucket>,
924 #[serde(default, skip_serializing_if = "Option::is_none")]
925 program_policy: Option<ReusableExecutionProgramPolicy>,
926}
927
928#[derive(Deserialize)]
929#[serde(deny_unknown_fields)]
930struct ReusableExecutionMemoryPlanWire {
931 maximum_reusable_lanes: u32,
932 maximum_device_executables: u64,
933 buckets: Vec<ResolvedReusableExecutionBucket>,
934 #[serde(default)]
935 program_policy: Option<ReusableExecutionProgramPolicy>,
936}
937
938impl ReusableExecutionMemoryPlan {
939 pub(crate) fn new(
940 maximum_reusable_lanes: u32,
941 maximum_device_executables: u64,
942 buckets: Vec<ResolvedReusableExecutionBucket>,
943 ) -> Result<Self, VNextError> {
944 Self::new_with_program_policy(
945 maximum_reusable_lanes,
946 maximum_device_executables,
947 buckets,
948 None,
949 )
950 }
951
952 pub(crate) fn new_with_program_policy(
953 maximum_reusable_lanes: u32,
954 maximum_device_executables: u64,
955 buckets: Vec<ResolvedReusableExecutionBucket>,
956 program_policy: Option<ReusableExecutionProgramPolicy>,
957 ) -> Result<Self, VNextError> {
958 let plan = Self {
959 maximum_reusable_lanes,
960 maximum_device_executables,
961 buckets,
962 program_policy,
963 };
964 plan.validate_local()?;
965 Ok(plan)
966 }
967
968 pub(crate) fn validate_local(&self) -> Result<(), VNextError> {
969 if self.maximum_reusable_lanes == 0
970 || self.maximum_device_executables == 0
971 || self.buckets.is_empty()
972 || self.buckets.len() > MAX_REUSABLE_EXECUTION_BUCKETS
973 {
974 return Err(invalid_plan(
975 "reusable execution memory plan has an invalid lane, executable, or bucket count",
976 ));
977 }
978 for bucket in &self.buckets {
979 bucket.validate()?;
980 }
981 let mut canonical = ReusableExecutionPolicy::new(
982 self.maximum_reusable_lanes,
983 self.buckets
984 .iter()
985 .map(|bucket| bucket.bucket().clone())
986 .collect(),
987 )?;
988 if let Some(program_policy) = self.program_policy.clone() {
989 canonical = canonical.with_program_policy(program_policy)?;
990 }
991 if canonical.buckets().iter().ne(self
992 .buckets
993 .iter()
994 .map(ResolvedReusableExecutionBucket::bucket))
995 {
996 return Err(invalid_plan(
997 "reusable execution memory buckets are non-canonical",
998 ));
999 }
1000 Ok(())
1001 }
1002
1003 pub const fn maximum_reusable_lanes(&self) -> u32 {
1004 self.maximum_reusable_lanes
1005 }
1006
1007 pub const fn maximum_device_executables(&self) -> u64 {
1008 self.maximum_device_executables
1009 }
1010
1011 pub fn buckets(&self) -> &[ResolvedReusableExecutionBucket] {
1012 &self.buckets
1013 }
1014
1015 pub fn program_policy(&self) -> Option<&ReusableExecutionProgramPolicy> {
1016 self.program_policy.as_ref()
1017 }
1018
1019 pub fn bucket(
1020 &self,
1021 bucket_id: &ReusableExecutionBucketId,
1022 ) -> Option<&ResolvedReusableExecutionBucket> {
1023 self.buckets
1024 .iter()
1025 .find(|bucket| bucket.bucket().bucket_id() == bucket_id)
1026 }
1027
1028 pub fn smallest_covering_bucket(
1029 &self,
1030 class_id: &ReusableExecutionClassId,
1031 sequences: u32,
1032 tokens: u64,
1033 pages: u64,
1034 ) -> Option<&ResolvedReusableExecutionBucket> {
1035 self.buckets.iter().find(|bucket| {
1036 bucket.bucket().class_id() == class_id
1037 && bucket.bucket().capacity().covers(sequences, tokens, pages)
1038 })
1039 }
1040
1041 pub(crate) fn policy(&self) -> Result<ReusableExecutionPolicy, VNextError> {
1042 let mut policy = ReusableExecutionPolicy::new(
1043 self.maximum_reusable_lanes,
1044 self.buckets
1045 .iter()
1046 .map(|bucket| bucket.bucket().clone())
1047 .collect(),
1048 )?;
1049 if let Some(program_policy) = self.program_policy.clone() {
1050 policy = policy.with_program_policy(program_policy)?;
1051 }
1052 Ok(policy)
1053 }
1054
1055 pub(crate) fn pool_workspace_ceilings(
1056 &self,
1057 ) -> Result<BTreeMap<DynamicBackingPoolId, u64>, VNextError> {
1058 let lanes = u64::from(self.maximum_reusable_lanes);
1059 let mut totals = BTreeMap::new();
1060 for bucket in &self.buckets {
1061 for budget in bucket.pool_budgets() {
1062 let bytes = budget
1063 .total_bytes()?
1064 .checked_mul(lanes)
1065 .ok_or_else(|| invalid_plan("reusable lane workspace budget overflows u64"))?;
1066 let total = totals.entry(budget.pool_id().clone()).or_insert(0_u64);
1067 *total = total
1068 .checked_add(bytes)
1069 .ok_or_else(|| invalid_plan("reusable pool workspace ceiling overflows u64"))?;
1070 }
1071 }
1072 Ok(totals)
1073 }
1074
1075 pub(crate) fn startup_sealed_pool_workspace_bytes(
1079 &self,
1080 ) -> Result<BTreeMap<DynamicBackingPoolId, u64>, VNextError> {
1081 let Some(policy) = self
1082 .program_policy()
1083 .filter(|policy| policy.catalog_lifetime().is_startup_sealed())
1084 else {
1085 return Ok(BTreeMap::new());
1086 };
1087 let mut captured = BTreeSet::new();
1088 let mut totals = BTreeMap::new();
1089 for program in policy.programs() {
1090 let shape = program.shape();
1091 let bucket = self
1092 .smallest_covering_bucket(
1093 program.class_id(),
1094 shape.request_capacity(),
1095 shape.token_capacity(),
1096 0,
1097 )
1098 .ok_or_else(|| invalid_plan("startup capture program has no workspace bucket"))?;
1099 if !captured.insert(bucket.bucket().bucket_id()) {
1100 continue;
1101 }
1102 for budget in bucket.pool_budgets() {
1103 let bytes = budget
1104 .total_bytes()?
1105 .checked_mul(u64::from(self.maximum_reusable_lanes))
1106 .ok_or_else(|| invalid_plan("sealed reusable lane workspace overflows u64"))?;
1107 let total = totals.entry(budget.pool_id().clone()).or_insert(0_u64);
1108 *total = total
1109 .checked_add(bytes)
1110 .ok_or_else(|| invalid_plan("sealed reusable pool workspace overflows u64"))?;
1111 }
1112 }
1113 Ok(totals)
1114 }
1115}
1116
1117impl<'de> Deserialize<'de> for ReusableExecutionMemoryPlan {
1118 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1119 where
1120 D: Deserializer<'de>,
1121 {
1122 let wire = ReusableExecutionMemoryPlanWire::deserialize(deserializer)?;
1123 Self::new_with_program_policy(
1124 wire.maximum_reusable_lanes,
1125 wire.maximum_device_executables,
1126 wire.buckets,
1127 wire.program_policy,
1128 )
1129 .map_err(serde::de::Error::custom)
1130 }
1131}
1132
1133#[cfg(test)]
1134mod tests {
1135 use super::{
1136 ReusableExecutionBucketSpec, ReusableExecutionCapacity, ReusableExecutionClassId,
1137 ReusableExecutionPolicy, ReusableExecutionProgramPolicy, ReusableExecutionProgramShape,
1138 ReusableExecutionProgramSpec,
1139 };
1140
1141 fn bucket(class: &str, sequences: u32, tokens: u64) -> ReusableExecutionBucketSpec {
1142 ReusableExecutionBucketSpec::new(
1143 ReusableExecutionClassId::new(class).unwrap(),
1144 ReusableExecutionCapacity::new(sequences, tokens, 1).unwrap(),
1145 )
1146 .unwrap()
1147 }
1148
1149 fn program(class: &str, shape: ReusableExecutionProgramShape) -> ReusableExecutionProgramSpec {
1150 ReusableExecutionProgramSpec::new(ReusableExecutionClassId::new(class).unwrap(), shape)
1151 .unwrap()
1152 }
1153
1154 #[test]
1155 fn reusable_policy_canonicalizes_and_selects_within_one_class() {
1156 let policy = ReusableExecutionPolicy::new(
1157 1,
1158 vec![
1159 bucket("uniform-query", 4, 4),
1160 bucket("packed-prefill", 1, 64),
1161 bucket("uniform-query", 1, 1),
1162 bucket("uniform-query", 2, 2),
1163 ],
1164 )
1165 .unwrap();
1166
1167 let class = ReusableExecutionClassId::new("uniform-query").unwrap();
1168 assert_eq!(
1169 policy
1170 .smallest_covering_bucket(&class, 3, 3, 0)
1171 .unwrap()
1172 .capacity(),
1173 ReusableExecutionCapacity::new(4, 4, 1).unwrap()
1174 );
1175 assert!(policy.smallest_covering_bucket(&class, 5, 5, 0).is_none());
1176 }
1177
1178 #[test]
1179 fn reusable_policy_wire_rejects_derived_id_tampering() {
1180 let policy =
1181 ReusableExecutionPolicy::new(1, vec![bucket("packed-prefill", 1, 64)]).unwrap();
1182 let mut value = serde_json::to_value(&policy).unwrap();
1183 value["buckets"][0]["bucket_id"] = serde_json::Value::String("forged".to_owned());
1184 assert!(serde_json::from_value::<ReusableExecutionPolicy>(value).is_err());
1185 }
1186
1187 #[test]
1188 fn reusable_policy_rejects_incomparable_capacities_within_class() {
1189 let first = bucket("mixed", 1, 64);
1190 let second = bucket("mixed", 4, 4);
1191 assert!(ReusableExecutionPolicy::new(1, vec![first, second]).is_err());
1192 }
1193
1194 #[test]
1195 fn exact_program_policy_is_canonical_and_adds_prefill_prerequisite() {
1196 let decode_4 = program(
1197 "uniform-query",
1198 ReusableExecutionProgramShape::uniform_decode(4, 1).unwrap(),
1199 );
1200 let decode_1 = program(
1201 "uniform-query",
1202 ReusableExecutionProgramShape::uniform_decode(1, 1).unwrap(),
1203 );
1204 let final_prefill = program(
1205 "packed-prefill",
1206 ReusableExecutionProgramShape::prefill(4, 4, 8).unwrap(),
1207 );
1208 let prerequisite = program(
1209 "packed-prefill",
1210 ReusableExecutionProgramShape::prefill(0, 4, 8).unwrap(),
1211 );
1212
1213 let program_policy = ReusableExecutionProgramPolicy::exact_startup_sealed(
1214 1,
1215 1,
1216 1,
1217 vec![
1218 decode_4.clone(),
1219 final_prefill.clone(),
1220 decode_1.clone(),
1221 decode_4.clone(),
1222 ],
1223 )
1224 .unwrap();
1225
1226 assert_eq!(
1227 program_policy.programs(),
1228 &[prerequisite, final_prefill, decode_1, decode_4]
1229 );
1230 }
1231
1232 #[test]
1233 fn on_demand_policy_preserves_exact_shapes_without_replaying_user_transitions() {
1234 let shapes = vec![program(
1235 "uniform-query",
1236 ReusableExecutionProgramShape::uniform_decode(3, 1).unwrap(),
1237 )];
1238 let startup =
1239 ReusableExecutionProgramPolicy::exact_startup_sealed(1, 1, 1, shapes.clone()).unwrap();
1240 let demand = ReusableExecutionProgramPolicy::exact_on_demand(shapes).unwrap();
1241 assert_eq!(startup.programs(), demand.programs());
1242 let value = serde_json::to_value(&demand).unwrap();
1243 assert_ne!(serde_json::to_value(&startup).unwrap(), value);
1244 assert_eq!(
1245 serde_json::from_value::<ReusableExecutionProgramPolicy>(value.clone()).unwrap(),
1246 demand
1247 );
1248 for field in [
1249 "warmup_passes",
1250 "capture_passes",
1251 "replay_validation_passes",
1252 ] {
1253 let mut invalid = value.clone();
1254 invalid[field] = serde_json::json!(2);
1255 assert!(serde_json::from_value::<ReusableExecutionProgramPolicy>(invalid).is_err());
1256 }
1257 }
1258
1259 #[test]
1260 fn program_policy_wire_rejects_missing_prefill_prerequisite() {
1261 let program_policy = ReusableExecutionProgramPolicy::exact_startup_sealed(
1262 1,
1263 1,
1264 1,
1265 vec![program(
1266 "packed-prefill",
1267 ReusableExecutionProgramShape::prefill(4, 4, 8).unwrap(),
1268 )],
1269 )
1270 .unwrap();
1271 let mut value = serde_json::to_value(program_policy).unwrap();
1272 value["programs"].as_array_mut().unwrap().remove(0);
1273
1274 assert!(serde_json::from_value::<ReusableExecutionProgramPolicy>(value).is_err());
1275 }
1276
1277 #[test]
1278 fn legacy_reusable_policy_wire_omits_program_policy() {
1279 let policy =
1280 ReusableExecutionPolicy::new(1, vec![bucket("packed-prefill", 1, 64)]).unwrap();
1281 let value = serde_json::to_value(&policy).unwrap();
1282
1283 assert!(value.get("program_policy").is_none());
1284 assert_eq!(
1285 serde_json::from_value::<ReusableExecutionPolicy>(value).unwrap(),
1286 policy
1287 );
1288 }
1289
1290 #[test]
1291 fn exact_program_policy_rejects_inconsistent_decode_shape_wire() {
1292 let shape = ReusableExecutionProgramShape::uniform_decode(4, 1).unwrap();
1293 let mut value = serde_json::to_value(shape).unwrap();
1294 value["token_capacity"] = serde_json::json!(3);
1295
1296 assert!(serde_json::from_value::<ReusableExecutionProgramShape>(value).is_err());
1297 }
1298}