self.chain_colors = get_color_mapping(chain_range)
        self.names = names
[docs]    @classmethod
    def from_llms(
        cls, llms: List[BaseLLM], prompt: Optional[PromptTemplate] = None
    ) -> ModelLaboratory:
        """Initialize with LLMs to experiment with and optional prompt.
        Args:
            llms: list of LLMs to experiment with
            prompt: Optional prompt to use to prompt the LLMs. Defaults to None.
                If a prompt was provided, it should only have one input variable.
        """
        if prompt is None:
            prompt = PromptTemplate(input_variables=["_input"], template="{_input}")
        chains = [LLMChain(llm=llm, prompt=prompt) for llm in llms]
        names = [str(llm) for llm in llms]
        return cls(chains, names=names)
[docs]    def compare(self, text: str) -> None:
        """Compare model outputs on an input text.
        If a prompt was provided with starting the laboratory, then this text will be
        fed into the prompt. If no prompt was provided, then the input text is the
        entire prompt.
        Args:
            text: input text to run all models on.
        """
        print(f"\033[1mInput:\033[0m\n{text}\n")
        for i, chain in enumerate(self.chains):
            if self.names is not None:
                name = self.names[i]
            else:
                name = str(chain)
            print_text(name, end="\n")
            output = chain.run(text)
            print_text(output, color=self.chain_colors[str(i)], end="\n\n")


Source code for langchain.hub
"""Interface with the LangChain Hub."""
from __future__ import annotations
from typing import TYPE_CHECKING, Any, Optional
from langchain.load.dump import dumps
from langchain.load.load import loads
if TYPE_CHECKING:
    from langchainhub import Client
def _get_client(api_url: Optional[str] = None, api_key: Optional[str] = None) -> Client:
    try:
        from langchainhub import Client
    except ImportError as e:
        raise ImportError(
            "Could not import langchainhub, please install with `pip install "
            "langchainhub`."
        ) from e
    # Client logic will also attempt to load URL/key from environment variables
    return Client(api_url, api_key=api_key)
[docs]def push(
    repo_full_name: str,
    object: Any,
    *,
    api_url: Optional[str] = None,
    api_key: Optional[str] = None,
    parent_commit_hash: Optional[str] = "latest",
    new_repo_is_public: bool = True,
    new_repo_description: str = "",
) -> str:
    """
    Pushes an object to the hub and returns the URL it can be viewed at in a browser.
    :param repo_full_name: The full name of the repo to push to in the format of
        `owner/repo`.
    :param object: The LangChain to serialize and push to the hub.
    :param api_url: The URL of the LangChain Hub API. Defaults to the hosted API service
        if you have an api key set, or a localhost instance if not.
    :param api_key: The API key to use to authenticate with the LangChain Hub API.


:param parent_commit_hash: The commit hash of the parent commit to push to. Defaults
        to the latest commit automatically.
    :param new_repo_is_public: Whether the repo should be public. Defaults to
        True (Public by default).
    :param new_repo_description: The description of the repo. Defaults to an empty
        string.
    """
    client = _get_client(api_url=api_url, api_key=api_key)
    manifest_json = dumps(object)
    message = client.push(
        repo_full_name,
        manifest_json,
        parent_commit_hash=parent_commit_hash,
        new_repo_is_public=new_repo_is_public,
        new_repo_description=new_repo_description,
    )
    return message
[docs]def pull(
    owner_repo_commit: str,
    *,
    api_url: Optional[str] = None,
    api_key: Optional[str] = None,
) -> Any:
    """
    Pulls an object from the hub and returns it as a LangChain object.
    :param owner_repo_commit: The full name of the repo to pull from in the format of
        `owner/repo:commit_hash`.
    :param api_url: The URL of the LangChain Hub API. Defaults to the hosted API service
        if you have an api key set, or a localhost instance if not.
    :param api_key: The API key to use to authenticate with the LangChain Hub API.
    """
    client = _get_client(api_url=api_url, api_key=api_key)
    resp: str = client.pull(owner_repo_commit)
    return loads(resp)


Source code for langchain.utilities.portkey
import json
import os
from typing import Dict, Optional
[docs]class Portkey:
    """Portkey configuration.
    Attributes:
        base: The base URL for the Portkey API.
          Default: "https://api.portkey.ai/v1/proxy"
    """
    base = "https://api.portkey.ai/v1/proxy"
[docs]    @staticmethod
    def Config(
        api_key: str,
        trace_id: Optional[str] = None,
        environment: Optional[str] = None,
        user: Optional[str] = None,
        organisation: Optional[str] = None,
        prompt: Optional[str] = None,
        retry_count: Optional[int] = None,
        cache: Optional[str] = None,
        cache_force_refresh: Optional[str] = None,
        cache_age: Optional[int] = None,
    ) -> Dict[str, str]:
        assert retry_count is None or retry_count in range(
            1, 6
        ), "retry_count must be an integer and in range [1, 2, 3, 4, 5]"
        assert cache is None or cache in [
            "simple",
            "semantic",
        ], "cache must be 'simple' or 'semantic'"
        assert cache_force_refresh is None or (
            isinstance(cache_force_refresh, str)
            and cache_force_refresh in ["True", "False"]
        ), "cache_force_refresh must be 'True' or 'False'"
        assert cache_age is None or isinstance(
            cache_age, int
        ), "cache_age must be an integer"
        os.environ["OPENAI_API_BASE"] = Portkey.base
        headers = {
            "x-portkey-api-key": api_key,


headers = {
            "x-portkey-api-key": api_key,
            "x-portkey-mode": "proxy openai",
        }
        if trace_id:
            headers["x-portkey-trace-id"] = trace_id
        if retry_count:
            headers["x-portkey-retry-count"] = str(retry_count)
        if cache:
            headers["x-portkey-cache"] = cache
        if cache_force_refresh:
            headers["x-portkey-cache-force-refresh"] = cache_force_refresh
        if cache_age:
            headers["Cache-Control"] = f"max-age:{str(cache_age)}"
        metadata = {}
        if environment:
            metadata["_environment"] = environment
        if user:
            metadata["_user"] = user
        if organisation:
            metadata["_organisation"] = organisation
        if prompt:
            metadata["_prompt"] = prompt
        if metadata:
            headers.update({"x-portkey-metadata": json.dumps(metadata)})
        return headers


Source code for langchain.utilities.google_scholar
"""Util that calls Google Scholar Search."""
from typing import Dict, Optional
from langchain.pydantic_v1 import BaseModel, Extra, root_validator
from langchain.utils import get_from_dict_or_env
[docs]class GoogleScholarAPIWrapper(BaseModel):
    """Wrapper for Google Scholar API
    You can create serpapi key by signing up at: https://serpapi.com/users/sign_up.
    The wrapper uses the serpapi python package:
    https://serpapi.com/integrations/python#search-google-scholar
    To use, you should have the environment variable ``SERP_API_KEY``
    set with your API key, or pass `serp_api_key` as a named parameter
    to the constructor.
    Attributes:
        top_k_results: number of results to return from google-scholar query search.
            By default it returns top 10 results.
        hl: attribute defines the language to use for the Google Scholar search.
            It's a two-letter language code.
            (e.g., en for English, es for Spanish, or fr for French). Head to the
            Google languages page for a full list of supported Google languages:
            https://serpapi.com/google-languages
        lr: attribute defines one or multiple languages to limit the search to.
            It uses lang_{two-letter language code} to specify languages
            and | as a delimiter. (e.g., lang_fr|lang_de will only search French
            and German pages). Head to the Google lr languages for a full
            list of supported languages: https://serpapi.com/google-lr-languages
     Example:
        .. code-block:: python
        from langchain.utilities import GoogleScholarAPIWrapper
        google_scholar = GoogleScholarAPIWrapper()


google_scholar = GoogleScholarAPIWrapper()
        google_scholar.run('langchain')
    """
    top_k_results: int = 10
    hl: str = "en"
    lr: str = "lang_en"
    serp_api_key: Optional[str] = None
    class Config:
        """Configuration for this pydantic object."""
        extra = Extra.forbid
    @root_validator()
    def validate_environment(cls, values: Dict) -> Dict:
        """Validate that api key and python package exists in environment."""
        serp_api_key = get_from_dict_or_env(values, "serp_api_key", "SERP_API_KEY")
        values["SERP_API_KEY"] = serp_api_key
        try:
            from serpapi import GoogleScholarSearch
        except ImportError:
            raise ImportError(
                "google-search-results is not installed. "
                "Please install it with `pip install google-search-results"
                ">=2.4.2`"
            )
        GoogleScholarSearch.SERP_API_KEY = serp_api_key
        values["google_scholar_engine"] = GoogleScholarSearch
        return values
[docs]    def run(self, query: str) -> str:
        """Run query through GoogleSearchScholar and parse result"""
        total_results = []
        page = 0
        while page < max((self.top_k_results - 20), 1):
            # We are getting 20 results from every page
            # which is the max in order to reduce the number of API CALLS.
            # 0 is the first page of results, 20 is the 2nd page of results,
            # 40 is the 3rd page of results, etc.
            results = (


results = (
                self.google_scholar_engine(  # type: ignore
                    {
                        "q": query,
                        "start": page,
                        "hl": self.hl,
                        "num": min(
                            self.top_k_results, 20
                        ),  # if top_k_result is less than 20.
                        "lr": self.lr,
                    }
                )
                .get_dict()
                .get("organic_results", [])
            )
            total_results.extend(results)
            if not results:  # No need to search for more pages if current page
                # has returned no results
                break
            page += 20
        if (
            self.top_k_results % 20 != 0 and page > 20 and total_results
        ):  # From the last page we would only need top_k_results%20 results
            # if k is not divisible by 20.
            results = (
                self.google_scholar_engine(  # type: ignore
                    {
                        "q": query,
                        "start": page,
                        "num": self.top_k_results % 20,
                        "hl": self.hl,
                        "lr": self.lr,
                    }
                )
                .get_dict()
                .get("organic_results", [])
            )
            total_results.extend(results)
        if not total_results:
            return "No good Google Scholar Result was found"
        docs = [
            f"Title: {result.get('title','')}\n"
            f"Authors: {','.join([author.get('name') for author in result.get('publication_info',{}).get('authors',[])])}\n"  # noqa: E501


f"Summary: {result.get('publication_info',{}).get('summary','')}\n"
            f"Total-Citations: {result.get('inline_links',{}).get('cited_by',{}).get('total','')}"  # noqa: E501
            for result in total_results
        ]
        return "\n\n".join(docs)


Source code for langchain.utilities.spark_sql
from __future__ import annotations
from typing import TYPE_CHECKING, Any, Iterable, List, Optional
if TYPE_CHECKING:
    from pyspark.sql import DataFrame, Row, SparkSession
[docs]class SparkSQL:
    """SparkSQL is a utility class for interacting with Spark SQL."""
[docs]    def __init__(
        self,
        spark_session: Optional[SparkSession] = None,
        catalog: Optional[str] = None,
        schema: Optional[str] = None,
        ignore_tables: Optional[List[str]] = None,
        include_tables: Optional[List[str]] = None,
        sample_rows_in_table_info: int = 3,
    ):
        """Initialize a SparkSQL object.
        Args:
            spark_session: A SparkSession object.
              If not provided, one will be created.
            catalog: The catalog to use.
              If not provided, the default catalog will be used.
            schema: The schema to use.
              If not provided, the default schema will be used.
            ignore_tables: A list of tables to ignore.
              If not provided, all tables will be used.
            include_tables: A list of tables to include.
              If not provided, all tables will be used.
            sample_rows_in_table_info: The number of rows to include in the table info.
              Defaults to 3.
        """
        try:
            from pyspark.sql import SparkSession
        except ImportError:
            raise ImportError(
                "pyspark is not installed. Please install it with `pip install pyspark`"
            )
        self._spark = (
            spark_session if spark_session else SparkSession.builder.getOrCreate()
        )
        if catalog is not None:


)
        if catalog is not None:
            self._spark.catalog.setCurrentCatalog(catalog)
        if schema is not None:
            self._spark.catalog.setCurrentDatabase(schema)
        self._all_tables = set(self._get_all_table_names())
        self._include_tables = set(include_tables) if include_tables else set()
        if self._include_tables:
            missing_tables = self._include_tables - self._all_tables
            if missing_tables:
                raise ValueError(
                    f"include_tables {missing_tables} not found in database"
                )
        self._ignore_tables = set(ignore_tables) if ignore_tables else set()
        if self._ignore_tables:
            missing_tables = self._ignore_tables - self._all_tables
            if missing_tables:
                raise ValueError(
                    f"ignore_tables {missing_tables} not found in database"
                )
        usable_tables = self.get_usable_table_names()
        self._usable_tables = set(usable_tables) if usable_tables else self._all_tables
        if not isinstance(sample_rows_in_table_info, int):
            raise TypeError("sample_rows_in_table_info must be an integer")
        self._sample_rows_in_table_info = sample_rows_in_table_info
[docs]    @classmethod
    def from_uri(
        cls, database_uri: str, engine_args: Optional[dict] = None, **kwargs: Any
    ) -> SparkSQL:
        """Creating a remote Spark Session via Spark connect.
        For example: SparkSQL.from_uri("sc://localhost:15002")
        """
        try:
            from pyspark.sql import SparkSession
        except ImportError:
            raise ValueError(
                "pyspark is not installed. Please install it with `pip install pyspark`"
            )


)
        spark = SparkSession.builder.remote(database_uri).getOrCreate()
        return cls(spark, **kwargs)
