@@ -5,6 +5,8 @@ use std::cmp::Ordering;
55use std:: collections:: BinaryHeap ;
66
77use anyhow:: { Context , Result , anyhow, bail} ;
8+ use rand:: rngs:: StdRng ;
9+ use rand:: { Rng , SeedableRng } ;
810use rustc_hash:: FxHashMap ;
911use uuid:: Uuid ;
1012
@@ -38,9 +40,12 @@ enum PromptMode {
3840 DeltaCumulative ,
3941}
4042
43+ const SYNTHETIC_DELTA_OUTPUT_SEED : u64 = 0xD37A_0A7E_5EED ;
44+
4145#[ derive( Debug , Clone , Copy , PartialEq , Eq ) ]
4246enum TurnOutcome {
4347 Completed ,
48+ Rejected ,
4449 Cancelled ,
4550}
4651
@@ -78,6 +83,7 @@ struct TurnRuntime {
7883struct InFlightTurn {
7984 session_index : usize ,
8085 turn_index : usize ,
86+ emitted_output_tokens : usize ,
8187}
8288
8389#[ derive( Debug , Clone , Copy ) ]
@@ -384,6 +390,7 @@ impl WorkloadDriver {
384390 u32:: try_from ( engine_block_size) . context ( "engine_block_size does not fit in u32" ) ?;
385391 let trace_block_size = trace. block_size ;
386392 let is_concurrency = matches ! ( & policy, SchedulingPolicy :: Concurrency ( _) ) ;
393+ let mut output_rng = StdRng :: seed_from_u64 ( SYNTHETIC_DELTA_OUTPUT_SEED ) ;
387394 let sessions: Vec < SessionRuntime > = trace
388395 . sessions
389396 . into_iter ( )
@@ -402,12 +409,22 @@ impl WorkloadDriver {
402409 } else {
403410 None
404411 } ;
412+ let tokens = turn. synthesize_tokens ( trace_block_size) ?;
413+ let output_token_ids = match turn. output_token_ids {
414+ Some ( output_token_ids) => Some ( output_token_ids) ,
415+ None if prompt_mode == PromptMode :: DeltaCumulative => Some (
416+ ( 0 ..turn. max_output_tokens )
417+ . map ( |_| output_rng. random :: < u32 > ( ) )
418+ . collect ( ) ,
419+ ) ,
420+ None => None ,
421+ } ;
405422 Ok ( TurnRuntime {
406423 request_id : None ,
407- tokens : turn . synthesize_tokens ( trace_block_size ) ? ,
424+ tokens,
408425 replay_key : turn. replay_key ,
409426 max_output_tokens : turn. max_output_tokens ,
410- output_token_ids : turn . output_token_ids ,
427+ output_token_ids,
411428 delay_after_previous_ms : turn. delay_after_previous_ms ,
412429 priority : turn. priority ,
413430 strict_priority : turn. strict_priority ,
@@ -417,7 +434,16 @@ impl WorkloadDriver {
417434 } )
418435 . collect :: < Result < Vec < _ > > > ( ) ?;
419436 let cumulative_capacity = if prompt_mode == PromptMode :: DeltaCumulative {
420- turns. iter ( ) . map ( |turn| turn. tokens . len ( ) ) . sum ( )
437+ turns
438+ . iter ( )
439+ . map ( |turn| {
440+ turn. tokens . len ( )
441+ + turn
442+ . output_token_ids
443+ . as_ref ( )
444+ . map_or ( 0 , |output| output. len ( ) )
445+ } )
446+ . sum ( )
421447 } else {
422448 0
423449 } ;
@@ -538,6 +564,7 @@ impl WorkloadDriver {
538564 InFlightTurn {
539565 session_index,
540566 turn_index,
567+ emitted_output_tokens : 0 ,
541568 } ,
542569 ) ;
543570 emitted. push ( ReadyTurn {
@@ -553,9 +580,60 @@ impl WorkloadDriver {
553580 emitted
554581 }
555582
583+ pub fn on_output_token ( & mut self , request_uuid : Uuid , token_id : u32 ) -> Result < ( ) > {
584+ if self . prompt_mode == PromptMode :: Full {
585+ return Ok ( ( ) ) ;
586+ }
587+ let in_flight = self
588+ . in_flight
589+ . get ( & request_uuid)
590+ . copied ( )
591+ . ok_or_else ( || anyhow ! ( "unknown workload request output for {request_uuid}" ) ) ?;
592+
593+ let turn = & self . sessions [ in_flight. session_index ] . turns [ in_flight. turn_index ] ;
594+ let planned_output_tokens = turn
595+ . output_token_ids
596+ . as_ref ( )
597+ . expect ( "delta turns must have planned output tokens" ) ;
598+ let expected_token = planned_output_tokens
599+ . get ( in_flight. emitted_output_tokens )
600+ . ok_or_else ( || {
601+ anyhow ! (
602+ "workload request {request_uuid} emitted more than {} planned output tokens" ,
603+ planned_output_tokens. len( )
604+ )
605+ } ) ?;
606+ if token_id != * expected_token {
607+ bail ! (
608+ "workload request {request_uuid} emitted token {token_id} at position {}, expected {}" ,
609+ in_flight. emitted_output_tokens,
610+ expected_token
611+ ) ;
612+ }
613+
614+ let in_flight = self
615+ . in_flight
616+ . get_mut ( & request_uuid)
617+ . expect ( "validated in-flight request must still exist" ) ;
618+ in_flight. emitted_output_tokens = in_flight
619+ . emitted_output_tokens
620+ . checked_add ( 1 )
621+ . context ( "workload emitted output token count overflow" ) ?;
622+ Ok ( ( ) )
623+ }
624+
556625 pub fn on_complete ( & mut self , request_uuid : Uuid , now_ms : f64 ) -> Result < ( ) > {
626+ self . on_terminal ( request_uuid, now_ms, false )
627+ }
628+
629+ pub fn on_terminal ( & mut self , request_uuid : Uuid , now_ms : f64 , rejected : bool ) -> Result < ( ) > {
630+ let outcome = if rejected {
631+ TurnOutcome :: Rejected
632+ } else {
633+ TurnOutcome :: Completed
634+ } ;
557635 let resolution = self
558- . resolve_turn ( request_uuid, now_ms, TurnOutcome :: Completed ) ?
636+ . resolve_turn ( request_uuid, now_ms, outcome ) ?
559637 . expect ( "completed turns require an in-flight request" ) ;
560638 self . apply_resolution ( resolution, now_ms) ;
561639 Ok ( ( ) )
@@ -569,7 +647,7 @@ impl WorkloadDriver {
569647 ) -> Result < Option < TurnResolution > > {
570648 let Some ( in_flight) = self . in_flight . get ( & request_uuid) . copied ( ) else {
571649 return match outcome {
572- TurnOutcome :: Completed => Err ( anyhow ! (
650+ TurnOutcome :: Completed | TurnOutcome :: Rejected => Err ( anyhow ! (
573651 "unknown workload request completion for {request_uuid}"
574652 ) ) ,
575653 TurnOutcome :: Cancelled => Ok ( None ) ,
@@ -604,8 +682,23 @@ impl WorkloadDriver {
604682 }
605683
606684 let request_id = turn. request_id . clone ( ) ;
685+ if outcome == TurnOutcome :: Rejected && in_flight. emitted_output_tokens != 0 {
686+ bail ! (
687+ "rejected workload request {request_uuid} emitted {} output tokens" ,
688+ in_flight. emitted_output_tokens
689+ ) ;
690+ }
691+ let completed_output_tokens = ( outcome == TurnOutcome :: Completed
692+ && self . prompt_mode == PromptMode :: DeltaCumulative )
693+ . then ( || {
694+ let planned_output_tokens = turn
695+ . output_token_ids
696+ . as_ref ( )
697+ . expect ( "delta turns must have planned output tokens" ) ;
698+ planned_output_tokens[ ..in_flight. emitted_output_tokens ] . to_vec ( )
699+ } ) ;
607700 let ( next_turn_index, next_ready_at_ms, session_ended) = match outcome {
608- TurnOutcome :: Completed => {
701+ TurnOutcome :: Completed | TurnOutcome :: Rejected => {
609702 let next_turn_index = in_flight
610703 . turn_index
611704 . checked_add ( 1 )
@@ -626,6 +719,11 @@ impl WorkloadDriver {
626719 session. in_flight = None ;
627720 session. next_turn_index = next_turn_index;
628721 session. next_ready_at_ms = next_ready_at_ms;
722+ if next_ready_at_ms. is_some ( )
723+ && let Some ( output_tokens) = completed_output_tokens
724+ {
725+ session. cumulative_tokens . extend ( output_tokens) ;
726+ }
629727 if let Some ( ready_at_ms) = next_ready_at_ms {
630728 self . ready_sessions . push ( ReadySession {
631729 ready_at_ms,
@@ -1057,7 +1155,7 @@ mod tests {
10571155 }
10581156
10591157 #[ test]
1060- fn accumulating_delta_mode_emits_cumulative_input_tokens ( ) {
1158+ fn accumulating_delta_mode_includes_previous_output_tokens ( ) {
10611159 let trace = Trace {
10621160 block_size : 4 ,
10631161 sessions : vec ! [ SessionTrace {
@@ -1066,8 +1164,8 @@ mod tests {
10661164 turns: vec![
10671165 TurnTrace {
10681166 input_length: 6 ,
1069- max_output_tokens: 1 ,
1070- output_token_ids: None ,
1167+ max_output_tokens: 2 ,
1168+ output_token_ids: Some ( vec! [ 20 , 21 ] ) ,
10711169 replay_key: None ,
10721170 hash_ids: vec![ 10 , 11 ] ,
10731171 delay_after_previous_ms: 0.0 ,
@@ -1094,20 +1192,145 @@ mod tests {
10941192 let first = driver. pop_ready ( 0.0 , usize:: MAX ) ;
10951193 assert_eq ! ( first. len( ) , 1 ) ;
10961194 assert_eq ! ( first[ 0 ] . request. tokens, vec![ 10 , 10 , 10 , 10 , 11 , 11 ] ) ;
1195+ assert_eq ! ( first[ 0 ] . request. output_token_ids, Some ( vec![ 20 , 21 ] ) ) ;
10971196 assert_eq ! ( first[ 0 ] . request. priority, 3 ) ;
10981197 assert_eq ! ( first[ 0 ] . request. strict_priority, 4 ) ;
1198+ driver. on_output_token ( first[ 0 ] . request_uuid , 20 ) . unwrap ( ) ;
1199+ driver. on_output_token ( first[ 0 ] . request_uuid , 21 ) . unwrap ( ) ;
10991200 driver. on_complete ( first[ 0 ] . request_uuid , 10.0 ) . unwrap ( ) ;
11001201
11011202 let second = driver. pop_ready ( 15.0 , usize:: MAX ) ;
11021203 assert_eq ! ( second. len( ) , 1 ) ;
11031204 assert_eq ! (
11041205 second[ 0 ] . request. tokens,
1105- vec![ 10 , 10 , 10 , 10 , 11 , 11 , 12 , 12 , 12 ]
1206+ vec![ 10 , 10 , 10 , 10 , 11 , 11 , 20 , 21 , 12 , 12 , 12 ]
11061207 ) ;
11071208 assert_eq ! ( second[ 0 ] . request. priority, -2 ) ;
11081209 assert_eq ! ( second[ 0 ] . request. strict_priority, 7 ) ;
11091210 }
11101211
1212+ #[ test]
1213+ fn accumulating_delta_mode_plans_missing_output_token_ids ( ) {
1214+ let trace = Trace {
1215+ block_size : 1 ,
1216+ sessions : vec ! [ SessionTrace {
1217+ session_id: "a" . into( ) ,
1218+ first_arrival_timestamp_ms: Some ( 0.0 ) ,
1219+ turns: vec![
1220+ TurnTrace {
1221+ input_length: 2 ,
1222+ max_output_tokens: 3 ,
1223+ hash_ids: vec![ 10 , 11 ] ,
1224+ ..Default :: default ( )
1225+ } ,
1226+ TurnTrace {
1227+ input_length: 1 ,
1228+ max_output_tokens: 1 ,
1229+ hash_ids: vec![ 12 ] ,
1230+ ..Default :: default ( )
1231+ } ,
1232+ ] ,
1233+ } ] ,
1234+ } ;
1235+ let mut driver = WorkloadDriver :: new_concurrency_accumulating_deltas ( trace, 1 , 1 ) . unwrap ( ) ;
1236+
1237+ let first = driver. pop_ready ( 0.0 , usize:: MAX ) ;
1238+ assert_eq ! ( first. len( ) , 1 ) ;
1239+ let planned_output = first[ 0 ]
1240+ . request
1241+ . output_token_ids
1242+ . clone ( )
1243+ . expect ( "delta replay should plan synthetic outputs" ) ;
1244+ assert_eq ! ( planned_output. len( ) , 3 ) ;
1245+ for & token_id in & planned_output {
1246+ driver
1247+ . on_output_token ( first[ 0 ] . request_uuid , token_id)
1248+ . unwrap ( ) ;
1249+ }
1250+ driver. on_complete ( first[ 0 ] . request_uuid , 1.0 ) . unwrap ( ) ;
1251+
1252+ let second = driver. pop_ready ( 1.0 , usize:: MAX ) ;
1253+ assert_eq ! ( second. len( ) , 1 ) ;
1254+ let mut expected = vec ! [ 10 , 11 ] ;
1255+ expected. extend ( planned_output) ;
1256+ expected. push ( 12 ) ;
1257+ assert_eq ! ( second[ 0 ] . request. tokens, expected) ;
1258+ assert_eq ! (
1259+ second[ 0 ] . request. output_token_ids. as_ref( ) . map( Vec :: len) ,
1260+ Some ( 1 )
1261+ ) ;
1262+ }
1263+
1264+ #[ test]
1265+ fn accumulating_delta_mode_appends_only_emitted_output_tokens ( ) {
1266+ let trace = Trace {
1267+ block_size : 1 ,
1268+ sessions : vec ! [ SessionTrace {
1269+ session_id: "a" . into( ) ,
1270+ first_arrival_timestamp_ms: Some ( 0.0 ) ,
1271+ turns: vec![
1272+ TurnTrace {
1273+ input_length: 1 ,
1274+ max_output_tokens: 3 ,
1275+ output_token_ids: Some ( vec![ 20 , 21 , 22 ] ) ,
1276+ hash_ids: vec![ 10 ] ,
1277+ ..Default :: default ( )
1278+ } ,
1279+ TurnTrace {
1280+ input_length: 1 ,
1281+ max_output_tokens: 1 ,
1282+ hash_ids: vec![ 12 ] ,
1283+ ..Default :: default ( )
1284+ } ,
1285+ ] ,
1286+ } ] ,
1287+ } ;
1288+ let mut driver = WorkloadDriver :: new_concurrency_accumulating_deltas ( trace, 1 , 1 ) . unwrap ( ) ;
1289+
1290+ let first = driver. pop_ready ( 0.0 , usize:: MAX ) ;
1291+ driver. on_output_token ( first[ 0 ] . request_uuid , 20 ) . unwrap ( ) ;
1292+ driver. on_output_token ( first[ 0 ] . request_uuid , 21 ) . unwrap ( ) ;
1293+ driver. on_complete ( first[ 0 ] . request_uuid , 1.0 ) . unwrap ( ) ;
1294+
1295+ let second = driver. pop_ready ( 1.0 , usize:: MAX ) ;
1296+ assert_eq ! ( second[ 0 ] . request. tokens, vec![ 10 , 20 , 21 , 12 ] ) ;
1297+ }
1298+
1299+ #[ test]
1300+ fn accumulating_delta_mode_does_not_append_rejected_output_tokens ( ) {
1301+ let trace = Trace {
1302+ block_size : 1 ,
1303+ sessions : vec ! [ SessionTrace {
1304+ session_id: "a" . into( ) ,
1305+ first_arrival_timestamp_ms: Some ( 0.0 ) ,
1306+ turns: vec![
1307+ TurnTrace {
1308+ input_length: 1 ,
1309+ max_output_tokens: 2 ,
1310+ output_token_ids: Some ( vec![ 20 , 21 ] ) ,
1311+ hash_ids: vec![ 10 ] ,
1312+ ..Default :: default ( )
1313+ } ,
1314+ TurnTrace {
1315+ input_length: 1 ,
1316+ max_output_tokens: 1 ,
1317+ hash_ids: vec![ 12 ] ,
1318+ ..Default :: default ( )
1319+ } ,
1320+ ] ,
1321+ } ] ,
1322+ } ;
1323+ let mut driver = WorkloadDriver :: new_concurrency_accumulating_deltas ( trace, 1 , 1 ) . unwrap ( ) ;
1324+
1325+ let first = driver. pop_ready ( 0.0 , usize:: MAX ) ;
1326+ driver
1327+ . on_terminal ( first[ 0 ] . request_uuid , 1.0 , true )
1328+ . unwrap ( ) ;
1329+
1330+ let second = driver. pop_ready ( 1.0 , usize:: MAX ) ;
1331+ assert_eq ! ( second[ 0 ] . request. tokens, vec![ 10 , 12 ] ) ;
1332+ }
1333+
11111334 #[ test]
11121335 fn agentic_mode_releases_turn_after_dependency_completion_plus_delay ( ) {
11131336 let trace = AgenticTrace {
0 commit comments