Skip to content
Draft
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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ All notable changes to this project will be documented in this file. The format

<!-- Add upcoming changes here -->

### Fixed
- Avoid importing `google.generativeai` during `import instructor`, which prevents an upstream `FutureWarning` in projects that are not using the legacy Gemini client.

## [1.14.4] - 2026-01-16

### Changed
Expand Down
2 changes: 1 addition & 1 deletion instructor/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import importlib.util

__version__ = "1.14.4"
__version__ = "1.14.6"

from .mode import Mode
from .processing.multimodal import Image, Audio
Expand Down
27 changes: 19 additions & 8 deletions instructor/providers/gemini/client.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
from __future__ import annotations

from typing import Any, Literal, overload

import google.generativeai as genai # type: ignore[import-not-found]
from typing import TYPE_CHECKING, Any, Literal, overload

import instructor


if TYPE_CHECKING:
import google.generativeai as genai # type: ignore[import-not-found]

GeminiGenerativeModel = genai.GenerativeModel
else:
GeminiGenerativeModel = Any


@overload
def from_gemini(
client: genai.GenerativeModel,
client: GeminiGenerativeModel,
mode: instructor.Mode = instructor.Mode.GEMINI_JSON,
use_async: Literal[True] = True,
**kwargs: Any,
Expand All @@ -18,15 +24,15 @@ def from_gemini(

@overload
def from_gemini(
client: genai.GenerativeModel,
client: GeminiGenerativeModel,
mode: instructor.Mode = instructor.Mode.GEMINI_JSON,
use_async: Literal[False] = False,
**kwargs: Any,
) -> instructor.Instructor: ...


def from_gemini(
client: genai.GenerativeModel,
client: GeminiGenerativeModel,
mode: instructor.Mode = instructor.Mode.GEMINI_JSON,
use_async: bool = False,
**kwargs: Any,
Expand Down Expand Up @@ -64,11 +70,16 @@ def from_gemini(
mode=str(mode), provider="Gemini", valid_modes=[str(m) for m in valid_modes]
)

if not isinstance(client, genai.GenerativeModel):
# We avoid importing `google.generativeai` at import-time (it emits a FutureWarning).
# Since `from_gemini` is deprecated, a simple duck-type check is enough here.
if not (
hasattr(client, "generate_content")
and hasattr(client, "generate_content_async")
):
from ...core.exceptions import ClientError

raise ClientError(
f"Client must be an instance of genai.GenerativeModel. "
"Client must look like a google.generativeai GenerativeModel. "
f"Got: {type(client).__name__}"
)

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ dependencies = [
"diskcache>=5.6.3",
]
name = "instructor"
version = "1.14.5"
version = "1.14.6"
description = "structured outputs for llm"
readme = "README.md"

Expand Down
43 changes: 43 additions & 0 deletions tests/test_gemini_imports.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import builtins
import importlib
import importlib.machinery
import importlib.util
import sys


def _purge_instructor_modules() -> None:
"""Remove instructor modules so we can import it fresh."""
for name in list(sys.modules.keys()):
if name == "instructor" or name.startswith("instructor."):
sys.modules.pop(name, None)


def test_import_instructor_does_not_import_google_generativeai(monkeypatch) -> None:
"""
Regression test: `import instructor` should not import `google.generativeai`.

The upstream `google.generativeai` package emits a FutureWarning at import time.
We don't want that warning unless the user is actively using the legacy Gemini client.
"""

def fake_find_spec(name: str, package: str | None = None): # noqa: ARG001
if name in {"google", "google.generativeai"}:
return importlib.machinery.ModuleSpec(name, loader=None)
return None

real_import = builtins.__import__

def guard_import(name, globals=None, locals=None, fromlist=(), level=0):
if name.startswith("google.generativeai"):
raise AssertionError(
"`google.generativeai` should not be imported during `import instructor`"
)
return real_import(name, globals, locals, fromlist, level)

monkeypatch.setattr(importlib.util, "find_spec", fake_find_spec)
monkeypatch.setattr(builtins, "__import__", guard_import)

_purge_instructor_modules()
importlib.import_module("instructor")

assert "google.generativeai" not in sys.modules
1 change: 0 additions & 1 deletion tests/test_xai_optional_dependency.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,3 @@ def test_direct_from_xai_has_clear_error_when_sdk_missing():
msg = str(excinfo.value)
assert "instructor[xai]" in msg
assert "xai-sdk" in msg

Loading
Loading