Skip to content

Commit bf8cd04

Browse files
committed
test(translation): cover exact replay sequencing
Signed-off-by: Alex Steiner <asteiner@nvidia.com>
1 parent dcd6558 commit bf8cd04

2 files changed

Lines changed: 52 additions & 1 deletion

File tree

crates/switchyard-translation/src/codecs/responses/stream.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ impl StreamCodec for OpenAiResponsesStreamCodec {
4848
let replayed_terminal = normalized
4949
.iter()
5050
.any(|chunk| matches!(chunk, LlmResponseChunk::MessageStop { .. }));
51+
// Exact replay emits `raw` once. Normalized encodings only advance codec state;
52+
// their generated events are discarded and must not create sequence-number gaps.
5153
for chunk in normalized {
5254
drop(encode_responses_stream(state, chunk));
5355
}

crates/switchyard-translation/tests/stream_translation.rs

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
66
use pretty_assertions::assert_eq;
77
use serde_json::json;
8-
use switchyard_protocol::{ResponseAccumulator, StopReason};
8+
use switchyard_protocol::{LlmResponseStreamEvent, ResponseAccumulator, StopReason};
99
use switchyard_translation::{
1010
LlmResponseChunk, StreamTranslationState, TranslationEngine, WireFormat, decode_stream_event,
1111
};
@@ -182,6 +182,55 @@ fn replayed_nonterminal_event_advances_encoder_state_before_finish() -> TestResu
182182
Ok(())
183183
}
184184

185+
// Exact replay advances sequencing by the one raw event actually emitted, not discarded
186+
// synthetic events produced while advancing encoder state.
187+
#[test]
188+
fn responses_replay_without_sequence_advances_by_one_emitted_event() -> TestResult {
189+
let engine = TranslationEngine::default();
190+
let format = WireFormat::OpenAiResponses;
191+
let raw = json!({
192+
"type": "response.output_item.added",
193+
"output_index": 0,
194+
"item": {
195+
"type": "function_call",
196+
"id": "fc_0",
197+
"call_id": "call_0",
198+
"name": "bash",
199+
"arguments": ""
200+
}
201+
});
202+
let replayed = LlmResponseStreamEvent::preserved(
203+
format,
204+
raw.clone(),
205+
vec![LlmResponseChunk::ToolCallDelta {
206+
index: 0,
207+
id: Some("call_0".to_string()),
208+
name: Some("bash".to_string()),
209+
arguments_delta: None,
210+
}],
211+
);
212+
let mut state = StreamTranslationState::new(format, format);
213+
214+
assert_eq!(
215+
engine.encode_stream_event(&mut state, format, replayed)?,
216+
vec![raw]
217+
);
218+
let generated = engine.encode_stream_event(
219+
&mut state,
220+
format,
221+
LlmResponseStreamEvent::new(vec![LlmResponseChunk::ToolCallDelta {
222+
index: 0,
223+
id: None,
224+
name: None,
225+
arguments_delta: Some("{}".to_string()),
226+
}]),
227+
)?;
228+
229+
assert_eq!(generated.len(), 1);
230+
assert_eq!(generated[0]["sequence_number"], 1);
231+
Ok(())
232+
}
233+
185234
#[test]
186235
fn replayed_anthropic_terminal_delta_finishes_with_message_stop_only() -> TestResult {
187236
let engine = TranslationEngine::default();

0 commit comments

Comments
 (0)