Skip to content
Snippets Groups Projects
Unverified Commit 610ea58f authored by Luca's avatar Luca Committed by GitHub
Browse files

Merge pull request #35 from aurelio-labs/simonas/upload-multiple-routes-at-once-using-hybridlayer

Embed all utterance at once
parents 9a2a4b8c a8365780
No related branches found
No related tags found
No related merge requests found
<?xml version="1.0" ?> <?xml version="1.0" ?>
<coverage version="7.3.3" timestamp="1702894511196" lines-valid="345" lines-covered="345" line-rate="1" branches-covered="0" branches-valid="0" branch-rate="0" complexity="0"> <coverage version="7.3.3" timestamp="1702906788381" lines-valid="352" lines-covered="352" line-rate="1" branches-covered="0" branches-valid="0" branch-rate="0" complexity="0">
<!-- Generated by coverage.py: https://coverage.readthedocs.io/en/7.3.3 --> <!-- Generated by coverage.py: https://coverage.readthedocs.io/en/7.3.3 -->
<!-- Based on https://raw.githubusercontent.com/cobertura/web/master/htdocs/xml/coverage-04.dtd --> <!-- Based on https://raw.githubusercontent.com/cobertura/web/master/htdocs/xml/coverage-04.dtd -->
<sources> <sources>
...@@ -21,26 +21,24 @@ ...@@ -21,26 +21,24 @@
<lines> <lines>
<line number="1" hits="1"/> <line number="1" hits="1"/>
<line number="2" hits="1"/> <line number="2" hits="1"/>
<line number="3" hits="1"/> <line number="4" hits="1"/>
<line number="5" hits="1"/> <line number="10" hits="1"/>
<line number="11" hits="1"/> <line number="11" hits="1"/>
<line number="12" hits="1"/> <line number="14" hits="1"/>
<line number="15" hits="1"/> <line number="15" hits="1"/>
<line number="16" hits="1"/> <line number="16" hits="1"/>
<line number="17" hits="1"/> <line number="17" hits="1"/>
<line number="18" hits="1"/> <line number="18" hits="1"/>
<line number="19" hits="1"/> <line number="20" hits="1"/>
<line number="21" hits="1"/> <line number="23" hits="1"/>
<line number="24" hits="1"/> <line number="24" hits="1"/>
<line number="25" hits="1"/> <line number="25" hits="1"/>
<line number="26" hits="1"/> <line number="27" hits="1"/>
<line number="28" hits="1"/> <line number="28" hits="1"/>
<line number="29" hits="1"/> <line number="29" hits="1"/>
<line number="30" hits="1"/> <line number="30" hits="1"/>
<line number="31" hits="1"/> <line number="32" hits="1"/>
<line number="33" hits="1"/> <line number="34" hits="1"/>
<line number="35" hits="1"/>
<line number="37" hits="1"/>
<line number="38" hits="1"/> <line number="38" hits="1"/>
<line number="40" hits="1"/> <line number="40" hits="1"/>
<line number="41" hits="1"/> <line number="41" hits="1"/>
...@@ -67,48 +65,58 @@ ...@@ -67,48 +65,58 @@
<line number="76" hits="1"/> <line number="76" hits="1"/>
<line number="78" hits="1"/> <line number="78" hits="1"/>
<line number="80" hits="1"/> <line number="80" hits="1"/>
<line number="85" hits="1"/> <line number="82" hits="1"/>
<line number="83" hits="1"/>
<line number="86" hits="1"/> <line number="86" hits="1"/>
<line number="88" hits="1"/> <line number="87" hits="1"/>
<line number="89" hits="1"/> <line number="90" hits="1"/>
<line number="91" hits="1"/> <line number="91" hits="1"/>
<line number="93" hits="1"/> <line number="92" hits="1"/>
<line number="95" hits="1"/>
<line number="96" hits="1"/>
<line number="97" hits="1"/>
<line number="99" hits="1"/> <line number="99" hits="1"/>
<line number="100" hits="1"/>
<line number="101" hits="1"/>
<line number="102" hits="1"/>
<line number="104" hits="1"/>
<line number="105" hits="1"/>
<line number="106" hits="1"/> <line number="106" hits="1"/>
<line number="108" hits="1"/>
<line number="109" hits="1"/>
<line number="111" hits="1"/>
<line number="112" hits="1"/> <line number="112" hits="1"/>
<line number="114" hits="1"/>
<line number="116" hits="1"/>
<line number="117" hits="1"/> <line number="117" hits="1"/>
<line number="118" hits="1"/> <line number="118" hits="1"/>
<line number="120" hits="1"/> <line number="120" hits="1"/>
<line number="121" hits="1"/> <line number="121" hits="1"/>
<line number="122" hits="1"/>
<line number="123" hits="1"/> <line number="123" hits="1"/>
<line number="124" hits="1"/>
<line number="125" hits="1"/> <line number="125" hits="1"/>
<line number="126" hits="1"/> <line number="127" hits="1"/>
<line number="128" hits="1"/> <line number="128" hits="1"/>
<line number="129" hits="1"/>
<line number="131" hits="1"/> <line number="131" hits="1"/>
<line number="132" hits="1"/> <line number="132" hits="1"/>
<line number="135" hits="1"/> <line number="133" hits="1"/>
<line number="134" hits="1"/>
<line number="136" hits="1"/> <line number="136" hits="1"/>
<line number="137" hits="1"/>
<line number="138" hits="1"/> <line number="138" hits="1"/>
<line number="139" hits="1"/> <line number="140" hits="1"/>
<line number="141" hits="1"/> <line number="141" hits="1"/>
<line number="142" hits="1"/>
<line number="143" hits="1"/> <line number="143" hits="1"/>
<line number="145" hits="1"/> <line number="144" hits="1"/>
<line number="146" hits="1"/>
<line number="148" hits="1"/>
<line number="149" hits="1"/>
<line number="150" hits="1"/>
<line number="152" hits="1"/>
<line number="153" hits="1"/>
<line number="154" hits="1"/>
<line number="155" hits="1"/>
<line number="156" hits="1"/>
<line number="157" hits="1"/>
<line number="158" hits="1"/>
<line number="160" hits="1"/>
<line number="163" hits="1"/>
<line number="164" hits="1"/>
<line number="167" hits="1"/>
<line number="168" hits="1"/>
<line number="170" hits="1"/>
<line number="171" hits="1"/>
<line number="173" hits="1"/>
<line number="174" hits="1"/>
<line number="175" hits="1"/>
<line number="177" hits="1"/>
</lines> </lines>
</class> </class>
<class name="layer.py" filename="layer.py" complexity="0" line-rate="1" branch-rate="0"> <class name="layer.py" filename="layer.py" complexity="0" line-rate="1" branch-rate="0">
...@@ -405,19 +413,18 @@ ...@@ -405,19 +413,18 @@
<line number="8" hits="1"/> <line number="8" hits="1"/>
<line number="23" hits="1"/> <line number="23" hits="1"/>
<line number="24" hits="1"/> <line number="24" hits="1"/>
<line number="25" hits="1"/>
<line number="26" hits="1"/> <line number="26" hits="1"/>
<line number="27" hits="1"/> <line number="27" hits="1"/>
<line number="29" hits="1"/> <line number="28" hits="1"/>
<line number="31" hits="1"/>
<line number="32" hits="1"/>
<line number="33" hits="1"/>
<line number="35" hits="1"/> <line number="35" hits="1"/>
<line number="37" hits="1"/> <line number="37" hits="1"/>
<line number="38" hits="1"/>
<line number="40" hits="1"/> <line number="40" hits="1"/>
<line number="41" hits="1"/> <line number="43" hits="1"/>
<line number="42" hits="1"/>
<line number="44" hits="1"/>
<line number="46" hits="1"/>
<line number="47" hits="1"/>
<line number="49" hits="1"/>
<line number="52" hits="1"/>
</lines> </lines>
</class> </class>
</classes> </classes>
......
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
## Define LLMs ## Define LLMs
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
# OpenAI # OpenAI
import openai import openai
from semantic_router.utils.logger import logger from semantic_router.utils.logger import logger
# Docs # https://platform.openai.com/docs/guides/function-calling # Docs # https://platform.openai.com/docs/guides/function-calling
def llm_openai(prompt: str, model: str = "gpt-4") -> str: def llm_openai(prompt: str, model: str = "gpt-4") -> str:
try: try:
logger.info(f"Calling {model} model") logger.info(f"Calling {model} model")
response = openai.chat.completions.create( response = openai.chat.completions.create(
model=model, model=model,
messages=[ messages=[
{"role": "system", "content": f"{prompt}"}, {"role": "system", "content": f"{prompt}"},
], ],
) )
ai_message = response.choices[0].message.content ai_message = response.choices[0].message.content
if not ai_message: if not ai_message:
raise Exception("AI message is empty", ai_message) raise Exception("AI message is empty", ai_message)
logger.info(f"AI message: {ai_message}") logger.info(f"AI message: {ai_message}")
return ai_message return ai_message
except Exception as e: except Exception as e:
raise Exception("Failed to call OpenAI API", e) raise Exception("Failed to call OpenAI API", e)
``` ```
%% Output
/Users/jakit/customers/aurelio/semantic-router/.venv/lib/python3.11/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html
from .autonotebook import tqdm as notebook_tqdm
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
# Mistral # Mistral
import os import os
import requests import requests
# Docs https://huggingface.co/docs/transformers/main_classes/text_generation # Docs https://huggingface.co/docs/transformers/main_classes/text_generation
HF_API_TOKEN = os.getenv("HF_API_TOKEN") HF_API_TOKEN = os.getenv("HF_API_TOKEN")
def llm_mistral(prompt: str) -> str: def llm_mistral(prompt: str) -> str:
api_url = "https://z5t4cuhg21uxfmc3.us-east-1.aws.endpoints.huggingface.cloud/" api_url = "https://z5t4cuhg21uxfmc3.us-east-1.aws.endpoints.huggingface.cloud/"
headers = { headers = {
"Authorization": f"Bearer {HF_API_TOKEN}", "Authorization": f"Bearer {HF_API_TOKEN}",
"Content-Type": "application/json", "Content-Type": "application/json",
} }
logger.info("Calling Mistral model") logger.info("Calling Mistral model")
response = requests.post( response = requests.post(
api_url, api_url,
headers=headers, headers=headers,
json={ json={
"inputs": f"You are a helpful assistant, user query: {prompt}", "inputs": f"You are a helpful assistant, user query: {prompt}",
"parameters": { "parameters": {
"max_new_tokens": 200, "max_new_tokens": 200,
"temperature": 0.01, "temperature": 0.01,
"num_beams": 5, "num_beams": 5,
"num_return_sequences": 1, "num_return_sequences": 1,
}, },
}, },
) )
if response.status_code != 200: if response.status_code != 200:
raise Exception("Failed to call HuggingFace API", response.text) raise Exception("Failed to call HuggingFace API", response.text)
ai_message = response.json()[0]["generated_text"] ai_message = response.json()[0]["generated_text"]
if not ai_message: if not ai_message:
raise Exception("AI message is empty", ai_message) raise Exception("AI message is empty", ai_message)
logger.info(f"AI message: {ai_message}") logger.info(f"AI message: {ai_message}")
return ai_message return ai_message
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
### Now we need to generate config from function schema using LLM ### Now we need to generate config from function schema using LLM
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
import inspect import inspect
from typing import Any from typing import Any
def get_function_schema(function) -> dict[str, Any]: def get_function_schema(function) -> dict[str, Any]:
schema = { schema = {
"name": function.__name__, "name": function.__name__,
"description": str(inspect.getdoc(function)), "description": str(inspect.getdoc(function)),
"signature": str(inspect.signature(function)), "signature": str(inspect.signature(function)),
"output": str( "output": str(
inspect.signature(function).return_annotation, inspect.signature(function).return_annotation,
), ),
} }
return schema return schema
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
import json import json
def is_valid_config(route_config_str: str) -> bool: def is_valid_config(route_config_str: str) -> bool:
try: try:
output_json = json.loads(route_config_str) output_json = json.loads(route_config_str)
return all(key in output_json for key in ["name", "utterances"]) return all(key in output_json for key in ["name", "utterances"])
except json.JSONDecodeError: except json.JSONDecodeError:
return False return False
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
import json import json
from semantic_router.utils.logger import logger from semantic_router.utils.logger import logger
def generate_route(function) -> dict: def generate_route(function) -> dict:
logger.info("Generating config...") logger.info("Generating config...")
function_schema = get_function_schema(function) function_schema = get_function_schema(function)
prompt = f""" prompt = f"""
You are tasked to generate a JSON configuration based on the provided You are tasked to generate a JSON configuration based on the provided
function schema. Please follow the template below: function schema. Please follow the template below:
{{ {{
"name": "<function_name>", "name": "<function_name>",
"utterances": [ "utterances": [
"<example_utterance_1>", "<example_utterance_1>",
"<example_utterance_2>", "<example_utterance_2>",
"<example_utterance_3>", "<example_utterance_3>",
"<example_utterance_4>", "<example_utterance_4>",
"<example_utterance_5>"] "<example_utterance_5>"]
}} }}
Only include the "name" and "utterances" keys in your answer. Only include the "name" and "utterances" keys in your answer.
The "name" should match the function name and the "utterances" The "name" should match the function name and the "utterances"
should comprise a list of 5 example phrases that could be used to invoke should comprise a list of 5 example phrases that could be used to invoke
the function. the function.
Input schema: Input schema:
{function_schema} {function_schema}
""" """
try: try:
ai_message = llm_mistral(prompt) ai_message = llm_mistral(prompt)
# Parse the response # Parse the response
ai_message = ai_message[ai_message.find("{") :] ai_message = ai_message[ai_message.find("{") :]
ai_message = ( ai_message = (
ai_message.replace("'", '"') ai_message.replace("'", '"')
.replace('"s', "'s") .replace('"s', "'s")
.strip() .strip()
.rstrip(",") .rstrip(",")
.replace("}", "}") .replace("}", "}")
) )
valid_config = is_valid_config(ai_message) valid_config = is_valid_config(ai_message)
if not valid_config: if not valid_config:
logger.warning(f"Mistral failed with error, falling back to OpenAI") logger.warning(f"Mistral failed with error, falling back to OpenAI")
ai_message = llm_openai(prompt) ai_message = llm_openai(prompt)
if not is_valid_config(ai_message): if not is_valid_config(ai_message):
raise Exception("Invalid config generated") raise Exception("Invalid config generated")
except Exception as e: except Exception as e:
logger.error(f"Fall back to OpenAI failed with error {e}") logger.error(f"Fall back to OpenAI failed with error {e}")
ai_message = llm_openai(prompt) ai_message = llm_openai(prompt)
if not is_valid_config(ai_message): if not is_valid_config(ai_message):
raise Exception("Failed to generate config") raise Exception("Failed to generate config")
try: try:
route_config = json.loads(ai_message) route_config = json.loads(ai_message)
logger.info(f"Generated config: {route_config}") logger.info(f"Generated config: {route_config}")
return route_config return route_config
except json.JSONDecodeError as json_error: except json.JSONDecodeError as json_error:
logger.error(f"JSON parsing error {json_error}") logger.error(f"JSON parsing error {json_error}")
print(f"AI message: {ai_message}") print(f"AI message: {ai_message}")
return {"error": "Failed to generate config"} return {"error": "Failed to generate config"}
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Extract function parameters using `Mistral` open-source model Extract function parameters using `Mistral` open-source model
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
def validate_parameters(function, parameters): def validate_parameters(function, parameters):
sig = inspect.signature(function) sig = inspect.signature(function)
for name, param in sig.parameters.items(): for name, param in sig.parameters.items():
if name not in parameters: if name not in parameters:
return False, f"Parameter {name} missing from query" return False, f"Parameter {name} missing from query"
if not isinstance(parameters[name], param.annotation): if not isinstance(parameters[name], param.annotation):
return False, f"Parameter {name} is not of type {param.annotation}" return False, f"Parameter {name} is not of type {param.annotation}"
return True, "Parameters are valid" return True, "Parameters are valid"
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
def extract_parameters(query: str, function) -> dict: def extract_parameters(query: str, function) -> dict:
logger.info("Extracting parameters...") logger.info("Extracting parameters...")
example_query = "How is the weather in Hawaii right now in International units?" example_query = "How is the weather in Hawaii right now in International units?"
example_schema = { example_schema = {
"name": "get_weather", "name": "get_weather",
"description": "Useful to get the weather in a specific location", "description": "Useful to get the weather in a specific location",
"signature": "(location: str, degree: str) -> str", "signature": "(location: str, degree: str) -> str",
"output": "<class 'str'>", "output": "<class 'str'>",
} }
example_parameters = { example_parameters = {
"location": "London", "location": "London",
"degree": "Celsius", "degree": "Celsius",
} }
prompt = f""" prompt = f"""
You are a helpful assistant designed to output JSON. You are a helpful assistant designed to output JSON.
Given the following function schema Given the following function schema
<< {get_function_schema(function)} >> << {get_function_schema(function)} >>
and query and query
<< {query} >> << {query} >>
extract the parameters values from the query, in a valid JSON format. extract the parameters values from the query, in a valid JSON format.
Example: Example:
Input: Input:
query: {example_query} query: {example_query}
schema: {example_schema} schema: {example_schema}
Result: {example_parameters} Result: {example_parameters}
Input: Input:
query: {query} query: {query}
schema: {get_function_schema(function)} schema: {get_function_schema(function)}
Result: Result:
""" """
try: try:
ai_message = llm_mistral(prompt) ai_message = llm_mistral(prompt)
ai_message = ( ai_message = (
ai_message.replace("Output:", "").replace("'", '"').strip().rstrip(",") ai_message.replace("Output:", "").replace("'", '"').strip().rstrip(",")
) )
except Exception as e: except Exception as e:
logger.error(f"Mistral failed with error {e}, falling back to OpenAI") logger.error(f"Mistral failed with error {e}, falling back to OpenAI")
ai_message = llm_openai(prompt) ai_message = llm_openai(prompt)
try: try:
parameters = json.loads(ai_message) parameters = json.loads(ai_message)
valid, message = validate_parameters(function, parameters) valid, message = validate_parameters(function, parameters)
if not valid: if not valid:
logger.warning( logger.warning(
f"Invalid parameters from Mistral, falling back to OpenAI: {message}" f"Invalid parameters from Mistral, falling back to OpenAI: {message}"
) )
# Fall back to OpenAI # Fall back to OpenAI
ai_message = llm_openai(prompt) ai_message = llm_openai(prompt)
parameters = json.loads(ai_message) parameters = json.loads(ai_message)
valid, message = validate_parameters(function, parameters) valid, message = validate_parameters(function, parameters)
if not valid: if not valid:
raise ValueError(message) raise ValueError(message)
logger.info(f"Extracted parameters: {parameters}") logger.info(f"Extracted parameters: {parameters}")
return parameters return parameters
except ValueError as e: except ValueError as e:
logger.error(f"Parameter validation error: {str(e)}") logger.error(f"Parameter validation error: {str(e)}")
return {"error": "Failed to validate parameters"} return {"error": "Failed to validate parameters"}
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Set up the routing layer Set up the routing layer
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
from semantic_router.schema import Route from semantic_router.schema import Route
from semantic_router.encoders import CohereEncoder from semantic_router.encoders import CohereEncoder
from semantic_router.layer import RouteLayer from semantic_router.layer import RouteLayer
from semantic_router.utils.logger import logger from semantic_router.utils.logger import logger
def create_router(routes: list[dict]) -> RouteLayer: def create_router(routes: list[dict]) -> RouteLayer:
logger.info("Creating route layer...") logger.info("Creating route layer...")
encoder = CohereEncoder() encoder = CohereEncoder()
route_list: list[Route] = [] route_list: list[Route] = []
for route in routes: for route in routes:
if "name" in route and "utterances" in route: if "name" in route and "utterances" in route:
print(f"Route: {route}") print(f"Route: {route}")
route_list.append(Route(name=route["name"], utterances=route["utterances"])) route_list.append(Route(name=route["name"], utterances=route["utterances"]))
else: else:
logger.warning(f"Misconfigured route: {route}") logger.warning(f"Misconfigured route: {route}")
return RouteLayer(encoder=encoder, routes=route_list) return RouteLayer(encoder=encoder, routes=route_list)
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Set up calling functions Set up calling functions
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
from typing import Callable from typing import Callable
from semantic_router.layer import RouteLayer from semantic_router.layer import RouteLayer
def call_function(function: Callable, parameters: dict[str, str]): def call_function(function: Callable, parameters: dict[str, str]):
try: try:
return function(**parameters) return function(**parameters)
except TypeError as e: except TypeError as e:
logger.error(f"Error calling function: {e}") logger.error(f"Error calling function: {e}")
def call_llm(query: str) -> str: def call_llm(query: str) -> str:
try: try:
ai_message = llm_mistral(query) ai_message = llm_mistral(query)
except Exception as e: except Exception as e:
logger.error(f"Mistral failed with error {e}, falling back to OpenAI") logger.error(f"Mistral failed with error {e}, falling back to OpenAI")
ai_message = llm_openai(query) ai_message = llm_openai(query)
return ai_message return ai_message
def call(query: str, functions: list[Callable], router: RouteLayer): def call(query: str, functions: list[Callable], router: RouteLayer):
function_name = router(query) function_name = router(query)
if not function_name: if not function_name:
logger.warning("No function found") logger.warning("No function found")
return call_llm(query) return call_llm(query)
for function in functions: for function in functions:
if function.__name__ == function_name: if function.__name__ == function_name:
parameters = extract_parameters(query, function) parameters = extract_parameters(query, function)
print(f"parameters: {parameters}") print(f"parameters: {parameters}")
return call_function(function, parameters) return call_function(function, parameters)
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
### Workflow ### Workflow
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
def get_time(location: str) -> str: def get_time(location: str) -> str:
"""Useful to get the time in a specific location""" """Useful to get the time in a specific location"""
print(f"Calling `get_time` function with location: {location}") print(f"Calling `get_time` function with location: {location}")
return "get_time" return "get_time"
def get_news(category: str, country: str) -> str: def get_news(category: str, country: str) -> str:
"""Useful to get the news in a specific country""" """Useful to get the news in a specific country"""
print( print(
f"Calling `get_news` function with category: {category} and country: {country}" f"Calling `get_news` function with category: {category} and country: {country}"
) )
return "get_news" return "get_news"
# Registering functions to the router # Registering functions to the router
route_get_time = generate_route(get_time) route_get_time = generate_route(get_time)
route_get_news = generate_route(get_news) route_get_news = generate_route(get_news)
routes = [route_get_time, route_get_news] routes = [route_get_time, route_get_news]
router = create_router(routes) router = create_router(routes)
# Tools # Tools
tools = [get_time, get_news] tools = [get_time, get_news]
``` ```
%% Output %% Output
2023-12-18 12:17:58 INFO semantic_router.utils.logger Generating config... 2023-12-18 14:47:47 INFO semantic_router.utils.logger Generating config...
2023-12-18 12:17:58 INFO semantic_router.utils.logger Calling Mistral model 2023-12-18 14:47:47 INFO semantic_router.utils.logger Calling Mistral model
2023-12-18 12:18:00 ERROR semantic_router.utils.logger Fall back to OpenAI failed with error ('Failed to call HuggingFace API', '{"error":"Bad Gateway"}') 2023-12-18 14:47:50 INFO semantic_router.utils.logger AI message:
2023-12-18 12:18:00 INFO semantic_router.utils.logger Calling gpt-4 model Example output:
2023-12-18 12:18:05 INFO semantic_router.utils.logger AI message: { {
"name": "get_time", "name": "get_time",
"utterances": [ "utterances": [
"what is the time in new york", "What's the time in New York?",
"can you tell me the time in london", "Tell me the time in Tokyo.",
"get me the current time in tokyo", "Can you give me the time in London?",
"i need to know the time in sydney", "What's the current time in Sydney?",
"please tell me the current time in paris" "Can you tell me the time in Berlin?"
] ]
} }
2023-12-18 12:18:05 INFO semantic_router.utils.logger Generated config: {'name': 'get_time', 'utterances': ['what is the time in new york', 'can you tell me the time in london', 'get me the current time in tokyo', 'i need to know the time in sydney', 'please tell me the current time in paris']} 2023-12-18 14:47:50 INFO semantic_router.utils.logger Generated config: {'name': 'get_time', 'utterances': ["What's the time in New York?", 'Tell me the time in Tokyo.', 'Can you give me the time in London?', "What's the current time in Sydney?", 'Can you tell me the time in Berlin?']}
2023-12-18 12:18:05 INFO semantic_router.utils.logger Generating config... 2023-12-18 14:47:50 INFO semantic_router.utils.logger Generating config...
2023-12-18 12:18:05 INFO semantic_router.utils.logger Calling Mistral model 2023-12-18 14:47:50 INFO semantic_router.utils.logger Calling Mistral model
2023-12-18 12:18:07 ERROR semantic_router.utils.logger Fall back to OpenAI failed with error ('Failed to call HuggingFace API', '{"error":"Bad Gateway"}') 2023-12-18 14:47:54 INFO semantic_router.utils.logger AI message:
2023-12-18 12:18:07 INFO semantic_router.utils.logger Calling gpt-4 model Example output:
2023-12-18 12:18:12 INFO semantic_router.utils.logger AI message: { {
"name": "get_news", "name": "get_news",
"utterances": [ "utterances": [
"Can I get the latest news in Canada?", "Tell me the latest news from the US",
"Show me the recent news in the US", "What's happening in India today?",
"I would like to know about the sports news in England", "Get me the top stories from Japan",
"Let's check the technology news in Japan", "Can you give me the breaking news from Brazil?",
"Show me the health related news in Germany" "What's the latest news from Germany?"
] ]
} }
2023-12-18 12:18:12 INFO semantic_router.utils.logger Generated config: {'name': 'get_news', 'utterances': ['Can I get the latest news in Canada?', 'Show me the recent news in the US', 'I would like to know about the sports news in England', "Let's check the technology news in Japan", 'Show me the health related news in Germany']} 2023-12-18 14:47:54 INFO semantic_router.utils.logger Generated config: {'name': 'get_news', 'utterances': ['Tell me the latest news from the US', "What's happening in India today?", 'Get me the top stories from Japan', 'Can you give me the breaking news from Brazil?', "What's the latest news from Germany?"]}
2023-12-18 12:18:12 INFO semantic_router.utils.logger Creating route layer... 2023-12-18 14:47:54 INFO semantic_router.utils.logger Creating route layer...
Route: {'name': 'get_time', 'utterances': ['what is the time in new york', 'can you tell me the time in london', 'get me the current time in tokyo', 'i need to know the time in sydney', 'please tell me the current time in paris']} Route: {'name': 'get_time', 'utterances': ["What's the time in New York?", 'Tell me the time in Tokyo.', 'Can you give me the time in London?', "What's the current time in Sydney?", 'Can you tell me the time in Berlin?']}
Route: {'name': 'get_news', 'utterances': ['Can I get the latest news in Canada?', 'Show me the recent news in the US', 'I would like to know about the sports news in England', "Let's check the technology news in Japan", 'Show me the health related news in Germany']} Route: {'name': 'get_news', 'utterances': ['Tell me the latest news from the US', "What's happening in India today?", 'Get me the top stories from Japan', 'Can you give me the breaking news from Brazil?', "What's the latest news from Germany?"]}
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
def get_time(location: str) -> str: def get_time(location: str) -> str:
"""Useful to get the time in a specific location""" """Useful to get the time in a specific location"""
print(f"Calling `get_time` function with location: {location}") print(f"Calling `get_time` function with location: {location}")
return "get_time" return "get_time"
def get_news(category: str, country: str) -> str: def get_news(category: str, country: str) -> str:
"""Useful to get the news in a specific country""" """Useful to get the news in a specific country"""
print( print(
f"Calling `get_news` function with category: {category} and country: {country}" f"Calling `get_news` function with category: {category} and country: {country}"
) )
return "get_news" return "get_news"
# Registering functions to the router # Registering functions to the router
route_get_time = generate_route(get_time) route_get_time = generate_route(get_time)
route_get_news = generate_route(get_news) route_get_news = generate_route(get_news)
routes = [route_get_time, route_get_news] routes = [route_get_time, route_get_news]
router = create_router(routes) router = create_router(routes)
# Tools # Tools
tools = [get_time, get_news] tools = [get_time, get_news]
``` ```
%% Output %% Output
2023-12-18 12:20:12 INFO semantic_router.utils.logger Generating config... 2023-12-18 14:47:55 INFO semantic_router.utils.logger Generating config...
2023-12-18 12:20:12 INFO semantic_router.utils.logger Calling Mistral model 2023-12-18 14:47:55 INFO semantic_router.utils.logger Calling Mistral model
2023-12-18 12:20:16 INFO semantic_router.utils.logger AI message: 2023-12-18 14:47:58 INFO semantic_router.utils.logger AI message:
Example output: Example output:
{ {
"name": "get_time", "name": "get_time",
"utterances": [ "utterances": [
"What's the time in New York?", "What's the time in New York?",
"Tell me the time in Tokyo.", "Tell me the time in Tokyo.",
"Can you give me the time in London?", "Can you give me the time in London?",
"What's the current time in Sydney?", "What's the current time in Sydney?",
"Can you tell me the time in Berlin?" "Can you tell me the time in Berlin?"
] ]
} }
2023-12-18 12:20:16 INFO semantic_router.utils.logger Generated config: {'name': 'get_time', 'utterances': ["What's the time in New York?", 'Tell me the time in Tokyo.', 'Can you give me the time in London?', "What's the current time in Sydney?", 'Can you tell me the time in Berlin?']} 2023-12-18 14:47:58 INFO semantic_router.utils.logger Generated config: {'name': 'get_time', 'utterances': ["What's the time in New York?", 'Tell me the time in Tokyo.', 'Can you give me the time in London?', "What's the current time in Sydney?", 'Can you tell me the time in Berlin?']}
2023-12-18 12:20:16 INFO semantic_router.utils.logger Generating config... 2023-12-18 14:47:58 INFO semantic_router.utils.logger Generating config...
2023-12-18 12:20:16 INFO semantic_router.utils.logger Calling Mistral model 2023-12-18 14:47:58 INFO semantic_router.utils.logger Calling Mistral model
2023-12-18 12:20:20 INFO semantic_router.utils.logger AI message: 2023-12-18 14:48:02 INFO semantic_router.utils.logger AI message:
Example output: Example output:
{ {
"name": "get_news", "name": "get_news",
"utterances": [ "utterances": [
"Tell me the latest news from the US", "Tell me the latest news from the US",
"What's happening in India today?", "What's happening in India today?",
"Get me the top stories from Japan", "Get me the top stories from Japan",
"Can you give me the breaking news from Brazil?", "Can you give me the breaking news from Brazil?",
"What's the latest news from Germany?" "What's the latest news from Germany?"
] ]
} }
2023-12-18 12:20:20 INFO semantic_router.utils.logger Generated config: {'name': 'get_news', 'utterances': ['Tell me the latest news from the US', "What's happening in India today?", 'Get me the top stories from Japan', 'Can you give me the breaking news from Brazil?', "What's the latest news from Germany?"]} 2023-12-18 14:48:02 INFO semantic_router.utils.logger Generated config: {'name': 'get_news', 'utterances': ['Tell me the latest news from the US', "What's happening in India today?", 'Get me the top stories from Japan', 'Can you give me the breaking news from Brazil?', "What's the latest news from Germany?"]}
2023-12-18 12:20:20 INFO semantic_router.utils.logger Creating route layer... 2023-12-18 14:48:02 INFO semantic_router.utils.logger Creating route layer...
Route: {'name': 'get_time', 'utterances': ["What's the time in New York?", 'Tell me the time in Tokyo.', 'Can you give me the time in London?', "What's the current time in Sydney?", 'Can you tell me the time in Berlin?']} Route: {'name': 'get_time', 'utterances': ["What's the time in New York?", 'Tell me the time in Tokyo.', 'Can you give me the time in London?', "What's the current time in Sydney?", 'Can you tell me the time in Berlin?']}
Route: {'name': 'get_news', 'utterances': ['Tell me the latest news from the US', "What's happening in India today?", 'Get me the top stories from Japan', 'Can you give me the breaking news from Brazil?', "What's the latest news from Germany?"]} Route: {'name': 'get_news', 'utterances': ['Tell me the latest news from the US', "What's happening in India today?", 'Get me the top stories from Japan', 'Can you give me the breaking news from Brazil?', "What's the latest news from Germany?"]}
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
call(query="What is the time in Stockholm?", functions=tools, router=router) call(query="What is the time in Stockholm?", functions=tools, router=router)
call(query="What is the tech news in the Lithuania?", functions=tools, router=router) call(query="What is the tech news in the Lithuania?", functions=tools, router=router)
call(query="Hi!", functions=tools, router=router) call(query="Hi!", functions=tools, router=router)
``` ```
%% Output %% Output
2023-12-18 12:20:02 INFO semantic_router.utils.logger Extracting parameters... 2023-12-18 14:48:02 INFO semantic_router.utils.logger Extracting parameters...
2023-12-18 12:20:02 INFO semantic_router.utils.logger Calling Mistral model 2023-12-18 14:48:02 INFO semantic_router.utils.logger Calling Mistral model
2023-12-18 12:20:04 INFO semantic_router.utils.logger AI message: 2023-12-18 14:48:04 INFO semantic_router.utils.logger AI message:
{ {
"location": "Stockholm" "location": "Stockholm"
} }
2023-12-18 12:20:04 INFO semantic_router.utils.logger Extracted parameters: {'location': 'Stockholm'} 2023-12-18 14:48:04 INFO semantic_router.utils.logger Extracted parameters: {'location': 'Stockholm'}
parameters: {'location': 'Stockholm'} parameters: {'location': 'Stockholm'}
Calling `get_time` function with location: Stockholm Calling `get_time` function with location: Stockholm
2023-12-18 12:20:04 INFO semantic_router.utils.logger Extracting parameters... 2023-12-18 14:48:04 INFO semantic_router.utils.logger Extracting parameters...
2023-12-18 12:20:04 INFO semantic_router.utils.logger Calling Mistral model 2023-12-18 14:48:04 INFO semantic_router.utils.logger Calling Mistral model
2023-12-18 12:20:05 INFO semantic_router.utils.logger AI message: 2023-12-18 14:48:05 INFO semantic_router.utils.logger AI message:
{ {
"category": "tech", "category": "tech",
"country": "Lithuania" "country": "Lithuania"
} }
2023-12-18 12:20:05 INFO semantic_router.utils.logger Extracted parameters: {'category': 'tech', 'country': 'Lithuania'} 2023-12-18 14:48:05 INFO semantic_router.utils.logger Extracted parameters: {'category': 'tech', 'country': 'Lithuania'}
parameters: {'category': 'tech', 'country': 'Lithuania'} parameters: {'category': 'tech', 'country': 'Lithuania'}
Calling `get_news` function with category: tech and country: Lithuania Calling `get_news` function with category: tech and country: Lithuania
2023-12-18 12:20:05 WARNING semantic_router.utils.logger No function found 2023-12-18 14:48:05 WARNING semantic_router.utils.logger No function found
2023-12-18 12:20:05 INFO semantic_router.utils.logger Calling Mistral model 2023-12-18 14:48:05 INFO semantic_router.utils.logger Calling Mistral model
2023-12-18 12:20:06 INFO semantic_router.utils.logger AI message: How can I help you today? 2023-12-18 14:48:06 INFO semantic_router.utils.logger AI message: How can I help you today?
' How can I help you today?' ' How can I help you today?'
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
``` ```
......
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
# Semantic Router: Hybrid Layer # Semantic Router: Hybrid Layer
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
The Hybrid Layer in the Semantic Router library can improve making performance particularly for niche use-cases that contain specific terminology, such as finance or medical. It helps us provide more importance to making based on the keywords contained in our utterances and user queries. The Hybrid Layer in the Semantic Router library can improve making performance particularly for niche use-cases that contain specific terminology, such as finance or medical. It helps us provide more importance to making based on the keywords contained in our utterances and user queries.
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
## Getting Started ## Getting Started
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
We start by installing the library: We start by installing the library:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
!pip install -qU semantic-router==0.0.6 !pip install -qU semantic-router==0.0.11
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
We start by defining a dictionary mapping s to example phrases that should trigger those s. We start by defining a dictionary mapping s to example phrases that should trigger those s.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
from semantic_router.schema import Route from semantic_router.schema import Route
politics = Route( politics = Route(
name="politics", name="politics",
utterances=[ utterances=[
"isn't politics the best thing ever", "isn't politics the best thing ever",
"why don't you tell me about your political opinions", "why don't you tell me about your political opinions",
"don't you just love the president", "don't you just love the president",
"don't you just hate the president", "don't you just hate the president",
"they're going to destroy this country!", "they're going to destroy this country!",
"they will save the country!", "they will save the country!",
], ],
) )
``` ```
%% Output
---------------------------------------------------------------------------
ImportError Traceback (most recent call last)
/Users/jakit/customers/aurelio/semantic-router/docs/examples/hybrid-layer.ipynb Cell 7 line 1
----> <a href='vscode-notebook-cell:/Users/jakit/customers/aurelio/semantic-router/docs/examples/hybrid-layer.ipynb#X10sZmlsZQ%3D%3D?line=0'>1</a> from semantic_router.schema import Route
<a href='vscode-notebook-cell:/Users/jakit/customers/aurelio/semantic-router/docs/examples/hybrid-layer.ipynb#X10sZmlsZQ%3D%3D?line=2'>3</a> politics = Route(
<a href='vscode-notebook-cell:/Users/jakit/customers/aurelio/semantic-router/docs/examples/hybrid-layer.ipynb#X10sZmlsZQ%3D%3D?line=3'>4</a> name="politics",
<a href='vscode-notebook-cell:/Users/jakit/customers/aurelio/semantic-router/docs/examples/hybrid-layer.ipynb#X10sZmlsZQ%3D%3D?line=4'>5</a> utterances=[
(...)
<a href='vscode-notebook-cell:/Users/jakit/customers/aurelio/semantic-router/docs/examples/hybrid-layer.ipynb#X10sZmlsZQ%3D%3D?line=11'>12</a> ],
<a href='vscode-notebook-cell:/Users/jakit/customers/aurelio/semantic-router/docs/examples/hybrid-layer.ipynb#X10sZmlsZQ%3D%3D?line=12'>13</a> )
ImportError: cannot import name 'Route' from 'semantic_router.schema' (/Users/jakit/customers/aurelio/semantic-router/.venv/lib/python3.11/site-packages/semantic_router/schema.py)
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Let's define another for good measure: Let's define another for good measure:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
```
%% Cell type:code id: tags:
``` python
chitchat = Route( chitchat = Route(
name="chitchat", name="chitchat",
utterances=[ utterances=[
"how's the weather today?", "how's the weather today?",
"how are things going?", "how are things going?",
"lovely weather today", "lovely weather today",
"the weather is horrendous", "the weather is horrendous",
"let's go to the chippy", "let's go to the chippy",
], ],
) )
chitchat = Route( chitchat = Route(
name="chitchat", name="chitchat",
utterances=[ utterances=[
"how's the weather today?", "how's the weather today?",
"how are things going?", "how are things going?",
"lovely weather today", "lovely weather today",
"the weather is horrendous", "the weather is horrendous",
"let's go to the chippy", "let's go to the chippy",
], ],
) )
routes = [politics, chitchat] routes = [politics, chitchat]
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Now we initialize our embedding model: Now we initialize our embedding model:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
import os import os
from semantic_router.encoders import CohereEncoder from semantic_router.encoders import CohereEncoder
from getpass import getpass from getpass import getpass
os.environ["COHERE_API_KEY"] = os.environ["COHERE_API_KEY"] or getpass( os.environ["COHERE_API_KEY"] = os.environ["COHERE_API_KEY"] or getpass(
"Enter Cohere API Key: " "Enter Cohere API Key: "
) )
encoder = CohereEncoder() encoder = CohereEncoder()
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Now we define the `RouteLayer`. When called, the route layer will consume text (a query) and output the category (`Route`) it belongs to — to initialize a `RouteLayer` we need our `encoder` model and a list of `routes`. Now we define the `RouteLayer`. When called, the route layer will consume text (a query) and output the category (`Route`) it belongs to — to initialize a `RouteLayer` we need our `encoder` model and a list of `routes`.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
from semantic_router.hybrid_layer import HybridRouteLayer from semantic_router.hybrid_layer import HybridRouteLayer
dl = HybridRouteLayer(encoder=encoder, routes=routes) dl = HybridRouteLayer(encoder=encoder, routes=routes)
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
dl("don't you love politics?") dl("don't you love politics?")
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
dl("how's the weather today?") dl("how's the weather today?")
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
--- ---
......
import numpy as np import numpy as np
from numpy.linalg import norm from numpy.linalg import norm
from tqdm.auto import tqdm
from semantic_router.encoders import ( from semantic_router.encoders import (
BaseEncoder, BaseEncoder,
...@@ -34,8 +33,9 @@ class HybridRouteLayer: ...@@ -34,8 +33,9 @@ class HybridRouteLayer:
# if routes list has been passed, we initialize index now # if routes list has been passed, we initialize index now
if routes: if routes:
# initialize index now # initialize index now
for route in tqdm(routes): # for route in tqdm(routes):
self._add_route(route=route) # self._add_route(route=route)
self._add_routes(routes)
def __call__(self, text: str) -> str | None: def __call__(self, text: str) -> str | None:
results = self._query(text) results = self._query(text)
...@@ -77,6 +77,38 @@ class HybridRouteLayer: ...@@ -77,6 +77,38 @@ class HybridRouteLayer:
else: else:
self.sparse_index = np.concatenate([self.sparse_index, sparse_embeds]) self.sparse_index = np.concatenate([self.sparse_index, sparse_embeds])
def _add_routes(self, routes: list[Route]):
# create embeddings for all routes
logger.info("Creating embeddings for all routes...")
all_utterances = [
utterance for route in routes for utterance in route.utterances
]
dense_embeds = np.array(self.encoder(all_utterances))
sparse_embeds = np.array(self.sparse_encoder(all_utterances))
# create route array
route_names = [route.name for route in routes for _ in route.utterances]
route_array = np.array(route_names)
self.categories = (
np.concatenate([self.categories, route_array])
if self.categories is not None
else route_array
)
# create utterance array (the dense index)
self.index = (
np.concatenate([self.index, dense_embeds])
if self.index is not None
else dense_embeds
)
# create sparse utterance array
self.sparse_index = (
np.concatenate([self.sparse_index, sparse_embeds])
if self.sparse_index is not None
else sparse_embeds
)
def _query(self, text: str, top_k: int = 5): def _query(self, text: str, top_k: int = 5):
"""Given some text, encodes and searches the index vector space to """Given some text, encodes and searches the index vector space to
retrieve the top_k most similar records. retrieve the top_k most similar records.
......
...@@ -22,18 +22,9 @@ class CustomFormatter(colorlog.ColoredFormatter): ...@@ -22,18 +22,9 @@ class CustomFormatter(colorlog.ColoredFormatter):
def add_coloured_handler(logger): def add_coloured_handler(logger):
formatter = CustomFormatter() formatter = CustomFormatter()
console_handler = logging.StreamHandler() console_handler = logging.StreamHandler()
console_handler.setFormatter(formatter) console_handler.setFormatter(formatter)
logging.basicConfig(
datefmt="%Y-%m-%d %H:%M:%S",
format="%(log_color)s%(asctime)s %(levelname)s %(name)s %(message)s",
force=True,
)
logger.addHandler(console_handler) logger.addHandler(console_handler)
return logger return logger
......
...@@ -60,7 +60,7 @@ class TestHybridRouteLayer: ...@@ -60,7 +60,7 @@ class TestHybridRouteLayer:
def test_add_route(self, openai_encoder): def test_add_route(self, openai_encoder):
route_layer = HybridRouteLayer(encoder=openai_encoder) route_layer = HybridRouteLayer(encoder=openai_encoder)
route = Route(name="Route 3", utterances=["Yes", "No"]) route = Route(name="Route 3", utterances=["Yes", "No"])
route_layer.add(route) route_layer._add_routes([route])
assert route_layer.index is not None and route_layer.categories is not None assert route_layer.index is not None and route_layer.categories is not None
assert len(route_layer.index) == 2 assert len(route_layer.index) == 2
assert len(set(route_layer.categories)) == 1 assert len(set(route_layer.categories)) == 1
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment