fix(ios): route forced realtime consults through Gateway relay (#117485)

* fix(ios): route forced realtime consults through Gateway relay

* fix(ios): route forced realtime consults through Gateway relay

* fix(ios): route forced realtime consults through Gateway relay

* fix(ios): route forced realtime consults through Gateway relay

* style(ios): drop redundant Self in requiresForcedAgentConsultRelay

* style(ios): use explicit self for firstString per swiftformat

---------

Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
This commit is contained in:
clawsweeper[bot]
2026-08-02 18:06:19 -07:00
committed by GitHub
parent e1c71fab4f
commit fa0e298d84
2 changed files with 81 additions and 2 deletions
@@ -401,13 +401,18 @@ enum TalkModeGatewayConfigParser {
let realtimeVoiceId = Self.firstString(realtime, keys: ["voice"])
?? Self.firstString(realtimeProviderConfig, keys: ["voice"])
let realtimeTransport = Self.firstString(realtime, keys: ["transport"])?.lowercased()
let requiresGatewayRealtimeTransport = realtimeTransport == "gateway-relay"
// Direct provider WebRTC can answer before consulting the agent, so this explicit
// policy must stay on the relay that enforces final-transcript consultations.
let requiresForcedAgentConsultRelay = Self.requiresForcedAgentConsultRelay(realtime)
let requiresGatewayRealtimeTransport = requiresForcedAgentConsultRelay
|| realtimeTransport == "gateway-relay"
|| realtimeTransport == "provider-websocket"
|| Self.usesAzureOpenAI(provider: realtimeProvider, config: realtimeProviderConfig)
let openAIProviderConfig = Self.realtimeProviderConfig(
providers: realtimeProviders,
provider: "openai")
let openAIRequiresGatewayRealtimeTransport = realtimeTransport == "gateway-relay"
let openAIRequiresGatewayRealtimeTransport = requiresForcedAgentConsultRelay
|| realtimeTransport == "gateway-relay"
|| realtimeTransport == "provider-websocket"
|| Self.usesAzureOpenAI(provider: "openai", config: openAIProviderConfig)
let executionMode = Self.resolvedExecutionMode(
@@ -450,6 +455,10 @@ enum TalkModeGatewayConfigParser {
return nil
}
private static func requiresForcedAgentConsultRelay(_ realtime: [String: AnyCodable]?) -> Bool {
self.firstString(realtime, keys: ["consultRouting"])?.lowercased() == "force-agent-consult"
}
private static func resolvedExecutionMode(
_ realtime: [String: AnyCodable]?,
requiresGatewayRealtimeTransport: Bool) -> TalkModeExecutionMode
@@ -635,8 +635,78 @@ struct TalkModeManagerTests {
defaultModelIdFallback: "eleven_v3",
defaultRealtimeModelIdFallback: "gpt-realtime-2",
defaultSilenceTimeoutMs: 900)
let routing = TalkModeRoutingResolver.resolve(
parsed: parsed,
providerSelection: .openAIRealtime,
defaultProvider: "elevenlabs",
defaultRealtimeModelId: "gpt-realtime-2")
#expect(parsed.executionMode == .realtimeWebRTC)
#expect(routing.route == .realtimeWebRTC)
}
@Test func `routes forced agent consultation through gateway relay`() {
let config: [String: Any] = [
"talk": [
"realtime": [
"provider": "openai",
"mode": "realtime",
"brain": "agent-consult",
"consultRouting": "force-agent-consult",
],
],
]
let parsed = TalkModeGatewayConfigParser.parse(
config: config,
defaultProvider: "elevenlabs",
defaultModelIdFallback: "eleven_v3",
defaultRealtimeModelIdFallback: "gpt-realtime-2",
defaultSilenceTimeoutMs: 900)
let gatewayDefaultRouting = TalkModeRoutingResolver.resolve(
parsed: parsed,
providerSelection: .gatewayDefault,
defaultProvider: "elevenlabs",
defaultRealtimeModelId: "gpt-realtime-2")
let openAIRouting = TalkModeRoutingResolver.resolve(
parsed: parsed,
providerSelection: .openAIRealtime,
defaultProvider: "elevenlabs",
defaultRealtimeModelId: "gpt-realtime-2")
#expect(parsed.requiresGatewayRealtimeTransport)
#expect(parsed.openAIRequiresGatewayRealtimeTransport)
#expect(parsed.executionMode == .realtimeRelay)
#expect(gatewayDefaultRouting.route == .realtimeRelay)
#expect(openAIRouting.route == .realtimeRelay)
}
@Test func `routes forced agent consultation through gateway relay without mode or brain`() {
let config: [String: Any] = [
"talk": [
"realtime": [
"provider": "openai",
"consultRouting": "force-agent-consult",
],
],
]
let parsed = TalkModeGatewayConfigParser.parse(
config: config,
defaultProvider: "elevenlabs",
defaultModelIdFallback: "eleven_v3",
defaultRealtimeModelIdFallback: "gpt-realtime-2",
defaultSilenceTimeoutMs: 900)
let routing = TalkModeRoutingResolver.resolve(
parsed: parsed,
providerSelection: .openAIRealtime,
defaultProvider: "elevenlabs",
defaultRealtimeModelId: "gpt-realtime-2")
#expect(parsed.requiresGatewayRealtimeTransport)
#expect(parsed.openAIRequiresGatewayRealtimeTransport)
#expect(routing.executionMode == .realtimeRelay)
#expect(routing.route == .realtimeRelay)
}
@Test func `keeps Azure open AI realtime on gateway relay`() {