[docs]    def get_usable_table_names(self) -> Iterable[str]:
        """Get names of tables available."""
        if self._include_tables:
            return self._include_tables
        # sorting the result can help LLM understanding it.
        return sorted(self._all_tables - self._ignore_tables)
    def _get_all_table_names(self) -> Iterable[str]:
        rows = self._spark.sql("SHOW TABLES").select("tableName").collect()
        return list(map(lambda row: row.tableName, rows))
    def _get_create_table_stmt(self, table: str) -> str:
        statement = (
            self._spark.sql(f"SHOW CREATE TABLE {table}").collect()[0].createtab_stmt
        )
        # Ignore the data source provider and options to reduce the number of tokens.
        using_clause_index = statement.find("USING")
        return statement[:using_clause_index] + ";"
[docs]    def get_table_info(self, table_names: Optional[List[str]] = None) -> str:
        all_table_names = self.get_usable_table_names()
        if table_names is not None:
            missing_tables = set(table_names).difference(all_table_names)
            if missing_tables:
                raise ValueError(f"table_names {missing_tables} not found in database")
            all_table_names = table_names
        tables = []
        for table_name in all_table_names:
            table_info = self._get_create_table_stmt(table_name)
            if self._sample_rows_in_table_info:
                table_info += "\n\n/*"
                table_info += f"\n{self._get_sample_spark_rows(table_name)}\n"


table_info += "*/"
            tables.append(table_info)
        final_str = "\n\n".join(tables)
        return final_str
    def _get_sample_spark_rows(self, table: str) -> str:
        query = f"SELECT * FROM {table} LIMIT {self._sample_rows_in_table_info}"
        df = self._spark.sql(query)
        columns_str = "\t".join(list(map(lambda f: f.name, df.schema.fields)))
        try:
            sample_rows = self._get_dataframe_results(df)
            # save the sample rows in string format
            sample_rows_str = "\n".join(["\t".join(row) for row in sample_rows])
        except Exception:
            sample_rows_str = ""
        return (
            f"{self._sample_rows_in_table_info} rows from {table} table:\n"
            f"{columns_str}\n"
            f"{sample_rows_str}"
        )
    def _convert_row_as_tuple(self, row: Row) -> tuple:
        return tuple(map(str, row.asDict().values()))
    def _get_dataframe_results(self, df: DataFrame) -> list:
        return list(map(self._convert_row_as_tuple, df.collect()))
[docs]    def run(self, command: str, fetch: str = "all") -> str:
        df = self._spark.sql(command)
        if fetch == "one":
            df = df.limit(1)
        return str(self._get_dataframe_results(df))
[docs]    def get_table_info_no_throw(self, table_names: Optional[List[str]] = None) -> str:
        """Get information about specified tables.
        Follows best practices as specified in: Rajkumar et al, 2022


Follows best practices as specified in: Rajkumar et al, 2022
        (https://arxiv.org/abs/2204.00498)
        If `sample_rows_in_table_info`, the specified number of sample rows will be
        appended to each table description. This can increase performance as
        demonstrated in the paper.
        """
        try:
            return self.get_table_info(table_names)
        except ValueError as e:
            """Format the error message"""
            return f"Error: {e}"
[docs]    def run_no_throw(self, command: str, fetch: str = "all") -> str:
        """Execute a SQL command and return a string representing the results.
        If the statement returns rows, a string of the results is returned.
        If the statement returns no rows, an empty string is returned.
        If the statement throws an error, the error message is returned.
        """
        try:
            return self.run(command, fetch)
        except Exception as e:
            """Format the error message"""
            return f"Error: {e}"


Source code for langchain.utilities.bibtex
"""Util that calls bibtexparser."""
import logging
from typing import Any, Dict, List, Mapping
from langchain.pydantic_v1 import BaseModel, Extra, root_validator
logger = logging.getLogger(__name__)
OPTIONAL_FIELDS = [
    "annotate",
    "booktitle",
    "editor",
    "howpublished",
    "journal",
    "keywords",
    "note",
    "organization",
    "publisher",
    "school",
    "series",
    "type",
    "doi",
    "issn",
    "isbn",
]
[docs]class BibtexparserWrapper(BaseModel):
    """Wrapper around bibtexparser.
    To use, you should have the ``bibtexparser`` python package installed.
    https://bibtexparser.readthedocs.io/en/master/
    This wrapper will use bibtexparser to load a collection of references from
    a bibtex file and fetch document summaries.
    """
    class Config:
        """Configuration for this pydantic object."""
        extra = Extra.forbid
    @root_validator()
    def validate_environment(cls, values: Dict) -> Dict:
        """Validate that the python package exists in environment."""
        try:
            import bibtexparser  # noqa
        except ImportError:
            raise ImportError(
                "Could not import bibtexparser python package. "
                "Please install it with `pip install bibtexparser`."
            )
        return values
[docs]    def load_bibtex_entries(self, path: str) -> List[Dict[str, Any]]:
        """Load bibtex entries from the bibtex file at the given path."""
        import bibtexparser


import bibtexparser
        with open(path) as file:
            entries = bibtexparser.load(file).entries
        return entries
[docs]    def get_metadata(
        self, entry: Mapping[str, Any], load_extra: bool = False
    ) -> Dict[str, Any]:
        """Get metadata for the given entry."""
        publication = entry.get("journal") or entry.get("booktitle")
        if "url" in entry:
            url = entry["url"]
        elif "doi" in entry:
            url = f'https://doi.org/{entry["doi"]}'
        else:
            url = None
        meta = {
            "id": entry.get("ID"),
            "published_year": entry.get("year"),
            "title": entry.get("title"),
            "publication": publication,
            "authors": entry.get("author"),
            "abstract": entry.get("abstract"),
            "url": url,
        }
        if load_extra:
            for field in OPTIONAL_FIELDS:
                meta[field] = entry.get(field)
        return {k: v for k, v in meta.items() if v is not None}


Source code for langchain.utilities.gitlab
"""Util that calls gitlab."""
from __future__ import annotations
import json
from typing import TYPE_CHECKING, Any, Dict, List, Optional
from langchain.pydantic_v1 import BaseModel, Extra, root_validator
from langchain.utils import get_from_dict_or_env
if TYPE_CHECKING:
    from gitlab.v4.objects import Issue
[docs]class GitLabAPIWrapper(BaseModel):
    """Wrapper for GitLab API."""
    gitlab: Any  #: :meta private:
    gitlab_repo_instance: Any  #: :meta private:
    gitlab_repository: Optional[str] = None
    """The name of the GitLab repository, in the form {username}/{repo-name}."""
    gitlab_personal_access_token: Optional[str] = None
    """Personal access token for the GitLab service, used for authentication."""
    gitlab_branch: Optional[str] = None
    """The specific branch in the GitLab repository where the bot will make
        its commits. Defaults to 'main'.
    """
    gitlab_base_branch: Optional[str] = None
    """The base branch in the GitLab repository, used for comparisons.
        Usually 'main' or 'master'. Defaults to 'main'.
    """
    class Config:
        """Configuration for this pydantic object."""
        extra = Extra.forbid
    @root_validator()
    def validate_environment(cls, values: Dict) -> Dict:
        """Validate that api key and python package exists in environment."""
        gitlab_repository = get_from_dict_or_env(
            values, "gitlab_repository", "GITLAB_REPOSITORY"
        )
        gitlab_personal_access_token = get_from_dict_or_env(
            values, "gitlab_personal_access_token", "GITLAB_PERSONAL_ACCESS_TOKEN"


values, "gitlab_personal_access_token", "GITLAB_PERSONAL_ACCESS_TOKEN"
        )
        gitlab_branch = get_from_dict_or_env(
            values, "gitlab_branch", "GITLAB_BRANCH", default="main"
        )
        gitlab_base_branch = get_from_dict_or_env(
            values, "gitlab_base_branch", "GITLAB_BASE_BRANCH", default="main"
        )
        try:
            import gitlab
        except ImportError:
            raise ImportError(
                "python-gitlab is not installed. "
                "Please install it with `pip install python-gitlab`"
            )
        g = gitlab.Gitlab(private_token=gitlab_personal_access_token)
        g.auth()
        values["gitlab"] = g
        values["gitlab_repo_instance"] = g.projects.get(gitlab_repository)
        values["gitlab_repository"] = gitlab_repository
        values["gitlab_personal_access_token"] = gitlab_personal_access_token
        values["gitlab_branch"] = gitlab_branch
        values["gitlab_base_branch"] = gitlab_base_branch
        return values
