2023-03-15 00:44:16 +01:00
# Standard Packages
import os
from datetime import datetime
2021-11-26 12:53:03 +01:00
# External Packages
import pytest
# Internal Packages
2023-03-14 18:27:58 +01:00
from khoj . processor . conversation . gpt import converse , message_to_log , message_to_prompt
2021-11-26 12:53:03 +01:00
2023-01-09 04:09:24 +01:00
# Initialize variables for tests
2023-03-15 00:44:16 +01:00
api_key = os . getenv ( " OPENAI_API_KEY " ) # Set your OPENAI_API_KEY as environment variable to run the tests below
2021-11-26 12:53:03 +01:00
# Test
# ----------------------------------------------------------------------------------------------------
def test_message_to_understand_prompt ( ) :
2022-01-12 16:36:01 +01:00
# Arrange
2023-02-17 17:04:26 +01:00
understand_primer = ' Extract information from each chat message \n \n remember(memory-type, data); \n memory-type=[ " companion " , " notes " , " ledger " , " image " , " music " ] \n search(search-type, data); \n search-type=[ " google " , " youtube " ] \n generate(activity); \n activity=[ " paint " , " write " , " chat " ] \n trigger-emotion(emotion); \n emotion=[ " happy " , " confidence " , " fear " , " surprise " , " sadness " , " disgust " , " anger " , " curiosity " , " calm " ] \n \n Q: How are you doing? \n A: activity( " chat " ); trigger-emotion( " surprise " ) \n Q: Do you remember what I told you about my brother Antoine when we were at the beach? \n A: remember( " notes " , " Brother Antoine when we were at the beach " ); trigger-emotion( " curiosity " ); \n Q: what did we talk about last time? \n A: remember( " notes " , " talk last time " ); trigger-emotion( " curiosity " ); \n Q: Let \' s make some drawings! \n A: generate( " paint " ); trigger-emotion( " happy " ); \n Q: Do you know anything about Lebanon? \n A: search( " google " , " lebanon " ); trigger-emotion( " confidence " ); \n Q: Find a video about a panda rolling in the grass \n A: search( " youtube " , " panda rolling in the grass " ); trigger-emotion( " happy " ); \n Q: Tell me a scary story \n A: generate( " write " " A story about some adventure " ); trigger-emotion( " fear " ); \n Q: What fiction book was I reading last week about AI starship? \n A: remember( " notes " , " read fiction book about AI starship last week " ); trigger-emotion( " curiosity " ); \n Q: How much did I spend at Subway for dinner last time? \n A: remember( " ledger " , " last Subway dinner " ); trigger-emotion( " curiosity " ); \n Q: I \' m feeling sleepy \n A: activity( " chat " ); trigger-emotion( " calm " ) \n Q: What was that popular Sri lankan song that Alex showed me recently? \n A: remember( " music " , " popular Sri lankan song that Alex showed recently " ); trigger-emotion( " curiosity " ); \n Q: You \' re pretty funny! \n A: activity( " chat " ); trigger-emotion( " pride " ) '
expected_response = ' Extract information from each chat message \n \n remember(memory-type, data); \n memory-type=[ " companion " , " notes " , " ledger " , " image " , " music " ] \n search(search-type, data); \n search-type=[ " google " , " youtube " ] \n generate(activity); \n activity=[ " paint " , " write " , " chat " ] \n trigger-emotion(emotion); \n emotion=[ " happy " , " confidence " , " fear " , " surprise " , " sadness " , " disgust " , " anger " , " curiosity " , " calm " ] \n \n Q: How are you doing? \n A: activity( " chat " ); trigger-emotion( " surprise " ) \n Q: Do you remember what I told you about my brother Antoine when we were at the beach? \n A: remember( " notes " , " Brother Antoine when we were at the beach " ); trigger-emotion( " curiosity " ); \n Q: what did we talk about last time? \n A: remember( " notes " , " talk last time " ); trigger-emotion( " curiosity " ); \n Q: Let \' s make some drawings! \n A: generate( " paint " ); trigger-emotion( " happy " ); \n Q: Do you know anything about Lebanon? \n A: search( " google " , " lebanon " ); trigger-emotion( " confidence " ); \n Q: Find a video about a panda rolling in the grass \n A: search( " youtube " , " panda rolling in the grass " ); trigger-emotion( " happy " ); \n Q: Tell me a scary story \n A: generate( " write " " A story about some adventure " ); trigger-emotion( " fear " ); \n Q: What fiction book was I reading last week about AI starship? \n A: remember( " notes " , " read fiction book about AI starship last week " ); trigger-emotion( " curiosity " ); \n Q: How much did I spend at Subway for dinner last time? \n A: remember( " ledger " , " last Subway dinner " ); trigger-emotion( " curiosity " ); \n Q: I \' m feeling sleepy \n A: activity( " chat " ); trigger-emotion( " calm " ) \n Q: What was that popular Sri lankan song that Alex showed me recently? \n A: remember( " music " , " popular Sri lankan song that Alex showed recently " ); trigger-emotion( " curiosity " ); \n Q: You \' re pretty funny! \n A: activity( " chat " ); trigger-emotion( " pride " ) \n Q: When did I last dine at Burger King? \n A: '
2021-11-26 12:53:03 +01:00
# Act
2023-02-17 17:04:26 +01:00
actual_response = message_to_prompt (
" When did I last dine at Burger King? " , understand_primer , start_sequence = " \n A: " , restart_sequence = " \n Q: "
)
2021-11-26 12:53:03 +01:00
# Assert
assert actual_response == expected_response
# ----------------------------------------------------------------------------------------------------
2023-02-17 17:04:26 +01:00
@pytest.mark.skipif (
api_key is None , reason = " Set api_key variable to your OpenAI API key from https://beta.openai.com/account/api-keys "
)
2023-03-15 00:44:16 +01:00
def test_chat_with_no_chat_history_or_retrieved_content ( ) :
2021-11-26 12:53:03 +01:00
# Act
2023-03-14 18:27:58 +01:00
response = converse (
text = " " , # Assume no context retrieved from notes for the user_query
user_query = " Hello, my name is Testatron. Who are you? " ,
api_key = api_key ,
)
2021-11-26 12:53:03 +01:00
# Assert
2023-03-14 18:27:58 +01:00
expected_responses = [ " Khoj " , " khoj " ]
2021-11-26 12:53:03 +01:00
assert len ( response ) > 0
2023-03-14 18:27:58 +01:00
assert any ( [ expected_response in response for expected_response in expected_responses ] ) , (
" Expected assistants name, [K|k]hoj, in response but got " + response
)
2021-11-26 12:53:03 +01:00
# ----------------------------------------------------------------------------------------------------
2023-02-17 17:04:26 +01:00
@pytest.mark.skipif (
api_key is None , reason = " Set api_key variable to your OpenAI API key from https://beta.openai.com/account/api-keys "
)
2023-03-15 00:44:16 +01:00
def test_answer_from_chat_history_and_no_content ( ) :
# Arrange
conversation_log = { " chat " : [ ] }
message_list = [
( " Hello, my name is Testatron. Who are you? " , " Hi, I am Khoj, a personal assistant. How can I help? " , " " ) ,
( " When was I born? " , " You were born on 1st April 1984. " , " " ) ,
]
# Generate conversation logs
for user_message , gpt_message , _ in message_list :
conversation_log [ " chat " ] + = message_to_log ( user_message , gpt_message )
# Act
response = converse (
text = " " , # Assume no context retrieved from notes for the user_query
user_query = " What is my name? " ,
conversation_log = conversation_log ,
api_key = api_key ,
)
# Assert
expected_responses = [ " Testatron " , " testatron " ]
assert len ( response ) > 0
assert any ( [ expected_response in response for expected_response in expected_responses ] ) , (
" Expected [T|t]estatron in response but got " + response
)
# ----------------------------------------------------------------------------------------------------
@pytest.mark.skipif (
api_key is None , reason = " Set api_key variable to your OpenAI API key from https://beta.openai.com/account/api-keys "
)
def test_answer_from_chat_history_and_previously_retrieved_content ( ) :
" Chatbot needs to use context in previous notes and chat history to answer question "
# Arrange
conversation_log = { " chat " : [ ] }
message_list = [
( " Hello, my name is Testatron. Who are you? " , " Hi, I am Khoj, a personal assistant. How can I help? " , " " ) ,
( " When was I born? " , " You were born on 1st April 1984. " , " Testatron was born on 1st April 1984 in Testville. " ) ,
]
# Generate conversation logs
for user_message , gpt_message , context in message_list :
conversation_log [ " chat " ] + = message_to_log ( user_message , gpt_message , { " context " : context } )
# Act
response = converse (
text = " " , # Assume no context retrieved from notes for the user_query
user_query = " Where was I born? " ,
conversation_log = conversation_log ,
api_key = api_key ,
)
# Assert
assert len ( response ) > 0
# Infer who I am and use that to infer I was born in Testville using chat history and previously retrieved notes
assert " Testville " in response
# ----------------------------------------------------------------------------------------------------
@pytest.mark.skipif (
api_key is None , reason = " Set api_key variable to your OpenAI API key from https://beta.openai.com/account/api-keys "
)
def test_answer_from_chat_history_and_currently_retrieved_content ( ) :
" Chatbot needs to use context across currently retrieved notes and chat history to answer question "
# Arrange
conversation_log = { " chat " : [ ] }
message_list = [
( " Hello, my name is Testatron. Who are you? " , " Hi, I am Khoj, a personal assistant. How can I help? " , " " ) ,
( " When was I born? " , " You were born on 1st April 1984. " , " " ) ,
]
# Generate conversation logs
for user_message , gpt_message , context in message_list :
conversation_log [ " chat " ] + = message_to_log ( user_message , gpt_message , { " context " : context } )
# Act
response = converse (
text = " Testatron was born on 1st April 1984 in Testville. " , # Assume context retrieved from notes for the user_query
user_query = " Where was I born? " ,
conversation_log = conversation_log ,
api_key = api_key ,
2023-03-14 18:27:58 +01:00
)
2021-11-26 12:53:03 +01:00
2023-03-15 00:44:16 +01:00
# Assert
assert len ( response ) > 0
assert " Testville " in response
# ----------------------------------------------------------------------------------------------------
@pytest.mark.skipif (
api_key is None , reason = " Set api_key variable to your OpenAI API key from https://beta.openai.com/account/api-keys "
)
def test_no_answer_in_chat_history_or_retrieved_content ( ) :
" Chatbot should say don ' t know as not enough contexts in chat history or retrieved to answer question "
# Arrange
conversation_log = { " chat " : [ ] }
message_list = [
( " Hello, my name is Testatron. Who are you? " , " Hi, I am Khoj, a personal assistant. How can I help? " , " " ) ,
( " When was I born? " , " You were born on 1st April 1984. " , " " ) ,
]
# Generate conversation logs
for user_message , gpt_message , context in message_list :
conversation_log [ " chat " ] + = message_to_log ( user_message , gpt_message , { " context " : context } )
2022-01-12 16:36:01 +01:00
# Act
2023-02-17 17:04:26 +01:00
response = converse (
2023-03-14 18:27:58 +01:00
text = " " , # Assume no context retrieved from notes for the user_query
2023-03-15 00:44:16 +01:00
user_query = " Where was I born? " ,
2023-03-14 18:27:58 +01:00
conversation_log = conversation_log ,
2023-02-17 17:04:26 +01:00
api_key = api_key ,
)
2021-11-26 12:53:03 +01:00
# Assert
2023-03-15 00:44:16 +01:00
expected_responses = [ " don ' t know " , " do not know " , " no information " , " do not have " , " don ' t have " ]
2021-11-26 12:53:03 +01:00
assert len ( response ) > 0
2023-03-15 00:44:16 +01:00
assert any ( [ expected_response in response for expected_response in expected_responses ] )
# ----------------------------------------------------------------------------------------------------
@pytest.mark.skipif (
api_key is None , reason = " Set api_key variable to your OpenAI API key from https://beta.openai.com/account/api-keys "
)
def test_answer_requires_current_date_awareness ( ) :
" Chatbot should be able to answer questions relative to current date using provided notes "
# Arrange
context = f """
# {datetime.now().strftime("%Y-%m-%d")} "Naco Taco" "Tacos for Dinner"
Expenses : Food : Dining 10.00 USD
# {datetime.now().strftime("%Y-%m-%d")} "Sagar Ratna" "Dosa for Lunch"
Expenses : Food : Dining 10.00 USD
# 2020-04-01 "SuperMercado" "Bananas"
Expenses : Food : Groceries 10.00 USD
# 2020-01-01 "Naco Taco" "Burittos for Dinner"
Expenses : Food : Dining 10.00 USD
"""
# Act
response = converse (
text = context , # Assume context retrieved from notes for the user_query
user_query = " What did I have for Dinner today? " ,
api_key = api_key ,
)
# Assert
expected_responses = [ " tacos " , " Tacos " ]
assert len ( response ) > 0
assert any ( [ expected_response in response for expected_response in expected_responses ] ) , (
" Expected [T|t]acos in response, but got: " + response
)
# ----------------------------------------------------------------------------------------------------
@pytest.mark.skipif (
api_key is None , reason = " Set api_key variable to your OpenAI API key from https://beta.openai.com/account/api-keys "
)
def test_answer_requires_date_aware_aggregation_across_provided_notes ( ) :
" Chatbot should be able to answer questions that require date aware aggregation across multiple notes "
# Arrange
context = f """
# {datetime.now().strftime("%Y-%m-%d")} "Naco Taco" "Tacos for Dinner"
Expenses : Food : Dining 10.00 USD
# {datetime.now().strftime("%Y-%m-%d")} "Sagar Ratna" "Dosa for Lunch"
Expenses : Food : Dining 10.00 USD
# 2020-04-01 "SuperMercado" "Bananas"
Expenses : Food : Groceries 10.00 USD
# 2020-01-01 "Naco Taco" "Burittos for Dinner"
Expenses : Food : Dining 10.00 USD
"""
# Act
response = converse (
text = context , # Assume context retrieved from notes for the user_query
user_query = " How much did I spend on dining this year? " ,
api_key = api_key ,
)
# Assert
assert len ( response ) > 0
assert " 20 " in response
# ----------------------------------------------------------------------------------------------------
@pytest.mark.skipif (
api_key is None , reason = " Set api_key variable to your OpenAI API key from https://beta.openai.com/account/api-keys "
)
def test_answer_general_question_not_in_chat_history_or_retrieved_content ( ) :
" Chatbot should be able to answer general questions not requiring looking at chat history or notes "
# Arrange
conversation_log = { " chat " : [ ] }
message_list = [
( " Hello, my name is Testatron. Who are you? " , " Hi, I am Khoj, a personal assistant. How can I help? " , " " ) ,
( " When was I born? " , " You were born on 1st April 1984. " , " " ) ,
( " Where was I born? " , " You were born Testville. " , " " ) ,
]
# Generate conversation logs
for user_message , gpt_message , context in message_list :
conversation_log [ " chat " ] + = message_to_log ( user_message , gpt_message , { " context " : context } )
# Act
response = converse (
text = " " , # Assume no context retrieved from notes for the user_query
user_query = " Write a haiku about unit testing " ,
conversation_log = conversation_log ,
api_key = api_key ,
)
# Assert
expected_responses = [ " test " , " Test " ]
assert len ( response . splitlines ( ) ) == 3 # haikus are 3 lines long
assert any ( [ expected_response in response for expected_response in expected_responses ] ) , (
" Expected [T|t]est in response, but got: " + response
)
# ----------------------------------------------------------------------------------------------------
@pytest.mark.xfail ( reason = " Chatbot not consistently capable of asking for clarification yet. " )
@pytest.mark.skipif (
api_key is None , reason = " Set api_key variable to your OpenAI API key from https://beta.openai.com/account/api-keys "
)
def test_ask_for_clarification_if_not_enough_context_in_question ( ) :
" Chatbot should ask for clarification if question cannot be answered unambiguously with the provided context "
# Arrange
context = f """
# Ramya
My sister , Ramya , is married to Kali Devi . They have 2 kids , Ravi and Rani .
# Fang
My sister , Fang Liu is married to Xi Li . They have 1 kid , Xiao Li .
# Aiyla
My sister , Aiyla is married to Tolga . They have 3 kids , Yildiz , Ali and Ahmet .
"""
# Act
response = converse (
text = context , # Assume context retrieved from notes for the user_query
user_query = " How many kids does my older sister have? " ,
api_key = api_key ,
)
# Assert
expected_responses = [ " which sister " , " Which sister " , " which of your sister " , " Which of your sister " ]
assert any ( [ expected_response in response for expected_response in expected_responses ] ) , (
" Expected chatbot to ask for clarification in response, but got: " + response
)