From e46126eac652d8a085dbc9ac56cf44b5dad05a12 Mon Sep 17 00:00:00 2001 From: Harrison Chase Date: Sun, 23 Jul 2023 09:16:16 -0700 Subject: [PATCH] add llamaapi (#8140) --- .../models/chat/integrations/llama_api.ipynb | 134 +++++++++++++++++ .../langchain_experimental/llms/__init__.py | 3 +- .../langchain_experimental/llms/llamaapi.py | 136 ++++++++++++++++++ 3 files changed, 272 insertions(+), 1 deletion(-) create mode 100644 docs/extras/modules/model_io/models/chat/integrations/llama_api.ipynb create mode 100644 libs/experimental/langchain_experimental/llms/llamaapi.py diff --git a/docs/extras/modules/model_io/models/chat/integrations/llama_api.ipynb b/docs/extras/modules/model_io/models/chat/integrations/llama_api.ipynb new file mode 100644 index 0000000000..4afcdc2fd2 --- /dev/null +++ b/docs/extras/modules/model_io/models/chat/integrations/llama_api.ipynb @@ -0,0 +1,134 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "90a1faf2", + "metadata": {}, + "source": [ + "# Llama API\n", + "\n", + "This notebook shows how to use LangChain with [LlamaAPI](https://llama-api.com/) - a hosted version of Llama2 that adds in support for function calling." + ] + }, + { + "cell_type": "markdown", + "id": "f5b652cf", + "metadata": {}, + "source": [ + "!pip install -U llamaapi" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "bfd385fd", + "metadata": {}, + "outputs": [], + "source": [ + "from llamaapi import LlamaAPI\n", + "\n", + "# Replace 'Your_API_Token' with your actual API token\n", + "llama = LlamaAPI('Your_API_Token')" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "632eb3e5", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/harrisonchase/.pyenv/versions/3.9.1/envs/langchain/lib/python3.9/site-packages/deeplake/util/check_latest_version.py:32: UserWarning: A newer version of deeplake (3.6.12) is available. It's recommended that you update to the latest version using `pip install -U deeplake`.\n", + " warnings.warn(\n" + ] + } + ], + "source": [ + "from langchain_experimental.llms import ChatLlamaAPI" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "6f850e82", + "metadata": {}, + "outputs": [], + "source": [ + "model = ChatLlamaAPI(client=llama)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "975c2bf4", + "metadata": {}, + "outputs": [], + "source": [ + "from langchain.chains import create_tagging_chain\n", + "\n", + "schema = {\n", + " \"properties\": {\n", + " \"sentiment\": {\"type\": \"string\", 'description': 'the sentiment encountered in the passage'},\n", + " \"aggressiveness\": {\"type\": \"integer\", 'description': 'a 0-10 score of how aggressive the passage is'},\n", + " \"language\": {\"type\": \"string\", 'description': 'the language of the passage'},\n", + " }\n", + "}\n", + "\n", + "chain = create_tagging_chain(schema, model)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "ef9638c3", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'sentiment': 'aggressive', 'aggressiveness': 8}" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "chain.run(\"give me your money\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "238b4f62", + "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.9.1" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/libs/experimental/langchain_experimental/llms/__init__.py b/libs/experimental/langchain_experimental/llms/__init__.py index 9bb2e73e23..ae4582198b 100644 --- a/libs/experimental/langchain_experimental/llms/__init__.py +++ b/libs/experimental/langchain_experimental/llms/__init__.py @@ -1,6 +1,7 @@ """Experimental LLM wrappers.""" from langchain_experimental.llms.jsonformer_decoder import JsonFormer +from langchain_experimental.llms.llamaapi import ChatLlamaAPI from langchain_experimental.llms.rellm_decoder import RELLM -__all__ = ["RELLM", "JsonFormer"] +__all__ = ["RELLM", "JsonFormer", "ChatLlamaAPI"] diff --git a/libs/experimental/langchain_experimental/llms/llamaapi.py b/libs/experimental/langchain_experimental/llms/llamaapi.py new file mode 100644 index 0000000000..e5fdff812f --- /dev/null +++ b/libs/experimental/langchain_experimental/llms/llamaapi.py @@ -0,0 +1,136 @@ +import json +import logging +from typing import ( + Any, + Dict, + List, + Mapping, + Optional, + Tuple, +) + +from langchain.callbacks.manager import ( + AsyncCallbackManagerForLLMRun, + CallbackManagerForLLMRun, +) +from langchain.chat_models.base import BaseChatModel +from langchain.schema import ( + ChatGeneration, + ChatResult, +) +from langchain.schema.messages import ( + AIMessage, + BaseMessage, + ChatMessage, + FunctionMessage, + HumanMessage, + SystemMessage, +) + +logger = logging.getLogger(__name__) + + +def _convert_dict_to_message(_dict: Mapping[str, Any]) -> BaseMessage: + role = _dict["role"] + if role == "user": + return HumanMessage(content=_dict["content"]) + elif role == "assistant": + # Fix for azure + # Also OpenAI returns None for tool invocations + content = _dict.get("content") or "" + if _dict.get("function_call"): + _dict["function_call"]["arguments"] = json.dumps( + _dict["function_call"]["arguments"] + ) + additional_kwargs = {"function_call": dict(_dict["function_call"])} + else: + additional_kwargs = {} + return AIMessage(content=content, additional_kwargs=additional_kwargs) + elif role == "system": + return SystemMessage(content=_dict["content"]) + elif role == "function": + return FunctionMessage(content=_dict["content"], name=_dict["name"]) + else: + return ChatMessage(content=_dict["content"], role=role) + + +def _convert_message_to_dict(message: BaseMessage) -> dict: + if isinstance(message, ChatMessage): + message_dict = {"role": message.role, "content": message.content} + elif isinstance(message, HumanMessage): + message_dict = {"role": "user", "content": message.content} + elif isinstance(message, AIMessage): + message_dict = {"role": "assistant", "content": message.content} + if "function_call" in message.additional_kwargs: + message_dict["function_call"] = message.additional_kwargs["function_call"] + elif isinstance(message, SystemMessage): + message_dict = {"role": "system", "content": message.content} + elif isinstance(message, FunctionMessage): + message_dict = { + "role": "function", + "content": message.content, + "name": message.name, + } + else: + raise ValueError(f"Got unknown type {message}") + if "name" in message.additional_kwargs: + message_dict["name"] = message.additional_kwargs["name"] + return message_dict + + +class ChatLlamaAPI(BaseChatModel): + client: Any #: :meta private: + + def _generate( + self, + messages: List[BaseMessage], + stop: Optional[List[str]] = None, + run_manager: Optional[CallbackManagerForLLMRun] = None, + **kwargs: Any, + ) -> ChatResult: + message_dicts, params = self._create_message_dicts(messages, stop) + _params = {"messages": message_dicts} + final_params = {**params, **kwargs, **_params} + response = self.client.run(final_params).json() + return self._create_chat_result(response) + + def _create_message_dicts( + self, messages: List[BaseMessage], stop: Optional[List[str]] + ) -> Tuple[List[Dict[str, Any]], Dict[str, Any]]: + params = dict(self._client_params) + if stop is not None: + if "stop" in params: + raise ValueError("`stop` found in both the input and default params.") + params["stop"] = stop + message_dicts = [_convert_message_to_dict(m) for m in messages] + return message_dicts, params + + def _create_chat_result(self, response: Mapping[str, Any]) -> ChatResult: + generations = [] + for res in response["choices"]: + message = _convert_dict_to_message(res["message"]) + gen = ChatGeneration( + message=message, + generation_info=dict(finish_reason=res.get("finish_reason")), + ) + generations.append(gen) + return ChatResult(generations=generations) + + async def _agenerate( + self, + messages: List[BaseMessage], + stop: Optional[List[str]] = None, + run_manager: Optional[AsyncCallbackManagerForLLMRun] = None, + **kwargs: Any, + ) -> ChatResult: + raise NotImplementedError + + @property + def _client_params(self) -> Mapping[str, Any]: + """Get the parameters used for the client.""" + return {} + + @property + def _llm_type(self) -> str: + """Return type of chat model.""" + return "llama-api"