[docs]    def parse_issues(self, issues: List[Issue]) -> List[dict]:
        """
        Extracts title and number from each Issue and puts them in a dictionary
        Parameters:
            issues(List[Issue]): A list of gitlab Issue objects
        Returns:
            List[dict]: A dictionary of issue titles and numbers
        """
        parsed = []
        for issue in issues:
            title = issue.title
            number = issue.iid
            parsed.append({"title": title, "number": number})
        return parsed
[docs]    def get_issues(self) -> str:
        """
        Fetches all open issues from the repo
        Returns:


"""
        Fetches all open issues from the repo
        Returns:
            str: A plaintext report containing the number of issues
            and each issue's title and number.
        """
        issues = self.gitlab_repo_instance.issues.list(state="opened")
        if len(issues) > 0:
            parsed_issues = self.parse_issues(issues)
            parsed_issues_str = (
                "Found " + str(len(parsed_issues)) + " issues:\n" + str(parsed_issues)
            )
            return parsed_issues_str
        else:
            return "No open issues available"
[docs]    def get_issue(self, issue_number: int) -> Dict[str, Any]:
        """
        Fetches a specific issue and its first 10 comments
        Parameters:
            issue_number(int): The number for the gitlab issue
        Returns:
            dict: A dictionary containing the issue's title,
            body, and comments as a string
        """
        issue = self.gitlab_repo_instance.issues.get(issue_number)
        page = 0
        comments: List[dict] = []
        while len(comments) <= 10:
            comments_page = issue.notes.list(page=page)
            if len(comments_page) == 0:
                break
            for comment in comments_page:
                comment = issue.notes.get(comment.id)
                comments.append(
                    {"body": comment.body, "user": comment.author["username"]}
                )
            page += 1
        return {
            "title": issue.title,
            "body": issue.description,
            "comments": str(comments),
        }
[docs]    def create_pull_request(self, pr_query: str) -> str:
        """
        Makes a pull request from the bot's branch to the base branch


"""
        Makes a pull request from the bot's branch to the base branch
        Parameters:
            pr_query(str): a string which contains the PR title
            and the PR body. The title is the first line
            in the string, and the body are the rest of the string.
            For example, "Updated README\nmade changes to add info"
        Returns:
            str: A success or failure message
        """
        if self.gitlab_base_branch == self.gitlab_branch:
            return """Cannot make a pull request because
            commits are already in the master branch"""
        else:
            try:
                title = pr_query.split("\n")[0]
                body = pr_query[len(title) + 2 :]
                pr = self.gitlab_repo_instance.mergerequests.create(
                    {
                        "source_branch": self.gitlab_branch,
                        "target_branch": self.gitlab_base_branch,
                        "title": title,
                        "description": body,
                        "labels": ["created-by-agent"],
                    }
                )
                return f"Successfully created PR number {pr.iid}"
            except Exception as e:
                return "Unable to make pull request due to error:\n" + str(e)
[docs]    def comment_on_issue(self, comment_query: str) -> str:
        """
        Adds a comment to a gitlab issue
        Parameters:
            comment_query(str): a string which contains the issue number,
            two newlines, and the comment.
            for example: "1\n\nWorking on it now"
            adds the comment "working on it now" to issue 1
        Returns:
            str: A success or failure message
        """
        issue_number = int(comment_query.split("\n\n")[0])


"""
        issue_number = int(comment_query.split("\n\n")[0])
        comment = comment_query[len(str(issue_number)) + 2 :]
        try:
            issue = self.gitlab_repo_instance.issues.get(issue_number)
            issue.notes.create({"body": comment})
            return "Commented on issue " + str(issue_number)
        except Exception as e:
            return "Unable to make comment due to error:\n" + str(e)
[docs]    def create_file(self, file_query: str) -> str:
        """
        Creates a new file on the gitlab repo
        Parameters:
            file_query(str): a string which contains the file path
            and the file contents. The file path is the first line
            in the string, and the contents are the rest of the string.
            For example, "hello_world.md\n# Hello World!"
        Returns:
            str: A success or failure message
        """
        file_path = file_query.split("\n")[0]
        file_contents = file_query[len(file_path) + 2 :]
        try:
            self.gitlab_repo_instance.files.get(file_path, self.gitlab_branch)
            return f"File already exists at {file_path}. Use update_file instead"
        except Exception:
            data = {
                "branch": self.gitlab_branch,
                "commit_message": "Create " + file_path,
                "file_path": file_path,
                "content": file_contents,
            }
            self.gitlab_repo_instance.files.create(data)
            return "Created file " + file_path
[docs]    def read_file(self, file_path: str) -> str:
        """
        Reads a file from the gitlab repo
        Parameters:
            file_path(str): the file path
        Returns:


Parameters:
            file_path(str): the file path
        Returns:
            str: The file decoded as a string
        """
        file = self.gitlab_repo_instance.files.get(file_path, self.gitlab_branch)
        return file.decode().decode("utf-8")
[docs]    def update_file(self, file_query: str) -> str:
        """
        Updates a file with new content.
        Parameters:
            file_query(str): Contains the file path and the file contents.
                The old file contents is wrapped in OLD <<<< and >>>> OLD
                The new file contents is wrapped in NEW <<<< and >>>> NEW
                For example:
                test/hello.txt
                OLD <<<<
                Hello Earth!
                >>>> OLD
                NEW <<<<
                Hello Mars!
                >>>> NEW
        Returns:
            A success or failure message
        """
        try:
            file_path = file_query.split("\n")[0]
            old_file_contents = (
                file_query.split("OLD <<<<")[1].split(">>>> OLD")[0].strip()
            )
            new_file_contents = (
                file_query.split("NEW <<<<")[1].split(">>>> NEW")[0].strip()
            )
            file_content = self.read_file(file_path)
            updated_file_content = file_content.replace(
                old_file_contents, new_file_contents
            )
            if file_content == updated_file_content:
                return (
                    "File content was not updated because old content was not found."
                    "It may be helpful to use the read_file action to get "
                    "the current file contents."
                )
            commit = {
                "branch": self.gitlab_branch,
                "commit_message": "Create " + file_path,
                "actions": [


"commit_message": "Create " + file_path,
                "actions": [
                    {
                        "action": "update",
                        "file_path": file_path,
                        "content": updated_file_content,
                    }
                ],
            }
            self.gitlab_repo_instance.commits.create(commit)
            return "Updated file " + file_path
        except Exception as e:
            return "Unable to update file due to error:\n" + str(e)
[docs]    def delete_file(self, file_path: str) -> str:
        """
        Deletes a file from the repo
        Parameters:
            file_path(str): Where the file is
        Returns:
            str: Success or failure message
        """
        try:
            self.gitlab_repo_instance.files.delete(
                file_path, self.gitlab_branch, "Delete " + file_path
            )
            return "Deleted file " + file_path
        except Exception as e:
            return "Unable to delete file due to error:\n" + str(e)
[docs]    def run(self, mode: str, query: str) -> str:
        if mode == "get_issues":
            return self.get_issues()
        elif mode == "get_issue":
            return json.dumps(self.get_issue(int(query)))
        elif mode == "comment_on_issue":
            return self.comment_on_issue(query)
        elif mode == "create_file":
            return self.create_file(query)
        elif mode == "create_pull_request":
            return self.create_pull_request(query)
        elif mode == "read_file":
            return self.read_file(query)
        elif mode == "update_file":
            return self.update_file(query)
        elif mode == "delete_file":
            return self.delete_file(query)
        else:


return self.delete_file(query)
        else:
            raise ValueError("Invalid mode" + mode)


Source code for langchain.utilities.clickup
"""Util that calls clickup."""
import json
import warnings
from dataclasses import asdict, dataclass, fields
from typing import Any, Dict, List, Mapping, Optional, Tuple, Type, Union
import requests
from langchain.pydantic_v1 import BaseModel, Extra, root_validator
from langchain.utils import get_from_dict_or_env
DEFAULT_URL = "https://api.clickup.com/api/v2"
[docs]@dataclass
class Component:
    """Base class for all components."""
[docs]    @classmethod
    def from_data(cls, data: Dict[str, Any]) -> "Component":
        raise NotImplementedError()
[docs]@dataclass
class Task(Component):
    """Class for a task."""
    id: int
    name: str
    text_content: str
    description: str
    status: str
    creator_id: int
    creator_username: str
    creator_email: str
    assignees: List[Dict[str, Any]]
    watchers: List[Dict[str, Any]]
    priority: Optional[str]
    due_date: Optional[str]
    start_date: Optional[str]
    points: int
    team_id: int
    project_id: int
[docs]    @classmethod
    def from_data(cls, data: Dict[str, Any]) -> "Task":
        priority = None if data["priority"] is None else data["priority"]["priority"]
        return cls(
            id=data["id"],
            name=data["name"],
            text_content=data["text_content"],
            description=data["description"],
            status=data["status"]["status"],
            creator_id=data["creator"]["id"],
            creator_username=data["creator"]["username"],
            creator_email=data["creator"]["email"],


creator_username=data["creator"]["username"],
            creator_email=data["creator"]["email"],
            assignees=data["assignees"],
            watchers=data["watchers"],
            priority=priority,
            due_date=data["due_date"],
            start_date=data["start_date"],
            points=data["points"],
            team_id=data["team_id"],
            project_id=data["project"]["id"],
        )
[docs]@dataclass
class CUList(Component):
    """Component class for a list."""
    folder_id: float
    name: str
    content: Optional[str] = None
    due_date: Optional[int] = None
    due_date_time: Optional[bool] = None
    priority: Optional[int] = None
    assignee: Optional[int] = None
    status: Optional[str] = None
[docs]    @classmethod
    def from_data(cls, data: dict) -> "CUList":
        return cls(
            folder_id=data["folder_id"],
            name=data["name"],
            content=data.get("content"),
            due_date=data.get("due_date"),
            due_date_time=data.get("due_date_time"),
            priority=data.get("priority"),
            assignee=data.get("assignee"),
            status=data.get("status"),
        )
[docs]@dataclass
class Member(Component):
    """Component class for a member."""
    id: int
    username: str
    email: str
    initials: str
[docs]    @classmethod
    def from_data(cls, data: Dict) -> "Member":
        return cls(
            id=data["user"]["id"],
            username=data["user"]["username"],
            email=data["user"]["email"],
            initials=data["user"]["initials"],
        )
[docs]@dataclass


initials=data["user"]["initials"],
        )
[docs]@dataclass
class Team(Component):
    """Component class for a team."""
    id: int
    name: str
    members: List[Member]
[docs]    @classmethod
    def from_data(cls, data: Dict) -> "Team":
        members = [Member.from_data(member_data) for member_data in data["members"]]
        return cls(id=data["id"], name=data["name"], members=members)
[docs]@dataclass
class Space(Component):
    """Component class for a space."""
    id: int
    name: str
    private: bool
    enabled_features: Dict[str, Any]
[docs]    @classmethod
    def from_data(cls, data: Dict[str, Any]) -> "Space":
        space_data = data["spaces"][0]
        enabled_features = {
            feature: value
            for feature, value in space_data["features"].items()
            if value["enabled"]
        }
        return cls(
            id=space_data["id"],
            name=space_data["name"],
            private=space_data["private"],
            enabled_features=enabled_features,
        )
[docs]def parse_dict_through_component(
    data: dict, component: Type[Component], fault_tolerant: bool = False
) -> Dict:
    """Parse a dictionary by creating
    a component and then turning it back into a dictionary.
    This helps with two things
    1. Extract and format data from a dictionary according to schema
    2. Provide a central place to do this in a fault-tolerant way
    """
    try:
        return asdict(component.from_data(data))
    except Exception as e:
        if fault_tolerant:


except Exception as e:
        if fault_tolerant:
            warning_str = f"""Error encountered while trying to parse
{str(data)}: {str(e)}\n Falling back to returning input data."""
            warnings.warn(warning_str)
            return data
        else:
            raise e
[docs]def extract_dict_elements_from_component_fields(
    data: dict, component: Type[Component]
) -> dict:
    """Extract elements from a dictionary.
    Args:
        data: The dictionary to extract elements from.
        component: The component to extract elements from.
    Returns:
        A dictionary containing the elements from the input dictionary that are also
        in the component.
    """
    output = {}
    for attribute in fields(component):
        if attribute.name in data:
            output[attribute.name] = data[attribute.name]
    return output
[docs]def load_query(
    query: str, fault_tolerant: bool = False
) -> Tuple[Optional[Dict], Optional[str]]:
    """Attempts to parse a JSON string and return the parsed object.
    If parsing fails, returns an error message.
    :param query: The JSON string to parse.
    :return: A tuple containing the parsed object or None and an error message or None.
    """
    try:
        return json.loads(query), None
    except json.JSONDecodeError as e:
        if fault_tolerant:
            return (
                None,
                f"""Input must be a valid JSON. Got the following error: {str(e)}.
