├── mendable ├── __init__.py └── chatapp.py ├── .gitignore ├── setup.py └── README.md /mendable/__init__.py: -------------------------------------------------------------------------------- 1 | from .chatapp import ChatApp 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | .env 3 | *.whl 4 | *.gz 5 | build/ 6 | dist/ 7 | *.egg-info/ 8 | main.ipynb 9 | __pycache__/ 10 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | 3 | setup( 4 | name='mendable-py', 5 | version='0.0.6', 6 | url='https://github.com/mendableai/mendable-py', 7 | author='Eric Ciarla', 8 | author_email='eric@mendable.ai', 9 | description='Python SDK for Mendable.ai', 10 | packages=find_packages(), 11 | install_requires=[ 12 | 'requests', 13 | ], 14 | ) 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Mendable Python SDK 2 | 3 | This is a Python SDK for Mendable.ai, which provides a Python interface to interact with Mendable.ai's API. 4 | 5 | ## Installation 6 | 7 | To install this package, use pip: 8 | 9 | ```bash 10 | pip install mendable-py 11 | ``` 12 | 13 | ## Prerequisites 14 | 15 | To use this package, you'll need to obtain an API key from Mendable.ai and make it available as an environment variable or set it in the constructor. 16 | 17 | In your environment (add to .env file): 18 | 19 | ```bash 20 | MENDABLE_API_KEY=your_api_key 21 | ``` 22 | 23 | OR 24 | 25 | ```python 26 | my_chat_bot = ChatApp(api_key="your-api-key") 27 | ``` 28 | 29 | 30 | ## Basic Usage 31 | 32 | You can use this package to add sources to Mendable and ask questions to it (history is an option parameter): 33 | 34 | ```python 35 | from mendable import ChatApp 36 | 37 | my_chat_bot = ChatApp(api_key="your-api-key") 38 | 39 | my_chat_bot.add("url", "https://www.mendable.ai/") 40 | 41 | answer = my_chat_bot.ask(question="What is Mendable?", history=[{ "prompt" : "How do I create a new project?", "response" : "You can create a new project by going to the projects page and clicking the new project button." }]) 42 | 43 | print(answer['answer']['text']) 44 | ``` 45 | 46 | Here is what the ask methods response object looks like: 47 | 48 | ```json 49 | { 50 | "answer": { 51 | "text": "This is how to deploy it..." 52 | }, 53 | "message_id": 123, 54 | "sources": [ 55 | { 56 | "id": 866, 57 | "content":"", 58 | "link": "", 59 | "relevance_score": 0.99 60 | }, 61 | ] 62 | } 63 | ``` 64 | 65 | 66 | ## Rate Message 67 | 68 | This is how you can rate a message positive (1) or negative (0). 69 | 70 | ```python 71 | from mendable import ChatApp 72 | 73 | my_chat_bot = ChatApp(api_key="your-api-key") 74 | 75 | my_chat_bot.add("url", "https://www.mendable.ai/") 76 | 77 | answer = my_chat_bot.ask(question="What is Mendable?", history=[{ "prompt" : "How do I create a new project?", "response" : "You can create a new project by going to the projects page and clicking the new project button." }]) 78 | 79 | message_id = answer["message_id"] 80 | 81 | my_chat_bot.rate_message(message_id, 1) 82 | ``` 83 | 84 | ## See all sources for project 85 | 86 | This method lists all unique sources for a project. 87 | 88 | ```python 89 | from mendable import ChatApp 90 | 91 | my_chat_bot = ChatApp(api_key="your-api-key") 92 | 93 | my_chat_bot.add("url", "https://www.mendable.ai/") 94 | 95 | my_chat_bot.get_sources() 96 | 97 | ``` 98 | 99 | The response object looks like this: 100 | ```json 101 | [ 102 | { 103 | "id": 52, 104 | "source": "https://mendable.ai" 105 | }, 106 | .. 107 | ] 108 | ``` 109 | 110 | ## Add and Delete Indexes 111 | 112 | You can also check/delete indexes using `get_sources` and `delete_source` functions: 113 | 114 | ```python 115 | from mendable import ChatApp 116 | 117 | my_chat_bot = ChatApp(api_key="your-api-key") 118 | 119 | my_chat_bot.add("url", "https://www.mendable.ai/") 120 | 121 | my_chat_bot.get_sources() 122 | 123 | my_chat_bot.delete_source("https://www.mendable.ai/") 124 | ``` 125 | 126 | ### Supported ingestion formats and type 127 | 128 | - Website Crawler URL -> "website-crawler" 129 | - Docusaurus site URL -> "docusaurus" 130 | - GitHub Repo URL -> "github" 131 | - YouTube Video URL -> "youtube" 132 | - Single Website URL -> "url" 133 | - Sitemap URL -> "sitemap" 134 | - OpenAPI YAML URL -> "openapi" 135 | 136 | ## Start new conversation 137 | 138 | This method makes a new conversation for a given project 139 | 140 | ```python 141 | from mendable import ChatApp 142 | 143 | my_chat_bot = ChatApp(api_key="your-api-key") 144 | 145 | my_chat_bot.start_new_conversation() 146 | 147 | ``` 148 | 149 | 150 | ## License 151 | 152 | This project is licensed under the terms of the MIT license. 153 | 154 | --- 155 | 156 | Please make sure to replace `your_api_key` with your actual API key and modify any part of this README according to your needs before adding it to your package. 157 | -------------------------------------------------------------------------------- /mendable/chatapp.py: -------------------------------------------------------------------------------- 1 | import os 2 | import requests 3 | import time 4 | 5 | class ChatApp: 6 | def __init__(self, api_key=None): 7 | self.api_key = api_key or os.getenv('MENDABLE_API_KEY') 8 | if self.api_key is None: 9 | raise ValueError('No API key provided') 10 | self.conversation_id = self.start_new_conversation() 11 | self.history = [] 12 | 13 | def get_sources(self): 14 | response = requests.post("https://api.mendable.ai/v0/getSources", json={"api_key": self.api_key}).json() 15 | if response: 16 | return response 17 | else: 18 | raise Exception('Failed to get sources') 19 | 20 | def delete_source(self, source=None, delete_all=False): 21 | if not source and not delete_all: 22 | raise ValueError('Either a source or delete_all must be provided') 23 | response = requests.post("https://api.mendable.ai/v0/deleteSource", json={"api_key": self.api_key, "source": source, "delete_all": delete_all}) 24 | if response: 25 | return response 26 | else: 27 | raise Exception('Failed to delete source(s)') 28 | 29 | def start_new_conversation(self): 30 | new_conversation_response = requests.post("https://api.mendable.ai/v0/newConversation", json={"api_key": self.api_key}).json() 31 | if new_conversation_response.get('conversation_id'): 32 | return new_conversation_response['conversation_id'] 33 | else: 34 | raise Exception('Failed to create a new conversation') 35 | 36 | def add(self, _type, url): 37 | task_id = self._start_ingestion(_type, url) 38 | while True: 39 | status = self._check_ingestion_status(task_id) 40 | if status == 'completed': 41 | print('Ingestion completed.') 42 | break 43 | elif status in ['queued', 'processing', 'pending']: 44 | print(f'Ingestion status: {status}') 45 | time.sleep(2.5) 46 | else: 47 | raise Exception('Unknown ingestion status') 48 | 49 | 50 | def _start_ingestion(self, _type, url): 51 | # this response can return a 400 error and i want to handle it 52 | try: 53 | response = requests.post("https://api.mendable.ai/v0/ingestData", json={"api_key": self.api_key, "url": url, "type": _type}) 54 | # Raise an exception if the request was not successful 55 | response.raise_for_status() 56 | 57 | except requests.exceptions.HTTPError as err: 58 | # Check for 400 status code 59 | if response.status_code == 400: 60 | print("\n"+response.text+"\n") 61 | else: 62 | print('An error occurred: ', err) 63 | raise Exception('Failed to start data ingestion') 64 | response = response.json() 65 | if response.get('task_id'): 66 | return response['task_id'] 67 | else: 68 | raise Exception('Failed to start data ingestion') 69 | 70 | 71 | def _check_ingestion_status(self, task_id): 72 | response = requests.post("https://api.mendable.ai/v0/ingestionStatus", json={"task_id": task_id, "api_key": self.api_key}).json() 73 | status = response.get('status') 74 | if status: 75 | return status 76 | else: 77 | raise Exception('Failed to check ingestion status') 78 | 79 | 80 | # Being depricated in favor of the new ask() method 81 | def query(self, question, history=[]): 82 | response = requests.post("https://api.mendable.ai/v0/mendableChat", json={ 83 | "api_key": self.api_key, 84 | "question": question, 85 | "history": history, 86 | "conversation_id": self.conversation_id, 87 | "shouldStream": False 88 | }).json() 89 | if response.get('answer') and response['answer'].get('text'): 90 | return response['answer']['text'] 91 | else: 92 | raise Exception('Failed to send the question or receive an answer') 93 | 94 | def ask(self, question, history=[]): 95 | response = requests.post("https://api.mendable.ai/v0/mendableChat", json={ 96 | "api_key": self.api_key, 97 | "question": question, 98 | "history": history, 99 | "conversation_id": self.conversation_id, 100 | "shouldStream": False 101 | }).json() 102 | if response.get('answer') and response['answer'].get('text'): 103 | return response 104 | else: 105 | raise Exception('Failed to send the question or receive an answer') 106 | 107 | def rate_message(self, message_id, message_rating): 108 | 109 | response = requests.post("https://api.mendable.ai/v0/rateMessage", json={ 110 | "api_key": self.api_key, 111 | "message_id": int(message_id), 112 | "rating_value": message_rating 113 | }) 114 | 115 | if not response.content: 116 | raise Exception('No content received from the server') 117 | 118 | if response.status_code == 200: 119 | return "Message Rated" # Attempt to parse JSON 120 | else: 121 | raise Exception(f'Failed to send the question or receive an answer. Status code: {response.status_code}') 122 | 123 | 124 | 125 | 126 | 127 | --------------------------------------------------------------------------------