-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
358a316
commit 0fd1204
Showing
7 changed files
with
1,114 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
{ | ||
"tests": [ | ||
{ | ||
"test_case": "Test Case 1", | ||
"user_input": "Get all recipes", | ||
"chat_history": "[{'role': 'user', 'content': 'Get all recipes'}]", | ||
"generate_intent": "true", | ||
"expected_output": { | ||
"result": { | ||
"type": "text", | ||
"file": "", | ||
"value": "| | type | intent |\n|---:|:-------|:-----------------------------------------------------------------------------------------------------------------------------------------------------|\n| 0 | recipe | plot a line chart of conflict events by month for a country using HDX data as an image |\n| 1 | recipe | plot a scatterplot of food price movements and number of fatalities by country using HDXData data, including regression line as an image |\n| 2 | recipe | plot a line chart of fatalities by month for a country using HDX data as an image |\n| 3 | recipe | plot a line chart of commodity prices monthly relative change for a country from 2008-01-01 using HDX data as an image |\n| 4 | recipe | plot a bar chart of humanitarian organizations by sector for a given region using Humanitarian Data Exchange data as an image |\n| 5 | recipe | provide a list of organizations providing food security for a region in a country |\n| 6 | recipe | provide the total population of a provided country using HDX data as text |\n| 7 | recipe | plot population pyramids by age for a country using HDX data as an image |\n| 8 | recipe | plot a scatterplot of food price movements and number of fatalities by country using HDXData data, including regression line as an image |\n| 9 | recipe | plot a map of population by admin1 or admin2 for a country using HAPI data as an image |\n| 10 | recipe | plot a line chart of commodity prices monthly relative change for a country from 2008-01-01 using HDX data as an image |\n| 11 | recipe | List organizations in top 3 states by population in provided IPC Phase in a country, using HAPI data |\n| 12 | recipe | plot a bar chart of humanitarian organizations by sector for a given region using Humanitarian Data Exchange data as an image |\n| 13 | recipe | provide a text summary of metadata by subnational region using HAPI data as text |\n| 14 | recipe | get all recipes |\n| 15 | recipe | plot a map of IPC phase data by admin_1 using HDX data as an image |\n| 0 | memory | plot a line chart of conflict events by month for Chad using HDX data as an image |\n| 1 | memory | plot a scatterplot of food price movements and number of fatalities in TCD from 2008-01-01 using HDXData data, including regression line as an image |\n| 2 | memory | plot a line chart of fatalities by month for Chad using HDX data as an image |\n| 3 | memory | plot a line chart of commodity prices monthly relative change for Chad from 2008-01-01 using HDX data as an image |\n| 4 | memory | plot a bar chart of humanitarian organizations in Wadi Fira by sector using Humanitarian Data Exchange data as an image |\n| 5 | memory | provide a list of organizations providing food security in Wadi Fira, Chad |\n| 6 | memory | provide the total population of Mali using HDX data as text |\n| 7 | memory | plot population pyramids by age for Chad using HDX data as an image |\n| 8 | memory | plot a scatterplot of food price movements and number of fatalities in TCD from 2008-01-01 using HDXData data, including regression line as an image |\n| 9 | memory | plot a map of population by admin1 for Haiti using HAPI data as an image |\n| 10 | memory | plot a line chart of commodity prices monthly relative change for Chad from 2008-01-01 using HDX data as an image |\n| 11 | memory | List organizations in the top 3 states by population in IPC Phase 3+ in Chad, using HAPI data |\n| 12 | memory | plot a bar chart of humanitarian organizations in Wadi Fira by sector using Humanitarian Data Exchange data as an image |\n| 13 | memory | provide a text summary of metadata for Wadi Fira using HAPI data as text |\n| 14 | memory | retrieve all recipes |\n| 15 | memory | plot a map of IPC phase 3 data by admin_1 in Chad using HDX data as an image |" | ||
}, | ||
"metadata": "{\"params\": {}, \"attribution\": \"\", \"data_url\": \"\", \"time_period\": {\"start\": \"\", \"end\": \"\"}}", | ||
"memory_type": "memory", | ||
"memory": "retrieve all recipes", | ||
"memory_found": "true" | ||
} | ||
}, | ||
{ | ||
"test_case": "Test Case 2", | ||
"user_input": "provide the total population of Mali using HDX data as text", | ||
"chat_history": "[{'role': 'user', 'content': 'Get all recipes'}, {'role': 'user', 'content': 'provide the total population of Mali using HDX data as text'}]", | ||
"generate_intent": "true", | ||
"expected_output": { | ||
"result": { | ||
"type": "number", | ||
"file": "", | ||
"value": "17907114" | ||
}, | ||
"metadata": "{\"params\": {\"country_code\": \"MLI\"}, \"attribution\": \"https://data.humdata.org/dataset/ce21c7db-d8f0-40f8-adc2-452d2d2d105c\", \"data_url\": \"https://data.humdata.org/dataset/ce21c7db-d8f0-40f8-adc2-452d2d2d105c/resource/6f243ba2-4d4a-4663-a7c4-e917dbbde73a/download/mli_pop_adm0_v2.csv\", \"time_period\": {\"start\": \"2018-01-01\", \"end\": \"2018-12-31T23:59:59\"}}", | ||
"memory_type": "memory", | ||
"memory": "provide the total population of Mali using HDX data as text", | ||
"memory_found": "true" | ||
} | ||
}, | ||
{ | ||
"test_case": "Test Case 3", | ||
"user_input": "plot a bar chart of humanitarian organizations in Wadi Fira by sector using Humanitarian Data Exchange data as an image", | ||
"chat_history": "[{'role': 'user', 'content': 'Get all recipes'}, {'role': 'user', 'content': 'provide the total population of Mali using HDX data as text'}, {'role': 'user', 'content': 'plot a bar chart of humanitarian organizations in Wadi Fira by sector using Humanitarian Data Exchange data as an image'}]", | ||
"generate_intent": "true", | ||
"expected_output": { | ||
"result": { | ||
"type": "image", | ||
"file": "http://localhost:8000/public/images/memory_image_e9d3e3ee-977f-4291-a51a-7e4e3e4cd5f3.png", | ||
"value": "" | ||
}, | ||
"metadata": "{\"params\": {\"region\": \"Wadi Fira\"}, \"attribution\": \"https://data.humdata.org/dataset/682c3db6-e253-430f-a3f3-305ef079e2de\", \"data_url\": \"https://data.humdata.org/dataset/682c3db6-e253-430f-a3f3-305ef079e2de/resource/1e3ba1f4-2dbc-4f46-9ce7-2274e157188e/download/3w-tcd-20240508.xlsx\", \"time_period\": {\"start\": \"2023-12-01\", \"end\": \"2024-05-31T23:59:59.999999\"}}", | ||
"memory_type": "memory", | ||
"memory": "plot a bar chart of humanitarian organizations in Wadi Fira by sector using Humanitarian Data Exchange data as an image", | ||
"memory_found": "true" | ||
} | ||
}, | ||
{ | ||
"test_case": "Test Case 4", | ||
"user_input": "Plot the distribution of internet access in Chad", | ||
"chat_history": "[{'role': 'user', 'content': 'Get all recipes'}, {'role': 'user', 'content': 'provide the total population of Mali using HDX data as text'}, {'role': 'user', 'content': 'plot a bar chart of humanitarian organizations in Wadi Fira by sector using Humanitarian Data Exchange data as an image'}, {'role': 'user', 'content': 'Plot the distribution of internet access in Chad'}]", | ||
"generate_intent": "true", | ||
"expected_output": {"result":"Sorry, no recipe or memory found","memory_found":"false"} | ||
} | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
from utils.general import call_execute_query_api, call_get_memory_recipe_api | ||
import json | ||
import pytest | ||
|
||
#load json file into variable and print it | ||
@pytest.fixture | ||
def get_test_cases(): | ||
with open('test_cases.json') as f: | ||
test_data = json.load(f) | ||
return test_data | ||
|
||
def test_get_memory_recipe(get_test_cases): | ||
""" | ||
Tests the get memory recipe API endpoint. | ||
""" | ||
|
||
for test in get_test_cases.get('tests', []): | ||
user_input = test["user_input"] | ||
chat_history = test["chat_history"] | ||
generate_intent = test["generate_intent"] | ||
expected_output = test["expected_output"] | ||
response = call_get_memory_recipe_api(user_input, chat_history, generate_intent) | ||
assert response == expected_output | ||
|
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
import os | ||
|
||
import pandas as pd | ||
import psycopg2 | ||
from dotenv import load_dotenv | ||
from sqlalchemy import create_engine | ||
|
||
from utils.general import call_execute_query_api, is_running_in_docker | ||
|
||
load_dotenv() | ||
|
||
|
||
def get_connection(instance="data"): | ||
""" | ||
This function gets a connection to the database | ||
Args: | ||
instance (str): The instance of the database to connect to, "recipe" or "data". Default is "data" | ||
""" | ||
instance = instance.upper() | ||
|
||
host = os.getenv(f"POSTGRES_{instance}_HOST") | ||
port = os.getenv(f"POSTGRES_{instance}_PORT") | ||
database = os.getenv(f"POSTGRES_{instance}_DB") | ||
user = os.getenv(f"POSTGRES_{instance}_USER") | ||
password = os.getenv(f"POSTGRES_{instance}_PASSWORD") | ||
|
||
conn = psycopg2.connect( | ||
dbname=database, user=user, password=password, host=host, port=port | ||
) | ||
return conn | ||
|
||
|
||
def execute_query(query, instance="data"): | ||
""" | ||
Executes a SQL query and returns the result as a DataFrame. | ||
Parameters: | ||
query (str): The SQL query to execute. | ||
instance (str): The database instance to connect to. Default is "data". | ||
Returns: | ||
pandas.DataFrame: The result of the query as a DataFrame | ||
""" | ||
|
||
conn = get_connection(instance) | ||
cur = conn.cursor() | ||
|
||
# Set to read-only mode | ||
cur.execute("SET TRANSACTION READ ONLY;") | ||
|
||
print(f"Executing query: {query}") | ||
|
||
# Execute the query | ||
cur.execute(query) | ||
|
||
# Fetch all the returned rows | ||
rows = cur.fetchall() | ||
|
||
print(f"Query returned {len(rows)} rows") | ||
|
||
# Get column names | ||
column_names = [desc[0] for desc in cur.description] | ||
|
||
# Close the cursor and connection | ||
cur.close() | ||
conn.close() | ||
|
||
# Convert rows to DataFrame | ||
df = pd.DataFrame(rows, columns=column_names) | ||
|
||
return df | ||
|
||
|
||
def connect_to_db(instance="recipe"): | ||
""" | ||
Connects to the specified database instance (RECIPE or DATA) DB and returns a connection object. | ||
Args: | ||
instance (str): The name of the database instance to connect to. Defaults to "RECIPE". | ||
Returns: | ||
sqlalchemy.engine.base.Engine: The connection object for the specified database instance. | ||
""" | ||
|
||
instance = instance.upper() | ||
|
||
# Fallback for CLI running outside of docker | ||
if not is_running_in_docker(): | ||
os.environ[f"POSTGRES_{instance}_HOST"] = "localhost" | ||
os.environ[f"POSTGRES_{instance}_PORT"] = "5433" | ||
|
||
host = os.getenv(f"POSTGRES_{instance}_HOST") | ||
port = os.getenv(f"POSTGRES_{instance}_PORT") | ||
database = os.getenv(f"POSTGRES_{instance}_DB") | ||
user = os.getenv(f"POSTGRES_{instance}_USER") | ||
password = os.getenv(f"POSTGRES_{instance}_PASSWORD") | ||
conn_str = f"postgresql://{user}:{password}@{host}:{port}/{database}" | ||
|
||
# add an echo=True to see the SQL queries | ||
conn = create_engine(conn_str) | ||
return conn | ||
|
||
|
||
async def get_data_info(): | ||
""" | ||
Get data info from the database. | ||
Returns: | ||
str: The data info. | ||
""" | ||
|
||
global data_info | ||
|
||
# run this query: select table_name, summary, columns from table_metadata | ||
|
||
query = """ | ||
SELECT | ||
table_name, | ||
summary, | ||
columns | ||
FROM | ||
table_metadata | ||
--WHERE | ||
-- countries is not null | ||
""" | ||
|
||
data_info = await call_execute_query_api(query) | ||
|
||
return data_info |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
import base64 | ||
import json | ||
import os | ||
import re | ||
import sys | ||
import warnings | ||
|
||
import pandas as pd | ||
import requests | ||
from dotenv import load_dotenv | ||
|
||
load_dotenv() | ||
|
||
# Suppress all warnings | ||
warnings.filterwarnings("ignore") | ||
|
||
execute_query_url = f"{os.getenv('RECIPE_SERVER_API')}execute_query" | ||
get_memory_recipe_url = f"{os.getenv('RECIPE_SERVER_API')}get_memory_recipe" | ||
|
||
data_info = None | ||
|
||
|
||
def replace_env_variables(value): | ||
""" | ||
Recursively replaces environment variable placeholders in a given value. | ||
Args: | ||
value (dict, list, str): The value to process | ||
Returns: | ||
The processed value with environment variable placeholders replaced. | ||
""" | ||
if isinstance(value, dict): | ||
return {k: replace_env_variables(v) for k, v in value.items()} | ||
elif isinstance(value, list): | ||
return [replace_env_variables(v) for v in value] | ||
elif isinstance(value, str): | ||
matches = re.findall(r"\{\{ (.+?) \}\}", value) | ||
for match in matches: | ||
if os.getenv(match) is None: | ||
print(f"Environment variable {match} is not set.") | ||
sys.exit(1) | ||
|
||
value = value.replace("{{ " + match + " }}", os.getenv(match)) | ||
return value | ||
else: | ||
return value | ||
|
||
|
||
def read_integration_config(integration_config_file): | ||
""" | ||
Read the APIs configuration from the integration config file. | ||
Args: | ||
integration_config_file (str): The path to the integration config file. | ||
Returns: | ||
apis (dict): A dictionary containing the API configurations. | ||
field_map (dict): A dictionary containing the field mappings. | ||
standard_names (dict): A dictionary containing the standard names. | ||
data_node (str): The data node to use for the integration. | ||
""" | ||
with open(integration_config_file) as f: | ||
print(f"Reading {integration_config_file}") | ||
config = json.load(f) | ||
config = replace_env_variables(config) | ||
apis = config["openapi_interfaces"] | ||
field_map = config["field_map"] | ||
standard_names = config["standard_names"] | ||
|
||
return apis, field_map, standard_names | ||
|
||
|
||
def is_running_in_docker(): | ||
""" | ||
Check if the code is running inside a Docker container. | ||
Returns: | ||
bool: True if running inside a Docker container, False otherwise. | ||
""" | ||
return os.path.exists("/.dockerenv") | ||
|
||
|
||
def make_api_request(url, payload): | ||
""" | ||
Makes an API request to the specified URL with the given payload. | ||
Args: | ||
url (str): The URL to make the API request to. | ||
payload (dict): The payload to send with the API request. | ||
Returns: | ||
dict: The response from the API as a dictionary. | ||
Raises: | ||
requests.exceptions.RequestException: If an error occurs while making the API request. | ||
""" | ||
headers = {"Content-Type": "application/json"} | ||
print(f"API URL: {url}") | ||
print(f"API Payload: {payload}") | ||
response = requests.post(url, headers=headers, json=payload) | ||
print(f"API Response Status Code: {response.status_code}") | ||
response = response.content | ||
print(f"API Response {response}") | ||
return response | ||
|
||
|
||
def call_execute_query_api(sql): | ||
""" | ||
Calls the execute query action API endpoint with the given SQL query. | ||
Args: | ||
sql (str): The SQL query to execute. | ||
Returns: | ||
dict: The response from the API. | ||
""" | ||
data = {"query": f"{sql}"} | ||
print(f"Calling execute query API {execute_query_url} with {sql} ...") | ||
return make_api_request(execute_query_url, data) | ||
|
||
|
||
def call_get_memory_recipe_api(user_input, history, generate_intent="true"): | ||
""" | ||
Calls the API to get a memory recipe action. | ||
Args: | ||
user_input (str): The user input. | ||
history (str): The chat history. | ||
generate_intent (str): Whether to generate the intent. | ||
Returns: | ||
The API response from the make_api_request function. | ||
""" | ||
|
||
data = { | ||
"user_input": f"{user_input}", | ||
"chat_history": history, | ||
"generate_intent": "true", | ||
} | ||
print(f"Calling execute query API {get_memory_recipe_url} with {data} ...") | ||
result = make_api_request(get_memory_recipe_url, data) | ||
|
||
if isinstance(result, bytes): | ||
result = result.decode("utf-8") | ||
|
||
print("IN API CALL", result) | ||
result = json.loads(result) | ||
|
||
return result |
Oops, something went wrong.