Files
wehub-resource-sync 97e91a83f3
Ruff / Ruff (push) Waiting to run
Test / Core Tests (push) Waiting to run
Test / Offline Coverage Tests (Python 3.10) (push) Waiting to run
Test / Offline Coverage Tests (Python 3.11) (push) Waiting to run
Test / Offline Coverage Tests (Python 3.12) (push) Waiting to run
Test / Offline Coverage Tests (Python 3.13) (push) Waiting to run
Test / Offline Coverage Tests (Python 3.9) (push) Waiting to run
Test / Full Coverage (Python 3.11) (push) Waiting to run
Test / Core Provider Tests (OpenAI) (push) Blocked by required conditions
Test / Core Provider Tests (Anthropic) (push) Blocked by required conditions
Test / Core Provider Tests (Google) (push) Blocked by required conditions
Test / Core Provider Tests (Other) (push) Blocked by required conditions
Test / Anthropic Tests (push) Blocked by required conditions
Test / Gemini Tests (push) Blocked by required conditions
Test / Google GenAI Tests (push) Blocked by required conditions
Test / Vertex AI Tests (push) Blocked by required conditions
Test / OpenAI Tests (push) Blocked by required conditions
Test / Writer Tests (push) Blocked by required conditions
Test / Auto Client Tests (push) Blocked by required conditions
ty / type-check (push) Waiting to run
chore: import upstream snapshot with attribution
2026-07-13 13:36:38 +08:00

1609 lines
48 KiB
Python

