| """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() |
|
|