test, refactor: follow-ups to LLMService generic refactor

Two follow-ups now that LLMService is generic over its adapter:

- Add an explicit backward-compat test verifying that an LLMService
  subclass with no generic parameter (the third-party-provider
  pattern) instantiates and returns a usable adapter. The existing
  MockLLMService (declared without brackets) already exercised this
  implicitly, but it's worth a named assertion.

- Drop the now-redundant `params: SomeLLMInvocationParams = ...`
  variable annotations on `adapter.get_llm_invocation_params()`
  results. Since `get_llm_adapter()` now returns the precise adapter
  type, and `BaseLLMAdapter` is generic in its invocation-params
  type, the call already infers the right TypedDict.
This commit is contained in:
Paul Kompfner
2026-04-28 09:43:07 -04:00
parent 49068ff557
commit c4f5f1ebbb
5 changed files with 29 additions and 8 deletions

View File

@@ -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),

View File

@@ -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

View File

@@ -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)
)

View File

@@ -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,

View File

@@ -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):