Skip to main content

inferlab_protocol/
wire.rs

1//! The versioned wire types used by framework integrations
2//! ([[RFC-0006:C-INTEGRATIONS]]) and by the release-owned Eval and Bench
3//! measurement clients ([[RFC-0004:C-MEASUREMENTS]]). [`AdapterProtocol`] and
4//! [`MeasurementProtocol`] are separate schema roots. Measurement data-asset
5//! declarations live in their owning sibling module.
6
7use crate::measurement_data_asset::{
8    EvalPreparedSourceBinding, MeasurementDataAssetPreparationRequest,
9    MeasurementDataAssetPreparationResult,
10};
11use schemars::JsonSchema;
12use serde::{Deserialize, Serialize};
13use std::collections::BTreeMap;
14use std::path::PathBuf;
15
16// Shared base types.
17
18/// The shared protocol version used by framework integrations and release-owned
19/// measurement clients. The only accepted value is `11` (serialized as the
20/// string `"11"`); a mismatch is rejected before lowering.
21#[derive(Clone, Copy, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
22pub enum ProtocolVersion {
23    /// Protocol version 11.
24    #[serde(rename = "11")]
25    V11,
26}
27
28impl ProtocolVersion {
29    /// The current adapter protocol version.
30    pub const CURRENT: Self = Self::V11;
31
32    /// The protocol version as spelled on the wire, projected for surfaces
33    /// such as the control plane version output ([[RFC-0006:C-INTEGRATIONS]]).
34    /// Kept exhaustive so a future variant forces this projection to follow.
35    #[must_use]
36    pub const fn as_str(self) -> &'static str {
37        match self {
38            Self::V11 => "11",
39        }
40    }
41}
42
43/// A framework-specific server setting value carried as structured JSON data
44/// (never a pre-rendered shell fragment) across the integration boundary.
45#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
46#[serde(untagged)]
47pub enum SettingValue {
48    /// A JSON boolean.
49    Bool(bool),
50    /// A JSON integer.
51    Integer(i64),
52    /// A JSON floating-point number.
53    Float(f64),
54    /// A JSON string.
55    String(String),
56    /// A JSON array of setting values.
57    Array(Vec<SettingValue>),
58    /// A JSON object of named setting values.
59    Object(BTreeMap<String, SettingValue>),
60}
61
62/// The workload-attached capture mechanism selected for a profiled serving
63/// topology ([[RFC-0004:C-WORKLOAD-PROFILING]]).
64#[derive(
65    Clone, Copy, Debug, Default, Deserialize, Eq, JsonSchema, Ord, PartialEq, PartialOrd, Serialize,
66)]
67#[serde(rename_all = "snake_case")]
68pub enum CaptureMechanism {
69    /// InferLab wraps each target process with a managed Nsight Systems
70    /// collection.
71    #[default]
72    ManagedCollection,
73    /// The serving framework's internal profiler writes per-rank trace
74    /// artifacts under InferLab-directed storage.
75    EngineTrace,
76}
77
78/// The synthetic acceptance declaration carried to the selected integration
79/// ([[RFC-0003:C-SERVE-SYNTHETIC-ACCEPTANCE]]): exactly one of the explicit
80/// acceptance length or the digest-verified golden curve with its lookup
81/// coordinates. The draft count is not declared; the integration determines
82/// it from the operator's speculative configuration ([[ADR-0043]]).
83#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
84#[serde(rename_all = "snake_case", deny_unknown_fields)]
85pub enum SyntheticAcceptanceInput {
86    /// The explicit form: the declared acceptance length, overlaid as-is.
87    Explicit {
88        /// The declared mean acceptance length.
89        acceptance_length: f64,
90    },
91    /// The curve form: the digest-verified golden curve text with its digest
92    /// and lookup coordinates; the integration resolves the effective
93    /// acceptance length from it.
94    Curve(SyntheticAcceptanceCurveInput),
95}
96
97/// The curve form's wire payload ([[RFC-0003:C-SERVE-SYNTHETIC-ACCEPTANCE]]).
98#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
99#[serde(deny_unknown_fields)]
100pub struct SyntheticAcceptanceCurveInput {
101    pub model_key: String,
102    /// The effective thinking mode: the declared mode, or `thinking_on` when
103    /// the declaration omits it and the model entry uses the thinking-mode
104    /// shape. Absent when the model entry is a flat list (no mode applies).
105    #[serde(default, skip_serializing_if = "Option::is_none")]
106    pub thinking_mode: Option<String>,
107    /// The complete digest-verified curve file text.
108    pub text: String,
109    /// The SHA-256 digest of the curve file bytes, verified by the control
110    /// plane against the declaration.
111    pub sha256: String,
112}
113
114/// The integration's resolved synthetic acceptance outcome, returned in the
115/// `plan_serve` response whenever the request carried the declaration
116/// ([[RFC-0006:C-INTEGRATIONS]]).
117#[derive(Clone, Copy, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
118#[serde(deny_unknown_fields)]
119pub struct SyntheticAcceptanceOutcome {
120    /// The effective acceptance length the integration resolved and overlaid.
121    pub acceptance_length: f64,
122    /// The draft count the integration determined from the operator's
123    /// speculative configuration; present for the curve form, absent for the
124    /// explicit form.
125    #[serde(default, skip_serializing_if = "Option::is_none")]
126    pub draft_count: Option<u32>,
127}
128
129/// A concrete host/port endpoint the control plane allocated for a process.
130#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
131#[serde(deny_unknown_fields)]
132pub struct EndpointAssignment {
133    pub host: String,
134    pub port: u16,
135}
136
137/// The application protocol a workload endpoint speaks.
138#[derive(Clone, Copy, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
139#[serde(rename_all = "snake_case")]
140pub enum EndpointProtocol {
141    /// HTTP.
142    Http,
143}
144
145/// The HTTP method of an action specification.
146#[derive(Clone, Copy, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
147#[serde(rename_all = "snake_case")]
148pub enum HttpMethod {
149    /// HTTP POST.
150    Post,
151}
152
153// Serve-adapter request and response envelope.
154
155/// The one JSON request an integration reads from stdin, tagged by the
156/// requested operation.
157#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
158#[serde(tag = "operation", rename_all = "snake_case", deny_unknown_fields)]
159pub enum AdapterRequest {
160    /// Plan a serve topology: declare roles, per-replica requirements, links,
161    /// and endpoint requirements from the requested shape.
162    PlanServe {
163        protocol_version: ProtocolVersion,
164        input: PlanServeInput,
165    },
166    /// Render final process invocations for a planned topology, given the
167    /// control plane's concrete process allocations.
168    RenderServe {
169        protocol_version: ProtocolVersion,
170        input: RenderServeInput,
171    },
172}
173
174impl AdapterRequest {
175    /// The protocol version carried by this request, regardless of operation.
176    #[must_use]
177    pub const fn protocol_version(&self) -> ProtocolVersion {
178        match self {
179            Self::PlanServe {
180                protocol_version, ..
181            }
182            | Self::RenderServe {
183                protocol_version, ..
184            } => *protocol_version,
185        }
186    }
187}
188
189/// The requested serve shape a `PlanServe` operation lowers into a topology.
190#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
191#[serde(deny_unknown_fields)]
192pub struct PlanServeInput {
193    pub model: ServeModelInput,
194    pub topology: ServeTopology,
195    /// The control-plane-owned state directory spelling,
196    /// workspace-root-relative, that the integration resolves operator-declared
197    /// relative paths against when reading them through the workspace
198    /// filesystem and when declaring the supplied render-input source paths
199    /// those reads produce.
200    pub state_dir: String,
201    #[serde(default, skip_serializing_if = "Option::is_none")]
202    pub gateway_backend: Option<String>,
203    #[serde(default, skip_serializing_if = "Option::is_none")]
204    pub pd_router_backend: Option<String>,
205    #[serde(default, skip_serializing_if = "Option::is_none")]
206    pub kv_transfer: Option<KvTransferMechanism>,
207    pub roles: Vec<ServeRoleInput>,
208    /// The effective capture mechanism when profiling is requested; absent
209    /// means no profiling ([[RFC-0004:C-WORKLOAD-PROFILING]]).
210    #[serde(default, skip_serializing_if = "Option::is_none")]
211    pub profiling: Option<CaptureMechanism>,
212    /// The synthetic acceptance declaration when the serve declaration
213    /// carries one ([[RFC-0003:C-SERVE-SYNTHETIC-ACCEPTANCE]]).
214    #[serde(default, skip_serializing_if = "Option::is_none")]
215    pub synthetic_acceptance: Option<SyntheticAcceptanceInput>,
216    /// The auxiliary model declaration when the serve declaration carries
217    /// one ([[RFC-0003:C-SERVE-AUXILIARY-MODELS]]).
218    #[serde(default, skip_serializing_if = "Vec::is_empty")]
219    pub auxiliary_models: Vec<AuxiliaryModelInput>,
220}
221
222/// The planned topology plus the control plane's concrete allocations that a
223/// `RenderServe` operation turns into final process invocations.
224#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
225#[serde(deny_unknown_fields)]
226pub struct RenderServeInput {
227    pub model: ServeModelInput,
228    pub topology: ServeTopology,
229    /// The control-plane-owned state directory spelling,
230    /// workspace-root-relative, that the integration resolves operator-declared
231    /// relative paths against when matching them to supplied render-input
232    /// source paths.
233    pub state_dir: String,
234    #[serde(default, skip_serializing_if = "Option::is_none")]
235    pub gateway_backend: Option<String>,
236    #[serde(default, skip_serializing_if = "Option::is_none")]
237    pub pd_router_backend: Option<String>,
238    #[serde(default, skip_serializing_if = "Option::is_none")]
239    pub kv_transfer: Option<KvTransferMechanism>,
240    pub allocations: Vec<ServeProcessAllocation>,
241    /// The effective capture mechanism when profiling is requested; absent
242    /// means no profiling ([[RFC-0004:C-WORKLOAD-PROFILING]]).
243    #[serde(default, skip_serializing_if = "Option::is_none")]
244    pub profiling: Option<CaptureMechanism>,
245    /// The synthetic acceptance declaration when the serve declaration
246    /// carries one ([[RFC-0003:C-SERVE-SYNTHETIC-ACCEPTANCE]]).
247    #[serde(default, skip_serializing_if = "Option::is_none")]
248    pub synthetic_acceptance: Option<SyntheticAcceptanceInput>,
249    /// The auxiliary model declaration when the serve declaration carries
250    /// one ([[RFC-0003:C-SERVE-AUXILIARY-MODELS]]).
251    #[serde(default, skip_serializing_if = "Vec::is_empty")]
252    pub auxiliary_models: Vec<AuxiliaryModelInput>,
253}
254
255/// The one JSON response an integration writes to stdout, tagged by outcome.
256#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
257#[serde(tag = "status", rename_all = "snake_case", deny_unknown_fields)]
258pub enum AdapterResponse {
259    /// The operation succeeded and carries its result.
260    Ok {
261        protocol_version: ProtocolVersion,
262        result: Box<AdapterResult>,
263    },
264    /// The operation was rejected with a structured error.
265    Error {
266        protocol_version: ProtocolVersion,
267        error: AdapterError,
268    },
269}
270
271impl AdapterResponse {
272    /// The protocol version carried by this response, regardless of outcome.
273    #[must_use]
274    pub const fn protocol_version(&self) -> ProtocolVersion {
275        match self {
276            Self::Ok {
277                protocol_version, ..
278            }
279            | Self::Error {
280                protocol_version, ..
281            } => *protocol_version,
282        }
283    }
284}
285
286/// The successful result of an [`AdapterRequest`], tagged by the operation it
287/// answers.
288#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
289#[serde(tag = "operation", rename_all = "snake_case", deny_unknown_fields)]
290pub enum AdapterResult {
291    /// The planned topology from a `PlanServe` request.
292    PlanServe { output: Box<PlanServeResult> },
293    /// The rendered process invocations from a `RenderServe` request.
294    RenderServe { output: Box<RenderServeResult> },
295}
296
297/// A structured rejection an integration returns in an [`AdapterResponse::Error`].
298#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
299#[serde(deny_unknown_fields)]
300pub struct AdapterError {
301    pub code: AdapterErrorCode,
302    pub message: String,
303}
304
305/// Machine-readable failure category an adapter reports in an [`AdapterError`].
306#[derive(Clone, Copy, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
307#[serde(rename_all = "snake_case")]
308pub enum AdapterErrorCode {
309    /// The request was malformed or missing required fields.
310    InvalidRequest,
311    /// The request's protocol version is not accepted.
312    UnsupportedProtocolVersion,
313    /// A framework setting was unknown or invalid.
314    InvalidSettings,
315    /// An unexpected internal failure occurred in the integration.
316    Internal,
317    /// The requested operation is not supported by this integration.
318    UnsupportedOperation,
319}
320
321/// The integration's identity recorded on its results: adapter id, adapter
322/// version, and the framework it lowers to.
323#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
324#[serde(deny_unknown_fields)]
325pub struct IntegrationIdentity {
326    pub adapter_id: String,
327    pub adapter_version: String,
328    pub framework: String,
329    pub framework_version: String,
330}
331
332// Serve-adapter plan and render shapes.
333
334/// The serving deployment topology.
335#[derive(Clone, Copy, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
336#[serde(rename_all = "snake_case")]
337pub enum ServeTopology {
338    /// One aggregated serving role.
339    Single,
340    /// Disaggregated prefill and decode roles.
341    PrefillDecode,
342}
343
344/// The logical role a replica plays in a serve topology.
345#[derive(Clone, Copy, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
346#[serde(rename_all = "snake_case")]
347pub enum ServeRoleKind {
348    /// Aggregated serving role of a single topology.
349    Serve,
350    /// Prefill role of a disaggregated topology.
351    Prefill,
352    /// Decode role of a disaggregated topology.
353    Decode,
354}
355
356impl ServeRoleKind {
357    /// The role id as spelled on the wire, projected for control-plane
358    /// surfaces that name roles. Kept exhaustive so a future variant forces
359    /// this projection to follow.
360    #[must_use]
361    pub const fn as_str(self) -> &'static str {
362        match self {
363            Self::Serve => "serve",
364            Self::Prefill => "prefill",
365            Self::Decode => "decode",
366        }
367    }
368}
369
370/// The KV-transfer mechanism connecting prefill and decode.
371#[derive(Clone, Copy, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
372#[serde(rename_all = "snake_case")]
373pub enum KvTransferMechanism {
374    /// Mooncake KV-cache transfer.
375    Mooncake,
376    /// NIXL KV-cache transfer.
377    Nixl,
378}
379
380/// Framework-neutral component-aware parallelism ([[RFC-0003:C-SERVE-PARALLELISM]]).
381/// Every component is optional so an operator can override one without
382/// repeating the rest; omitted components are filled by the integration into a
383/// complete effective shape.
384#[derive(Clone, Debug, Default, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
385#[serde(default, deny_unknown_fields)]
386pub struct Parallelism {
387    /// Outer deployment parallelism shared by attention and experts.
388    #[serde(skip_serializing_if = "Option::is_none")]
389    pub outer: Option<ParallelismOuter>,
390    /// Attention-block parallelism.
391    #[serde(skip_serializing_if = "Option::is_none")]
392    pub attention: Option<ParallelismAttention>,
393    /// MoE expert parallelism.
394    #[serde(skip_serializing_if = "Option::is_none")]
395    pub experts: Option<ParallelismExperts>,
396}
397
398impl Parallelism {
399    /// The declared parallelism dimensions as dotted-name/value pairs — the
400    /// single field projection consumed by both the load-time and the
401    /// resolution-time validators, so a new axis cannot silently escape one
402    /// of them ([[RFC-0003:C-SERVE-PARALLELISM]]).
403    pub fn field_values(&self) -> [(&'static str, Option<u32>); 9] {
404        [
405            (
406                "outer.tensor_parallel_size",
407                self.outer
408                    .as_ref()
409                    .and_then(|value| value.tensor_parallel_size),
410            ),
411            (
412                "outer.pipeline_parallel_size",
413                self.outer
414                    .as_ref()
415                    .and_then(|value| value.pipeline_parallel_size),
416            ),
417            (
418                "attention.tensor_parallel_size",
419                self.attention
420                    .as_ref()
421                    .and_then(|value| value.tensor_parallel_size),
422            ),
423            (
424                "attention.data_parallel_size",
425                self.attention
426                    .as_ref()
427                    .and_then(|value| value.data_parallel_size),
428            ),
429            (
430                "attention.context_parallel_size",
431                self.attention
432                    .as_ref()
433                    .and_then(|value| value.context_parallel_size),
434            ),
435            (
436                "experts.tensor_parallel_size",
437                self.experts
438                    .as_ref()
439                    .and_then(|value| value.tensor_parallel_size),
440            ),
441            (
442                "experts.data_parallel_size",
443                self.experts
444                    .as_ref()
445                    .and_then(|value| value.data_parallel_size),
446            ),
447            (
448                "experts.expert_parallel_size",
449                self.experts
450                    .as_ref()
451                    .and_then(|value| value.expert_parallel_size),
452            ),
453            (
454                "experts.dense_tensor_parallel_size",
455                self.experts
456                    .as_ref()
457                    .and_then(|value| value.dense_tensor_parallel_size),
458            ),
459        ]
460    }
461
462    /// Overlay the components present in `other` onto `self`, leaving
463    /// components `other` omits untouched (the per-component precedence merge).
464    pub fn merge_from(&mut self, other: &Self) {
465        if let Some(other) = &other.outer {
466            self.outer.get_or_insert_default().merge_from(other);
467        }
468        if let Some(other) = &other.attention {
469            self.attention.get_or_insert_default().merge_from(other);
470        }
471        if let Some(other) = &other.experts {
472            self.experts.get_or_insert_default().merge_from(other);
473        }
474    }
475}
476
477/// Outer deployment parallelism: tensor and pipeline degrees.
478#[derive(Clone, Debug, Default, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
479#[serde(default, deny_unknown_fields)]
480pub struct ParallelismOuter {
481    #[schemars(range(min = 1))]
482    #[serde(skip_serializing_if = "Option::is_none")]
483    pub tensor_parallel_size: Option<u32>,
484    #[schemars(range(min = 1))]
485    #[serde(skip_serializing_if = "Option::is_none")]
486    pub pipeline_parallel_size: Option<u32>,
487}
488
489impl ParallelismOuter {
490    fn merge_from(&mut self, other: &Self) {
491        if other.tensor_parallel_size.is_some() {
492            self.tensor_parallel_size = other.tensor_parallel_size;
493        }
494        if other.pipeline_parallel_size.is_some() {
495            self.pipeline_parallel_size = other.pipeline_parallel_size;
496        }
497    }
498}
499
500/// Attention-block parallelism: tensor, data, and context degrees.
501#[derive(Clone, Debug, Default, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
502#[serde(default, deny_unknown_fields)]
503pub struct ParallelismAttention {
504    #[schemars(range(min = 1))]
505    #[serde(skip_serializing_if = "Option::is_none")]
506    pub tensor_parallel_size: Option<u32>,
507    #[schemars(range(min = 1))]
508    #[serde(skip_serializing_if = "Option::is_none")]
509    pub data_parallel_size: Option<u32>,
510    #[schemars(range(min = 1))]
511    #[serde(skip_serializing_if = "Option::is_none")]
512    pub context_parallel_size: Option<u32>,
513}
514
515impl ParallelismAttention {
516    fn merge_from(&mut self, other: &Self) {
517        if other.tensor_parallel_size.is_some() {
518            self.tensor_parallel_size = other.tensor_parallel_size;
519        }
520        if other.data_parallel_size.is_some() {
521            self.data_parallel_size = other.data_parallel_size;
522        }
523        if other.context_parallel_size.is_some() {
524            self.context_parallel_size = other.context_parallel_size;
525        }
526    }
527}
528
529/// MoE expert parallelism: tensor, data, expert, and dense-tensor degrees.
530#[derive(Clone, Debug, Default, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
531#[serde(default, deny_unknown_fields)]
532pub struct ParallelismExperts {
533    #[schemars(range(min = 1))]
534    #[serde(skip_serializing_if = "Option::is_none")]
535    pub tensor_parallel_size: Option<u32>,
536    #[schemars(range(min = 1))]
537    #[serde(skip_serializing_if = "Option::is_none")]
538    pub data_parallel_size: Option<u32>,
539    #[schemars(range(min = 1))]
540    #[serde(skip_serializing_if = "Option::is_none")]
541    pub expert_parallel_size: Option<u32>,
542    #[schemars(range(min = 1))]
543    #[serde(skip_serializing_if = "Option::is_none")]
544    pub dense_tensor_parallel_size: Option<u32>,
545}
546
547impl ParallelismExperts {
548    fn merge_from(&mut self, other: &Self) {
549        if other.tensor_parallel_size.is_some() {
550            self.tensor_parallel_size = other.tensor_parallel_size;
551        }
552        if other.data_parallel_size.is_some() {
553            self.data_parallel_size = other.data_parallel_size;
554        }
555        if other.expert_parallel_size.is_some() {
556            self.expert_parallel_size = other.expert_parallel_size;
557        }
558        if other.dense_tensor_parallel_size.is_some() {
559            self.dense_tensor_parallel_size = other.dense_tensor_parallel_size;
560        }
561    }
562}
563
564/// The logical model supplied during serving planning and rendering.
565#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
566#[serde(deny_unknown_fields)]
567pub struct ServeModelInput {
568    pub id: String,
569    pub served_name: String,
570}
571
572/// The governed auxiliary weight artifact kinds a serve declaration may attach
573/// ([[RFC-0003:C-SERVE-AUXILIARY-MODELS]]).
574#[derive(Clone, Copy, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
575#[serde(rename_all = "kebab-case")]
576pub enum AuxiliaryModelKind {
577    /// Separate weights the operator's framework speculative decoding
578    /// configuration consumes as its draft.
579    DraftModel,
580}
581
582/// A logical auxiliary weight identity supplied during serving planning and
583/// rendering: its governed kind plus the referenced logical model
584/// ([[RFC-0003:C-SERVE-AUXILIARY-MODELS]]).
585#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
586#[serde(deny_unknown_fields)]
587pub struct AuxiliaryModelInput {
588    pub kind: AuxiliaryModelKind,
589    pub model: ServeModelInput,
590}
591
592/// A machine-resolved auxiliary weight locator on a model-rank allocation
593/// ([[RFC-0003:C-SERVE-AUXILIARY-MODELS]]).
594#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
595#[serde(deny_unknown_fields)]
596pub struct AuxiliaryModelLocator {
597    pub kind: AuxiliaryModelKind,
598    pub locator: String,
599}
600
601/// A requested serving role: its identity, kind, replica cardinality, and
602/// declared (not-yet-completed) parallelism and settings.
603#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
604#[serde(deny_unknown_fields)]
605pub struct ServeRoleInput {
606    pub id: String,
607    pub kind: ServeRoleKind,
608    pub replica_count: u32,
609    pub parallelism: Parallelism,
610    pub settings: BTreeMap<String, SettingValue>,
611}
612
613/// A role as the integration resolved it: preserved identity and cardinality
614/// with the complete effective settings and parallelism.
615#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
616#[serde(deny_unknown_fields)]
617pub struct ServeRoleResult {
618    pub id: String,
619    pub kind: ServeRoleKind,
620    pub declared_replica_count: u32,
621    pub effective_replica_count: u32,
622    pub effective_settings: BTreeMap<String, SettingValue>,
623    pub effective_parallelism: Parallelism,
624    /// The public endpoint capability declaration for a direct `single`
625    /// Engine. Gateway-backed shapes leave this absent and carry the
626    /// declaration on Gateway.
627    #[serde(default, skip_serializing_if = "Option::is_none")]
628    pub public_endpoint: Option<EndpointDeclaration>,
629    #[serde(default)]
630    pub render_inputs: Vec<RenderInputDeclaration>,
631}
632
633/// The lowered topology returned by a `PlanServe`: effective Engine roles,
634/// whole-replica requirements, logical links, and separate optional Gateway
635/// and P/D Router component plans.
636#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
637#[serde(deny_unknown_fields)]
638pub struct PlanServeResult {
639    pub integration: IntegrationIdentity,
640    /// The resolved synthetic acceptance outcome; required in the accepted
641    /// plan whenever the request carried the declaration, and forbidden
642    /// otherwise ([[RFC-0003:C-SERVE-SYNTHETIC-ACCEPTANCE]]).
643    #[serde(default, skip_serializing_if = "Option::is_none")]
644    pub synthetic_acceptance: Option<SyntheticAcceptanceOutcome>,
645    pub roles: Vec<ServeRoleResult>,
646    pub replicas: Vec<ServeReplicaRequirement>,
647    pub links: Vec<ServeRoleLink>,
648    #[serde(default, skip_serializing_if = "Option::is_none")]
649    pub gateway: Option<GatewayPlan>,
650    #[serde(default, skip_serializing_if = "Option::is_none")]
651    pub pd_router: Option<PdRouterPlan>,
652}
653
654/// A whole-replica resource and readiness requirement the integration declares
655/// without choosing placement, ranks, or concrete endpoints.
656#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
657#[serde(deny_unknown_fields)]
658pub struct ServeReplicaRequirement {
659    pub id: String,
660    pub role_id: String,
661    pub replica_index: u32,
662    pub device_count: u32,
663    pub ports: Vec<String>,
664    pub primary_ports: Vec<String>,
665    pub primary_readiness: ReadinessProbe,
666    pub worker_readiness: ReadinessProbe,
667    #[serde(default, skip_serializing_if = "Option::is_none")]
668    pub capture_target: Option<CaptureTargetRequirement>,
669}
670
671/// A directed link between serve roles the integration declares as part of the
672/// topology.
673#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
674#[serde(tag = "kind", rename_all = "snake_case", deny_unknown_fields)]
675pub enum ServeRoleLink {
676    /// The source role routes requests to the target roles.
677    RequestRouting {
678        source: String,
679        targets: Vec<String>,
680    },
681    /// KV cache is transferred from source to target over `mechanism`.
682    KvTransfer {
683        source: String,
684        target: String,
685        mechanism: KvTransferMechanism,
686    },
687    /// The source discovers the target through a bootstrap port.
688    Bootstrap {
689        source: String,
690        target: String,
691        port: String,
692    },
693    /// The source and target exchange out-of-band data over a side-channel port.
694    SideChannel {
695        source: String,
696        target: String,
697        port: String,
698    },
699}
700
701/// One workspace-authored UTF-8 source file an integration declares during
702/// planning for the control plane to supply during final rendering.
703#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
704#[serde(deny_unknown_fields)]
705pub struct RenderInputDeclaration {
706    pub source_path: String,
707}
708
709/// The original declared path plus the exact UTF-8 contents and digest the
710/// control plane supplies to an integration during final rendering.
711#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
712#[serde(deny_unknown_fields)]
713pub struct SuppliedRenderInput {
714    pub source_path: String,
715    pub text: String,
716    pub sha256: String,
717}
718
719/// The plan-response endpoint capability declaration
720/// ([[RFC-0006:C-OPENAI-ENDPOINT-CONTRACT]]): the protocol plus the
721/// integration-owned capability members (server metrics, prefix-cache
722/// actions, and the cache-read representation). The control plane owns the
723/// route paths, so this declaration carries no path values and rejects them
724/// as unknown fields.
725#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
726#[serde(deny_unknown_fields)]
727pub struct EndpointDeclaration {
728    pub protocol: EndpointProtocol,
729    #[serde(default, skip_serializing_if = "Option::is_none")]
730    pub server_metrics: Option<ServerMetricsEndpointRequirement>,
731    #[serde(default, skip_serializing_if = "Option::is_none")]
732    pub prefix_cache_reset: Option<HttpActionSpec>,
733    /// A Gateway-backend conditioning fan-out action: the frontend routes one
734    /// conditioning request per prefill replica and attention data-parallel
735    /// rank ([[RFC-0004:C-BENCH-CACHE-STATE]]).
736    #[serde(default, skip_serializing_if = "Option::is_none")]
737    pub prefix_cache_conditioning: Option<HttpActionSpec>,
738    #[serde(default, skip_serializing_if = "Option::is_none")]
739    pub prompt_cache_read_zero_representation: Option<PromptCacheReadZeroRepresentation>,
740}
741
742/// The resolved workload endpoint requirement: the accepted declaration plus
743/// the named OpenAI paths. The control plane fills the pinned
744/// `/v1/completions` and `/v1/chat/completions` paths at plan acceptance
745/// ([[RFC-0006:C-OPENAI-ENDPOINT-CONTRACT]]); resolved evidence and client
746/// inputs preserve both strings exactly.
747#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
748#[serde(deny_unknown_fields)]
749pub struct EndpointRequirement {
750    pub protocol: EndpointProtocol,
751    pub completions_path: String,
752    pub chat_completions_path: String,
753    #[serde(default, skip_serializing_if = "Option::is_none")]
754    pub server_metrics: Option<ServerMetricsEndpointRequirement>,
755    #[serde(default, skip_serializing_if = "Option::is_none")]
756    pub prefix_cache_reset: Option<HttpActionSpec>,
757    /// A Gateway-backend conditioning fan-out action: the frontend routes one
758    /// conditioning request per prefill replica and attention data-parallel
759    /// rank ([[RFC-0004:C-BENCH-CACHE-STATE]]).
760    #[serde(default, skip_serializing_if = "Option::is_none")]
761    pub prefix_cache_conditioning: Option<HttpActionSpec>,
762    #[serde(default, skip_serializing_if = "Option::is_none")]
763    pub prompt_cache_read_zero_representation: Option<PromptCacheReadZeroRepresentation>,
764}
765
766/// An integration-owned logical server-metrics endpoint.
767#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
768#[serde(deny_unknown_fields)]
769pub struct ServerMetricsEndpointRequirement {
770    pub path: String,
771    #[serde(default, skip_serializing_if = "Option::is_none")]
772    pub port: Option<String>,
773}
774
775/// How the control plane decides a process is ready.
776#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
777#[serde(tag = "kind", rename_all = "snake_case", deny_unknown_fields)]
778pub enum ReadinessProbe {
779    /// Ready when an HTTP GET of `path` succeeds.
780    Http { path: String },
781    /// Ready when the public endpoint succeeds and its HTTP target registry
782    /// contains every control-plane-derived serving target.
783    HttpTargetRegistry(Box<HttpTargetRegistryReadiness>),
784    /// Ready as soon as the process is alive.
785    ProcessAlive,
786}
787
788/// The integration-owned HTTP registry contract for target-aware readiness.
789#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
790#[serde(deny_unknown_fields)]
791pub struct HttpTargetRegistryReadiness {
792    pub target_scheme: TargetEndpointScheme,
793    pub readiness_path: String,
794    pub registry_path: String,
795    pub targets_field: String,
796    pub target_url_field: String,
797    pub target_role_field: String,
798    pub target_healthy_field: String,
799    pub target_bootstrap_port_field: String,
800    pub prefill_role_value: String,
801    pub decode_role_value: String,
802    pub prefill_bootstrap_port: String,
803}
804
805/// An HTTP action invoked against a logical serving endpoint.
806#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
807#[serde(deny_unknown_fields)]
808pub struct HttpActionSpec {
809    pub method: HttpMethod,
810    pub path: String,
811}
812
813// Capture window control ([[RFC-0004:C-WORKLOAD-PROFILING]]).
814
815/// Marks a replica as a profiling capture target and carries its window
816/// control ([[RFC-0004:C-WORKLOAD-PROFILING]]).
817#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
818#[serde(deny_unknown_fields)]
819pub struct CaptureTargetRequirement {
820    /// The capture mechanism this target declares; it must equal the
821    /// effective mechanism requested on the plan.
822    pub mechanism: CaptureMechanism,
823    pub window_control: CaptureWindowControlRequirement,
824}
825
826/// The logical workload endpoint and typed actions that open and close a
827/// capture window.
828#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
829#[serde(deny_unknown_fields)]
830pub struct CaptureWindowControlRequirement {
831    pub endpoint: CaptureWindowControlEndpoint,
832    pub start: CaptureWindowHttpActionSpec,
833    pub stop: CaptureWindowHttpActionSpec,
834}
835
836/// The logical workload endpoint exposing a capture target's window-control
837/// actions.
838#[derive(Clone, Copy, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
839#[serde(rename_all = "snake_case")]
840pub enum CaptureWindowControlEndpoint {
841    /// The entry process of the capture target's Engine replica.
842    ReplicaEntry,
843    /// The separately planned Gateway process.
844    Gateway,
845}
846
847/// A capture-window HTTP action invoked against a logical serving endpoint.
848/// The integration owns any framework-specific JSON body; the control plane
849/// owns execution and evidence.
850#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
851#[serde(deny_unknown_fields)]
852pub struct CaptureWindowHttpActionSpec {
853    pub method: HttpMethod,
854    pub path: String,
855    #[serde(default, skip_serializing_if = "Option::is_none")]
856    pub body: Option<BTreeMap<String, SettingValue>>,
857}
858
859/// One concrete process allocation supplied to `RenderServe`. Model-rank and
860/// process-only frontend identities are distinct so a frontend cannot acquire
861/// model coordinates or a model locator by construction.
862#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
863#[serde(tag = "kind", rename_all = "snake_case", deny_unknown_fields)]
864pub enum ServeProcessAllocation {
865    ModelRank {
866        process: String,
867        role: String,
868        role_kind: ServeRoleKind,
869        replica: u32,
870        rank: u32,
871        rank_count: u32,
872        machine: String,
873        devices: Vec<u32>,
874        model_locator: String,
875        /// The machine-resolved auxiliary weight locators for each declared
876        /// kind ([[RFC-0003:C-SERVE-AUXILIARY-MODELS]]).
877        #[serde(default, skip_serializing_if = "Vec::is_empty")]
878        auxiliary_model_locators: Vec<AuxiliaryModelLocator>,
879        #[serde(default, skip_serializing_if = "Option::is_none")]
880        endpoint: Option<EndpointAssignment>,
881        ports: BTreeMap<String, EndpointAssignment>,
882        cache: String,
883        /// The control-plane-assigned persistent trace directory; present
884        /// only when this rank belongs to an engine-trace capture target
885        /// ([[RFC-0004:C-WORKLOAD-PROFILING]]).
886        #[serde(default, skip_serializing_if = "Option::is_none")]
887        capture_storage: Option<String>,
888        launch: AllocationLaunch,
889        effective_settings: BTreeMap<String, SettingValue>,
890        effective_parallelism: Parallelism,
891        #[serde(default)]
892        links: Vec<ServeRoleLink>,
893        #[serde(default)]
894        dependencies: Vec<String>,
895        #[serde(default)]
896        render_inputs: Vec<SuppliedRenderInput>,
897    },
898    Frontend {
899        process: String,
900        process_role: FrontendProcessRole,
901        components: FrontendComponents,
902        machine: String,
903        devices: Vec<u32>,
904        endpoint: EndpointAssignment,
905        ports: BTreeMap<String, EndpointAssignment>,
906        cache: String,
907        launch: AllocationLaunch,
908        gateway: Box<GatewayPlan>,
909        #[serde(default, skip_serializing_if = "Option::is_none")]
910        pd_router: Option<Box<PdRouterPlan>>,
911        #[serde(default)]
912        links: Vec<ServeRoleLink>,
913        #[serde(default)]
914        dependencies: Vec<String>,
915        #[serde(default)]
916        render_inputs: Vec<SuppliedRenderInput>,
917    },
918}
919
920/// The machine-local launch channel selected by the control plane.
921#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
922#[serde(tag = "kind", rename_all = "snake_case", deny_unknown_fields)]
923pub enum AllocationLaunch {
924    Local,
925    Ssh { target: String },
926}
927
928/// The final process invocations returned by a `RenderServe`, one per supplied
929/// allocation.
930#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
931#[serde(deny_unknown_fields)]
932pub struct RenderServeResult {
933    pub integration: IntegrationIdentity,
934    pub processes: Vec<RenderedServeProcess>,
935}
936
937/// A rendered process bound to the model-rank or frontend allocation identity
938/// it was produced for.
939#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
940#[serde(tag = "kind", rename_all = "snake_case", deny_unknown_fields)]
941pub enum RenderedServeProcess {
942    ModelRank {
943        process: String,
944        role: String,
945        replica: u32,
946        rank: u32,
947        rank_count: u32,
948        launch_files: Vec<LaunchFileDeclaration>,
949        command: ProcessSpec,
950    },
951    Frontend {
952        process: String,
953        process_role: FrontendProcessRole,
954        components: FrontendComponents,
955        launch_files: Vec<LaunchFileDeclaration>,
956        command: ProcessSpec,
957    },
958}
959
960/// One immutable text input a rendered process requires before it can launch.
961#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
962#[serde(deny_unknown_fields)]
963pub struct LaunchFileDeclaration {
964    pub relative_path: String,
965    pub text: String,
966    pub sha256: String,
967}
968
969/// A launchable process: its argument vector and environment.
970#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
971#[serde(deny_unknown_fields)]
972pub struct ProcessSpec {
973    pub argv: Vec<String>,
974    pub env: BTreeMap<String, String>,
975}
976
977// Frontend components.
978
979/// Which bounded implementation renders an accepted concrete frontend
980/// allocation. This is a lowering boundary only; the control plane always
981/// owns placement, lifecycle, cleanup, endpoints, and records.
982#[derive(Clone, Copy, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
983#[serde(rename_all = "snake_case")]
984pub enum RenderSource {
985    ControlPlane,
986    Integration,
987}
988
989/// The one canonical process role available to a frontend.
990#[derive(Clone, Copy, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
991#[serde(rename_all = "snake_case")]
992pub enum FrontendProcessRole {
993    Gateway,
994}
995
996/// The fixed co-rendering requirement shared by compatible frontend plans.
997#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
998#[serde(deny_unknown_fields)]
999pub struct FrontendCoRendering {
1000    pub process_role: FrontendProcessRole,
1001}
1002
1003/// The literal first member of every closed frontend component binding.
1004#[derive(Clone, Copy, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
1005#[serde(rename_all = "snake_case")]
1006pub enum FrontendGatewayComponent {
1007    Gateway,
1008}
1009
1010/// The literal second member of the fused P/D frontend component binding.
1011#[derive(Clone, Copy, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
1012#[serde(rename_all = "snake_case")]
1013pub enum FrontendPdRouterComponent {
1014    PdRouter,
1015}
1016
1017/// The stable schema branch for a Gateway-only frontend binding.
1018#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
1019#[serde(transparent)]
1020pub struct GatewayFrontendBinding(pub [FrontendGatewayComponent; 1]);
1021
1022/// The stable schema branch for a fused Gateway and P/D Router binding.
1023#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
1024#[serde(transparent)]
1025pub struct GatewayPdRouterFrontendBinding(
1026    pub (FrontendGatewayComponent, FrontendPdRouterComponent),
1027);
1028
1029/// The only two frontend bindings the adapter protocol accepts. Tuple representation
1030/// deliberately serializes as the closed ordered arrays `["gateway"]` and
1031/// `["gateway", "pd_router"]` rather than as an open component list.
1032#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
1033#[serde(untagged)]
1034pub enum FrontendComponents {
1035    Gateway(GatewayFrontendBinding),
1036    GatewayPdRouter(GatewayPdRouterFrontendBinding),
1037}
1038
1039impl FrontendComponents {
1040    #[must_use]
1041    pub const fn gateway() -> Self {
1042        Self::Gateway(GatewayFrontendBinding([FrontendGatewayComponent::Gateway]))
1043    }
1044
1045    #[must_use]
1046    pub const fn gateway_pd_router() -> Self {
1047        Self::GatewayPdRouter(GatewayPdRouterFrontendBinding((
1048            FrontendGatewayComponent::Gateway,
1049            FrontendPdRouterComponent::PdRouter,
1050        )))
1051    }
1052
1053    #[must_use]
1054    pub const fn includes_pd_router(&self) -> bool {
1055        matches!(self, Self::GatewayPdRouter(_))
1056    }
1057}
1058
1059/// The target a Gateway forwards accepted public requests to.
1060#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
1061#[serde(tag = "kind", rename_all = "snake_case", deny_unknown_fields)]
1062pub enum GatewayTarget {
1063    /// A routed-single Gateway targets the sole Engine role entry point.
1064    Engine { role: String },
1065    /// A P/D Gateway hands requests to its co-rendered P/D Router component.
1066    PdRouter,
1067}
1068
1069/// The public frontend component plan returned independently from Engine and
1070/// P/D Router planning.
1071#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
1072#[serde(deny_unknown_fields)]
1073pub struct GatewayPlan {
1074    pub backend: String,
1075    pub implementation: String,
1076    pub implementation_version: String,
1077    pub effective_settings: BTreeMap<String, SettingValue>,
1078    pub endpoint: EndpointDeclaration,
1079    pub readiness: ReadinessProbe,
1080    #[serde(default)]
1081    pub ports: Vec<String>,
1082    pub targets: Vec<GatewayTarget>,
1083    #[serde(default)]
1084    pub render_inputs: Vec<RenderInputDeclaration>,
1085    pub render_source: RenderSource,
1086    pub co_rendering: FrontendCoRendering,
1087}
1088
1089/// Independent policies for choosing prefill and decode targets.
1090#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
1091#[serde(deny_unknown_fields)]
1092pub struct PdRoutingPolicies {
1093    pub prefill: String,
1094    pub decode: String,
1095}
1096
1097/// The currently demonstrated Gateway-to-P/D Router handoff is internal to
1098/// one fused frontend process.
1099#[derive(Clone, Copy, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
1100#[serde(rename_all = "snake_case")]
1101pub enum FrontendHandoff {
1102    InProcess,
1103}
1104
1105/// The P/D orchestration component plan, kept separate even when the same
1106/// process and provider also realize Gateway.
1107#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
1108#[serde(deny_unknown_fields)]
1109pub struct PdRouterPlan {
1110    pub backend: String,
1111    pub implementation: String,
1112    pub implementation_version: String,
1113    pub effective_settings: BTreeMap<String, SettingValue>,
1114    pub policies: PdRoutingPolicies,
1115    pub prefill_role: String,
1116    pub decode_role: String,
1117    pub target_scheme: TargetEndpointScheme,
1118    #[serde(default)]
1119    pub ports: Vec<String>,
1120    pub readiness: ReadinessProbe,
1121    pub handoff: FrontendHandoff,
1122    #[serde(default)]
1123    pub render_inputs: Vec<RenderInputDeclaration>,
1124    pub render_source: RenderSource,
1125    pub co_rendering: FrontendCoRendering,
1126}
1127
1128/// The application protocol used to identify serving targets in a registry.
1129#[derive(Clone, Copy, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
1130#[serde(rename_all = "snake_case")]
1131pub enum TargetEndpointScheme {
1132    /// HTTP serving endpoint.
1133    Http,
1134    /// gRPC serving endpoint.
1135    Grpc,
1136}
1137
1138// Measurement-client shared base.
1139
1140/// The model identity used by measurement clients. Unlike integration
1141/// planning, a benchmark client may need a controller-visible tokenizer
1142/// locator.
1143#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
1144#[serde(deny_unknown_fields)]
1145pub struct MeasurementModelInput {
1146    pub locator: String,
1147    pub served_name: String,
1148}
1149
1150/// The public workload endpoint an Eval or Bench client connects to.
1151#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
1152#[serde(deny_unknown_fields)]
1153pub struct ClientEndpointInput {
1154    pub protocol: EndpointProtocol,
1155    pub host: String,
1156    pub port: u16,
1157    pub completions_path: String,
1158    pub chat_completions_path: String,
1159    #[serde(default)]
1160    pub server_metrics: Option<ServerMetricsEndpointInput>,
1161    #[serde(default)]
1162    pub prompt_cache_read_zero_representation: Option<PromptCacheReadZeroRepresentation>,
1163}
1164
1165/// The resolved server-metrics endpoint supplied to a measurement client.
1166#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
1167#[serde(deny_unknown_fields)]
1168pub struct ServerMetricsEndpointInput {
1169    pub path: String,
1170    #[serde(default)]
1171    pub port_name: Option<String>,
1172    pub url: String,
1173}
1174
1175/// How a backend with cache reporting enabled represents a zero-token cache read.
1176#[derive(Clone, Copy, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
1177#[serde(rename_all = "snake_case")]
1178pub enum PromptCacheReadZeroRepresentation {
1179    Explicit,
1180    Omitted,
1181}
1182
1183/// The terminal outcome a measurement client reports.
1184#[derive(Clone, Copy, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
1185#[serde(rename_all = "snake_case")]
1186pub enum ClientStatus {
1187    /// The client completed its measurement successfully.
1188    Succeeded,
1189    /// The client did not complete successfully.
1190    Failed,
1191}
1192
1193/// A raw output file a client produced, retained as workload evidence.
1194#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
1195#[serde(deny_unknown_fields)]
1196pub struct RawArtifact {
1197    pub name: String,
1198    pub kind: String,
1199    pub path: PathBuf,
1200}
1201
1202// Eval client.
1203
1204/// The request the Eval measurement runtime passes to its client: the endpoint
1205/// to hit, the model, the eval definition, and where to write artifacts.
1206#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
1207#[serde(deny_unknown_fields)]
1208pub struct EvalClientRequest {
1209    pub protocol_version: ProtocolVersion,
1210    pub workspace_root: PathBuf,
1211    pub workspace_source_exclusions: Vec<PathBuf>,
1212    pub endpoint: ClientEndpointInput,
1213    pub model: MeasurementModelInput,
1214    pub definition: EvalDefinitionInput,
1215    #[serde(default)]
1216    pub prepared_source: Option<EvalPreparedSourceBinding>,
1217    /// Remaining control-plane case budget when the client is released.
1218    pub case_budget_seconds: f64,
1219    pub artifact_dir: PathBuf,
1220}
1221
1222/// The measurement an Eval client runs against the workload endpoint.
1223#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
1224#[serde(tag = "kind", rename_all = "snake_case", deny_unknown_fields)]
1225pub enum EvalDefinitionInput {
1226    /// A single-prompt liveness check.
1227    #[serde(rename = "openai_smoke")]
1228    OpenAiSmoke {
1229        prompt: String,
1230        max_tokens: u32,
1231        timeout_seconds: u64,
1232        /// The effective vision-mode selection
1233        /// ([[RFC-0004:C-MEASUREMENTS]]): a vision smoke routes to chat
1234        /// completions carrying the release-owned fixed test image.
1235        #[serde(default)]
1236        vision: bool,
1237    },
1238    /// An lm-eval task run with a pass threshold on the chosen metric.
1239    LmEval {
1240        task: Box<EvalTaskSourceInput>,
1241        /// The prompt rendering authority resolution selected.
1242        prompt: EvalPromptInput,
1243        /// The authority the definition declared, absent when it was defaulted.
1244        #[serde(default)]
1245        declared_prompt: Option<EvalPromptInput>,
1246        #[serde(default)]
1247        request_body: BTreeMap<String, SettingValue>,
1248        limit: Option<u32>,
1249        few_shot: Option<u32>,
1250        /// The seed the definition declared, absent when it omitted one; a
1251        /// single trial with no declared seed does not invent one.
1252        seed: Option<u64>,
1253        /// The effective base seed of the trial schedule, resolved by the
1254        /// control plane: the declared seed, or the shared fallback when the
1255        /// definition declared none ([[RFC-0004:C-LM-EVAL]]).
1256        base_seed: u64,
1257        trials: u32,
1258        max_tokens: Option<u32>,
1259        concurrency: Option<u32>,
1260        metric: String,
1261        #[serde(default)]
1262        metric_filter: Option<String>,
1263        threshold: f64,
1264        timeout_seconds: u64,
1265    },
1266}
1267
1268/// The prompt rendering authority for a generative lm-eval definition.
1269///
1270/// The authority determines the protocol route and the release-pinned lm-eval
1271/// client; the runner rejects an authority the resolved task output type does
1272/// not permit.
1273#[derive(Clone, Copy, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
1274#[serde(tag = "kind", rename_all = "snake_case", deny_unknown_fields)]
1275pub enum EvalPromptInput {
1276    /// Ordinary text on the completions path with no chat construction.
1277    Flat,
1278    /// Structured messages on the chat-completions path, rendered by the server.
1279    ServerChat,
1280}
1281
1282/// The resolved lm-eval task source consumed by the release-owned Eval runner.
1283#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
1284#[serde(tag = "kind", rename_all = "snake_case", deny_unknown_fields)]
1285pub enum EvalTaskSourceInput {
1286    /// One individual task shipped by the pinned lm-eval runtime.
1287    BuiltIn { name: String },
1288    /// One task closure shipped and identity-bound by the Inferlab release.
1289    Bundled {
1290        name: String,
1291        task_identity: String,
1292        path: PathBuf,
1293        task_closure_sha256: String,
1294        task_definition_sha256: String,
1295        prompt_asset_sha256: String,
1296        dataset_asset_sha256: String,
1297        scorer_sha256: String,
1298    },
1299    /// One validated workspace-local lm-eval YAML file.
1300    WorkspaceYaml { path: PathBuf },
1301}
1302
1303/// The result an Eval client writes for the measurement runtime to consume.
1304#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
1305#[serde(deny_unknown_fields)]
1306pub struct EvalClientResult {
1307    /// Result envelope version; clients write `1`. The measurement runtime
1308    /// rejects an eval result whose version is not `1`.
1309    pub schema_version: u32,
1310    pub status: ClientStatus,
1311    pub metrics: BTreeMap<String, f64>,
1312    #[serde(default)]
1313    pub normalized_metrics: BTreeMap<String, EvalNormalizedMetric>,
1314    #[serde(default)]
1315    pub gate: Option<EvalMetricGate>,
1316    #[serde(default)]
1317    pub trial_summary: Option<EvalTrialSummary>,
1318    pub native_command: Vec<String>,
1319    #[serde(default)]
1320    pub native_exit_code: Option<i32>,
1321    #[serde(default)]
1322    pub native_timed_out: bool,
1323    pub raw_artifacts: Vec<RawArtifact>,
1324    pub failure_kind: Option<EvalFailureKind>,
1325    pub error: Option<String>,
1326}
1327
1328/// A typed Eval failure category preserved across the client boundary.
1329#[derive(Clone, Copy, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
1330#[serde(rename_all = "snake_case")]
1331pub enum EvalFailureKind {
1332    TaskResolution,
1333    ProbeTokenizer,
1334    ProbeTransport,
1335    ProbeHttp,
1336    ProbeMalformedResponse,
1337    ProbeGeneratedOnlyLogprobs,
1338    ProbeTokenizerAlignment,
1339    MetricNormalization,
1340}
1341
1342/// The threshold comparison selected from lm-eval's scoring direction.
1343#[derive(Clone, Copy, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
1344#[serde(rename_all = "snake_case")]
1345pub enum EvalMetricComparison {
1346    AtLeast,
1347    AtMost,
1348}
1349
1350/// The terminal conclusion of an lm-eval metric threshold gate.
1351#[derive(Clone, Copy, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
1352#[serde(rename_all = "snake_case")]
1353pub enum EvalMetricGateConclusion {
1354    Passed,
1355    Failed,
1356}
1357
1358/// One finite lm-eval metric with its exact native provenance.
1359#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
1360#[serde(deny_unknown_fields)]
1361pub struct EvalNormalizedMetric {
1362    pub source_identity: String,
1363    pub metric: String,
1364    pub filter: Option<String>,
1365    pub native_metric_key: String,
1366    pub value: f64,
1367    pub higher_is_better: bool,
1368    /// The prompt rendering authority that produced this value. The same task
1369    /// scored under another authority is a different measurement, so the value
1370    /// never travels without it.
1371    pub prompt_authority: EvalPromptInput,
1372}
1373
1374/// The effective threshold comparison for the configured primary metric.
1375#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
1376#[serde(deny_unknown_fields)]
1377pub struct EvalMetricGate {
1378    pub metric: EvalNormalizedMetric,
1379    pub threshold: f64,
1380    pub comparison: EvalMetricComparison,
1381    pub conclusion: EvalMetricGateConclusion,
1382}
1383
1384/// Reconstructible aggregate counts for fixed outer Eval trials.
1385#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
1386#[serde(deny_unknown_fields)]
1387pub struct EvalTrialSummary {
1388    pub requested_trials: u32,
1389    pub issued_trials: u32,
1390    pub unissued_trials: u32,
1391    pub completed_trials: u32,
1392    pub request_failure_trials: u32,
1393    pub passed_trials: u32,
1394    pub pass_rate: Option<f64>,
1395    pub per_trial_metric: String,
1396    pub per_trial_filter: Option<String>,
1397    pub higher_is_better: bool,
1398}
1399
1400// Bench client.
1401
1402/// The request the Bench measurement runtime passes to its client: the
1403/// endpoint, model, bench definition, the load case to run, and the artifact
1404/// directory.
1405#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
1406#[serde(deny_unknown_fields)]
1407pub struct BenchClientRequest {
1408    pub protocol_version: ProtocolVersion,
1409    pub endpoint: ClientEndpointInput,
1410    pub model: MeasurementModelInput,
1411    pub definition: BenchDefinitionInput,
1412    #[serde(default)]
1413    pub population: Option<BenchPopulationInput>,
1414    pub case: BenchCaseInput,
1415    /// Remaining control-plane case budget when the client is released.
1416    pub case_budget_seconds: f64,
1417    pub artifact_dir: PathBuf,
1418}
1419
1420/// The workload shape a Bench client drives, shared across its load cases.
1421#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
1422#[serde(deny_unknown_fields)]
1423pub struct BenchDefinitionInput {
1424    #[serde(default)]
1425    pub request_source: Option<BenchRequestSourceInput>,
1426    #[serde(default)]
1427    pub session_source: Option<BenchSessionSourceInput>,
1428    #[serde(default)]
1429    pub agentic_source: Option<BenchAgenticSourceInput>,
1430    pub prompt: BenchPromptInput,
1431    #[serde(default)]
1432    pub server_metrics: bool,
1433    #[serde(default)]
1434    pub artifact_level: BenchArtifactLevelInput,
1435    pub seed: u64,
1436    #[serde(default)]
1437    pub request_body: BTreeMap<String, SettingValue>,
1438    #[serde(default)]
1439    pub request_slo: Option<BenchRequestSloInput>,
1440    pub timeout_seconds: u64,
1441    pub cache_start: BenchCacheStartInput,
1442}
1443
1444/// The artifact level a Bench case requests from the measurement runtime.
1445/// Omission resolves to `diagnostic`, which retains the full raw
1446/// request/response export; `performance` keeps only normalized per-request
1447/// records and the summary export ([[RFC-0004:C-BENCH-ARTIFACT-LEVEL]]).
1448#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
1449#[serde(rename_all = "snake_case")]
1450pub enum BenchArtifactLevelInput {
1451    Performance,
1452    #[default]
1453    Diagnostic,
1454}
1455
1456#[derive(Clone, Copy, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
1457#[serde(rename_all = "snake_case")]
1458pub enum BenchCacheStartInput {
1459    Uncontrolled,
1460    Cold,
1461    Primed,
1462}
1463
1464/// The frozen prompt representation, route, and rendering authority for a
1465/// serving Bench population.
1466#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
1467#[serde(tag = "kind", rename_all = "snake_case", deny_unknown_fields)]
1468pub enum BenchPromptInput {
1469    Flat {
1470        request_representation: BenchRequestRepresentationInput,
1471        route: BenchPromptRouteInput,
1472        rendering_authority: BenchRenderingAuthorityInput,
1473    },
1474    RenderedChat {
1475        #[serde(default)]
1476        chat_template: Option<String>,
1477        #[serde(default)]
1478        chat_template_kwargs: BTreeMap<String, SettingValue>,
1479        request_representation: BenchRequestRepresentationInput,
1480        route: BenchPromptRouteInput,
1481        rendering_authority: BenchRenderingAuthorityInput,
1482    },
1483    ServerChat {
1484        request_representation: BenchRequestRepresentationInput,
1485        route: BenchPromptRouteInput,
1486        rendering_authority: BenchRenderingAuthorityInput,
1487    },
1488}
1489
1490/// The request payload representation frozen by prompt resolution.
1491#[derive(Clone, Copy, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
1492#[serde(rename_all = "snake_case")]
1493pub enum BenchRequestRepresentationInput {
1494    FlatPrompt,
1495    StructuredMessages,
1496}
1497
1498/// The named OpenAI-compatible endpoint family derived from prompt authority.
1499#[derive(Clone, Copy, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
1500#[serde(rename_all = "snake_case")]
1501pub enum BenchPromptRouteInput {
1502    Completions,
1503    ChatCompletions,
1504}
1505
1506/// The owner that turns semantic prompt input into the final prompt tokens.
1507#[derive(Clone, Copy, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
1508#[serde(rename_all = "snake_case")]
1509pub enum BenchRenderingAuthorityInput {
1510    LocalFlat,
1511    LocalTemplate,
1512    Server,
1513}
1514
1515/// One closed request origin lowered by Inferlab for the Bench runtime.
1516#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
1517#[serde(tag = "kind", rename_all = "snake_case", deny_unknown_fields)]
1518pub enum BenchRequestSourceInput {
1519    /// AIPerf generates exact token-shape prompts from the release-pinned
1520    /// synthetic generator.
1521    Random {
1522        input_tokens: BenchTokenSelectorInput,
1523        output_tokens: BenchTokenSelectorInput,
1524        #[serde(default)]
1525        prefix_sharing: Option<BenchPrefixSharingInput>,
1526        #[serde(default)]
1527        shared_system_content: Option<BenchSharedSystemContentInput>,
1528        #[serde(default)]
1529        corpus: Option<BenchCorpusInput>,
1530        /// The effective per-request image decoration
1531        /// ([[RFC-0004:C-BENCH-REQUEST-SOURCES]]); absent when the source
1532        /// declares no images.
1533        #[serde(default)]
1534        images: Option<BenchImagesInput>,
1535    },
1536    /// AIPerf samples exact token-shape pairs from one seeded categorical
1537    /// distribution.
1538    RandomMixture {
1539        shapes: Vec<BenchRandomShapeInput>,
1540        total_weight: u64,
1541        #[serde(default)]
1542        prefix_sharing: Option<BenchPrefixSharingInput>,
1543    },
1544    /// Inferlab materializes a release-catalog conversation snapshot before
1545    /// AIPerf starts.
1546    Dataset {
1547        dataset: String,
1548        #[serde(default)]
1549        profile: Option<String>,
1550        max_input_tokens: u32,
1551        output_tokens: Option<u32>,
1552        catalog: Box<BenchDatasetCatalogInput>,
1553    },
1554    /// Inferlab replays one workspace-local frozen population file unchanged;
1555    /// the file bytes are the sole population authority
1556    /// ([[RFC-0004:C-BENCH-REQUEST-SOURCES]]).
1557    Replay {
1558        /// Workspace-relative population path as declared. The preparation
1559        /// request's `source_path` carries its absolute resolution.
1560        path: String,
1561        #[serde(default)]
1562        expected_sha256: Option<String>,
1563        #[serde(default)]
1564        prefix_sharing: Option<BenchPrefixSharingInput>,
1565    },
1566}
1567
1568/// One fixed or bounded inclusive-uniform token-length selector.
1569#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
1570#[serde(untagged)]
1571pub enum BenchTokenSelectorInput {
1572    Fixed(u32),
1573    InclusiveUniform(BenchInclusiveUniformInput),
1574}
1575
1576/// The one bounded distribution currently supported for a token selector.
1577#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
1578#[serde(deny_unknown_fields)]
1579pub struct BenchInclusiveUniformInput {
1580    pub kind: BenchTokenDistributionKindInput,
1581    pub min: u32,
1582    pub max: u32,
1583}
1584
1585#[derive(Clone, Copy, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
1586#[serde(rename_all = "snake_case")]
1587pub enum BenchTokenDistributionKindInput {
1588    InclusiveUniform,
1589}
1590
1591/// One declared exact final-prompt prefix geometry.
1592#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
1593#[serde(untagged)]
1594pub enum BenchPrefixSharingInput {
1595    Tokens { shared_prefix_tokens: u32 },
1596    Ratio { shared_prefix_ratio: f64 },
1597}
1598
1599/// One pre-template shared system-content declaration for server-rendered
1600/// synthetic chat.
1601#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
1602#[serde(untagged)]
1603pub enum BenchSharedSystemContentInput {
1604    Tokens { tokens: u32 },
1605    Ratio { ratio: f64 },
1606}
1607
1608/// One operator-supplied text corpus binding for the random request source;
1609/// entry content is drawn as exact token-length slices of the corpus token
1610/// stream ([[RFC-0004:C-BENCH-REQUEST-SOURCES]]).
1611#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
1612#[serde(deny_unknown_fields)]
1613pub struct BenchCorpusInput {
1614    /// Workspace-relative corpus path as declared. The preparation request's
1615    /// `source_path` carries its absolute resolution.
1616    pub path: String,
1617    #[serde(default)]
1618    pub expected_sha256: Option<String>,
1619}
1620
1621/// The effective per-request image decoration of a random request source
1622/// ([[RFC-0004:C-BENCH-REQUEST-SOURCES]]): fixed pixel dimensions and the
1623/// per-request image count. Images attach at measurement-run time on the
1624/// chat-completions route and never enter the frozen population.
1625#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
1626#[serde(deny_unknown_fields)]
1627pub struct BenchImagesInput {
1628    /// The fixed image width in pixels.
1629    pub width: u32,
1630    /// The fixed image height in pixels.
1631    pub height: u32,
1632    /// The effective number of images attached to each request.
1633    pub count: u32,
1634    /// The image supply; absent selects the measurement runtime's synthetic
1635    /// noise image supply.
1636    #[serde(default)]
1637    pub source: Option<BenchImageSourceInput>,
1638}
1639
1640/// One operator-supplied image directory binding for the random request
1641/// source's image decoration ([[RFC-0004:C-BENCH-REQUEST-SOURCES]]).
1642#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
1643#[serde(deny_unknown_fields)]
1644pub struct BenchImageSourceInput {
1645    /// Workspace-relative directory path as declared.
1646    pub path: String,
1647    /// Absolute resolution of `path` against the workspace root.
1648    pub resolved_path: PathBuf,
1649    /// The declared digest binding over the release-owned recursive
1650    /// enumeration digest of the directory.
1651    #[serde(default)]
1652    pub expected_sha256: Option<String>,
1653    /// The effective source-image sampling policy.
1654    pub sampling: BenchImageSamplingInput,
1655}
1656
1657/// The closed source-image sampling vocabulary
1658/// ([[RFC-0004:C-BENCH-REQUEST-SOURCES]]).
1659#[derive(Clone, Copy, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
1660#[serde(rename_all = "kebab-case")]
1661pub enum BenchImageSamplingInput {
1662    /// Draw each source image independently; repeats may occur immediately.
1663    RandomWithReplacement,
1664    /// Draw every source image once per shuffled cycle, reshuffling after
1665    /// exhaustion.
1666    ShuffleCycle,
1667    /// Walk source images in sorted load order and wrap after exhaustion.
1668    SequentialCycle,
1669}
1670
1671/// One exact ISL/OSL pair and its relative categorical sampling weight.
1672#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
1673#[serde(deny_unknown_fields)]
1674pub struct BenchRandomShapeInput {
1675    pub input_tokens: u32,
1676    pub output_tokens: u32,
1677    pub weight: u32,
1678}
1679
1680/// Immutable release-catalog facts resolved by the Rust control plane.
1681#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
1682#[serde(deny_unknown_fields)]
1683pub struct BenchDatasetCatalogInput {
1684    pub dataset: String,
1685    #[serde(default)]
1686    pub profile: Option<String>,
1687    pub source: String,
1688    pub upstream_identity: String,
1689    pub url: String,
1690    pub sha256: String,
1691    pub source_format: String,
1692    pub aiperf_format: String,
1693    #[serde(default)]
1694    pub configuration: Option<String>,
1695    #[serde(default)]
1696    pub split: Option<String>,
1697    #[serde(default)]
1698    pub filter: Option<BenchDatasetFilterInput>,
1699    pub license: String,
1700    pub cache_path: PathBuf,
1701    pub cache_state: BenchDatasetCacheState,
1702    pub materialization_identity: String,
1703    pub provides_output_targets: bool,
1704}
1705
1706#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
1707#[serde(deny_unknown_fields)]
1708pub struct BenchDatasetFilterInput {
1709    pub field: String,
1710    pub value: String,
1711}
1712
1713/// Read-only cache state observed while resolving the Bench plan.
1714#[derive(Clone, Copy, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
1715#[serde(rename_all = "snake_case")]
1716pub enum BenchDatasetCacheState {
1717    Missing,
1718    Present,
1719}
1720
1721/// One release-qualified population of dependent linear session templates.
1722#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
1723#[serde(deny_unknown_fields)]
1724pub struct BenchSessionSourceInput {
1725    pub dataset: String,
1726    #[serde(default)]
1727    pub profile: Option<String>,
1728    pub max_input_tokens: u32,
1729    pub output_tokens: Option<u32>,
1730    pub inter_turn_delay_scale: f64,
1731    pub max_inter_turn_delay_seconds: Option<f64>,
1732    pub catalog: Box<BenchSessionDatasetCatalogInput>,
1733}
1734
1735/// Immutable release-catalog facts for a linear-session materializer. AIPerf
1736/// loader names are deliberately absent from this source boundary.
1737#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
1738#[serde(deny_unknown_fields)]
1739pub struct BenchSessionDatasetCatalogInput {
1740    pub dataset: String,
1741    #[serde(default)]
1742    pub profile: Option<String>,
1743    pub source: String,
1744    pub upstream_identity: String,
1745    pub url: String,
1746    pub sha256: String,
1747    pub source_format: String,
1748    #[serde(default)]
1749    pub configuration: Option<String>,
1750    #[serde(default)]
1751    pub split: Option<String>,
1752    #[serde(default)]
1753    pub filter: Option<BenchDatasetFilterInput>,
1754    pub license: String,
1755    pub cache_path: PathBuf,
1756    pub cache_state: BenchDatasetCacheState,
1757    pub materialization_identity: String,
1758    pub provides_output_targets: bool,
1759}
1760
1761/// One release-qualified agentic trace source and its complete effective policy.
1762#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
1763#[serde(deny_unknown_fields)]
1764pub struct BenchAgenticSourceInput {
1765    pub dataset: String,
1766    pub profile: String,
1767    pub catalog: Box<BenchAgenticCatalogInput>,
1768}
1769
1770#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
1771#[serde(deny_unknown_fields)]
1772pub struct BenchAgenticCatalogInput {
1773    pub repository: String,
1774    pub revision: String,
1775    pub filename: String,
1776    pub sha256: String,
1777    #[serde(default)]
1778    pub cache_path: Option<PathBuf>,
1779    #[serde(default)]
1780    pub cache_state: Option<BenchDatasetCacheState>,
1781    pub trace_count: u32,
1782    pub approximate_bytes: u64,
1783    pub license: String,
1784    pub source_format: String,
1785    pub aiperf_loader: String,
1786    pub materialization_identity: String,
1787    pub scenario: String,
1788    pub concurrency_semantics: String,
1789    pub replay_semantics: String,
1790    pub cache_bust: String,
1791    pub trajectory_start_min: f64,
1792    pub trajectory_start_max: f64,
1793    pub global_idle_gap_cap_seconds: f64,
1794    pub trace_idle_gap_cap_seconds: f64,
1795    pub cache_warmup_requests_per_lane: u64,
1796    pub warmup_grace_seconds: u64,
1797    pub dataset_configuration_timeout_seconds: u64,
1798    pub service_profile_configuration_timeout_seconds: u64,
1799    pub default_duration_seconds: u64,
1800    pub minimum_duration_seconds: u64,
1801    pub failure_threshold: f64,
1802    pub dataset_entries: u32,
1803    pub streaming: bool,
1804    pub ignore_eos: bool,
1805    pub use_server_token_count: bool,
1806    pub gpu_telemetry: bool,
1807    pub server_metric_slice_seconds: u64,
1808    pub required_artifacts: Vec<String>,
1809    pub unavailable_dimensions: Vec<String>,
1810    pub inferencex_repository: String,
1811    pub inferencex_revision: String,
1812    pub inferencex_reference: String,
1813    pub aiperf_revision: String,
1814    pub aiperf_version: String,
1815}
1816
1817/// A single Bench case: its load shape and the number of requests to send.
1818#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
1819#[serde(deny_unknown_fields)]
1820pub struct BenchCaseInput {
1821    pub load_shape: BenchLoadInput,
1822    pub request_count: u32,
1823    #[serde(default)]
1824    pub warmup_request_count: u32,
1825    #[serde(default)]
1826    pub duration_seconds: Option<u64>,
1827    #[serde(default)]
1828    pub session_count: Option<u32>,
1829    #[serde(default)]
1830    pub warmup_session_count: Option<u32>,
1831}
1832
1833/// How a Bench case paces its requests.
1834#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
1835#[serde(tag = "kind", rename_all = "snake_case", deny_unknown_fields)]
1836pub enum BenchLoadInput {
1837    /// A fixed number of in-flight requests.
1838    ConcurrencyLimited { concurrency: u32 },
1839    /// A target arrival rate, optionally shaped by a burstiness factor.
1840    RequestRateLimited {
1841        request_rate: f64,
1842        burstiness: Option<f64>,
1843    },
1844    /// All requests issued as fast as possible.
1845    UnboundedRequestRate,
1846}
1847
1848/// Per-request latency bounds lowered to the release-owned Bench runner.
1849#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
1850#[serde(deny_unknown_fields)]
1851pub struct BenchRequestSloInput {
1852    #[serde(default)]
1853    pub request_latency_ms: Option<f64>,
1854    #[serde(default)]
1855    pub ttft_ms: Option<f64>,
1856    #[serde(default)]
1857    pub tpot_ms: Option<f64>,
1858    pub minimum_good_request_ratio: f64,
1859}
1860
1861/// One frozen dataset population consumed sequentially by every Bench case.
1862#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
1863#[serde(deny_unknown_fields)]
1864pub struct BenchPopulationInput {
1865    pub path: PathBuf,
1866    pub evidence_path: PathBuf,
1867    pub sha256: String,
1868    pub entries: u32,
1869    pub tpot_applicable: bool,
1870    #[serde(default)]
1871    pub session_templates: Vec<BenchSessionTemplateInput>,
1872}
1873
1874/// One frozen linear-template summary needed to assign case slices without
1875/// duplicating the content-bearing population evidence artifact.
1876#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
1877#[serde(deny_unknown_fields)]
1878pub struct BenchSessionTemplateInput {
1879    pub template_identity: String,
1880    pub turn_count: u32,
1881}
1882
1883/// The bounded tokenizer-backed operation that freezes one request population
1884/// before any Bench case starts.
1885#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
1886#[serde(deny_unknown_fields)]
1887pub struct BenchPopulationPreparationRequest {
1888    pub protocol_version: ProtocolVersion,
1889    pub model: MeasurementModelInput,
1890    pub tokenizer_backend: String,
1891    pub transformers_version: String,
1892    #[serde(default)]
1893    pub request_source: Option<BenchRequestSourceInput>,
1894    #[serde(default)]
1895    pub session_source: Option<BenchSessionSourceInput>,
1896    pub prompt: BenchPromptInput,
1897    pub cache_start: BenchCacheStartInput,
1898    #[serde(default)]
1899    pub source_path: Option<PathBuf>,
1900    pub required_entries: u32,
1901    pub seed: u64,
1902    #[serde(default)]
1903    pub request_body: BTreeMap<String, SettingValue>,
1904    pub artifact_dir: PathBuf,
1905}
1906
1907/// Summary of the realized token counts. Exact per-entry values remain in the
1908/// population evidence artifact.
1909#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
1910#[serde(deny_unknown_fields)]
1911pub struct BenchTokenCountSummary {
1912    pub minimum: u32,
1913    pub maximum: u32,
1914    pub mean: f64,
1915}
1916
1917/// Where the concrete template used for local prompt-length projection came
1918/// from. The model server remains authoritative for transport rendering.
1919#[derive(Clone, Copy, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
1920#[serde(rename_all = "snake_case")]
1921pub enum BenchPromptTemplateSource {
1922    RequestBody,
1923    PromptTable,
1924    TokenizerDefault,
1925}
1926
1927/// The concrete template used only for local complete-prompt projection.
1928#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
1929#[serde(deny_unknown_fields)]
1930pub struct BenchPromptTemplateProjection {
1931    pub source: BenchPromptTemplateSource,
1932    pub content: String,
1933    pub sha256: String,
1934}
1935
1936/// Summary of best-effort complete-prompt targeting for a synthetic population.
1937#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
1938#[serde(deny_unknown_fields)]
1939pub struct BenchPromptTokenTargetingSummary {
1940    pub selected_prompt_tokens: BenchTokenCountSummary,
1941    pub pre_template_content_tokens: BenchTokenCountSummary,
1942    #[serde(default)]
1943    pub projection_template: Option<BenchPromptTemplateProjection>,
1944    pub exact_entries: u32,
1945    pub fallback_entries: u32,
1946    #[serde(default)]
1947    pub fallback_reasons: BTreeMap<String, u32>,
1948}
1949
1950/// Resolved exact final-prompt prefix geometry across one frozen population.
1951#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
1952#[serde(deny_unknown_fields)]
1953pub struct BenchPrefixGeometrySummary {
1954    pub shared_prefix_tokens: BenchTokenCountSummary,
1955    pub unique_suffix_tokens: BenchTokenCountSummary,
1956    pub maximum_shared_prefix_tokens: u32,
1957    pub canonical_prefix_sha256: String,
1958    pub full_prompt_entries: u32,
1959}
1960
1961/// File-bound exact canonical prefix used to condition a primed cache start.
1962#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
1963#[serde(deny_unknown_fields)]
1964pub struct BenchPrefixConditioningInput {
1965    pub path: PathBuf,
1966    pub sha256: String,
1967    pub prompt_tokens: u32,
1968}
1969
1970/// Resolved pre-template shared system-content shape across one frozen
1971/// server-chat population.
1972#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
1973#[serde(deny_unknown_fields)]
1974pub struct BenchSharedSystemContentSummary {
1975    pub system_content_tokens: BenchTokenCountSummary,
1976    pub user_content_tokens: BenchTokenCountSummary,
1977    pub canonical_system_content_sha256: String,
1978}
1979
1980/// Terminal result of request-population materialization.
1981#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
1982#[serde(deny_unknown_fields)]
1983pub struct BenchPopulationPreparationResult {
1984    pub schema_version: u32,
1985    pub status: ClientStatus,
1986    pub materialization_identity: String,
1987    pub requested_entries: u32,
1988    pub candidate_entries: u64,
1989    pub admitted_entries: u64,
1990    pub ineligible_entries: u64,
1991    #[serde(default)]
1992    pub ineligible_reasons: BTreeMap<String, u64>,
1993    pub population: Option<BenchPopulationInput>,
1994    pub input_tokens: Option<BenchTokenCountSummary>,
1995    pub output_tokens: Option<BenchTokenCountSummary>,
1996    #[serde(default)]
1997    pub prompt_token_targeting: Option<BenchPromptTokenTargetingSummary>,
1998    #[serde(default)]
1999    pub prefix_geometry: Option<BenchPrefixGeometrySummary>,
2000    #[serde(default)]
2001    pub prefix_conditioning: Option<BenchPrefixConditioningInput>,
2002    #[serde(default)]
2003    pub shared_system_content: Option<BenchSharedSystemContentSummary>,
2004    pub evidence_path: Option<PathBuf>,
2005    pub error: Option<String>,
2006}
2007
2008/// The result a Bench client writes for the measurement runtime to consume.
2009#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
2010#[serde(deny_unknown_fields)]
2011pub struct BenchClientResult {
2012    /// Result envelope version; clients write `1`. The measurement runtime
2013    /// rejects a bench result whose version is not `1`.
2014    pub schema_version: u32,
2015    pub status: ClientStatus,
2016    pub completed_requests: u64,
2017    pub failed_requests: u64,
2018    pub normalization_schema: String,
2019    pub metrics: BTreeMap<String, f64>,
2020    #[serde(default)]
2021    pub request_slo: Option<BenchRequestSloResult>,
2022    #[serde(default)]
2023    pub session_evidence: Option<BenchSessionResultEvidence>,
2024    #[serde(default)]
2025    pub agentic_evidence: Option<Box<BenchAgenticResultEvidence>>,
2026    #[serde(default)]
2027    pub prompt_token_reconciliation: Vec<BenchPromptTokenReconciliation>,
2028    #[serde(default)]
2029    pub prompt_cache_observations: Vec<BenchPromptCacheObservation>,
2030    pub native_command: Vec<String>,
2031    pub native_exit_code: Option<i32>,
2032    #[serde(default)]
2033    pub report_invocations: Vec<BenchNativeInvocation>,
2034    pub raw_artifacts: Vec<RawArtifact>,
2035    pub error: Option<String>,
2036}
2037
2038/// Backend-reported prompt/cache token accounting for one completed profiling request.
2039#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
2040#[serde(deny_unknown_fields)]
2041pub struct BenchPromptCacheObservation {
2042    pub request_id: u64,
2043    pub prompt_tokens: u64,
2044    pub cache_read_tokens: u64,
2045    pub uncached_prompt_tokens: u64,
2046    pub cache_read_ratio: f64,
2047}
2048
2049#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
2050#[serde(deny_unknown_fields)]
2051pub struct BenchAgenticSourceVerification {
2052    pub repository: String,
2053    pub expected_revision: String,
2054    #[serde(default)]
2055    pub observed_revision: Option<String>,
2056    pub filename: String,
2057    pub expected_sha256: String,
2058    #[serde(default)]
2059    pub observed_sha256: Option<String>,
2060    #[serde(default)]
2061    pub cache_path: Option<PathBuf>,
2062    #[serde(default)]
2063    pub cache_state_before: Option<BenchDatasetCacheState>,
2064    #[serde(default)]
2065    pub acquisition_outcome: Option<BenchAgenticAcquisitionOutcome>,
2066}
2067
2068#[derive(Clone, Copy, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
2069#[serde(rename_all = "snake_case")]
2070pub enum BenchAgenticAcquisitionOutcome {
2071    Reused,
2072    Downloaded,
2073}
2074
2075#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
2076#[serde(deny_unknown_fields)]
2077pub struct BenchAgenticBranchStats {
2078    pub children_spawned: u64,
2079    pub children_completed: u64,
2080    pub children_errored: u64,
2081    pub children_truncated: u64,
2082    pub children_delayed: u64,
2083    pub parents_suspended: u64,
2084    pub parents_resumed: u64,
2085    pub parents_failed_due_to_child_error: u64,
2086    pub joins_suppressed: u64,
2087}
2088
2089#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
2090#[serde(deny_unknown_fields)]
2091pub struct BenchAgenticResultEvidence {
2092    pub source: BenchAgenticSourceVerification,
2093    #[serde(default)]
2094    pub run: Option<Box<BenchAgenticRunEvidence>>,
2095}
2096
2097#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
2098#[serde(deny_unknown_fields)]
2099pub struct BenchAgenticRunEvidence {
2100    pub native_run_id: String,
2101    pub scenario: String,
2102    pub submission_valid: bool,
2103    #[serde(default)]
2104    pub submission_invalid_reasons: Vec<String>,
2105    pub warmup_records: u64,
2106    pub warmup_error_records: u64,
2107    /// Whether the native run crossed the warmup phase and entered profiling
2108    /// (`profiling_records > 0`); snapshot-warmup failure aborts natively
2109    /// before profiling and therefore surfaces as an invalid submission.
2110    pub warmup_succeeded: bool,
2111    pub profiling_records: u64,
2112    pub distinct_runtime_conversations: u64,
2113    pub distinct_transport_requests: u64,
2114    pub context_overflow_count: u64,
2115    pub ordinary_failure_count: u64,
2116    pub branch_stats: BenchAgenticBranchStats,
2117    pub aggregate_artifact: PathBuf,
2118    /// The raw request/response artifact; absent at the `performance`
2119    /// artifact level, where raw export is not requested.
2120    #[serde(default)]
2121    pub raw_records_artifact: Option<PathBuf>,
2122    #[serde(default)]
2123    pub unavailable_dimensions: Vec<String>,
2124    /// Warmup records whose raw-derived source coordinates were observed;
2125    /// absent when the artifact level makes that mapping unavailable.
2126    #[serde(default)]
2127    pub warmup_source_coordinate_records: Option<u64>,
2128    /// Profiling records whose raw-derived source coordinates were observed;
2129    /// absent when the artifact level makes that mapping unavailable.
2130    #[serde(default)]
2131    pub source_coordinate_records: Option<u64>,
2132    /// Distinct source traces identified through the raw-derived coordinate
2133    /// mapping; absent when the artifact level makes it unavailable.
2134    #[serde(default)]
2135    pub distinct_source_traces: Option<u64>,
2136    /// Profiling records carrying a raw cache-bust marker observation;
2137    /// absent when the artifact level makes that observation unavailable.
2138    #[serde(default)]
2139    pub cache_bust_records: Option<u64>,
2140}
2141
2142/// One synthetic profiling request's planned-to-observed prompt-token check.
2143#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
2144#[serde(deny_unknown_fields)]
2145pub struct BenchPromptTokenReconciliation {
2146    pub population_index: u32,
2147    pub native_session_num: u64,
2148    pub planned_prompt_tokens: u32,
2149    #[serde(default)]
2150    pub observed_prompt_tokens: Option<u32>,
2151    pub reconciled: bool,
2152}
2153
2154/// Reconciled native evidence for one session-bounded Bench phase.
2155#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
2156#[serde(deny_unknown_fields)]
2157pub struct BenchSessionPhaseSummary {
2158    pub planned_sessions: u32,
2159    pub started_sessions: u32,
2160    pub succeeded_sessions: u32,
2161    pub failed_sessions: u32,
2162    pub planned_requests: u32,
2163    pub attempted_requests: u32,
2164    pub completed_requests: u32,
2165    pub failed_requests: u32,
2166    pub reconciled: bool,
2167}
2168
2169/// Terminal evidence for one admitted runtime session.
2170#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
2171#[serde(deny_unknown_fields)]
2172pub struct BenchRuntimeSessionResult {
2173    pub phase: String,
2174    pub runtime_session_id: String,
2175    pub template_identity: String,
2176    pub planned_turns: u32,
2177    pub attempted_turns: u32,
2178    pub status: ClientStatus,
2179    #[serde(default)]
2180    pub failure_classification: Option<String>,
2181    #[serde(default)]
2182    pub diagnostic: Option<String>,
2183    #[serde(default)]
2184    pub failing_turn: Option<u32>,
2185    pub suppressed_later_turns: u32,
2186}
2187
2188/// One native transport request reconciled to a linear-session turn.
2189#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
2190#[serde(deny_unknown_fields)]
2191pub struct BenchSessionTurnResult {
2192    pub phase: String,
2193    pub runtime_session_id: String,
2194    pub turn_index: u32,
2195    /// Measured from the raw request payload; absent when the artifact level
2196    /// does not produce the raw request/response artifact.
2197    #[serde(default)]
2198    pub pre_template_content_tokens: Option<u32>,
2199    #[serde(default)]
2200    pub observed_prompt_tokens: Option<u32>,
2201    pub native_session_num: u64,
2202    #[serde(default)]
2203    pub preceding_native_session_num: Option<u64>,
2204    #[serde(default)]
2205    pub preceding_terminal_response_receipt_ns: Option<u64>,
2206    #[serde(default)]
2207    pub effective_inter_turn_delay_seconds: Option<f64>,
2208    pub request_start_ns: u64,
2209    #[serde(default)]
2210    pub inter_turn_delay_reconciled: Option<bool>,
2211    #[serde(default)]
2212    pub post_failure_continuation: bool,
2213    pub native_artifact_name: String,
2214}
2215
2216/// Session and transport reconciliation returned by the Bench client.
2217#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
2218#[serde(deny_unknown_fields)]
2219pub struct BenchSessionResultEvidence {
2220    pub warmup: BenchSessionPhaseSummary,
2221    pub profiling: BenchSessionPhaseSummary,
2222    pub sessions: Vec<BenchRuntimeSessionResult>,
2223    pub turns: Vec<BenchSessionTurnResult>,
2224    pub population_slice_reconciled: bool,
2225    pub sessions_reconciled: bool,
2226    pub turn_order_reconciled: bool,
2227    pub inter_turn_delays_reconciled: bool,
2228    /// Whether raw requests reconcile to normalized metric records; absent at
2229    /// the `performance` artifact level, where no raw artifact exists.
2230    #[serde(default)]
2231    pub native_requests_reconciled: Option<bool>,
2232    pub counts_reconciled: bool,
2233    /// Evidence dimensions recorded as unavailable due to the effective
2234    /// artifact level ([[RFC-0004:C-BENCH-ARTIFACT-LEVEL]]).
2235    #[serde(default)]
2236    pub unavailable_dimensions: Vec<String>,
2237}
2238
2239/// One bounded native post-processing command and its terminal outcome.
2240#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
2241#[serde(deny_unknown_fields)]
2242pub struct BenchNativeInvocation {
2243    pub purpose: String,
2244    pub command: Vec<String>,
2245    pub exit_code: Option<i32>,
2246    pub interrupted: bool,
2247    pub timed_out: bool,
2248}
2249
2250/// File-bound request-SLO evidence derived from AIPerf profiling records.
2251#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
2252#[serde(deny_unknown_fields)]
2253pub struct BenchRequestSloResult {
2254    pub good_requests: u64,
2255    pub good_request_ratio: f64,
2256    pub goodput: f64,
2257    pub profiling_duration_seconds: f64,
2258    pub profiling_duration_source: String,
2259    pub request_count_reconciled: bool,
2260    #[serde(default)]
2261    pub native_aggregate_good_request_count: Option<u64>,
2262    #[serde(default)]
2263    pub native_aggregate_good_request_count_consistent: Option<bool>,
2264}
2265
2266// Schema roots.
2267
2268/// The schema root for the independently released framework adapter SDK.
2269#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
2270#[serde(deny_unknown_fields)]
2271pub struct AdapterProtocol {
2272    pub request: AdapterRequest,
2273    pub response: AdapterResponse,
2274}
2275
2276/// The schema root aggregating the request and result surfaces used by the
2277/// product-owned Eval and Bench measurement clients. Its optional fields are
2278/// code-generation anchors and are never all populated in one message.
2279#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
2280#[serde(deny_unknown_fields)]
2281pub struct MeasurementProtocol {
2282    #[serde(default, skip_serializing_if = "Option::is_none")]
2283    pub eval_client_request: Option<EvalClientRequest>,
2284    #[serde(default, skip_serializing_if = "Option::is_none")]
2285    pub eval_client_result: Option<EvalClientResult>,
2286    #[serde(default, skip_serializing_if = "Option::is_none")]
2287    pub bench_client_request: Option<BenchClientRequest>,
2288    #[serde(default, skip_serializing_if = "Option::is_none")]
2289    pub bench_client_result: Option<BenchClientResult>,
2290    #[serde(default, skip_serializing_if = "Option::is_none")]
2291    pub bench_population_preparation_request: Option<BenchPopulationPreparationRequest>,
2292    #[serde(default, skip_serializing_if = "Option::is_none")]
2293    pub bench_population_preparation_result: Option<BenchPopulationPreparationResult>,
2294    #[serde(default, skip_serializing_if = "Option::is_none")]
2295    pub data_asset_preparation_request: Option<MeasurementDataAssetPreparationRequest>,
2296    #[serde(default, skip_serializing_if = "Option::is_none")]
2297    pub data_asset_preparation_result: Option<MeasurementDataAssetPreparationResult>,
2298}