From 061826a26497750e2fa7e5c57d07caf5af173e6a Mon Sep 17 00:00:00 2001 From: jamesrochabrun Date: Sun, 16 Nov 2025 23:06:32 -0800 Subject: [PATCH 1/2] Add response.done message handling for Realtime API error reporting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add responseDone case to OpenAIRealtimeMessage enum to capture response completion events - Implement handler for response.done messages in OpenAIRealtimeSession - Parse and expose error details from status_details.error for API errors (insufficient_quota, invalid_api_key, etc.) - Improve logging: upgrade unhandled message warnings from debug to warning level - Add detailed JSON logging for debugging unhandled message types This change ensures that critical API errors like quota exhaustion and authentication failures are properly surfaced to consuming applications instead of being silently discarded. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../Realtime/OpenAIRealtimeSession.swift | 26 +++++++++++++++++-- .../Realtime/OpenAIRealtimeMessage.swift | 3 +++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/Sources/OpenAI/Private/Realtime/OpenAIRealtimeSession.swift b/Sources/OpenAI/Private/Realtime/OpenAIRealtimeSession.swift index bba3bef..2cc009c 100644 --- a/Sources/OpenAI/Private/Realtime/OpenAIRealtimeSession.swift +++ b/Sources/OpenAI/Private/Realtime/OpenAIRealtimeSession.swift @@ -249,9 +249,31 @@ open class OpenAIRealtimeSession { continuation?.yield(.mcpListToolsFailed(fullError)) + case "response.done": + // Handle response completion (may contain errors like insufficient_quota) + if let response = json["response"] as? [String: Any], + let status = response["status"] as? String { + + logger.debug("Response done with status: \(status)") + + // Pass the full response object for detailed error handling + continuation?.yield(.responseDone(status: status, statusDetails: response)) + + // Log errors for debugging + if let statusDetails = response["status_details"] as? [String: Any], + let error = statusDetails["error"] as? [String: Any] { + let code = error["code"] as? String ?? "unknown" + let message = error["message"] as? String ?? "Unknown error" + logger.error("Response error: [\(code)] \(message)") + } + } else { + logger.warning("Received response.done with unexpected format") + } + default: - // Log unhandled message types for debugging - logger.debug("Unhandled message type: \(messageType)") + // Log unhandled message types with more detail for debugging + logger.warning("⚠️ Unhandled message type: \(messageType)") + logger.debug("Full JSON: \(String(describing: json))") break } diff --git a/Sources/OpenAI/Public/ResponseModels/Realtime/OpenAIRealtimeMessage.swift b/Sources/OpenAI/Public/ResponseModels/Realtime/OpenAIRealtimeMessage.swift index 2545ee3..c32490b 100644 --- a/Sources/OpenAI/Public/ResponseModels/Realtime/OpenAIRealtimeMessage.swift +++ b/Sources/OpenAI/Public/ResponseModels/Realtime/OpenAIRealtimeMessage.swift @@ -26,4 +26,7 @@ public enum OpenAIRealtimeMessage: Sendable { case mcpListToolsInProgress // "mcp_list_tools.in_progress" case mcpListToolsCompleted([String: Any]) // "mcp_list_tools.completed" with tools data case mcpListToolsFailed(String?) // "mcp_list_tools.failed" with error details + + // Response completion with potential errors + case responseDone(status: String, statusDetails: [String: Any]?) // "response.done" } From 1bc5b037763238dd5059b3dce8fbf335cf729d6a Mon Sep 17 00:00:00 2001 From: jamesrochabrun Date: Sun, 16 Nov 2025 23:10:50 -0800 Subject: [PATCH 2/2] Fix SwiftFormat lint issues --- .../Private/Realtime/OpenAIRealtimeSession.swift | 13 ++++++++----- .../Realtime/OpenAIRealtimeMessage.swift | 2 +- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/Sources/OpenAI/Private/Realtime/OpenAIRealtimeSession.swift b/Sources/OpenAI/Private/Realtime/OpenAIRealtimeSession.swift index 2cc009c..1a11963 100644 --- a/Sources/OpenAI/Private/Realtime/OpenAIRealtimeSession.swift +++ b/Sources/OpenAI/Private/Realtime/OpenAIRealtimeSession.swift @@ -251,17 +251,20 @@ open class OpenAIRealtimeSession { case "response.done": // Handle response completion (may contain errors like insufficient_quota) - if let response = json["response"] as? [String: Any], - let status = response["status"] as? String { - + if + let response = json["response"] as? [String: Any], + let status = response["status"] as? String + { logger.debug("Response done with status: \(status)") // Pass the full response object for detailed error handling continuation?.yield(.responseDone(status: status, statusDetails: response)) // Log errors for debugging - if let statusDetails = response["status_details"] as? [String: Any], - let error = statusDetails["error"] as? [String: Any] { + if + let statusDetails = response["status_details"] as? [String: Any], + let error = statusDetails["error"] as? [String: Any] + { let code = error["code"] as? String ?? "unknown" let message = error["message"] as? String ?? "Unknown error" logger.error("Response error: [\(code)] \(message)") diff --git a/Sources/OpenAI/Public/ResponseModels/Realtime/OpenAIRealtimeMessage.swift b/Sources/OpenAI/Public/ResponseModels/Realtime/OpenAIRealtimeMessage.swift index c32490b..cda21a9 100644 --- a/Sources/OpenAI/Public/ResponseModels/Realtime/OpenAIRealtimeMessage.swift +++ b/Sources/OpenAI/Public/ResponseModels/Realtime/OpenAIRealtimeMessage.swift @@ -27,6 +27,6 @@ public enum OpenAIRealtimeMessage: Sendable { case mcpListToolsCompleted([String: Any]) // "mcp_list_tools.completed" with tools data case mcpListToolsFailed(String?) // "mcp_list_tools.failed" with error details - // Response completion with potential errors + /// Response completion with potential errors case responseDone(status: String, statusDetails: [String: Any]?) // "response.done" }