From c3eb69165c901ebde2de213f2e7ff964b33eb79f Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Thu, 23 Apr 2026 18:32:46 -0400 Subject: [PATCH] fix(types): accept SDK NotGiven in LLM Settings fields used for passthrough Three LLM services initialize certain Settings fields with the SDK's NOT_GIVEN (openai.NOT_GIVEN or anthropic.NOT_GIVEN) so the value flows unmodified into SDK API calls. The inherited field types from LLMSettings only admit pipecat's _NotGiven, so pyright flagged each constructor call as a flavor mismatch. Widen the field types in each service-specific Settings subclass so they accept both pipecat's _NotGiven (for delta-mode defaults) and the corresponding SDK NotGiven (for store-mode passthrough): - OpenAILLMSettings: frequency_penalty, presence_penalty, seed, temperature, top_p, max_tokens, max_completion_tokens. - OpenAIResponsesLLMSettings: temperature, top_p, max_completion_tokens. - AnthropicLLMSettings: temperature, top_k, top_p, thinking. Every overridden field is genuinely read from self._settings and passed directly to the SDK, so none of the overrides are vestigial. Clears 21 pyright errors and restores test_service_settings_complete parity with the pre-NOT_GIVEN-swap state. --- src/pipecat/services/anthropic/llm.py | 11 ++++++++++- src/pipecat/services/openai/base_llm.py | 20 +++++++++++++++++++- src/pipecat/services/openai/responses/llm.py | 12 +++++++++++- 3 files changed, 40 insertions(+), 3 deletions(-) diff --git a/src/pipecat/services/anthropic/llm.py b/src/pipecat/services/anthropic/llm.py index 916bc351c..138aa6981 100644 --- a/src/pipecat/services/anthropic/llm.py +++ b/src/pipecat/services/anthropic/llm.py @@ -44,6 +44,7 @@ from pipecat.utils.tracing.service_decorators import traced_llm try: from anthropic import NOT_GIVEN, APITimeoutError, AsyncAnthropic + from anthropic import NotGiven as AnthropicNotGiven except ModuleNotFoundError as e: logger.error(f"Exception: {e}") logger.error("In order to use Anthropic, you need to `pip install pipecat-ai[anthropic]`.") @@ -79,7 +80,15 @@ class AnthropicLLMSettings(LLMSettings): """ enable_prompt_caching: bool | _NotGiven = field(default_factory=lambda: _NOT_GIVEN) - thinking: Union["AnthropicLLMService.ThinkingConfig", _NotGiven] = field( + # Override inherited LLMSettings fields to also accept anthropic's NotGiven + # sentinel. The service stores anthropic's NOT_GIVEN in these fields so + # they can be passed through unchanged to the AsyncAnthropic client. + temperature: float | None | _NotGiven | AnthropicNotGiven = field( + default_factory=lambda: _NOT_GIVEN + ) + top_k: int | None | _NotGiven | AnthropicNotGiven = field(default_factory=lambda: _NOT_GIVEN) + top_p: float | None | _NotGiven | AnthropicNotGiven = field(default_factory=lambda: _NOT_GIVEN) + thinking: Union["AnthropicLLMService.ThinkingConfig", _NotGiven, AnthropicNotGiven] = field( default_factory=lambda: _NOT_GIVEN ) diff --git a/src/pipecat/services/openai/base_llm.py b/src/pipecat/services/openai/base_llm.py index 2933c528d..8f494b193 100644 --- a/src/pipecat/services/openai/base_llm.py +++ b/src/pipecat/services/openai/base_llm.py @@ -22,6 +22,7 @@ from openai import ( AsyncStream, DefaultAsyncHttpxClient, ) +from openai._types import NotGiven as OpenAINotGiven from openai.types.chat import ChatCompletionChunk from pydantic import BaseModel, Field @@ -50,7 +51,24 @@ class OpenAILLMSettings(LLMSettings): max_completion_tokens: Maximum completion tokens to generate. """ - max_completion_tokens: int | _NotGiven = field(default_factory=lambda: _NOT_GIVEN) + # Override inherited LLMSettings fields to also accept openai's NotGiven + # sentinel. The service stores openai's NOT_GIVEN in these fields so they + # can be passed through unchanged to the AsyncOpenAI client. + frequency_penalty: float | None | _NotGiven | OpenAINotGiven = field( + default_factory=lambda: _NOT_GIVEN + ) + presence_penalty: float | None | _NotGiven | OpenAINotGiven = field( + default_factory=lambda: _NOT_GIVEN + ) + seed: int | None | _NotGiven | OpenAINotGiven = field(default_factory=lambda: _NOT_GIVEN) + temperature: float | None | _NotGiven | OpenAINotGiven = field( + default_factory=lambda: _NOT_GIVEN + ) + top_p: float | None | _NotGiven | OpenAINotGiven = field(default_factory=lambda: _NOT_GIVEN) + max_tokens: int | None | _NotGiven | OpenAINotGiven = field(default_factory=lambda: _NOT_GIVEN) + max_completion_tokens: int | _NotGiven | OpenAINotGiven = field( + default_factory=lambda: _NOT_GIVEN + ) class BaseOpenAILLMService(LLMService): diff --git a/src/pipecat/services/openai/responses/llm.py b/src/pipecat/services/openai/responses/llm.py index adfdebd22..6528303f9 100644 --- a/src/pipecat/services/openai/responses/llm.py +++ b/src/pipecat/services/openai/responses/llm.py @@ -18,6 +18,7 @@ from typing import Any import httpx from loguru import logger from openai import NOT_GIVEN, AsyncOpenAI, AsyncStream, DefaultAsyncHttpxClient +from openai._types import NotGiven as OpenAINotGiven from openai.types.responses import ( ResponseCompletedEvent, ResponseFunctionCallArgumentsDeltaEvent, @@ -97,7 +98,16 @@ class OpenAIResponsesLLMSettings(LLMSettings): max_completion_tokens: Maximum completion tokens to generate. """ - max_completion_tokens: int | _NotGiven = field(default_factory=lambda: _NOT_GIVEN) + # Override inherited LLMSettings fields to also accept openai's NotGiven + # sentinel. The service stores openai's NOT_GIVEN in these fields so they + # can be passed through unchanged to the AsyncOpenAI client. + temperature: float | None | _NotGiven | OpenAINotGiven = field( + default_factory=lambda: _NOT_GIVEN + ) + top_p: float | None | _NotGiven | OpenAINotGiven = field(default_factory=lambda: _NOT_GIVEN) + max_completion_tokens: int | _NotGiven | OpenAINotGiven = field( + default_factory=lambda: _NOT_GIVEN + ) # ---------------------------------------------------------------------------