"Please reformat and try again.""",
            )
        else:
            raise e
[docs]def fetch_first_id(data: dict, key: str) -> Optional[int]:


[docs]def fetch_first_id(data: dict, key: str) -> Optional[int]:
    """Fetch the first id from a dictionary."""
    if key in data and len(data[key]) > 0:
        if len(data[key]) > 1:
            warnings.warn(f"Found multiple {key}: {data[key]}. Defaulting to first.")
        return data[key][0]["id"]
    return None
[docs]def fetch_data(url: str, access_token: str, query: Optional[dict] = None) -> dict:
    """Fetch data from a URL."""
    headers = {"Authorization": access_token}
    response = requests.get(url, headers=headers, params=query)
    response.raise_for_status()
    return response.json()
[docs]def fetch_team_id(access_token: str) -> Optional[int]:
    """Fetch the team id."""
    url = f"{DEFAULT_URL}/team"
    data = fetch_data(url, access_token)
    return fetch_first_id(data, "teams")
[docs]def fetch_space_id(team_id: int, access_token: str) -> Optional[int]:
    """Fetch the space id."""
    url = f"{DEFAULT_URL}/team/{team_id}/space"
    data = fetch_data(url, access_token, query={"archived": "false"})
    return fetch_first_id(data, "spaces")
[docs]def fetch_folder_id(space_id: int, access_token: str) -> Optional[int]:
    """Fetch the folder id."""
    url = f"{DEFAULT_URL}/space/{space_id}/folder"
    data = fetch_data(url, access_token, query={"archived": "false"})
    return fetch_first_id(data, "folders")


return fetch_first_id(data, "folders")
[docs]def fetch_list_id(space_id: int, folder_id: int, access_token: str) -> Optional[int]:
    """Fetch the list id."""
    if folder_id:
        url = f"{DEFAULT_URL}/folder/{folder_id}/list"
    else:
        url = f"{DEFAULT_URL}/space/{space_id}/list"
    data = fetch_data(url, access_token, query={"archived": "false"})
    # The structure to fetch list id differs based if its folderless
    if folder_id and "id" in data:
        return data["id"]
    else:
        return fetch_first_id(data, "lists")
[docs]class ClickupAPIWrapper(BaseModel):
    """Wrapper for Clickup API."""
    access_token: Optional[str] = None
    team_id: Optional[str] = None
    space_id: Optional[str] = None
    folder_id: Optional[str] = None
    list_id: Optional[str] = None
    class Config:
        """Configuration for this pydantic object."""
        extra = Extra.forbid
[docs]    @classmethod
    def get_access_code_url(
        cls, oauth_client_id: str, redirect_uri: str = "https://google.com"
    ) -> str:
        """Get the URL to get an access code."""
        url = f"https://app.clickup.com/api?client_id={oauth_client_id}"
        return f"{url}&redirect_uri={redirect_uri}"
[docs]    @classmethod
    def get_access_token(
        cls, oauth_client_id: str, oauth_client_secret: str, code: str
    ) -> Optional[str]:
        """Get the access token."""
        url = f"{DEFAULT_URL}/oauth/token"


"""Get the access token."""
        url = f"{DEFAULT_URL}/oauth/token"
        params = {
            "client_id": oauth_client_id,
            "client_secret": oauth_client_secret,
            "code": code,
        }
        response = requests.post(url, params=params)
        data = response.json()
        if "access_token" not in data:
            print(f"Error: {data}")
            if "ECODE" in data and data["ECODE"] == "OAUTH_014":
                url = ClickupAPIWrapper.get_access_code_url(oauth_client_id)
                print(
                    "You already used this code once. Generate a new one.",
                    f"Our best guess for the url to get a new code is:\n{url}",
                )
            return None
        return data["access_token"]
    @root_validator()
    def validate_environment(cls, values: Dict) -> Dict:
        """Validate that api key and python package exists in environment."""
        values["access_token"] = get_from_dict_or_env(
            values, "access_token", "CLICKUP_ACCESS_TOKEN"
        )
        values["team_id"] = fetch_team_id(values["access_token"])
        values["space_id"] = fetch_space_id(values["team_id"], values["access_token"])
        values["folder_id"] = fetch_folder_id(
            values["space_id"], values["access_token"]
        )
        values["list_id"] = fetch_list_id(
            values["space_id"], values["folder_id"], values["access_token"]
        )
        return values
[docs]    def attempt_parse_teams(self, input_dict: dict) -> Dict[str, List[dict]]:
        """Parse appropriate content from the list of teams."""


"""Parse appropriate content from the list of teams."""
        parsed_teams: Dict[str, List[dict]] = {"teams": []}
        for team in input_dict["teams"]:
            try:
                team = parse_dict_through_component(team, Team, fault_tolerant=False)
                parsed_teams["teams"].append(team)
            except Exception as e:
                warnings.warn(f"Error parsing a team {e}")
        return parsed_teams
[docs]    def get_headers(
        self,
    ) -> Mapping[str, Union[str, bytes]]:
        """Get the headers for the request."""
        if not isinstance(self.access_token, str):
            raise TypeError(f"Access Token: {self.access_token}, must be str.")
        headers = {
            "Authorization": str(self.access_token),
            "Content-Type": "application/json",
        }
        return headers
[docs]    def get_default_params(self) -> Dict:
        return {"archived": "false"}
[docs]    def get_authorized_teams(self) -> Dict[Any, Any]:
        """Get all teams for the user."""
        url = f"{DEFAULT_URL}/team"
        response = requests.get(url, headers=self.get_headers())
        data = response.json()
        parsed_teams = self.attempt_parse_teams(data)
        return parsed_teams
[docs]    def get_folders(self) -> Dict:
        """
        Get all the folders for the team.
        """
        url = f"{DEFAULT_URL}/team/" + str(self.team_id) + "/space"
        params = self.get_default_params()
        response = requests.get(url, headers=self.get_headers(), params=params)
        return {"response": response}


return {"response": response}
[docs]    def get_task(self, query: str, fault_tolerant: bool = True) -> Dict:
        """
        Retrieve a specific task.
        """
        params, error = load_query(query, fault_tolerant=True)
        if params is None:
            return {"Error": error}
        url = f"{DEFAULT_URL}/task/{params['task_id']}"
        params = {
            "custom_task_ids": "true",
            "team_id": self.team_id,
            "include_subtasks": "true",
        }
        response = requests.get(url, headers=self.get_headers(), params=params)
        data = response.json()
        parsed_task = parse_dict_through_component(
            data, Task, fault_tolerant=fault_tolerant
        )
        return parsed_task
[docs]    def get_lists(self) -> Dict:
        """
        Get all available lists.
        """
        url = f"{DEFAULT_URL}/folder/{self.folder_id}/list"
        params = self.get_default_params()
        response = requests.get(url, headers=self.get_headers(), params=params)
        return {"response": response}
[docs]    def query_tasks(self, query: str) -> Dict:
        """
        Query tasks that match certain fields
        """
        params, error = load_query(query, fault_tolerant=True)
        if params is None:
            return {"Error": error}
        url = f"{DEFAULT_URL}/list/{params['list_id']}/task"
        params = self.get_default_params()
        response = requests.get(url, headers=self.get_headers(), params=params)
        return {"response": response}
[docs]    def get_spaces(self) -> Dict:
        """


[docs]    def get_spaces(self) -> Dict:
        """
        Get all spaces for the team.
        """
        url = f"{DEFAULT_URL}/team/{self.team_id}/space"
        response = requests.get(
            url, headers=self.get_headers(), params=self.get_default_params()
        )
        data = response.json()
        parsed_spaces = parse_dict_through_component(data, Space, fault_tolerant=True)
        return parsed_spaces
[docs]    def get_task_attribute(self, query: str) -> Dict:
        """
        Update an attribute of a specified task.
        """
        task = self.get_task(query, fault_tolerant=True)
        params, error = load_query(query, fault_tolerant=True)
        if not isinstance(params, dict):
            return {"Error": error}
        if params["attribute_name"] not in task:
            return {
                "Error": f"""attribute_name = {params['attribute_name']} was not
