| | """Implement an LLM driven browser.""" |
| |
|
| | from __future__ import annotations |
| |
|
| | import warnings |
| | from typing import Any, Dict, List, Optional |
| |
|
| | from langchain_core._api import deprecated |
| | from langchain_core.caches import BaseCache as BaseCache |
| | from langchain_core.callbacks import CallbackManagerForChainRun |
| | from langchain_core.callbacks import Callbacks as Callbacks |
| | from langchain_core.language_models import BaseLanguageModel |
| | from langchain_core.output_parsers import StrOutputParser |
| | from langchain_core.runnables import Runnable |
| | from pydantic import ConfigDict, model_validator |
| |
|
| | from langchain.chains.base import Chain |
| | from langchain.chains.natbot.prompt import PROMPT |
| |
|
| |
|
| | @deprecated( |
| | since="0.2.13", |
| | message=( |
| | "Importing NatBotChain from langchain is deprecated and will be removed in " |
| | "langchain 1.0. Please import from langchain_community instead: " |
| | "from langchain_community.chains.natbot import NatBotChain. " |
| | "You may need to pip install -U langchain-community." |
| | ), |
| | removal="1.0", |
| | ) |
| | class NatBotChain(Chain): |
| | """Implement an LLM driven browser. |
| | |
| | **Security Note**: This toolkit provides code to control a web-browser. |
| | |
| | The web-browser can be used to navigate to: |
| | |
| | - Any URL (including any internal network URLs) |
| | - And local files |
| | |
| | Exercise care if exposing this chain to end-users. Control who is able to |
| | access and use this chain, and isolate the network access of the server |
| | that hosts this chain. |
| | |
| | See https://python.langchain.com/docs/security for more information. |
| | |
| | Example: |
| | .. code-block:: python |
| | |
| | from langchain.chains import NatBotChain |
| | natbot = NatBotChain.from_default("Buy me a new hat.") |
| | """ |
| |
|
| | llm_chain: Runnable |
| | objective: str |
| | """Objective that NatBot is tasked with completing.""" |
| | llm: Optional[BaseLanguageModel] = None |
| | """[Deprecated] LLM wrapper to use.""" |
| | input_url_key: str = "url" |
| | input_browser_content_key: str = "browser_content" |
| | previous_command: str = "" |
| | output_key: str = "command" |
| |
|
| | model_config = ConfigDict( |
| | arbitrary_types_allowed=True, |
| | extra="forbid", |
| | ) |
| |
|
| | @model_validator(mode="before") |
| | @classmethod |
| | def raise_deprecation(cls, values: Dict) -> Any: |
| | if "llm" in values: |
| | warnings.warn( |
| | "Directly instantiating an NatBotChain with an llm is deprecated. " |
| | "Please instantiate with llm_chain argument or using the from_llm " |
| | "class method." |
| | ) |
| | if "llm_chain" not in values and values["llm"] is not None: |
| | values["llm_chain"] = PROMPT | values["llm"] | StrOutputParser() |
| | return values |
| |
|
| | @classmethod |
| | def from_default(cls, objective: str, **kwargs: Any) -> NatBotChain: |
| | """Load with default LLMChain.""" |
| | raise NotImplementedError( |
| | "This method is no longer implemented. Please use from_llm." |
| | "llm = OpenAI(temperature=0.5, best_of=10, n=3, max_tokens=50)" |
| | "For example, NatBotChain.from_llm(llm, objective)" |
| | ) |
| |
|
| | @classmethod |
| | def from_llm( |
| | cls, llm: BaseLanguageModel, objective: str, **kwargs: Any |
| | ) -> NatBotChain: |
| | """Load from LLM.""" |
| | llm_chain = PROMPT | llm | StrOutputParser() |
| | return cls(llm_chain=llm_chain, objective=objective, **kwargs) |
| |
|
| | @property |
| | def input_keys(self) -> List[str]: |
| | """Expect url and browser content. |
| | |
| | :meta private: |
| | """ |
| | return [self.input_url_key, self.input_browser_content_key] |
| |
|
| | @property |
| | def output_keys(self) -> List[str]: |
| | """Return command. |
| | |
| | :meta private: |
| | """ |
| | return [self.output_key] |
| |
|
| | def _call( |
| | self, |
| | inputs: Dict[str, str], |
| | run_manager: Optional[CallbackManagerForChainRun] = None, |
| | ) -> Dict[str, str]: |
| | _run_manager = run_manager or CallbackManagerForChainRun.get_noop_manager() |
| | url = inputs[self.input_url_key] |
| | browser_content = inputs[self.input_browser_content_key] |
| | llm_cmd = self.llm_chain.invoke( |
| | { |
| | "objective": self.objective, |
| | "url": url[:100], |
| | "previous_command": self.previous_command, |
| | "browser_content": browser_content[:4500], |
| | }, |
| | config={"callbacks": _run_manager.get_child()}, |
| | ) |
| | llm_cmd = llm_cmd.strip() |
| | self.previous_command = llm_cmd |
| | return {self.output_key: llm_cmd} |
| |
|
| | def execute(self, url: str, browser_content: str) -> str: |
| | """Figure out next browser command to run. |
| | |
| | Args: |
| | url: URL of the site currently on. |
| | browser_content: Content of the page as currently displayed by the browser. |
| | |
| | Returns: |
| | Next browser command to run. |
| | |
| | Example: |
| | .. code-block:: python |
| | |
| | browser_content = "...." |
| | llm_command = natbot.run("www.google.com", browser_content) |
| | """ |
| | _inputs = { |
| | self.input_url_key: url, |
| | self.input_browser_content_key: browser_content, |
| | } |
| | return self(_inputs)[self.output_key] |
| |
|
| | @property |
| | def _chain_type(self) -> str: |
| | return "nat_bot_chain" |
| |
|
| |
|
| | NatBotChain.model_rebuild() |
| |
|