1use super::{
2 canonical_fingerprint, invalid_plan, BTreeMap, Deserialize, Deserializer, DynamicBackingPoolId,
3 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}
434
435#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
441#[serde(deny_unknown_fields)]
442pub struct ReusableExecutionProgramPolicy {
443 shape_semantics: ReusableExecutionProgramShapeSemantics,
444 catalog_miss_policy: ReusableExecutionCatalogMissPolicy,
445 catalog_lifetime: ReusableExecutionCatalogLifetime,
446 warmup_passes: u32,
447 capture_passes: u32,
448 replay_validation_passes: u32,
449 programs: Vec<ReusableExecutionProgramSpec>,
450}
451
452#[derive(Deserialize)]
453#[serde(deny_unknown_fields)]
454struct ReusableExecutionProgramPolicyWire {
455 shape_semantics: ReusableExecutionProgramShapeSemantics,
456 catalog_miss_policy: ReusableExecutionCatalogMissPolicy,
457 catalog_lifetime: ReusableExecutionCatalogLifetime,
458 warmup_passes: u32,
459 capture_passes: u32,
460 replay_validation_passes: u32,
461 programs: Vec<ReusableExecutionProgramSpec>,
462}
463
464impl ReusableExecutionProgramPolicy {
465 pub fn exact_startup_sealed(
466 warmup_passes: u32,
467 capture_passes: u32,
468 replay_validation_passes: u32,
469 mut programs: Vec<ReusableExecutionProgramSpec>,
470 ) -> Result<Self, VNextError> {
471 let prerequisite_prefill_programs = programs
472 .iter()
473 .filter_map(|program| match program.shape() {
474 ReusableExecutionProgramShape::Prefill {
475 tokens_processed,
476 total_prompt_tokens,
477 ..
478 } if tokens_processed > 0 => Some(ReusableExecutionProgramSpec {
479 class_id: program.class_id().clone(),
480 shape: ReusableExecutionProgramShape::Prefill {
481 tokens_processed: 0,
482 token_capacity: tokens_processed,
483 total_prompt_tokens,
484 },
485 }),
486 _ => None,
487 })
488 .collect::<Vec<_>>();
489 programs.extend(prerequisite_prefill_programs);
490 programs.sort_unstable();
491 programs.dedup();
492 let policy = Self {
493 shape_semantics: ReusableExecutionProgramShapeSemantics::Exact,
494 catalog_miss_policy: ReusableExecutionCatalogMissPolicy::EagerFallback,
495 catalog_lifetime: ReusableExecutionCatalogLifetime::StartupSealed,
496 warmup_passes,
497 capture_passes,
498 replay_validation_passes,
499 programs,
500 };
501 policy.validate()?;
502 Ok(policy)
503 }
504
505 fn validate(&self) -> Result<(), VNextError> {
506 if self.shape_semantics != ReusableExecutionProgramShapeSemantics::Exact
507 || self.catalog_miss_policy != ReusableExecutionCatalogMissPolicy::EagerFallback
508 || self.catalog_lifetime != ReusableExecutionCatalogLifetime::StartupSealed
509 || self.warmup_passes == 0
510 || self.capture_passes == 0
511 || self.replay_validation_passes == 0
512 || self.programs.is_empty()
513 || self.programs.len() > MAX_REUSABLE_EXECUTION_PROGRAM_SHAPES
514 {
515 return Err(invalid_plan(
516 "reusable execution program policy has invalid semantics, passes, or shape count",
517 ));
518 }
519 if self.programs.windows(2).any(|pair| pair[0] >= pair[1]) {
520 return Err(invalid_plan(
521 "reusable execution program shapes are duplicate or non-canonical",
522 ));
523 }
524 for program in &self.programs {
525 program.shape().validate()?;
526 }
527 let mut shape_classes = BTreeMap::new();
528 for program in &self.programs {
529 if shape_classes
530 .insert(program.shape(), program.class_id())
531 .is_some()
532 {
533 return Err(invalid_plan(
534 "reusable execution logical shape is assigned to multiple workspace classes",
535 ));
536 }
537 }
538 if self.programs.iter().any(|program| match program.shape() {
539 ReusableExecutionProgramShape::Prefill {
540 tokens_processed,
541 total_prompt_tokens,
542 ..
543 } if tokens_processed > 0 => !self.programs.contains(&ReusableExecutionProgramSpec {
544 class_id: program.class_id().clone(),
545 shape: ReusableExecutionProgramShape::Prefill {
546 tokens_processed: 0,
547 token_capacity: tokens_processed,
548 total_prompt_tokens,
549 },
550 }),
551 _ => false,
552 }) {
553 return Err(invalid_plan(
554 "reusable prefill program policy omits a prerequisite prefix shape",
555 ));
556 }
557 Ok(())
558 }
559
560 pub const fn shape_semantics(&self) -> ReusableExecutionProgramShapeSemantics {
561 self.shape_semantics
562 }
563
564 pub const fn catalog_miss_policy(&self) -> ReusableExecutionCatalogMissPolicy {
565 self.catalog_miss_policy
566 }
567
568 pub const fn catalog_lifetime(&self) -> ReusableExecutionCatalogLifetime {
569 self.catalog_lifetime
570 }
571
572 pub const fn warmup_passes(&self) -> u32 {
573 self.warmup_passes
574 }
575
576 pub const fn capture_passes(&self) -> u32 {
577 self.capture_passes
578 }
579
580 pub const fn replay_validation_passes(&self) -> u32 {
581 self.replay_validation_passes
582 }
583
584 pub fn programs(&self) -> &[ReusableExecutionProgramSpec] {
585 &self.programs
586 }
587}
588
589impl<'de> Deserialize<'de> for ReusableExecutionProgramPolicy {
590 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
591 where
592 D: Deserializer<'de>,
593 {
594 let wire = ReusableExecutionProgramPolicyWire::deserialize(deserializer)?;
595 let policy = Self {
596 shape_semantics: wire.shape_semantics,
597 catalog_miss_policy: wire.catalog_miss_policy,
598 catalog_lifetime: wire.catalog_lifetime,
599 warmup_passes: wire.warmup_passes,
600 capture_passes: wire.capture_passes,
601 replay_validation_passes: wire.replay_validation_passes,
602 programs: wire.programs,
603 };
604 policy.validate().map_err(serde::de::Error::custom)?;
605 Ok(policy)
606 }
607}
608
609#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
610#[serde(deny_unknown_fields)]
611pub struct ReusableExecutionPolicy {
612 maximum_reusable_lanes: u32,
613 buckets: Vec<ReusableExecutionBucketSpec>,
614 #[serde(default, skip_serializing_if = "Option::is_none")]
615 program_policy: Option<ReusableExecutionProgramPolicy>,
616}
617
618#[derive(Deserialize)]
619#[serde(deny_unknown_fields)]
620struct ReusableExecutionPolicyWire {
621 maximum_reusable_lanes: u32,
622 buckets: Vec<ReusableExecutionBucketSpec>,
623 #[serde(default)]
624 program_policy: Option<ReusableExecutionProgramPolicy>,
625}
626
627impl ReusableExecutionPolicy {
628 pub fn new(
629 maximum_reusable_lanes: u32,
630 mut buckets: Vec<ReusableExecutionBucketSpec>,
631 ) -> Result<Self, VNextError> {
632 buckets.sort_by(|left, right| {
633 (left.class_id(), left.capacity()).cmp(&(right.class_id(), right.capacity()))
634 });
635 let policy = Self {
636 maximum_reusable_lanes,
637 buckets,
638 program_policy: None,
639 };
640 policy.validate()?;
641 Ok(policy)
642 }
643
644 pub fn with_program_policy(
645 mut self,
646 program_policy: ReusableExecutionProgramPolicy,
647 ) -> Result<Self, VNextError> {
648 self.program_policy = Some(program_policy);
649 self.validate()?;
650 Ok(self)
651 }
652
653 pub(crate) fn validate(&self) -> Result<(), VNextError> {
654 if self.maximum_reusable_lanes == 0
655 || self.buckets.is_empty()
656 || self.buckets.len() > MAX_REUSABLE_EXECUTION_BUCKETS
657 {
658 return Err(invalid_plan(
659 "reusable execution policy has an invalid lane or bucket count",
660 ));
661 }
662 for bucket in &self.buckets {
663 bucket.validate()?;
664 }
665 if let Some(program_policy) = &self.program_policy {
666 program_policy.validate()?;
667 if program_policy.programs().iter().any(|program| {
668 let shape = program.shape();
669 self.smallest_covering_bucket(
670 program.class_id(),
671 shape.request_capacity(),
672 shape.token_capacity(),
673 0,
674 )
675 .is_none()
676 }) {
677 return Err(invalid_plan(
678 "reusable execution program shape has no covering workspace bucket in its class",
679 ));
680 }
681 }
682 if self.buckets.windows(2).any(|pair| {
683 let left = &pair[0];
684 let right = &pair[1];
685 left.class_id() > right.class_id()
686 || (left.class_id() == right.class_id()
687 && !right.capacity().strictly_extends(left.capacity()))
688 }) {
689 return Err(invalid_plan(
690 "reusable execution buckets are not canonical monotonic class chains",
691 ));
692 }
693 Ok(())
694 }
695
696 pub const fn maximum_reusable_lanes(&self) -> u32 {
697 self.maximum_reusable_lanes
698 }
699
700 pub fn buckets(&self) -> &[ReusableExecutionBucketSpec] {
701 &self.buckets
702 }
703
704 pub fn program_policy(&self) -> Option<&ReusableExecutionProgramPolicy> {
705 self.program_policy.as_ref()
706 }
707
708 pub fn startup_capture_case_count(&self) -> usize {
709 self.program_policy
710 .as_ref()
711 .map_or(self.buckets.len(), |policy| policy.programs().len())
712 }
713
714 pub fn bucket(
715 &self,
716 bucket_id: &ReusableExecutionBucketId,
717 ) -> Option<&ReusableExecutionBucketSpec> {
718 self.buckets
719 .iter()
720 .find(|bucket| bucket.bucket_id() == bucket_id)
721 }
722
723 pub fn smallest_covering_bucket(
724 &self,
725 class_id: &ReusableExecutionClassId,
726 sequences: u32,
727 tokens: u64,
728 pages: u64,
729 ) -> Option<&ReusableExecutionBucketSpec> {
730 self.buckets.iter().find(|bucket| {
731 bucket.class_id() == class_id && bucket.capacity().covers(sequences, tokens, pages)
732 })
733 }
734}
735
736impl<'de> Deserialize<'de> for ReusableExecutionPolicy {
737 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
738 where
739 D: Deserializer<'de>,
740 {
741 let wire = ReusableExecutionPolicyWire::deserialize(deserializer)?;
742 let policy = Self {
743 maximum_reusable_lanes: wire.maximum_reusable_lanes,
744 buckets: wire.buckets,
745 program_policy: wire.program_policy,
746 };
747 policy.validate().map_err(serde::de::Error::custom)?;
748 Ok(policy)
749 }
750}
751
752#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
753#[serde(deny_unknown_fields)]
754pub struct ReusablePoolWorkspaceBudget {
755 pool_id: DynamicBackingPoolId,
756 step_bytes: u64,
757 invocation_bytes: u64,
758}
759
760#[derive(Deserialize)]
761#[serde(deny_unknown_fields)]
762struct ReusablePoolWorkspaceBudgetWire {
763 pool_id: DynamicBackingPoolId,
764 step_bytes: u64,
765 invocation_bytes: u64,
766}
767
768impl ReusablePoolWorkspaceBudget {
769 pub(crate) fn new(
770 pool_id: DynamicBackingPoolId,
771 step_bytes: u64,
772 invocation_bytes: u64,
773 ) -> Result<Self, VNextError> {
774 let budget = Self {
775 pool_id,
776 step_bytes,
777 invocation_bytes,
778 };
779 budget.validate()?;
780 Ok(budget)
781 }
782
783 fn validate(&self) -> Result<(), VNextError> {
784 self.step_bytes
785 .checked_add(self.invocation_bytes)
786 .filter(|total| *total > 0)
787 .ok_or_else(|| {
788 invalid_plan("reusable pool workspace budget is empty or overflows u64")
789 })?;
790 Ok(())
791 }
792
793 pub fn pool_id(&self) -> &DynamicBackingPoolId {
794 &self.pool_id
795 }
796
797 pub const fn step_bytes(&self) -> u64 {
798 self.step_bytes
799 }
800
801 pub const fn invocation_bytes(&self) -> u64 {
802 self.invocation_bytes
803 }
804
805 pub fn total_bytes(&self) -> Result<u64, VNextError> {
806 self.step_bytes
807 .checked_add(self.invocation_bytes)
808 .ok_or_else(|| invalid_plan("reusable pool workspace budget overflows u64"))
809 }
810}
811
812impl<'de> Deserialize<'de> for ReusablePoolWorkspaceBudget {
813 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
814 where
815 D: Deserializer<'de>,
816 {
817 let wire = ReusablePoolWorkspaceBudgetWire::deserialize(deserializer)?;
818 Self::new(wire.pool_id, wire.step_bytes, wire.invocation_bytes)
819 .map_err(serde::de::Error::custom)
820 }
821}
822
823#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
824#[serde(deny_unknown_fields)]
825pub struct ResolvedReusableExecutionBucket {
826 bucket: ReusableExecutionBucketSpec,
827 pool_budgets: Vec<ReusablePoolWorkspaceBudget>,
828}
829
830#[derive(Deserialize)]
831#[serde(deny_unknown_fields)]
832struct ResolvedReusableExecutionBucketWire {
833 bucket: ReusableExecutionBucketSpec,
834 pool_budgets: Vec<ReusablePoolWorkspaceBudget>,
835}
836
837impl ResolvedReusableExecutionBucket {
838 pub(crate) fn new(
839 bucket: ReusableExecutionBucketSpec,
840 pool_budgets: Vec<ReusablePoolWorkspaceBudget>,
841 ) -> Result<Self, VNextError> {
842 let resolved = Self {
843 bucket,
844 pool_budgets,
845 };
846 resolved.validate()?;
847 Ok(resolved)
848 }
849
850 fn validate(&self) -> Result<(), VNextError> {
851 self.bucket.validate()?;
852 if self
853 .pool_budgets
854 .windows(2)
855 .any(|pair| pair[0].pool_id() >= pair[1].pool_id())
856 {
857 return Err(invalid_plan(
858 "resolved reusable bucket pool budgets are duplicate or non-canonical",
859 ));
860 }
861 for budget in &self.pool_budgets {
862 budget.validate()?;
863 }
864 Ok(())
865 }
866
867 pub fn bucket(&self) -> &ReusableExecutionBucketSpec {
868 &self.bucket
869 }
870
871 pub fn pool_budgets(&self) -> &[ReusablePoolWorkspaceBudget] {
872 &self.pool_budgets
873 }
874}
875
876impl<'de> Deserialize<'de> for ResolvedReusableExecutionBucket {
877 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
878 where
879 D: Deserializer<'de>,
880 {
881 let wire = ResolvedReusableExecutionBucketWire::deserialize(deserializer)?;
882 Self::new(wire.bucket, wire.pool_budgets).map_err(serde::de::Error::custom)
883 }
884}
885
886#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
887#[serde(deny_unknown_fields)]
888pub struct ReusableExecutionMemoryPlan {
889 maximum_reusable_lanes: u32,
890 maximum_device_executables: u64,
891 buckets: Vec<ResolvedReusableExecutionBucket>,
892 #[serde(default, skip_serializing_if = "Option::is_none")]
893 program_policy: Option<ReusableExecutionProgramPolicy>,
894}
895
896#[derive(Deserialize)]
897#[serde(deny_unknown_fields)]
898struct ReusableExecutionMemoryPlanWire {
899 maximum_reusable_lanes: u32,
900 maximum_device_executables: u64,
901 buckets: Vec<ResolvedReusableExecutionBucket>,
902 #[serde(default)]
903 program_policy: Option<ReusableExecutionProgramPolicy>,
904}
905
906impl ReusableExecutionMemoryPlan {
907 pub(crate) fn new(
908 maximum_reusable_lanes: u32,
909 maximum_device_executables: u64,
910 buckets: Vec<ResolvedReusableExecutionBucket>,
911 ) -> Result<Self, VNextError> {
912 Self::new_with_program_policy(
913 maximum_reusable_lanes,
914 maximum_device_executables,
915 buckets,
916 None,
917 )
918 }
919
920 pub(crate) fn new_with_program_policy(
921 maximum_reusable_lanes: u32,
922 maximum_device_executables: u64,
923 buckets: Vec<ResolvedReusableExecutionBucket>,
924 program_policy: Option<ReusableExecutionProgramPolicy>,
925 ) -> Result<Self, VNextError> {
926 let plan = Self {
927 maximum_reusable_lanes,
928 maximum_device_executables,
929 buckets,
930 program_policy,
931 };
932 plan.validate_local()?;
933 Ok(plan)
934 }
935
936 pub(crate) fn validate_local(&self) -> Result<(), VNextError> {
937 if self.maximum_reusable_lanes == 0
938 || self.maximum_device_executables == 0
939 || self.buckets.is_empty()
940 || self.buckets.len() > MAX_REUSABLE_EXECUTION_BUCKETS
941 {
942 return Err(invalid_plan(
943 "reusable execution memory plan has an invalid lane, executable, or bucket count",
944 ));
945 }
946 for bucket in &self.buckets {
947 bucket.validate()?;
948 }
949 let mut canonical = ReusableExecutionPolicy::new(
950 self.maximum_reusable_lanes,
951 self.buckets
952 .iter()
953 .map(|bucket| bucket.bucket().clone())
954 .collect(),
955 )?;
956 if let Some(program_policy) = self.program_policy.clone() {
957 canonical = canonical.with_program_policy(program_policy)?;
958 }
959 if canonical.buckets().iter().ne(self
960 .buckets
961 .iter()
962 .map(ResolvedReusableExecutionBucket::bucket))
963 {
964 return Err(invalid_plan(
965 "reusable execution memory buckets are non-canonical",
966 ));
967 }
968 Ok(())
969 }
970
971 pub const fn maximum_reusable_lanes(&self) -> u32 {
972 self.maximum_reusable_lanes
973 }
974
975 pub const fn maximum_device_executables(&self) -> u64 {
976 self.maximum_device_executables
977 }
978
979 pub fn buckets(&self) -> &[ResolvedReusableExecutionBucket] {
980 &self.buckets
981 }
982
983 pub fn program_policy(&self) -> Option<&ReusableExecutionProgramPolicy> {
984 self.program_policy.as_ref()
985 }
986
987 pub fn bucket(
988 &self,
989 bucket_id: &ReusableExecutionBucketId,
990 ) -> Option<&ResolvedReusableExecutionBucket> {
991 self.buckets
992 .iter()
993 .find(|bucket| bucket.bucket().bucket_id() == bucket_id)
994 }
995
996 pub fn smallest_covering_bucket(
997 &self,
998 class_id: &ReusableExecutionClassId,
999 sequences: u32,
1000 tokens: u64,
1001 pages: u64,
1002 ) -> Option<&ResolvedReusableExecutionBucket> {
1003 self.buckets.iter().find(|bucket| {
1004 bucket.bucket().class_id() == class_id
1005 && bucket.bucket().capacity().covers(sequences, tokens, pages)
1006 })
1007 }
1008
1009 pub(crate) fn policy(&self) -> Result<ReusableExecutionPolicy, VNextError> {
1010 let mut policy = ReusableExecutionPolicy::new(
1011 self.maximum_reusable_lanes,
1012 self.buckets
1013 .iter()
1014 .map(|bucket| bucket.bucket().clone())
1015 .collect(),
1016 )?;
1017 if let Some(program_policy) = self.program_policy.clone() {
1018 policy = policy.with_program_policy(program_policy)?;
1019 }
1020 Ok(policy)
1021 }
1022
1023 pub(crate) fn pool_workspace_ceilings(
1024 &self,
1025 ) -> Result<BTreeMap<DynamicBackingPoolId, u64>, VNextError> {
1026 let lanes = u64::from(self.maximum_reusable_lanes);
1027 let mut totals = BTreeMap::new();
1028 for bucket in &self.buckets {
1029 for budget in bucket.pool_budgets() {
1030 let bytes = budget
1031 .total_bytes()?
1032 .checked_mul(lanes)
1033 .ok_or_else(|| invalid_plan("reusable lane workspace budget overflows u64"))?;
1034 let total = totals.entry(budget.pool_id().clone()).or_insert(0_u64);
1035 *total = total
1036 .checked_add(bytes)
1037 .ok_or_else(|| invalid_plan("reusable pool workspace ceiling overflows u64"))?;
1038 }
1039 }
1040 Ok(totals)
1041 }
1042}
1043
1044impl<'de> Deserialize<'de> for ReusableExecutionMemoryPlan {
1045 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1046 where
1047 D: Deserializer<'de>,
1048 {
1049 let wire = ReusableExecutionMemoryPlanWire::deserialize(deserializer)?;
1050 Self::new_with_program_policy(
1051 wire.maximum_reusable_lanes,
1052 wire.maximum_device_executables,
1053 wire.buckets,
1054 wire.program_policy,
1055 )
1056 .map_err(serde::de::Error::custom)
1057 }
1058}
1059
1060#[cfg(test)]
1061mod tests {
1062 use super::{
1063 ReusableExecutionBucketSpec, ReusableExecutionCapacity, ReusableExecutionClassId,
1064 ReusableExecutionPolicy, ReusableExecutionProgramPolicy, ReusableExecutionProgramShape,
1065 ReusableExecutionProgramSpec,
1066 };
1067
1068 fn bucket(class: &str, sequences: u32, tokens: u64) -> ReusableExecutionBucketSpec {
1069 ReusableExecutionBucketSpec::new(
1070 ReusableExecutionClassId::new(class).unwrap(),
1071 ReusableExecutionCapacity::new(sequences, tokens, 1).unwrap(),
1072 )
1073 .unwrap()
1074 }
1075
1076 fn program(class: &str, shape: ReusableExecutionProgramShape) -> ReusableExecutionProgramSpec {
1077 ReusableExecutionProgramSpec::new(ReusableExecutionClassId::new(class).unwrap(), shape)
1078 .unwrap()
1079 }
1080
1081 #[test]
1082 fn reusable_policy_canonicalizes_and_selects_within_one_class() {
1083 let policy = ReusableExecutionPolicy::new(
1084 1,
1085 vec![
1086 bucket("uniform-query", 4, 4),
1087 bucket("packed-prefill", 1, 64),
1088 bucket("uniform-query", 1, 1),
1089 bucket("uniform-query", 2, 2),
1090 ],
1091 )
1092 .unwrap();
1093
1094 let class = ReusableExecutionClassId::new("uniform-query").unwrap();
1095 assert_eq!(
1096 policy
1097 .smallest_covering_bucket(&class, 3, 3, 0)
1098 .unwrap()
1099 .capacity(),
1100 ReusableExecutionCapacity::new(4, 4, 1).unwrap()
1101 );
1102 assert!(policy.smallest_covering_bucket(&class, 5, 5, 0).is_none());
1103 }
1104
1105 #[test]
1106 fn reusable_policy_wire_rejects_derived_id_tampering() {
1107 let policy =
1108 ReusableExecutionPolicy::new(1, vec![bucket("packed-prefill", 1, 64)]).unwrap();
1109 let mut value = serde_json::to_value(&policy).unwrap();
1110 value["buckets"][0]["bucket_id"] = serde_json::Value::String("forged".to_owned());
1111 assert!(serde_json::from_value::<ReusableExecutionPolicy>(value).is_err());
1112 }
1113
1114 #[test]
1115 fn reusable_policy_rejects_incomparable_capacities_within_class() {
1116 let first = bucket("mixed", 1, 64);
1117 let second = bucket("mixed", 4, 4);
1118 assert!(ReusableExecutionPolicy::new(1, vec![first, second]).is_err());
1119 }
1120
1121 #[test]
1122 fn exact_program_policy_is_canonical_and_adds_prefill_prerequisite() {
1123 let decode_4 = program(
1124 "uniform-query",
1125 ReusableExecutionProgramShape::uniform_decode(4, 1).unwrap(),
1126 );
1127 let decode_1 = program(
1128 "uniform-query",
1129 ReusableExecutionProgramShape::uniform_decode(1, 1).unwrap(),
1130 );
1131 let final_prefill = program(
1132 "packed-prefill",
1133 ReusableExecutionProgramShape::prefill(4, 4, 8).unwrap(),
1134 );
1135 let prerequisite = program(
1136 "packed-prefill",
1137 ReusableExecutionProgramShape::prefill(0, 4, 8).unwrap(),
1138 );
1139
1140 let program_policy = ReusableExecutionProgramPolicy::exact_startup_sealed(
1141 1,
1142 1,
1143 1,
1144 vec![
1145 decode_4.clone(),
1146 final_prefill.clone(),
1147 decode_1.clone(),
1148 decode_4.clone(),
1149 ],
1150 )
1151 .unwrap();
1152
1153 assert_eq!(
1154 program_policy.programs(),
1155 &[prerequisite, final_prefill, decode_1, decode_4]
1156 );
1157 }
1158
1159 #[test]
1160 fn program_policy_wire_rejects_missing_prefill_prerequisite() {
1161 let program_policy = ReusableExecutionProgramPolicy::exact_startup_sealed(
1162 1,
1163 1,
1164 1,
1165 vec![program(
1166 "packed-prefill",
1167 ReusableExecutionProgramShape::prefill(4, 4, 8).unwrap(),
1168 )],
1169 )
1170 .unwrap();
1171 let mut value = serde_json::to_value(program_policy).unwrap();
1172 value["programs"].as_array_mut().unwrap().remove(0);
1173
1174 assert!(serde_json::from_value::<ReusableExecutionProgramPolicy>(value).is_err());
1175 }
1176
1177 #[test]
1178 fn legacy_reusable_policy_wire_omits_program_policy() {
1179 let policy =
1180 ReusableExecutionPolicy::new(1, vec![bucket("packed-prefill", 1, 64)]).unwrap();
1181 let value = serde_json::to_value(&policy).unwrap();
1182
1183 assert!(value.get("program_policy").is_none());
1184 assert_eq!(
1185 serde_json::from_value::<ReusableExecutionPolicy>(value).unwrap(),
1186 policy
1187 );
1188 }
1189
1190 #[test]
1191 fn exact_program_policy_rejects_inconsistent_decode_shape_wire() {
1192 let shape = ReusableExecutionProgramShape::uniform_decode(4, 1).unwrap();
1193 let mut value = serde_json::to_value(shape).unwrap();
1194 value["token_capacity"] = serde_json::json!(3);
1195
1196 assert!(serde_json::from_value::<ReusableExecutionProgramShape>(value).is_err());
1197 }
1198}