Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions instructor/providers/gemini/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -834,6 +834,17 @@ def handle_genai_structured_outputs(
if new_kwargs.get("stream", False) and not issubclass(response_model, PartialBase):
response_model = Partial[response_model]

# Extract thinking_config from user-provided config object if present
# This fixes issue #1966 where thinking_config inside config was ignored
user_config = new_kwargs.get("config")
user_thinking_config = None
if user_config is not None and hasattr(user_config, "thinking_config"):
user_thinking_config = user_config.thinking_config

# Prioritize kwarg thinking_config over config.thinking_config
if "thinking_config" not in new_kwargs and user_thinking_config is not None:
new_kwargs["thinking_config"] = user_thinking_config

if new_kwargs.get("system"):
system_message = new_kwargs.pop("system")
elif new_kwargs.get("messages"):
Expand Down Expand Up @@ -898,6 +909,17 @@ def handle_genai_tools(
if new_kwargs.get("stream", False) and not issubclass(response_model, PartialBase):
response_model = Partial[response_model]

# Extract thinking_config from user-provided config object if present
# This fixes issue #1966 where thinking_config inside config was ignored
user_config = new_kwargs.get("config")
user_thinking_config = None
if user_config is not None and hasattr(user_config, "thinking_config"):
user_thinking_config = user_config.thinking_config

# Prioritize kwarg thinking_config over config.thinking_config
if "thinking_config" not in new_kwargs and user_thinking_config is not None:
new_kwargs["thinking_config"] = user_thinking_config

schema = map_to_gemini_function_schema(_get_model_schema(response_model))
function_definition = types.FunctionDeclaration(
name=_get_model_name(response_model),
Expand Down
94 changes: 94 additions & 0 deletions tests/llm/test_genai/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,3 +201,97 @@ def test_update_genai_kwargs_no_thinking_config():
assert result["temperature"] == 0.7
# Check that thinking_config is not included when not provided
assert "thinking_config" not in result


def test_handle_genai_structured_outputs_thinking_config_in_config():
"""Test that thinking_config inside config parameter is extracted (issue #1966)."""
from google.genai import types
from pydantic import BaseModel

from instructor.providers.gemini.utils import handle_genai_structured_outputs

class SimpleModel(BaseModel):
text: str

# Create a mock ThinkingConfig-like object
thinking_config = types.ThinkingConfig(thinking_budget=1024)

# User passes thinking_config inside config parameter
user_config = types.GenerateContentConfig(
temperature=0.7,
max_output_tokens=1000,
thinking_config=thinking_config,
)

kwargs = {
"messages": [{"role": "user", "content": "Hello"}],
"config": user_config,
}

_, result_kwargs = handle_genai_structured_outputs(SimpleModel, kwargs)

# The resulting config should include thinking_config
assert "config" in result_kwargs
assert result_kwargs["config"].thinking_config is not None
assert result_kwargs["config"].thinking_config.thinking_budget == 1024


def test_handle_genai_structured_outputs_thinking_config_kwarg_priority():
"""Test that thinking_config as separate kwarg takes priority over config.thinking_config."""
from google.genai import types
from pydantic import BaseModel

from instructor.providers.gemini.utils import handle_genai_structured_outputs

class SimpleModel(BaseModel):
text: str

# User passes thinking_config both ways - kwarg should take priority
config_thinking = types.ThinkingConfig(thinking_budget=500)
kwarg_thinking = types.ThinkingConfig(thinking_budget=2000)

user_config = types.GenerateContentConfig(
temperature=0.7,
thinking_config=config_thinking,
)

kwargs = {
"messages": [{"role": "user", "content": "Hello"}],
"config": user_config,
"thinking_config": kwarg_thinking,
}

_, result_kwargs = handle_genai_structured_outputs(SimpleModel, kwargs)

# The kwarg thinking_config should take priority
assert result_kwargs["config"].thinking_config.thinking_budget == 2000


def test_handle_genai_tools_thinking_config_in_config():
"""Test that thinking_config inside config parameter is extracted for tools mode (issue #1966)."""
from google.genai import types
from pydantic import BaseModel

from instructor.providers.gemini.utils import handle_genai_tools

class SimpleModel(BaseModel):
text: str

thinking_config = types.ThinkingConfig(thinking_budget=1024)

user_config = types.GenerateContentConfig(
temperature=0.7,
thinking_config=thinking_config,
)

kwargs = {
"messages": [{"role": "user", "content": "Hello"}],
"config": user_config,
}

_, result_kwargs = handle_genai_tools(SimpleModel, kwargs)

# The resulting config should include thinking_config
assert "config" in result_kwargs
assert result_kwargs["config"].thinking_config is not None
assert result_kwargs["config"].thinking_config.thinking_budget == 1024