refactor(types): name the LLMContext/OpenAI boundary with explicit cast helpers
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.
This commit is contained in:
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
"""OpenAI LLM adapter for Pipecat."""
|
"""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 import NotGiven as OpenAINotGiven
|
||||||
from openai.types.chat import (
|
from openai.types.chat import (
|
||||||
@@ -22,12 +22,39 @@ from pipecat.processors.aggregators.llm_context import (
|
|||||||
LLMContextMessage,
|
LLMContextMessage,
|
||||||
LLMContextToolChoice,
|
LLMContextToolChoice,
|
||||||
LLMSpecificMessage,
|
LLMSpecificMessage,
|
||||||
|
LLMStandardMessage,
|
||||||
NotGiven,
|
NotGiven,
|
||||||
)
|
)
|
||||||
|
|
||||||
_T = TypeVar("_T")
|
_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]:
|
def is_given(value: _T | OpenAINotGiven) -> TypeGuard[_T]:
|
||||||
"""Check whether a value was explicitly provided.
|
"""Check whether a value was explicitly provided.
|
||||||
|
|
||||||
@@ -117,7 +144,7 @@ class OpenAILLMAdapter(BaseLLMAdapter[OpenAILLMInvocationParams]):
|
|||||||
"messages": messages,
|
"messages": messages,
|
||||||
# NOTE; LLMContext's tools are guaranteed to be a ToolsSchema (or NOT_GIVEN)
|
# NOTE; LLMContext's tools are guaranteed to be a ToolsSchema (or NOT_GIVEN)
|
||||||
"tools": self.from_standard_tools(context.tools),
|
"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]:
|
def to_provider_tools_format(self, tools_schema: ToolsSchema) -> list[ChatCompletionToolParam]:
|
||||||
@@ -166,7 +193,7 @@ class OpenAILLMAdapter(BaseLLMAdapter[OpenAILLMInvocationParams]):
|
|||||||
result.append(message.message)
|
result.append(message.message)
|
||||||
else:
|
else:
|
||||||
# Standard message, pass through unchanged
|
# Standard message, pass through unchanged
|
||||||
result.append(message)
|
result.append(_openai_from_llm_standard_message(message))
|
||||||
|
|
||||||
if convert_developer_to_user:
|
if convert_developer_to_user:
|
||||||
for msg in result:
|
for msg in result:
|
||||||
@@ -178,5 +205,4 @@ class OpenAILLMAdapter(BaseLLMAdapter[OpenAILLMInvocationParams]):
|
|||||||
def _from_standard_tool_choice(
|
def _from_standard_tool_choice(
|
||||||
self, tool_choice: LLMContextToolChoice | NotGiven
|
self, tool_choice: LLMContextToolChoice | NotGiven
|
||||||
) -> ChatCompletionToolChoiceOptionParam | OpenAINotGiven:
|
) -> ChatCompletionToolChoiceOptionParam | OpenAINotGiven:
|
||||||
# Just a pass-through: tool_choice is already the right type
|
return _openai_from_llm_context_tool_choice(tool_choice)
|
||||||
return tool_choice
|
|
||||||
|
|||||||
Reference in New Issue
Block a user