Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions amadeusgpt/app_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ def __init__(self, amadeus_answer=None, json_entry=None):
self.data = {}
self.data["role"] = "ai"
if amadeus_answer:
self.data.update(amadeus_answer.asdict())
self.data.update(amadeus_answer)

def render(self):
"""
Expand Down Expand Up @@ -305,7 +305,8 @@ def summon_the_beast():
def ask_amadeus(question):
answer = AMADEUS.chat_iteration(
question
) # use chat_iteration to support some magic commands
).asdict() # use chat_iteration to support some magic commands

# Get the current process
AmadeusLogger.log_process_memory(log_position="ask_amadeus")
return answer
Expand Down
24 changes: 11 additions & 13 deletions amadeusgpt/brains/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,23 +198,23 @@ def manage_memory(cls, user_msg, bot_answer):

# it's maybe important to keep the answer in the context window. Otherwise we are teaching the model to output empty string
# need to be very careful as LLMs fewshot learning can wrongly link the answer (even if it is invalid) to the question
if bot_answer["ndarray"]:
cls.update_history("assistant", bot_answer["function_code"])
if bot_answer.ndarray:
cls.update_history("assistant", bot_answer.function_code)
else:

answer_for_memory = ''

if bot_answer["function_code"]:
for code in bot_answer["function_code"]:
if bot_answer.function_code:
for code in bot_answer.function_code:
answer_for_memory += code

answer_for_memory += '\n' + bot_answer['str_answer']
answer_for_memory += '\n' + bot_answer.str_answer

captions = ''
if isinstance(bot_answer['plots'], list):
for plot in bot_answer['plots']:
if plot['plot_caption'] !='':
captions+=plot['plot_caption']
if isinstance(bot_answer.plots, list):
for plot in bot_answer.plots:
if plot.plot_caption !='':
captions+=plot.plot_caption
if captions!='':
answer_for_memory+=captions

Expand Down Expand Up @@ -279,12 +279,10 @@ def manage_task_programs(cls, symbol_name, bot_answer):

# if there is valid function code and there is a corresponding task program in the task program table
if (
"function_code" in bot_answer
bot_answer.function_code
and symbol_name in AnimalBehaviorAnalysis.task_programs
):
AnimalBehaviorAnalysis.task_programs[symbol_name] = bot_answer[
"function_code"
]
AnimalBehaviorAnalysis.task_programs[symbol_name] = bot_answer.function_code

@classmethod
def print_history(cls):
Expand Down
26 changes: 8 additions & 18 deletions amadeusgpt/datamodel/amadeus_answer.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
class Figure:
plot_type: str
axes: list
figure: plt.Figure
figure: plt.Figure = field(default=None)
plot_caption: str = ""


Expand All @@ -38,12 +38,6 @@ class AmadeusAnswer:
ndarray: List[any] = field(default_factory=list)
role = "ai"

def __getitem__(self, key):
return getattr(self, key)

def __setitem__(self, key, value):
setattr(self, key, value)

def asdict(self):
return dataclasses.asdict(self)

Expand Down Expand Up @@ -72,7 +66,7 @@ def parse_plot_tuple(self, tu, plot_type="general_plot"):
data["plot_caption"] = ""

if "figure" in data and "axes" in data:
ret = Figure(**data)
ret = Figure(**data)
else:
ret = None

Expand Down Expand Up @@ -149,27 +143,25 @@ def from_function_returns(cls, function_returns, function_code, thought_process)
function_returns: Tuple(ret1, ret2, ret3 ... )
populate the data fields from function returns.
"""
instance = AmadeusAnswer()
instance = AmadeusAnswer()
instance.function_code = function_code
instance.chain_of_thoughts = thought_process
# If the function returns are tuple, try to parse the tuple and generate plots
# If the function returns are tuple, try to parse the tuple and generate plots
if isinstance(function_returns, tuple):
# if the returns contain plots, the return must be tuple (fig, axes)
_plots = instance.parse_plot_tuple(function_returns)
if _plots:
instance.plots.append(_plots)

instance.plots.append(_plots)
# without wrapping it in a list, the following for loop can cause problems
if isinstance(function_returns, tuple):
function_returns = list(function_returns)
else:
function_returns = [function_returns]

