Skip to content
Snippets Groups Projects
Unverified Commit eecc8281 authored by James Briggs's avatar James Briggs
Browse files

restructure and tweaks

parent b2dd6e39
No related branches found
No related tags found
No related merge requests found
.env
mac.env
.DS_Store
**/__pycache__
**/*.py[cod]
\ No newline at end of file
This diff is collapsed.
from decision_layer.decision_layer import DecisionLayer
\ No newline at end of file
from decision_layer.encoders.base import BaseEncoder
from decision_layer.encoders.cohere import CohereEncoder
from decision_layer.encoders.huggingface import HuggingFaceEncoder
from decision_layer.encoders.openai import OpenAIEncoder
\ No newline at end of file
[tool.poetry]
name = "decision-layer"
name = "semantic-router"
version = "0.0.1"
description = "Super fast decision layer for AI"
authors = ["James Briggs <james@aurelio.ai>"]
description = "Super fast semantic router for AI decision making"
authors = [
"James Briggs <james@aurelio.ai>",
"Siraj Aizlewood <siraj@aurelio.ai>"
]
readme = "README.md"
[tool.poetry.dependencies]
......
Source diff could not be displayed: it is too large. Options to address this: view the blob.
from semantic_router.layer import DecisionLayer
\ No newline at end of file
from semantic_router.encoders.base import BaseEncoder
from semantic_router.encoders.cohere import CohereEncoder
from semantic_router.encoders.huggingface import HuggingFaceEncoder
from semantic_router.encoders.openai import OpenAIEncoder
\ No newline at end of file
File moved
import os
import cohere
from decision_layer.encoders import BaseEncoder
from semantic_router.encoders import BaseEncoder
class CohereEncoder(BaseEncoder):
client: cohere.Client | None
def __init__(self, name: str, cohere_api_key: str | None = None):
def __init__(self, name: str = "embed-english-v3.0", cohere_api_key: str | None = None):
super().__init__(name=name, client=None)
cohere_api_key = cohere_api_key or os.getenv("COHERE_API_KEY")
if cohere_api_key is None:
......@@ -17,6 +17,6 @@ class CohereEncoder(BaseEncoder):
else:
input_type = "search_document"
embeds = self.client.embed(
texts, input_type=input_type, model="embed-english-v3.0"
texts, input_type=input_type, model=self.name
)
return embeds.embeddings
\ No newline at end of file
from decision_layer.encoders import BaseEncoder
from semantic_router.encoders import BaseEncoder
class HuggingFaceEncoder(BaseEncoder):
......
import os
from decision_layer.encoders import BaseEncoder
from semantic_router.encoders import BaseEncoder
import openai
from time import time
......
from decision_layer.encoders import BaseEncoder
from decision_layer.schema import Decision
from semantic_router.encoders import BaseEncoder, OpenAIEncoder, CohereEncoder
from semantic_router.schema import Decision
import numpy as np
from numpy.linalg import norm
class DecisionLayer:
index = None
categories = None
threshold = 0.82
def __init__(self, encoder: BaseEncoder, decisions: list[Decision] = []):
self.encoder = encoder
self.embeddings_classified = False
# decide on default threshold based on encoder
if isinstance(encoder, OpenAIEncoder):
self.threshold = 0.82
elif isinstance(encoder, CohereEncoder):
self.threshold = 0.3
else:
self.threshold = 0.82
# if decisions list has been passed, we initialize index now
if decisions:
# initialize index now
for decision in decisions:
self._add_decision(decision=decision)
def __call__(self, text: str, _method: str='raw', _threshold: float=0.5):
def __call__(self, text: str) -> str | None:
results = self._query(text)
top_class, top_class_scores = self._semantic_classify(results)
# TODO: Once we determine a threshold methodlogy, we can use it here.
passed = self._pass_threshold(top_class_scores, self.threshold)
if passed:
return top_class
else:
return None
def add(self, decision: Decision):
self._add_decision(devision=decision)
......@@ -41,12 +53,6 @@ class DecisionLayer:
embed_arr = np.array(embeds)
self.index = np.concatenate([self.index, embed_arr])
def _cosine_similarity(self, v1, v2):
"""Compute the dot product between two embeddings using numpy functions."""
np_v1 = np.array(v1)
np_v2 = np.array(v2)
return np.dot(np_v1, np_v2) / (np.linalg.norm(np_v1) * np.linalg.norm(np_v2))
def _query(self, text: str, top_k: int=5):
"""Given some text, encodes and searches the index vector space to
retrieve the top_k most similar records.
......@@ -64,10 +70,8 @@ class DecisionLayer:
return [
{"decision": d, "score": s.item()} for d, s in zip(decisions, scores)
]
def _semantic_classify(self, query_results: dict):
scores_by_class = {}
for result in query_results:
score = result['score']
......@@ -76,9 +80,12 @@ class DecisionLayer:
scores_by_class[decision].append(score)
else:
scores_by_class[decision] = [score]
# Calculate total score for each class
total_scores = {decision: sum(scores) for decision, scores in scores_by_class.items()}
top_class = max(total_scores, key=total_scores.get, default=None)
# Return the top class and its associated scores
return top_class, scores_by_class.get(top_class, [])
return str(top_class), scores_by_class.get(top_class, [])
def _pass_threshold(self, scores: list[float], threshold: float):
"""Returns true if the threshold has been passed."""
return max(scores) > threshold
from enum import Enum
from pydantic import BaseModel
from pydantic.dataclasses import dataclass
from decision_layer.encoders import (
from semantic_router.encoders import (
BaseEncoder,
HuggingFaceEncoder,
OpenAIEncoder,
......
%% Cell type:markdown id: tags:
# Decision Layer Walkthrough
# Semantic Router Walkthrough
%% Cell type:markdown id: tags:
The decision layer library can be used as a super fast decision making layer on top of LLMs. That means that rather than waiting on a slow agent to decide what to do, we can use the magic of semantic vector space to make decisions. Cutting decision making time down from seconds to milliseconds.
The Semantic Router library can be used as a super fast decision making layer on top of LLMs. That means rather than waiting on a slow agent to decide what to do, we can use the magic of semantic vector space to make decisions. Cutting decision making time down from seconds to milliseconds.
%% Cell type:markdown id: tags:
## Getting Started
%% Cell type:markdown id: tags:
We start by installing the library:
%% Cell type:code id: tags:
``` python
!pip install -qU \
decision-layer
!pip install -qU semantic-router==0.0.1
```
%% Output
[notice] A new release of pip is available: 23.1.2 -> 23.3.1
[notice] To update, run: python.exe -m pip install --upgrade pip
%% Cell type:markdown id: tags:
We start by defining a dictionary mapping decisions to example phrases that should trigger those decisions.
%% Cell type:code id: tags:
``` python
from decision_layer.schema import Decision
from semantic_router.schema import Decision
politics = Decision(
name="politics",
utterances=[
"isn't politics the best thing ever",
"why don't you tell me about your political opinions",
"don't you just love the president"
"don't you just hate the president",
"they're going to destroy this country!",
"they will save the country!",
"did you hear about the new goverment proposal regarding the ownership of cats and dogs",
"they will save the country!"
]
)
```
%% Cell type:markdown id: tags:
Let's define another for good measure:
%% Cell type:code id: tags:
``` python
chitchat = Decision(
name="chitchat",
utterances=[
"how's the weather today?",
"how are things going?",
"lovely weather today",
"the weather is horrendous",
"let's go to the chippy",
"it's raining cats and dogs",
"let's go to the chippy"
]
)
decisions = [politics, chitchat]
```
%% Cell type:markdown id: tags:
Now we initialize our embedding model (we will add support for Hugging Face):
Now we initialize our embedding model:
%% Cell type:code id: tags:
``` python
from decision_layer.encoders import OpenAIEncoder
from semantic_router.encoders import CohereEncoder
from getpass import getpass
import os
encoder = OpenAIEncoder(name="text-embedding-ada-002")
os.environ["COHERE_API_KEY"] = getpass("Enter Cohere API Key: ")
encoder = CohereEncoder()
```
%% Cell type:markdown id: tags:
Now we define the `DecisionLayer`. When called, the decision layer will consume text (a query) and output the category (`Decision`) it belongs to — for now we can only `_query` and get the most similar `Decision` `utterances`.
Now we define the `DecisionLayer`. When called, the decision layer will consume text (a query) and output the category (`Decision`) it belongs to — to initialize a `DecisionLayer` we need our `encoder` model and a list of `decisions`.
%% Cell type:code id: tags:
``` python
from decision_layer import DecisionLayer
from semantic_router import DecisionLayer
dl = DecisionLayer(encoder=encoder, decisions=decisions)
```
%% Cell type:code id: tags:
``` python
out = dl._query("don't you love politics?")
out
```
%% Output
[{'decision': 'politics', 'score': 0.22792677421560453},
{'decision': 'politics', 'score': 0.2315237823644528},
{'decision': 'politics', 'score': 0.2516642096551168},
{'decision': 'politics', 'score': 0.2531645714220874},
{'decision': 'politics', 'score': 0.2566108224655662}]
%% Cell type:markdown id: tags:
Using the most similar `Decision` `utterances` and their `cosine similarity scores`, use `simple_classify` to apply scoring a secondary scoring system which chooses the `decision` that the utterance belongs to.
%% Cell type:markdown id: tags:
Here we use `apply_tan=True`, which means that a `tan` function is assigned to each score boosting the score of `decisions` whose datapoints had greater `cosine similarlity` and reducing the score of those which had lower `cosine similarity`.
Now we can test it:
%% Cell type:code id: tags:
``` python
decision, scores_by_category = dl.simple_classify(query_results=out, apply_tan=True)
decision
dl("don't you love politics?")
```
%% Output
'politics'
%% Cell type:code id: tags:
``` python
scores_by_category
dl("how's the weather today?")
```
%% Output
{'politics': 2.018519173992354}
'chitchat'
%% Cell type:markdown id: tags:
The correct category was chosen. Let's try again for a less clear-cut case:
%% Cell type:code id: tags:
``` python
out = dl._query("i love cats and dogs!")
out
```
%% Output
[{'decision': 'chitchat', 'score': 0.22320888353212376},
{'decision': 'politics', 'score': 0.22367029584935166},
{'decision': 'politics', 'score': 0.2274250403127478},
{'decision': 'politics', 'score': 0.23451692377042876},
{'decision': 'chitchat', 'score': 0.24924083653953585}]
%% Cell type:code id: tags:
``` python
decision, scores_by_category = dl.simple_classify(query_results=out, apply_tan=True)
decision
```
%% Output
'politics'
Both are classified accurately, what if we send a query that is unrelated to our existing `Decision` objects?
%% Cell type:code id: tags:
``` python
scores_by_category
dl("I'm interested in learning about llama 2")
```
%% Output
{'chitchat': 0.7785435459589187, 'politics': 1.1258003022715952}
%% Cell type:code id: tags:
``` python
dl.categories
```
%% Output
%% Cell type:markdown id: tags:
array(['politics', 'politics', 'politics', 'politics', 'politics',
'politics', 'chitchat', 'chitchat', 'chitchat', 'chitchat',
'chitchat', 'chitchat'], dtype='<U8')
In this case, we return `None` because no matches were identified.
......
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