Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add ai/ml model and embedding components #2781

Merged
merged 13 commits into from
Jul 18, 2024
77 changes: 77 additions & 0 deletions src/backend/base/langflow/base/models/aiml_constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
CHAT_MODELS = [
"zero-one-ai/Yi-34B-Chat",
"allenai/OLMo-7B-Instruct",
"allenai/OLMo-7B-Twin-2T",
"allenai/OLMo-7B",
"Austism/chronos-hermes-13b",
"cognitivecomputations/dolphin-2.5-mixtral-8x7b",
"deepseek-ai/deepseek-coder-33b-instruct",
"deepseek-ai/deepseek-llm-67b-chat",
"garage-bAInd/Platypus2-70B-instruct",
"google/gemma-2b-it",
"google/gemma-7b-it",
"Gryphe/MythoMax-L2-13b",
"lmsys/vicuna-13b-v1.5",
"lmsys/vicuna-7b-v1.5",
"codellama/CodeLlama-13b-Instruct-hf",
"codellama/CodeLlama-34b-Instruct-hf",
"codellama/CodeLlama-70b-Instruct-hf",
"codellama/CodeLlama-7b-Instruct-hf",
"meta-llama/Llama-2-70b-chat-hf",
"meta-llama/Llama-2-13b-chat-hf",
"meta-llama/Llama-2-7b-chat-hf",
"mistralai/Mistral-7B-Instruct-v0.1",
"mistralai/Mistral-7B-Instruct-v0.2",
"mistralai/Mixtral-8x7B-Instruct-v0.1",
"NousResearch/Nous-Capybara-7B-V1p9",
"NousResearch/Nous-Hermes-2-Mixtral-8x7B-DPO",
"NousResearch/Nous-Hermes-2-Mixtral-8x7B-SFT",
"NousResearch/Nous-Hermes-llama-2-7b",
"NousResearch/Nous-Hermes-Llama2-13b",
"NousResearch/Nous-Hermes-2-Yi-34B",
"openchat/openchat-3.5-1210",
"Open-Orca/Mistral-7B-OpenOrca",
"togethercomputer/Qwen-7B-Chat",
"Qwen/Qwen1.5-0.5B-Chat",
"Qwen/Qwen1.5-1.8B-Chat",
"Qwen/Qwen1.5-4B-Chat",
"Qwen/Qwen1.5-7B-Chat",
"Qwen/Qwen1.5-14B-Chat",
"Qwen/Qwen1.5-72B-Chat",
"snorkelai/Snorkel-Mistral-PairRM-DPO",
"togethercomputer/alpaca-7b",
"teknium/OpenHermes-2-Mistral-7B",
"teknium/OpenHermes-2p5-Mistral-7B",
"togethercomputer/falcon-40b-instruct",
"togethercomputer/falcon-7b-instruct",
"togethercomputer/Llama-2-7B-32K-Instruct",
"togethercomputer/RedPajama-INCITE-Chat-3B-v1",
"togethercomputer/RedPajama-INCITE-7B-Chat",
"togethercomputer/StripedHyena-Nous-7B",
"Undi95/ReMM-SLERP-L2-13B",
"Undi95/Toppy-M-7B",
"WizardLM/WizardLM-13B-V1.2",
"upstage/SOLAR-10.7B-Instruct-v1.0",
"gpt-4",
"gpt-4-turbo",
"gpt-4-0613",
"gpt-4-32k",
"gpt-4-32k-0613",
"gpt-3.5-turbo-0125",
"gpt-3.5-turbo",
"gpt-3.5-turbo-1106",
"gpt-3.5-turbo-instruct",
"gpt-3.5-turbo-16k",
"gpt-3.5-turbo-0613",
"gpt-3.5-turbo-16k-0613",
"gpt-4o",
"claude-3-opus-20240229",
"claude-3-sonnet-20240229",
"claude-3-haiku-20240307",
]

EMBEDDING_MODELS = [
"text-embedding-3-small",
"text-embedding-3-large",
"text-embedding-ada-002",
]
36 changes: 36 additions & 0 deletions src/backend/base/langflow/components/embeddings/AIMLEmbeddings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from typing import Any
from langflow.base.embeddings.model import LCEmbeddingsModel
from langflow.base.models.aiml_constants import EMBEDDING_MODELS
from langflow.components.embeddings.util.AIMLEmbeddingsImpl import AIMLEmbeddingsImpl
from langflow.field_typing import Embeddings
from langflow.inputs.inputs import DropdownInput
from langflow.io import SecretStrInput


class AIMLEmbeddingsComponent(LCEmbeddingsModel):
display_name = "AI/ML Embeddings"
description = "Generate embeddings using the AI/ML API."
icon = "OpenAI" # TODO: icon
anovazzi1 marked this conversation as resolved.
Show resolved Hide resolved
name = "AIMLEmbeddings"

inputs = [
DropdownInput(
name="model_name",
display_name="Model Name",
options=EMBEDDING_MODELS,
required=True,
),
SecretStrInput(
name="aiml_api_key",
display_name="AI/ML API Key",
value="AIML_API_KEY",
required=True,
),
]

def build_embeddings(self) -> Embeddings:
return AIMLEmbeddingsImpl(
api_key=self.aiml_api_key,
model=self.model_name,
chunk_size=self.chunk_size,
)
2 changes: 2 additions & 0 deletions src/backend/base/langflow/components/embeddings/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from .AIMLEmbeddings import AIMLEmbeddingsComponent
from .AmazonBedrockEmbeddings import AmazonBedrockEmbeddingsComponent
from .AstraVectorize import AstraVectorizeComponent
from .AzureOpenAIEmbeddings import AzureOpenAIEmbeddingsComponent
Expand All @@ -9,6 +10,7 @@
from .VertexAIEmbeddings import VertexAIEmbeddingsComponent

__all__ = [
"AIMLEmbeddingsComponent",
"AmazonBedrockEmbeddingsComponent",
"AstraVectorizeComponent",
"AzureOpenAIEmbeddingsComponent",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import json
from typing import Any, List

import httpx
from langflow.field_typing import Embeddings
from langchain_core.runnables.config import run_in_executor
from langchain_core.pydantic_v1 import BaseModel, SecretStr
from loguru import logger

class AIMLEmbeddingsImpl(BaseModel, Embeddings):
embeddings_completion_url: str = "https://api.aimlapi.com/v1/embeddings"

api_key: SecretStr
model: str

def embed_documents(self, texts: List[str]) -> List[List[float]]:
result_vectors = []
for text in texts:
vector = self.embed_query(text)
result_vectors.append(vector)

return result_vectors

def embed_query(self, text: str) -> List[float]:
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {self.api_key.get_secret_value()}",
}

payload = {
"model": self.model,
"input": text,
}
vector = []
try:
response = httpx.post(
self.embeddings_completion_url,
headers=headers,
json=payload,
)
try:
response.raise_for_status()
result_data = response.json()
vector = result_data["data"][0]["embedding"]
except httpx.HTTPStatusError as http_err:
logger.error(f"HTTP error occurred: {http_err}")
raise http_err
except httpx.RequestError as req_err:
logger.error(f"Request error occurred: {req_err}")
raise req_err
except json.JSONDecodeError:
logger.warning(f"Failed to decode JSON, response text: {response.text}")
except KeyError as key_err:
logger.warning(f"Key error: {key_err}, response content: {result_data}")
raise key_err
except httpx.TimeoutException:
logger.error("Request timed out.")
raise
except Exception as exc:
logger.error(f"Error: {exc}")
raise

return vector

async def aembed_documents(self, texts: List[str]) -> List[List[float]]:
return await run_in_executor(None, self.embed_documents, texts)

async def aembed_query(self, text: str) -> List[float]:
return await run_in_executor(None, self.embed_query, text)
Empty file.
151 changes: 151 additions & 0 deletions src/backend/base/langflow/components/models/AIMLModel.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
import json
import httpx
from langflow.base.models.aiml_constants import CHAT_MODELS
from langflow.custom.custom_component.component import Component

from langflow.inputs.inputs import FloatInput, IntInput, MessageInput, SecretStrInput
from langflow.schema.message import Message
from langflow.template.field.base import Output
from loguru import logger
from pydantic.v1 import SecretStr

from langflow.inputs import (
DropdownInput,
StrInput,
)
import httpx


class AIMLModelComponent(Component):
display_name = "AI/ML API"
description = "Generates text using the AI/ML API"
icon = "ChatInput" # TODO: Get their icon.
anovazzi1 marked this conversation as resolved.
Show resolved Hide resolved
chat_completion_url = "https://api.aimlapi.com/v1/chat/completions"

outputs = [
Output(display_name="Text", name="text_output", method="make_request"),
]

