diff --git a/tests/daily_test/test_daily_agent_websocket.py b/tests/daily_test/test_daily_agent_websocket.py new file mode 100644 index 00000000..90ade82a --- /dev/null +++ b/tests/daily_test/test_daily_agent_websocket.py @@ -0,0 +1,630 @@ +# Copyright 2024 Deepgram SDK contributors. All Rights Reserved. +# Use of this source code is governed by a MIT license that can be found in the LICENSE file. +# SPDX-License-Identifier: MIT + +import contextlib +import os +import json +import pytest +import hashlib +import time +from typing import Dict, Any, List, Optional + +from deepgram import ( + DeepgramClient, + DeepgramClientOptions, + AgentWebSocketEvents, + SettingsOptions, + InjectUserMessageOptions, + FunctionCallRequest, + FunctionCallResponse, + InjectAgentMessageOptions, +) + +from tests.utils import save_metadata_string + +# Enhanced test configurations covering all agent functionality +test_cases = [ + { + "name": "basic_conversation", + "description": "Basic conversation with simple questions", + "agent_config": { + "think": { + "provider": {"type": "open_ai", "model": "gpt-4o-mini"}, + "prompt": "You are a helpful AI assistant. Keep responses brief and conversational." + }, + "speak": {"provider": {"type": "deepgram", "model": "aura-2-thalia-en"}}, + "listen": {"provider": {"type": "deepgram", "model": "nova-3"}}, + "language": "en" + }, + "inject_messages": [ + "Hello, can you help me with a simple question?", + "What is 2 + 2?", + "Thank you for your help." + ], + "expected_events": [ + "Welcome", + "SettingsApplied", + "ConversationText", + "AgentAudioDone" + ], + "test_inject_user_message": True, + "test_inject_agent_message": False, + "test_function_calls": False + }, + { + "name": "fallback_providers", + "description": "Test fallback functionality with multiple speak providers", + "agent_config": { + "think": { + "provider": {"type": "open_ai", "model": "gpt-4o-mini"}, + "prompt": "You are a helpful assistant. Keep responses brief." + }, + "speak": [ + {"provider": {"type": "deepgram", "model": "aura-2-thalia-en"}}, + {"provider": {"type": "deepgram", "model": "aura-2-luna-en"}} + ], + "listen": {"provider": {"type": "deepgram", "model": "nova-3"}}, + "language": "en" + }, + "inject_messages": [ + "Hello, can you test speaking with fallback providers?", + "Please say something else to test the fallback." + ], + "expected_events": [ + "Welcome", + "SettingsApplied", + "ConversationText", + "AgentAudioDone" + ], + "test_inject_user_message": True, + "test_inject_agent_message": False, + "test_function_calls": False + }, + { + "name": "inject_agent_message", + "description": "Test inject_agent_message functionality (expected to fail until #553 is resolved)", + "agent_config": { + "think": { + "provider": {"type": "open_ai", "model": "gpt-4o-mini"}, + "prompt": "You are a helpful assistant. Keep responses brief and conversational." + }, + "speak": {"provider": {"type": "deepgram", "model": "aura-2-thalia-en"}}, + "listen": {"provider": {"type": "deepgram", "model": "nova-3"}}, + "language": "en" + }, + "inject_messages": [ + "Hello, I'm going to inject some agent messages." + ], + "agent_messages": [ + "Hello! I'm an agent message injected directly.", + "This is another agent message to test the functionality." + ], + "expected_events": [ + "Welcome", + "SettingsApplied", + "ConversationText" + ], + "conditional_events": [ + "AgentStartedSpeaking", + "AgentAudioDone" + ], + "test_inject_user_message": True, + "test_inject_agent_message": True, + "test_function_calls": False, + "expect_error": True # Still expecting errors due to SDK function calling bugs (#528) + }, + { + "name": "function_call_conversation", + "description": "Test function calling with corrected HTTP method case (expected to fail due to #528)", + "agent_config": { + "think": { + "provider": {"type": "open_ai", "model": "gpt-4o-mini"}, + "prompt": "You are a helpful assistant that can call functions to get weather information.", + "functions": [ + { + "name": "get_weather", + "description": "Get current weather information for a location", + "parameters": { + "type": "object", + "properties": { + "location": { + "type": "string", + "description": "The location to get weather for" + } + }, + "required": ["location"] + }, + "method": "get", + "url": "https://api.example.com/weather" + } + ] + }, + "speak": {"provider": {"type": "deepgram", "model": "aura-2-thalia-en"}}, + "listen": {"provider": {"type": "deepgram", "model": "nova-3"}}, + "language": "en" + }, + "inject_messages": [ + "What's the weather like in New York?", + "Can you also check the weather in London?" + ], + "expected_events": [ + "Welcome", + "SettingsApplied", + "ConversationText" + ], + "conditional_events": [ + "FunctionCallRequest", + "AgentStartedSpeaking", + "AgentAudioDone" + ], + "test_inject_user_message": True, + "test_inject_agent_message": False, + "test_function_calls": True, + "expect_error": True # Still expecting errors due to SDK function calling bugs + }, + # NOTE: function_call_conversation and inject_agent_message tests are marked as xfail + # - function_call_conversation: #528 - SDK function calling structure doesn't match new API spec + # - inject_agent_message: #553 - SDK missing inject_agent_message method implementation + # TODO: These should be re-enabled once the bugs are fixed +] + + +@pytest.mark.parametrize("test_case", test_cases) +def test_daily_agent_websocket(test_case: Dict[str, Any]): + """ + Enhanced test for agent websocket functionality with comprehensive coverage. + + This test covers: + 1. Basic conversation flow + 2. Function calling + 3. Fallback provider functionality + 4. InjectUserMessage and InjectAgentMessage + 5. Comprehensive event validation + 6. Error handling and recovery + + Note: Some events like EndOfThought may appear as "Unhandled" - this is expected + as they are not officially documented as supported features yet. + + Note: some features might have bugs, like inject_agent_message and function_call_conversation. We intend to fix these in the future and update the tests. + """ + + # Mark tests as expected to fail for known issues + if test_case["name"] == "inject_agent_message": + pytest.xfail(reason="#553 - inject_agent_message method not implemented in SDK") + elif test_case["name"] == "function_call_conversation": + pytest.xfail(reason="#528 - SDK function calling structure doesn't match new API spec") + + # Check for required environment variables + if not os.getenv("DEEPGRAM_API_KEY"): + pytest.skip("DEEPGRAM_API_KEY environment variable not set") + + # Setup unique test ID + test_name = test_case["name"] + config_hash = hashlib.sha256(json.dumps(test_case["agent_config"], sort_keys=True).encode()).hexdigest() + unique = f"{test_name}-{config_hash[:8]}" + + print(f"\n{'='*60}") + print(f"Running Test: {test_name}") + print(f"Description: {test_case['description']}") + print(f"Test ID: {unique}") + print(f"{'='*60}") + + # File paths for metadata + file_config = f"tests/response_data/agent/websocket/{unique}-config.json" + file_events = f"tests/response_data/agent/websocket/{unique}-events.json" + file_error = f"tests/response_data/agent/websocket/{unique}-error.json" + file_function_calls = f"tests/response_data/agent/websocket/{unique}-function_calls.json" + + # Cleanup previous runs + for file_path in [file_config, file_events, file_error, file_function_calls]: + with contextlib.suppress(FileNotFoundError): + os.remove(file_path) + + # Test state tracking + received_events = [] + conversation_text_list = [] + function_calls = [] + function_call_bugs = [] + injection_refused_events = [] + connection_established = False + conversation_complete = False + + # Create Deepgram client with enhanced options + config = DeepgramClientOptions( + options={ + "keepalive": "true", + "experimental": "true" # Enable experimental features + } + ) + deepgram = DeepgramClient("", config) + dg_connection = deepgram.agent.websocket.v("1") + + # Enhanced event handlers + def on_open(self, open, **kwargs): + nonlocal connection_established + connection_established = True + received_events.append({ + "type": "Open", + "timestamp": time.time(), + "data": open.to_dict() if hasattr(open, 'to_dict') else str(open) + }) + print(f"āœ“ Connection opened at {time.time()}") + + def on_welcome(self, welcome, **kwargs): + received_events.append({ + "type": "Welcome", + "timestamp": time.time(), + "data": welcome.to_dict() + }) + print(f"āœ“ Welcome received: {welcome.to_dict()}") + + def on_settings_applied(self, settings_applied, **kwargs): + received_events.append({ + "type": "SettingsApplied", + "timestamp": time.time(), + "data": settings_applied.to_dict() + }) + print(f"āœ“ Settings applied: {settings_applied.to_dict()}") + + def on_conversation_text(self, conversation_text, **kwargs): + conversation_text_list.append(conversation_text.to_dict()) + received_events.append({ + "type": "ConversationText", + "timestamp": time.time(), + "data": conversation_text.to_dict() + }) + print(f"šŸ’¬ Conversation text: {conversation_text.to_dict()}") + + def on_function_call_request(self, function_call_request: FunctionCallRequest, **kwargs): + """ + Enhanced function call handler that tests for SDK bugs. + + The official API spec expects: + - functions: array of {id, name, arguments, client_side} + + But the SDK currently has: + - function_name: string + - function_call_id: string + - input: string + """ + function_call_data = function_call_request.to_dict() + function_calls.append(function_call_data) + received_events.append({ + "type": "FunctionCallRequest", + "timestamp": time.time(), + "data": function_call_data + }) + + print(f"šŸ”§ Function call request: {function_call_data}") + + # Test for SDK bug: Check current SDK structure vs official API spec + sdk_bug_detected = False + bug_details = {} + + # Check for SDK's current incorrect structure + if "function_name" in function_call_data and "function_call_id" in function_call_data: + sdk_bug_detected = True + bug_details.update({ + "bug_type": "incorrect_sdk_structure", + "current_sdk_fields": ["function_name", "function_call_id", "input"], + "expected_api_fields": ["functions"], + "description": "SDK uses flat structure instead of functions array" + }) + + # Check for missing official API spec fields + if "functions" not in function_call_data: + sdk_bug_detected = True + bug_details.update({ + "missing_field": "functions", + "description": "Official API spec requires 'functions' array" + }) + + if sdk_bug_detected: + function_call_bugs.append({ + "timestamp": time.time(), + "request_data": function_call_data, + "bug_details": bug_details + }) + print(f"🚨 SDK Bug detected: {bug_details}") + + # Respond to function call using current SDK structure (even though it's wrong) + try: + if hasattr(function_call_request, 'function_call_id'): + # Use SDK's incorrect structure + response = FunctionCallResponse( + function_call_id=function_call_request.function_call_id, + output=json.dumps({ + "success": True, + "result": "Mock function response", + "timestamp": time.time() + }) + ) + dg_connection.send_function_call_response(response) + print(f"āœ“ Function call response sent using SDK structure") + else: + print(f"āŒ Cannot respond to function call - no function_call_id field") + except Exception as e: + print(f"āŒ Function call response failed: {e}") + received_events.append({ + "type": "FunctionCallResponseError", + "timestamp": time.time(), + "error": str(e) + }) + + def on_agent_started_speaking(self, agent_started_speaking, **kwargs): + received_events.append({ + "type": "AgentStartedSpeaking", + "timestamp": time.time(), + "data": agent_started_speaking.to_dict() + }) + print(f"šŸ—£ļø Agent started speaking: {agent_started_speaking.to_dict()}") + + def on_agent_audio_done(self, agent_audio_done, **kwargs): + received_events.append({ + "type": "AgentAudioDone", + "timestamp": time.time(), + "data": agent_audio_done.to_dict() + }) + print(f"āœ“ Agent audio done: {agent_audio_done.to_dict()}") + + def on_injection_refused(self, injection_refused, **kwargs): + injection_refused_events.append(injection_refused.to_dict()) + received_events.append({ + "type": "InjectionRefused", + "timestamp": time.time(), + "data": injection_refused.to_dict() + }) + print(f"āŒ Injection refused: {injection_refused.to_dict()}") + + def on_error(self, error, **kwargs): + received_events.append({ + "type": "Error", + "timestamp": time.time(), + "data": error.to_dict() + }) + print(f"āŒ Error: {error.to_dict()}") + + def on_unhandled(self, unhandled, **kwargs): + received_events.append({ + "type": "Unhandled", + "timestamp": time.time(), + "data": unhandled.to_dict() + }) + # Note: EndOfThought events are expected to be unhandled as they're not officially documented as supported features yet + print(f"ā“ Unhandled: {unhandled.to_dict()}") + + # Register all event handlers + dg_connection.on(AgentWebSocketEvents.Open, on_open) + dg_connection.on(AgentWebSocketEvents.Welcome, on_welcome) + dg_connection.on(AgentWebSocketEvents.SettingsApplied, on_settings_applied) + dg_connection.on(AgentWebSocketEvents.ConversationText, on_conversation_text) + dg_connection.on(AgentWebSocketEvents.FunctionCallRequest, on_function_call_request) + dg_connection.on(AgentWebSocketEvents.AgentStartedSpeaking, on_agent_started_speaking) + dg_connection.on(AgentWebSocketEvents.AgentAudioDone, on_agent_audio_done) + dg_connection.on(AgentWebSocketEvents.InjectionRefused, on_injection_refused) + dg_connection.on(AgentWebSocketEvents.Error, on_error) + dg_connection.on(AgentWebSocketEvents.Unhandled, on_unhandled) + + try: + # Create enhanced settings from test case + settings = SettingsOptions() + settings.agent = test_case["agent_config"] + settings.experimental = True # Enable experimental features + + print(f"šŸ”§ Starting connection with settings: {settings.to_dict()}") + + # Test 1: Connection establishment + print("\n--- Test 1: Connection Establishment ---") + connection_started = dg_connection.start(settings) + assert connection_started, f"Test ID: {unique} - Connection should start successfully" + + # Wait for connection establishment with timeout + timeout = 0 + while not connection_established and timeout < 15: + time.sleep(0.5) + timeout += 1 + + assert connection_established, f"Test ID: {unique} - Should receive Open event within 15 seconds" + print("āœ“ Connection established successfully") + + # Test 2: Inject user messages and validate responses + if test_case.get("test_inject_user_message", False): + print("\n--- Test 2: InjectUserMessage Testing ---") + for i, message in enumerate(test_case["inject_messages"]): + print(f"šŸ“¤ Injecting user message {i+1}: '{message}'") + time.sleep(1) # Allow previous conversation to settle + + options = InjectUserMessageOptions(content=message) + inject_success = dg_connection.inject_user_message(options) + assert inject_success, f"Test ID: {unique} - InjectUserMessage should succeed for message {i+1}" + + # Wait for agent response with improved timeout handling + response_timeout = 0 + initial_event_count = len(received_events) + + while response_timeout < 30: + if len(received_events) > initial_event_count: + recent_events = [e["type"] for e in received_events[initial_event_count:]] + if "ConversationText" in recent_events or "AgentStartedSpeaking" in recent_events: + print(f"āœ“ Agent responded to message {i+1}") + break + time.sleep(0.5) + response_timeout += 1 + + if response_timeout >= 30: + print(f"āš ļø Agent did not respond to message {i+1} within timeout") + + # Test 3: Inject agent messages (if enabled) + if test_case.get("test_inject_agent_message", False): + print("\n--- Test 3: InjectAgentMessage Testing ---") + for i, message in enumerate(test_case.get("agent_messages", [])): + print(f"šŸ“¤ Injecting agent message {i+1}: '{message}'") + time.sleep(1) # Allow previous conversation to settle + + options = InjectAgentMessageOptions(message=message) + inject_success = dg_connection.inject_agent_message(options) + + if inject_success: + print(f"āœ“ Agent message {i+1} injected successfully") + else: + print(f"āŒ Agent message {i+1} injection failed") + + # Wait for any response or events + response_timeout = 0 + initial_event_count = len(received_events) + + while response_timeout < 15: + if len(received_events) > initial_event_count: + recent_events = [e["type"] for e in received_events[initial_event_count:]] + print(f"šŸ“Š Events after agent message {i+1}: {recent_events}") + break + time.sleep(0.5) + response_timeout += 1 + + if response_timeout >= 15: + print(f"āš ļø No events received after agent message {i+1}") + + # Allow final processing + time.sleep(3) + print("\n--- Test Results Analysis ---") + + # Test 4: Validate expected events were received + event_types = [event["type"] for event in received_events] + print(f"šŸ“Š Received events: {event_types}") + + # Check for required events (always expected) + for expected_event in test_case["expected_events"]: + assert expected_event in event_types, f"Test ID: {unique} - Should receive {expected_event} event" + print(f"āœ“ Expected event received: {expected_event}") + + # Check for conditional events (only if no error expected or no error occurred) + conditional_events = test_case.get("conditional_events", []) + expect_error = test_case.get("expect_error", False) + + if conditional_events: + if expect_error: + # For error scenarios, check if conditional events are present but don't require them + for conditional_event in conditional_events: + if conditional_event in event_types: + print(f"āœ“ Conditional event received: {conditional_event}") + else: + print(f"ā„¹ļø Conditional event not received (expected in error scenario): {conditional_event}") + else: + # For non-error scenarios, require conditional events + for conditional_event in conditional_events: + assert conditional_event in event_types, f"Test ID: {unique} - Should receive {conditional_event} event" + print(f"āœ“ Conditional event received: {conditional_event}") + + # Test 5: Validate conversation flow + if test_case.get("test_inject_user_message", False) and test_case["inject_messages"]: + assert len(conversation_text_list) > 0, f"Test ID: {unique} - Should receive conversation text" + print(f"āœ“ Conversation flow validated ({len(conversation_text_list)} conversation texts)") + + # Test 6: Validate function calls and detect SDK bugs + if test_case.get("test_function_calls", False): + print("\n--- Function Call Analysis ---") + if len(function_calls) > 0: + print(f"āœ“ Function calls received: {len(function_calls)}") + + # Analyze function call structure for SDK bugs + for i, func_call in enumerate(function_calls): + print(f"Function call {i+1}: {func_call}") + + # Test current SDK structure (incorrect) + sdk_fields = ["function_name", "function_call_id", "input"] + api_fields = ["functions"] + + has_sdk_fields = all(field in func_call for field in sdk_fields) + has_api_fields = any(field in func_call for field in api_fields) + + if has_sdk_fields and not has_api_fields: + print(f"🚨 SDK Bug confirmed: Function call uses incorrect structure") + print(f" Current SDK fields: {[f for f in sdk_fields if f in func_call]}") + print(f" Missing API fields: {[f for f in api_fields if f not in func_call]}") + elif has_api_fields: + print(f"āœ“ Correct API structure detected") + else: + print(f"ā“ Unexpected function call structure") + + print(f"šŸ“Š SDK bugs detected: {len(function_call_bugs)}") + for bug in function_call_bugs: + print(f" Bug: {bug['bug_details']}") + + elif "function_call_conversation" in test_case["name"]: + print(f"āŒ Expected function calls but none received") + # This might be expected if the bug prevents function calls from working + else: + print("ā„¹ļø No function calls expected or received") + + # Test 7: Validate injection refused events + if len(injection_refused_events) > 0: + print(f"šŸ“Š Injection refused events: {len(injection_refused_events)}") + for event in injection_refused_events: + print(f" Refused: {event}") + + conversation_complete = True + print("\nāœ… All tests completed successfully!") + + except Exception as e: + print(f"\nāŒ Test failed with exception: {e}") + error_data = { + "error": str(e), + "events": received_events, + "function_calls": function_calls, + "function_call_bugs": function_call_bugs, + "conversation_texts": conversation_text_list, + "injection_refused": injection_refused_events + } + save_metadata_string(file_error, json.dumps(error_data, indent=2)) + raise + + finally: + # Cleanup connection + print("\nšŸ”§ Cleaning up connection...") + if dg_connection: + dg_connection.finish() + time.sleep(1) + + # Save comprehensive test metadata + save_metadata_string(file_config, json.dumps(test_case, indent=2)) + save_metadata_string(file_events, json.dumps(received_events, indent=2)) + + # Save function call analysis + if function_calls or function_call_bugs: + function_call_analysis = { + "function_calls": function_calls, + "sdk_bugs_detected": function_call_bugs, + "total_calls": len(function_calls), + "total_bugs": len(function_call_bugs) + } + save_metadata_string(file_function_calls, json.dumps(function_call_analysis, indent=2)) + + # Final comprehensive validations + assert conversation_complete, f"Test ID: {unique} - Conversation should complete successfully" + assert len(received_events) >= len(test_case["expected_events"]), f"Test ID: {unique} - Should receive minimum expected events" + + # Report test summary + print(f"\nšŸ“‹ Test Summary for {unique}:") + print(f" Events received: {len(received_events)}") + print(f" Conversation texts: {len(conversation_text_list)}") + print(f" Function calls: {len(function_calls)}") + print(f" SDK bugs detected: {len(function_call_bugs)}") + print(f" Injection refused: {len(injection_refused_events)}") + + # Count and report unhandled events + unhandled_events = [e for e in received_events if e["type"] == "Unhandled"] + if unhandled_events: + print(f" Unhandled events: {len(unhandled_events)} (expected for undocumented features like EndOfThought)") + + # If function call bugs were detected, provide detailed information + if function_call_bugs: + print(f"\n🚨 IMPORTANT: SDK Function Call Bugs Detected!") + print(f" The SDK implementation does not match the official API specification.") + print(f" See {file_function_calls} for detailed analysis.") + + # This assertion will fail if bugs are detected, highlighting the issue + if test_case.get("test_function_calls", False): + # Don't fail the test for the bug detection - that's expected + # But log it clearly for the developer + print(f" This is the expected bug you wanted to test for.") diff --git a/tests/response_data/agent/websocket/basic_conversation-a40b2785-config.json b/tests/response_data/agent/websocket/basic_conversation-a40b2785-config.json new file mode 100644 index 00000000..0bdf4dab --- /dev/null +++ b/tests/response_data/agent/websocket/basic_conversation-a40b2785-config.json @@ -0,0 +1,40 @@ +{ + "name": "basic_conversation", + "description": "Basic conversation with simple questions", + "agent_config": { + "think": { + "provider": { + "type": "open_ai", + "model": "gpt-4o-mini" + }, + "prompt": "You are a helpful AI assistant. Keep responses brief and conversational." + }, + "speak": { + "provider": { + "type": "deepgram", + "model": "aura-2-thalia-en" + } + }, + "listen": { + "provider": { + "type": "deepgram", + "model": "nova-3" + } + }, + "language": "en" + }, + "inject_messages": [ + "Hello, can you help me with a simple question?", + "What is 2 + 2?", + "Thank you for your help." + ], + "expected_events": [ + "Welcome", + "SettingsApplied", + "ConversationText", + "AgentAudioDone" + ], + "test_inject_user_message": true, + "test_inject_agent_message": false, + "test_function_calls": false +} \ No newline at end of file diff --git a/tests/response_data/agent/websocket/basic_conversation-a40b2785-events.json b/tests/response_data/agent/websocket/basic_conversation-a40b2785-events.json new file mode 100644 index 00000000..bcf06759 --- /dev/null +++ b/tests/response_data/agent/websocket/basic_conversation-a40b2785-events.json @@ -0,0 +1,215 @@ +[ + { + "type": "Welcome", + "timestamp": 1752530127.050476, + "data": { + "type": "Welcome", + "request_id": "472b08e0-7b15-4356-9809-01c7a842a8b5" + } + }, + { + "type": "Open", + "timestamp": 1752530127.050602, + "data": { + "type": "Open" + } + }, + { + "type": "SettingsApplied", + "timestamp": 1752530127.090345, + "data": { + "type": "SettingsApplied" + } + }, + { + "type": "ConversationText", + "timestamp": 1752530128.093272, + "data": { + "type": "ConversationText", + "role": "user", + "content": "Hello, can you help me with a simple question?" + } + }, + { + "type": "Unhandled", + "timestamp": 1752530128.094154, + "data": { + "type": "Unhandled", + "raw": "{\"type\":\"History\",\"role\":\"user\",\"content\":\"Hello, can you help me with a simple question?\"}" + } + }, + { + "type": "Unhandled", + "timestamp": 1752530128.0947661, + "data": { + "type": "Unhandled", + "raw": "{\"type\":\"EndOfThought\"}" + } + }, + { + "type": "ConversationText", + "timestamp": 1752530128.828342, + "data": { + "type": "ConversationText", + "role": "assistant", + "content": "Of course!" + } + }, + { + "type": "Unhandled", + "timestamp": 1752530128.8294551, + "data": { + "type": "Unhandled", + "raw": "{\"type\":\"History\",\"role\":\"assistant\",\"content\":\"Of course!\"}" + } + }, + { + "type": "AgentStartedSpeaking", + "timestamp": 1752530128.830455, + "data": { + "total_latency": 0.73322023, + "tts_latency": 0.309270146, + "ttt_latency": 0.423949872 + } + }, + { + "type": "ConversationText", + "timestamp": 1752530129.596714, + "data": { + "type": "ConversationText", + "role": "user", + "content": "What is 2 + 2?" + } + }, + { + "type": "Unhandled", + "timestamp": 1752530129.597664, + "data": { + "type": "Unhandled", + "raw": "{\"type\":\"History\",\"role\":\"user\",\"content\":\"What is 2 + 2?\"}" + } + }, + { + "type": "Unhandled", + "timestamp": 1752530129.5983481, + "data": { + "type": "Unhandled", + "raw": "{\"type\":\"EndOfThought\"}" + } + }, + { + "type": "AgentAudioDone", + "timestamp": 1752530129.598641, + "data": { + "type": "AgentAudioDone" + } + }, + { + "type": "ConversationText", + "timestamp": 1752530130.478443, + "data": { + "type": "ConversationText", + "role": "assistant", + "content": "2 + 2 equals 4!" + } + }, + { + "type": "Unhandled", + "timestamp": 1752530130.4791858, + "data": { + "type": "Unhandled", + "raw": "{\"type\":\"History\",\"role\":\"assistant\",\"content\":\"2 + 2 equals 4!\"}" + } + }, + { + "type": "AgentStartedSpeaking", + "timestamp": 1752530130.4796212, + "data": { + "total_latency": 0.85823752, + "tts_latency": 0.308952974, + "ttt_latency": 0.549284319 + } + }, + { + "type": "AgentAudioDone", + "timestamp": 1752530130.874018, + "data": { + "type": "AgentAudioDone" + } + }, + { + "type": "ConversationText", + "timestamp": 1752530131.098005, + "data": { + "type": "ConversationText", + "role": "user", + "content": "Thank you for your help." + } + }, + { + "type": "Unhandled", + "timestamp": 1752530131.0988889, + "data": { + "type": "Unhandled", + "raw": "{\"type\":\"History\",\"role\":\"user\",\"content\":\"Thank you for your help.\"}" + } + }, + { + "type": "Unhandled", + "timestamp": 1752530131.0994189, + "data": { + "type": "Unhandled", + "raw": "{\"type\":\"EndOfThought\"}" + } + }, + { + "type": "ConversationText", + "timestamp": 1752530131.756989, + "data": { + "type": "ConversationText", + "role": "assistant", + "content": "You\u2019re welcome!" + } + }, + { + "type": "Unhandled", + "timestamp": 1752530131.761224, + "data": { + "type": "Unhandled", + "raw": "{\"type\":\"History\",\"role\":\"assistant\",\"content\":\"You\u2019re welcome!\"}" + } + }, + { + "type": "AgentStartedSpeaking", + "timestamp": 1752530131.7613928, + "data": { + "total_latency": 0.663211677, + "tts_latency": 0.298665893, + "ttt_latency": 0.364545557 + } + }, + { + "type": "ConversationText", + "timestamp": 1752530132.8296478, + "data": { + "type": "ConversationText", + "role": "assistant", + "content": "If you have any more questions, feel free to ask!" + } + }, + { + "type": "Unhandled", + "timestamp": 1752530132.830574, + "data": { + "type": "Unhandled", + "raw": "{\"type\":\"History\",\"role\":\"assistant\",\"content\":\"If you have any more questions, feel free to ask!\"}" + } + }, + { + "type": "AgentAudioDone", + "timestamp": 1752530132.913596, + "data": { + "type": "AgentAudioDone" + } + } +] \ No newline at end of file diff --git a/tests/response_data/agent/websocket/basic_conversation-e5062e8c-config.json b/tests/response_data/agent/websocket/basic_conversation-e5062e8c-config.json new file mode 100644 index 00000000..14767e4c --- /dev/null +++ b/tests/response_data/agent/websocket/basic_conversation-e5062e8c-config.json @@ -0,0 +1,38 @@ +{ + "name": "basic_conversation", + "description": "Basic conversation with simple questions", + "agent_config": { + "think": { + "provider": { + "type": "open_ai", + "model": "gpt-4o-mini" + }, + "prompt": "You are a helpful AI assistant. Keep responses brief and conversational." + }, + "speak": { + "provider": { + "type": "deepgram", + "model": "aura-2-thalia-en" + } + }, + "listen": { + "provider": "deepgram", + "model": "nova-3" + }, + "language": "en" + }, + "inject_messages": [ + "Hello, can you help me with a simple question?", + "What is 2 + 2?", + "Thank you for your help." + ], + "expected_events": [ + "Welcome", + "SettingsApplied", + "ConversationText", + "AgentAudioDone" + ], + "test_inject_user_message": true, + "test_inject_agent_message": false, + "test_function_calls": false +} \ No newline at end of file diff --git a/tests/response_data/agent/websocket/basic_conversation-e5062e8c-error.json b/tests/response_data/agent/websocket/basic_conversation-e5062e8c-error.json new file mode 100644 index 00000000..71b451ed --- /dev/null +++ b/tests/response_data/agent/websocket/basic_conversation-e5062e8c-error.json @@ -0,0 +1,33 @@ +{ + "error": "Test ID: basic_conversation-e5062e8c - Should receive SettingsApplied event\nassert 'SettingsApplied' in ['Open', 'Welcome', 'Error']", + "events": [ + { + "type": "Open", + "timestamp": 1752529261.19441, + "data": { + "type": "Open" + } + }, + { + "type": "Welcome", + "timestamp": 1752529261.236177, + "data": { + "type": "Welcome", + "request_id": "b17f0618-a671-4fcf-aa55-c2328feeb40d" + } + }, + { + "type": "Error", + "timestamp": 1752529261.274173, + "data": { + "description": "Error parsing client message. Check the agent.listen.model field against the API spec.", + "message": "", + "type": "Error" + } + } + ], + "function_calls": [], + "function_call_bugs": [], + "conversation_texts": [], + "injection_refused": [] +} \ No newline at end of file diff --git a/tests/response_data/agent/websocket/basic_conversation-e5062e8c-events.json b/tests/response_data/agent/websocket/basic_conversation-e5062e8c-events.json new file mode 100644 index 00000000..2313aaa2 --- /dev/null +++ b/tests/response_data/agent/websocket/basic_conversation-e5062e8c-events.json @@ -0,0 +1,26 @@ +[ + { + "type": "Open", + "timestamp": 1752529261.19441, + "data": { + "type": "Open" + } + }, + { + "type": "Welcome", + "timestamp": 1752529261.236177, + "data": { + "type": "Welcome", + "request_id": "b17f0618-a671-4fcf-aa55-c2328feeb40d" + } + }, + { + "type": "Error", + "timestamp": 1752529261.274173, + "data": { + "description": "Error parsing client message. Check the agent.listen.model field against the API spec.", + "message": "", + "type": "Error" + } + } +] \ No newline at end of file diff --git a/tests/response_data/agent/websocket/comprehensive_conversation-67b42a09-config.json b/tests/response_data/agent/websocket/comprehensive_conversation-67b42a09-config.json new file mode 100644 index 00000000..081084fb --- /dev/null +++ b/tests/response_data/agent/websocket/comprehensive_conversation-67b42a09-config.json @@ -0,0 +1,50 @@ +{ + "name": "comprehensive_conversation", + "description": "Comprehensive test with multiple features", + "agent_config": { + "think": { + "provider": { + "type": "open_ai", + "model": "gpt-4o-mini" + }, + "prompt": "You are a helpful assistant with access to tools. Be conversational and helpful.", + "functions": [ + { + "name": "get_time", + "description": "Get current time", + "url": "https://worldtimeapi.org/api/timezone/Etc/UTC", + "method": "GET" + } + ] + }, + "speak": { + "provider": { + "type": "deepgram", + "model": "aura-2-thalia-en" + } + }, + "listen": { + "provider": { + "type": "deepgram", + "model": "nova-3" + } + }, + "language": "en" + }, + "inject_messages": [ + "Hello, I'd like to test multiple features.", + "What time is it?", + "Can you tell me a joke?" + ], + "expected_events": [ + "Welcome", + "SettingsApplied", + "ConversationText", + "FunctionCallRequest", + "AgentStartedSpeaking", + "AgentAudioDone" + ], + "test_inject_user_message": true, + "test_inject_agent_message": false, + "test_function_calls": true +} \ No newline at end of file diff --git a/tests/response_data/agent/websocket/comprehensive_conversation-67b42a09-error.json b/tests/response_data/agent/websocket/comprehensive_conversation-67b42a09-error.json new file mode 100644 index 00000000..b88dbd50 --- /dev/null +++ b/tests/response_data/agent/websocket/comprehensive_conversation-67b42a09-error.json @@ -0,0 +1,33 @@ +{ + "error": "Test ID: comprehensive_conversation-67b42a09 - Should receive SettingsApplied event\nassert 'SettingsApplied' in ['Open', 'Welcome', 'Error']", + "events": [ + { + "type": "Open", + "timestamp": 1752275914.618198, + "data": { + "type": "Open" + } + }, + { + "type": "Welcome", + "timestamp": 1752275914.646944, + "data": { + "type": "Welcome", + "request_id": "64bdfc12-2290-4ec4-a9b6-e3e71c202665" + } + }, + { + "type": "Error", + "timestamp": 1752275914.689991, + "data": { + "description": "Error parsing client message. Check the agent.think.functions[0].method field against the API spec.", + "message": "", + "type": "Error" + } + } + ], + "function_calls": [], + "function_call_bugs": [], + "conversation_texts": [], + "injection_refused": [] +} \ No newline at end of file diff --git a/tests/response_data/agent/websocket/comprehensive_conversation-67b42a09-events.json b/tests/response_data/agent/websocket/comprehensive_conversation-67b42a09-events.json new file mode 100644 index 00000000..0e64dcfc --- /dev/null +++ b/tests/response_data/agent/websocket/comprehensive_conversation-67b42a09-events.json @@ -0,0 +1,26 @@ +[ + { + "type": "Open", + "timestamp": 1752275914.618198, + "data": { + "type": "Open" + } + }, + { + "type": "Welcome", + "timestamp": 1752275914.646944, + "data": { + "type": "Welcome", + "request_id": "64bdfc12-2290-4ec4-a9b6-e3e71c202665" + } + }, + { + "type": "Error", + "timestamp": 1752275914.689991, + "data": { + "description": "Error parsing client message. Check the agent.think.functions[0].method field against the API spec.", + "message": "", + "type": "Error" + } + } +] \ No newline at end of file diff --git a/tests/response_data/agent/websocket/fallback_providers-e16542b1-config.json b/tests/response_data/agent/websocket/fallback_providers-e16542b1-config.json new file mode 100644 index 00000000..940a388c --- /dev/null +++ b/tests/response_data/agent/websocket/fallback_providers-e16542b1-config.json @@ -0,0 +1,47 @@ +{ + "name": "fallback_providers", + "description": "Test fallback functionality with multiple speak providers", + "agent_config": { + "think": { + "provider": { + "type": "open_ai", + "model": "gpt-4o-mini" + }, + "prompt": "You are a helpful assistant. Keep responses brief." + }, + "speak": [ + { + "provider": { + "type": "deepgram", + "model": "aura-2-thalia-en" + } + }, + { + "provider": { + "type": "deepgram", + "model": "aura-2-luna-en" + } + } + ], + "listen": { + "provider": { + "type": "deepgram", + "model": "nova-3" + } + }, + "language": "en" + }, + "inject_messages": [ + "Hello, can you test speaking with fallback providers?", + "Please say something else to test the fallback." + ], + "expected_events": [ + "Welcome", + "SettingsApplied", + "ConversationText", + "AgentAudioDone" + ], + "test_inject_user_message": true, + "test_inject_agent_message": false, + "test_function_calls": false +} \ No newline at end of file diff --git a/tests/response_data/agent/websocket/fallback_providers-e16542b1-events.json b/tests/response_data/agent/websocket/fallback_providers-e16542b1-events.json new file mode 100644 index 00000000..5da2c212 --- /dev/null +++ b/tests/response_data/agent/websocket/fallback_providers-e16542b1-events.json @@ -0,0 +1,174 @@ +[ + { + "type": "Welcome", + "timestamp": 1752530136.289093, + "data": { + "type": "Welcome", + "request_id": "5bbdcfdc-9ebd-4be0-8f79-fec5a3610561" + } + }, + { + "type": "Open", + "timestamp": 1752530136.2892659, + "data": { + "type": "Open" + } + }, + { + "type": "SettingsApplied", + "timestamp": 1752530136.328533, + "data": { + "type": "SettingsApplied" + } + }, + { + "type": "ConversationText", + "timestamp": 1752530137.3379571, + "data": { + "type": "ConversationText", + "role": "user", + "content": "Hello, can you test speaking with fallback providers?" + } + }, + { + "type": "Unhandled", + "timestamp": 1752530137.338785, + "data": { + "type": "Unhandled", + "raw": "{\"type\":\"History\",\"role\":\"user\",\"content\":\"Hello, can you test speaking with fallback providers?\"}" + } + }, + { + "type": "Unhandled", + "timestamp": 1752530137.339294, + "data": { + "type": "Unhandled", + "raw": "{\"type\":\"EndOfThought\"}" + } + }, + { + "type": "ConversationText", + "timestamp": 1752530137.937674, + "data": { + "type": "ConversationText", + "role": "assistant", + "content": "Hello!" + } + }, + { + "type": "Unhandled", + "timestamp": 1752530137.9388192, + "data": { + "type": "Unhandled", + "raw": "{\"type\":\"History\",\"role\":\"assistant\",\"content\":\"Hello!\"}" + } + }, + { + "type": "AgentStartedSpeaking", + "timestamp": 1752530137.9395552, + "data": { + "total_latency": 0.596796341, + "tts_latency": 0.302318792, + "ttt_latency": 0.294477495 + } + }, + { + "type": "ConversationText", + "timestamp": 1752530138.764678, + "data": { + "type": "ConversationText", + "role": "assistant", + "content": "I can\u2019t perform live tests or interactions with external systems." + } + }, + { + "type": "Unhandled", + "timestamp": 1752530138.765677, + "data": { + "type": "Unhandled", + "raw": "{\"type\":\"History\",\"role\":\"assistant\",\"content\":\"I can\u2019t perform live tests or interactions with external systems.\"}" + } + }, + { + "type": "ConversationText", + "timestamp": 1752530138.8807511, + "data": { + "type": "ConversationText", + "role": "user", + "content": "Please say something else to test the fallback." + } + }, + { + "type": "Unhandled", + "timestamp": 1752530138.881373, + "data": { + "type": "Unhandled", + "raw": "{\"type\":\"History\",\"role\":\"user\",\"content\":\"Please say something else to test the fallback.\"}" + } + }, + { + "type": "Unhandled", + "timestamp": 1752530138.881688, + "data": { + "type": "Unhandled", + "raw": "{\"type\":\"EndOfThought\"}" + } + }, + { + "type": "AgentAudioDone", + "timestamp": 1752530138.952996, + "data": { + "type": "AgentAudioDone" + } + }, + { + "type": "ConversationText", + "timestamp": 1752530139.439183, + "data": { + "type": "ConversationText", + "role": "assistant", + "content": "Sure!" + } + }, + { + "type": "Unhandled", + "timestamp": 1752530139.4404411, + "data": { + "type": "Unhandled", + "raw": "{\"type\":\"History\",\"role\":\"assistant\",\"content\":\"Sure!\"}" + } + }, + { + "type": "AgentStartedSpeaking", + "timestamp": 1752530139.441181, + "data": { + "total_latency": 0.595967156, + "tts_latency": 0.257245105, + "ttt_latency": 0.338721977 + } + }, + { + "type": "ConversationText", + "timestamp": 1752530140.0408692, + "data": { + "type": "ConversationText", + "role": "assistant", + "content": "How about this: \"This is a test message to check the fallback functionality.\"" + } + }, + { + "type": "Unhandled", + "timestamp": 1752530140.041711, + "data": { + "type": "Unhandled", + "raw": "{\"type\":\"History\",\"role\":\"assistant\",\"content\":\"How about this: \\\"This is a test message to check the fallback functionality.\\\"\"}" + } + }, + { + "type": "AgentAudioDone", + "timestamp": 1752530140.907404, + "data": { + "type": "AgentAudioDone" + } + } +] \ No newline at end of file diff --git a/tests/response_data/agent/websocket/function_call_conversation-71cdabfd-config.json b/tests/response_data/agent/websocket/function_call_conversation-71cdabfd-config.json new file mode 100644 index 00000000..e9042858 --- /dev/null +++ b/tests/response_data/agent/websocket/function_call_conversation-71cdabfd-config.json @@ -0,0 +1,61 @@ +{ + "name": "function_call_conversation", + "description": "Test function calling with weather API - demonstrates SDK bug", + "agent_config": { + "think": { + "provider": { + "type": "open_ai", + "model": "gpt-4o-mini" + }, + "prompt": "You are a helpful assistant that can check weather. Use the get_weather function when users ask about weather.", + "functions": [ + { + "name": "get_weather", + "description": "Get current weather for a location", + "url": "https://api.weather.com/v1/weather", + "method": "GET", + "parameters": { + "type": "object", + "properties": { + "location": { + "type": "string", + "description": "The city and state/country for the weather request" + } + }, + "required": [ + "location" + ] + } + } + ] + }, + "speak": { + "provider": { + "type": "deepgram", + "model": "aura-2-thalia-en" + } + }, + "listen": { + "provider": { + "type": "deepgram", + "model": "nova-3" + } + }, + "language": "en" + }, + "inject_messages": [ + "Hello, what's the weather like in New York?", + "How about in London?" + ], + "expected_events": [ + "Welcome", + "SettingsApplied", + "ConversationText", + "FunctionCallRequest", + "AgentStartedSpeaking", + "AgentAudioDone" + ], + "test_inject_user_message": true, + "test_inject_agent_message": false, + "test_function_calls": true +} \ No newline at end of file diff --git a/tests/response_data/agent/websocket/function_call_conversation-71cdabfd-error.json b/tests/response_data/agent/websocket/function_call_conversation-71cdabfd-error.json new file mode 100644 index 00000000..38ca7356 --- /dev/null +++ b/tests/response_data/agent/websocket/function_call_conversation-71cdabfd-error.json @@ -0,0 +1,33 @@ +{ + "error": "Test ID: function_call_conversation-71cdabfd - Should receive SettingsApplied event\nassert 'SettingsApplied' in ['Open', 'Welcome', 'Error']", + "events": [ + { + "type": "Open", + "timestamp": 1752275864.535816, + "data": { + "type": "Open" + } + }, + { + "type": "Welcome", + "timestamp": 1752275864.564481, + "data": { + "type": "Welcome", + "request_id": "56146094-4cc2-44e2-9ab3-a2898fe7837e" + } + }, + { + "type": "Error", + "timestamp": 1752275864.6050282, + "data": { + "description": "Error parsing client message. Check the agent.think.functions[0].method field against the API spec.", + "message": "", + "type": "Error" + } + } + ], + "function_calls": [], + "function_call_bugs": [], + "conversation_texts": [], + "injection_refused": [] +} \ No newline at end of file diff --git a/tests/response_data/agent/websocket/function_call_conversation-71cdabfd-events.json b/tests/response_data/agent/websocket/function_call_conversation-71cdabfd-events.json new file mode 100644 index 00000000..97c1e9c8 --- /dev/null +++ b/tests/response_data/agent/websocket/function_call_conversation-71cdabfd-events.json @@ -0,0 +1,26 @@ +[ + { + "type": "Open", + "timestamp": 1752275864.535816, + "data": { + "type": "Open" + } + }, + { + "type": "Welcome", + "timestamp": 1752275864.564481, + "data": { + "type": "Welcome", + "request_id": "56146094-4cc2-44e2-9ab3-a2898fe7837e" + } + }, + { + "type": "Error", + "timestamp": 1752275864.6050282, + "data": { + "description": "Error parsing client message. Check the agent.think.functions[0].method field against the API spec.", + "message": "", + "type": "Error" + } + } +] \ No newline at end of file diff --git a/tests/response_data/agent/websocket/inject_agent_message-800037f7-config.json b/tests/response_data/agent/websocket/inject_agent_message-800037f7-config.json new file mode 100644 index 00000000..78aa61e8 --- /dev/null +++ b/tests/response_data/agent/websocket/inject_agent_message-800037f7-config.json @@ -0,0 +1,43 @@ +{ + "name": "inject_agent_message", + "description": "Test InjectAgentMessage functionality", + "agent_config": { + "think": { + "provider": { + "type": "open_ai", + "model": "gpt-4o-mini" + }, + "prompt": "You are a helpful assistant. Respond naturally to injected messages." + }, + "speak": { + "provider": { + "type": "deepgram", + "model": "aura-2-thalia-en" + } + }, + "listen": { + "provider": { + "type": "deepgram", + "model": "nova-3" + } + }, + "language": "en" + }, + "inject_messages": [ + "Hello, I'm going to inject some agent messages." + ], + "agent_messages": [ + "I'm an injected agent message to test this functionality.", + "This is another injected message from the agent." + ], + "expected_events": [ + "Welcome", + "SettingsApplied", + "ConversationText", + "AgentStartedSpeaking", + "AgentAudioDone" + ], + "test_inject_user_message": true, + "test_inject_agent_message": true, + "test_function_calls": false +} \ No newline at end of file diff --git a/tests/response_data/agent/websocket/inject_agent_message-800037f7-error.json b/tests/response_data/agent/websocket/inject_agent_message-800037f7-error.json new file mode 100644 index 00000000..2301cd12 --- /dev/null +++ b/tests/response_data/agent/websocket/inject_agent_message-800037f7-error.json @@ -0,0 +1,93 @@ +{ + "error": "'AgentWebSocketClient' object has no attribute 'inject_agent_message'", + "events": [ + { + "type": "Open", + "timestamp": 1752275909.885432, + "data": { + "type": "Open" + } + }, + { + "type": "Welcome", + "timestamp": 1752275909.914839, + "data": { + "type": "Welcome", + "request_id": "4f34b723-a7ee-4dcb-b24a-aeaf20963905" + } + }, + { + "type": "SettingsApplied", + "timestamp": 1752275909.95723, + "data": { + "type": "SettingsApplied" + } + }, + { + "type": "ConversationText", + "timestamp": 1752275910.9646258, + "data": { + "type": "ConversationText", + "role": "user", + "content": "Hello, I'm going to inject some agent messages." + } + }, + { + "type": "Unhandled", + "timestamp": 1752275910.965034, + "data": { + "type": "Unhandled", + "raw": "{\"type\":\"History\",\"role\":\"user\",\"content\":\"Hello, I'm going to inject some agent messages.\"}" + } + }, + { + "type": "Unhandled", + "timestamp": 1752275910.965467, + "data": { + "type": "Unhandled", + "raw": "{\"type\":\"EndOfThought\"}" + } + }, + { + "type": "ConversationText", + "timestamp": 1752275911.849447, + "data": { + "type": "ConversationText", + "role": "assistant", + "content": "Sure, go ahead!" + } + }, + { + "type": "Unhandled", + "timestamp": 1752275911.849788, + "data": { + "type": "Unhandled", + "raw": "{\"type\":\"History\",\"role\":\"assistant\",\"content\":\"Sure, go ahead!\"}" + } + }, + { + "type": "AgentStartedSpeaking", + "timestamp": 1752275911.850084, + "data": { + "total_latency": 0.780141509, + "tts_latency": 0.30484981, + "ttt_latency": 0.475291647 + } + } + ], + "function_calls": [], + "function_call_bugs": [], + "conversation_texts": [ + { + "type": "ConversationText", + "role": "user", + "content": "Hello, I'm going to inject some agent messages." + }, + { + "type": "ConversationText", + "role": "assistant", + "content": "Sure, go ahead!" + } + ], + "injection_refused": [] +} \ No newline at end of file diff --git a/tests/response_data/agent/websocket/inject_agent_message-800037f7-events.json b/tests/response_data/agent/websocket/inject_agent_message-800037f7-events.json new file mode 100644 index 00000000..4d992c9a --- /dev/null +++ b/tests/response_data/agent/websocket/inject_agent_message-800037f7-events.json @@ -0,0 +1,75 @@ +[ + { + "type": "Open", + "timestamp": 1752275909.885432, + "data": { + "type": "Open" + } + }, + { + "type": "Welcome", + "timestamp": 1752275909.914839, + "data": { + "type": "Welcome", + "request_id": "4f34b723-a7ee-4dcb-b24a-aeaf20963905" + } + }, + { + "type": "SettingsApplied", + "timestamp": 1752275909.95723, + "data": { + "type": "SettingsApplied" + } + }, + { + "type": "ConversationText", + "timestamp": 1752275910.9646258, + "data": { + "type": "ConversationText", + "role": "user", + "content": "Hello, I'm going to inject some agent messages." + } + }, + { + "type": "Unhandled", + "timestamp": 1752275910.965034, + "data": { + "type": "Unhandled", + "raw": "{\"type\":\"History\",\"role\":\"user\",\"content\":\"Hello, I'm going to inject some agent messages.\"}" + } + }, + { + "type": "Unhandled", + "timestamp": 1752275910.965467, + "data": { + "type": "Unhandled", + "raw": "{\"type\":\"EndOfThought\"}" + } + }, + { + "type": "ConversationText", + "timestamp": 1752275911.849447, + "data": { + "type": "ConversationText", + "role": "assistant", + "content": "Sure, go ahead!" + } + }, + { + "type": "Unhandled", + "timestamp": 1752275911.849788, + "data": { + "type": "Unhandled", + "raw": "{\"type\":\"History\",\"role\":\"assistant\",\"content\":\"Sure, go ahead!\"}" + } + }, + { + "type": "AgentStartedSpeaking", + "timestamp": 1752275911.850084, + "data": { + "total_latency": 0.780141509, + "tts_latency": 0.30484981, + "ttt_latency": 0.475291647 + } + } +] \ No newline at end of file