From 0fe9133bac41eb983b83dca372de0e5b858b8667 Mon Sep 17 00:00:00 2001 From: Lance Martin Date: Tue, 5 Nov 2024 20:52:32 -0800 Subject: [PATCH] Add ntbks --- README.md | 74 +++++++ ntbk/audio_ux.ipynb | 377 +++++++++++++++++++++++++++++++++ ntbk/connecting_to_graph.ipynb | 112 ++++++++++ requirements.txt | 3 +- 4 files changed, 565 insertions(+), 1 deletion(-) create mode 100644 README.md create mode 100644 ntbk/audio_ux.ipynb create mode 100644 ntbk/connecting_to_graph.ipynb diff --git a/README.md b/README.md new file mode 100644 index 0000000..4362002 --- /dev/null +++ b/README.md @@ -0,0 +1,74 @@ +# Task mAIstro + +Task mAIstro allows you to track your ToDos through natural conversation: + +* Tell it about yourself +* Tell it tasks you need to complete +* Update tasks easily through conversation +* Ask task mAIstro what to to next + +Task mAIstro combines an agent with long-term memory to produce a highly personalized task management experience. All information can be stored locally using the LangGraph Studio Desktop App or it can be deployed to LangGraph Platform. + +## Key Features + +### Natural Language Interface + +- Create and update tasks through natural conversation +- No need to learn specific commands or syntax +- Just chat with the assistant as you would with a human + +### Smart Memory System + +Task mAIstro maintains three types of memory: + +1. **ToDo List** + - Tasks with descriptions + - Estimated completion times + - Deadlines + - Actionable solutions + - Status tracking + +2. **User Profile** + - Remembers personal details + - Tracks preferences + - Maintains context about your life and work + +3. **Update Instructions** + - Learns how you prefer tasks to be managed + - Adapts to your organizational style + - Maintains your preferences for task updates + +## Quickstart + +You can deploy the app locally with the LangGraph Studio Desktop App or to LangGraph Cloud. + +### Locally + +Populate the `.env` file with your `OPENAI_API_KEY` key: +``` +cp .env.example .env +``` + +Download the LangGraph Studio desktop app for Mac [here](https://github.com/langchain-ai/langgraph-studio?tab=readme-ov-file#download). + +Load this repository as a project in LangGraph Studio. + +Start chatting with the task mAIstro! + +### LangGraph Cloud + +In your LangSmith account, create a new deployment with this repository's `main` branch. + +Set your `OPENAI_API_KEY` as a secret when creating the deployment. + +Interact with the deployment through the LangGraph Studio web UI. + +Use the `ntbk/connecting_to_graph.ipynb` notebook to interact with the deployed graph via . + +## Audio UX + +See the `ntbk/audio_ux.ipynb` notebook for an example of how to add an audio interface to your graph. + +## Learning More + +See [Module 5 of our LangChain Academy Course on LangGraph](https://academy.langchain.com/courses/intro-to-langgraph) to learn how to build this app from scratch! diff --git a/ntbk/audio_ux.ipynb b/ntbk/audio_ux.ipynb new file mode 100644 index 0000000..2f27714 --- /dev/null +++ b/ntbk/audio_ux.ipynb @@ -0,0 +1,377 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "b4cd66e8", + "metadata": {}, + "source": [ + "# Audio UX\n", + "\n", + "An emerging agent interaction pattern [is](https://www.technologyreview.com/2024/09/24/1104422/openai-released-its-advanced-voice-mode-to-more-people-heres-how-to-get-it/) [voice](https://openai.com/index/introducing-the-realtime-api/). \n", + "\n", + "Let's create a simple voice interface around `task_mAIstro`: \n", + "\n", + "* We use [OpenAI's Whisper](https://platform.openai.com/docs/guides/speech-to-text) to transcribe audio to text on the input.\n", + "* We use [ElevenLabs Python API](https://github.com/elevenlabs/elevenlabs-python) to convert text to speech on the output\n", + "\n", + "### Install dependencies" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "d1fd88e4", + "metadata": {}, + "outputs": [], + "source": [ + "%%capture --no-stderr\n", + "%pip install -U langchain_openai langgraph langchain_core sounddevice scipy elevenlabs " + ] + }, + { + "cell_type": "markdown", + "id": "44b724c3", + "metadata": {}, + "source": [ + "Ensure you have `ffmpeg` installed for using ElevenLabs. \n", + "\n", + "On MacOS, you can install it with `brew install ffmpeg` or `pip install ffmpeg-python`.\n", + "\n", + "### Set environment variables\n", + "\n", + "In addition to `OPENAI_API_KEY`, ensure that you have an `ELEVENLABS_API_KEY` environment variable set. \n", + "\n", + "You can get one [here](https://elevenlabs.io/api).\n", + "\n", + "We'll also use [LangSmith](https://smith.langchain.com/) to connect with our deployment." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c7311ebb", + "metadata": {}, + "outputs": [], + "source": [ + "import os, getpass\n", + "\n", + "def _set_env(var: str):\n", + " # Check if the variable is set in the OS environment\n", + " env_value = os.environ.get(var)\n", + " if not env_value:\n", + " # If not set, prompt the user for input\n", + " env_value = getpass.getpass(f\"{var}: \")\n", + " \n", + " # Set the environment variable for the current process\n", + " os.environ[var] = env_value\n", + "\n", + "_set_env(\"LANGCHAIN_API_KEY\")\n", + "_set_env(\"ELEVENLABS_API_KEY\")\n", + "_set_env(\"OPENAI_API_KEY\")" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "d9b251d4-3ff5-4ce1-9a8b-ebc09934658e", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "os.environ[\"ELEVENLABS_API_KEY\"] = \"sk_d5e2758a4020c6eb4b752a74d02122512dc26ddbd223eb0e\"\n", + "os.environ[\"LANGCHAIN_API_KEY\"] = \"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\"\n", + "os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n", + "os.environ[\"LANGCHAIN_PROJECT\"] = \"task-maistro-deployment\"" + ] + }, + { + "cell_type": "markdown", + "id": "45f5e99c-9c95-4ec9-a879-45cb80c76179", + "metadata": {}, + "source": [ + "### Graph connection\n", + "\n", + "Let's first test that we can connect to the graph. \n", + "\n", + "We can find the URL of the graph in the [LangSmith](https://smith.langchain.com/) UI.\n", + "\n", + "We get the `url` from the deployment page. " + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "b2bdfbed-694a-4dbc-8ddf-e2649ec28181", + "metadata": {}, + "outputs": [], + "source": [ + "from langgraph.pregel.remote import RemoteGraph\n", + "from langchain_core.messages import convert_to_messages\n", + "from langchain_core.messages import HumanMessage, SystemMessage\n", + "\n", + "# Get remote graph\n", + "url = \"https://task-maistro-1b681add7a2b549499bb0cd21a7e5be4.default.us.langgraph.app\"\n", + "graph_name = \"task_maistro\" \n", + "remote_graph = RemoteGraph(graph_name, url=url)" + ] + }, + { + "cell_type": "markdown", + "id": "1169896f", + "metadata": {}, + "source": [ + "### Add audio input and output nodes to our remote graph\n", + "\n", + "Add the `audio input` and `audio output` nodes to our remote graph. \n", + "\n", + "The audio input node will:\n", + "* Record audio from the microphone until the user presses Enter\n", + "* Use Whisper to transcribe the audio to text\n", + "\n", + "The text will be passed the the deployed task mAIstro application. \n", + "\n", + "The audio output node: \n", + "* Get the response from task mAIstro\n", + "* Convert the response to speech and play it back to the user using ElevenLabs" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "ea79d376-af11-4a85-af8e-9da2f8f1da2d", + "metadata": {}, + "outputs": [ + { + "data": { + "image/jpeg": "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7q2P13945vse6tj9d/eNCvO6nnPhM0NcGRzfY91bH67+8c32PdWx+u/vGhXndTznwmaGuDI5vse6tj9d/eOb7HurY/Xf3jQrzup5z4TNDXBkc32PdWx+u/vHN9j3Vsfrv7xoV53U858JmhrgyOb7HurY/Xf3jm+x7q2P13940K87qec+EzQ1wZHN9j3Vsfrv7xzfY91bH67+8aFed1POfCZoa4Mjm+x7q2P13945vse6tj9d/eNCvO6nnPhM0NcGRzfY91bH67+8c32PdWx+u/vGhXndTznwmaGuDI5vse6tj9d/eOb7HurY/Xf3jQrzup5z4TNDXBkc32PdWx+u/vHN9j3Vsfrv7xoV53U858JmhrgyOb7HurY/Xf3jm+x7q2P13940K87qec+EzQ1wZHN9j3Vsfrv7xzfY91bH67+8aFed1POfCZoa4Mjm+x7q2P13945vse6tj9d/eNCvO6nnPhM0NcGRzfY91bH67+8c32PdWx+u/vGhXndTznwmaGuDI5vse6tj9d/eOb7HurY/Xf3jQrzup5z4TNDXBkc32PdWx+u/vHN9j3Vsfrv7xoV53U858JmhrgyOb7HurY/Xf3jm+x7q2P13940K87qec+EzQ1wZHN9j3Vsfrv7xzfY91bH67+8aFed1POfCZoa4Mjm+x7q2P13945vse6tj9d/eNCvO6nnPhM0NcGRzfY91bH67+8c32PdWx+u/vGhXndTznwmaGuDI5vse6tj9d/eOb7HurY/Xf3jQrzup5z4TNDXBkc32PdWx+u/vHN9j3Vsfrv7xoV53U858JmhrgyOb7HurY/Xf3jm+x7q2P13940K87qec+EzQ1wZHN9j3Vsfrv7xzfY91bH67+8aFed1POfCZoa4Mjm+x7q2P13945vse6tj9d/eNCvO6nnPhM0NcGRzfY91bH67+8c32PdWx+u/vGhXndTznwmaGuDI5vse6tj9d/eOb7HurY/Xf3jQrzup5z4TNDXBkc32PdWx+u/vHN9j3Vsfrv7xoV53U858JmhrgyOb7HurY/Xf3jm+x7q2P13940K87qec+EzQ1wZHN9j3Vsfrv7xzfY91bH67+8aFed1POfCZoa4Mjm+x7q2P13945vse6tj9d/eNCvO6nnPhM0NcGRzfY91bH67+8c32PdWx+u/vGhXndTznwmaGuDI5vse6tj9d/eOb7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", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import io\n", + "import threading\n", + "import numpy as np\n", + "import sounddevice as sd\n", + "from scipy.io.wavfile import write\n", + "from IPython.display import Image, display\n", + "\n", + "import openai\n", + "from openai import OpenAI\n", + "\n", + "from elevenlabs import play, VoiceSettings\n", + "from elevenlabs.client import ElevenLabs\n", + "\n", + "from langgraph.graph import StateGraph, MessagesState, END, START\n", + "\n", + "# Initialize OpenAI client\n", + "openai_client = OpenAI()\n", + "\n", + "# Initialize ElevenLabs client\n", + "elevenlabs_client = ElevenLabs(api_key=os.getenv(\"ELEVENLABS_API_KEY\"))\n", + "\n", + "def record_audio_until_stop(state: MessagesState):\n", + "\n", + " \"\"\"Records audio from the microphone until Enter is pressed, then saves it to a .wav file.\"\"\"\n", + " \n", + " audio_data = [] # List to store audio chunks\n", + " recording = True # Flag to control recording\n", + " sample_rate = 16000 # (kHz) Adequate for human voice frequency\n", + "\n", + " def record_audio():\n", + " \"\"\"Continuously records audio until the recording flag is set to False.\"\"\"\n", + " nonlocal audio_data, recording\n", + " with sd.InputStream(samplerate=sample_rate, channels=1, dtype='int16') as stream:\n", + " print(\"Recording your instruction! ... Press Enter to stop recording.\")\n", + " while recording:\n", + " audio_chunk, _ = stream.read(1024) # Read audio data in chunks\n", + " audio_data.append(audio_chunk)\n", + "\n", + " def stop_recording():\n", + " \"\"\"Waits for user input to stop the recording.\"\"\"\n", + " input() # Wait for Enter key press\n", + " nonlocal recording\n", + " recording = False\n", + "\n", + " # Start recording in a separate thread\n", + " recording_thread = threading.Thread(target=record_audio)\n", + " recording_thread.start()\n", + "\n", + " # Start a thread to listen for the Enter key\n", + " stop_thread = threading.Thread(target=stop_recording)\n", + " stop_thread.start()\n", + "\n", + " # Wait for both threads to complete\n", + " stop_thread.join()\n", + " recording_thread.join()\n", + "\n", + " # Stack all audio chunks into a single NumPy array and write to file\n", + " audio_data = np.concatenate(audio_data, axis=0)\n", + " \n", + " # Convert to WAV format in-memory\n", + " audio_bytes = io.BytesIO()\n", + " write(audio_bytes, sample_rate, audio_data) # Use scipy's write function to save to BytesIO\n", + " audio_bytes.seek(0) # Go to the start of the BytesIO buffer\n", + " audio_bytes.name = \"audio.wav\" # Set a filename for the in-memory file\n", + "\n", + " # Transcribe via Whisper\n", + " transcription = openai_client.audio.transcriptions.create(\n", + " model=\"whisper-1\", \n", + " file=audio_bytes,\n", + " )\n", + "\n", + " # Print the transcription\n", + " print(\"Here is the transcription:\", transcription.text)\n", + "\n", + " # Write to messages \n", + " return {\"messages\": [HumanMessage(content=transcription.text)]}\n", + "\n", + "def play_audio(state: MessagesState):\n", + " \n", + " \"\"\"Plays the audio response from the remote graph with ElevenLabs.\"\"\"\n", + "\n", + " # Response from the agent \n", + " response = state['messages'][-1]\n", + "\n", + " # Prepare text by replacing ** with empty strings\n", + " # These can cause unexpected behavior in ElevenLabs\n", + " cleaned_text = response.content.replace(\"**\", \"\")\n", + " \n", + " # Call text_to_speech API with turbo model for low latency\n", + " response = elevenlabs_client.text_to_speech.convert(\n", + " voice_id=\"pNInz6obpgDQGcFmaJgB\", # Adam pre-made voice\n", + " output_format=\"mp3_22050_32\",\n", + " text=cleaned_text,\n", + " model_id=\"eleven_turbo_v2_5\", \n", + " voice_settings=VoiceSettings(\n", + " stability=0.0,\n", + " similarity_boost=1.0,\n", + " style=0.0,\n", + " use_speaker_boost=True,\n", + " ),\n", + " )\n", + " \n", + " # Play the audio back\n", + " play(response)\n", + "\n", + "# Define parent graph\n", + "builder = StateGraph(MessagesState)\n", + "\n", + "# Add remote graph directly as a node\n", + "builder.add_node(\"audio_input\", record_audio_until_stop)\n", + "builder.add_node(\"todo_app\", remote_graph)\n", + "builder.add_node(\"audio_output\", play_audio)\n", + "builder.add_edge(START, \"audio_input\")\n", + "builder.add_edge(\"audio_input\", \"todo_app\")\n", + "builder.add_edge(\"todo_app\",\"audio_output\")\n", + "builder.add_edge(\"audio_output\",END)\n", + "graph = builder.compile()\n", + "\n", + "display(Image(graph.get_graph(xray=1).draw_mermaid_png()))" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "00a73196-6e53-44c5-8cdb-bf75405c92b0", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "================================\u001b[1m Human Message \u001b[0m=================================\n", + "\n", + "Follow the user's instructions:\n", + "Recording your instruction! ... Press Enter to stop recording.\n" + ] + }, + { + "name": "stdin", + "output_type": "stream", + "text": [ + " \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Here is the transcription: Create a task to clean the kitchen.\n", + "================================\u001b[1m Human Message \u001b[0m=================================\n", + "\n", + "Create a task to clean the kitchen.\n", + "==================================\u001b[1m Ai Message \u001b[0m==================================\n", + "\n", + "I've added \"Clean the kitchen\" to your ToDo list. If there's anything else you need, just let me know!\n" + ] + } + ], + "source": [ + "# Set user ID for storing memories\n", + "config = {\"configurable\": {\"user_id\": \"Test-User\"}}\n", + "\n", + "# Kick off the graph, which will record user input until the user presses Enter\n", + "for chunk in graph.stream({\"messages\":HumanMessage(content=\"Follow the user's instructions:\")}, stream_mode=\"values\", config=config):\n", + " chunk[\"messages\"][-1].pretty_print()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "dbf44933-74ed-414f-8816-2580b49878df", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "dc0e513a-b53f-48e1-9abf-23ad45c46d32", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5689339a-48f2-4c54-bcfb-4ee546ef24cd", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": 40, + "id": "0419b314-8f20-419a-976a-df8e9d02701c", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.11.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/ntbk/connecting_to_graph.ipynb b/ntbk/connecting_to_graph.ipynb new file mode 100644 index 0000000..9d0a905 --- /dev/null +++ b/ntbk/connecting_to_graph.ipynb @@ -0,0 +1,112 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "b1e3eaa4", + "metadata": {}, + "source": [ + "# Connecting To The Deployment\n", + "\n", + "Connected to the [LangSmith](https://smith.langchain.com/) deployment of `task_maistro`.\n", + "\n", + "Ensure the `LANGCHAIN_API_KEY` for the LangSmith account with your deployment is set.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f4de2831", + "metadata": {}, + "outputs": [], + "source": [ + "import os, getpass\n", + "\n", + "def _set_env(var: str):\n", + " # Check if the variable is set in the OS environment\n", + " env_value = os.environ.get(var)\n", + " if not env_value:\n", + " # If not set, prompt the user for input\n", + " env_value = getpass.getpass(f\"{var}: \")\n", + " \n", + " # Set the environment variable for the current process\n", + " os.environ[var] = env_value\n", + "\n", + "_set_env(\"LANGCHAIN_API_KEY\")" + ] + }, + { + "cell_type": "markdown", + "id": "386677ba", + "metadata": {}, + "source": [ + "### Graph connection\n", + "\n", + "Let's first test that we can connect to the graph. \n", + "\n", + "We can find the URL of the graph in the [LangSmith](https://smith.langchain.com/) UI.\n", + "\n", + "We get the `url` from the deployment page. " + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": {}, + "outputs": [], + "source": [ + "from langgraph.pregel.remote import RemoteGraph\n", + "from langchain_core.messages import convert_to_messages\n", + "from langchain_core.messages import HumanMessage, SystemMessage\n", + "\n", + "# Add your deployment URL here\n", + "url = \"https://task-maistro-1b681add7a2b549499bb0cd21a7e5be4.default.us.langgraph.app\"\n", + "# Graph name is from langgraph.json\n", + "graph_name = \"task_maistro\" \n", + "# Connect to the graph\n", + "remote_graph = RemoteGraph(graph_name, url=url)" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "id": "fc8102cc", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "================================\u001b[1m Human Message \u001b[0m=================================\n", + "\n", + "Hi I'm Lance. I live in San Francisco with my wife and have a 1 year old.\n", + "==================================\u001b[1m Ai Message \u001b[0m==================================\n", + "\n", + "Hi Lance! It's great to meet you. I already have that information in my memory. How can I assist you today?\n" + ] + } + ], + "source": [ + "# Interact with the graph\n", + "user_input = \"Hi I'm Lance. I live in San Francisco with my wife and have a 1 year old.\"\n", + "config = {\"configurable\": {\"user_id\": \"Lance\"}}\n", + "for chunk in remote_graph.stream({\"messages\": [HumanMessage(content=user_input)]}, stream_mode=\"values\", config=config):\n", + " convert_to_messages(chunk[\"messages\"])[-1].pretty_print()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5851ecb", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/requirements.txt b/requirements.txt index a5930a3..4b14122 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,5 @@ langgraph langchain-core langchain-community langchain-openai -trustcall \ No newline at end of file +trustcall +ipython \ No newline at end of file