V4 split/03 core session rewrite - #200
Draft
renal128 wants to merge 8 commits into
Draft
Conversation
…event types - EventParser: drop internal_tentative_agent_response and asr_initiation_metadata parsing (server no longer needs them handled; now silently ignored alongside other known-but-unhandled event types instead of throwing), default expects_response to false when absent, and fall back to top-level code/message when client_error lacks an error_event sub-object. - IncomingEvents: remove the now-dead TentativeAgentResponseEvent/ ASRInitiationMetadataEvent types and .tentativeAgentResponse/ .asrInitiationMetadata cases; ClientToolCallEvent.expectsResponse defaults to false. - EndReason: drop .agentNotConnected (no longer distinguished from .remoteDisconnected). - Conversation+Events.swift: minimal compatibility edit dropping the two switch cases for the removed IncomingEvent cases above (the rest of this file's rewrite lands in the core-session-rewrite PR, which also needs to land together with Message.isPartial -- adding that field while this file's message-store logic is still unmigrated was verified to cause runtime data corruption in unrelated codepaths, so it's deferred there instead of included here). Note: EventSerializer.swift and OutgoingEvents.swift are NOT included here despite being conceptually "event wire protocol" -- their target versions require ConversationConfig.textOnly, DynamicVariableValue, and ConversationError.invalidToolResult, which don't exist until the core-session-rewrite PR. They land there instead.
…tives Replaces the ConversationOptions-era config/error/state shape with the v4 primitives that the rest of this session rewrite builds on: - ConversationAuth (new) replaces ElevenLabsConfiguration's auth cases (publicAgent/conversationToken/signedWebSocketURL); the .customTokenProvider case and participantName/agentId fields have no replacement. - ElevenLabsEndpoints (new) makes API/WebSocket endpoints configurable per-session instead of hardcoded (see ConnectionConstants removal). - ConversationConfig absorbs ConversationOptions' non-callback fields: new LogLevel/DynamicVariableValue types, relayOnly: Bool (replacing the deleted LiveKitNetworkConfiguration's 3-way ICE strategy + custom ICE servers -- those are no longer configurable), agentJoinTimeout/ conversationInitTimeout (replacing ConversationStartupConfiguration). - ConversationCallbacks (new) holds every onXxx closure split out of ConversationOptions. Dropped with no replacement: onCanSendFeedbackChange, onSpeechActivity, onConversationMetadata (now a published property instead), onAgentStateChange/onStartupStateChange (superseded by published agentState/state). - ConversationError gains .initializationTimeout, .microphonePermissionDenied, .invalidToolResult, .invalidURL, and a recoverable .underlyingError; drops .localNetworkPermissionRequired (see LocalNetworkPermissionMonitor removal) and .noSoftwareMuteHandlerConfigured. - ConversationState replaces .active(CallInfo) with .connected (the connected agent id is no longer retrievable from state at all -- CallInfo is deleted), .error(ConversationError) with .startupFailed(...), and adds StartupPhase for the new .connecting(phase:) case (no more startup-timing telemetry -- ConversationAgentReadyReport/StartupMetrics/StartupState/ StartupResult are all deleted). - AgentState is un-nested from ElevenLabs.AgentState to a top-level type (old nested one stays for now, removed with ElevenLabs.swift later in this stack).
…dpoints TokenService.fetchConnectionDetails(configuration:) -> ConnectionDetails (serverUrl/roomName/participantToken bundle) becomes fetchRoomToken(auth:apiBase:environment:) -> String -- WebRTCConnectionManager now owns constructing the room connection from that token plus ConversationConfig.endpoints instead of TokenService handing back a ready-made LiveKit connection bundle. The .customTokenProvider auth case has no replacement (matches ConversationAuth). ConnectionConstants' three hardcoded URLs are deleted now that endpoints are configurable per-session via ElevenLabsEndpoints.
…ig/Auth - ConnectionManaging: connect(auth:config:) throws unifies what was a differently-shaped connect() per concrete manager; onStartupPhaseChange replaces errorHandler (typed ConversationStartupFailure throws instead of a generic error callback). - WebRTCConnectionManager: mic-permission request now runs concurrently with the token fetch (was sequential); readiness gate races LiveKitReadinessDelegate.awaitRemoteParticipant() (first remote participant joins) instead of waiting for the agent's audio track to subscribe -- this specific change already shipped separately as #197, landing here only because the v4 import was authored before that merged. Startup timing is now logged, not returned to the caller. Also drops the LocalNetworkPermissionMonitor-based "check Local Network permission" diagnostic with nothing replacing it. - Dependencies: init(logLevel:) replaces a parameterless init; TokenService is now constructed with no endpoint config baked in. Adds an opt-in LogLevel.debugWithRTC tier that forwards LiveKit/WebRTC internal logs. - SDKLogger: default level changes from .info to .warning; logLevel param renamed to levelOverride. - LiveKitReadinessDelegate (in WebRTCConnectionManager.swift) changed from private back to internal so LiveKitReadinessDelegateTests (added by #197) can still construct it via @testable import; its call site there is updated for SDKLogger's renamed parameter.
…estore - AudioPipelineConfiguration: MicrophoneMuteMode is no longer a LiveKit re-export -- it's the SDK's own 4-case enum (.inputMixer/.restart/ .voiceProcessing/.software(speechThreshold:)), folding in what used to be 4 separate fields (microphoneMuteMode/useSoftwareMute/ mutedSpeechThreshold + the onSpeechActivity/onMutedSpeech callback split). recordingAlwaysPrepared opt-out is removed -- engine pre-warm is now unconditional. LiveKit's SpeechActivityEvent no longer leaks into this public config surface. - SoftwareMuteProcessor: onMutedSpeech(MutedSpeechEvent) (throttled, periodic, level-based) becomes onSpeakingWhileMutedChange(Bool) (fires once on the started/ended edge of a hangover-latched speech segment, including on unmute mid-segment). mutedSpeechThrottleInSeconds is gone. - ConversationAudioManager: configure(with:) now snapshots and restores AudioManager.shared's capturePostProcessingDelegate/ isVoiceProcessingBypassed/isVoiceProcessingAGCEnabled around this instance's lifetime (guarded by an identity check on restore) instead of unconditionally clearing them on cleanup -- fixes clobbering another component that took over the process-wide slot. setRecordingAlwaysPreparedMode is now awaited inside configure() (called before WebRTCConnectionManager.connect) instead of fired in the background at init -- this adds a new async hop to the front of the startup critical path; worth a real-device timing check given this SDK's startup-latency history, but not blocking this PR.
…alization - Message.isPartial (true while an agent message is streaming in from agent_chat_response_part chunks, or a user transcript is tentative; false once finalized or for locally-appended messages) -- lands here, not with the rest of the event-model changes, because adding the field while this file's message-store logic was still unmigrated was verified to cause runtime data corruption in unrelated codepaths (a sendToolResult call started publishing garbage that looked like an unrelated feedback event) -- a partial-migration hazard specific to that combination, not present in either the fully-old or fully-new state. - Conversation+Events.swift: insertUserTranscript/upsertAgentMessage replaced by applyUserTranscript/applyTentativeUserTranscript/ applyAgentResponse/applyAgentResponsePart, backed by shared messageIndex/isNewerThanHighestEventId/appendMessage helpers -- guards against reopening a finalized message or applying a stale/ out-of-order part, which the old version didn't have. Ping replies now fire via a detached Task instead of being awaited inline, so a slow publish doesn't stall the serialized event-handling queue. callbacks.onPing added. Two behavior changes worth explicit reviewer sign-off, not mentioned in the v4 import's stated deviations: the .agentToolResponse handler for toolName == "end_call" no longer calls endConversation() (grep confirms nothing else does either -- server-driven auto-hangup appears gone), and onCanSendFeedbackChange/lastFeedbackSubmittedEventId tracking is removed entirely with no replacement. - EventSerializer/OutgoingEvents: config.textOnly (flattened from conversationOverrides.textOnly) and DynamicVariableValue-based dynamic_variables serialization (was String-only) now that ConversationConfig carries them. ClientToolResultEvent's Any-based init is no longer deprecated (the Encodable overload it pointed reviewers to is gone -- see ConversationClient in the next commit) and throws ConversationError.invalidToolResult instead of silently stringifying an un-encodable result.
- ConversationClient (new, ObservableObject) replaces the static ElevenLabs.startConversation(...) factories and process-wide ElevenLabs.configure(_:)/ElevenLabsConfiguration -- it's a reusable, long-lived object (init(callbacks:), start(auth:config:) async throws, endConversation(), reset()) instead of a one-shot factory returning a Conversation. ElevenLabs.version (public) has no replacement -- SDKVersion.version is internal-only now. - Conversation is no longer public -- it's an internal session type ConversationClient owns per start() call. Its new AsyncStream-serialized event pipeline (eventStreamContinuation/eventConsumerTask) replaces direct closure dispatch from the connection manager. toggleMute/ setMuted/isMuted are renamed toggleMicMute/setMicMuted/isMicMuted, and gain a symmetric toggleAgentMute/setAgentMuted/isAgentMuted; calling the mic-mute methods while disconnected is now a lenient no-op instead of throwing .notConnected. inputTrack/agentAudioTrack (raw LiveKit track access) and audioDevices/selectedAudioDeviceID (device enumeration) are removed with no replacement. sendToolResult's generic Encodable overload is gone -- only the Any-based overload remains, so a plain Encodable struct now throws ConversationError.invalidToolResult instead of JSON-encoding automatically. - CallInfo (agentId-carrying struct) is deleted -- ConversationState no longer exposes the connected agent's id at all. - Removes the dead onRawMessage plumbing that shipped with this v4 import: both connection managers declared it and Conversation.messageSource(for:) existed to feed it, but nothing ever wired connectionManager.onRawMessage to actually fire, so the documented public ConversationCallbacks.onMessage callback could never fire either. Removed both rather than ship a callback that silently never calls back; confirmed via a test (testRawMessageCallbackReceivesSourceAndRawJSON) that failed exactly this way before the fix. - Test updates are almost entirely mechanical renames to match the above (ConversationOptions -> ConversationCallbacks/ConversationConfig split, .active(CallInfo) -> .connected, ElevenLabs.* -> ConversationClient/ ConversationAuth/ElevenLabsEndpoints), plus one real fix: the hardcoded SDK-version string in ElevenLabsSDKTests was still "3.2.0", stale against the intentional 3.2.2 bump already on this branch. ConversationConfigTests/EventSerializerTests/ConversationEventHandlerTests gained real new coverage for the config/event changes earlier in this stack (see their respective commits). Coverage lost with no direct replacement: testSendToolResultEncodesEncodableResult (tracks the Encodable-overload removal above) and testTextOnlyStartDisconnectsPreviousActiveManagerBeforeSwitchingTransports (WebRTC->WebSocket teardown-before-switch ordering). Full suite: 154 tests, 0 failures.
renal128
force-pushed
the
v4-split/03-core-session-rewrite
branch
from
July 3, 2026 13:08
f7cadfc to
c4c7881
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.