"""Chain that interprets a prompt and executes python code to do math."""from__future__importannotationsimportmathimportreimportwarningsfromtypingimportAny,Dict,List,Optionalfromlangchain_core._apiimportdeprecatedfromlangchain_core.callbacksimport(AsyncCallbackManagerForChainRun,CallbackManagerForChainRun,)fromlangchain_core.language_modelsimportBaseLanguageModelfromlangchain_core.promptsimportBasePromptTemplatefrompydanticimportConfigDict,model_validatorfromlangchain.chains.baseimportChainfromlangchain.chains.llmimportLLMChainfromlangchain.chains.llm_math.promptimportPROMPT
[docs]@deprecated(since="0.2.13",message=("This class is deprecated and will be removed in langchain 1.0. ""See API reference for replacement: ""https://api.python.langchain.com/en/latest/chains/langchain.chains.llm_math.base.LLMMathChain.html"# noqa: E501),removal="1.0",)classLLMMathChain(Chain):"""Chain that interprets a prompt and executes python code to do math. Note: this class is deprecated. See below for a replacement implementation using LangGraph. The benefits of this implementation are: - Uses LLM tool calling features; - Support for both token-by-token and step-by-step streaming; - Support for checkpointing and memory of chat history; - Easier to modify or extend (e.g., with additional tools, structured responses, etc.) Install LangGraph with: .. code-block:: bash pip install -U langgraph .. code-block:: python import math from typing import Annotated, Sequence from langchain_core.messages import BaseMessage from langchain_core.runnables import RunnableConfig from langchain_core.tools import tool from langchain_openai import ChatOpenAI from langgraph.graph import END, StateGraph from langgraph.graph.message import add_messages from langgraph.prebuilt.tool_node import ToolNode import numexpr from typing_extensions import TypedDict @tool def calculator(expression: str) -> str: \"\"\"Calculate expression using Python's numexpr library. Expression should be a single line mathematical expression that solves the problem. Examples: "37593 * 67" for "37593 times 67" "37593**(1/5)" for "37593^(1/5)" \"\"\" local_dict = {"pi": math.pi, "e": math.e} return str( numexpr.evaluate( expression.strip(), global_dict={}, # restrict access to globals local_dict=local_dict, # add common mathematical functions ) ) llm = ChatOpenAI(model="gpt-4o-mini", temperature=0) tools = [calculator] llm_with_tools = llm.bind_tools(tools, tool_choice="any") class ChainState(TypedDict): \"\"\"LangGraph state.\"\"\" messages: Annotated[Sequence[BaseMessage], add_messages] async def acall_chain(state: ChainState, config: RunnableConfig): last_message = state["messages"][-1] response = await llm_with_tools.ainvoke(state["messages"], config) return {"messages": [response]} async def acall_model(state: ChainState, config: RunnableConfig): response = await llm.ainvoke(state["messages"], config) return {"messages": [response]} graph_builder = StateGraph(ChainState) graph_builder.add_node("call_tool", acall_chain) graph_builder.add_node("execute_tool", ToolNode(tools)) graph_builder.add_node("call_model", acall_model) graph_builder.set_entry_point("call_tool") graph_builder.add_edge("call_tool", "execute_tool") graph_builder.add_edge("execute_tool", "call_model") graph_builder.add_edge("call_model", END) chain = graph_builder.compile() .. code-block:: python example_query = "What is 551368 divided by 82" events = chain.astream( {"messages": [("user", example_query)]}, stream_mode="values", ) async for event in events: event["messages"][-1].pretty_print() .. code-block:: none ================================ Human Message ================================= What is 551368 divided by 82 ================================== Ai Message ================================== Tool Calls: calculator (call_MEiGXuJjJ7wGU4aOT86QuGJS) Call ID: call_MEiGXuJjJ7wGU4aOT86QuGJS Args: expression: 551368 / 82 ================================= Tool Message ================================= Name: calculator 6724.0 ================================== Ai Message ================================== 551368 divided by 82 equals 6724. Example: .. code-block:: python from langchain.chains import LLMMathChain from langchain_community.llms import OpenAI llm_math = LLMMathChain.from_llm(OpenAI()) """# noqa: E501llm_chain:LLMChainllm:Optional[BaseLanguageModel]=None"""[Deprecated] LLM wrapper to use."""prompt:BasePromptTemplate=PROMPT"""[Deprecated] Prompt to use to translate to python if necessary."""input_key:str="question"#: :meta private:output_key:str="answer"#: :meta private:model_config=ConfigDict(arbitrary_types_allowed=True,extra="forbid",)@model_validator(mode="before")@classmethoddefraise_deprecation(cls,values:Dict)->Any:try:importnumexpr# noqa: F401exceptImportError:raiseImportError("LLMMathChain requires the numexpr package. ""Please install it with `pip install numexpr`.")if"llm"invalues:warnings.warn("Directly instantiating an LLMMathChain with an llm is deprecated. ""Please instantiate with llm_chain argument or using the from_llm ""class method.")if"llm_chain"notinvaluesandvalues["llm"]isnotNone:prompt=values.get("prompt",PROMPT)values["llm_chain"]=LLMChain(llm=values["llm"],prompt=prompt)returnvalues@propertydefinput_keys(self)->List[str]:"""Expect input key. :meta private: """return[self.input_key]@propertydefoutput_keys(self)->List[str]:"""Expect output key. :meta private: """return[self.output_key]def_evaluate_expression(self,expression:str)->str:importnumexprtry:local_dict={"pi":math.pi,"e":math.e}output=str(numexpr.evaluate(expression.strip(),global_dict={},# restrict access to globalslocal_dict=local_dict,# add common mathematical functions))exceptExceptionase:raiseValueError(f'LLMMathChain._evaluate("{expression}") raised error: {e}.'" Please try again with a valid numerical expression")# Remove any leading and trailing brackets from the outputreturnre.sub(r"^\[|\]$","",output)def_process_llm_result(self,llm_output:str,run_manager:CallbackManagerForChainRun)->Dict[str,str]:run_manager.on_text(llm_output,color="green",verbose=self.verbose)llm_output=llm_output.strip()text_match=re.search(r"^```text(.*?)```",llm_output,re.DOTALL)iftext_match:expression=text_match.group(1)output=self._evaluate_expression(expression)run_manager.on_text("\nAnswer: ",verbose=self.verbose)run_manager.on_text(output,color="yellow",verbose=self.verbose)answer="Answer: "+outputelifllm_output.startswith("Answer:"):answer=llm_outputelif"Answer:"inllm_output:answer="Answer: "+llm_output.split("Answer:")[-1]else:raiseValueError(f"unknown format from LLM: {llm_output}")return{self.output_key:answer}asyncdef_aprocess_llm_result(self,llm_output:str,run_manager:AsyncCallbackManagerForChainRun,)->Dict[str,str]:awaitrun_manager.on_text(llm_output,color="green",verbose=self.verbose)llm_output=llm_output.strip()text_match=re.search(r"^```text(.*?)```",llm_output,re.DOTALL)iftext_match:expression=text_match.group(1)output=self._evaluate_expression(expression)awaitrun_manager.on_text("\nAnswer: ",verbose=self.verbose)awaitrun_manager.on_text(output,color="yellow",verbose=self.verbose)answer="Answer: "+outputelifllm_output.startswith("Answer:"):answer=llm_outputelif"Answer:"inllm_output:answer="Answer: "+llm_output.split("Answer:")[-1]else:raiseValueError(f"unknown format from LLM: {llm_output}")return{self.output_key:answer}def_call(self,inputs:Dict[str,str],run_manager:Optional[CallbackManagerForChainRun]=None,)->Dict[str,str]:_run_manager=run_managerorCallbackManagerForChainRun.get_noop_manager()_run_manager.on_text(inputs[self.input_key])llm_output=self.llm_chain.predict(question=inputs[self.input_key],stop=["```output"],callbacks=_run_manager.get_child(),)returnself._process_llm_result(llm_output,_run_manager)asyncdef_acall(self,inputs:Dict[str,str],run_manager:Optional[AsyncCallbackManagerForChainRun]=None,)->Dict[str,str]:_run_manager=run_managerorAsyncCallbackManagerForChainRun.get_noop_manager()await_run_manager.on_text(inputs[self.input_key])llm_output=awaitself.llm_chain.apredict(question=inputs[self.input_key],stop=["```output"],callbacks=_run_manager.get_child(),)returnawaitself._aprocess_llm_result(llm_output,_run_manager)@propertydef_chain_type(self)->str:return"llm_math_chain"