found in task keys {task.keys()}. Please call again with one of the key names."""
            }
        return {params["attribute_name"]: task[params["attribute_name"]]}
[docs]    def update_task(self, query: str) -> Dict:
        """
        Update an attribute of a specified task.
        """
        query_dict, error = load_query(query, fault_tolerant=True)
        if query_dict is None:
            return {"Error": error}
        url = f"{DEFAULT_URL}/task/{query_dict['task_id']}"
        params = {
            "custom_task_ids": "true",
            "team_id": self.team_id,
            "include_subtasks": "true",
        }
        headers = self.get_headers()


}
        headers = self.get_headers()
        payload = {query_dict["attribute_name"]: query_dict["value"]}
        response = requests.put(url, headers=headers, params=params, json=payload)
        return {"response": response}
[docs]    def update_task_assignees(self, query: str) -> Dict:
        """
        Add or remove assignees of a specified task.
        """
        query_dict, error = load_query(query, fault_tolerant=True)
        if query_dict is None:
            return {"Error": error}
        for user in query_dict["users"]:
            if not isinstance(user, int):
                return {
                    "Error": f"""All users must be integers, not strings!
"Got user {user} if type {type(user)}"""
                }
        url = f"{DEFAULT_URL}/task/{query_dict['task_id']}"
        headers = self.get_headers()
        if query_dict["operation"] == "add":
            assigne_payload = {"add": query_dict["users"], "rem": []}
        elif query_dict["operation"] == "rem":
            assigne_payload = {"add": [], "rem": query_dict["users"]}
        else:
            raise ValueError(
                f"Invalid operation ({query_dict['operation']}). ",
                "Valid options ['add', 'rem'].",
            )
        params = {
            "custom_task_ids": "true",
            "team_id": self.team_id,
            "include_subtasks": "true",
        }
        payload = {"assignees": assigne_payload}
        response = requests.put(url, headers=headers, params=params, json=payload)
        return {"response": response}
[docs]    def create_task(self, query: str) -> Dict:
        """


[docs]    def create_task(self, query: str) -> Dict:
        """
        Creates a new task.
        """
        query_dict, error = load_query(query, fault_tolerant=True)
        if query_dict is None:
            return {"Error": error}
        list_id = self.list_id
        url = f"{DEFAULT_URL}/list/{list_id}/task"
        params = {"custom_task_ids": "true", "team_id": self.team_id}
        payload = extract_dict_elements_from_component_fields(query_dict, Task)
        headers = self.get_headers()
        response = requests.post(url, json=payload, headers=headers, params=params)
        data: Dict = response.json()
        return parse_dict_through_component(data, Task, fault_tolerant=True)
[docs]    def create_list(self, query: str) -> Dict:
        """
        Creates a new list.
        """
        query_dict, error = load_query(query, fault_tolerant=True)
        if query_dict is None:
            return {"Error": error}
        # Default to using folder as location if it exists.
        # If not, fall back to using the space.
        location = self.folder_id if self.folder_id else self.space_id
        url = f"{DEFAULT_URL}/folder/{location}/list"
        payload = extract_dict_elements_from_component_fields(query_dict, Task)
        headers = self.get_headers()
        response = requests.post(url, json=payload, headers=headers)
        data = response.json()
        parsed_list = parse_dict_through_component(data, CUList, fault_tolerant=True)
        # set list id to new list
        if "id" in parsed_list:
            self.list_id = parsed_list["id"]
        return parsed_list


self.list_id = parsed_list["id"]
        return parsed_list
[docs]    def create_folder(self, query: str) -> Dict:
        """
        Creates a new folder.
        """
        query_dict, error = load_query(query, fault_tolerant=True)
        if query_dict is None:
            return {"Error": error}
        space_id = self.space_id
        url = f"{DEFAULT_URL}/space/{space_id}/folder"
        payload = {
            "name": query_dict["name"],
        }
        headers = self.get_headers()
        response = requests.post(url, json=payload, headers=headers)
        data = response.json()
        if "id" in data:
            self.list_id = data["id"]
        return data
[docs]    def run(self, mode: str, query: str) -> str:
        """Run the API."""
        if mode == "get_task":
            output = self.get_task(query)
        elif mode == "get_task_attribute":
            output = self.get_task_attribute(query)
        elif mode == "get_teams":
            output = self.get_authorized_teams()
        elif mode == "create_task":
            output = self.create_task(query)
        elif mode == "create_list":
            output = self.create_list(query)
        elif mode == "create_folder":
            output = self.create_folder(query)
        elif mode == "get_lists":
            output = self.get_lists()
        elif mode == "get_folders":
            output = self.get_folders()
        elif mode == "get_spaces":
            output = self.get_spaces()
        elif mode == "update_task":
            output = self.update_task(query)
        elif mode == "update_task_assignees":


output = self.update_task(query)
        elif mode == "update_task_assignees":
            output = self.update_task_assignees(query)
        else:
            output = {"ModeError": f"Got unexpected mode {mode}."}
        try:
            return json.dumps(output)
        except Exception:
            return str(output)


Source code for langchain.utilities.vertexai
"""Utilities to init Vertex AI."""
from importlib import metadata
from typing import TYPE_CHECKING, Optional
if TYPE_CHECKING:
    from google.api_core.gapic_v1.client_info import ClientInfo
    from google.auth.credentials import Credentials
[docs]def raise_vertex_import_error(minimum_expected_version: str = "1.35.0") -> None:
    """Raise ImportError related to Vertex SDK being not available.
    Args:
        minimum_expected_version: The lowest expected version of the SDK.
    Raises:
        ImportError: an ImportError that mentions a required version of the SDK.
    """
    raise ImportError(
        "Could not import VertexAI. Please, install it with "
        f"pip install google-cloud-aiplatform>={minimum_expected_version}"
    )
[docs]def init_vertexai(
    project: Optional[str] = None,
    location: Optional[str] = None,
    credentials: Optional["Credentials"] = None,
) -> None:
    """Init vertexai.
    Args:
        project: The default GCP project to use when making Vertex API calls.
        location: The default location to use when making API calls.
        credentials: The default custom
            credentials to use when making API calls. If not provided credentials
            will be ascertained from the environment.
    Raises:
        ImportError: If importing vertexai SDK did not succeed.
    """
    try:
        import vertexai
    except ImportError:
        raise_vertex_import_error()
    vertexai.init(
        project=project,
        location=location,
        credentials=credentials,
    )
[docs]def get_client_info(module: Optional[str] = None) -> "ClientInfo":
    r"""Returns a custom user agent header.


r"""Returns a custom user agent header.
    Args:
        module (Optional[str]):
            Optional. The module for a custom user agent header.
    Returns:
        google.api_core.gapic_v1.client_info.ClientInfo
    """
    try:
        from google.api_core.gapic_v1.client_info import ClientInfo
    except ImportError as exc:
        raise ImportError(
            "Could not import ClientInfo. Please, install it with "
            "pip install google-api-core"
        ) from exc
    langchain_version = metadata.version("langchain")
    client_library_version = (
        f"{langchain_version}-{module}" if module else langchain_version
    )
    return ClientInfo(
        client_library_version=client_library_version,
        user_agent=f"langchain/{client_library_version}",
    )


Source code for langchain.utilities.apify
from typing import TYPE_CHECKING, Any, Callable, Dict, Optional
from langchain.pydantic_v1 import BaseModel, root_validator
from langchain.schema.document import Document
from langchain.utils import get_from_dict_or_env
if TYPE_CHECKING:
    from langchain.document_loaders import ApifyDatasetLoader
[docs]class ApifyWrapper(BaseModel):
    """Wrapper around Apify.
    To use, you should have the ``apify-client`` python package installed,
    and the environment variable ``APIFY_API_TOKEN`` set with your API key, or pass
    `apify_api_token` as a named parameter to the constructor.
    """
    apify_client: Any
    apify_client_async: Any
    @root_validator()
    def validate_environment(cls, values: Dict) -> Dict:
        """Validate environment.
        Validate that an Apify API token is set and the apify-client
        Python package exists in the current environment.
        """
        apify_api_token = get_from_dict_or_env(
            values, "apify_api_token", "APIFY_API_TOKEN"
        )
        try:
            from apify_client import ApifyClient, ApifyClientAsync
            values["apify_client"] = ApifyClient(apify_api_token)
            values["apify_client_async"] = ApifyClientAsync(apify_api_token)
        except ImportError:
            raise ImportError(
                "Could not import apify-client Python package. "
                "Please install it with `pip install apify-client`."
            )
        return values
[docs]    def call_actor(
        self,
        actor_id: str,
        run_input: Dict,
        dataset_mapping_function: Callable[[Dict], Document],
        *,


dataset_mapping_function: Callable[[Dict], Document],
        *,
        build: Optional[str] = None,
        memory_mbytes: Optional[int] = None,
        timeout_secs: Optional[int] = None,
    ) -> "ApifyDatasetLoader":
        """Run an Actor on the Apify platform and wait for results to be ready.
        Args:
            actor_id (str): The ID or name of the Actor on the Apify platform.
            run_input (Dict): The input object of the Actor that you're trying to run.
            dataset_mapping_function (Callable): A function that takes a single
                dictionary (an Apify dataset item) and converts it to an
                instance of the Document class.
            build (str, optional): Optionally specifies the actor build to run.
                It can be either a build tag or build number.
            memory_mbytes (int, optional): Optional memory limit for the run,
                in megabytes.
            timeout_secs (int, optional): Optional timeout for the run, in seconds.
        Returns:
            ApifyDatasetLoader: A loader that will fetch the records from the
                Actor run's default dataset.
        """
        from langchain.document_loaders import ApifyDatasetLoader
        actor_call = self.apify_client.actor(actor_id).call(
            run_input=run_input,
            build=build,
            memory_mbytes=memory_mbytes,
            timeout_secs=timeout_secs,
        )
        return ApifyDatasetLoader(
            dataset_id=actor_call["defaultDatasetId"],
            dataset_mapping_function=dataset_mapping_function,
        )
