from __future__ import annotations from typing import Any, Union, Literal, overload from .core.client import AsyncInstructor, Instructor import instructor from instructor import __version__ from instructor.models import KnownModelName from instructor.cache import BaseCache import warnings import logging # Type alias for the return type InstructorType = Union[Instructor, AsyncInstructor] logger = logging.getLogger("instructor.auto_client") # List of supported providers supported_providers = [ "openai", "azure_openai", "databricks", "anthropic", "google", "generative-ai", "vertexai", "mistral", "cohere", "perplexity", "groq", "writer", "bedrock", "cerebras", "deepseek", "fireworks", "ollama", "openrouter", "xai", "litellm", ] @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: KnownModelName, 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: ... @overload def from_provider( model: str, async_client: Literal[False] = False, cache: BaseCache | None = None, # noqa: ARG001 **kwargs: Any, ) -> Instructor: ... def from_provider( model: Union[str, KnownModelName], # noqa: UP007 async_client: bool = False, cache: BaseCache | None = None, mode: Union[instructor.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 .core.exceptions 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, ) if provider == "openai": try: import openai import httpx from instructor import from_openai # type: ignore[attr-defined] from openai import DEFAULT_MAX_RETRIES, NotGiven, Timeout, not_given from collections.abc import Mapping from typing import cast # Extract base_url and other OpenAI client parameters from kwargs base_url = kwargs.pop("base_url", None) organization = cast(str | None, 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(float | Timeout | None, 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( Mapping[str, str] | None, kwargs.pop("default_headers", None) ) default_query = cast( Mapping[str, object] | None, 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(httpx.AsyncClient | None, 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(httpx.Client | None, 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, ) result = from_openai( client, model=model_name, mode=mode if mode else instructor.Mode.TOOLS, **kwargs, ) logger.info( "Client initialized", extra={**provider_info, "status": "success"}, ) return result except ImportError: from .core.exceptions import ConfigurationError raise ConfigurationError( "The openai package is required to use the 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 elif provider == "azure_openai": try: import os from openai import AzureOpenAI, AsyncAzureOpenAI from instructor import from_openai # type: ignore[attr-defined] # 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 .core.exceptions import ConfigurationError raise ConfigurationError( "AZURE_OPENAI_API_KEY is not set. " "Set it with `export AZURE_OPENAI_API_KEY=` or pass it as kwarg api_key=" ) if not azure_endpoint: from .core.exceptions import ConfigurationError raise ConfigurationError( "AZURE_OPENAI_ENDPOINT is not set. " "Set it with `export AZURE_OPENAI_ENDPOINT=` or pass it as kwarg azure_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 instructor.Mode.TOOLS, **kwargs, ) logger.info( "Client initialized", extra={**provider_info, "status": "success"}, ) return result except ImportError: from .core.exceptions 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 elif provider == "databricks": try: import os import openai from instructor import from_openai # type: ignore[attr-defined] api_key = ( api_key or os.environ.get("DATABRICKS_TOKEN") or os.environ.get("DATABRICKS_API_KEY") ) if not api_key: from .core.exceptions import ConfigurationError raise ConfigurationError( "DATABRICKS_TOKEN is not set. " "Set it with `export DATABRICKS_TOKEN=` or `export DATABRICKS_API_KEY=` " "or pass it as kwarg `api_key=`." ) 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 .core.exceptions import ConfigurationError raise ConfigurationError( "DATABRICKS_HOST is not set. " "Set it with `export DATABRICKS_HOST=` or `export DATABRICKS_WORKSPACE_URL=` " "or pass `base_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 instructor.Mode.TOOLS, **kwargs, ) logger.info( "Client initialized", extra={**provider_info, "status": "success"}, ) return result except ImportError: from .core.exceptions 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 elif provider == "anthropic": try: import anthropic from instructor import from_anthropic # type: ignore[attr-defined] # type: ignore[attr-defined] 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__}"}, ) ) max_tokens = kwargs.pop("max_tokens", 4096) result = from_anthropic( client, model=model_name, mode=mode if mode else instructor.Mode.ANTHROPIC_TOOLS, max_tokens=max_tokens, **kwargs, ) logger.info( "Client initialized", extra={**provider_info, "status": "success"}, ) return result except ImportError: from .core.exceptions 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 elif provider == "google": # Import google-genai package - catch ImportError only for actual imports try: import google.genai as genai from instructor import from_genai # type: ignore[attr-defined] except ImportError as e: from .core.exceptions 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, ) # type: ignore if async_client: result = from_genai( client, use_async=True, model=model_name, mode=mode if mode else instructor.Mode.GENAI_TOOLS, **kwargs, ) # type: ignore else: result = from_genai( client, model=model_name, mode=mode if mode else instructor.Mode.GENAI_TOOLS, **kwargs, ) # type: ignore 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 elif provider == "mistral": try: from mistralai import Mistral from instructor import from_mistral # type: ignore[attr-defined] 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=`." ) 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 .core.exceptions 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 elif provider == "cohere": try: import cohere from instructor import from_cohere # type: ignore[attr-defined] client = ( cohere.AsyncClientV2(api_key=api_key) if async_client else cohere.ClientV2(api_key=api_key) ) result = from_cohere(client, model=model_name, **kwargs) logger.info( "Client initialized", extra={**provider_info, "status": "success"}, ) return result except ImportError: from .core.exceptions 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 elif provider == "perplexity": try: import openai from instructor import from_perplexity # type: ignore[attr-defined] import os api_key = api_key or os.environ.get("PERPLEXITY_API_KEY") if not api_key: raise ValueError( "PERPLEXITY_API_KEY is not set. " "Set it with `export PERPLEXITY_API_KEY=` or pass it as a kwarg 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 .core.exceptions 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 elif provider == "groq": try: import groq from instructor import from_groq # type: ignore[attr-defined] 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 .core.exceptions 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 elif provider == "writer": try: from writerai import AsyncWriter, Writer from instructor import from_writer # type: ignore[attr-defined] 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 .core.exceptions 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 elif provider == "bedrock": try: import os import boto3 from instructor import from_bedrock # type: ignore[attr-defined] # 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 = instructor.Mode.BEDROCK_TOOLS else: default_mode = instructor.Mode.BEDROCK_JSON else: default_mode = mode result = from_bedrock( client, mode=default_mode, async_client=async_client, _async=async_client, # for backward compatibility **kwargs, ) logger.info( "Client initialized", extra={**provider_info, "status": "success"}, ) return result except ImportError: from .core.exceptions 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 elif provider == "cerebras": try: from cerebras.cloud.sdk import AsyncCerebras, Cerebras from instructor import from_cerebras # type: ignore[attr-defined] 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 .core.exceptions 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 elif provider == "fireworks": try: from fireworks.client import AsyncFireworks, Fireworks from instructor import from_fireworks # type: ignore[attr-defined] 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 .core.exceptions 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 elif provider == "vertexai": 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 google-genai package - catch ImportError only for actual imports try: import google.genai as genai # type: ignore from instructor import from_genai # type: ignore[attr-defined] except ImportError as e: from .core.exceptions import ConfigurationError raise ConfigurationError( "The google-genai package is required to use the VertexAI provider. " "Install it with `pip install google-genai`." ) 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=` " "or pass it as kwarg project=" ) client = genai.Client( vertexai=True, project=project, location=location, **kwargs, ) # type: ignore kwargs["model"] = model_name # Pass model as part of kwargs if async_client: result = from_genai( client, use_async=True, mode=mode if mode else instructor.Mode.GENAI_TOOLS, **kwargs, ) # type: ignore else: result = from_genai( client, mode=mode if mode else instructor.Mode.GENAI_TOOLS, **kwargs ) # type: ignore 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 elif provider == "generative-ai": 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 import from_genai # type: ignore[attr-defined] except ImportError as e: from .core.exceptions 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 instructor.Mode.GENAI_TOOLS, **kwargs, ) # type: ignore else: result = from_genai( client, model=model_name, mode=mode if mode else instructor.Mode.GENAI_TOOLS, **kwargs, ) # type: ignore 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 elif provider == "ollama": try: import openai from instructor import from_openai # type: ignore[attr-defined] # 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 = ( instructor.Mode.TOOLS if supports_tools else instructor.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 .core.exceptions 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 elif provider == "deepseek": try: import openai from instructor import from_openai # type: ignore[attr-defined] 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 .core.exceptions import ConfigurationError raise ConfigurationError( "DEEPSEEK_API_KEY is not set. " "Set it with `export DEEPSEEK_API_KEY=` or pass it as kwarg 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_openai( client, model=model_name, mode=mode if mode else instructor.Mode.TOOLS, **kwargs, ) logger.info( "Client initialized", extra={**provider_info, "status": "success"}, ) return result except ImportError: from .core.exceptions 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 elif provider == "xai": try: from xai_sdk.sync.client import Client as SyncClient from xai_sdk.aio.client import Client as AsyncClient from instructor import from_xai # type: ignore[attr-defined] client = ( AsyncClient(api_key=api_key) if async_client else SyncClient(api_key=api_key) ) result = from_xai( client, mode=mode if mode else instructor.Mode.XAI_JSON, model=model_name, **kwargs, ) logger.info( "Client initialized", extra={**provider_info, "status": "success"}, ) return result except ImportError: from .core.exceptions 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 elif provider == "openrouter": try: import openai from instructor import from_openai # type: ignore[attr-defined] 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 .core.exceptions import ConfigurationError raise ConfigurationError( "OPENROUTER_API_KEY is not set. " "Set it with `export OPENROUTER_API_KEY=` or pass it as kwarg 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_openai( client, model=model_name, mode=mode if mode else instructor.Mode.TOOLS, **kwargs, ) logger.info( "Client initialized", extra={**provider_info, "status": "success"}, ) return result except ImportError: from .core.exceptions 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 elif provider == "litellm": try: from litellm import completion, acompletion from instructor import from_litellm completion_func = acompletion if async_client else completion result = from_litellm( completion_func, mode=mode if mode else instructor.Mode.TOOLS, **kwargs, ) logger.info( "Client initialized", extra={**provider_info, "status": "success"}, ) return result except ImportError: from .core.exceptions 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 else: from .core.exceptions 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}" )