for function_return in function_returns:
if isinstance(function_return, (pd.Series, pd.DataFrame, np.ndarray)):
if not isinstance(function_return, np.ndarray):
function_return = function_return.to_numpy()
instance.ndarray.append(function_return)
if isinstance(function_return, (pd.Series,pd.DataFrame)):
function_return = function_return.to_numpy()
elif isinstance(function_return, AnimalEvent):
instance.get_plots_for_animal_events(function_return)
elif isinstance(function_return, AnimalAnimalEvent):
Expand All @@ -180,6 +172,4 @@ def from_function_returns(cls, function_returns, function_code, thought_process)
(matplotlib.figure.Figure, matplotlib.axes._axes.Axes),
):
instance.str_answer = str(function_return)


return instance
4 changes: 2 additions & 2 deletions amadeusgpt/gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ def select_roi_from_video(video_filename):
fig, axs = plt.subplots(1)
axs.imshow(frame)
selector = ROISelector(axs)
plt.show()
#plt.show()
return selector.paths


Expand Down Expand Up @@ -94,4 +94,4 @@ def select_roi_from_plot(fig, ax):
plt.ylabel("Count of True values in mask")
plt.title("Animal occurrence in ROI")
# Show the plot
plt.show()
#plt.show()
45 changes: 18 additions & 27 deletions amadeusgpt/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -305,45 +305,38 @@ def chat(
text,
function_code,
thought_process,
) = cls.code_generator_brain.parse_openai_response(response)




) = cls.code_generator_brain.parse_openai_response(response)

with open("temp_for_debug.json", "w") as f:
out = {'function_code': function_code,
'query': rephrased_user_msg}
json.dump(out, f, indent=4)
# handle_function_codes gives the answer with function outputs
# handle_function_codes gives the answer with function outputs
amadeus_answer = cls.core_loop(
rephrased_user_msg, text, function_code, thought_process
)
amadeus_answer = amadeus_answer.asdict()

# export the generated function to code_output
if amadeus_answer['function_code'] and code_output != "":
if amadeus_answer.function_code and code_output != "":
cls.export_function_code(
original_user_msg, amadeus_answer["function_code"], code_output
original_user_msg, amadeus_answer.function_code, code_output
)

# if there is an error or the function code is empty, we want to make sure we prevent ChatGPT to learn to output nothing from few-shot learning
# is this used anymore?
if amadeus_answer["has_error"]:
if amadeus_answer.has_error:
cls.code_generator_brain.context_window[-1][
"content"
] += "\n While executing the code above, there was error so it is not correct answer\n"

elif amadeus_answer["has_error"]:
elif amadeus_answer.has_error:
cls.code_generator_brain.context_window.pop()
cls.code_generator_brain.history.pop()
else:
# needs to manage memory of Amadeus for context window management and state restore etc.
# we have it remember user's original question instead of the rephrased one for better
cls.code_generator_brain.manage_memory(
original_user_msg, copy.deepcopy(amadeus_answer)
)

original_user_msg, amadeus_answer
)
return amadeus_answer

# this should become an async function so the user can continue to ask question
Expand All @@ -354,7 +347,7 @@ def execute_python_function(
function_code,
):
# we might register a few helper functions into globals()
result = None
result = None
exec(function_code, globals())
if "task_program" not in globals():
return None
Expand Down Expand Up @@ -534,8 +527,8 @@ def collect_function_result(cls, function_returns, function_code, thought_proces
function_returns, function_code, thought_process
)

if cls.plot:
plt.show()
#if cls.plot:
# plt.show()

# deduplicate as both events and plot could append plots
return amadeus_answer
Expand Down Expand Up @@ -608,9 +601,7 @@ def chat_iteration(cls, user_input, code_output="", functions=None, rephrased=[]
code_output=code_output,
functions=functions,
)
if not isinstance(answer, AmadeusAnswer):
answer = AmadeusAnswer.fromdict(answer)


return answer

# @classmethod
Expand Down Expand Up @@ -653,19 +644,19 @@ def compile_amadeus_answer_when_no_error(
):
explanation = cls.explainer_brain.generate_explanation(
user_query,
amadeus_answer["chain_of_thoughts"],
amadeus_answer["str_answer"],
amadeus_answer["plots"]
amadeus_answer.chain_of_thoughts,
amadeus_answer.str_answer,
amadeus_answer.plots
)
amadeus_answer["summary"] = explanation
amadeus_answer.summary = explanation
AmadeusLogger.info("Generated explanation from the explainer:")
AmadeusLogger.info(explanation)
else:
amadeus_answer["summary"] = ""
amadeus_answer.summary = ""
else:
# if gpt apologies or asks for clarification, it will be no error but no function
amadeus_answer = AmadeusAnswer()
amadeus_answer["chain_of_thoughts"] = thought_process
amadeus_answer.chain_of_thoughts = thought_process
return amadeus_answer

@classmethod
Expand Down