[add] better error handling
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Evan Reichard 2023-11-08 20:52:29 -05:00
parent 3168bfffd1
commit ca8c306534
7 changed files with 112 additions and 74 deletions

View File

@ -19,36 +19,17 @@ def get_response():
resp = minyma.oai.query(message) resp = minyma.oai.query(message)
# Derive LLM Data
# llm_resp = resp.get("llm", {})
# llm_choices = llm_resp.get("choices", [])
# Derive VDB Data
# vdb_resp = resp.get("vdb", {})
# combined_context = [{
# "id": vdb_resp.get("ids")[i],
# "distance": vdb_resp.get("distances")[i],
# "doc": vdb_resp.get("docs")[i],
# "metadata": vdb_resp.get("metadatas")[i],
# } for i, _ in enumerate(vdb_resp.get("docs", []))]
# Return Data # Return Data
return resp return resp
""" """
Return the raw vector db related response TODO - Embeds and loads data into the local ChromaDB.
{
"input": "string",
"normalizer": "string",
}
""" """
@bp.route("/related", methods=["POST"]) bp.route("/embed", methods=["POST"])
def get_related(): def post_embeddings():
data = request.get_json() pass
if not data:
return {"error": "Missing Message"}
message = str(data.get("message"))
if message == "":
return {"error": "Empty Message"}
related_documents = minyma.vdb.get_related(message)
return related_documents

View File

@ -1,11 +1,12 @@
import os import os
def get_env(key, default=None, required=False) -> str: def get_env(key, default=None, required=False) -> str | None:
"""Wrapper for gathering env vars.""" """Wrapper for gathering env vars."""
if required: if required:
assert key in os.environ, "Missing Environment Variable: %s" % key assert key in os.environ, "Missing Environment Variable: %s" % key
return str(os.environ.get(key, default)) env = os.environ.get(key, default)
return str(env) if env is not None else None
class Config: class Config:
@ -19,7 +20,7 @@ class Config:
OpenAI API Key - Required OpenAI API Key - Required
""" """
CHROMA_DATA_PATH: str = get_env("CHROMA_DATA_PATH", required=False) CHROMA_DATA_PATH: str | None = get_env("CHROMA_DATA_PATH", required=False)
HOME_ASSISTANT_API_KEY: str = get_env("HOME_ASSISTANT_API_KEY", required=False) HOME_ASSISTANT_API_KEY: str | None = get_env("HOME_ASSISTANT_API_KEY", required=False)
HOME_ASSISTANT_URL: str = get_env("HOME_ASSISTANT_URL", required=False) HOME_ASSISTANT_URL: str | None = get_env("HOME_ASSISTANT_URL", required=False)
OPENAI_API_KEY: str = get_env("OPENAI_API_KEY", required=True) OPENAI_API_KEY: str | None = get_env("OPENAI_API_KEY", required=True)

View File

