Source code for langchain_community.chains.openapi.chain
"""Chain that makes API calls and summarizes the responses to answer a question."""from__future__importannotationsimportjsonfromtypingimportAny,Dict,List,NamedTuple,Optional,castfromlangchain.chains.api.openapi.requests_chainimportAPIRequesterChainfromlangchain.chains.api.openapi.response_chainimportAPIResponderChainfromlangchain.chains.baseimportChainfromlangchain.chains.llmimportLLMChainfromlangchain_core.callbacksimportCallbackManagerForChainRun,Callbacksfromlangchain_core.language_modelsimportBaseLanguageModelfrompydanticimportBaseModel,FieldfromrequestsimportResponsefromlangchain_community.tools.openapi.utils.api_modelsimportAPIOperationfromlangchain_community.utilities.requestsimportRequestsclass_ParamMapping(NamedTuple):"""Mapping from parameter name to parameter value."""query_params:List[str]body_params:List[str]path_params:List[str]
[docs]classOpenAPIEndpointChain(Chain,BaseModel):"""Chain interacts with an OpenAPI endpoint using natural language."""api_request_chain:LLMChainapi_response_chain:Optional[LLMChain]=Noneapi_operation:APIOperationrequests:Requests=Field(exclude=True,default_factory=Requests)param_mapping:_ParamMapping=Field(alias="param_mapping")return_intermediate_steps:bool=Falseinstructions_key:str="instructions"#: :meta private:output_key:str="output"#: :meta private:max_text_length:Optional[int]=Field(ge=0)#: :meta private:@propertydefinput_keys(self)->List[str]:"""Expect input key. :meta private: """return[self.instructions_key]@propertydefoutput_keys(self)->List[str]:"""Expect output key. :meta private: """ifnotself.return_intermediate_steps:return[self.output_key]else:return[self.output_key,"intermediate_steps"]def_construct_path(self,args:Dict[str,str])->str:"""Construct the path from the deserialized input."""path=self.api_operation.base_url+self.api_operation.pathforparaminself.param_mapping.path_params:path=path.replace(f"{{{param}}}",str(args.pop(param,"")))returnpathdef_extract_query_params(self,args:Dict[str,str])->Dict[str,str]:"""Extract the query params from the deserialized input."""query_params={}forparaminself.param_mapping.query_params:ifparaminargs:query_params[param]=args.pop(param)returnquery_paramsdef_extract_body_params(self,args:Dict[str,str])->Optional[Dict[str,str]]:"""Extract the request body params from the deserialized input."""body_params=Noneifself.param_mapping.body_params:body_params={}forparaminself.param_mapping.body_params:ifparaminargs:body_params[param]=args.pop(param)returnbody_params
[docs]defdeserialize_json_input(self,serialized_args:str)->dict:"""Use the serialized typescript dictionary. Resolve the path, query params dict, and optional requestBody dict. """args:dict=json.loads(serialized_args)path=self._construct_path(args)body_params=self._extract_body_params(args)query_params=self._extract_query_params(args)return{"url":path,"data":body_params,"params":query_params,}
def_get_output(self,output:str,intermediate_steps:dict)->dict:"""Return the output from the API call."""ifself.return_intermediate_steps:return{self.output_key:output,"intermediate_steps":intermediate_steps,}else:return{self.output_key:output}def_call(self,inputs:Dict[str,Any],run_manager:Optional[CallbackManagerForChainRun]=None,)->Dict[str,str]:_run_manager=run_managerorCallbackManagerForChainRun.get_noop_manager()intermediate_steps={}instructions=inputs[self.instructions_key]instructions=instructions[:self.max_text_length]_api_arguments=self.api_request_chain.predict_and_parse(instructions=instructions,callbacks=_run_manager.get_child())api_arguments=cast(str,_api_arguments)intermediate_steps["request_args"]=api_arguments_run_manager.on_text(api_arguments,color="green",end="\n",verbose=self.verbose)ifapi_arguments.startswith("ERROR"):returnself._get_output(api_arguments,intermediate_steps)elifapi_arguments.startswith("MESSAGE:"):returnself._get_output(api_arguments[len("MESSAGE:"):],intermediate_steps)try:request_args=self.deserialize_json_input(api_arguments)method=getattr(self.requests,self.api_operation.method.value)api_response:Response=method(**request_args)ifapi_response.status_code!=200:method_str=str(self.api_operation.method.value)response_text=(f"{api_response.status_code}: {api_response.reason}"+f"\nFor {method_str.upper()}{request_args['url']}\n"+f"Called with args: {request_args['params']}")else:response_text=api_response.textexceptExceptionase:response_text=f"Error with message {str(e)}"response_text=response_text[:self.max_text_length]intermediate_steps["response_text"]=response_text_run_manager.on_text(response_text,color="blue",end="\n",verbose=self.verbose)ifself.api_response_chainisnotNone:_answer=self.api_response_chain.predict_and_parse(response=response_text,instructions=instructions,callbacks=_run_manager.get_child(),)answer=cast(str,_answer)_run_manager.on_text(answer,color="yellow",end="\n",verbose=self.verbose)returnself._get_output(answer,intermediate_steps)else:returnself._get_output(response_text,intermediate_steps)
[docs]@classmethoddeffrom_url_and_method(cls,spec_url:str,path:str,method:str,llm:BaseLanguageModel,requests:Optional[Requests]=None,return_intermediate_steps:bool=False,**kwargs:Any,# TODO: Handle async)->"OpenAPIEndpointChain":"""Create an OpenAPIEndpoint from a spec at the specified url."""operation=APIOperation.from_openapi_url(spec_url,path,method)returncls.from_api_operation(operation,requests=requests,llm=llm,return_intermediate_steps=return_intermediate_steps,**kwargs,)
[docs]@classmethoddeffrom_api_operation(cls,operation:APIOperation,llm:BaseLanguageModel,requests:Optional[Requests]=None,verbose:bool=False,return_intermediate_steps:bool=False,raw_response:bool=False,callbacks:Callbacks=None,**kwargs:Any,# TODO: Handle async)->"OpenAPIEndpointChain":"""Create an OpenAPIEndpointChain from an operation and a spec."""param_mapping=_ParamMapping(query_params=operation.query_params,body_params=operation.body_params,path_params=operation.path_params,)requests_chain=APIRequesterChain.from_llm_and_typescript(llm,typescript_definition=operation.to_typescript(),verbose=verbose,callbacks=callbacks,)ifraw_response:response_chain=Noneelse:response_chain=APIResponderChain.from_llm(llm,verbose=verbose,callbacks=callbacks)_requests=requestsorRequests()returncls(api_request_chain=requests_chain,api_response_chain=response_chain,api_operation=operation,requests=_requests,param_mapping=param_mapping,verbose=verbose,return_intermediate_steps=return_intermediate_steps,callbacks=callbacks,**kwargs,)