[docs]    async def acall_actor(
        self,
        actor_id: str,
        run_input: Dict,
        dataset_mapping_function: Callable[[Dict], Document],
        *,


dataset_mapping_function: Callable[[Dict], Document],
        *,
        build: Optional[str] = None,
        memory_mbytes: Optional[int] = None,
        timeout_secs: Optional[int] = None,
    ) -> "ApifyDatasetLoader":
        """Run an Actor on the Apify platform and wait for results to be ready.
        Args:
            actor_id (str): The ID or name of the Actor on the Apify platform.
            run_input (Dict): The input object of the Actor that you're trying to run.
            dataset_mapping_function (Callable): A function that takes a single
                dictionary (an Apify dataset item) and converts it to
                an instance of the Document class.
            build (str, optional): Optionally specifies the actor build to run.
                It can be either a build tag or build number.
            memory_mbytes (int, optional): Optional memory limit for the run,
                in megabytes.
            timeout_secs (int, optional): Optional timeout for the run, in seconds.
        Returns:
            ApifyDatasetLoader: A loader that will fetch the records from the
                Actor run's default dataset.
        """
        from langchain.document_loaders import ApifyDatasetLoader
        actor_call = await self.apify_client_async.actor(actor_id).call(
            run_input=run_input,
            build=build,
            memory_mbytes=memory_mbytes,
            timeout_secs=timeout_secs,
        )
        return ApifyDatasetLoader(
            dataset_id=actor_call["defaultDatasetId"],
            dataset_mapping_function=dataset_mapping_function,
        )
[docs]    def call_actor_task(
        self,
        task_id: str,
        task_input: Dict,
        dataset_mapping_function: Callable[[Dict], Document],
        *,


dataset_mapping_function: Callable[[Dict], Document],
        *,
        build: Optional[str] = None,
        memory_mbytes: Optional[int] = None,
        timeout_secs: Optional[int] = None,
    ) -> "ApifyDatasetLoader":
        """Run a saved Actor task on Apify and wait for results to be ready.
        Args:
            task_id (str): The ID or name of the task on the Apify platform.
            task_input (Dict): The input object of the task that you're trying to run.
                Overrides the task's saved input.
            dataset_mapping_function (Callable): A function that takes a single
                dictionary (an Apify dataset item) and converts it to an
                instance of the Document class.
            build (str, optional): Optionally specifies the actor build to run.
                It can be either a build tag or build number.
            memory_mbytes (int, optional): Optional memory limit for the run,
                in megabytes.
            timeout_secs (int, optional): Optional timeout for the run, in seconds.
        Returns:
            ApifyDatasetLoader: A loader that will fetch the records from the
                task run's default dataset.
        """
        from langchain.document_loaders import ApifyDatasetLoader
        task_call = self.apify_client.task(task_id).call(
            task_input=task_input,
            build=build,
            memory_mbytes=memory_mbytes,
            timeout_secs=timeout_secs,
        )
        return ApifyDatasetLoader(
            dataset_id=task_call["defaultDatasetId"],
            dataset_mapping_function=dataset_mapping_function,
        )
[docs]    async def acall_actor_task(
        self,
        task_id: str,
        task_input: Dict,


self,
        task_id: str,
        task_input: Dict,
        dataset_mapping_function: Callable[[Dict], Document],
        *,
        build: Optional[str] = None,
        memory_mbytes: Optional[int] = None,
        timeout_secs: Optional[int] = None,
    ) -> "ApifyDatasetLoader":
        """Run a saved Actor task on Apify and wait for results to be ready.
        Args:
            task_id (str): The ID or name of the task on the Apify platform.
            task_input (Dict): The input object of the task that you're trying to run.
                Overrides the task's saved input.
            dataset_mapping_function (Callable): A function that takes a single
                dictionary (an Apify dataset item) and converts it to an
                instance of the Document class.
            build (str, optional): Optionally specifies the actor build to run.
                It can be either a build tag or build number.
            memory_mbytes (int, optional): Optional memory limit for the run,
                in megabytes.
            timeout_secs (int, optional): Optional timeout for the run, in seconds.
        Returns:
            ApifyDatasetLoader: A loader that will fetch the records from the
                task run's default dataset.
        """
        from langchain.document_loaders import ApifyDatasetLoader
        task_call = await self.apify_client_async.task(task_id).call(
            task_input=task_input,
            build=build,
            memory_mbytes=memory_mbytes,
            timeout_secs=timeout_secs,
        )
        return ApifyDatasetLoader(
            dataset_id=task_call["defaultDatasetId"],
            dataset_mapping_function=dataset_mapping_function,
        )


Source code for langchain.utilities.scenexplain
"""Util that calls SceneXplain.
In order to set this up, you need API key for the SceneXplain API.
You can obtain a key by following the steps below.
- Sign up for a free account at https://scenex.jina.ai/.
- Navigate to the API Access page (https://scenex.jina.ai/api) and create a new API key.
"""
from typing import Dict
import requests
from langchain.pydantic_v1 import BaseModel, BaseSettings, Field, root_validator
from langchain.utils import get_from_dict_or_env
[docs]class SceneXplainAPIWrapper(BaseSettings, BaseModel):
    """Wrapper for SceneXplain API.
    In order to set this up, you need API key for the SceneXplain API.
    You can obtain a key by following the steps below.
    - Sign up for a free account at https://scenex.jina.ai/.
    - Navigate to the API Access page (https://scenex.jina.ai/api)
      and create a new API key.
    """
    scenex_api_key: str = Field(..., env="SCENEX_API_KEY")
    scenex_api_url: str = "https://api.scenex.jina.ai/v1/describe"
    def _describe_image(self, image: str) -> str:
        headers = {
            "x-api-key": f"token {self.scenex_api_key}",
            "content-type": "application/json",
        }
        payload = {
            "data": [
                {
                    "image": image,
                    "algorithm": "Ember",
                    "languages": ["en"],
                }
            ]
        }


"languages": ["en"],
                }
            ]
        }
        response = requests.post(self.scenex_api_url, headers=headers, json=payload)
        response.raise_for_status()
        result = response.json().get("result", [])
        img = result[0] if result else {}
        return img.get("text", "")
    @root_validator(pre=True)
    def validate_environment(cls, values: Dict) -> Dict:
        """Validate that api key exists in environment."""
        scenex_api_key = get_from_dict_or_env(
            values, "scenex_api_key", "SCENEX_API_KEY"
        )
        values["scenex_api_key"] = scenex_api_key
        return values
[docs]    def run(self, image: str) -> str:
        """Run SceneXplain image explainer."""
        description = self._describe_image(image)
        if not description:
            return "No description found."
        return description


Source code for langchain.utilities.openweathermap
"""Util that calls OpenWeatherMap using PyOWM."""
from typing import Any, Dict, Optional
from langchain.pydantic_v1 import BaseModel, Extra, root_validator
from langchain.utils import get_from_dict_or_env
[docs]class OpenWeatherMapAPIWrapper(BaseModel):
    """Wrapper for OpenWeatherMap API using PyOWM.
    Docs for using:
    1. Go to OpenWeatherMap and sign up for an API key
    2. Save your API KEY into OPENWEATHERMAP_API_KEY env variable
    3. pip install pyowm
    """
    owm: Any
    openweathermap_api_key: Optional[str] = None
    class Config:
        """Configuration for this pydantic object."""
        extra = Extra.forbid
    @root_validator(pre=True)
    def validate_environment(cls, values: Dict) -> Dict:
        """Validate that api key exists in environment."""
        openweathermap_api_key = get_from_dict_or_env(
            values, "openweathermap_api_key", "OPENWEATHERMAP_API_KEY"
        )
        try:
            import pyowm
        except ImportError:
            raise ImportError(
                "pyowm is not installed. Please install it with `pip install pyowm`"
            )
        owm = pyowm.OWM(openweathermap_api_key)
        values["owm"] = owm
        return values
    def _format_weather_info(self, location: str, w: Any) -> str:
        detailed_status = w.detailed_status
        wind = w.wind()
        humidity = w.humidity
        temperature = w.temperature("celsius")
        rain = w.rain
        heat_index = w.heat_index
        clouds = w.clouds


heat_index = w.heat_index
        clouds = w.clouds
        return (
            f"In {location}, the current weather is as follows:\n"
            f"Detailed status: {detailed_status}\n"
            f"Wind speed: {wind['speed']} m/s, direction: {wind['deg']}°\n"
            f"Humidity: {humidity}%\n"
            f"Temperature: \n"
            f"  - Current: {temperature['temp']}°C\n"
            f"  - High: {temperature['temp_max']}°C\n"
            f"  - Low: {temperature['temp_min']}°C\n"
            f"  - Feels like: {temperature['feels_like']}°C\n"
            f"Rain: {rain}\n"
            f"Heat index: {heat_index}\n"
            f"Cloud cover: {clouds}%"
        )
[docs]    def run(self, location: str) -> str:
        """Get the current weather information for a specified location."""
        mgr = self.owm.weather_manager()
        observation = mgr.weather_at_place(location)
        w = observation.weather
        return self._format_weather_info(location, w)


Source code for langchain.utilities.anthropic
from typing import Any, List
def _get_anthropic_client() -> Any:
    try:
        import anthropic
    except ImportError:
        raise ImportError(
            "Could not import anthropic python package. "
            "This is needed in order to accurately tokenize the text "
            "for anthropic models. Please install it with `pip install anthropic`."
        )
    return anthropic.Anthropic()
[docs]def get_num_tokens_anthropic(text: str) -> int:
    """Get the number of tokens in a string of text."""
    client = _get_anthropic_client()
    return client.count_tokens(text=text)
[docs]def get_token_ids_anthropic(text: str) -> List[int]:
    """Get the token ids for a string of text."""
    client = _get_anthropic_client()
    tokenizer = client.get_tokenizer()
    encoded_text = tokenizer.encode(text)
    return encoded_text.ids


Source code for langchain.utilities.twilio
"""Util that calls Twilio."""
from typing import Any, Dict, Optional
from langchain.pydantic_v1 import BaseModel, Extra, root_validator
from langchain.utils import get_from_dict_or_env
[docs]class TwilioAPIWrapper(BaseModel):
    """Messaging Client using Twilio.
    To use, you should have the ``twilio`` python package installed,
    and the environment variables ``TWILIO_ACCOUNT_SID``, ``TWILIO_AUTH_TOKEN``, and
    ``TWILIO_FROM_NUMBER``, or pass `account_sid`, `auth_token`, and `from_number` as
    named parameters to the constructor.
    Example:
        .. code-block:: python
            from langchain.utilities.twilio import TwilioAPIWrapper
            twilio = TwilioAPIWrapper(
                account_sid="ACxxx",
                auth_token="xxx",
                from_number="+10123456789"
            )
            twilio.run('test', '+12484345508')
    """
    client: Any  #: :meta private:
    account_sid: Optional[str] = None
    """Twilio account string identifier."""
    auth_token: Optional[str] = None
    """Twilio auth token."""
    from_number: Optional[str] = None
    """A Twilio phone number in [E.164](https://www.twilio.com/docs/glossary/what-e164)
        format, an
        [alphanumeric sender ID](https://www.twilio.com/docs/sms/send-messages#use-an-alphanumeric-sender-id),
        or a [Channel Endpoint address](https://www.twilio.com/docs/sms/channels#channel-addresses)
        that is enabled for the type of message you want to send. Phone numbers or


that is enabled for the type of message you want to send. Phone numbers or
        [short codes](https://www.twilio.com/docs/sms/api/short-code) purchased from
        Twilio also work here. You cannot, for example, spoof messages from a private
        cell phone number. If you are using `messaging_service_sid`, this parameter
        must be empty.
    """  # noqa: E501
    class Config:
        """Configuration for this pydantic object."""
        extra = Extra.forbid
        arbitrary_types_allowed = False
    @root_validator()
    def validate_environment(cls, values: Dict) -> Dict:
        """Validate that api key and python package exists in environment."""
        try:
            from twilio.rest import Client
        except ImportError:
            raise ImportError(
                "Could not import twilio python package. "
                "Please install it with `pip install twilio`."
            )
        account_sid = get_from_dict_or_env(values, "account_sid", "TWILIO_ACCOUNT_SID")
        auth_token = get_from_dict_or_env(values, "auth_token", "TWILIO_AUTH_TOKEN")
        values["from_number"] = get_from_dict_or_env(
            values, "from_number", "TWILIO_FROM_NUMBER"
        )
        values["client"] = Client(account_sid, auth_token)
        return values
