Skip to content

Commit

Permalink
Merge pull request #71 from intelligentnode/Release0.2.3
Browse files Browse the repository at this point in the history
Release0.2.3
  • Loading branch information
intelligentnode authored Mar 9, 2024
2 parents c8070cd + 79299d6 commit 1495f31
Show file tree
Hide file tree
Showing 10 changed files with 163 additions and 53 deletions.
12 changes: 8 additions & 4 deletions PIPREADME.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
# Intelli
Create chatbots and AI agent work flows. It allows to connect your data with multiple AI models like OpenAI, Gemini, and Mistral through a unified access layer.

<p>
<a href="https://github.com/intelligentnode/Intelli/blob/release-documentation/LICENSE" alt="licenses tag">
<img src="https://img.shields.io/github/license/intelligentnode/Intelli?style=flat-square" />
Expand All @@ -12,12 +10,18 @@ Create chatbots and AI agent work flows. It allows to connect your data with mul

</p>

# Install
Create chatbots and AI agent work flows. It allows to connect your data with multiple AI models like OpenAI, Gemini, and Mistral through a unified access layer.


```bash
pip install intelli
```

For detailed usage instructions, refer to [intelli documentation](https://docs.intellinode.ai/docs/python).
# Latest changes
- Add Anthropic claude 3 to the chatbot providers.
- Update the agent to send dynamic parameters to the image, vision, and text models.

For detailed instructions, refer to [intelli documentation](https://docs.intellinode.ai/docs/python).

# Code Examples

Expand Down
13 changes: 8 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ For detailed usage instructions, refer to the [documentation](https://doc.intell
Switch between multiple chatbot providers without changing your code.

```python
from intelli.function.chatbot import Chatbot
from intelli.function.chatbot import Chatbot, ChatProvider
from intelli.model.input.chatbot_input import ChatModelInput

def call_chatbot(provider, model=None):
Expand All @@ -44,14 +44,17 @@ def call_chatbot(provider, model=None):

return response

# call openai
call_chatbot("openai", "gpt-4")
# call chatGPT
call_chatbot(ChatProvider.OPENAI, "gpt-4")

# call mistralai
call_chatbot("mistral", "mistral-medium")
call_chatbot(ChatProvider.MISTRAL, "mistral-medium")

# call claude3
call_chatbot(ChatProvider.ANTHROPIC, "claude-3-sonnet-20240229")

# call google gemini
call_chatbot("gemini")
call_chatbot(ChatProvider.GEMINI)
```

## Create AI Flows
Expand Down
1 change: 1 addition & 0 deletions intelli/.example.env
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ REPLICATE_API_KEY=
MISTRAL_API_KEY=
INTELLI_ONE_KEY=
GEMINI_API_KEY=
ANTHROPIC_API_KEY=
KAGGLE_USERNAME=
KAGGLE_API_KEY=
2 changes: 1 addition & 1 deletion intelli/controller/remote_speech_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

SupportedSpeechModels = {
'GOOGLE': 'google',
'OPENAI': 'openAi',
'OPENAI': 'openai',
}


Expand Down
72 changes: 58 additions & 14 deletions intelli/function/chatbot.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,50 @@
from intelli.wrappers.intellicloud_wrapper import IntellicloudWrapper
from intelli.wrappers.mistralai_wrapper import MistralAIWrapper
from intelli.wrappers.openai_wrapper import OpenAIWrapper
from intelli.wrappers.anthropic_wrapper import AnthropicWrapper
from enum import Enum

class ChatProvider(Enum):
OPENAI = "openai"
GEMINI = "gemini"
MISTRAL = "mistral"
ANTHROPIC = "anthropic"

class Chatbot:

def __init__(self, api_key, provider, options=None):
if options is None:
options = {}
self.api_key = api_key
self.provider = provider.lower()
self.provider = self._get_provider(provider)
self.options = options
self.wrapper = self._initialize_provider()
self.extended_search = IntellicloudWrapper(options['one_key'],
options.get('api_base', None)) if 'one_key' in options else None
self.system_helper = SystemHelper()

def _get_provider(self, provider):

if isinstance(provider, str):
provider = provider.lower()
if provider not in (p.value for p in ChatProvider):
raise ValueError(f"Unsupported provider: {provider}")
return provider
elif isinstance(provider, ChatProvider):
return provider.value
else:
raise ValueError(f"Unsupported provider: {provider}")

def _initialize_provider(self):
if self.provider == 'openai':
if self.provider == ChatProvider.OPENAI.value:
proxy_helper = self.options.get('proxy_helper', None)
return OpenAIWrapper(self.api_key, proxy_helper=proxy_helper)
elif self.provider == 'mistral':
elif self.provider == ChatProvider.MISTRAL.value:
return MistralAIWrapper(self.api_key)
elif self.provider == 'gemini':
elif self.provider == ChatProvider.GEMINI.value:
return GeminiAIWrapper(self.api_key)
elif self.provider == ChatProvider.ANTHROPIC.value:
return AnthropicWrapper(self.api_key)
else:
raise ValueError(f"Unsupported provider: {self.provider}")

Expand Down Expand Up @@ -67,21 +88,25 @@ def _chat_gemini(self, params):
raise Exception("Error when calling gemini: {}".format(response))
return output

def _chat_anthropic(self, params):
response = self.wrapper.generate_text(params)

return [message['text'] for message in response['content']]

def stream(self, chat_input):
"""
Streams responses from OpenAI for the given chat input.
Each yielded content is the text content alone, extracted from the streamed response.
"""
if self.provider != 'openai':
raise NotImplementedError("Streaming is only supported for OpenAI.")
"""Streams responses from the selected provider for the given chat input."""

streaming_method = getattr(self, f"_stream_{self.provider}", None)

if not streaming_method:
raise NotImplementedError(f"Streaming is not implemented for {self.provider}.")

if self.extended_search:
_ = self._augment_with_semantic_search(chat_input)

params = chat_input.get_openai_input()
params = getattr(chat_input, f"get_{self.provider}_input")()

for content in self._stream_openai(params):
for content in streaming_method(params):
yield content

def _stream_openai(self, params):
Expand All @@ -101,6 +126,24 @@ def _stream_openai(self, params):
except json.JSONDecodeError as e:
print("Error decoding JSON:", e)

def _stream_anthropic(self, params):
"""Stream text from Anthropic and directly yield text content."""
params['stream'] = True

for line in self.wrapper.stream_text(params):
# process lines starting with 'data:'
if line.startswith("data:"):
try:

json_payload = line[len("data:"):]
line_data = json.loads(json_payload)

if 'type' in line_data and line_data['type'] == 'content_block_delta' and 'text' in line_data[
'delta']:
yield line_data['delta']['text']
except json.JSONDecodeError as e:
print("Error decoding JSON from stream:", e)

# helpers
def _parse_openai_responses(self, results):
responses = []
Expand All @@ -117,7 +160,8 @@ def _augment_with_semantic_search(self, chat_input):
if last_user_message:
# Perform the semantic search based on the last user message.
filters = {'document_name': chat_input.doc_name} if chat_input.doc_name else None
search_results = self.extended_search.semantic_search(last_user_message, chat_input.search_k, filters=filters)
search_results = self.extended_search.semantic_search(last_user_message, chat_input.search_k,
filters=filters)

# Accumulate document names from the search results for references.
references = {}
Expand Down
22 changes: 22 additions & 0 deletions intelli/model/input/chatbot_input.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,25 @@ def get_gemini_input(self):
**self.options
}
return params

def get_anthropic_input(self):
# prepare the messages
system = ""
contents = []
for msg in self.messages:
if msg.role == 'system':
system += msg.content + " "
else:
contents.append({'role': msg.role, 'content': msg.content})

# construct params dictionary
params = {
'model': self.model,
'system': system.strip(), # Use a system prompt
'messages': contents,
'max_tokens': self.max_tokens or 1024,
**({'temperature': self.temperature} if self.temperature is not None else {}),
**self.options,
}

return params
14 changes: 7 additions & 7 deletions intelli/model/input/image_input.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ def __init__(self, prompt, number_images=1, imageSize=None,
diffusion_steps=None, engine=None, model=None):

self.prompt = prompt
self.numberOfImages = number_images
self.number_images = number_images
self.imageSize = imageSize
self.responseFormat = response_format
self.response_format = response_format
self.width = width
self.height = height
self.diffusion_cfgScale = diffusion_cfgScale
Expand All @@ -28,10 +28,10 @@ def __init__(self, prompt, number_images=1, imageSize=None,
def get_openai_inputs(self):
inputs = {
"prompt": self.prompt,
"n": self.numberOfImages,
"n": self.number_images,
"model": self.model,
"size": self.imageSize,
"response_format": self.responseFormat
"response_format": self.response_format
}

inputs = {key: value for key, value in inputs.items() if value is not None}
Expand All @@ -41,7 +41,7 @@ def get_stability_inputs(self):

inputs = {
"text_prompts": [{"text": self.prompt}],
"samples": self.numberOfImages,
"samples": self.number_images,
"height": self.height,
"width": self.width,
"cfg_scale": self.diffusion_cfgScale,
Expand All @@ -55,10 +55,10 @@ def get_stability_inputs(self):

def set_default_values(self, provider):
if provider == "openai":
self.numberOfImages = 1
self.number_images = 1
self.imageSize = '1024x1024'
elif provider == "stability":
self.numberOfImages = 1
self.number_images = 1
self.height = 1024
self.width = 1024
self.engine = 'stable-diffusion-xl-1024-v1-0'
Expand Down
72 changes: 53 additions & 19 deletions intelli/test/integration/test_chatbot.py
Original file line number Diff line number Diff line change
@@ -1,56 +1,70 @@
import unittest
import os
import asyncio
from intelli.function.chatbot import Chatbot
from intelli.function.chatbot import Chatbot, ChatProvider
from intelli.model.input.chatbot_input import ChatModelInput
from dotenv import load_dotenv

load_dotenv()


class TestChatbot(unittest.TestCase):
def setUp(self):
self.openai_api_key = os.getenv("OPENAI_API_KEY")
self.gemini_api_key = os.getenv("GEMINI_API_KEY")
self.mistral_api_key = os.getenv("MISTRAL_API_KEY")

self.anthropic_api_key = os.getenv("ANTHROPIC_API_KEY")

# Creating Chatbot instances for each AI model
self.openai_bot = Chatbot(self.openai_api_key, "openai")
self.gemini_bot = Chatbot(self.gemini_api_key, "gemini")
self.mistral_bot = Chatbot(self.mistral_api_key, "mistral")

self.openai_bot = Chatbot(self.openai_api_key, ChatProvider.OPENAI)
self.gemini_bot = Chatbot(self.gemini_api_key, ChatProvider.GEMINI)
self.mistral_bot = Chatbot(self.mistral_api_key, ChatProvider.MISTRAL)
self.anthropic_bot = Chatbot(self.anthropic_api_key, ChatProvider.ANTHROPIC)

def test_openai_chat(self):
print('---- start openai ----')
input = ChatModelInput("You are a helpful assistant.", "gpt-3.5-turbo")
input.add_user_message("What is the capital of France?")

response = self.openai_bot.chat(input)

print('openai response: ', response)

self.assertTrue(len(response) > 0, "OpenAI chat response should not be empty")

def test_gemini_chat(self):
print('---- start gemini ----')
input = ChatModelInput("You are a helpful assistant.", "gemini-model")
input.add_user_message("Describe a starry night.")

response = self.gemini_bot.chat(input)

print('gemini response: ', response)

self.assertTrue(len(response) > 0, "Gemini chat response should not be empty")


def test_mistral_chat(self):
print('---- start mistral ----')
input = ChatModelInput("You are a helpful assistant.", "mistral-tiny")
input.add_user_message("Who is Leonardo da Vinci?")

response = self.mistral_bot.chat(input)

print('mistral response: ', response)

self.assertTrue(len(response) > 0, "Mistral chat response should not be empty")

def test_anthropic_chat(self):
print('---- start anthropic ----')
input = ChatModelInput("You are a helpful assistant.", "claude-3-sonnet-20240229")
input.add_user_message("What is the capital of France?")

response = self.anthropic_bot.chat(input)

print('- anthropic response: ', response[0])

self.assertTrue(len(response) > 0, "Anthropic chat response should not be empty")

def test_openai_stream(self):
print('---- start openai stream ----')
input = ChatModelInput("You are a helpful assistant.", "gpt-3.5-turbo")
Expand All @@ -64,14 +78,34 @@ def test_openai_stream(self):
self.assertTrue(len(full_text) > 0, "OpenAI stream response should not be empty")

async def _get_openai_stream(self, chat_input):

full_text = ''

for content in self.openai_bot.stream(chat_input):
full_text += content
print('content item: ', content)
print('content item: ', content)

return full_text


def test_anthropic_stream(self):
print('---- start anthropic stream ----')
input = ChatModelInput("You are a helpful assistant.", "claude-3-sonnet-20240229")
input.add_user_message("Give me a detailed explanation of quantum computing.")

# use asyncio.run() to get the result of the coroutine
full_text = asyncio.run(self._get_anthropic_stream(input))

print('anthropic stream response: ', full_text)

self.assertTrue(len(full_text) > 0, "Anthropic stream response should not be empty")

async def _get_anthropic_stream(self, chat_input):
full_text = ''

for content in self.anthropic_bot.stream(chat_input):
full_text += content
print('content item: ', content)

return full_text

if __name__ == '__main__':
unittest.main()
unittest.main()
Loading

0 comments on commit 1495f31

Please sign in to comment.