from __future__ import annotations
import importlib
from typing import Any, Callable, Literal, Optional, Union, cast, overload
from instructor.v2.core.client import AsyncInstructor, Instructor
from instructor import __version__
from instructor.v2.core.mode import Mode
from instructor.models import KnownModelName
from instructor.cache import BaseCache
from instructor.v2.core.provider_specs import ALIAS_TO_PROVIDER
import warnings
import logging
# Type alias for the return type
InstructorType = Union[Instructor, AsyncInstructor]
logger = logging.getLogger("instructor.auto_client")
# Canonical strings and compatibility aliases accepted by from_provider().
supported_providers = list(ALIAS_TO_PROVIDER)
@overload
def from_provider(
model: KnownModelName,
async_client: Literal[False] = False,
cache: BaseCache | None = None, # noqa: ARG001
**kwargs: Any,
) -> Instructor: ...
@overload
def from_provider(
model: KnownModelName,
async_client: Literal[True] = True,
cache: BaseCache | None = None, # noqa: ARG001
**kwargs: Any,
) -> AsyncInstructor: ...
@overload
def from_provider(
model: str,
async_client: Literal[False] = False,
cache: BaseCache | None = None, # noqa: ARG001
**kwargs: Any,
) -> Instructor: ...
@overload
def from_provider(
model: str,
async_client: Literal[True] = True,
cache: BaseCache | None = None, # noqa: ARG001
**kwargs: Any,
) -> AsyncInstructor: ...
def from_provider(
model: Union[str, KnownModelName], # noqa: UP007
async_client: bool = False,
cache: BaseCache | None = None,
mode: Union[Mode, None] = None, # noqa: ARG001, UP007
**kwargs: Any,
) -> Union[Instructor, AsyncInstructor]: # noqa: UP007
"""Create an Instructor client from a model string.
Args:
model: String in format "provider/model-name"
(e.g., "openai/gpt-4", "anthropic/claude-3-sonnet", "google/gemini-pro")
async_client: Whether to return an async client
cache: Optional cache adapter (e.g., ``AutoCache`` or ``RedisCache``)
to enable transparent response caching. Automatically flows through
**kwargs to all provider implementations.
mode: Override the default mode for the provider. If not specified, uses the
recommended default mode for each provider.
**kwargs: Additional arguments passed to the provider client functions.
This includes the cache parameter and any provider-specific options.
Returns:
Instructor or AsyncInstructor instance
Raises:
ValueError: If provider is not supported or model string is invalid
ImportError: If required package for provider is not installed
Examples:
>>> import instructor
>>> from instructor.cache import AutoCache
>>>
>>> # Basic usage
>>> client = instructor.from_provider("openai/gpt-4")
>>> client = instructor.from_provider("anthropic/claude-3-sonnet")
>>>
>>> # With caching
>>> cache = AutoCache(maxsize=1000)
>>> client = instructor.from_provider("openai/gpt-4", cache=cache)
>>>
>>> # Async clients
>>> async_client = instructor.from_provider("openai/gpt-4", async_client=True)
"""
# Add cache to kwargs if provided so it flows through to provider functions
if cache is not None:
kwargs["cache"] = cache
try:
provider, model_name = model.split("/", 1)
except ValueError:
from instructor.v2.core.errors import ConfigurationError
raise ConfigurationError(
'Model string must be in format "provider/model-name" '
'(e.g. "openai/gpt-4" or "anthropic/claude-3-sonnet")'
) from None
provider_info = {"provider": provider, "operation": "initialize"}
logger.info(
"Initializing %s provider with model %s",
provider,
model_name,
extra=provider_info,
)
logger.debug(
"Provider configuration: async_client=%s, mode=%s",
async_client,
mode,
extra=provider_info,
)
api_key = None
if "api_key" in kwargs:
api_key = kwargs.pop("api_key")
if api_key:
logger.debug(
"API key provided for %s provider (length: %d characters)",
provider,
len(api_key),
extra=provider_info,
)
builder = _PROVIDER_BUILDERS.get(provider)
if builder is None:
from instructor.v2.core.errors import ConfigurationError
logger.error(
"Error initializing %s client: unsupported provider",
provider,
extra={**provider_info, "status": "error"},
)
raise ConfigurationError(
f"Unsupported provider: {provider}. "
f"Supported providers are: {supported_providers}"
)
return builder(
provider=provider,
model_name=model_name,
async_client=async_client,
mode=mode,
api_key=api_key,
kwargs=kwargs,
provider_info=provider_info,
)
def _build_openai(
*,
provider: str,
model_name: str,
async_client: bool,
mode: Mode | None,
api_key: str | None,
kwargs: dict[str, Any],
provider_info: dict[str, str],
) -> InstructorType:
try:
import openai
import httpx
from openai import DEFAULT_MAX_RETRIES, NotGiven, Timeout, not_given
from collections.abc import Mapping
from typing import cast
except ImportError as err:
missing_root = (getattr(err, "name", "") or "").split(".")[0]
if missing_root not in {"openai", "httpx"}:
raise
from instructor.v2.core.errors import ConfigurationError
raise ConfigurationError(
"The openai package is required to use the OpenAI provider. "
"Install it with `pip install openai`."
) from None
try:
# Extract base_url and other OpenAI client parameters from kwargs
base_url = kwargs.pop("base_url", None)
organization = cast(Optional[str], kwargs.pop("organization", None))
timeout_raw = kwargs.pop("timeout", not_given)
timeout: float | Timeout | None | NotGiven
timeout = (
not_given
if timeout_raw is not_given
else cast(Optional[Union[float, Timeout]], timeout_raw)
)
max_retries_raw = kwargs.pop("max_retries", None)
max_retries = (
DEFAULT_MAX_RETRIES
if max_retries_raw is None
else int(cast(int, max_retries_raw))
)
default_headers = cast(
Optional[Mapping[str, str]], kwargs.pop("default_headers", None)
)
default_query = cast(
Optional[Mapping[str, object]], kwargs.pop("default_query", None)
)
http_client_raw = kwargs.pop("http_client", None)
strict_response_validation = bool(
kwargs.pop("_strict_response_validation", False)
)
if async_client:
http_client = cast(Optional[httpx.AsyncClient], http_client_raw)
client = openai.AsyncOpenAI(
api_key=api_key,
base_url=base_url,
organization=organization,
timeout=timeout,
max_retries=max_retries,
default_headers=default_headers,
default_query=default_query,
http_client=http_client,
_strict_response_validation=strict_response_validation,
)
else:
http_client = cast(Optional[httpx.Client], http_client_raw)
client = openai.OpenAI(
api_key=api_key,
base_url=base_url,
organization=organization,
timeout=timeout,
max_retries=max_retries,
default_headers=default_headers,
default_query=default_query,
http_client=http_client,
_strict_response_validation=strict_response_validation,
)
import instructor
result = instructor.from_openai(
client,
model=model_name,
mode=mode if mode else Mode.TOOLS,
**kwargs,
)
logger.info(
"Client initialized",
extra={**provider_info, "status": "success"},
)
return result
except Exception as e:
logger.error(
"Error initializing %s client: %s",
provider,
e,
exc_info=True,
extra={**provider_info, "status": "error"},
)
raise
def _build_azure_openai(
*,
provider: str,
model_name: str,
async_client: bool,
mode: Mode | None,
api_key: str | None,
kwargs: dict[str, Any],
provider_info: dict[str, str],
) -> InstructorType:
try:
import os
from openai import AzureOpenAI, AsyncAzureOpenAI
from instructor.v2.providers.openai.client import from_openai
# Get required Azure OpenAI configuration from environment
api_key = api_key or os.environ.get("AZURE_OPENAI_API_KEY")
azure_endpoint = kwargs.pop(
"azure_endpoint", os.environ.get("AZURE_OPENAI_ENDPOINT")
)
api_version = kwargs.pop("api_version", "2024-02-01")
if not api_key:
from instructor.v2.core.errors import ConfigurationError
raise ConfigurationError(
"AZURE_OPENAI_API_KEY is not set. "
"Set it with `export AZURE_OPENAI_API_KEY=<your-api-key>` or pass it as kwarg api_key=<your-api-key>"
)
if not azure_endpoint:
from instructor.v2.core.errors import ConfigurationError
raise ConfigurationError(
"AZURE_OPENAI_ENDPOINT is not set. "
"Set it with `export AZURE_OPENAI_ENDPOINT=<your-endpoint>` or pass it as kwarg azure_endpoint=<your-endpoint>"
)
client = (
AsyncAzureOpenAI(
api_key=api_key,
api_version=api_version,
azure_endpoint=azure_endpoint,
)
if async_client
else AzureOpenAI(
api_key=api_key,
api_version=api_version,
azure_endpoint=azure_endpoint,
)
)
result = from_openai(
client,
model=model_name,
mode=mode if mode else Mode.TOOLS,
**kwargs,
)
logger.info(
"Client initialized",
extra={**provider_info, "status": "success"},
)
return result
except ImportError:
from instructor.v2.core.errors import ConfigurationError
raise ConfigurationError(
"The openai package is required to use the Azure OpenAI provider. "
"Install it with `pip install openai`."
) from None
except Exception as e:
logger.error(
"Error initializing %s client: %s",
provider,
e,
exc_info=True,
extra={**provider_info, "status": "error"},
)
raise
def _build_openai_compatible(
*,
provider: str,
model_name: str,
async_client: bool,
mode: Mode | None,
api_key: str | None,
kwargs: dict[str, Any],
provider_info: dict[str, str],
env_var: str,
default_base_url: str,
factory_name: str,
) -> InstructorType:
try:
import os
import openai
from instructor.v2.providers.openai import client as openai_client
api_key = api_key or os.environ.get(env_var)
if not api_key:
from instructor.v2.core.errors import ConfigurationError
raise ConfigurationError(
f"{env_var} is not set. "
f"Set it with `export {env_var}=<your-api-key>` or pass it as kwarg api_key=<your-api-key>"
)
base_url = kwargs.pop("base_url", default_base_url)
client = (
openai.AsyncOpenAI(api_key=api_key, base_url=base_url)
if async_client
else openai.OpenAI(api_key=api_key, base_url=base_url)
)
factory = getattr(openai_client, factory_name)
result = factory(
client,
model=model_name,
mode=mode if mode else Mode.TOOLS,
**kwargs,
)
logger.info(
"Client initialized",
extra={**provider_info, "status": "success"},
)
return result
except ImportError:
from instructor.v2.core.errors import ConfigurationError
raise ConfigurationError(
f"The openai package is required to use the {provider} provider. "
"Install it with `pip install openai`."
) from None
except Exception as e:
logger.error(
"Error initializing %s client: %s",
provider,
e,
exc_info=True,
extra={**provider_info, "status": "error"},
)
raise
def _build_anyscale(**kwargs: Any) -> InstructorType:
return _build_openai_compatible(
**kwargs,
env_var="ANYSCALE_API_KEY",
default_base_url="https://api.endpoints.anyscale.com/v1",
factory_name="from_anyscale",
)
def _build_together(**kwargs: Any) -> InstructorType:
return _build_openai_compatible(
**kwargs,
env_var="TOGETHER_API_KEY",
default_base_url="https://api.together.xyz/v1",
factory_name="from_together",
)
def _build_databricks(
*,
provider: str,
model_name: str,
async_client: bool,
mode: Mode | None,
api_key: str | None,
kwargs: dict[str, Any],
provider_info: dict[str, str],
) -> InstructorType:
try:
import os
import openai
from instructor import from_openai
api_key = (
api_key
or os.environ.get("DATABRICKS_TOKEN")
or os.environ.get("DATABRICKS_API_KEY")
)
if not api_key:
from instructor.v2.core.errors import ConfigurationError
raise ConfigurationError(
"DATABRICKS_TOKEN is not set. "
"Set it with `export DATABRICKS_TOKEN=<your-token>` or `export DATABRICKS_API_KEY=<your-token>` "
"or pass it as kwarg `api_key=<your-token>`."
)
base_url = kwargs.pop("base_url", None)
if base_url is None:
base_url = (
os.environ.get("DATABRICKS_BASE_URL")
or os.environ.get("DATABRICKS_HOST")
or os.environ.get("DATABRICKS_WORKSPACE_URL")
)
if not base_url:
from instructor.v2.core.errors import ConfigurationError
raise ConfigurationError(
"DATABRICKS_HOST is not set. "
"Set it with `export DATABRICKS_HOST=<your-workspace-url>` or `export DATABRICKS_WORKSPACE_URL=<your-workspace-url>` "
"or pass `base_url=<your-workspace-url>`."
)
base_url = str(base_url).rstrip("/")
if not base_url.endswith("/serving-endpoints"):
base_url = f"{base_url}/serving-endpoints"
openai_client_kwargs = {}
for key in (
"organization",
"timeout",
"max_retries",
"default_headers",
"http_client",
"app_info",
):
if key in kwargs:
openai_client_kwargs[key] = kwargs.pop(key)
client = (
openai.AsyncOpenAI(
api_key=api_key, base_url=base_url, **openai_client_kwargs
)
if async_client
else openai.OpenAI(
api_key=api_key, base_url=base_url, **openai_client_kwargs
)
)
result = from_openai(
client,
model=model_name,
mode=mode if mode else Mode.TOOLS,
**kwargs,
)
logger.info(
"Client initialized",
extra={**provider_info, "status": "success"},
)
return result
except ImportError:
from instructor.v2.core.errors import ConfigurationError
raise ConfigurationError(
"The openai package is required to use the Databricks provider. "
"Install it with `pip install openai`."
) from None
except Exception as e:
logger.error(
"Error initializing %s client: %s",
provider,
e,
exc_info=True,
extra={**provider_info, "status": "error"},
)
raise
def _build_anthropic(
*,
provider: str,
model_name: str,
async_client: bool,
mode: Mode | None,
api_key: str | None,
kwargs: dict[str, Any],
provider_info: dict[str, str],
) -> InstructorType:
try:
import anthropic
from instructor.v2.providers.anthropic.client import from_anthropic
if from_anthropic is None:
from instructor.v2.core.errors import ConfigurationError
raise ConfigurationError(
"Failed to import Anthropic provider. "
"This may be due to a configuration error or missing dependencies."
)
client = (
anthropic.AsyncAnthropic(
api_key=api_key,
default_headers={"User-Agent": f"instructor/{__version__}"},
)
if async_client
else anthropic.Anthropic(
api_key=api_key,
default_headers={"User-Agent": f"instructor/{__version__}"},
)
)
# Set default max_tokens if not provided (like v1)
if "max_tokens" not in kwargs:
kwargs["max_tokens"] = 4096
# Use Mode.TOOLS instead of Mode.ANTHROPIC_TOOLS
result = from_anthropic(
client,
model=model_name,
mode=mode if mode else Mode.TOOLS,
**kwargs,
)
logger.info(
"Client initialized",
extra={**provider_info, "status": "success"},
)
return result
except ImportError:
from instructor.v2.core.errors import ConfigurationError
raise ConfigurationError(
"The anthropic package is required to use the Anthropic provider. "
"Install it with `pip install anthropic`."
) from None
except Exception as e:
logger.error(
"Error initializing %s client: %s",
provider,
e,
exc_info=True,
extra={**provider_info, "status": "error"},
)
raise
def _build_google(
*,
provider: str,
model_name: str,
async_client: bool,
mode: Mode | None,
api_key: str | None,
kwargs: dict[str, Any],
provider_info: dict[str, str],
) -> InstructorType:
# Import google-genai package - catch ImportError only for actual imports
try:
import google.genai as genai
except ImportError as e:
from instructor.v2.core.errors import ConfigurationError
raise ConfigurationError(
"The google-genai package is required to use the Google provider. "
"Install it with `pip install google-genai`."
) from e
try:
import os
# Remove vertexai from kwargs if present to avoid passing it twice
vertexai_flag = kwargs.pop("vertexai", False)
# Get API key from kwargs or environment
api_key = api_key or os.environ.get("GOOGLE_API_KEY")
# Extract client-specific parameters
client_kwargs = {}
for key in [
"debug_config",
"http_options",
"credentials",
"project",
"location",
]:
if key in kwargs:
client_kwargs[key] = kwargs.pop(key)
client = genai.Client(
vertexai=vertexai_flag,
api_key=api_key,
**client_kwargs,
)
# Default to TOOLS for v2
# Extract model from kwargs if present, otherwise use model_name
model_param = kwargs.pop("model", model_name)
import instructor
result = instructor.from_genai(
client,
mode=mode if mode else Mode.TOOLS,
use_async=async_client,
model=model_param,
**kwargs,
)
logger.info(
"Client initialized",
extra={**provider_info, "status": "success"},
)
return result
except Exception as e:
logger.error(
"Error initializing %s client: %s",
provider,
e,
exc_info=True,
extra={**provider_info, "status": "error"},
)
raise
def _build_gemini(
*,
provider: str,
model_name: str,
async_client: bool,
mode: Mode | None,
api_key: str | None,
kwargs: dict[str, Any],
provider_info: dict[str, str],
) -> InstructorType:
try:
import os
genai = cast(Any, importlib.import_module("google.generativeai"))
from instructor.v2.providers.gemini.client import from_gemini
api_key = api_key or os.environ.get("GOOGLE_API_KEY")
if api_key:
genai.configure(api_key=api_key)
client = genai.GenerativeModel(model_name)
result = from_gemini(
client,
mode=mode if mode else Mode.MD_JSON,
use_async=async_client,
**kwargs,
)
logger.info(
"Client initialized",
extra={**provider_info, "status": "success"},
)
return result
except ImportError:
from instructor.v2.core.errors import ConfigurationError
raise ConfigurationError(
"The google-generativeai package is required to use the Gemini provider. "
"Install it with `pip install google-generativeai`."
) from None
except Exception as e:
logger.error(
"Error initializing %s client: %s",
provider,
e,
exc_info=True,
extra={**provider_info, "status": "error"},
)
raise
def _build_mistral(
*,
provider: str,
model_name: str,
async_client: bool,
mode: Mode | None, # noqa: ARG001
api_key: str | None,
kwargs: dict[str, Any],
provider_info: dict[str, str],
) -> InstructorType:
try:
from mistralai import Mistral
from instructor.v2.providers.mistral.client import from_mistral
import os
api_key = api_key or os.environ.get("MISTRAL_API_KEY")
if api_key:
client = Mistral(api_key=api_key)
else:
raise ValueError(
"MISTRAL_API_KEY is not set. "
"Set it with `export MISTRAL_API_KEY=<your-api-key>`."
)
if async_client:
result = from_mistral(client, model=model_name, use_async=True, **kwargs)
else:
result = from_mistral(client, model=model_name, **kwargs)
logger.info(
"Client initialized",
extra={**provider_info, "status": "success"},
)
return result
except ImportError:
from instructor.v2.core.errors import ConfigurationError
raise ConfigurationError(
"The mistralai package is required to use the Mistral provider. "
"Install it with `pip install mistralai`."
) from None
except Exception as e:
logger.error(
"Error initializing %s client: %s",
provider,
e,
exc_info=True,
extra={**provider_info, "status": "error"},
)
raise
def _build_cohere(
*,
provider: str,
model_name: str,
async_client: bool,
mode: Mode | None,
api_key: str | None,
kwargs: dict[str, Any],
provider_info: dict[str, str],
) -> InstructorType:
try:
import cohere
from instructor.v2.providers.cohere.client import from_cohere
client = (
cohere.AsyncClientV2(api_key=api_key)
if async_client
else cohere.ClientV2(api_key=api_key)
)
# Use Mode.TOOLS as default for Cohere
result = from_cohere(
client,
mode=mode if mode else Mode.TOOLS,
model=model_name,
**kwargs,
)
logger.info(
"Client initialized",
extra={**provider_info, "status": "success"},
)
return result
except ImportError:
from instructor.v2.core.errors import ConfigurationError
raise ConfigurationError(
"The cohere package is required to use the Cohere provider. "
"Install it with `pip install cohere`."
) from None
except Exception as e:
logger.error(
"Error initializing %s client: %s",
provider,
e,
exc_info=True,
extra={**provider_info, "status": "error"},
)
raise
def _build_perplexity(
*,
provider: str,
model_name: str,
async_client: bool,
mode: Mode | None, # noqa: ARG001
api_key: str | None,
kwargs: dict[str, Any],
provider_info: dict[str, str],
) -> InstructorType:
try:
import openai
from instructor.v2.providers.perplexity.client import from_perplexity
import os
api_key = api_key or os.environ.get("PERPLEXITY_API_KEY")
if not api_key:
from instructor.v2.core.errors import ConfigurationError
raise ConfigurationError(
"PERPLEXITY_API_KEY is not set. "
"Set it with `export PERPLEXITY_API_KEY=<your-api-key>` or pass it as a kwarg api_key=<your-api-key>"
)
client = (
openai.AsyncOpenAI(api_key=api_key, base_url="https://api.perplexity.ai")
if async_client
else openai.OpenAI(api_key=api_key, base_url="https://api.perplexity.ai")
)
result = from_perplexity(client, model=model_name, **kwargs)
logger.info(
"Client initialized",
extra={**provider_info, "status": "success"},
)
return result
except ImportError:
from instructor.v2.core.errors import ConfigurationError
raise ConfigurationError(
"The openai package is required to use the Perplexity provider. "
"Install it with `pip install openai`."
) from None
except Exception as e:
logger.error(
"Error initializing %s client: %s",
provider,
e,
exc_info=True,
extra={**provider_info, "status": "error"},
)
raise
def _build_groq(
*,
provider: str,
model_name: str,
async_client: bool,
mode: Mode | None, # noqa: ARG001
api_key: str | None,
kwargs: dict[str, Any],
provider_info: dict[str, str],
) -> InstructorType:
try:
import groq
from instructor.v2.providers.groq.client import from_groq
client = (
groq.AsyncGroq(api_key=api_key)
if async_client
else groq.Groq(api_key=api_key)
)
result = from_groq(client, model=model_name, **kwargs)
logger.info(
"Client initialized",
extra={**provider_info, "status": "success"},
)
return result
except ImportError:
from instructor.v2.core.errors import ConfigurationError
raise ConfigurationError(
"The groq package is required to use the Groq provider. "
"Install it with `pip install groq`."
) from None
except Exception as e:
logger.error(
"Error initializing %s client: %s",
provider,
e,
exc_info=True,
extra={**provider_info, "status": "error"},
)
raise
def _build_writer(
*,
provider: str,
model_name: str,
async_client: bool,
mode: Mode | None, # noqa: ARG001
api_key: str | None,
kwargs: dict[str, Any],
provider_info: dict[str, str],
) -> InstructorType:
try:
from writerai import AsyncWriter, Writer
from instructor.v2.providers.writer.client import from_writer
client = (
AsyncWriter(api_key=api_key) if async_client else Writer(api_key=api_key)
)
result = from_writer(client, model=model_name, **kwargs)
logger.info(
"Client initialized",
extra={**provider_info, "status": "success"},
)
return result
except ImportError:
from instructor.v2.core.errors import ConfigurationError
raise ConfigurationError(
"The writerai package is required to use the Writer provider. "
"Install it with `pip install writer-sdk`."
) from None
except Exception as e:
logger.error(
"Error initializing %s client: %s",
provider,
e,
exc_info=True,
extra={**provider_info, "status": "error"},
)
raise
def _build_bedrock(
*,
provider: str,
model_name: str,
async_client: bool,
mode: Mode | None,
api_key: str | None, # noqa: ARG001
kwargs: dict[str, Any],
provider_info: dict[str, str],
) -> InstructorType:
try:
import os
import boto3
from instructor.v2.providers.bedrock.client import from_bedrock
# Get AWS configuration from environment or kwargs
if "region" in kwargs:
region = kwargs.pop("region")
else:
logger.debug(
"AWS_DEFAULT_REGION is not set. Using default region us-east-1"
)
region = os.environ.get("AWS_DEFAULT_REGION", "us-east-1")
# Extract AWS-specific parameters
# Dictionary to collect AWS credentials and session parameters for boto3 client
aws_kwargs = {}
for key in [
"aws_access_key_id",
"aws_secret_access_key",
"aws_session_token",
]:
if key in kwargs:
aws_kwargs[key] = kwargs.pop(key)
elif key.upper() in os.environ:
logger.debug(f"Using {key.upper()} from environment variable")
aws_kwargs[key] = os.environ[key.upper()]
# Add region to client configuration
aws_kwargs["region_name"] = region
# Create bedrock-runtime client
client = boto3.client("bedrock-runtime", **aws_kwargs)
# Determine default mode based on model
if mode is None:
# Anthropic models (Claude) support tools, others use JSON
if model_name and (
"anthropic" in model_name.lower() or "claude" in model_name.lower()
):
default_mode = Mode.TOOLS
else:
default_mode = Mode.MD_JSON
else:
default_mode = mode
result = from_bedrock(
client,
mode=default_mode,
async_client=async_client,
**kwargs,
)
logger.info(
"Client initialized",
extra={**provider_info, "status": "success"},
)
return result
except ImportError:
from instructor.v2.core.errors import ConfigurationError
raise ConfigurationError(
"The boto3 package is required to use the AWS Bedrock provider. "
"Install it with `pip install boto3`."
) from None
except Exception as e:
logger.error(
"Error initializing %s client: %s",
provider,
e,
exc_info=True,
extra={**provider_info, "status": "error"},
)
raise
def _build_cerebras(
*,
provider: str,
model_name: str,
async_client: bool,
mode: Mode | None, # noqa: ARG001
api_key: str | None,
kwargs: dict[str, Any],
provider_info: dict[str, str],
) -> InstructorType:
try:
from cerebras.cloud.sdk import AsyncCerebras, Cerebras
from instructor.v2.providers.cerebras.client import from_cerebras
client = (
AsyncCerebras(api_key=api_key)
if async_client
else Cerebras(api_key=api_key)
)
result = from_cerebras(client, model=model_name, **kwargs)
logger.info(
"Client initialized",
extra={**provider_info, "status": "success"},
)
return result
except ImportError:
from instructor.v2.core.errors import ConfigurationError
raise ConfigurationError(
"The cerebras package is required to use the Cerebras provider. "
"Install it with `pip install cerebras`."
) from None
except Exception as e:
logger.error(
"Error initializing %s client: %s",
provider,
e,
exc_info=True,
extra={**provider_info, "status": "error"},
)
raise
def _build_fireworks(
*,
provider: str,
model_name: str,
async_client: bool,
mode: Mode | None, # noqa: ARG001
api_key: str | None,
kwargs: dict[str, Any],
provider_info: dict[str, str],
) -> InstructorType:
try:
from fireworks.client import AsyncFireworks, Fireworks
from instructor.v2.providers.fireworks.client import from_fireworks
client = (
AsyncFireworks(api_key=api_key)
if async_client
else Fireworks(api_key=api_key)
)
result = from_fireworks(client, model=model_name, **kwargs)
logger.info(
"Client initialized",
extra={**provider_info, "status": "success"},
)
return result
except ImportError:
from instructor.v2.core.errors import ConfigurationError
raise ConfigurationError(
"The fireworks-ai package is required to use the Fireworks provider. "
"Install it with `pip install fireworks-ai`."
) from None
except Exception as e:
logger.error(
"Error initializing %s client: %s",
provider,
e,
exc_info=True,
extra={**provider_info, "status": "error"},
)
raise
def _build_vertexai(
*,
provider: str,
model_name: str,
async_client: bool,
mode: Mode | None,
api_key: str | None, # noqa: ARG001
kwargs: dict[str, Any],
provider_info: dict[str, str],
) -> InstructorType:
warnings.warn(
"The 'vertexai' provider is deprecated. Use 'google' provider with vertexai=True instead. "
"Example: instructor.from_provider('google/gemini-pro', vertexai=True)",
DeprecationWarning,
stacklevel=2,
)
# Import Vertex AI SDK
try:
import vertexai
import vertexai.generative_models as gm
except ImportError as e:
from instructor.v2.core.errors import ConfigurationError
raise ConfigurationError(
"The vertexai package is required to use the VertexAI provider. "
"Install it with `pip install google-cloud-aiplatform`."
) from e
try:
import os
# Get project and location from kwargs or environment
project = kwargs.pop("project", os.environ.get("GOOGLE_CLOUD_PROJECT"))
location = kwargs.pop(
"location", os.environ.get("GOOGLE_CLOUD_LOCATION", "us-central1")
)
if not project:
raise ValueError(
"Project ID is required for Vertex AI. "
"Set it with `export GOOGLE_CLOUD_PROJECT=<your-project-id>` "
"or pass it as kwarg project=<your-project-id>"
)
credentials = kwargs.pop("credentials", None)
vertexai.init(project=project, location=location, credentials=credentials)
client = gm.GenerativeModel(model_name)
import instructor
result = instructor.from_vertexai(
client,
use_async=async_client,
mode=mode if mode else Mode.TOOLS,
**kwargs,
)
logger.info(
"Client initialized",
extra={**provider_info, "status": "success"},
)
return result
except Exception as e:
logger.error(
"Error initializing %s client: %s",
provider,
e,
exc_info=True,
extra={**provider_info, "status": "error"},
)
raise
def _build_generative_ai(
*,
provider: str,
model_name: str,
async_client: bool,
mode: Mode | None,
api_key: str | None,
kwargs: dict[str, Any],
provider_info: dict[str, str],
) -> InstructorType:
warnings.warn(
"The 'generative-ai' provider is deprecated. Use 'google' provider instead. "
"Example: instructor.from_provider('google/gemini-pro')",
DeprecationWarning,
stacklevel=2,
)
# Import google-genai package - catch ImportError only for actual imports
try:
from google import genai
from instructor.v2.providers.genai.client import from_genai
except ImportError as e:
from instructor.v2.core.errors import ConfigurationError
raise ConfigurationError(
"The google-genai package is required to use the Google GenAI provider. "
"Install it with `pip install google-genai`."
) from e
try:
import os
# Get API key from kwargs or environment
api_key = api_key or os.environ.get("GOOGLE_API_KEY")
client = genai.Client(vertexai=False, api_key=api_key)
if async_client:
result = from_genai(
client,
use_async=True,
model=model_name,
mode=mode if mode else Mode.TOOLS,
**kwargs,
)
else:
result = from_genai(
client,
model=model_name,
mode=mode if mode else Mode.TOOLS,
**kwargs,
)
logger.info(
"Client initialized",
extra={**provider_info, "status": "success"},
)
return result
except Exception as e:
logger.error(
"Error initializing %s client: %s",
provider,
e,
exc_info=True,
extra={**provider_info, "status": "error"},
)
raise
def _build_ollama(
*,
provider: str,
model_name: str,
async_client: bool,
mode: Mode | None,
api_key: str | None,
kwargs: dict[str, Any],
provider_info: dict[str, str],
) -> InstructorType:
try:
import openai
from instructor.v2.providers.openai.client import from_openai
# Get base_url from kwargs or use default
base_url = kwargs.pop("base_url", "http://localhost:11434/v1")
api_key = kwargs.pop("api_key", "ollama") # required but unused
client = (
openai.AsyncOpenAI(base_url=base_url, api_key=api_key)
if async_client
else openai.OpenAI(base_url=base_url, api_key=api_key)
)
# Models that support function calling (tools mode)
tool_capable_models = {
"llama3.1",
"llama3.2",
"llama4",
"mistral-nemo",
"firefunction-v2",
"command-a",
"command-r",
"command-r-plus",
"command-r7b",
"qwen2.5",
"qwen2.5-coder",
"qwen3",
"devstral",
}
# Check if model supports tools by looking at model name
supports_tools = any(
capable_model in model_name.lower() for capable_model in tool_capable_models
)
default_mode = Mode.TOOLS if supports_tools else Mode.JSON
result = from_openai(
client,
model=model_name,
mode=mode if mode else default_mode,
**kwargs,
)
logger.info(
"Client initialized",
extra={**provider_info, "status": "success"},
)
return result
except ImportError:
from instructor.v2.core.errors import ConfigurationError
raise ConfigurationError(
"The openai package is required to use the Ollama provider. "
"Install it with `pip install openai`."
) from None
except Exception as e:
logger.error(
"Error initializing %s client: %s",
provider,
e,
exc_info=True,
extra={**provider_info, "status": "error"},
)
raise
def _build_deepseek(
*,
provider: str,
model_name: str,
async_client: bool,
mode: Mode | None,
api_key: str | None,
kwargs: dict[str, Any],
provider_info: dict[str, str],
) -> InstructorType:
try:
import openai
from instructor.v2.providers.openai.client import from_deepseek
import os
# Get API key from kwargs or environment
api_key = api_key or os.environ.get("DEEPSEEK_API_KEY")
if not api_key:
from instructor.v2.core.errors import ConfigurationError
raise ConfigurationError(
"DEEPSEEK_API_KEY is not set. "
"Set it with `export DEEPSEEK_API_KEY=<your-api-key>` or pass it as kwarg api_key=<your-api-key>"
)
# DeepSeek uses OpenAI-compatible API
base_url = kwargs.pop("base_url", "https://api.deepseek.com")
client = (
openai.AsyncOpenAI(api_key=api_key, base_url=base_url)
if async_client
else openai.OpenAI(api_key=api_key, base_url=base_url)
)
result = from_deepseek(
client,
model=model_name,
mode=mode if mode else Mode.TOOLS,
**kwargs,
)
logger.info(
"Client initialized",
extra={**provider_info, "status": "success"},
)
return result
except ImportError:
from instructor.v2.core.errors import ConfigurationError
raise ConfigurationError(
"The openai package is required to use the DeepSeek provider. "
"Install it with `pip install openai`."
) from None
except Exception as e:
logger.error(
"Error initializing %s client: %s",
provider,
e,
exc_info=True,
extra={**provider_info, "status": "error"},
)
raise
def _build_xai(
*,
provider: str,
model_name: str,
async_client: bool,
mode: Mode | None,
api_key: str | None,
kwargs: dict[str, Any],
provider_info: dict[str, str],
) -> InstructorType:
try:
from xai_sdk.sync.client import Client as SyncClient
from xai_sdk.aio.client import Client as AsyncClient
from instructor.v2.providers.xai.client import from_xai
if from_xai is None:
from instructor.v2.core.errors import ConfigurationError
raise ConfigurationError(
"Failed to import xAI provider. "
"This may be due to a configuration error or missing dependencies."
)
client = (
AsyncClient(api_key=api_key)
if async_client
else SyncClient(api_key=api_key)
)
# Use Mode.TOOLS instead of Mode.XAI_TOOLS (v2 uses generic modes)
result = from_xai(
client,
mode=mode if mode else Mode.TOOLS,
model=model_name,
**kwargs,
)
logger.info(
"Client initialized",
extra={**provider_info, "status": "success"},
)
return result
except ImportError:
from instructor.v2.core.errors import ConfigurationError
raise ConfigurationError(
"The xAI provider needs the optional dependency `xai-sdk`. "
'Install it with `uv pip install "instructor[xai]"` (or `pip install "instructor[xai]"`). '
"Note: xai-sdk requires Python 3.10+."
) from None
except Exception as e:
logger.error(
"Error initializing %s client: %s",
provider,
e,
exc_info=True,
extra={**provider_info, "status": "error"},
)
raise
def _build_openrouter(
*,
provider: str,
model_name: str,
async_client: bool,
mode: Mode | None,
api_key: str | None,
kwargs: dict[str, Any],
provider_info: dict[str, str],
) -> InstructorType:
try:
import openai
from instructor.v2.providers.openrouter.client import from_openrouter
import os
# Get API key from kwargs or environment
api_key = api_key or os.environ.get("OPENROUTER_API_KEY")
if not api_key:
from instructor.v2.core.errors import ConfigurationError
raise ConfigurationError(
"OPENROUTER_API_KEY is not set. "
"Set it with `export OPENROUTER_API_KEY=<your-api-key>` or pass it as kwarg api_key=<your-api-key>"
)
# OpenRouter uses OpenAI-compatible API
base_url = kwargs.pop("base_url", "https://openrouter.ai/api/v1")
client = (
openai.AsyncOpenAI(api_key=api_key, base_url=base_url)
if async_client
else openai.OpenAI(api_key=api_key, base_url=base_url)
)
result = from_openrouter(
client,
model=model_name,
mode=mode if mode else Mode.TOOLS,
**kwargs,
)
logger.info(
"Client initialized",
extra={**provider_info, "status": "success"},
)
return result
except ImportError:
from instructor.v2.core.errors import ConfigurationError
raise ConfigurationError(
"The openai package is required to use the OpenRouter provider. "
"Install it with `pip install openai`."
) from None
except Exception as e:
logger.error(
"Error initializing %s client: %s",
provider,
e,
exc_info=True,
extra={**provider_info, "status": "error"},
)
raise
def _build_litellm(
*,
provider: str,
model_name: str, # noqa: ARG001
async_client: bool,
mode: Mode | None,
api_key: str | None, # noqa: ARG001
kwargs: dict[str, Any],
provider_info: dict[str, str],
) -> InstructorType:
try:
from litellm import completion, acompletion
from instructor.v2.providers.litellm.client import from_litellm
completion_func = acompletion if async_client else completion
result = from_litellm(
completion_func,
mode=mode if mode else Mode.TOOLS,
**kwargs,
)
logger.info(
"Client initialized",
extra={**provider_info, "status": "success"},
)
return result
except ImportError:
from instructor.v2.core.errors import ConfigurationError
raise ConfigurationError(
"The litellm package is required to use the LiteLLM provider. "
"Install it with `pip install litellm`."
) from None
except Exception as e:
logger.error(
"Error initializing %s client: %s",
provider,
e,
exc_info=True,
extra={**provider_info, "status": "error"},
)
raise
ProviderBuilder = Callable[..., InstructorType]
_PROVIDER_BUILDERS: dict[str, ProviderBuilder] = {
"openai": _build_openai,
"anyscale": _build_anyscale,
"together": _build_together,
"azure_openai": _build_azure_openai,
"databricks": _build_databricks,
"anthropic": _build_anthropic,
"google": _build_google,
"gemini": _build_gemini,
"mistral": _build_mistral,
"cohere": _build_cohere,
"perplexity": _build_perplexity,
"groq": _build_groq,
"writer": _build_writer,
"bedrock": _build_bedrock,
"cerebras": _build_cerebras,
"fireworks": _build_fireworks,
"vertexai": _build_vertexai,
"generative-ai": _build_generative_ai,
"ollama": _build_ollama,
"deepseek": _build_deepseek,
"xai": _build_xai,
"openrouter": _build_openrouter,
"litellm": _build_litellm,
}