[docs]    def run(self, body: str, to: str) -> str:
        """Run body through Twilio and respond with message sid.
        Args:
            body: The text of the message you want to send. Can be up to 1,600
                characters in length.
            to: The destination phone number in


characters in length.
            to: The destination phone number in
                [E.164](https://www.twilio.com/docs/glossary/what-e164) format for
                SMS/MMS or
                [Channel user address](https://www.twilio.com/docs/sms/channels#channel-addresses)
                for other 3rd-party channels.
        """  # noqa: E501
        message = self.client.messages.create(to, from_=self.from_number, body=body)
        return message.sid


Source code for langchain.utilities.requests
"""Lightweight wrapper around requests library, with async support."""
from contextlib import asynccontextmanager
from typing import Any, AsyncGenerator, Dict, Optional
import aiohttp
import requests
from langchain.pydantic_v1 import BaseModel, Extra
[docs]class Requests(BaseModel):
    """Wrapper around requests to handle auth and async.
    The main purpose of this wrapper is to handle authentication (by saving
    headers) and enable easy async methods on the same base object.
    """
    headers: Optional[Dict[str, str]] = None
    aiosession: Optional[aiohttp.ClientSession] = None
    auth: Optional[Any] = None
    class Config:
        """Configuration for this pydantic object."""
        extra = Extra.forbid
        arbitrary_types_allowed = True
[docs]    def get(self, url: str, **kwargs: Any) -> requests.Response:
        """GET the URL and return the text."""
        return requests.get(url, headers=self.headers, auth=self.auth, **kwargs)
[docs]    def post(self, url: str, data: Dict[str, Any], **kwargs: Any) -> requests.Response:
        """POST to the URL and return the text."""
        return requests.post(
            url, json=data, headers=self.headers, auth=self.auth, **kwargs
        )
[docs]    def patch(self, url: str, data: Dict[str, Any], **kwargs: Any) -> requests.Response:
        """PATCH the URL and return the text."""
        return requests.patch(
            url, json=data, headers=self.headers, auth=self.auth, **kwargs
        )
[docs]    def put(self, url: str, data: Dict[str, Any], **kwargs: Any) -> requests.Response:


"""PUT the URL and return the text."""
        return requests.put(
            url, json=data, headers=self.headers, auth=self.auth, **kwargs
        )
[docs]    def delete(self, url: str, **kwargs: Any) -> requests.Response:
        """DELETE the URL and return the text."""
        return requests.delete(url, headers=self.headers, auth=self.auth, **kwargs)
    @asynccontextmanager
    async def _arequest(
        self, method: str, url: str, **kwargs: Any
    ) -> AsyncGenerator[aiohttp.ClientResponse, None]:
        """Make an async request."""
        if not self.aiosession:
            async with aiohttp.ClientSession() as session:
                async with session.request(
                    method, url, headers=self.headers, auth=self.auth, **kwargs
                ) as response:
                    yield response
        else:
            async with self.aiosession.request(
                method, url, headers=self.headers, auth=self.auth, **kwargs
            ) as response:
                yield response
[docs]    @asynccontextmanager
    async def aget(
        self, url: str, **kwargs: Any
    ) -> AsyncGenerator[aiohttp.ClientResponse, None]:
        """GET the URL and return the text asynchronously."""
        async with self._arequest("GET", url, **kwargs) as response:
            yield response
[docs]    @asynccontextmanager
    async def apost(
        self, url: str, data: Dict[str, Any], **kwargs: Any
    ) -> AsyncGenerator[aiohttp.ClientResponse, None]:
        """POST to the URL and return the text asynchronously."""
        async with self._arequest("POST", url, json=data, **kwargs) as response:
            yield response


yield response
[docs]    @asynccontextmanager
    async def apatch(
        self, url: str, data: Dict[str, Any], **kwargs: Any
    ) -> AsyncGenerator[aiohttp.ClientResponse, None]:
        """PATCH the URL and return the text asynchronously."""
        async with self._arequest("PATCH", url, json=data, **kwargs) as response:
            yield response
[docs]    @asynccontextmanager
    async def aput(
        self, url: str, data: Dict[str, Any], **kwargs: Any
    ) -> AsyncGenerator[aiohttp.ClientResponse, None]:
        """PUT the URL and return the text asynchronously."""
        async with self._arequest("PUT", url, json=data, **kwargs) as response:
            yield response
[docs]    @asynccontextmanager
    async def adelete(
        self, url: str, **kwargs: Any
    ) -> AsyncGenerator[aiohttp.ClientResponse, None]:
        """DELETE the URL and return the text asynchronously."""
        async with self._arequest("DELETE", url, **kwargs) as response:
            yield response
[docs]class TextRequestsWrapper(BaseModel):
    """Lightweight wrapper around requests library.
    The main purpose of this wrapper is to always return a text output.
    """
    headers: Optional[Dict[str, str]] = None
    aiosession: Optional[aiohttp.ClientSession] = None
    auth: Optional[Any] = None
    class Config:
        """Configuration for this pydantic object."""
        extra = Extra.forbid
        arbitrary_types_allowed = True
    @property
    def requests(self) -> Requests:
        return Requests(
            headers=self.headers, aiosession=self.aiosession, auth=self.auth
        )


headers=self.headers, aiosession=self.aiosession, auth=self.auth
        )
[docs]    def get(self, url: str, **kwargs: Any) -> str:
        """GET the URL and return the text."""
        return self.requests.get(url, **kwargs).text
[docs]    def post(self, url: str, data: Dict[str, Any], **kwargs: Any) -> str:
        """POST to the URL and return the text."""
        return self.requests.post(url, data, **kwargs).text
[docs]    def patch(self, url: str, data: Dict[str, Any], **kwargs: Any) -> str:
        """PATCH the URL and return the text."""
        return self.requests.patch(url, data, **kwargs).text
[docs]    def put(self, url: str, data: Dict[str, Any], **kwargs: Any) -> str:
        """PUT the URL and return the text."""
        return self.requests.put(url, data, **kwargs).text
[docs]    def delete(self, url: str, **kwargs: Any) -> str:
        """DELETE the URL and return the text."""
        return self.requests.delete(url, **kwargs).text
[docs]    async def aget(self, url: str, **kwargs: Any) -> str:
        """GET the URL and return the text asynchronously."""
        async with self.requests.aget(url, **kwargs) as response:
            return await response.text()
[docs]    async def apost(self, url: str, data: Dict[str, Any], **kwargs: Any) -> str:
        """POST to the URL and return the text asynchronously."""
        async with self.requests.apost(url, data, **kwargs) as response:
            return await response.text()


return await response.text()
[docs]    async def apatch(self, url: str, data: Dict[str, Any], **kwargs: Any) -> str:
        """PATCH the URL and return the text asynchronously."""
        async with self.requests.apatch(url, data, **kwargs) as response:
            return await response.text()
[docs]    async def aput(self, url: str, data: Dict[str, Any], **kwargs: Any) -> str:
        """PUT the URL and return the text asynchronously."""
        async with self.requests.aput(url, data, **kwargs) as response:
            return await response.text()
[docs]    async def adelete(self, url: str, **kwargs: Any) -> str:
        """DELETE the URL and return the text asynchronously."""
        async with self.requests.adelete(url, **kwargs) as response:
            return await response.text()
# For backwards compatibility
RequestsWrapper = TextRequestsWrapper


Source code for langchain.utilities.max_compute
from __future__ import annotations
from typing import TYPE_CHECKING, Iterator, List, Optional
from langchain.utils import get_from_env
if TYPE_CHECKING:
    from odps import ODPS
[docs]class MaxComputeAPIWrapper:
    """Interface for querying Alibaba Cloud MaxCompute tables."""
[docs]    def __init__(self, client: ODPS):
        """Initialize MaxCompute document loader.
        Args:
            client: odps.ODPS MaxCompute client object.
        """
        self.client = client
[docs]    @classmethod
    def from_params(
        cls,
        endpoint: str,
        project: str,
        *,
        access_id: Optional[str] = None,
        secret_access_key: Optional[str] = None,
    ) -> MaxComputeAPIWrapper:
        """Convenience constructor that builds the odsp.ODPS MaxCompute client from
            given parameters.
        Args:
            endpoint: MaxCompute endpoint.
            project: A project is a basic organizational unit of MaxCompute, which is
                similar to a database.
            access_id: MaxCompute access ID. Should be passed in directly or set as the
                environment variable `MAX_COMPUTE_ACCESS_ID`.
            secret_access_key: MaxCompute secret access key. Should be passed in
                directly or set as the environment variable
                `MAX_COMPUTE_SECRET_ACCESS_KEY`.
        """
        try:
            from odps import ODPS
        except ImportError as ex:
            raise ImportError(
                "Could not import pyodps python package. "
                "Please install it with `pip install pyodps` or refer to "
                "https://pyodps.readthedocs.io/."
            ) from ex


"https://pyodps.readthedocs.io/."
            ) from ex
        access_id = access_id or get_from_env("access_id", "MAX_COMPUTE_ACCESS_ID")
        secret_access_key = secret_access_key or get_from_env(
            "secret_access_key", "MAX_COMPUTE_SECRET_ACCESS_KEY"
        )
        client = ODPS(
            access_id=access_id,
            secret_access_key=secret_access_key,
            project=project,
            endpoint=endpoint,
        )
        if not client.exist_project(project):
            raise ValueError(f'The project "{project}" does not exist.')
        return cls(client)
