Fix forward reference crash in Google and Anthropic LLM ThinkingConfig
ThinkingConfig was defined as an inner class on the service but referenced in the Settings dataclass declared before the service class, causing a crash at import time. Move ThinkingConfig to a standalone class defined before Settings, and keep a class attribute alias for backward compatibility.
This commit is contained in:
@@ -70,6 +70,25 @@ except ModuleNotFoundError as e:
|
|||||||
raise Exception(f"Missing module: {e}")
|
raise Exception(f"Missing module: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
class AnthropicThinkingConfig(BaseModel):
|
||||||
|
"""Configuration for extended thinking.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
type: Type of thinking mode (currently only "enabled" or "disabled").
|
||||||
|
budget_tokens: Maximum number of tokens for thinking.
|
||||||
|
With today's models, the minimum is 1024.
|
||||||
|
Only allowed if type is "enabled".
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Why `| str` here? To not break compatibility in case Anthropic adds
|
||||||
|
# more types in the future.
|
||||||
|
type: Literal["enabled", "disabled"] | str
|
||||||
|
|
||||||
|
# Why not enforce minimnum of 1024 here? To not break compatibility in
|
||||||
|
# case Anthropic changes this requirement in the future.
|
||||||
|
budget_tokens: int
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class AnthropicLLMSettings(LLMSettings):
|
class AnthropicLLMSettings(LLMSettings):
|
||||||
"""Settings for Anthropic LLM services.
|
"""Settings for Anthropic LLM services.
|
||||||
@@ -80,20 +99,18 @@ class AnthropicLLMSettings(LLMSettings):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
enable_prompt_caching: bool | _NotGiven = field(default_factory=lambda: _NOT_GIVEN)
|
enable_prompt_caching: bool | _NotGiven = field(default_factory=lambda: _NOT_GIVEN)
|
||||||
thinking: "AnthropicLLMService.ThinkingConfig" | _NotGiven = field(
|
thinking: AnthropicThinkingConfig | _NotGiven = field(default_factory=lambda: _NOT_GIVEN)
|
||||||
default_factory=lambda: _NOT_GIVEN
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_mapping(cls, settings):
|
def from_mapping(cls, settings):
|
||||||
"""Convert a plain dict to settings, coercing thinking dicts.
|
"""Convert a plain dict to settings, coercing thinking dicts.
|
||||||
|
|
||||||
For backward compatibility, a ``thinking`` value that is a plain dict
|
For backward compatibility, a ``thinking`` value that is a plain dict
|
||||||
is converted to a :class:`AnthropicLLMService.ThinkingConfig`.
|
is converted to a :class:`AnthropicThinkingConfig`.
|
||||||
"""
|
"""
|
||||||
instance = super().from_mapping(settings)
|
instance = super().from_mapping(settings)
|
||||||
if is_given(instance.thinking) and isinstance(instance.thinking, dict):
|
if is_given(instance.thinking) and isinstance(instance.thinking, dict):
|
||||||
instance.thinking = AnthropicLLMService.ThinkingConfig(**instance.thinking)
|
instance.thinking = AnthropicThinkingConfig(**instance.thinking)
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
|
|
||||||
@@ -148,23 +165,8 @@ class AnthropicLLMService(LLMService):
|
|||||||
# Overriding the default adapter to use the Anthropic one.
|
# Overriding the default adapter to use the Anthropic one.
|
||||||
adapter_class = AnthropicLLMAdapter
|
adapter_class = AnthropicLLMAdapter
|
||||||
|
|
||||||
class ThinkingConfig(BaseModel):
|
# Backward compatibility: ThinkingConfig used to be defined inline here.
|
||||||
"""Configuration for extended thinking.
|
ThinkingConfig = AnthropicThinkingConfig
|
||||||
|
|
||||||
Parameters:
|
|
||||||
type: Type of thinking mode (currently only "enabled" or "disabled").
|
|
||||||
budget_tokens: Maximum number of tokens for thinking.
|
|
||||||
With today's models, the minimum is 1024.
|
|
||||||
Only allowed if type is "enabled".
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Why `| str` here? To not break compatibility in case Anthropic adds
|
|
||||||
# more types in the future.
|
|
||||||
type: Literal["enabled", "disabled"] | str
|
|
||||||
|
|
||||||
# Why not enforce minimnum of 1024 here? To not break compatibility in
|
|
||||||
# case Anthropic changes this requirement in the future.
|
|
||||||
budget_tokens: int
|
|
||||||
|
|
||||||
class InputParams(BaseModel):
|
class InputParams(BaseModel):
|
||||||
"""Input parameters for Anthropic model inference.
|
"""Input parameters for Anthropic model inference.
|
||||||
@@ -193,9 +195,7 @@ class AnthropicLLMService(LLMService):
|
|||||||
temperature: Optional[float] = Field(default_factory=lambda: NOT_GIVEN, ge=0.0, le=1.0)
|
temperature: Optional[float] = Field(default_factory=lambda: NOT_GIVEN, ge=0.0, le=1.0)
|
||||||
top_k: Optional[int] = Field(default_factory=lambda: NOT_GIVEN, ge=0)
|
top_k: Optional[int] = Field(default_factory=lambda: NOT_GIVEN, ge=0)
|
||||||
top_p: Optional[float] = Field(default_factory=lambda: NOT_GIVEN, ge=0.0, le=1.0)
|
top_p: Optional[float] = Field(default_factory=lambda: NOT_GIVEN, ge=0.0, le=1.0)
|
||||||
thinking: Optional["AnthropicLLMService.ThinkingConfig"] = Field(
|
thinking: Optional[AnthropicThinkingConfig] = Field(default_factory=lambda: NOT_GIVEN)
|
||||||
default_factory=lambda: NOT_GIVEN
|
|
||||||
)
|
|
||||||
extra: Optional[Dict[str, Any]] = Field(default_factory=dict)
|
extra: Optional[Dict[str, Any]] = Field(default_factory=dict)
|
||||||
|
|
||||||
def model_post_init(self, __context):
|
def model_post_init(self, __context):
|
||||||
|
|||||||
@@ -673,45 +673,7 @@ class GoogleLLMContext(OpenAILLMContext):
|
|||||||
self._messages = [m for m in self._messages if m.parts]
|
self._messages = [m for m in self._messages if m.parts]
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
class GoogleThinkingConfig(BaseModel):
|
||||||
class GoogleLLMSettings(LLMSettings):
|
|
||||||
"""Settings for Google LLM services.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
thinking: Thinking configuration.
|
|
||||||
"""
|
|
||||||
|
|
||||||
thinking: "GoogleLLMService.ThinkingConfig" | _NotGiven = field(
|
|
||||||
default_factory=lambda: NOT_GIVEN
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_mapping(cls, settings):
|
|
||||||
"""Convert a plain dict to settings, coercing thinking dicts.
|
|
||||||
|
|
||||||
For backward compatibility, a ``thinking`` value that is a plain dict
|
|
||||||
is converted to a :class:`GoogleLLMService.ThinkingConfig`.
|
|
||||||
"""
|
|
||||||
instance = super().from_mapping(settings)
|
|
||||||
if is_given(instance.thinking) and isinstance(instance.thinking, dict):
|
|
||||||
instance.thinking = GoogleLLMService.ThinkingConfig(**instance.thinking)
|
|
||||||
return instance
|
|
||||||
|
|
||||||
|
|
||||||
class GoogleLLMService(LLMService):
|
|
||||||
"""Google AI (Gemini) LLM service implementation.
|
|
||||||
|
|
||||||
This class implements inference with Google's AI models, translating internally
|
|
||||||
from an OpenAILLMContext or a universal LLMContext to the messages format
|
|
||||||
expected by the Google AI model.
|
|
||||||
"""
|
|
||||||
|
|
||||||
_settings: GoogleLLMSettings
|
|
||||||
|
|
||||||
# Overriding the default adapter to use the Gemini one.
|
|
||||||
adapter_class = GeminiLLMAdapter
|
|
||||||
|
|
||||||
class ThinkingConfig(BaseModel):
|
|
||||||
"""Configuration for controlling the model's internal "thinking" process used before generating a response.
|
"""Configuration for controlling the model's internal "thinking" process used before generating a response.
|
||||||
|
|
||||||
Gemini 2.5 and 3 series models have this thinking process.
|
Gemini 2.5 and 3 series models have this thinking process.
|
||||||
@@ -743,6 +705,46 @@ class GoogleLLMService(LLMService):
|
|||||||
|
|
||||||
include_thoughts: Optional[bool] = Field(default=None)
|
include_thoughts: Optional[bool] = Field(default=None)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class GoogleLLMSettings(LLMSettings):
|
||||||
|
"""Settings for Google LLM services.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
thinking: Thinking configuration.
|
||||||
|
"""
|
||||||
|
|
||||||
|
thinking: GoogleThinkingConfig | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_mapping(cls, settings):
|
||||||
|
"""Convert a plain dict to settings, coercing thinking dicts.
|
||||||
|
|
||||||
|
For backward compatibility, a ``thinking`` value that is a plain dict
|
||||||
|
is converted to a :class:`GoogleThinkingConfig`.
|
||||||
|
"""
|
||||||
|
instance = super().from_mapping(settings)
|
||||||
|
if is_given(instance.thinking) and isinstance(instance.thinking, dict):
|
||||||
|
instance.thinking = GoogleThinkingConfig(**instance.thinking)
|
||||||
|
return instance
|
||||||
|
|
||||||
|
|
||||||
|
class GoogleLLMService(LLMService):
|
||||||
|
"""Google AI (Gemini) LLM service implementation.
|
||||||
|
|
||||||
|
This class implements inference with Google's AI models, translating internally
|
||||||
|
from an OpenAILLMContext or a universal LLMContext to the messages format
|
||||||
|
expected by the Google AI model.
|
||||||
|
"""
|
||||||
|
|
||||||
|
_settings: GoogleLLMSettings
|
||||||
|
|
||||||
|
# Overriding the default adapter to use the Gemini one.
|
||||||
|
adapter_class = GeminiLLMAdapter
|
||||||
|
|
||||||
|
# Backward compatibility: ThinkingConfig used to be defined inline here.
|
||||||
|
ThinkingConfig = GoogleThinkingConfig
|
||||||
|
|
||||||
class InputParams(BaseModel):
|
class InputParams(BaseModel):
|
||||||
"""Input parameters for Google AI models.
|
"""Input parameters for Google AI models.
|
||||||
|
|
||||||
@@ -764,7 +766,7 @@ class GoogleLLMService(LLMService):
|
|||||||
temperature: Optional[float] = Field(default=None, ge=0.0, le=2.0)
|
temperature: Optional[float] = Field(default=None, ge=0.0, le=2.0)
|
||||||
top_k: Optional[int] = Field(default=None, ge=0)
|
top_k: Optional[int] = Field(default=None, ge=0)
|
||||||
top_p: Optional[float] = Field(default=None, ge=0.0, le=1.0)
|
top_p: Optional[float] = Field(default=None, ge=0.0, le=1.0)
|
||||||
thinking: Optional["GoogleLLMService.ThinkingConfig"] = Field(default=None)
|
thinking: Optional[GoogleThinkingConfig] = Field(default=None)
|
||||||
extra: Optional[Dict[str, Any]] = Field(default_factory=dict)
|
extra: Optional[Dict[str, Any]] = Field(default_factory=dict)
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
|
|||||||
Reference in New Issue
Block a user