inputs = [
DropdownInput(
name="model_name",
display_name="Model Name",
options=CHAT_MODELS,
required=True,
),
SecretStrInput(
name="aiml_api_key",
display_name="AI/ML API Key",
value="AIML_API_KEY",
),
MessageInput(name="input_value", display_name="Input", required=True),
IntInput(
name="max_tokens",
display_name="Max Tokens",
advanced=True,
info="The maximum number of tokens to generate. Set to 0 for unlimited tokens.",
),
StrInput(
name="stop_tokens",
display_name="Stop Tokens",
info="Comma-separated list of tokens to signal the model to stop generating text.",
advanced=True,
),
IntInput(
name="top_k",
display_name="Top K",
info="Limits token selection to top K. (Default: 40)",
advanced=True,
),
FloatInput(
name="top_p",
display_name="Top P",
info="Works together with top-k. (Default: 0.9)",
advanced=True,
),
FloatInput(
name="repeat_penalty",
display_name="Repeat Penalty",
info="Penalty for repetitions in generated text. (Default: 1.1)",
advanced=True,
),
FloatInput(
name="temperature",
display_name="Temperature",
value=0.2,
info="Controls the creativity of model responses.",
),
StrInput(
name="system_message",
display_name="System Message",
info="System message to pass to the model.",
advanced=True,
),
]

def make_request(self) -> Message:
api_key = SecretStr(self.aiml_api_key) if self.aiml_api_key else None

headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {api_key.get_secret_value()}" if api_key else "",
}

messages = []
if self.system_message:
messages.append({"role": "system", "content": self.system_message})

if self.input_value:
if isinstance(self.input_value, Message):
# Though we aren't using langchain here, the helper method is useful
message = self.input_value.to_lc_message()
if message.type == "human":
messages.append({"role": "user", "content": message.content})
else:
raise ValueError(f"Expected user message, saw: {message.type}")
else:
raise TypeError(f"Expected Message type, saw: {type(self.input_value)}")
else:
raise ValueError("Please provide an input value")

payload = {
"model": self.model_name,
"messages": messages,
"max_tokens": self.max_tokens or None,
"temperature": self.temperature or 0.2,
"top_k": self.top_k or 40,
"top_p": self.top_p or 0.9,
"repeat_penalty": self.repeat_penalty or 1.1,
"stop_tokens": self.stop_tokens or None,
}

try:
response = httpx.post(
self.chat_completion_url, headers=headers, json=payload
)
try:
response.raise_for_status()
result_data = response.json()
choice = result_data["choices"][0]
result = choice["message"]["content"]
except httpx.HTTPStatusError as http_err:
logger.error(f"HTTP error occurred: {http_err}")
raise http_err
except httpx.RequestError as req_err:
logger.error(f"Request error occurred: {req_err}")
raise req_err
except json.JSONDecodeError:
logger.warning("Failed to decode JSON, response text: {response.text}")
result = response.text
except KeyError as key_err:
logger.warning(f"Key error: {key_err}, response content: {result_data}")
raise key_err

self.status = result
except httpx.TimeoutException:
return Message(text="Request timed out.")
except Exception as exc:
logger.error(f"Error: {exc}")
raise

return Message(text=result)
4 changes: 2 additions & 2 deletions src/backend/base/langflow/components/models/MistralModel.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ class MistralAIModelComponent(LCModelComponent):
),
),
SecretStrInput(
name="mistral_api_key",
name="api_key",
display_name="Mistral API Key",
info="The Mistral API Key to use for the Mistral model.",
advanced=False,
Expand All @@ -67,7 +67,7 @@ class MistralAIModelComponent(LCModelComponent):
]

def build_model(self) -> LanguageModel: # type: ignore[type-var]
mistral_api_key = self.mistral_api_key
mistral_api_key = self.api_key
temperature = self.temperature
model_name = self.model_name
max_tokens = self.max_tokens
Expand Down
5 changes: 3 additions & 2 deletions src/backend/base/langflow/components/models/OllamaModel.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ def get_model(self, url: str) -> list[str]:
value="http://localhost:11434",
),
DropdownInput(
name="model",
name="model_name",
display_name="Model Name",
value="llama2",
info="Refer to https://ollama.ai/library for more models.",
Expand Down Expand Up @@ -107,6 +107,7 @@ def get_model(self, url: str) -> list[str]:
info="Enable/disable Mirostat sampling for controlling perplexity.",
value="Disabled",
advanced=True,
real_time_refresh=True,
),
FloatInput(
name="mirostat_eta",
Expand Down Expand Up @@ -238,7 +239,7 @@ def build_model(self) -> LanguageModel: # type: ignore[type-var]
# Mapping system settings to their corresponding values
llm_params = {
"base_url": self.base_url,
"model": self.model,
"model": self.model_name,
"mirostat": mirostat_value,
"format": self.format,
"metadata": self.metadata,
Expand Down
Loading
Loading