[docs]    def lazy_query(self, query: str) -> Iterator[dict]:
        # Execute SQL query.
        with self.client.execute_sql(query).open_reader() as reader:
            if reader.count == 0:
                raise ValueError("Table contains no data.")
            for record in reader:
                yield {k: v for k, v in record}
[docs]    def query(self, query: str) -> List[dict]:
        return list(self.lazy_query(query))


Source code for langchain.utilities.python
import functools
import logging
import multiprocessing
import sys
from io import StringIO
from typing import Dict, Optional
from langchain.pydantic_v1 import BaseModel, Field
logger = logging.getLogger(__name__)
@functools.lru_cache(maxsize=None)
def warn_once() -> None:
    """Warn once about the dangers of PythonREPL."""
    logger.warning("Python REPL can execute arbitrary code. Use with caution.")
[docs]class PythonREPL(BaseModel):
    """Simulates a standalone Python REPL."""
    globals: Optional[Dict] = Field(default_factory=dict, alias="_globals")
    locals: Optional[Dict] = Field(default_factory=dict, alias="_locals")
[docs]    @classmethod
    def worker(
        cls,
        command: str,
        globals: Optional[Dict],
        locals: Optional[Dict],
        queue: multiprocessing.Queue,
    ) -> None:
        old_stdout = sys.stdout
        sys.stdout = mystdout = StringIO()
        try:
            exec(command, globals, locals)
            sys.stdout = old_stdout
            queue.put(mystdout.getvalue())
        except Exception as e:
            sys.stdout = old_stdout
            queue.put(repr(e))
[docs]    def run(self, command: str, timeout: Optional[int] = None) -> str:
        """Run command with own globals/locals and returns anything printed.
        Timeout after the specified number of seconds."""
        # Warn against dangers of PythonREPL
        warn_once()
        queue: multiprocessing.Queue = multiprocessing.Queue()
        # Only use multiprocessing if we are enforcing a timeout
        if timeout is not None:
            # create a Process
            p = multiprocessing.Process(


# create a Process
            p = multiprocessing.Process(
                target=self.worker, args=(command, self.globals, self.locals, queue)
            )
            # start it
            p.start()
            # wait for the process to finish or kill it after timeout seconds
            p.join(timeout)
            if p.is_alive():
                p.terminate()
                return "Execution timed out"
        else:
            self.worker(command, self.globals, self.locals, queue)
        # get the result from the worker function
        return queue.get()


Source code for langchain.utilities.github
"""Util that calls GitHub."""
from __future__ import annotations
import json
from typing import TYPE_CHECKING, Any, Dict, List, Optional
from langchain.pydantic_v1 import BaseModel, Extra, root_validator
from langchain.utils import get_from_dict_or_env
if TYPE_CHECKING:
    from github.Issue import Issue
[docs]class GitHubAPIWrapper(BaseModel):
    """Wrapper for GitHub API."""
    github: Any  #: :meta private:
    github_repo_instance: Any  #: :meta private:
    github_repository: Optional[str] = None
    github_app_id: Optional[str] = None
    github_app_private_key: Optional[str] = None
    github_branch: Optional[str] = None
    github_base_branch: Optional[str] = None
    class Config:
        """Configuration for this pydantic object."""
        extra = Extra.forbid
    @root_validator()
    def validate_environment(cls, values: Dict) -> Dict:
        """Validate that api key and python package exists in environment."""
        github_repository = get_from_dict_or_env(
            values, "github_repository", "GITHUB_REPOSITORY"
        )
        github_app_id = get_from_dict_or_env(values, "github_app_id", "GITHUB_APP_ID")
        github_app_private_key = get_from_dict_or_env(
            values, "github_app_private_key", "GITHUB_APP_PRIVATE_KEY"
        )
        github_branch = get_from_dict_or_env(
            values, "github_branch", "GITHUB_BRANCH", default="master"
        )
        github_base_branch = get_from_dict_or_env(
            values, "github_base_branch", "GITHUB_BASE_BRANCH", default="master"
        )
        try:
            from github import Auth, GithubIntegration


)
        try:
            from github import Auth, GithubIntegration
        except ImportError:
            raise ImportError(
                "PyGithub is not installed. "
                "Please install it with `pip install PyGithub`"
            )
        with open(github_app_private_key, "r") as f:
            private_key = f.read()
        auth = Auth.AppAuth(
            github_app_id,
            private_key,
        )
        gi = GithubIntegration(auth=auth)
        installation = gi.get_installations()[0]
        # create a GitHub instance:
        g = installation.get_github_for_installation()
        values["github"] = g
        values["github_repo_instance"] = g.get_repo(github_repository)
        values["github_repository"] = github_repository
        values["github_app_id"] = github_app_id
        values["github_app_private_key"] = github_app_private_key
        values["github_branch"] = github_branch
        values["github_base_branch"] = github_base_branch
        return values
[docs]    def parse_issues(self, issues: List[Issue]) -> List[dict]:
        """
        Extracts title and number from each Issue and puts them in a dictionary
        Parameters:
            issues(List[Issue]): A list of Github Issue objects
        Returns:
            List[dict]: A dictionary of issue titles and numbers
        """
        parsed = []
        for issue in issues:
            title = issue.title
            number = issue.number
            parsed.append({"title": title, "number": number})
        return parsed
[docs]    def get_issues(self) -> str:
        """
        Fetches all open issues from the repo
        Returns:
            str: A plaintext report containing the number of issues
            and each issue's title and number.
        """


and each issue's title and number.
        """
        issues = self.github_repo_instance.get_issues(state="open")
        if issues.totalCount > 0:
            parsed_issues = self.parse_issues(issues)
            parsed_issues_str = (
                "Found " + str(len(parsed_issues)) + " issues:\n" + str(parsed_issues)
            )
            return parsed_issues_str
        else:
            return "No open issues available"
[docs]    def get_issue(self, issue_number: int) -> Dict[str, Any]:
        """
        Fetches a specific issue and its first 10 comments
        Parameters:
            issue_number(int): The number for the github issue
        Returns:
            dict: A doctionary containing the issue's title,
            body, and comments as a string
        """
        issue = self.github_repo_instance.get_issue(number=issue_number)
        page = 0
        comments: List[dict] = []
        while len(comments) <= 10:
            comments_page = issue.get_comments().get_page(page)
            if len(comments_page) == 0:
                break
            for comment in comments_page:
                comments.append({"body": comment.body, "user": comment.user.login})
            page += 1
        return {
            "title": issue.title,
            "body": issue.body,
            "comments": str(comments),
        }
[docs]    def create_pull_request(self, pr_query: str) -> str:
        """
        Makes a pull request from the bot's branch to the base branch
        Parameters:
            pr_query(str): a string which contains the PR title
            and the PR body. The title is the first line
            in the string, and the body are the rest of the string.


in the string, and the body are the rest of the string.
            For example, "Updated README\nmade changes to add info"
        Returns:
            str: A success or failure message
        """
        if self.github_base_branch == self.github_branch:
            return """Cannot make a pull request because
            commits are already in the master branch"""
        else:
            try:
                title = pr_query.split("\n")[0]
                body = pr_query[len(title) + 2 :]
                pr = self.github_repo_instance.create_pull(
                    title=title,
                    body=body,
                    head=self.github_branch,
                    base=self.github_base_branch,
                )
                return f"Successfully created PR number {pr.number}"
            except Exception as e:
                return "Unable to make pull request due to error:\n" + str(e)
[docs]    def comment_on_issue(self, comment_query: str) -> str:
        """
        Adds a comment to a github issue
        Parameters:
            comment_query(str): a string which contains the issue number,
            two newlines, and the comment.
            for example: "1\n\nWorking on it now"
            adds the comment "working on it now" to issue 1
        Returns:
            str: A success or failure message
        """
        issue_number = int(comment_query.split("\n\n")[0])
        comment = comment_query[len(str(issue_number)) + 2 :]
        try:
            issue = self.github_repo_instance.get_issue(number=issue_number)
            issue.create_comment(comment)
            return "Commented on issue " + str(issue_number)
        except Exception as e:
            return "Unable to make comment due to error:\n" + str(e)


return "Unable to make comment due to error:\n" + str(e)
[docs]    def create_file(self, file_query: str) -> str:
        """
        Creates a new file on the Github repo
        Parameters:
            file_query(str): a string which contains the file path
            and the file contents. The file path is the first line
            in the string, and the contents are the rest of the string.
            For example, "hello_world.md\n# Hello World!"
        Returns:
            str: A success or failure message
        """
        file_path = file_query.split("\n")[0]
        file_contents = file_query[len(file_path) + 2 :]
        try:
            exists = self.github_repo_instance.get_contents(file_path)
            if exists is None:
                self.github_repo_instance.create_file(
                    path=file_path,
                    message="Create " + file_path,
                    content=file_contents,
                    branch=self.github_branch,
                )
                return "Created file " + file_path
            else:
                return f"File already exists at {file_path}. Use update_file instead"
        except Exception as e:
            return "Unable to make file due to error:\n" + str(e)
[docs]    def read_file(self, file_path: str) -> str:
        """
        Reads a file from the github repo
        Parameters:
            file_path(str): the file path
        Returns:
            str: The file decoded as a string
        """
        file = self.github_repo_instance.get_contents(file_path)
        return file.decoded_content.decode("utf-8")
[docs]    def update_file(self, file_query: str) -> str:
        """
        Updates a file with new content.
        Parameters:


"""
        Updates a file with new content.
        Parameters:
            file_query(str): Contains the file path and the file contents.
                The old file contents is wrapped in OLD <<<< and >>>> OLD
                The new file contents is wrapped in NEW <<<< and >>>> NEW
                For example:
                /test/hello.txt
                OLD <<<<
                Hello Earth!
                >>>> OLD
                NEW <<<<
                Hello Mars!
                >>>> NEW
        Returns:
            A success or failure message
        """
        try:
            file_path = file_query.split("\n")[0]
            old_file_contents = (
                file_query.split("OLD <<<<")[1].split(">>>> OLD")[0].strip()
            )
            new_file_contents = (
                file_query.split("NEW <<<<")[1].split(">>>> NEW")[0].strip()
            )
            file_content = self.read_file(file_path)
            updated_file_content = file_content.replace(
                old_file_contents, new_file_contents
            )
            if file_content == updated_file_content:
                return (
                    "File content was not updated because old content was not found."
                    "It may be helpful to use the read_file action to get "
                    "the current file contents."
                )
            self.github_repo_instance.update_file(
                path=file_path,
                message="Update " + file_path,
                content=updated_file_content,
                branch=self.github_branch,
                sha=self.github_repo_instance.get_contents(file_path).sha,
            )
            return "Updated file " + file_path
        except Exception as e:
            return "Unable to update file due to error:\n" + str(e)
[docs]    def delete_file(self, file_path: str) -> str:
        """