From 3be0c4348b3e158e9f6908a41ae9ddcb340c57dc Mon Sep 17 00:00:00 2001 From: Cole Medin <47287758+coleam00@users.noreply.github.com> Date: Sun, 18 Aug 2024 13:16:12 -0500 Subject: [PATCH] AI Agents Masterclass #8 - n8n AI Agent --- 8-n8n-asana-agent/.env.example | 8 + 8-n8n-asana-agent/n8n-asana-agent.py | 83 +++++++ .../n8n_Workflow_Asana_AI_Agent.json | 234 ++++++++++++++++++ .../n8n_Workflow_Create_Asana_Task.json | 68 +++++ 8-n8n-asana-agent/requirements.txt | 5 + 5 files changed, 398 insertions(+) create mode 100644 8-n8n-asana-agent/.env.example create mode 100644 8-n8n-asana-agent/n8n-asana-agent.py create mode 100644 8-n8n-asana-agent/n8n_Workflow_Asana_AI_Agent.json create mode 100644 8-n8n-asana-agent/n8n_Workflow_Create_Asana_Task.json create mode 100644 8-n8n-asana-agent/requirements.txt diff --git a/8-n8n-asana-agent/.env.example b/8-n8n-asana-agent/.env.example new file mode 100644 index 0000000..7acb416 --- /dev/null +++ b/8-n8n-asana-agent/.env.example @@ -0,0 +1,8 @@ +# Rename this file to .env once you have filled in the below environment variables! + +# The URL of your webhook to start the N8N workflow (get this from the first node (Webhook) of the workflow) +WEBHOOK_URL= + +# This is the Auth password for the webhook in N8N that you set up when creating the webhook +# Also make sure the name of your Header Auth in N8N is "Authorization" or you will get a 403 error! +WEBHOOK_AUTH= \ No newline at end of file diff --git a/8-n8n-asana-agent/n8n-asana-agent.py b/8-n8n-asana-agent/n8n-asana-agent.py new file mode 100644 index 0000000..561976b --- /dev/null +++ b/8-n8n-asana-agent/n8n-asana-agent.py @@ -0,0 +1,83 @@ +from dotenv import load_dotenv +import streamlit as st +import requests +import uuid +import json +import os + +from langchain_core.messages import AIMessage, HumanMessage + +""" +This Python script is an example of how to use Streamlit with +an n8n AI Agent as a webhook (API endpoint). This code pretty much just +defines a Streamlit UI that interacts with the n8n AI Agent for +each user message and displays the AI response from n8n back to the +UI just like other AI Agents in this masterclass. All chat history +and tool calling is managed within the n8n workflow. +""" + +load_dotenv() + +webhook_url = os.getenv('WEBHOOK_URL') +webhook_auth = os.getenv('WEBHOOK_AUTH') + +@st.cache_resource +def get_session_id(): + return str(uuid.uuid4()) + +session_id = get_session_id() + +def prompt_ai(user_input): + payload = { + "chatInput": user_input, + "sessionId": session_id + } + + headers = { + "Authorization": f"Bearer {webhook_auth}", + "Content-Type": "application/json" + } + + response = requests.post(webhook_url, json=payload, headers=headers) + response.raise_for_status() + return response.json() + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~~~~~~~~~~~~~~~~~ Main Function with UI Creation ~~~~~~~~~~~~~~~~~~~~ +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +def main(): + st.title("N8N Asana Chatbot") + + # Initialize chat history + if "messages" not in st.session_state: + st.session_state.messages = [] + + # Display chat messages from history on app rerun + for message in st.session_state.messages: + message_json = json.loads(message.json()) + message_type = message_json["type"] + if message_type in ["human", "ai", "system"]: + with st.chat_message(message_type): + st.markdown(message_json["content"]) + + # React to user input + if prompt := st.chat_input("What would you like to do today?"): + # Display user message in chat message container + st.chat_message("user").markdown(prompt) + # Add user message to chat history + st.session_state.messages.append(HumanMessage(content=prompt)) + + # Display assistant response in chat message container + with st.chat_message("assistant"): + response = prompt_ai(prompt) + st.markdown(response["output"]) + + st.session_state.messages.append(AIMessage(content=response["output"])) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/8-n8n-asana-agent/n8n_Workflow_Asana_AI_Agent.json b/8-n8n-asana-agent/n8n_Workflow_Asana_AI_Agent.json new file mode 100644 index 0000000..213906c --- /dev/null +++ b/8-n8n-asana-agent/n8n_Workflow_Asana_AI_Agent.json @@ -0,0 +1,234 @@ +{ + "name": "Asana AI Agent", + "nodes": [ + { + "parameters": { + "model": "gpt-4o-mini", + "options": {} + }, + "id": "555cd8d7-331e-43dd-8dae-cfe225c04f89", + "name": "OpenAI Chat Model", + "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi", + "typeVersion": 1, + "position": [ + 1260, + 580 + ], + "credentials": { + "openAiApi": { + "id": "JJjD91oisPv9cs01", + "name": "OpenAi account" + } + } + }, + { + "parameters": { + "sessionIdType": "customKey", + "sessionKey": "={{ $('When chat message received').item.json.sessionId }}" + }, + "id": "1c63f69f-bc07-4a3c-b832-59c5bb575a48", + "name": "Window Buffer Memory", + "type": "@n8n/n8n-nodes-langchain.memoryBufferWindow", + "typeVersion": 1.2, + "position": [ + 1380, + 600 + ] + }, + { + "parameters": { + "options": {} + }, + "id": "76db7743-f5ae-410d-8eff-73a465b57fa9", + "name": "Respond to Webhook", + "type": "n8n-nodes-base.respondToWebhook", + "typeVersion": 1.1, + "position": [ + 1860, + 360 + ] + }, + { + "parameters": { + "name": "Asana", + "description": "Call this tool to create a task in Asana. Give two parameters - the name of the task and the day it is due in the format YYYY-MM-DD", + "workflowId": "nCnW5wOp1qjTpOvp", + "responsePropertyName": "permalink_url", + "fields": { + "values": [ + {} + ] + }, + "specifyInputSchema": true, + "jsonSchemaExample": "{\n\t\"name\": \"Study for Test\",\n \"due_date\": \"2024-07-13\"\n}" + }, + "id": "f0dbf665-1a80-4ea8-ae51-7c0f421a7a56", + "name": "Call n8n Workflow Tool", + "type": "@n8n/n8n-nodes-langchain.toolWorkflow", + "typeVersion": 1.1, + "position": [ + 1560, + 580 + ] + }, + { + "parameters": { + "options": {} + }, + "id": "d9019c17-0180-4dbe-8e85-60474aaafe93", + "name": "Date & Time", + "type": "n8n-nodes-base.dateTime", + "typeVersion": 2, + "position": [ + 1000, + 360 + ] + }, + { + "parameters": { + "httpMethod": "POST", + "path": "asana_chatbot", + "authentication": "headerAuth", + "responseMode": "responseNode", + "options": {} + }, + "id": "0a1664a4-4ade-4f2c-9692-cbcd343aae17", + "name": "Webhook", + "type": "n8n-nodes-base.webhook", + "typeVersion": 2, + "position": [ + 640, + 180 + ], + "webhookId": "59003970-6f1f-4dfe-b7ea-1ef8e1c7a5d1", + "credentials": { + "httpHeaderAuth": { + "id": "FWbnHWdNKFyEHa1W", + "name": "Header Auth account" + } + } + }, + { + "parameters": { + "promptType": "define", + "text": "={{ $('When chat message received').item.json.chatInput }}", + "options": { + "systemMessage": "=You are a helpful assistant who helps create tasks in Asana. The current date is {{ $json.currentDate }}" + } + }, + "id": "3822b0eb-cd1b-419c-b12e-97f98ef0f604", + "name": "AI Agent", + "type": "@n8n/n8n-nodes-langchain.agent", + "typeVersion": 1.6, + "position": [ + 1260, + 360 + ] + }, + { + "parameters": { + "options": {} + }, + "id": "27ede384-f66c-4ad5-9659-e5851c43e626", + "name": "When chat message received", + "type": "@n8n/n8n-nodes-langchain.chatTrigger", + "typeVersion": 1.1, + "position": [ + 640, + 360 + ], + "webhookId": "aaf606e6-c311-472c-88ec-6267dd5255e2" + } + ], + "pinData": {}, + "connections": { + "OpenAI Chat Model": { + "ai_languageModel": [ + [ + { + "node": "AI Agent", + "type": "ai_languageModel", + "index": 0 + } + ] + ] + }, + "Window Buffer Memory": { + "ai_memory": [ + [ + { + "node": "AI Agent", + "type": "ai_memory", + "index": 0 + } + ] + ] + }, + "Call n8n Workflow Tool": { + "ai_tool": [ + [ + { + "node": "AI Agent", + "type": "ai_tool", + "index": 0 + } + ] + ] + }, + "Date & Time": { + "main": [ + [ + { + "node": "AI Agent", + "type": "main", + "index": 0 + } + ] + ] + }, + "Webhook": { + "main": [ + [ + { + "node": "Date & Time", + "type": "main", + "index": 0 + } + ] + ] + }, + "AI Agent": { + "main": [ + [ + { + "node": "Respond to Webhook", + "type": "main", + "index": 0 + } + ] + ] + }, + "When chat message received": { + "main": [ + [ + { + "node": "Date & Time", + "type": "main", + "index": 0 + } + ] + ] + } + }, + "active": true, + "settings": { + "executionOrder": "v1" + }, + "versionId": "ffbc203b-f652-43b2-84fb-4f899a0d977c", + "meta": { + "templateCredsSetupCompleted": true, + "instanceId": "620f0d7e3114cb344761d7d45a21ef2a32096f91d8696e7057756042e1999e2c" + }, + "id": "HF7kvnDcr2SUOYaE", + "tags": [] +} \ No newline at end of file diff --git a/8-n8n-asana-agent/n8n_Workflow_Create_Asana_Task.json b/8-n8n-asana-agent/n8n_Workflow_Create_Asana_Task.json new file mode 100644 index 0000000..5a1913e --- /dev/null +++ b/8-n8n-asana-agent/n8n_Workflow_Create_Asana_Task.json @@ -0,0 +1,68 @@ +{ + "name": "Create Asana Task", + "nodes": [ + { + "parameters": {}, + "id": "f7d7e146-3f88-4701-87ab-1aa09df7a83d", + "name": "Execute Workflow Trigger", + "type": "n8n-nodes-base.executeWorkflowTrigger", + "typeVersion": 1, + "position": [ + 820, + 360 + ] + }, + { + "parameters": { + "workspace": "1207638003439377", + "name": "={{ $json.query.name }}", + "otherProperties": { + "due_on": "={{ $json.query.due_date }}T00:00:00", + "projects": [ + "1207790694984047" + ] + } + }, + "id": "163fab0b-947d-4b62-b17f-b6a420a10104", + "name": "Asana", + "type": "n8n-nodes-base.asana", + "typeVersion": 1, + "position": [ + 1040, + 360 + ], + "alwaysOutputData": false, + "credentials": { + "asanaApi": { + "id": "3SVncDjuaKuQ7AQo", + "name": "Asana account" + } + } + } + ], + "pinData": {}, + "connections": { + "Execute Workflow Trigger": { + "main": [ + [ + { + "node": "Asana", + "type": "main", + "index": 0 + } + ] + ] + } + }, + "active": false, + "settings": { + "executionOrder": "v1" + }, + "versionId": "2295c979-5624-47bf-89d6-5d784e3c9a6e", + "meta": { + "templateCredsSetupCompleted": true, + "instanceId": "620f0d7e3114cb344761d7d45a21ef2a32096f91d8696e7057756042e1999e2c" + }, + "id": "nCnW5wOp1qjTpOvp", + "tags": [] +} \ No newline at end of file diff --git a/8-n8n-asana-agent/requirements.txt b/8-n8n-asana-agent/requirements.txt new file mode 100644 index 0000000..b85827a --- /dev/null +++ b/8-n8n-asana-agent/requirements.txt @@ -0,0 +1,5 @@ +python-dotenv==0.13.0 +langchain==0.2.6 +langchain-core==0.2.10 +streamlit==1.36.0 +requests==2.32.3 \ No newline at end of file