diff --git a/docs/extras/modules/model_io/models/llms/integrations/tongyi.ipynb b/docs/extras/modules/model_io/models/llms/integrations/tongyi.ipynb new file mode 100644 index 00000000000..c8e1b1a5968 --- /dev/null +++ b/docs/extras/modules/model_io/models/llms/integrations/tongyi.ipynb @@ -0,0 +1,169 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Tongyi Qwen\n", + "Tongyi Qwen is a large-scale language model developed by Alibaba's Damo Academy. It is capable of understanding user intent through natural language understanding and semantic analysis, based on user input in natural language. It provides services and assistance to users in different domains and tasks. By providing clear and detailed instructions, you can obtain results that better align with your expectations." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "ExecuteTime": { + "end_time": "2023-07-10T19:55:36.492467Z", + "start_time": "2023-07-10T19:55:34.037914Z" + } + }, + "outputs": [], + "source": [ + "# Install the package\n", + "!pip install dashscope" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "ExecuteTime": { + "end_time": "2023-07-10T19:55:38.553933Z", + "start_time": "2023-07-10T19:55:36.492287Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "········\n" + ] + } + ], + "source": [ + "# Get a new token: https://help.aliyun.com/document_detail/611472.html?spm=a2c4g.2399481.0.0\n", + "from getpass import getpass\n", + "\n", + "DASHSCOPE_API_KEY = getpass()" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "ExecuteTime": { + "end_time": "2023-07-10T19:55:38.554152Z", + "start_time": "2023-07-10T19:55:38.537376Z" + } + }, + "outputs": [], + "source": [ + "import os\n", + "\n", + "os.environ[\"DASHSCOPE_API_KEY\"] = DASHSCOPE_API_KEY" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "ExecuteTime": { + "end_time": "2023-07-10T19:55:39.812664Z", + "start_time": "2023-07-10T19:55:38.540246Z" + } + }, + "outputs": [], + "source": [ + "from langchain.llms import Tongyi\n", + "from langchain import PromptTemplate, LLMChain" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "ExecuteTime": { + "end_time": "2023-07-10T19:55:39.817327Z", + "start_time": "2023-07-10T19:55:39.814825Z" + } + }, + "outputs": [], + "source": [ + "template = \"\"\"Question: {question}\n", + "\n", + "Answer: Let's think step by step.\"\"\"\n", + "\n", + "prompt = PromptTemplate(template=template, input_variables=[\"question\"])" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "llm = Tongyi()" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "llm_chain = LLMChain(prompt=prompt, llm=llm)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"The year Justin Bieber was born was 1994. The Denver Broncos won the Super Bowl in 1997, which means they would have been the team that won the Super Bowl during Justin Bieber's birth year. So the answer is the Denver Broncos.\"" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "question = \"What NFL team won the Super Bowl in the year Justin Beiber was born?\"\n", + "\n", + "llm_chain.run(question)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/langchain/llms/__init__.py b/langchain/llms/__init__.py index 71f16379034..57902853ad4 100644 --- a/langchain/llms/__init__.py +++ b/langchain/llms/__init__.py @@ -50,6 +50,7 @@ from langchain.llms.self_hosted import SelfHostedPipeline from langchain.llms.self_hosted_hugging_face import SelfHostedHuggingFaceLLM from langchain.llms.stochasticai import StochasticAI from langchain.llms.textgen import TextGen +from langchain.llms.tongyi import Tongyi from langchain.llms.vertexai import VertexAI from langchain.llms.writer import Writer @@ -104,6 +105,7 @@ __all__ = [ "SelfHostedHuggingFaceLLM", "SelfHostedPipeline", "StochasticAI", + "Tongyi", "VertexAI", "Writer", "OctoAIEndpoint", @@ -154,6 +156,7 @@ type_to_cls_dict: Dict[str, Type[BaseLLM]] = { "self_hosted": SelfHostedPipeline, "self_hosted_hugging_face": SelfHostedHuggingFaceLLM, "stochasticai": StochasticAI, + "tongyi": Tongyi, "vertexai": VertexAI, "openllm": OpenLLM, "openllm_client": OpenLLM, diff --git a/langchain/llms/tongyi.py b/langchain/llms/tongyi.py new file mode 100644 index 00000000000..39d407d2745 --- /dev/null +++ b/langchain/llms/tongyi.py @@ -0,0 +1,262 @@ +"""Wrapper around Cohere APIs.""" +from __future__ import annotations + +import logging +from typing import Any, Callable, Dict, List, Optional + +from pydantic import Field, root_validator +from requests.exceptions import HTTPError +from tenacity import ( + before_sleep_log, + retry, + retry_if_exception_type, + stop_after_attempt, + wait_exponential, +) + +from langchain.callbacks.manager import ( + AsyncCallbackManagerForLLMRun, + CallbackManagerForLLMRun, +) +from langchain.llms.base import LLM +from langchain.schema import Generation, LLMResult +from langchain.utils import get_from_dict_or_env + +logger = logging.getLogger(__name__) + + +def _create_retry_decorator(llm: Tongyi) -> Callable[[Any], Any]: + min_seconds = 1 + max_seconds = 4 + # Wait 2^x * 1 second between each retry starting with + # 4 seconds, then up to 10 seconds, then 10 seconds afterwards + return retry( + reraise=True, + stop=stop_after_attempt(llm.max_retries), + wait=wait_exponential(multiplier=1, min=min_seconds, max=max_seconds), + retry=(retry_if_exception_type(HTTPError)), + before_sleep=before_sleep_log(logger, logging.WARNING), + ) + + +def generate_with_retry(llm: Tongyi, **kwargs: Any) -> Any: + """Use tenacity to retry the completion call.""" + retry_decorator = _create_retry_decorator(llm) + + @retry_decorator + def _generate_with_retry(**_kwargs: Any) -> Any: + resp = llm.client.call(**_kwargs) + if resp.status_code == 200: + return resp + elif resp.status_code in [400, 401]: + raise ValueError( + f"status_code: {resp.status_code} \n " + f"code: {resp.code} \n message: {resp.message}" + ) + else: + raise HTTPError( + f"HTTP error occurred: status_code: {resp.status_code} \n " + f"code: {resp.code} \n message: {resp.message}" + ) + + return _generate_with_retry(**kwargs) + + +def stream_generate_with_retry(llm: Tongyi, **kwargs: Any) -> Any: + """Use tenacity to retry the completion call.""" + retry_decorator = _create_retry_decorator(llm) + + @retry_decorator + def _stream_generate_with_retry(**_kwargs: Any) -> Any: + stream_resps = [] + resps = llm.client.call(**_kwargs) + for resp in resps: + if resp.status_code == 200: + stream_resps.append(resp) + elif resp.status_code in [400, 401]: + raise ValueError( + f"status_code: {resp.status_code} \n " + f"code: {resp.code} \n message: {resp.message}" + ) + else: + raise HTTPError( + f"HTTP error occurred: status_code: {resp.status_code} \n " + f"code: {resp.code} \n message: {resp.message}" + ) + return stream_resps + + return _stream_generate_with_retry(**kwargs) + + +class Tongyi(LLM): + """Wrapper around Tongyi Qwen large language models. + + To use, you should have the ``dashscope`` python package installed, and the + environment variable ``DASHSCOPE_API_KEY`` set with your API key, or pass + it as a named parameter to the constructor. + + Example: + .. code-block:: python + + from langchain.llms import Tongyi + Tongyi = tongyi() + """ + + @property + def lc_secrets(self) -> Dict[str, str]: + return {"dashscope_api_key": "DASHSCOPE_API_KEY"} + + @property + def lc_serializable(self) -> bool: + return True + + client: Any #: :meta private: + model_name: str = "qwen-plus-v1" + + """Model name to use.""" + model_kwargs: Dict[str, Any] = Field(default_factory=dict) + + top_p: float = 0.8 + """Total probability mass of tokens to consider at each step.""" + + dashscope_api_key: Optional[str] = None + """Dashscope api key provide by alicloud.""" + + n: int = 1 + """How many completions to generate for each prompt.""" + + streaming: bool = False + """Whether to stream the results or not.""" + + max_retries: int = 10 + """Maximum number of retries to make when generating.""" + + prefix_messages: List = Field(default_factory=list) + """Series of messages for Chat input.""" + + @property + def _llm_type(self) -> str: + """Return type of llm.""" + return "tongyi" + + @root_validator() + def validate_environment(cls, values: Dict) -> Dict: + """Validate that api key and python package exists in environment.""" + get_from_dict_or_env(values, "dashscope_api_key", "DASHSCOPE_API_KEY") + try: + import dashscope + except ImportError: + raise ImportError( + "Could not import dashscope python package. " + "Please install it with `pip install dashscope`." + ) + try: + values["client"] = dashscope.Generation + except AttributeError: + raise ValueError( + "`dashscope` has no `Generation` attribute, this is likely " + "due to an old version of the dashscope package. Try upgrading it " + "with `pip install --upgrade dashscope`." + ) + + return values + + @property + def _default_params(self) -> Dict[str, Any]: + """Get the default parameters for calling OpenAI API.""" + normal_params = { + "top_p": self.top_p, + } + + return {**normal_params, **self.model_kwargs} + + def _call( + self, + prompt: str, + stop: Optional[List[str]] = None, + run_manager: Optional[CallbackManagerForLLMRun] = None, + **kwargs: Any, + ) -> str: + """Call out to Tongyi's generate endpoint. + + Args: + prompt: The prompt to pass into the model. + + Returns: + The string generated by the model. + + Example: + .. code-block:: python + + response = tongyi("Tell me a joke.") + """ + params: Dict[str, Any] = { + **{"model": self.model_name}, + **self._default_params, + **kwargs, + } + + completion = generate_with_retry( + self, + prompt=prompt, + **params, + ) + return completion["output"]["text"] + + def _generate( + self, + prompts: List[str], + stop: Optional[List[str]] = None, + run_manager: Optional[CallbackManagerForLLMRun] = None, + **kwargs: Any, + ) -> LLMResult: + generations = [] + params: Dict[str, Any] = { + **{"model": self.model_name}, + **self._default_params, + **kwargs, + } + if self.streaming: + if len(prompts) > 1: + raise ValueError("Cannot stream results with multiple prompts.") + params["stream"] = True + for stream_resp in stream_generate_with_retry( + self, prompt=prompts[0], **params + ): + generations.append( + [ + Generation( + text=stream_resp["output"]["text"], + generation_info=dict( + finish_reason=stream_resp["output"]["finish_reason"], + ), + ) + ] + ) + else: + for prompt in prompts: + completion = generate_with_retry( + self, + prompt=prompt, + **params, + ) + generations.append( + [ + Generation( + text=completion["output"]["text"], + generation_info=dict( + finish_reason=completion["output"]["finish_reason"], + ), + ) + ] + ) + return LLMResult(generations=generations) + + async def _agenerate( + self, + prompts: List[str], + stop: Optional[List[str]] = None, + run_manager: Optional[AsyncCallbackManagerForLLMRun] = None, + **kwargs: Any, + ) -> LLMResult: + raise NotImplementedError() diff --git a/tests/integration_tests/llms/test_tongyi.py b/tests/integration_tests/llms/test_tongyi.py new file mode 100644 index 00000000000..de37ff9861a --- /dev/null +++ b/tests/integration_tests/llms/test_tongyi.py @@ -0,0 +1,27 @@ +"""Test Tongyi API wrapper.""" +from langchain.llms.tongyi import Tongyi +from langchain.schema import LLMResult + + +def test_tongyi_call() -> None: + """Test valid call to tongyi.""" + llm = Tongyi() + output = llm("who are you") + assert isinstance(output, str) + + +def test_tongyi_generate() -> None: + """Test valid call to tongyi.""" + llm = Tongyi() + output = llm.generate(["who are you"]) + assert isinstance(output, LLMResult) + assert isinstance(output.generations, list) + + +def test_tongyi_generate_stream() -> None: + """Test valid call to tongyi.""" + llm = Tongyi(streaming=True) + output = llm.generate(["who are you"]) + print(output) + assert isinstance(output, LLMResult) + assert isinstance(output.generations, list)