Refine streaming parser handling and cache cleanup#24
Refine streaming parser handling and cache cleanup#24christopherkarani merged 2 commits intomainfrom
Conversation
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
@claude review |
|
Claude finished @christopherkarani's task in 3m 53s —— View job Code Review
SummaryOverall this is a well-structured hardening PR. The core ideas — flushing the SSE parser at EOF, emitting a synthetic completion chunk to prevent consumer hangs, and sharing the tool-call finalization path — are all sound. A few issues are worth addressing before merging. Issues🔴
|
Code Review: Refine streaming parser handling and cache cleanupOverall this is a solid, targeted set of fixes. The refactoring reduces meaningful duplication, and the correctness fixes (SSE parser, JsonRepair, cache eviction) are well-scoped. A few items worth discussing before merge: Issues1. Tool call finalization condition is semantically wider than intended
// Before
let isToolCallsComplete = finishReason == .toolCalls || finishReason == .toolCall
// After
let shouldFinalizeToolCalls = finishReason != nil && !toolCallAccumulators.isEmptyThe new condition finalizes accumulators on any non-nil finish reason, including if finishReason == .toolCalls || finishReason == .toolCall || finishReason == .stop {
// only finalize on expected terminal reasons
}2.
|
PR Review: Refine streaming parser handling and cache cleanupThanks for this set of fixes. The overall direction is solid: draining buffered SSE events after the byte stream closes, deduplicating completion chunks, and wiring up NSCache evictions to actor state cleanup are all valuable improvements. A few issues need attention before merge. Critical: Duplicate switch cases in JsonRepair.swift (won't compile)File: Sources/Conduit/Utilities/JsonRepair.swift The diff inserts new case statements for '[', ']', and an expanded '}' into the switch, but the original case '[': and its body are left as context lines rather than being deleted. The resulting file contains a duplicate case '[: Swift rejects duplicate switch cases at compile time, so this branch will not build. The old single-line case bodies need to be marked as deleted lines in the patch. There is also a logic error in the resulting case ']': after popping '[' from the stack, the code immediately tries to also pop '{'. This would erroneously collapse a brace context on every ']' encountered. Fix: mark the original 'if bracketStack.last == "{"' body and the old 'case "[":' as deleted lines. Critical: seenFields in ServerSentEventParser.finish() — verify the property existsFile: Sources/Conduit/Utilities/ServerSentEventParser.swift finish() now references seenFields.contains("data"): The diff does not show where seenFields is declared or populated. If it is new, its declaration and the insertion logic inside ingestLine must also be part of this PR — without it the build fails. If it already exists in the file, a brief note confirming that would help reviewers. The behavior change itself (dispatching an event with empty-string data when 'data:' was present but blank) aligns with the SSE spec, so the intent is correct — but consumers that currently assume data payloads are always non-empty should be audited. Concern: shouldFinalizeToolCalls is broader than intendedFile: Sources/Conduit/Providers/OpenAI/OpenAIProvider+Streaming.swift Old guard: New guard: This now finalizes accumulated (possibly partial) tool calls on any terminal finish reason including .stop, .length, .contentFilter, and error variants. A model that starts streaming a tool call and then halts mid-stream due to a content-policy hit would cause this path to attempt JSON repair and yield a synthesized tool-call chunk. The consumer would receive a fabricated tool invocation for a generation the model explicitly did not complete. Suggested approach: retain the original .toolCalls / .toolCall guard for the in-stream path, and keep the new end-of-stream fallback (the block after the loop) only for the case where the stream ended without any finish reason at all. Minor: Redundant continuation.finish() in the Responses completed pathFile: Sources/Conduit/Providers/OpenAI/OpenAIProvider+Streaming.swift When processResponsesEventData returns true for a .completed event, the call site calls continuation.finish() explicitly. Since continuation.finish() already sits unconditionally at the bottom of the outer function, removing the explicit call here and letting control fall through would simplify the flow without changing behaviour. What looks good
Summary
Happy to help clarify any of the above. |
JsonRepairJsonRepaircontext detection plus parity test expectations for the SSE parser