From c113cacd59e2496393ca1651c167371c0dee546c Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Fri, 24 Apr 2026 10:10:03 -0400 Subject: [PATCH] refactor(types): name the LLMContext/OpenAI boundary with explicit cast helpers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit LLMContext's NotGiven, LLMContextToolChoice, and LLMStandardMessage are currently aliased to their OpenAI equivalents, so passing values between the two sides type-checks implicitly. That works today but obscures the fact that these are meant to be conceptually distinct — if LLMContext ever diverges from OpenAI's types, every implicit crossing would silently break. Introduce two module-private cast helpers in open_ai_adapter.py: - _openai_from_llm_context_tool_choice(tool_choice) - _openai_from_llm_standard_message(message) Both are typed no-ops today (implemented with typing.cast) but each carries a docstring explaining why the cast is present, and every boundary crossing now routes through a named function. Future readers (and future greps) can find the crossings; a later divergence becomes a mechanical find-and-update rather than hunting through adapter code. No behavior change, no pyright error delta. --- .../adapters/services/open_ai_adapter.py | 36 ++++++++++++++++--- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/src/pipecat/adapters/services/open_ai_adapter.py b/src/pipecat/adapters/services/open_ai_adapter.py index 212e4f8aa..b5ba63c75 100644 --- a/src/pipecat/adapters/services/open_ai_adapter.py +++ b/src/pipecat/adapters/services/open_ai_adapter.py @@ -6,7 +6,7 @@ """OpenAI LLM adapter for Pipecat.""" -from typing import Any, TypedDict, TypeGuard, TypeVar +from typing import Any, TypedDict, TypeGuard, TypeVar, cast from openai._types import NotGiven as OpenAINotGiven from openai.types.chat import ( @@ -22,12 +22,39 @@ from pipecat.processors.aggregators.llm_context import ( LLMContextMessage, LLMContextToolChoice, LLMSpecificMessage, + LLMStandardMessage, NotGiven, ) _T = TypeVar("_T") +def _openai_from_llm_context_tool_choice( + tool_choice: LLMContextToolChoice | NotGiven, +) -> ChatCompletionToolChoiceOptionParam | OpenAINotGiven: + """Reinterpret an LLMContext ``tool_choice`` as OpenAI's type. + + The underlying types are currently aliased — ``LLMContextToolChoice`` is + ``ChatCompletionToolChoiceOptionParam`` and LLMContext's ``NotGiven`` is + OpenAI's — so this is a typed no-op today. It's kept as a named boundary + so that if the LLMContext side ever diverges from OpenAI's types, every + crossing is visible and easy to update. + """ + return cast("ChatCompletionToolChoiceOptionParam | OpenAINotGiven", tool_choice) + + +def _openai_from_llm_standard_message( + message: LLMStandardMessage, +) -> ChatCompletionMessageParam: + """Reinterpret an LLMContext standard message as OpenAI's type. + + Same rationale as :func:`_openai_from_llm_context_tool_choice`: the + aliased types make this a no-op today, but the boundary is preserved + for future divergence. + """ + return cast("ChatCompletionMessageParam", message) + + def is_given(value: _T | OpenAINotGiven) -> TypeGuard[_T]: """Check whether a value was explicitly provided. @@ -117,7 +144,7 @@ class OpenAILLMAdapter(BaseLLMAdapter[OpenAILLMInvocationParams]): "messages": messages, # NOTE; LLMContext's tools are guaranteed to be a ToolsSchema (or NOT_GIVEN) "tools": self.from_standard_tools(context.tools), - "tool_choice": context.tool_choice, + "tool_choice": _openai_from_llm_context_tool_choice(context.tool_choice), } def to_provider_tools_format(self, tools_schema: ToolsSchema) -> list[ChatCompletionToolParam]: @@ -166,7 +193,7 @@ class OpenAILLMAdapter(BaseLLMAdapter[OpenAILLMInvocationParams]): result.append(message.message) else: # Standard message, pass through unchanged - result.append(message) + result.append(_openai_from_llm_standard_message(message)) if convert_developer_to_user: for msg in result: @@ -178,5 +205,4 @@ class OpenAILLMAdapter(BaseLLMAdapter[OpenAILLMInvocationParams]): def _from_standard_tool_choice( self, tool_choice: LLMContextToolChoice | NotGiven ) -> ChatCompletionToolChoiceOptionParam | OpenAINotGiven: - # Just a pass-through: tool_choice is already the right type - return tool_choice + return _openai_from_llm_context_tool_choice(tool_choice)