def_stream_response_to_generation_chunk(stream_response:Dict[str,Any],)->GenerationChunk:"""Convert a stream response to a generation chunk."""ifnotstream_response["choices"]:returnGenerationChunk(text="")returnGenerationChunk(text=stream_response["choices"][0]["text"],generation_info=dict(finish_reason=stream_response["choices"][0].get("finish_reason",None),logprobs=stream_response["choices"][0].get("logprobs",None),),)def_update_response(response:Dict[str,Any],stream_response:Dict[str,Any])->None:"""Update response from the stream response."""response["choices"][0]["text"]+=stream_response["choices"][0]["text"]response["choices"][0]["finish_reason"]=stream_response["choices"][0].get("finish_reason",None)response["choices"][0]["logprobs"]=stream_response["choices"][0]["logprobs"]def_streaming_response_template()->Dict[str,Any]:return{"choices":[{"text":"","finish_reason":None,"logprobs":None,}]}def_create_retry_decorator(llm:Union[BaseOpenAI,OpenAIChat],run_manager:Optional[Union[AsyncCallbackManagerForLLMRun,CallbackManagerForLLMRun]]=None,)->Callable[[Any],Any]:importopenaierrors=[openai.error.Timeout,# type: ignore[attr-defined]openai.error.APIError,# type: ignore[attr-defined]openai.error.APIConnectionError,# type: ignore[attr-defined]openai.error.RateLimitError,# type: ignore[attr-defined]openai.error.ServiceUnavailableError,# type: ignore[attr-defined]]returncreate_base_retry_decorator(error_types=errors,max_retries=llm.max_retries,run_manager=run_manager)
[docs]defcompletion_with_retry(llm:Union[BaseOpenAI,OpenAIChat],run_manager:Optional[CallbackManagerForLLMRun]=None,**kwargs:Any,)->Any:"""Use tenacity to retry the completion call."""ifis_openai_v1():returnllm.client.create(**kwargs)retry_decorator=_create_retry_decorator(llm,run_manager=run_manager)@retry_decoratordef_completion_with_retry(**kwargs:Any)->Any:returnllm.client.create(**kwargs)return_completion_with_retry(**kwargs)
[docs]asyncdefacompletion_with_retry(llm:Union[BaseOpenAI,OpenAIChat],run_manager:Optional[AsyncCallbackManagerForLLMRun]=None,**kwargs:Any,)->Any:"""Use tenacity to retry the async completion call."""ifis_openai_v1():returnawaitllm.async_client.create(**kwargs)retry_decorator=_create_retry_decorator(llm,run_manager=run_manager)@retry_decoratorasyncdef_completion_with_retry(**kwargs:Any)->Any:# Use OpenAI's async api https://github.com/openai/openai-python#async-apireturnawaitllm.client.acreate(**kwargs)returnawait_completion_with_retry(**kwargs)
[docs]classBaseOpenAI(BaseLLM):"""Base OpenAI large language model class."""@propertydeflc_secrets(self)->Dict[str,str]:return{"openai_api_key":"OPENAI_API_KEY"}@classmethoddefget_lc_namespace(cls)->List[str]:"""Get the namespace of the langchain object."""return["langchain","llms","openai"]@propertydeflc_attributes(self)->Dict[str,Any]:attributes:Dict[str,Any]={}ifself.openai_api_base:attributes["openai_api_base"]=self.openai_api_baseifself.openai_organization:attributes["openai_organization"]=self.openai_organizationifself.openai_proxy:attributes["openai_proxy"]=self.openai_proxyreturnattributes@classmethoddefis_lc_serializable(cls)->bool:returnTrueclient:Any=Field(default=None,exclude=True)#: :meta private:async_client:Any=Field(default=None,exclude=True)#: :meta private:model_name:str=Field(default="gpt-3.5-turbo-instruct",alias="model")"""Model name to use."""temperature:float=0.7"""What sampling temperature to use."""max_tokens:int=256"""The maximum number of tokens to generate in the completion. -1 returns as many tokens as possible given the prompt and the models maximal context size."""top_p:float=1"""Total probability mass of tokens to consider at each step."""frequency_penalty:float=0"""Penalizes repeated tokens according to frequency."""presence_penalty:float=0"""Penalizes repeated tokens."""n:int=1"""How many completions to generate for each prompt."""best_of:int=1"""Generates best_of completions server-side and returns the "best"."""model_kwargs:Dict[str,Any]=Field(default_factory=dict)"""Holds any model parameters valid for `create` call not explicitly specified."""# When updating this to use a SecretStr# Check for classes that derive from this class (as some of them# may assume openai_api_key is a str)openai_api_key:Optional[str]=Field(default=None,alias="api_key")"""Automatically inferred from env var `OPENAI_API_KEY` if not provided."""openai_api_base:Optional[str]=Field(default=None,alias="base_url")"""Base URL path for API requests, leave blank if not using a proxy or service emulator."""openai_organization:Optional[str]=Field(default=None,alias="organization")"""Automatically inferred from env var `OPENAI_ORG_ID` if not provided."""# to support explicit proxy for OpenAIopenai_proxy:Optional[str]=Nonebatch_size:int=20"""Batch size to use when passing multiple documents to generate."""request_timeout:Union[float,Tuple[float,float],Any,None]=Field(default=None,alias="timeout")"""Timeout for requests to OpenAI completion API. Can be float, httpx.Timeout or None."""logit_bias:Optional[Dict[str,float]]=Field(default_factory=dict)# type: ignore[arg-type]"""Adjust the probability of specific tokens being generated."""max_retries:int=2"""Maximum number of retries to make when generating."""streaming:bool=False"""Whether to stream the results or not."""allowed_special:Union[Literal["all"],AbstractSet[str]]=set()"""Set of special tokens that are allowed。"""disallowed_special:Union[Literal["all"],Collection[str]]="all""""Set of special tokens that are not allowed。"""tiktoken_model_name:Optional[str]=None"""The model name to pass to tiktoken when using this class. Tiktoken is used to count the number of tokens in documents to constrain them to be under a certain limit. By default, when set to None, this will be the same as the embedding model name. However, there are some cases where you may want to use this Embedding class with a model name not supported by tiktoken. This can include when using Azure embeddings or when using one of the many model providers that expose an OpenAI-like API but with different models. In those cases, in order to avoid erroring when tiktoken is called, you can specify a model name to use here."""default_headers:Union[Mapping[str,str],None]=Nonedefault_query:Union[Mapping[str,object],None]=None# Configure a custom httpx client. See the# [httpx documentation](https://www.python-httpx.org/api/#client) for more details.http_client:Union[Any,None]=None"""Optional httpx.Client."""def__new__(cls,**data:Any)->Union[OpenAIChat,BaseOpenAI]:# type: ignore"""Initialize the OpenAI object."""model_name=data.get("model_name","")if(model_name.startswith("gpt-3.5-turbo")ormodel_name.startswith("gpt-4"))and"-instruct"notinmodel_name:warnings.warn("You are trying to use a chat model. This way of initializing it is ""no longer supported. Instead, please use: ""`from langchain_community.chat_models import ChatOpenAI`")returnOpenAIChat(**data)returnsuper().__new__(cls)model_config=ConfigDict(populate_by_name=True,)@model_validator(mode="before")@classmethoddefbuild_extra(cls,values:Dict[str,Any])->Any:"""Build extra kwargs from additional params that were passed in."""all_required_field_names=get_pydantic_field_names(cls)values=_build_model_kwargs(values,all_required_field_names)returnvalues
[docs]@pre_initdefvalidate_environment(cls,values:Dict)->Dict:"""Validate that api key and python package exists in environment."""ifvalues["n"]<1:raiseValueError("n must be at least 1.")ifvalues["streaming"]andvalues["n"]>1:raiseValueError("Cannot stream results when n > 1.")ifvalues["streaming"]andvalues["best_of"]>1:raiseValueError("Cannot stream results when best_of > 1.")values["openai_api_key"]=get_from_dict_or_env(values,"openai_api_key","OPENAI_API_KEY")values["openai_api_base"]=values["openai_api_base"]oros.getenv("OPENAI_API_BASE")values["openai_proxy"]=get_from_dict_or_env(values,"openai_proxy","OPENAI_PROXY",default="",)values["openai_organization"]=(values["openai_organization"]oros.getenv("OPENAI_ORG_ID")oros.getenv("OPENAI_ORGANIZATION"))try:importopenaiexceptImportError:raiseImportError("Could not import openai python package. ""Please install it with `pip install openai`.")ifis_openai_v1():client_params={"api_key":values["openai_api_key"],"organization":values["openai_organization"],"base_url":values["openai_api_base"],"timeout":values["request_timeout"],"max_retries":values["max_retries"],"default_headers":values["default_headers"],"default_query":values["default_query"],"http_client":values["http_client"],}ifnotvalues.get("client"):values["client"]=openai.OpenAI(**client_params).completionsifnotvalues.get("async_client"):values["async_client"]=openai.AsyncOpenAI(**client_params).completionselifnotvalues.get("client"):values["client"]=openai.Completion# type: ignore[attr-defined]else:passreturnvalues
@propertydef_default_params(self)->Dict[str,Any]:"""Get the default parameters for calling OpenAI API."""normal_params:Dict[str,Any]={"temperature":self.temperature,"top_p":self.top_p,"frequency_penalty":self.frequency_penalty,"presence_penalty":self.presence_penalty,"n":self.n,"logit_bias":self.logit_bias,}ifself.max_tokensisnotNone:normal_params["max_tokens"]=self.max_tokensifself.request_timeoutisnotNoneandnotis_openai_v1():normal_params["request_timeout"]=self.request_timeout# Azure gpt-35-turbo doesn't support best_of# don't specify best_of if it is 1ifself.best_of>1:normal_params["best_of"]=self.best_ofreturn{**normal_params,**self.model_kwargs}def_stream(self,prompt:str,stop:Optional[List[str]]=None,run_manager:Optional[CallbackManagerForLLMRun]=None,**kwargs:Any,)->Iterator[GenerationChunk]:params={**self._invocation_params,**kwargs,"stream":True}self.get_sub_prompts(params,[prompt],stop)# this mutates paramsforstream_respincompletion_with_retry(self,prompt=prompt,run_manager=run_manager,**params):ifnotisinstance(stream_resp,dict):stream_resp=stream_resp.dict()chunk=_stream_response_to_generation_chunk(stream_resp)ifrun_manager:run_manager.on_llm_new_token(chunk.text,chunk=chunk,verbose=self.verbose,logprobs=chunk.generation_info["logprobs"]ifchunk.generation_infoelseNone,)yieldchunkasyncdef_astream(self,prompt:str,stop:Optional[List[str]]=None,run_manager:Optional[AsyncCallbackManagerForLLMRun]=None,**kwargs:Any,)->AsyncIterator[GenerationChunk]:params={**self._invocation_params,**kwargs,"stream":True}self.get_sub_prompts(params,[prompt],stop)# this mutates paramsasyncforstream_respinawaitacompletion_with_retry(self,prompt=prompt,run_manager=run_manager,**params):ifnotisinstance(stream_resp,dict):stream_resp=stream_resp.dict()chunk=_stream_response_to_generation_chunk(stream_resp)ifrun_manager:awaitrun_manager.on_llm_new_token(chunk.text,chunk=chunk,verbose=self.verbose,logprobs=chunk.generation_info["logprobs"]ifchunk.generation_infoelseNone,)yieldchunkdef_generate(self,prompts:List[str],stop:Optional[List[str]]=None,run_manager:Optional[CallbackManagerForLLMRun]=None,**kwargs:Any,)->LLMResult:"""Call out to OpenAI's endpoint with k unique prompts. Args: prompts: The prompts to pass into the model. stop: Optional list of stop words to use when generating. Returns: The full LLM output. Example: .. code-block:: python response = openai.generate(["Tell me a joke."]) """# TODO: write a unit test for thisparams=self._invocation_paramsparams={**params,**kwargs}sub_prompts=self.get_sub_prompts(params,prompts,stop)choices=[]token_usage:Dict[str,int]={}# Get the token usage from the response.# Includes prompt, completion, and total tokens used._keys={"completion_tokens","prompt_tokens","total_tokens"}system_fingerprint:Optional[str]=Nonefor_promptsinsub_prompts:ifself.streaming:iflen(_prompts)>1:raiseValueError("Cannot stream results with multiple prompts.")generation:Optional[GenerationChunk]=Noneforchunkinself._stream(_prompts[0],stop,run_manager,**kwargs):ifgenerationisNone:generation=chunkelse:generation+=chunkassertgenerationisnotNonechoices.append({"text":generation.text,"finish_reason":generation.generation_info.get("finish_reason")ifgeneration.generation_infoelseNone,"logprobs":generation.generation_info.get("logprobs")ifgeneration.generation_infoelseNone,})else:response=completion_with_retry(self,prompt=_prompts,run_manager=run_manager,**params)ifnotisinstance(response,dict):# V1 client returns the response in an PyDantic object instead of# dict. For the transition period, we deep convert it to dict.response=response.dict()choices.extend(response["choices"])update_token_usage(_keys,response,token_usage)ifnotsystem_fingerprint:system_fingerprint=response.get("system_fingerprint")returnself.create_llm_result(choices,prompts,params,token_usage,system_fingerprint=system_fingerprint,)asyncdef_agenerate(self,prompts:List[str],stop:Optional[List[str]]=None,run_manager:Optional[AsyncCallbackManagerForLLMRun]=None,**kwargs:Any,)->LLMResult:"""Call out to OpenAI's endpoint async with k unique prompts."""params=self._invocation_paramsparams={**params,**kwargs}sub_prompts=self.get_sub_prompts(params,prompts,stop)choices=[]token_usage:Dict[str,int]={}# Get the token usage from the response.# Includes prompt, completion, and total tokens used._keys={"completion_tokens","prompt_tokens","total_tokens"}system_fingerprint:Optional[str]=Nonefor_promptsinsub_prompts:ifself.streaming:iflen(_prompts)>1:raiseValueError("Cannot stream results with multiple prompts.")generation:Optional[GenerationChunk]=Noneasyncforchunkinself._astream(_prompts[0],stop,run_manager,**kwargs):ifgenerationisNone:generation=chunkelse:generation+=chunkassertgenerationisnotNonechoices.append({"text":generation.text,"finish_reason":generation.generation_info.get("finish_reason")ifgeneration.generation_infoelseNone,"logprobs":generation.generation_info.get("logprobs")ifgeneration.generation_infoelseNone,})else:response=awaitacompletion_with_retry(self,prompt=_prompts,run_manager=run_manager,**params)ifnotisinstance(response,dict):response=response.dict()choices.extend(response["choices"])update_token_usage(_keys,response,token_usage)returnself.create_llm_result(choices,prompts,params,token_usage,system_fingerprint=system_fingerprint,)
[docs]defget_sub_prompts(self,params:Dict[str,Any],prompts:List[str],stop:Optional[List[str]]=None,)->List[List[str]]:"""Get the sub prompts for llm call."""ifstopisnotNone:if"stop"inparams:raiseValueError("`stop` found in both the input and default params.")params["stop"]=stopifparams["max_tokens"]==-1:iflen(prompts)!=1:raiseValueError("max_tokens set to -1 not supported for multiple inputs.")params["max_tokens"]=self.max_tokens_for_prompt(prompts[0])sub_prompts=[prompts[i:i+self.batch_size]foriinrange(0,len(prompts),self.batch_size)]returnsub_prompts
[docs]defcreate_llm_result(self,choices:Any,prompts:List[str],params:Dict[str,Any],token_usage:Dict[str,int],*,system_fingerprint:Optional[str]=None,)->LLMResult:"""Create the LLMResult from the choices and prompts."""generations=[]n=params.get("n",self.n)fori,_inenumerate(prompts):sub_choices=choices[i*n:(i+1)*n]generations.append([Generation(text=choice["text"],generation_info=dict(finish_reason=choice.get("finish_reason"),logprobs=choice.get("logprobs"),),)forchoiceinsub_choices])llm_output={"token_usage":token_usage,"model_name":self.model_name}ifsystem_fingerprint:llm_output["system_fingerprint"]=system_fingerprintreturnLLMResult(generations=generations,llm_output=llm_output)
@propertydef_invocation_params(self)->Dict[str,Any]:"""Get the parameters used to invoke the model."""openai_creds:Dict[str,Any]={}ifnotis_openai_v1():openai_creds.update({"api_key":self.openai_api_key,"api_base":self.openai_api_base,"organization":self.openai_organization,})ifself.openai_proxy:importopenaiopenai.proxy={"http":self.openai_proxy,"https":self.openai_proxy}# type: ignore[assignment] # type: ignore[attr-defined] # type: ignore[attr-defined] # type: ignore[attr-defined] # type: ignore[attr-defined] # type: ignore[attr-defined] # type: ignore[attr-defined]return{**openai_creds,**self._default_params}@propertydef_identifying_params(self)->Mapping[str,Any]:"""Get the identifying parameters."""return{**{"model_name":self.model_name},**self._default_params}@propertydef_llm_type(self)->str:"""Return type of llm."""return"openai"
[docs]defget_token_ids(self,text:str)->List[int]:"""Get the token IDs using the tiktoken package."""# tiktoken NOT supported for Python < 3.8ifsys.version_info[1]<8:returnsuper().get_num_tokens(text)try:importtiktokenexceptImportError:raiseImportError("Could not import tiktoken python package. ""This is needed in order to calculate get_num_tokens. ""Please install it with `pip install tiktoken`.")model_name=self.tiktoken_model_nameorself.model_nametry:enc=tiktoken.encoding_for_model(model_name)exceptKeyError:logger.warning("Warning: model not found. Using cl100k_base encoding.")model="cl100k_base"enc=tiktoken.get_encoding(model)returnenc.encode(text,allowed_special=self.allowed_special,disallowed_special=self.disallowed_special,)
[docs]@staticmethoddefmodelname_to_contextsize(modelname:str)->int:"""Calculate the maximum number of tokens possible to generate for a model. Args: modelname: The modelname we want to know the context size for. Returns: The maximum context size Example: .. code-block:: python max_tokens = openai.modelname_to_contextsize("gpt-3.5-turbo-instruct") """model_token_mapping={"gpt-4o":128_000,"gpt-4o-2024-05-13":128_000,"gpt-4":8192,"gpt-4-0314":8192,"gpt-4-0613":8192,"gpt-4-32k":32768,"gpt-4-32k-0314":32768,"gpt-4-32k-0613":32768,"gpt-3.5-turbo":4096,"gpt-3.5-turbo-0301":4096,"gpt-3.5-turbo-0613":4096,"gpt-3.5-turbo-16k":16385,"gpt-3.5-turbo-16k-0613":16385,"gpt-3.5-turbo-instruct":4096,"text-ada-001":2049,"ada":2049,"text-babbage-001":2040,"babbage":2049,"text-curie-001":2049,"curie":2049,"davinci":2049,"text-davinci-003":4097,"text-davinci-002":4097,"code-davinci-002":8001,"code-davinci-001":8001,"code-cushman-002":2048,"code-cushman-001":2048,}# handling finetuned modelsif"ft-"inmodelname:modelname=modelname.split(":")[0]context_size=model_token_mapping.get(modelname,None)ifcontext_sizeisNone:raiseValueError(f"Unknown model: {modelname}. Please provide a valid OpenAI model name.""Known models are: "+", ".join(model_token_mapping.keys()))returncontext_size
@propertydefmax_context_size(self)->int:"""Get max context size for this model."""returnself.modelname_to_contextsize(self.model_name)
[docs]defmax_tokens_for_prompt(self,prompt:str)->int:"""Calculate the maximum number of tokens possible to generate for a prompt. Args: prompt: The prompt to pass into the model. Returns: The maximum number of tokens to generate for a prompt. Example: .. code-block:: python max_tokens = openai.max_tokens_for_prompt("Tell me a joke.") """num_tokens=self.get_num_tokens(prompt)returnself.max_context_size-num_tokens
[docs]@deprecated(since="0.0.10",removal="1.0",alternative_import="langchain_openai.OpenAI")classOpenAI(BaseOpenAI):"""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: .. code-block:: python from langchain_community.llms import OpenAI openai = OpenAI(model_name="gpt-3.5-turbo-instruct") """@classmethoddefget_lc_namespace(cls)->List[str]:"""Get the namespace of the langchain object."""return["langchain","llms","openai"]@propertydef_invocation_params(self)->Dict[str,Any]:return{**{"model":self.model_name},**super()._invocation_params}
[docs]@deprecated(since="0.0.10",removal="1.0",alternative_import="langchain_openai.AzureOpenAI")classAzureOpenAI(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: .. code-block:: python from langchain_community.llms import AzureOpenAI openai = AzureOpenAI(model_name="gpt-3.5-turbo-instruct") """azure_endpoint:Union[str,None]=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:Union[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=Field(default="",alias="api_version")"""Automatically inferred from env var `OPENAI_API_VERSION` if not provided."""openai_api_key:Union[str,None]=Field(default=None,alias="api_key")"""Automatically inferred from env var `AZURE_OPENAI_API_KEY` if not provided."""azure_ad_token:Union[str,None]=None"""Your Azure Active Directory token. Automatically inferred from env var `AZURE_OPENAI_AD_TOKEN` if not provided. For more: https://www.microsoft.com/en-us/security/business/identity-access/microsoft-entra-id. """azure_ad_token_provider:Union[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:Union[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="""""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. """@classmethoddefget_lc_namespace(cls)->List[str]:"""Get the namespace of the langchain object."""return["langchain","llms","openai"]
[docs]@pre_initdefvalidate_environment(cls,values:Dict)->Dict:"""Validate that api key and python package exists in environment."""ifvalues["n"]<1:raiseValueError("n must be at least 1.")ifvalues["streaming"]andvalues["n"]>1:raiseValueError("Cannot stream results when n > 1.")ifvalues["streaming"]andvalues["best_of"]>1:raiseValueError("Cannot stream results when best_of > 1.")# Check OPENAI_KEY for backwards compatibility.# TODO: Remove OPENAI_API_KEY support to avoid possible conflict when using# other forms of azure credentials.values["openai_api_key"]=(values["openai_api_key"]oros.getenv("AZURE_OPENAI_API_KEY")oros.getenv("OPENAI_API_KEY"))values["azure_endpoint"]=values["azure_endpoint"]oros.getenv("AZURE_OPENAI_ENDPOINT")values["azure_ad_token"]=values["azure_ad_token"]oros.getenv("AZURE_OPENAI_AD_TOKEN")values["openai_api_base"]=values["openai_api_base"]oros.getenv("OPENAI_API_BASE")values["openai_proxy"]=get_from_dict_or_env(values,"openai_proxy","OPENAI_PROXY",default="",)values["openai_organization"]=(values["openai_organization"]oros.getenv("OPENAI_ORG_ID")oros.getenv("OPENAI_ORGANIZATION"))values["openai_api_version"]=values["openai_api_version"]oros.getenv("OPENAI_API_VERSION")values["openai_api_type"]=get_from_dict_or_env(values,"openai_api_type","OPENAI_API_TYPE",default="azure")try:importopenaiexceptImportError:raiseImportError("Could not import openai python package. ""Please install it with `pip install openai`.")ifis_openai_v1():# For backwards compatibility. Before openai v1, no distinction was made# between azure_endpoint and base_url (openai_api_base).openai_api_base=values["openai_api_base"]ifopenai_api_baseandvalues["validate_base_url"]:if"/openai"notinopenai_api_base:values["openai_api_base"]=(values["openai_api_base"].rstrip("/")+"/openai")warnings.warn("As of openai>=1.0.0, Azure endpoints should be specified via "f"the `azure_endpoint` param not `openai_api_base` "f"(or alias `base_url`). Updating `openai_api_base` from "f"{openai_api_base} to {values['openai_api_base']}.")ifvalues["deployment_name"]:warnings.warn("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`.")ifvalues["deployment_name"]notinvalues["openai_api_base"]:warnings.warn("As of openai>=1.0.0, if `openai_api_base` ""(or alias `base_url`) is specified it is expected to be ""of the form ""https://example-resource.azure.openai.com/openai/deployments/example-deployment. "# noqa: E501f"Updating {openai_api_base} to "f"{values['openai_api_base']}.")values["openai_api_base"]+=("/deployments/"+values["deployment_name"])values["deployment_name"]=Noneclient_params={"api_version":values["openai_api_version"],"azure_endpoint":values["azure_endpoint"],"azure_deployment":values["deployment_name"],"api_key":values["openai_api_key"],"azure_ad_token":values["azure_ad_token"],"azure_ad_token_provider":values["azure_ad_token_provider"],"organization":values["openai_organization"],"base_url":values["openai_api_base"],"timeout":values["request_timeout"],"max_retries":values["max_retries"],"default_headers":{**(values["default_headers"]or{}),"User-Agent":"langchain-comm-python-azure-openai",},"default_query":values["default_query"],"http_client":values["http_client"],}values["client"]=openai.AzureOpenAI(**client_params).completionsazure_ad_async_token_provider=values["azure_ad_async_token_provider"]ifazure_ad_async_token_provider:client_params["azure_ad_token_provider"]=azure_ad_async_token_providervalues["async_client"]=openai.AsyncAzureOpenAI(**client_params).completionselse:values["client"]=openai.Completion# type: ignore[attr-defined]returnvalues
@propertydef_identifying_params(self)->Mapping[str,Any]:return{**{"deployment_name":self.deployment_name},**super()._identifying_params,}@propertydef_invocation_params(self)->Dict[str,Any]:ifis_openai_v1():openai_params={"model":self.deployment_name}else:openai_params={"engine":self.deployment_name,"api_type":self.openai_api_type,"api_version":self.openai_api_version,}return{**openai_params,**super()._invocation_params}@propertydef_llm_type(self)->str:"""Return type of llm."""return"azure"@propertydeflc_attributes(self)->Dict[str,Any]:return{"openai_api_type":self.openai_api_type,"openai_api_version":self.openai_api_version,}
[docs]@deprecated(since="0.0.1",removal="1.0",alternative_import="langchain_openai.ChatOpenAI",)classOpenAIChat(BaseLLM):"""OpenAI Chat 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: .. code-block:: python from langchain_community.llms import OpenAIChat openaichat = OpenAIChat(model_name="gpt-3.5-turbo") """client:Any=Field(default=None,exclude=True)#: :meta private:async_client:Any=Field(default=None,exclude=True)#: :meta private:model_name:str="gpt-3.5-turbo""""Model name to use."""model_kwargs:Dict[str,Any]=Field(default_factory=dict)"""Holds any model parameters valid for `create` call not explicitly specified."""# When updating this to use a SecretStr# Check for classes that derive from this class (as some of them# may assume openai_api_key is a str)openai_api_key:Optional[str]=Field(default=None,alias="api_key")"""Automatically inferred from env var `OPENAI_API_KEY` if not provided."""openai_api_base:Optional[str]=Field(default=None,alias="base_url")"""Base URL path for API requests, leave blank if not using a proxy or service emulator."""# to support explicit proxy for OpenAIopenai_proxy:Optional[str]=Nonemax_retries:int=6"""Maximum number of retries to make when generating."""prefix_messages:List=Field(default_factory=list)"""Series of messages for Chat input."""streaming:bool=False"""Whether to stream the results or not."""allowed_special:Union[Literal["all"],AbstractSet[str]]=set()"""Set of special tokens that are allowed。"""disallowed_special:Union[Literal["all"],Collection[str]]="all""""Set of special tokens that are not allowed。"""@model_validator(mode="before")@classmethoddefbuild_extra(cls,values:Dict[str,Any])->Any:"""Build extra kwargs from additional params that were passed in."""all_required_field_names={field.aliasforfieldinget_fields(cls).values()}extra=values.get("model_kwargs",{})forfield_nameinlist(values):iffield_namenotinall_required_field_names:iffield_nameinextra:raiseValueError(f"Found {field_name} supplied twice.")extra[field_name]=values.pop(field_name)values["model_kwargs"]=extrareturnvalues
[docs]@pre_initdefvalidate_environment(cls,values:Dict)->Dict:"""Validate that api key and python package exists in environment."""openai_api_key=get_from_dict_or_env(values,"openai_api_key","OPENAI_API_KEY")openai_api_base=get_from_dict_or_env(values,"openai_api_base","OPENAI_API_BASE",default="",)openai_proxy=get_from_dict_or_env(values,"openai_proxy","OPENAI_PROXY",default="",)openai_organization=get_from_dict_or_env(values,"openai_organization","OPENAI_ORGANIZATION",default="")try:importopenaiopenai.api_key=openai_api_keyifopenai_api_base:openai.api_base=openai_api_base# type: ignore[attr-defined]ifopenai_organization:openai.organization=openai_organizationifopenai_proxy:openai.proxy={"http":openai_proxy,"https":openai_proxy}# type: ignore[assignment] # type: ignore[attr-defined] # type: ignore[attr-defined] # type: ignore[attr-defined] # type: ignore[attr-defined] # type: ignore[attr-defined] # type: ignore[attr-defined]exceptImportError:raiseImportError("Could not import openai python package. ""Please install it with `pip install openai`.")try:values["client"]=openai.ChatCompletion# type: ignore[attr-defined]exceptAttributeError:raiseValueError("`openai` has no `ChatCompletion` attribute, this is likely ""due to an old version of the openai package. Try upgrading it ""with `pip install --upgrade openai`.")warnings.warn("You are trying to use a chat model. This way of initializing it is ""no longer supported. Instead, please use: ""`from langchain_community.chat_models import ChatOpenAI`")returnvalues
@propertydef_default_params(self)->Dict[str,Any]:"""Get the default parameters for calling OpenAI API."""returnself.model_kwargsdef_get_chat_params(self,prompts:List[str],stop:Optional[List[str]]=None)->Tuple:iflen(prompts)>1:raiseValueError(f"OpenAIChat currently only supports single prompt, got {prompts}")messages=self.prefix_messages+[{"role":"user","content":prompts[0]}]params:Dict[str,Any]={**{"model":self.model_name},**self._default_params}ifstopisnotNone:if"stop"inparams:raiseValueError("`stop` found in both the input and default params.")params["stop"]=stopifparams.get("max_tokens")==-1:# for ChatGPT api, omitting max_tokens is equivalent to having no limitdelparams["max_tokens"]returnmessages,paramsdef_stream(self,prompt:str,stop:Optional[List[str]]=None,run_manager:Optional[CallbackManagerForLLMRun]=None,**kwargs:Any,)->Iterator[GenerationChunk]:messages,params=self._get_chat_params([prompt],stop)params={**params,**kwargs,"stream":True}forstream_respincompletion_with_retry(self,messages=messages,run_manager=run_manager,**params):ifnotisinstance(stream_resp,dict):stream_resp=stream_resp.dict()token=stream_resp["choices"][0]["delta"].get("content","")chunk=GenerationChunk(text=token)ifrun_manager:run_manager.on_llm_new_token(token,chunk=chunk)yieldchunkasyncdef_astream(self,prompt:str,stop:Optional[List[str]]=None,run_manager:Optional[AsyncCallbackManagerForLLMRun]=None,**kwargs:Any,)->AsyncIterator[GenerationChunk]:messages,params=self._get_chat_params([prompt],stop)params={**params,**kwargs,"stream":True}asyncforstream_respinawaitacompletion_with_retry(self,messages=messages,run_manager=run_manager,**params):ifnotisinstance(stream_resp,dict):stream_resp=stream_resp.dict()token=stream_resp["choices"][0]["delta"].get("content","")chunk=GenerationChunk(text=token)ifrun_manager:awaitrun_manager.on_llm_new_token(token,chunk=chunk)yieldchunkdef_generate(self,prompts:List[str],stop:Optional[List[str]]=None,run_manager:Optional[CallbackManagerForLLMRun]=None,**kwargs:Any,)->LLMResult:ifself.streaming:generation:Optional[GenerationChunk]=Noneforchunkinself._stream(prompts[0],stop,run_manager,**kwargs):ifgenerationisNone:generation=chunkelse:generation+=chunkassertgenerationisnotNonereturnLLMResult(generations=[[generation]])messages,params=self._get_chat_params(prompts,stop)params={**params,**kwargs}full_response=completion_with_retry(self,messages=messages,run_manager=run_manager,**params)ifnotisinstance(full_response,dict):full_response=full_response.dict()llm_output={"token_usage":full_response["usage"],"model_name":self.model_name,}returnLLMResult(generations=[[Generation(text=full_response["choices"][0]["message"]["content"])]],llm_output=llm_output,)asyncdef_agenerate(self,prompts:List[str],stop:Optional[List[str]]=None,run_manager:Optional[AsyncCallbackManagerForLLMRun]=None,**kwargs:Any,)->LLMResult:ifself.streaming:generation:Optional[GenerationChunk]=Noneasyncforchunkinself._astream(prompts[0],stop,run_manager,**kwargs):ifgenerationisNone:generation=chunkelse:generation+=chunkassertgenerationisnotNonereturnLLMResult(generations=[[generation]])messages,params=self._get_chat_params(prompts,stop)params={**params,**kwargs}full_response=awaitacompletion_with_retry(self,messages=messages,run_manager=run_manager,**params)ifnotisinstance(full_response,dict):full_response=full_response.dict()llm_output={"token_usage":full_response["usage"],"model_name":self.model_name,}returnLLMResult(generations=[[Generation(text=full_response["choices"][0]["message"]["content"])]],llm_output=llm_output,)@propertydef_identifying_params(self)->Mapping[str,Any]:"""Get the identifying parameters."""return{**{"model_name":self.model_name},**self._default_params}@propertydef_llm_type(self)->str:"""Return type of llm."""return"openai-chat"
[docs]defget_token_ids(self,text:str)->List[int]:"""Get the token IDs using the tiktoken package."""# tiktoken NOT supported for Python < 3.8ifsys.version_info[1]<8:returnsuper().get_token_ids(text)try:importtiktokenexceptImportError:raiseImportError("Could not import tiktoken python package. ""This is needed in order to calculate get_num_tokens. ""Please install it with `pip install tiktoken`.")enc=tiktoken.encoding_for_model(self.model_name)returnenc.encode(text,allowed_special=self.allowed_special,disallowed_special=self.disallowed_special,)