| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232 |
- """Azure OpenAI large language models. Not to be confused with chat models."""
- from __future__ import annotations
- import logging
- from collections.abc import Awaitable, Callable, Mapping
- from typing import Any, cast
- import openai
- from langchain_core.language_models import LangSmithParams
- from langchain_core.utils import from_env, secret_from_env
- from pydantic import Field, SecretStr, model_validator
- from typing_extensions import Self
- from langchain_openai.llms.base import BaseOpenAI
- logger = logging.getLogger(__name__)
- class AzureOpenAI(BaseOpenAI):
- """Azure-specific OpenAI large language models.
- To use, you should have the `openai` python package installed, and the
- environment variable `OPENAI_API_KEY` set with your API key.
- Any parameters that are valid to be passed to the openai.create call can be passed
- in, even if not explicitly saved on this class.
- Example:
- ```python
- from langchain_openai import AzureOpenAI
- openai = AzureOpenAI(model_name="gpt-3.5-turbo-instruct")
- ```
- """
- azure_endpoint: str | None = Field(
- default_factory=from_env("AZURE_OPENAI_ENDPOINT", default=None)
- )
- """Your Azure endpoint, including the resource.
- Automatically inferred from env var `AZURE_OPENAI_ENDPOINT` if not provided.
- Example: `'https://example-resource.azure.openai.com/'`
- """
- deployment_name: str | None = Field(default=None, alias="azure_deployment")
- """A model deployment.
- If given sets the base client URL to include `/deployments/{azure_deployment}`.
- !!! note
- This means you won't be able to use non-deployment endpoints.
- """
- openai_api_version: str | None = Field(
- alias="api_version",
- default_factory=from_env("OPENAI_API_VERSION", default=None),
- )
- """Automatically inferred from env var `OPENAI_API_VERSION` if not provided."""
- # Check OPENAI_KEY for backwards compatibility.
- # TODO: Remove OPENAI_API_KEY support to avoid possible conflict when using
- # other forms of azure credentials.
- openai_api_key: SecretStr | None = Field(
- alias="api_key",
- default_factory=secret_from_env(
- ["AZURE_OPENAI_API_KEY", "OPENAI_API_KEY"], default=None
- ),
- )
- azure_ad_token: SecretStr | None = Field(
- default_factory=secret_from_env("AZURE_OPENAI_AD_TOKEN", default=None)
- )
- """Your Azure Active Directory token.
- Automatically inferred from env var `AZURE_OPENAI_AD_TOKEN` if not provided.
- `For more, see this page <https://www.microsoft.com/en-us/security/business/identity-access/microsoft-entra-id>.`__
- """
- azure_ad_token_provider: Callable[[], str] | None = None
- """A function that returns an Azure Active Directory token.
- Will be invoked on every sync request. For async requests,
- will be invoked if `azure_ad_async_token_provider` is not provided.
- """
- azure_ad_async_token_provider: Callable[[], Awaitable[str]] | None = None
- """A function that returns an Azure Active Directory token.
- Will be invoked on every async request.
- """
- openai_api_type: str | None = Field(
- default_factory=from_env("OPENAI_API_TYPE", default="azure")
- )
- """Legacy, for `openai<1.0.0` support."""
- validate_base_url: bool = True
- """For backwards compatibility. If legacy val openai_api_base is passed in, try to
- infer if it is a base_url or azure_endpoint and update accordingly.
- """
- @classmethod
- def get_lc_namespace(cls) -> list[str]:
- """Get the namespace of the LangChain object.
- Returns:
- `["langchain", "llms", "openai"]`
- """
- return ["langchain", "llms", "openai"]
- @property
- def lc_secrets(self) -> dict[str, str]:
- """Mapping of secret keys to environment variables."""
- return {
- "openai_api_key": "AZURE_OPENAI_API_KEY",
- "azure_ad_token": "AZURE_OPENAI_AD_TOKEN",
- }
- @classmethod
- def is_lc_serializable(cls) -> bool:
- """Return whether this model can be serialized by LangChain."""
- return True
- @model_validator(mode="after")
- def validate_environment(self) -> Self:
- """Validate that api key and python package exists in environment."""
- if self.n < 1:
- msg = "n must be at least 1."
- raise ValueError(msg)
- if self.streaming and self.n > 1:
- msg = "Cannot stream results when n > 1."
- raise ValueError(msg)
- if self.streaming and self.best_of > 1:
- msg = "Cannot stream results when best_of > 1."
- raise ValueError(msg)
- # For backwards compatibility. Before openai v1, no distinction was made
- # between azure_endpoint and base_url (openai_api_base).
- openai_api_base = self.openai_api_base
- if openai_api_base and self.validate_base_url:
- if "/openai" not in openai_api_base:
- self.openai_api_base = (
- cast(str, self.openai_api_base).rstrip("/") + "/openai"
- )
- msg = (
- "As of openai>=1.0.0, Azure endpoints should be specified via "
- "the `azure_endpoint` param not `openai_api_base` "
- "(or alias `base_url`)."
- )
- raise ValueError(msg)
- if self.deployment_name:
- msg = (
- "As of openai>=1.0.0, if `deployment_name` (or alias "
- "`azure_deployment`) is specified then "
- "`openai_api_base` (or alias `base_url`) should not be. "
- "Instead use `deployment_name` (or alias `azure_deployment`) "
- "and `azure_endpoint`."
- )
- raise ValueError(msg)
- self.deployment_name = None
- client_params: dict = {
- "api_version": self.openai_api_version,
- "azure_endpoint": self.azure_endpoint,
- "azure_deployment": self.deployment_name,
- "api_key": self.openai_api_key.get_secret_value()
- if self.openai_api_key
- else None,
- "azure_ad_token": self.azure_ad_token.get_secret_value()
- if self.azure_ad_token
- else None,
- "azure_ad_token_provider": self.azure_ad_token_provider,
- "organization": self.openai_organization,
- "base_url": self.openai_api_base,
- "timeout": self.request_timeout,
- "max_retries": self.max_retries,
- "default_headers": {
- **(self.default_headers or {}),
- "User-Agent": "langchain-partner-python-azure-openai",
- },
- "default_query": self.default_query,
- }
- if not self.client:
- sync_specific = {"http_client": self.http_client}
- self.client = openai.AzureOpenAI(
- **client_params,
- **sync_specific, # type: ignore[arg-type]
- ).completions
- if not self.async_client:
- async_specific = {"http_client": self.http_async_client}
- if self.azure_ad_async_token_provider:
- client_params["azure_ad_token_provider"] = (
- self.azure_ad_async_token_provider
- )
- self.async_client = openai.AsyncAzureOpenAI(
- **client_params,
- **async_specific, # type: ignore[arg-type]
- ).completions
- return self
- @property
- def _identifying_params(self) -> Mapping[str, Any]:
- return {
- "deployment_name": self.deployment_name,
- **super()._identifying_params,
- }
- @property
- def _invocation_params(self) -> dict[str, Any]:
- openai_params = {"model": self.deployment_name}
- return {**openai_params, **super()._invocation_params}
- def _get_ls_params(
- self, stop: list[str] | None = None, **kwargs: Any
- ) -> LangSmithParams:
- """Get standard params for tracing."""
- params = super()._get_ls_params(stop=stop, **kwargs)
- invocation_params = self._invocation_params
- params["ls_provider"] = "azure"
- if model_name := invocation_params.get("model"):
- params["ls_model_name"] = model_name
- return params
- @property
- def _llm_type(self) -> str:
- """Return type of llm."""
- return "azure"
- @property
- def lc_attributes(self) -> dict[str, Any]:
- """Attributes relevant to tracing."""
- return {
- "openai_api_type": self.openai_api_type,
- "openai_api_version": self.openai_api_version,
- }
|