diff --git a/src/pipecat/services/anthropic/llm.py b/src/pipecat/services/anthropic/llm.py index c028ec7aa..cd00f15b8 100644 --- a/src/pipecat/services/anthropic/llm.py +++ b/src/pipecat/services/anthropic/llm.py @@ -329,7 +329,7 @@ class AnthropicLLMService(LLMService[AnthropicLLMAdapter]): def _get_llm_invocation_params(self, context: LLMContext) -> AnthropicLLMInvocationParams: adapter = self.get_llm_adapter() - params: AnthropicLLMInvocationParams = adapter.get_llm_invocation_params( + params = adapter.get_llm_invocation_params( context, enable_prompt_caching=assert_given(self._settings.enable_prompt_caching), system_instruction=assert_given(self._settings.system_instruction), diff --git a/src/pipecat/services/aws/llm.py b/src/pipecat/services/aws/llm.py index 7266821a1..6e68c5c6d 100644 --- a/src/pipecat/services/aws/llm.py +++ b/src/pipecat/services/aws/llm.py @@ -283,7 +283,7 @@ class AWSBedrockLLMService(LLMService[AWSBedrockLLMAdapter]): self._settings.system_instruction ) adapter = self.get_llm_adapter() - params: AWSBedrockLLMInvocationParams = adapter.get_llm_invocation_params( + params = adapter.get_llm_invocation_params( context, system_instruction=effective_instruction ) messages = params["messages"] @@ -372,7 +372,7 @@ class AWSBedrockLLMService(LLMService[AWSBedrockLLMAdapter]): def _get_llm_invocation_params(self, context: LLMContext) -> AWSBedrockLLMInvocationParams: adapter = self.get_llm_adapter() - params: AWSBedrockLLMInvocationParams = adapter.get_llm_invocation_params( + params = adapter.get_llm_invocation_params( context, system_instruction=assert_given(self._settings.system_instruction) ) return params diff --git a/src/pipecat/services/google/llm.py b/src/pipecat/services/google/llm.py index 355291e1b..da9659872 100644 --- a/src/pipecat/services/google/llm.py +++ b/src/pipecat/services/google/llm.py @@ -21,7 +21,7 @@ from loguru import logger from PIL import Image from pydantic import BaseModel, Field -from pipecat.adapters.services.gemini_adapter import GeminiLLMAdapter, GeminiLLMInvocationParams +from pipecat.adapters.services.gemini_adapter import GeminiLLMAdapter from pipecat.frames.frames import ( AssistantImageRawFrame, Frame, @@ -292,7 +292,7 @@ class GoogleLLMService(LLMService[GeminiLLMAdapter]): tools = [] effective_instruction = system_instruction or self._settings.system_instruction adapter = self.get_llm_adapter() - params: GeminiLLMInvocationParams = adapter.get_llm_invocation_params( + params = adapter.get_llm_invocation_params( context, system_instruction=effective_instruction ) messages = params["messages"] @@ -387,7 +387,7 @@ class GoogleLLMService(LLMService[GeminiLLMAdapter]): async def _stream_content(self, context: LLMContext) -> AsyncIterator[GenerateContentResponse]: adapter = self.get_llm_adapter() - params: GeminiLLMInvocationParams = adapter.get_llm_invocation_params( + params = adapter.get_llm_invocation_params( context, system_instruction=assert_given(self._settings.system_instruction) ) diff --git a/src/pipecat/services/openai/base_llm.py b/src/pipecat/services/openai/base_llm.py index 144927a8c..72d4360a9 100644 --- a/src/pipecat/services/openai/base_llm.py +++ b/src/pipecat/services/openai/base_llm.py @@ -297,7 +297,7 @@ class BaseOpenAILLMService(LLMService[OpenAILLMAdapter]): f"{self}: Generating chat from context {adapter.get_messages_for_logging(context)}" ) - params_from_context: OpenAILLMInvocationParams = adapter.get_llm_invocation_params( + params_from_context = adapter.get_llm_invocation_params( context, system_instruction=self._settings.system_instruction, convert_developer_to_user=not self.supports_developer_role, @@ -374,7 +374,7 @@ class BaseOpenAILLMService(LLMService[OpenAILLMAdapter]): """ effective_instruction = system_instruction or self._settings.system_instruction adapter = self.get_llm_adapter() - invocation_params: OpenAILLMInvocationParams = adapter.get_llm_invocation_params( + invocation_params = adapter.get_llm_invocation_params( context, system_instruction=effective_instruction, convert_developer_to_user=not self.supports_developer_role, diff --git a/tests/test_llm_service.py b/tests/test_llm_service.py index c7018d9f0..707c255f3 100644 --- a/tests/test_llm_service.py +++ b/tests/test_llm_service.py @@ -7,6 +7,8 @@ import unittest from unittest.mock import AsyncMock, patch +from pipecat.adapters.base_llm_adapter import BaseLLMAdapter +from pipecat.adapters.services.open_ai_adapter import OpenAILLMAdapter from pipecat.frames.frames import ( FunctionCallFromLLM, FunctionCallInProgressFrame, @@ -39,6 +41,25 @@ class MockLLMService(LLMService): super().__init__(settings=settings, **kwargs) +class TestUnparameterizedSubclass(unittest.TestCase): + """Backward-compat coverage: third-party providers subclass LLMService + without specifying a generic adapter parameter. That should keep working + after LLMService became `Generic[TAdapter]`. + """ + + def test_unparameterized_subclass_instantiates(self): + # MockLLMService is declared as `class MockLLMService(LLMService):` + # — no generic bracket. The TypeVar's `bound=BaseLLMAdapter` should + # resolve TAdapter to BaseLLMAdapter for callers that don't opt in. + service = MockLLMService() + adapter = service.get_llm_adapter() + + # Default adapter_class is OpenAILLMAdapter; the runtime instance + # should reflect that, regardless of how generics are erased. + self.assertIsInstance(adapter, OpenAILLMAdapter) + self.assertIsInstance(adapter, BaseLLMAdapter) + + class TestLLMService(unittest.IsolatedAsyncioTestCase): async def _run_function_calls_inline(self, service: MockLLMService): async def run_inline(runner_items):