diff --git a/docs/examples/agents/react.ipynb b/docs/examples/agents/react.ipynb index aef1239030f7e..4af4c6727f183 100644 --- a/docs/examples/agents/react.ipynb +++ b/docs/examples/agents/react.ipynb @@ -12,7 +12,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 6, "id": "4e272b47", "metadata": {}, "outputs": [], @@ -32,13 +32,13 @@ " )\n", "]\n", "\n", - "llm = OpenAI(temperature=0)\n", + "llm = OpenAI(temperature=0, model_name=\"text-davinci-002\")\n", "react = initialize_agent(tools, llm, agent=\"react-docstore\", verbose=True)" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 7, "id": "8078c8f1", "metadata": {}, "outputs": [ @@ -46,9 +46,31 @@ "name": "stdout", "output_type": "stream", "text": [ + "\n", + "\n", + "\u001b[1m> Entering new ReActDocstoreAgent chain...\u001b[0m\n", "Author David Chanoff has collaborated with a U.S. Navy admiral who served as the ambassador to the United Kingdom under which President?\n", - "Thought 1:" + "Thought 1:\u001b[32;1m\u001b[1;3m I need to search David Chanoff and find the U.S. Navy admiral he collaborated\n", + "with.\n", + "Action 1: Search[David Chanoff]\u001b[0m\n", + "Observation 1: \u001b[36;1m\u001b[1;3mDavid Chanoff is a noted author of non-fiction work. His work has typically involved collaborations with the principal protagonist of the work concerned. His collaborators have included; Augustus A. White, Joycelyn Elders, Đoàn Văn Toại, William J. Crowe, Ariel Sharon, Kenneth Good and Felix Zandman. He has also written about a wide range of subjects including literary history, education and foreign for The Washington Post, The New Republic and The New York Times Magazine. He has published more than twelve books.\u001b[0m\n", + "Thought 2:\u001b[32;1m\u001b[1;3m The U.S. Navy admiral David Chanoff collaborated with is William J. Crowe.\n", + "Action 2: Search[William J. Crowe]\u001b[0m\n", + "Observation 2: \u001b[36;1m\u001b[1;3mWilliam James Crowe Jr. (January 2, 1925 – October 18, 2007) was a United States Navy admiral and diplomat who served as the 11th chairman of the Joint Chiefs of Staff under Presidents Ronald Reagan and George H. W. Bush, and as the ambassador to the United Kingdom and Chair of the Intelligence Oversight Board under President Bill Clinton.\u001b[0m\n", + "Thought 3:\u001b[32;1m\u001b[1;3m William J. Crowe served as the ambassador to the United Kingdom under President Bill Clinton.\n", + "Action 3: Finish[Bill Clinton]\u001b[0m\n", + "\u001b[1m> Finished ReActDocstoreAgent chain.\u001b[0m\n" ] + }, + { + "data": { + "text/plain": [ + "'Bill Clinton'" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" } ], "source": [ @@ -81,7 +103,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.6" + "version": "3.10.8" } }, "nbformat": 4, diff --git a/langchain/__init__.py b/langchain/__init__.py index 9f0b4b79a52af..869a2d3f1bf4f 100644 --- a/langchain/__init__.py +++ b/langchain/__init__.py @@ -13,6 +13,7 @@ ) from langchain.docstore import InMemoryDocstore, Wikipedia from langchain.llms import Cohere, HuggingFaceHub, OpenAI +from langchain.logger import BaseLogger from langchain.prompts import ( BasePromptTemplate, FewShotPromptTemplate, @@ -23,6 +24,8 @@ from langchain.sql_database import SQLDatabase from langchain.vectorstores import FAISS, ElasticVectorSearch +logger = BaseLogger() + __all__ = [ "LLMChain", "LLMMathChain", diff --git a/langchain/agents/agent.py b/langchain/agents/agent.py index 80ee3ab1dc471..99ace7a0d09f7 100644 --- a/langchain/agents/agent.py +++ b/langchain/agents/agent.py @@ -2,24 +2,18 @@ from __future__ import annotations from abc import ABC, abstractmethod -from typing import Any, ClassVar, Dict, List, NamedTuple, Optional, Tuple +from typing import Any, ClassVar, Dict, List, Optional, Tuple from pydantic import BaseModel +from langchain.agents.input import ChainedInput from langchain.agents.tools import Tool from langchain.chains.base import Chain from langchain.chains.llm import LLMChain -from langchain.input import ChainedInput, get_color_mapping +from langchain.input import get_color_mapping from langchain.llms.base import LLM from langchain.prompts.base import BasePromptTemplate - - -class Action(NamedTuple): - """Action to take.""" - - tool: str - tool_input: str - log: str +from langchain.schema import AgentAction class Agent(Chain, BaseModel, ABC): @@ -99,7 +93,7 @@ def from_llm_and_tools(cls, llm: LLM, tools: List[Tool], **kwargs: Any) -> Agent llm_chain = LLMChain(llm=llm, prompt=cls.create_prompt(tools)) return cls(llm_chain=llm_chain, tools=tools, **kwargs) - def get_action(self, text: str) -> Action: + def get_action(self, text: str) -> AgentAction: """Given input, decided what to do. Args: @@ -119,7 +113,7 @@ def get_action(self, text: str) -> Action: full_output += output parsed_output = self._extract_tool_and_input(full_output) tool, tool_input = parsed_output - return Action(tool, tool_input, full_output) + return AgentAction(tool, tool_input, full_output) def _call(self, inputs: Dict[str, str]) -> Dict[str, str]: """Run text through and get agent response.""" @@ -145,7 +139,7 @@ def _call(self, inputs: Dict[str, str]) -> Dict[str, str]: # Call the LLM to see what to do. output = self.get_action(chained_input.input) # Add the log to the Chained Input. - chained_input.add(output.log, color="green") + chained_input.add_action(output, color="green") # If the tool chosen is the finishing tool, then we end and return. if output.tool == self.finish_tool_name: return {self.output_key: output.tool_input} @@ -154,8 +148,9 @@ def _call(self, inputs: Dict[str, str]) -> Dict[str, str]: # We then call the tool on the tool input to get an observation observation = chain(output.tool_input) # We then log the observation - chained_input.add(f"\n{self.observation_prefix}") - chained_input.add(observation, color=color_mapping[output.tool]) - # We then add the LLM prefix into the prompt to get the LLM to start - # thinking, and start the loop all over. - chained_input.add(f"\n{self.llm_prefix}") + chained_input.add_observation( + observation, + self.observation_prefix, + self.llm_prefix, + color=color_mapping[output.tool], + ) diff --git a/langchain/agents/input.py b/langchain/agents/input.py new file mode 100644 index 0000000000000..6659ad091c063 --- /dev/null +++ b/langchain/agents/input.py @@ -0,0 +1,44 @@ +"""Input manager for agents.""" +from typing import Optional + +import langchain +from langchain.schema import AgentAction + + +class ChainedInput: + """Class for working with input that is the result of chains.""" + + def __init__(self, text: str, verbose: bool = False): + """Initialize with verbose flag and initial text.""" + self._verbose = verbose + if self._verbose: + langchain.logger.log_agent_start(text) + self._input = text + + def add_action(self, action: AgentAction, color: Optional[str] = None) -> None: + """Add text to input, print if in verbose mode.""" + if self._verbose: + langchain.logger.log_agent_action(action, color=color) + self._input += action.log + + def add_observation( + self, + observation: str, + observation_prefix: str, + llm_prefix: str, + color: Optional[str], + ) -> None: + """Add observation to input, print if in verbose mode.""" + if self._verbose: + langchain.logger.log_agent_observation( + observation, + color=color, + observation_prefix=observation_prefix, + llm_prefix=llm_prefix, + ) + self._input += f"\n{observation_prefix}{observation}\n{llm_prefix}" + + @property + def input(self) -> str: + """Return the accumulated input.""" + return self._input diff --git a/langchain/chains/api/base.py b/langchain/chains/api/base.py index b73c96bb6cf2f..807e86354d587 100644 --- a/langchain/chains/api/base.py +++ b/langchain/chains/api/base.py @@ -80,7 +80,7 @@ def _call(self, inputs: Dict[str, str]) -> Dict[str, str]: print_text(api_url, color="green", end="\n") api_response = self.requests_wrapper.run(api_url) if self.verbose: - print_text(api_url, color="yellow", end="\n") + print_text(api_response, color="yellow", end="\n") answer = self.api_answer_chain.predict( question=question, api_docs=self.api_docs, diff --git a/langchain/chains/llm.py b/langchain/chains/llm.py index 544628524c812..a1575ed450fdf 100644 --- a/langchain/chains/llm.py +++ b/langchain/chains/llm.py @@ -3,8 +3,8 @@ from pydantic import BaseModel, Extra +import langchain from langchain.chains.base import Chain -from langchain.input import print_text from langchain.llms.base import LLM from langchain.prompts.base import BasePromptTemplate @@ -55,12 +55,13 @@ def _call(self, inputs: Dict[str, Any]) -> Dict[str, str]: selected_inputs = {k: inputs[k] for k in self.prompt.input_variables} prompt = self.prompt.format(**selected_inputs) if self.verbose: - print("Prompt after formatting:") - print_text(prompt, color="green", end="\n") + langchain.logger.log_llm_inputs(selected_inputs, prompt) kwargs = {} if "stop" in inputs: kwargs["stop"] = inputs["stop"] response = self.llm(prompt, **kwargs) + if self.verbose: + langchain.logger.log_llm_response(response) return {self.output_key: response} def predict(self, **kwargs: Any) -> str: diff --git a/langchain/input.py b/langchain/input.py index 782cd41a8bff1..680685fff518e 100644 --- a/langchain/input.py +++ b/langchain/input.py @@ -27,25 +27,3 @@ def print_text(text: str, color: Optional[str] = None, end: str = "") -> None: else: color_str = _TEXT_COLOR_MAPPING[color] print(f"\u001b[{color_str}m\033[1;3m{text}\u001b[0m", end=end) - - -class ChainedInput: - """Class for working with input that is the result of chains.""" - - def __init__(self, text: str, verbose: bool = False): - """Initialize with verbose flag and initial text.""" - self._verbose = verbose - if self._verbose: - print_text(text, color=None) - self._input = text - - def add(self, text: str, color: Optional[str] = None) -> None: - """Add text to input, print if in verbose mode.""" - if self._verbose: - print_text(text, color=color) - self._input += text - - @property - def input(self) -> str: - """Return the accumulated input.""" - return self._input diff --git a/langchain/logger.py b/langchain/logger.py new file mode 100644 index 0000000000000..0b9a83383ce25 --- /dev/null +++ b/langchain/logger.py @@ -0,0 +1,56 @@ +from typing import Any, Optional + +from langchain.input import print_text +from langchain.schema import AgentAction + + +class BaseLogger: + def log_agent_start(self, text: str, **kwargs: Any): + pass + + def log_agent_end(self, text: str, **kwargs: Any): + pass + + def log_agent_action(self, action: AgentAction, **kwargs: Any): + pass + + def log_agent_observation(self, observation: str, **kwargs: Any): + pass + + def log_llm_inputs(self, inputs: dict, prompt: str, **kwargs): + pass + + def log_llm_response(self, output: str, **kwargs): + pass + + +class StOutLogger(BaseLogger): + def log_agent_start(self, text: str, **kwargs: Any): + print_text(text) + + def log_agent_end(self, text: str, **kwargs: Any): + pass + + def log_agent_action( + self, action: AgentAction, color: Optional[str] = None, **kwargs: Any + ): + print_text(action.log, color=color) + + def log_llm_inputs(self, inputs: dict, prompt: str, **kwargs): + print("Prompt after formatting:") + print_text(prompt, color="green", end="\n") + + def log_llm_response(self, output: str, **kwargs): + pass + + def log_agent_observation( + self, + observation: str, + color: Optional[str] = None, + observation_prefix: Optional[str] = None, + llm_prefix: Optional[str] = None, + **kwargs: Any, + ): + print_text(f"\n{observation_prefix}") + print_text(observation, color=color) + print_text(f"\n{llm_prefix}") diff --git a/langchain/schema.py b/langchain/schema.py new file mode 100644 index 0000000000000..389839f489c5c --- /dev/null +++ b/langchain/schema.py @@ -0,0 +1,11 @@ +from __future__ import annotations + +from typing import NamedTuple + + +class AgentAction(NamedTuple): + """Agent's action to take.""" + + tool: str + tool_input: str + log: str diff --git a/pyproject.toml b/pyproject.toml index c690a8080ac8a..5083153d21753 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "langchain" -version = "0.0.28" +version = "0.0.29" description = "Building applications with LLMs through composability" authors = [] license = "MIT" @@ -21,7 +21,6 @@ manifest-ml = {version = "^0.0.1", optional = true} spacy = {version = "^3", optional = true} nltk = {version = "^3", optional = true} transformers = {version = "^4", optional = true} -types-toml = "^0.10.8.1" [tool.poetry.group.test.dependencies] pytest = "^7.2.0" @@ -37,6 +36,7 @@ flake8 = "^6.0.0" mypy = "^0.991" types-pyyaml = "^6.0.12.2" types-requests = "^2.28.11.5" +types-toml = "^0.10.8.1" [tool.poetry.group.dev] optional = true diff --git a/tests/unit_tests/test_input.py b/tests/unit_tests/test_input.py index 43dd3b080b889..fb85593dfb358 100644 --- a/tests/unit_tests/test_input.py +++ b/tests/unit_tests/test_input.py @@ -3,7 +3,8 @@ import sys from io import StringIO -from langchain.input import ChainedInput, get_color_mapping +from langchain.agents.input import ChainedInput +from langchain.input import get_color_mapping def test_chained_input_not_verbose() -> None: