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.
This commit is contained in:
Paul Kompfner
2026-04-23 18:32:46 -04:00
parent 0302f6d05c
commit c3eb69165c
3 changed files with 40 additions and 3 deletions

View File

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

View File

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

View File

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