@ -79,20 +79,33 @@ class OpenAIConnector:
print("[OpenAIConnector] Completed Initial OAI Query:\n", indent(json.dumps({ "usage": response.usage, "function_calls": all_funcs }, indent=2), ' ' * 2)) print("[OpenAIConnector] Completed Initial OAI Query:\n", indent(json.dumps({ "usage": response.usage, "function_calls": all_funcs }, indent=2), ' ' * 2))
# Execute Requested Functions # Build Response Text & Metadata
func_responses = {} func_metadata = {}
for func in all_funcs: func_response = []
func_responses[func] = minyma.plugins.execute(func)
# Build Response Text for func in all_funcs:
response_content_arr = [] # Execute Requested Function
for key, val in func_responses.items(): resp = minyma.plugins.execute(func)
indented_val = indent(val, ' ' * 2)
response_content_arr.append("- %s\n%s" % (key, indented_val)) # Unknown Response
response_content = "\n".join(response_content_arr) if resp is None:
print("[OpenAIConnector] Invalid Function Response: %s" % func)
continue
# Get Response
content = resp.get("content")
metadata = resp.get("metadata")
error = resp.get("error")
# Append Responses & Metadata
indented_val = indent(content or error or "Unknown Error", ' ' * 2)
func_response.append("- %s\n%s" % (func, indented_val))
func_metadata[func] = { "metadata": metadata, "error": error }
func_response = "\n".join(func_response)
# Create Follow Up Prompt # Create Follow Up Prompt
prompt = FOLLOW_UP_PROMPT_TEMPLATE.format(question = question, response = response_content) prompt = FOLLOW_UP_PROMPT_TEMPLATE.format(question = question, response = func_response)
messages = [{"role": "user", "content": prompt}] messages = [{"role": "user", "content": prompt}]
print("[OpenAIConnector] Running Follup Up OAI Query") print("[OpenAIConnector] Running Follup Up OAI Query")
@ -116,7 +129,7 @@ class OpenAIConnector:
# Return Response # Return Response
return { return {
"response": content, "response": content,
"functions": func_responses, "functions": func_metadata,
"usage": { "usage": {
"prompt_tokens": prompt_tokens, "prompt_tokens": prompt_tokens,
"completion_tokens": completion_tokens, "completion_tokens": completion_tokens,

View File

@ -13,8 +13,9 @@ class ChromaDBPlugin(MinymaPlugin):
def __init__(self, config): def __init__(self, config):
self.name = "chroma_db" self.name = "chroma_db"
self.config = config self.config = config
self.word_cap = 1000
if not config.CHROMA_DATA_PATH: if config.CHROMA_DATA_PATH is None:
self.functions = [] self.functions = []
else: else:
self.vdb = ChromaDB(config.CHROMA_DATA_PATH) self.vdb = ChromaDB(config.CHROMA_DATA_PATH)
@ -25,17 +26,28 @@ class ChromaDBPlugin(MinymaPlugin):
# Get Related # Get Related
related = self.vdb.get_related(collection_name, query) related = self.vdb.get_related(collection_name, query)
# Get Metadata
metadata = [{
"id": related.get("ids")[i],
"distance": related.get("distances")[i],
"metadata": related.get("metadatas")[i],
} for i, _ in enumerate(related.get("docs", []))]
# Normalize Data # Normalize Data
return list( return list(
map( map(
lambda x: " ".join(x.split()[:self.vdb.word_cap]), lambda x: " ".join(x.split()[:self.word_cap]),
related.get("docs", []) related.get("docs", [])
) )
) ), metadata
def lookup_pubmed_data(self, query: str): def lookup_pubmed_data(self, query: str):
COLLECTION_NAME = "pubmed" COLLECTION_NAME = "pubmed"
documents = self.__lookup_data(COLLECTION_NAME, query) documents, metadata = self.__lookup_data(COLLECTION_NAME, query)
context = '\n'.join(documents) context = '\n'.join(documents)
return context return {
"content": context,
"metadata": metadata,
"error": None
}

View File

@ -21,6 +21,7 @@ class DuckDuckGoPlugin(MinymaPlugin):
resp = requests.get("https://html.duckduckgo.com/html/?q=%s" % query, headers=HEADERS) resp = requests.get("https://html.duckduckgo.com/html/?q=%s" % query, headers=HEADERS)
soup = BeautifulSoup(resp.text, features="html.parser") soup = BeautifulSoup(resp.text, features="html.parser")
# Get Results
results = [] results = []
for item in soup.select(".result > div"): for item in soup.select(".result > div"):
title_el = item.select_one(".result__title > a") title_el = item.select_one(".result__title > a")
@ -31,4 +32,18 @@ class DuckDuckGoPlugin(MinymaPlugin):
results.append({"title": title, "description": description}) results.append({"title": title, "description": description})
return json.dumps(results[:5]) # Derive Metadata (Title)
metadata = {
"titles": list(
map(
lambda x: x.get("title"),
results[:5]
)
)
}
return {
"content": json.dumps(results[:5]),
"metadata": metadata,
"error": None
}

View File

@ -10,18 +10,15 @@ class HomeAssistantPlugin(MinymaPlugin):
def __init__(self, config): def __init__(self, config):
self.config = config self.config = config
self.name = "home_assistant" self.name = "home_assistant"
self.functions = []
if config.HOME_ASSISTANT_API_KEY and config.HOME_ASSISTANT_URL:
if not config.HOME_ASSISTANT_API_KEY or not config.HOME_ASSISTANT_URL: self.functions = [self.home_automation_command]
if not config.HOME_ASSISTANT_API_KEY: if not config.HOME_ASSISTANT_API_KEY:
print("[HomeAssistantPlugin] Missing HOME_ASSISTANT_API_KEY") print("[HomeAssistantPlugin] Missing HOME_ASSISTANT_API_KEY")
if not config.HOME_ASSISTANT_URL: if not config.HOME_ASSISTANT_URL:
print("[HomeAssistantPlugin] Missing HOME_ASSISTANT_URL") print("[HomeAssistantPlugin] Missing HOME_ASSISTANT_URL")
self.functions = []
else:
self.functions = [self.home_automation_command]
def home_automation_command(self, natural_language_command: str): def home_automation_command(self, natural_language_command: str):
url = urllib.parse.urljoin(self.config.HOME_ASSISTANT_URL, "/api/conversation/process") url = urllib.parse.urljoin(self.config.HOME_ASSISTANT_URL, "/api/conversation/process")
headers = { headers = {
@ -34,6 +31,17 @@ class HomeAssistantPlugin(MinymaPlugin):
# Parse JSON # Parse JSON
try: try:
return json.dumps(resp.json()) r = resp.json()
text = r["response"]["speech"]["plain"]["speech"]
return {
"content": text,
"metadata": r,
"error": None
}
except requests.JSONDecodeError: except requests.JSONDecodeError:
return json.dumps({ "error": "Command Failed" }) return {
"content": None,
"metadata": None,
"error": "Command Failed"
}

View File

@ -50,10 +50,11 @@ class VehicleLookupPlugin(MinymaPlugin):
# Invalid JSON # Invalid JSON
if json_resp is None: if json_resp is None:
return json.dumps({ return{
"content": None,
"metadata": text_resp,
"error": error, "error": error,
"response": text_resp, }
})
try: try:
# Check Result # Check Result
@ -63,7 +64,11 @@ class VehicleLookupPlugin(MinymaPlugin):
error = "No Results" error = "No Results"
else: else:
error = "API Error: %s" % status_resp error = "API Error: %s" % status_resp
return {"error": error, "response": text_resp} return {
"content": None,
"metadata": json_resp,
"error": error,
}
# Parse Result # Parse Result
vehicle_info = json_resp.get("content") vehicle_info = json_resp.get("content")
@ -74,17 +79,20 @@ class VehicleLookupPlugin(MinymaPlugin):
trim = vehicle_info.get("vehicles")[0].get("trim") trim = vehicle_info.get("vehicles")[0].get("trim")
except Exception as e: except Exception as e:
return json.dumps({ return {
"content": None,
"metadata": text_resp,
"error": "Unknown Error: %s" % e, "error": "Unknown Error: %s" % e,
"response": text_resp, }
})
return json.dumps({ return {
"result": { "content": json.dumps({
"vin": vin, "vin": vin,
"year": year, "year": year,
"make": make, "make": make,
"model": model, "model": model,
"trim": trim, "trim": trim,
}, }),
}) "metadata": json_resp,
"error": None
}