diff --git a/week8/community_contributions/muthama/price_is_right_final.py b/week8/community_contributions/muthama/price_is_right_final.py new file mode 100644 index 0000000..eb32e6b --- /dev/null +++ b/week8/community_contributions/muthama/price_is_right_final.py @@ -0,0 +1,164 @@ +import logging +import queue +import threading +import time +import gradio as gr +import plotly.graph_objects as go + +from deal_agent_framework import DealAgentFramework +# from agents.deals import Opportunity, Deal +from log_utils import reformat + + + +class QueueHandler(logging.Handler): + def __init__(self, log_queue): + super().__init__() + self.log_queue = log_queue + + def emit(self, record): + self.log_queue.put(self.format(record)) + +def html_for(log_data): + output = '
'.join(log_data[-18:]) + return f""" +
+ {output} +
+ """ + +def setup_logging(log_queue): + handler = QueueHandler(log_queue) + formatter = logging.Formatter( + "[%(asctime)s] %(message)s", + datefmt="%Y-%m-%d %H:%M:%S %z", + ) + handler.setFormatter(formatter) + logger = logging.getLogger() + logger.addHandler(handler) + logger.setLevel(logging.INFO) + + +class App: + + def __init__(self): + self.agent_framework = None + + def get_agent_framework(self): + if not self.agent_framework: + self.agent_framework = DealAgentFramework() + self.agent_framework.init_agents_as_needed() + return self.agent_framework + + def run(self): + with gr.Blocks(title="The Price is Right", fill_width=True) as ui: + + log_data = gr.State([]) + + def table_for(opps): + return [[opp.deal.product_description, f"${opp.deal.price:.2f}", f"${opp.estimate:.2f}", f"${opp.discount:.2f}", opp.deal.url] for opp in opps] + + def update_output(log_data, log_queue, result_queue): + def live_table(): + return table_for(self.get_agent_framework().memory) + + final_result = None + while True: + try: + message = log_queue.get_nowait() + log_data.append(reformat(message)) + yield log_data, html_for(log_data), live_table() + continue + except queue.Empty: + pass + + try: + final_result = result_queue.get_nowait() + yield log_data, html_for(log_data), (final_result or live_table()) + break + except queue.Empty: + time.sleep(0.05) + + def get_plot(): + documents, vectors, colors = DealAgentFramework.get_plot_data(max_datapoints=1000) + # Create the 3D scatter plot + fig = go.Figure(data=[go.Scatter3d( + x=vectors[:, 0], + y=vectors[:, 1], + z=vectors[:, 2], + mode='markers', + marker=dict(size=2, color=colors, opacity=0.7), + )]) + + fig.update_layout( + scene=dict(xaxis_title='x', + yaxis_title='y', + zaxis_title='z', + aspectmode='manual', + aspectratio=dict(x=2.2, y=2.2, z=1), # Make x-axis twice as long + camera=dict( + eye=dict(x=1.6, y=1.6, z=0.8) # Adjust camera position + )), + height=400, + margin=dict(r=5, b=1, l=5, t=2) + ) + + return fig + + def do_run(): + new_opportunities = self.get_agent_framework().run() + table = table_for(new_opportunities) + return table + + def run_with_logging(initial_log_data): + log_queue = queue.Queue() + result_queue = queue.Queue() + setup_logging(log_queue) + + def worker(): + result = do_run() # this updates memory during execution + result_queue.put(result) + + thread = threading.Thread(target=worker, daemon=True) + thread.start() + + for log_data, output_html, table in update_output(initial_log_data, log_queue, result_queue): + yield log_data, output_html, table + + def do_select(selected_index: gr.SelectData): + opportunities = self.get_agent_framework().memory + row = selected_index.index[0] + opportunity = opportunities[row] + self.get_agent_framework().planner.messenger.alert(opportunity) + + with gr.Row(): + gr.Markdown('
The Price is Right - Autonomous Agent Framework that hunts for deals
') + with gr.Row(): + gr.Markdown('
A proprietary fine-tuned LLM deployed on Modal and a RAG pipeline with a frontier model collaborate to send push notifications with great online deals.
') + with gr.Row(): + opportunities_dataframe = gr.Dataframe( + headers=["Deals found so far", "Price", "Estimate", "Discount", "URL"], + wrap=True, + column_widths=[6, 1, 1, 1, 3], + row_count=10, + col_count=5, + max_height=400, + ) + with gr.Row(): + with gr.Column(scale=1): + logs = gr.HTML() + with gr.Column(scale=1): + plot = gr.Plot(value=get_plot(), show_label=False) + + ui.load(run_with_logging, inputs=[log_data], outputs=[log_data, logs, opportunities_dataframe]) + + timer = gr.Timer(value=300, active=True) + timer.tick(run_with_logging, inputs=[log_data], outputs=[log_data, logs, opportunities_dataframe]) + + opportunities_dataframe.select(do_select) + + ui.launch(share=False, inbrowser=True) + +if __name__=="__main__": + App().run() + \ No newline at end of file diff --git a/week8/community_contributions/muthama/week8_exercise_solution-Stephen.ipynb b/week8/community_contributions/muthama/week8_exercise_solution-Stephen.ipynb new file mode 100644 index 0000000..75da0cf --- /dev/null +++ b/week8/community_contributions/muthama/week8_exercise_solution-Stephen.ipynb @@ -0,0 +1,88 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "a71ed017-e1b0-4299-88b3-f0eb05adc4df", + "metadata": {}, + "source": [ + "# The Price is Right\n", + "\n", + "The final step is to build a User Interface\n", + "\n", + "We will use more advanced aspects of Gradio - building piece by piece." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b77940b8", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "133.0\n" + ] + } + ], + "source": [ + "import modal" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6449363f", + "metadata": {}, + "outputs": [], + "source": [ + "!modal deploy -m pricer_service2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3c67160e", + "metadata": {}, + "outputs": [], + "source": [ + "Pricer = modal.Cls.from_name(\"pricer-service\", \"Pricer\")\n", + "pricer = Pricer()\n", + "reply = pricer.price.remote(\"Quadcast HyperX condenser mic, connects via usb-c to your computer for crystal clear audio\")\n", + "print(reply)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "48506465-1c7a-433f-a665-b277a8b4665c", + "metadata": {}, + "outputs": [], + "source": [ + "!python price_is_right_final.py" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +}