From 36e064b240bca43981df3d5ee17be59c177eb861 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Tue, 11 Mar 2025 07:33:05 +0000 Subject: [PATCH] Address PR feedback: Enhance type safety and add tests for multimodal validation Co-Authored-By: Joe Moura --- src/crewai/utilities/events/llm_events.py | 40 +++++++++++++++++++++-- tests/test_multimodal_validation.py | 24 ++++++++++++++ 2 files changed, 62 insertions(+), 2 deletions(-) diff --git a/src/crewai/utilities/events/llm_events.py b/src/crewai/utilities/events/llm_events.py index 10a648e86..2f2881e3e 100644 --- a/src/crewai/utilities/events/llm_events.py +++ b/src/crewai/utilities/events/llm_events.py @@ -1,5 +1,5 @@ from enum import Enum -from typing import Any, Dict, List, Optional, Union +from typing import Any, Dict, List, Literal, Optional, Union from crewai.utilities.events.base_events import CrewEvent @@ -11,11 +11,47 @@ class LLMCallType(Enum): LLM_CALL = "llm_call" +class ContentType(str, Enum): + """Types of content in multimodal messages""" + + TEXT = "text" + IMAGE_URL = "image_url" + + class LLMCallStartedEvent(CrewEvent): """Event emitted when a LLM call starts""" type: str = "llm_call_started" - messages: Union[str, List[Dict[str, Any]]] + messages: Union[ + str, + List[Union[ + str, + Dict[str, Union[ + str, + List[Dict[str, Union[ + str, + Dict[Literal["url"], str] + ]]] + ]] + ]] + ] + """ + Supports both string messages and structured messages including multimodal content. + Formats supported: + 1. Simple string: "This is a message" + 2. List of message objects: [{"role": "user", "content": "Hello"}] + 3. Mixed list with strings and objects: ["Simple message", {"role": "user", "content": "Hello"}] + 4. Multimodal format: + { + 'role': str, + 'content': List[ + Union[ + Dict[Literal["type", "text"], str], + Dict[Literal["type", "image_url"], Dict[str, str]] + ] + ] + } + """ tools: Optional[List[dict]] = None callbacks: Optional[List[Any]] = None available_functions: Optional[Dict[str, Any]] = None diff --git a/tests/test_multimodal_validation.py b/tests/test_multimodal_validation.py index 9057e4456..536d0f5d5 100644 --- a/tests/test_multimodal_validation.py +++ b/tests/test_multimodal_validation.py @@ -62,3 +62,27 @@ def test_llm_call_started_event_with_standard_messages(): assert event.messages[0]['content'] == 'You are a helpful assistant' assert event.messages[1]['role'] == 'user' assert event.messages[1]['content'] == 'Hello, how are you?' + + +def test_llm_call_started_event_with_mixed_content(): + """Test that LLMCallStartedEvent handles mixed content types.""" + mixed_messages = [ + "Simple string message", + { + 'role': 'user', + 'content': [ + {'type': 'text', 'text': 'With image'}, + {'type': 'image_url', 'image_url': {'url': 'https://example.com/image.jpg'}}, + ], + } + ] + + # This should not raise a ValidationError + event = LLMCallStartedEvent(messages=mixed_messages) + + # Verify the event was created correctly + assert isinstance(event.messages, list) + assert isinstance(event.messages[0], str) + assert isinstance(event.messages[1], dict) + assert event.messages[1]['role'] == 'user' + assert isinstance(event.messages[1]['content'], list)