Skip to content

Commit 0802e2f

Browse files
committed
agents streaming
1 parent 38de4bb commit 0802e2f

4 files changed

Lines changed: 115 additions & 0 deletions

File tree

Sources/ElevenLabs/Conversation.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,13 @@ public final class Conversation: ObservableObject, RoomDelegate {
474474
// Handle agent response corrections
475475
break
476476

477+
case let .agentChatResponsePart(e):
478+
if e.type == .stop {
479+
agentState = .listening
480+
} else {
481+
agentState = .speaking
482+
}
483+
477484
case .audio:
478485
// Don't change agent state - let voice activity detection handle it
479486
break

Sources/ElevenLabs/Protocol/EventParser.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,15 @@ enum EventParser {
205205
return .asrInitiationMetadata(ASRInitiationMetadataEvent(metadataData: metadataData))
206206
}
207207

208+
case "agent_chat_response_part":
209+
if let event = json["text_response_part"] as? [String: Any],
210+
let text = event["text"] as? String
211+
{
212+
let partTypeStr = event["type"] as? String ?? "delta"
213+
let partType = AgentChatResponsePartType(rawValue: partTypeStr) ?? .delta
214+
return .agentChatResponsePart(AgentChatResponsePartEvent(text: text, type: partType))
215+
}
216+
208217
case "error":
209218
// Skip for now as requested
210219
break

Sources/ElevenLabs/Protocol/IncomingEvents.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ public enum IncomingEvent: Sendable {
88
case tentativeUserTranscript(TentativeUserTranscriptEvent)
99
case agentResponse(AgentResponseEvent)
1010
case agentResponseCorrection(AgentResponseCorrectionEvent)
11+
case agentChatResponsePart(AgentChatResponsePartEvent)
1112
case audio(AudioEvent)
1213
case interruption(InterruptionEvent)
1314
case vadScore(VadScoreEvent)
@@ -22,6 +23,12 @@ public enum IncomingEvent: Sendable {
2223
case error(ErrorEvent)
2324
}
2425

26+
public enum AgentChatResponsePartType: String, Sendable {
27+
case start = "start"
28+
case delta = "delta"
29+
case stop = "stop"
30+
}
31+
2532
/// User's speech transcription
2633
public struct UserTranscriptEvent: Sendable {
2734
public let transcript: String
@@ -47,6 +54,11 @@ public struct AgentResponseCorrectionEvent: Sendable {
4754
public let eventId: Int
4855
}
4956

57+
public struct AgentChatResponsePartEvent: Sendable {
58+
public let text: String
59+
public let type: AgentChatResponsePartType
60+
}
61+
5062
/// Audio data from the agent
5163
public struct AudioEvent: Sendable {
5264
public let audioBase64: String

Tests/ElevenLabsTests/Tests/EventParserTests.swift

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,4 +175,91 @@ final class EventParserTests: XCTestCase {
175175

176176
XCTAssertThrowsError(try EventParser.parseIncomingEvent(from: json))
177177
}
178+
179+
func testParseAgentChatResponsePartStart() throws {
180+
let json = """
181+
{
182+
"type": "agent_chat_response_part",
183+
"text_response_part": {
184+
"text": "",
185+
"type": "start"
186+
}
187+
}
188+
""".data(using: .utf8)!
189+
190+
let event = try EventParser.parseIncomingEvent(from: json)
191+
192+
guard case let .agentChatResponsePart(part) = event else {
193+
XCTFail("Expected agentChatResponsePart event")
194+
return
195+
}
196+
197+
XCTAssertEqual(part.text, "")
198+
XCTAssertEqual(part.type, .start)
199+
}
200+
201+
func testParseAgentChatResponsePartDelta() throws {
202+
let json = """
203+
{
204+
"type": "agent_chat_response_part",
205+
"text_response_part": {
206+
"text": "Hello",
207+
"type": "delta"
208+
}
209+
}
210+
""".data(using: .utf8)!
211+
212+
let event = try EventParser.parseIncomingEvent(from: json)
213+
214+
guard case let .agentChatResponsePart(part) = event else {
215+
XCTFail("Expected agentChatResponsePart event")
216+
return
217+
}
218+
219+
XCTAssertEqual(part.text, "Hello")
220+
XCTAssertEqual(part.type, .delta)
221+
}
222+
223+
func testParseAgentChatResponsePartStop() throws {
224+
let json = """
225+
{
226+
"type": "agent_chat_response_part",
227+
"text_response_part": {
228+
"text": "",
229+
"type": "stop"
230+
}
231+
}
232+
""".data(using: .utf8)!
233+
234+
let event = try EventParser.parseIncomingEvent(from: json)
235+
236+
guard case let .agentChatResponsePart(part) = event else {
237+
XCTFail("Expected agentChatResponsePart event")
238+
return
239+
}
240+
241+
XCTAssertEqual(part.text, "")
242+
XCTAssertEqual(part.type, .stop)
243+
}
244+
245+
func testParseAgentChatResponsePartDefaultsToDelta() throws {
246+
let json = """
247+
{
248+
"type": "agent_chat_response_part",
249+
"text_response_part": {
250+
"text": "Test"
251+
}
252+
}
253+
""".data(using: .utf8)!
254+
255+
let event = try EventParser.parseIncomingEvent(from: json)
256+
257+
guard case let .agentChatResponsePart(part) = event else {
258+
XCTFail("Expected agentChatResponsePart event")
259+
return
260+
}
261+
262+
XCTAssertEqual(part.text, "Test")
263+
XCTAssertEqual(part.type, .delta)
264+
}
178265
}

0 commit comments

Comments
 (0)