diff --git a/openai-java-core/src/main/kotlin/com/openai/helpers/ResponseAccumulator.kt b/openai-java-core/src/main/kotlin/com/openai/helpers/ResponseAccumulator.kt index 0e7ee6365..63862ddf7 100644 --- a/openai-java-core/src/main/kotlin/com/openai/helpers/ResponseAccumulator.kt +++ b/openai-java-core/src/main/kotlin/com/openai/helpers/ResponseAccumulator.kt @@ -116,7 +116,13 @@ class ResponseAccumulator private constructor() { * accumulated. A [ResponseAccumulator] can only be used to accumulate a single [Response]. */ fun accumulate(event: ResponseStreamEvent): ResponseStreamEvent { - check(response == null) { "Response has already been completed." } + // The API may send non-terminal events (e.g. `response.rate_limits.updated`) after a + // terminal event (`response.completed`, `response.failed`, `response.incomplete`). + // These events do not carry response data and are safe to ignore. Throwing here would + // break callers that pass every streamed event to the accumulator. + if (response != null) { + return event + } event.accept( object : ResponseStreamEvent.Visitor { diff --git a/openai-java-core/src/test/kotlin/com/openai/helpers/ResponseAccumulatorTest.kt b/openai-java-core/src/test/kotlin/com/openai/helpers/ResponseAccumulatorTest.kt index 2b12569d7..6488c266a 100644 --- a/openai-java-core/src/test/kotlin/com/openai/helpers/ResponseAccumulatorTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/helpers/ResponseAccumulatorTest.kt @@ -62,16 +62,17 @@ internal class ResponseAccumulatorTest { } @Test - fun accumulateAfterCompleted() { + fun accumulateAfterCompletedIgnoresPostCompletionEvents() { val accumulator = ResponseAccumulator.create() accumulator.accumulate(ResponseStreamEvent.ofCompleted(responseCompletedEvent())) - assertThatThrownBy { - accumulator.accumulate(ResponseStreamEvent.ofCompleted(responseCompletedEvent())) - } - .isExactlyInstanceOf(IllegalStateException::class.java) - .hasMessage("Response has already been completed.") + // Post-completion events (e.g. `response.rate_limits.updated`) should be silently + // ignored rather than throwing. The original response should remain intact. + assertThatNoException().isThrownBy { + accumulator.accumulate(ResponseStreamEvent.ofCreated(responseCreatedEvent())) + } + assertThat(accumulator.response().id()).isEqualTo("response-id") } @Test