From 80fc5c4464c09531b789766c0e3438ef11b0ae07 Mon Sep 17 00:00:00 2001 From: Cole Medin Date: Wed, 4 Dec 2024 10:10:31 -0600 Subject: [PATCH] Adding Flowise to the Local AI Starter Kit --- local-ai-packaged/README.md | 5 +- local-ai-packaged/docker-compose.yml | 16 + .../Web Search + n8n Agent Chatflow.json | 863 ++++++++++++++++++ .../flowise/create_google_doc-CustomTool.json | 8 + .../get_postgres_tables-CustomTool.json | 8 + ..._slack_message_through_n8n-CustomTool.json | 8 + ...mmarize_slack_conversation-CustomTool.json | 8 + .../n8n-tool-workflows/Create_Google_Doc.json | 114 +++ .../Get_Postgres_Tables.json | 130 +++ .../Post_Message_to_Slack.json | 108 +++ .../Summarize_Slack_Conversation.json | 193 ++++ 11 files changed, 1460 insertions(+), 1 deletion(-) create mode 100644 local-ai-packaged/flowise/Web Search + n8n Agent Chatflow.json create mode 100644 local-ai-packaged/flowise/create_google_doc-CustomTool.json create mode 100644 local-ai-packaged/flowise/get_postgres_tables-CustomTool.json create mode 100644 local-ai-packaged/flowise/send_slack_message_through_n8n-CustomTool.json create mode 100644 local-ai-packaged/flowise/summarize_slack_conversation-CustomTool.json create mode 100644 local-ai-packaged/n8n-tool-workflows/Create_Google_Doc.json create mode 100644 local-ai-packaged/n8n-tool-workflows/Get_Postgres_Tables.json create mode 100644 local-ai-packaged/n8n-tool-workflows/Post_Message_to_Slack.json create mode 100644 local-ai-packaged/n8n-tool-workflows/Summarize_Slack_Conversation.json diff --git a/local-ai-packaged/README.md b/local-ai-packaged/README.md index 14269a6..4032303 100644 --- a/local-ai-packaged/README.md +++ b/local-ai-packaged/README.md @@ -4,7 +4,7 @@ quickly bootstraps a fully featured Local AI and Low Code development environment including Open WebUI for an interface to chat with your N8N agents. -This is Cole's version with a couple of improvements and the addition of Open WebUI! +This is Cole's version with a couple of improvements and the addition of Open WebUI and Flowise! Also, the local RAG AI Agent workflow from the video will be automatically in your n8n instance if you use this setup instead of the base one provided by n8n! @@ -30,6 +30,9 @@ and run the latest local LLMs ✅ [**Open WebUI**](https://openwebui.com/) - ChatGPT-like interface to privately interact with your local models and N8N agents +✅ [**Flowise**](https://flowiseai.com/) - No/low code AI agent +builder that pairs very well with n8n + ✅ [**Qdrant**](https://qdrant.tech/) - Open-source, high performance vector store with an comprehensive API diff --git a/local-ai-packaged/docker-compose.yml b/local-ai-packaged/docker-compose.yml index 1998c35..7424816 100644 --- a/local-ai-packaged/docker-compose.yml +++ b/local-ai-packaged/docker-compose.yml @@ -4,6 +4,7 @@ volumes: ollama_storage: qdrant_storage: open-webui: + flowise: networks: demo: @@ -45,6 +46,21 @@ x-init-ollama: &init-ollama - "sleep 3; OLLAMA_HOST=ollama:11434 ollama pull llama3.1; OLLAMA_HOST=ollama:11434 ollama pull nomic-embed-text" services: + flowise: + image: flowiseai/flowise + networks: ['demo'] + restart: unless-stopped + container_name: flowise + environment: + - PORT=3001 + ports: + - 3001:3001 + extra_hosts: + - "host.docker.internal:host-gateway" + volumes: + - ~/.flowise:/root/.flowise + entrypoint: /bin/sh -c "sleep 3; flowise start" + open-webui: image: ghcr.io/open-webui/open-webui:main networks: ['demo'] diff --git a/local-ai-packaged/flowise/Web Search + n8n Agent Chatflow.json b/local-ai-packaged/flowise/Web Search + n8n Agent Chatflow.json new file mode 100644 index 0000000..e660433 --- /dev/null +++ b/local-ai-packaged/flowise/Web Search + n8n Agent Chatflow.json @@ -0,0 +1,863 @@ +{ + "nodes": [ + { + "id": "toolAgent_0", + "position": { + "x": 656, + "y": 156 + }, + "type": "customNode", + "data": { + "id": "toolAgent_0", + "label": "Tool Agent", + "version": 2, + "name": "toolAgent", + "type": "AgentExecutor", + "baseClasses": [ + "AgentExecutor", + "BaseChain", + "Runnable" + ], + "category": "Agents", + "description": "Agent that uses Function Calling to pick the tools and args to call", + "inputParams": [ + { + "label": "System Message", + "name": "systemMessage", + "type": "string", + "default": "You are a helpful AI assistant.", + "description": "If Chat Prompt Template is provided, this will be ignored", + "rows": 4, + "optional": true, + "additionalParams": true, + "id": "toolAgent_0-input-systemMessage-string" + }, + { + "label": "Max Iterations", + "name": "maxIterations", + "type": "number", + "optional": true, + "additionalParams": true, + "id": "toolAgent_0-input-maxIterations-number" + } + ], + "inputAnchors": [ + { + "label": "Tools", + "name": "tools", + "type": "Tool", + "list": true, + "id": "toolAgent_0-input-tools-Tool" + }, + { + "label": "Memory", + "name": "memory", + "type": "BaseChatMemory", + "id": "toolAgent_0-input-memory-BaseChatMemory" + }, + { + "label": "Tool Calling Chat Model", + "name": "model", + "type": "BaseChatModel", + "description": "Only compatible with models that are capable of function calling: ChatOpenAI, ChatMistral, ChatAnthropic, ChatGoogleGenerativeAI, ChatVertexAI, GroqChat", + "id": "toolAgent_0-input-model-BaseChatModel" + }, + { + "label": "Chat Prompt Template", + "name": "chatPromptTemplate", + "type": "ChatPromptTemplate", + "description": "Override existing prompt with Chat Prompt Template. Human Message must includes {input} variable", + "optional": true, + "id": "toolAgent_0-input-chatPromptTemplate-ChatPromptTemplate" + }, + { + "label": "Input Moderation", + "description": "Detect text that could generate harmful output and prevent it from being sent to the language model", + "name": "inputModeration", + "type": "Moderation", + "optional": true, + "list": true, + "id": "toolAgent_0-input-inputModeration-Moderation" + } + ], + "inputs": { + "tools": [ + "{{braveSearchAPI_0.data.instance}}", + "{{customTool_0.data.instance}}", + "{{customTool_1.data.instance}}", + "{{customTool_2.data.instance}}", + "{{customTool_3.data.instance}}" + ], + "memory": "{{bufferMemory_0.data.instance}}", + "model": "{{chatOllama_0.data.instance}}", + "chatPromptTemplate": "", + "systemMessage": "You are a helpful AI assistant.", + "inputModeration": "", + "maxIterations": "" + }, + "outputAnchors": [ + { + "id": "toolAgent_0-output-toolAgent-AgentExecutor|BaseChain|Runnable", + "name": "toolAgent", + "label": "AgentExecutor", + "description": "Agent that uses Function Calling to pick the tools and args to call", + "type": "AgentExecutor | BaseChain | Runnable" + } + ], + "outputs": {}, + "selected": false + }, + "width": 300, + "height": 484, + "selected": false, + "positionAbsolute": { + "x": 656, + "y": 156 + } + }, + { + "id": "bufferMemory_0", + "position": { + "x": 232.6843615160352, + "y": -162.57529096209916 + }, + "type": "customNode", + "data": { + "id": "bufferMemory_0", + "label": "Buffer Memory", + "version": 2, + "name": "bufferMemory", + "type": "BufferMemory", + "baseClasses": [ + "BufferMemory", + "BaseChatMemory", + "BaseMemory" + ], + "category": "Memory", + "description": "Retrieve chat messages stored in database", + "inputParams": [ + { + "label": "Session Id", + "name": "sessionId", + "type": "string", + "description": "If not specified, a random id will be used. Learn more", + "default": "", + "additionalParams": true, + "optional": true, + "id": "bufferMemory_0-input-sessionId-string" + }, + { + "label": "Memory Key", + "name": "memoryKey", + "type": "string", + "default": "chat_history", + "additionalParams": true, + "id": "bufferMemory_0-input-memoryKey-string" + } + ], + "inputAnchors": [], + "inputs": { + "sessionId": "", + "memoryKey": "chat_history" + }, + "outputAnchors": [ + { + "id": "bufferMemory_0-output-bufferMemory-BufferMemory|BaseChatMemory|BaseMemory", + "name": "bufferMemory", + "label": "BufferMemory", + "description": "Retrieve chat messages stored in database", + "type": "BufferMemory | BaseChatMemory | BaseMemory" + } + ], + "outputs": {}, + "selected": false + }, + "width": 300, + "height": 251, + "selected": false, + "positionAbsolute": { + "x": 232.6843615160352, + "y": -162.57529096209916 + }, + "dragging": false + }, + { + "id": "chatOllama_0", + "position": { + "x": 203.62742857142882, + "y": 133.58191020408157 + }, + "type": "customNode", + "data": { + "id": "chatOllama_0", + "label": "ChatOllama", + "version": 5, + "name": "chatOllama", + "type": "ChatOllama", + "baseClasses": [ + "ChatOllama", + "ChatOllama", + "BaseChatModel", + "BaseLanguageModel", + "Runnable" + ], + "category": "Chat Models", + "description": "Chat completion using open-source LLM on Ollama", + "inputParams": [ + { + "label": "Base URL", + "name": "baseUrl", + "type": "string", + "default": "http://localhost:11434", + "id": "chatOllama_0-input-baseUrl-string" + }, + { + "label": "Model Name", + "name": "modelName", + "type": "string", + "placeholder": "llama2", + "id": "chatOllama_0-input-modelName-string" + }, + { + "label": "Temperature", + "name": "temperature", + "type": "number", + "description": "The temperature of the model. Increasing the temperature will make the model answer more creatively. (Default: 0.8). Refer to docs for more details", + "step": 0.1, + "default": 0.9, + "optional": true, + "id": "chatOllama_0-input-temperature-number" + }, + { + "label": "Allow Image Uploads", + "name": "allowImageUploads", + "type": "boolean", + "description": "Allow image input. Refer to the docs for more details.", + "default": false, + "optional": true, + "id": "chatOllama_0-input-allowImageUploads-boolean" + }, + { + "label": "Streaming", + "name": "streaming", + "type": "boolean", + "default": true, + "optional": true, + "additionalParams": true, + "id": "chatOllama_0-input-streaming-boolean" + }, + { + "label": "JSON Mode", + "name": "jsonMode", + "type": "boolean", + "description": "Coerces model outputs to only return JSON. Specify in the system prompt to return JSON. Ex: Format all responses as JSON object", + "optional": true, + "additionalParams": true, + "id": "chatOllama_0-input-jsonMode-boolean" + }, + { + "label": "Keep Alive", + "name": "keepAlive", + "type": "string", + "description": "How long to keep connection alive. A duration string (such as \"10m\" or \"24h\")", + "default": "5m", + "optional": true, + "additionalParams": true, + "id": "chatOllama_0-input-keepAlive-string" + }, + { + "label": "Top P", + "name": "topP", + "type": "number", + "description": "Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9). Refer to docs for more details", + "step": 0.1, + "optional": true, + "additionalParams": true, + "id": "chatOllama_0-input-topP-number" + }, + { + "label": "Top K", + "name": "topK", + "type": "number", + "description": "Reduces the probability of generating nonsense. A higher value (e.g. 100) will give more diverse answers, while a lower value (e.g. 10) will be more conservative. (Default: 40). Refer to docs for more details", + "step": 1, + "optional": true, + "additionalParams": true, + "id": "chatOllama_0-input-topK-number" + }, + { + "label": "Mirostat", + "name": "mirostat", + "type": "number", + "description": "Enable Mirostat sampling for controlling perplexity. (default: 0, 0 = disabled, 1 = Mirostat, 2 = Mirostat 2.0). Refer to docs for more details", + "step": 1, + "optional": true, + "additionalParams": true, + "id": "chatOllama_0-input-mirostat-number" + }, + { + "label": "Mirostat ETA", + "name": "mirostatEta", + "type": "number", + "description": "Influences how quickly the algorithm responds to feedback from the generated text. A lower learning rate will result in slower adjustments, while a higher learning rate will make the algorithm more responsive. (Default: 0.1) Refer to docs for more details", + "step": 0.1, + "optional": true, + "additionalParams": true, + "id": "chatOllama_0-input-mirostatEta-number" + }, + { + "label": "Mirostat TAU", + "name": "mirostatTau", + "type": "number", + "description": "Controls the balance between coherence and diversity of the output. A lower value will result in more focused and coherent text. (Default: 5.0) Refer to docs for more details", + "step": 0.1, + "optional": true, + "additionalParams": true, + "id": "chatOllama_0-input-mirostatTau-number" + }, + { + "label": "Context Window Size", + "name": "numCtx", + "type": "number", + "description": "Sets the size of the context window used to generate the next token. (Default: 2048) Refer to docs for more details", + "step": 1, + "optional": true, + "additionalParams": true, + "id": "chatOllama_0-input-numCtx-number" + }, + { + "label": "Number of GPU", + "name": "numGpu", + "type": "number", + "description": "The number of layers to send to the GPU(s). On macOS it defaults to 1 to enable metal support, 0 to disable. Refer to docs for more details", + "step": 1, + "optional": true, + "additionalParams": true, + "id": "chatOllama_0-input-numGpu-number" + }, + { + "label": "Number of Thread", + "name": "numThread", + "type": "number", + "description": "Sets the number of threads to use during computation. By default, Ollama will detect this for optimal performance. It is recommended to set this value to the number of physical CPU cores your system has (as opposed to the logical number of cores). Refer to docs for more details", + "step": 1, + "optional": true, + "additionalParams": true, + "id": "chatOllama_0-input-numThread-number" + }, + { + "label": "Repeat Last N", + "name": "repeatLastN", + "type": "number", + "description": "Sets how far back for the model to look back to prevent repetition. (Default: 64, 0 = disabled, -1 = num_ctx). Refer to docs for more details", + "step": 1, + "optional": true, + "additionalParams": true, + "id": "chatOllama_0-input-repeatLastN-number" + }, + { + "label": "Repeat Penalty", + "name": "repeatPenalty", + "type": "number", + "description": "Sets how strongly to penalize repetitions. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. (Default: 1.1). Refer to docs for more details", + "step": 0.1, + "optional": true, + "additionalParams": true, + "id": "chatOllama_0-input-repeatPenalty-number" + }, + { + "label": "Stop Sequence", + "name": "stop", + "type": "string", + "rows": 4, + "placeholder": "AI assistant:", + "description": "Sets the stop sequences to use. Use comma to seperate different sequences. Refer to docs for more details", + "optional": true, + "additionalParams": true, + "id": "chatOllama_0-input-stop-string" + }, + { + "label": "Tail Free Sampling", + "name": "tfsZ", + "type": "number", + "description": "Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (Default: 1). Refer to docs for more details", + "step": 0.1, + "optional": true, + "additionalParams": true, + "id": "chatOllama_0-input-tfsZ-number" + } + ], + "inputAnchors": [ + { + "label": "Cache", + "name": "cache", + "type": "BaseCache", + "optional": true, + "id": "chatOllama_0-input-cache-BaseCache" + } + ], + "inputs": { + "cache": "{{inMemoryCache_0.data.instance}}", + "baseUrl": "http://host.docker.internal:11434", + "modelName": "qwen2.5-coder:32b", + "temperature": "0.5", + "allowImageUploads": "", + "streaming": true, + "jsonMode": "", + "keepAlive": "5m", + "topP": "", + "topK": "", + "mirostat": "", + "mirostatEta": "", + "mirostatTau": "", + "numCtx": "32768", + "numGpu": "", + "numThread": "", + "repeatLastN": "", + "repeatPenalty": "", + "stop": "", + "tfsZ": "" + }, + "outputAnchors": [ + { + "id": "chatOllama_0-output-chatOllama-ChatOllama|ChatOllama|BaseChatModel|BaseLanguageModel|Runnable", + "name": "chatOllama", + "label": "ChatOllama", + "description": "Chat completion using open-source LLM on Ollama", + "type": "ChatOllama | ChatOllama | BaseChatModel | BaseLanguageModel | Runnable" + } + ], + "outputs": {}, + "selected": false + }, + "width": 300, + "height": 675, + "selected": false, + "positionAbsolute": { + "x": 203.62742857142882, + "y": 133.58191020408157 + }, + "dragging": false + }, + { + "id": "inMemoryCache_0", + "position": { + "x": -222.16839650145752, + "y": 87.76136209912531 + }, + "type": "customNode", + "data": { + "id": "inMemoryCache_0", + "label": "InMemory Cache", + "version": 1, + "name": "inMemoryCache", + "type": "InMemoryCache", + "baseClasses": [ + "InMemoryCache", + "BaseCache" + ], + "category": "Cache", + "description": "Cache LLM response in memory, will be cleared once app restarted", + "inputParams": [], + "inputAnchors": [], + "inputs": {}, + "outputAnchors": [ + { + "id": "inMemoryCache_0-output-inMemoryCache-InMemoryCache|BaseCache", + "name": "inMemoryCache", + "label": "InMemoryCache", + "description": "Cache LLM response in memory, will be cleared once app restarted", + "type": "InMemoryCache | BaseCache" + } + ], + "outputs": {}, + "selected": false + }, + "width": 300, + "height": 143, + "selected": false, + "positionAbsolute": { + "x": -222.16839650145752, + "y": 87.76136209912531 + }, + "dragging": false + }, + { + "id": "braveSearchAPI_0", + "position": { + "x": 557.8984956268223, + "y": -202.80796734693882 + }, + "type": "customNode", + "data": { + "id": "braveSearchAPI_0", + "label": "BraveSearch API", + "version": 1, + "name": "braveSearchAPI", + "type": "BraveSearchAPI", + "baseClasses": [ + "BraveSearchAPI", + "Tool", + "StructuredTool", + "Runnable" + ], + "category": "Tools", + "description": "Wrapper around BraveSearch API - a real-time API to access Brave search results", + "inputParams": [ + { + "label": "Connect Credential", + "name": "credential", + "type": "credential", + "credentialNames": [ + "braveSearchApi" + ], + "id": "braveSearchAPI_0-input-credential-credential" + } + ], + "inputAnchors": [], + "inputs": {}, + "outputAnchors": [ + { + "id": "braveSearchAPI_0-output-braveSearchAPI-BraveSearchAPI|Tool|StructuredTool|Runnable", + "name": "braveSearchAPI", + "label": "BraveSearchAPI", + "description": "Wrapper around BraveSearch API - a real-time API to access Brave search results", + "type": "BraveSearchAPI | Tool | StructuredTool | Runnable" + } + ], + "outputs": {}, + "selected": false + }, + "width": 300, + "height": 275, + "selected": false, + "positionAbsolute": { + "x": 557.8984956268223, + "y": -202.80796734693882 + }, + "dragging": false + }, + { + "id": "customTool_0", + "position": { + "x": 919.9367100136845, + "y": -303.9223829475814 + }, + "type": "customNode", + "data": { + "id": "customTool_0", + "label": "Custom Tool", + "version": 2, + "name": "customTool", + "type": "CustomTool", + "baseClasses": [ + "CustomTool", + "Tool", + "StructuredTool", + "Runnable" + ], + "category": "Tools", + "description": "Use custom tool you've created in Flowise within chatflow", + "inputParams": [ + { + "label": "Select Tool", + "name": "selectedTool", + "type": "asyncOptions", + "loadMethod": "listTools", + "id": "customTool_0-input-selectedTool-asyncOptions" + }, + { + "label": "Return Direct", + "name": "returnDirect", + "description": "Return the output of the tool directly to the user", + "type": "boolean", + "optional": true, + "id": "customTool_0-input-returnDirect-boolean" + } + ], + "inputAnchors": [], + "inputs": { + "selectedTool": "cbc24643-ad81-4769-911f-089c8e4c87ab", + "returnDirect": "" + }, + "outputAnchors": [ + { + "id": "customTool_0-output-customTool-CustomTool|Tool|StructuredTool|Runnable", + "name": "customTool", + "label": "CustomTool", + "description": "Use custom tool you've created in Flowise within chatflow", + "type": "CustomTool | Tool | StructuredTool | Runnable" + } + ], + "outputs": {}, + "selected": false + }, + "width": 300, + "height": 371, + "selected": false, + "positionAbsolute": { + "x": 919.9367100136845, + "y": -303.9223829475814 + }, + "dragging": false + }, + { + "id": "customTool_1", + "position": { + "x": 1055.4606486069163, + "y": 157.78311597410726 + }, + "type": "customNode", + "data": { + "id": "customTool_1", + "label": "Custom Tool", + "version": 2, + "name": "customTool", + "type": "CustomTool", + "baseClasses": [ + "CustomTool", + "Tool", + "StructuredTool", + "Runnable" + ], + "category": "Tools", + "description": "Use custom tool you've created in Flowise within chatflow", + "inputParams": [ + { + "label": "Select Tool", + "name": "selectedTool", + "type": "asyncOptions", + "loadMethod": "listTools", + "id": "customTool_1-input-selectedTool-asyncOptions" + }, + { + "label": "Return Direct", + "name": "returnDirect", + "description": "Return the output of the tool directly to the user", + "type": "boolean", + "optional": true, + "id": "customTool_1-input-returnDirect-boolean" + } + ], + "inputAnchors": [], + "inputs": { + "selectedTool": "54bc0754-c127-416c-8cd0-db2902f2fce8", + "returnDirect": "" + }, + "outputAnchors": [ + { + "id": "customTool_1-output-customTool-CustomTool|Tool|StructuredTool|Runnable", + "name": "customTool", + "label": "CustomTool", + "description": "Use custom tool you've created in Flowise within chatflow", + "type": "CustomTool | Tool | StructuredTool | Runnable" + } + ], + "outputs": {}, + "selected": false + }, + "width": 300, + "height": 371, + "selected": false, + "dragging": false, + "positionAbsolute": { + "x": 1055.4606486069163, + "y": 157.78311597410726 + } + }, + { + "id": "customTool_2", + "position": { + "x": 1058.5329191281724, + "y": 583.5403469198069 + }, + "type": "customNode", + "data": { + "id": "customTool_2", + "label": "Custom Tool", + "version": 2, + "name": "customTool", + "type": "CustomTool", + "baseClasses": [ + "CustomTool", + "Tool", + "StructuredTool", + "Runnable" + ], + "category": "Tools", + "description": "Use custom tool you've created in Flowise within chatflow", + "inputParams": [ + { + "label": "Select Tool", + "name": "selectedTool", + "type": "asyncOptions", + "loadMethod": "listTools", + "id": "customTool_2-input-selectedTool-asyncOptions" + }, + { + "label": "Return Direct", + "name": "returnDirect", + "description": "Return the output of the tool directly to the user", + "type": "boolean", + "optional": true, + "id": "customTool_2-input-returnDirect-boolean" + } + ], + "inputAnchors": [], + "inputs": { + "selectedTool": "83ef936c-4579-48a3-b95f-fbefc6926b65", + "returnDirect": "" + }, + "outputAnchors": [ + { + "id": "customTool_2-output-customTool-CustomTool|Tool|StructuredTool|Runnable", + "name": "customTool", + "label": "CustomTool", + "description": "Use custom tool you've created in Flowise within chatflow", + "type": "CustomTool | Tool | StructuredTool | Runnable" + } + ], + "outputs": {}, + "selected": false + }, + "width": 300, + "height": 371, + "selected": false, + "dragging": false, + "positionAbsolute": { + "x": 1058.5329191281724, + "y": 583.5403469198069 + } + }, + { + "id": "customTool_3", + "position": { + "x": 650.5308240717925, + "y": 747.2264330934981 + }, + "type": "customNode", + "data": { + "id": "customTool_3", + "label": "Custom Tool", + "version": 2, + "name": "customTool", + "type": "CustomTool", + "baseClasses": [ + "CustomTool", + "Tool", + "StructuredTool", + "Runnable" + ], + "category": "Tools", + "description": "Use custom tool you've created in Flowise within chatflow", + "inputParams": [ + { + "label": "Select Tool", + "name": "selectedTool", + "type": "asyncOptions", + "loadMethod": "listTools", + "id": "customTool_3-input-selectedTool-asyncOptions" + }, + { + "label": "Return Direct", + "name": "returnDirect", + "description": "Return the output of the tool directly to the user", + "type": "boolean", + "optional": true, + "id": "customTool_3-input-returnDirect-boolean" + } + ], + "inputAnchors": [], + "inputs": { + "selectedTool": "59ace78e-575c-4f38-958d-a80e46be1e64", + "returnDirect": "" + }, + "outputAnchors": [ + { + "id": "customTool_3-output-customTool-CustomTool|Tool|StructuredTool|Runnable", + "name": "customTool", + "label": "CustomTool", + "description": "Use custom tool you've created in Flowise within chatflow", + "type": "CustomTool | Tool | StructuredTool | Runnable" + } + ], + "outputs": {}, + "selected": false + }, + "width": 300, + "height": 371, + "selected": false, + "positionAbsolute": { + "x": 650.5308240717925, + "y": 747.2264330934981 + }, + "dragging": false + } + ], + "edges": [ + { + "source": "bufferMemory_0", + "sourceHandle": "bufferMemory_0-output-bufferMemory-BufferMemory|BaseChatMemory|BaseMemory", + "target": "toolAgent_0", + "targetHandle": "toolAgent_0-input-memory-BaseChatMemory", + "type": "buttonedge", + "id": "bufferMemory_0-bufferMemory_0-output-bufferMemory-BufferMemory|BaseChatMemory|BaseMemory-toolAgent_0-toolAgent_0-input-memory-BaseChatMemory" + }, + { + "source": "inMemoryCache_0", + "sourceHandle": "inMemoryCache_0-output-inMemoryCache-InMemoryCache|BaseCache", + "target": "chatOllama_0", + "targetHandle": "chatOllama_0-input-cache-BaseCache", + "type": "buttonedge", + "id": "inMemoryCache_0-inMemoryCache_0-output-inMemoryCache-InMemoryCache|BaseCache-chatOllama_0-chatOllama_0-input-cache-BaseCache" + }, + { + "source": "chatOllama_0", + "sourceHandle": "chatOllama_0-output-chatOllama-ChatOllama|ChatOllama|BaseChatModel|BaseLanguageModel|Runnable", + "target": "toolAgent_0", + "targetHandle": "toolAgent_0-input-model-BaseChatModel", + "type": "buttonedge", + "id": "chatOllama_0-chatOllama_0-output-chatOllama-ChatOllama|ChatOllama|BaseChatModel|BaseLanguageModel|Runnable-toolAgent_0-toolAgent_0-input-model-BaseChatModel" + }, + { + "source": "braveSearchAPI_0", + "sourceHandle": "braveSearchAPI_0-output-braveSearchAPI-BraveSearchAPI|Tool|StructuredTool|Runnable", + "target": "toolAgent_0", + "targetHandle": "toolAgent_0-input-tools-Tool", + "type": "buttonedge", + "id": "braveSearchAPI_0-braveSearchAPI_0-output-braveSearchAPI-BraveSearchAPI|Tool|StructuredTool|Runnable-toolAgent_0-toolAgent_0-input-tools-Tool" + }, + { + "source": "customTool_0", + "sourceHandle": "customTool_0-output-customTool-CustomTool|Tool|StructuredTool|Runnable", + "target": "toolAgent_0", + "targetHandle": "toolAgent_0-input-tools-Tool", + "type": "buttonedge", + "id": "customTool_0-customTool_0-output-customTool-CustomTool|Tool|StructuredTool|Runnable-toolAgent_0-toolAgent_0-input-tools-Tool" + }, + { + "source": "customTool_1", + "sourceHandle": "customTool_1-output-customTool-CustomTool|Tool|StructuredTool|Runnable", + "target": "toolAgent_0", + "targetHandle": "toolAgent_0-input-tools-Tool", + "type": "buttonedge", + "id": "customTool_1-customTool_1-output-customTool-CustomTool|Tool|StructuredTool|Runnable-toolAgent_0-toolAgent_0-input-tools-Tool" + }, + { + "source": "customTool_2", + "sourceHandle": "customTool_2-output-customTool-CustomTool|Tool|StructuredTool|Runnable", + "target": "toolAgent_0", + "targetHandle": "toolAgent_0-input-tools-Tool", + "type": "buttonedge", + "id": "customTool_2-customTool_2-output-customTool-CustomTool|Tool|StructuredTool|Runnable-toolAgent_0-toolAgent_0-input-tools-Tool" + }, + { + "source": "customTool_3", + "sourceHandle": "customTool_3-output-customTool-CustomTool|Tool|StructuredTool|Runnable", + "target": "toolAgent_0", + "targetHandle": "toolAgent_0-input-tools-Tool", + "type": "buttonedge", + "id": "customTool_3-customTool_3-output-customTool-CustomTool|Tool|StructuredTool|Runnable-toolAgent_0-toolAgent_0-input-tools-Tool" + } + ] +} \ No newline at end of file diff --git a/local-ai-packaged/flowise/create_google_doc-CustomTool.json b/local-ai-packaged/flowise/create_google_doc-CustomTool.json new file mode 100644 index 0000000..d4ddf9d --- /dev/null +++ b/local-ai-packaged/flowise/create_google_doc-CustomTool.json @@ -0,0 +1,8 @@ +{ + "name": "create_google_doc", + "description": "Use this tool to create a Google Doc.", + "color": "linear-gradient(rgb(148,138,24), rgb(211,202,27))", + "iconSrc": "", + "schema": "[{\"id\":0,\"property\":\"document_text\",\"description\":\"The text to put in the Google Doc\",\"type\":\"string\",\"required\":true},{\"id\":1,\"property\":\"document_title\",\"description\":\"The title for the document\",\"type\":\"string\",\"required\":true}]", + "func": "/*\n* You can use any libraries imported in Flowise\n* You can use properties specified in Input Schema as variables. Ex: Property = userid, Variable = $userid\n* You can get default flow config: $flow.sessionId, $flow.chatId, $flow.chatflowId, $flow.input, $flow.state\n* You can get custom variables: $vars.\n* Must return a string value at the end of function\n*/\n\nconst fetch = require('node-fetch');\nconst url = 'Your n8n Webhook URL';\nconst options = {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${$vars.headerauth}`\n },\n body: JSON.stringify({\n 'document_title': $document_title,\n 'document_text': $document_text\n })\n};\ntry {\n const response = await fetch(url, options);\n const text = await response.text();\n return text;\n} catch (error) {\n console.error(error);\n return '';\n}" +} \ No newline at end of file diff --git a/local-ai-packaged/flowise/get_postgres_tables-CustomTool.json b/local-ai-packaged/flowise/get_postgres_tables-CustomTool.json new file mode 100644 index 0000000..6144676 --- /dev/null +++ b/local-ai-packaged/flowise/get_postgres_tables-CustomTool.json @@ -0,0 +1,8 @@ +{ + "name": "get_postgres_tables", + "description": "Use this tool to get the Postgres table.", + "color": "linear-gradient(rgb(233,65,152), rgb(71,142,232))", + "iconSrc": "", + "schema": "[{\"id\":0,\"property\":\"database\",\"description\":\"the database name\",\"type\":\"string\",\"required\":true}]", + "func": "/*\n* You can use any libraries imported in Flowise\n* You can use properties specified in Input Schema as variables. Ex: Property = userid, Variable = $userid\n* You can get default flow config: $flow.sessionId, $flow.chatId, $flow.chatflowId, $flow.input, $flow.state\n* You can get custom variables: $vars.\n* Must return a string value at the end of function\n*/\n\nconst fetch = require('node-fetch');\nconst url = 'http://n8n:5678/webhook/d8db9fa3-04fe-43c8-9acf-e1912463477f';\nconst options = {\n method: 'GET',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${$vars.headerauth}`\n }\n};\ntry {\n const response = await fetch(url, options);\n const text = await response.text();\n return text;\n} catch (error) {\n console.error(error);\n return '';\n}" +} \ No newline at end of file diff --git a/local-ai-packaged/flowise/send_slack_message_through_n8n-CustomTool.json b/local-ai-packaged/flowise/send_slack_message_through_n8n-CustomTool.json new file mode 100644 index 0000000..5c146ae --- /dev/null +++ b/local-ai-packaged/flowise/send_slack_message_through_n8n-CustomTool.json @@ -0,0 +1,8 @@ +{ + "name": "send_slack_message_through_n8n", + "description": "Use this tool to Send a message in Slack.", + "color": "linear-gradient(rgb(25,248,134), rgb(46,226,32))", + "iconSrc": "", + "schema": "[{\"id\":0,\"property\":\"message\",\"description\":\"The message to send in Slack\",\"type\":\"string\",\"required\":true}]", + "func": "/*\n* You can use any libraries imported in Flowise\n* You can use properties specified in Input Schema as variables. Ex: Property = userid, Variable = $userid\n* You can get default flow config: $flow.sessionId, $flow.chatId, $flow.chatflowId, $flow.input, $flow.state\n* You can get custom variables: $vars.\n* Must return a string value at the end of function\n*/\n\nconst fetch = require('node-fetch');\nconst url = 'Your n8n Webhook URL';\nconst options = {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${$vars.headerauth}`\n },\n body: JSON.stringify({\n 'message': $message\n })\n};\ntry {\n const response = await fetch(url, options);\n const text = await response.text();\n return text;\n} catch (error) {\n console.error(error);\n return '';\n}" +} \ No newline at end of file diff --git a/local-ai-packaged/flowise/summarize_slack_conversation-CustomTool.json b/local-ai-packaged/flowise/summarize_slack_conversation-CustomTool.json new file mode 100644 index 0000000..e3fdf11 --- /dev/null +++ b/local-ai-packaged/flowise/summarize_slack_conversation-CustomTool.json @@ -0,0 +1,8 @@ +{ + "name": "summarize_slack_conversation", + "description": "Use this tool to get a summary of a Slack conversation in a channel.", + "color": "linear-gradient(rgb(235,70,180), rgb(87,203,87))", + "iconSrc": "", + "schema": "[{\"id\":0,\"property\":\"channel\",\"description\":\"The channel name to summarize\",\"type\":\"string\",\"required\":false}]", + "func": "/*\n* You can use any libraries imported in Flowise\n* You can use properties specified in Input Schema as variables. Ex: Property = userid, Variable = $userid\n* You can get default flow config: $flow.sessionId, $flow.chatId, $flow.chatflowId, $flow.input, $flow.state\n* You can get custom variables: $vars.\n* Must return a string value at the end of function\n*/\n\nconst fetch = require('node-fetch');\nconst url = 'Your n8n Webhook URL';\nconst options = {\n method: 'GET',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${$vars.headerauth}`\n }\n};\ntry {\n const response = await fetch(url, options);\n const text = await response.text();\n return text;\n} catch (error) {\n console.error(error);\n return '';\n}" +} \ No newline at end of file diff --git a/local-ai-packaged/n8n-tool-workflows/Create_Google_Doc.json b/local-ai-packaged/n8n-tool-workflows/Create_Google_Doc.json new file mode 100644 index 0000000..a46d9ec --- /dev/null +++ b/local-ai-packaged/n8n-tool-workflows/Create_Google_Doc.json @@ -0,0 +1,114 @@ +{ + "name": "Create Google Doc", + "nodes": [ + { + "parameters": { + "operation": "createFromText", + "content": "={{ $json.body.document_text }}", + "name": "={{ $json.body.document_title }}", + "driveId": { + "__rl": true, + "mode": "list", + "value": "My Drive" + }, + "folderId": { + "__rl": true, + "value": "1914m3M7kRzkd5RJqAfzRY9EBcJrKemZC", + "mode": "list", + "cachedResultName": "Meeting Notes", + "cachedResultUrl": "https://drive.google.com/drive/folders/1914m3M7kRzkd5RJqAfzRY9EBcJrKemZC" + }, + "options": { + "convertToGoogleDocument": true + } + }, + "id": "abb2ee3f-7dd0-4d6e-96f0-6cc91eb64a5e", + "name": "Google Drive", + "type": "n8n-nodes-base.googleDrive", + "typeVersion": 3, + "position": [ + 1040, + 360 + ], + "credentials": { + "googleDriveOAuth2Api": { + "id": "cfNochbuJikPwwl2", + "name": "Google Drive account" + } + } + }, + { + "parameters": { + "options": {} + }, + "id": "9904e7b7-c9f6-49b5-ab72-6b199c6e2f46", + "name": "Respond to Webhook", + "type": "n8n-nodes-base.respondToWebhook", + "typeVersion": 1.1, + "position": [ + 1260, + 360 + ] + }, + { + "parameters": { + "httpMethod": "POST", + "path": "d8db9fa3-04fe-43c8-9acf-e1912463477f", + "authentication": "headerAuth", + "responseMode": "responseNode", + "options": {} + }, + "id": "7e761bed-99f6-4a8a-959b-d1b3542b6071", + "name": "Webhook", + "type": "n8n-nodes-base.webhook", + "typeVersion": 2, + "position": [ + 820, + 360 + ], + "webhookId": "d8db9fa3-04fe-43c8-9acf-e1912463477f", + "credentials": { + "httpHeaderAuth": { + "id": "PGr0hc0kn43Di1sz", + "name": "testauth" + } + } + } + ], + "pinData": {}, + "connections": { + "Google Drive": { + "main": [ + [ + { + "node": "Respond to Webhook", + "type": "main", + "index": 0 + } + ] + ] + }, + "Webhook": { + "main": [ + [ + { + "node": "Google Drive", + "type": "main", + "index": 0 + } + ] + ] + } + }, + "active": true, + "settings": { + "executionOrder": "v1" + }, + "versionId": "c1f66b1c-c7dc-48ba-9197-6a0e3ac3e8f4", + "meta": { + "templateCredsSetupCompleted": true, + "instanceId": "620f0d7e3114cb344761d7d45a21ef2a32096f91d8696e7057756042e1999e2c" + }, + "id": "LYrReDbpmqK3eX2P", + "tags": [] +} \ No newline at end of file diff --git a/local-ai-packaged/n8n-tool-workflows/Get_Postgres_Tables.json b/local-ai-packaged/n8n-tool-workflows/Get_Postgres_Tables.json new file mode 100644 index 0000000..2fbf827 --- /dev/null +++ b/local-ai-packaged/n8n-tool-workflows/Get_Postgres_Tables.json @@ -0,0 +1,130 @@ +{ + "name": "Get Postgres Tables", + "nodes": [ + { + "parameters": { + "options": {} + }, + "id": "74a8e48c-c13f-450c-835e-1702d87f894c", + "name": "Respond to Webhook", + "type": "n8n-nodes-base.respondToWebhook", + "typeVersion": 1.1, + "position": [ + 880, + 60 + ] + }, + { + "parameters": { + "operation": "executeQuery", + "query": "SELECT table_name \nFROM information_schema.tables\nWHERE table_schema = 'public'\n AND table_type = 'BASE TABLE'\nORDER BY table_name;", + "options": {} + }, + "id": "2cd15801-ea79-4a53-b1be-c0470429966a", + "name": "Postgres", + "type": "n8n-nodes-base.postgres", + "typeVersion": 2.5, + "position": [ + 400, + 60 + ], + "credentials": { + "postgres": { + "id": "AXJoQJaFRsoL9Qk8", + "name": "Postgres account" + } + } + }, + { + "parameters": { + "fieldsToSummarize": { + "values": [ + { + "aggregation": "concatenate", + "field": "table_name" + } + ] + }, + "options": {} + }, + "id": "8b66e814-e553-451c-818a-fc93699b341c", + "name": "Summarize", + "type": "n8n-nodes-base.summarize", + "typeVersion": 1, + "position": [ + 640, + 60 + ] + }, + { + "parameters": { + "path": "d8db9fa3-04fe-43c8-9acf-e1912463477f", + "authentication": "headerAuth", + "responseMode": "responseNode", + "options": {} + }, + "id": "3eb54a82-034c-4f3f-aa99-3b6aeb744ce2", + "name": "Webhook", + "type": "n8n-nodes-base.webhook", + "typeVersion": 2, + "position": [ + 180, + 60 + ], + "webhookId": "d8db9fa3-04fe-43c8-9acf-e1912463477f", + "credentials": { + "httpHeaderAuth": { + "id": "upxO7NGaOTeIP4XU", + "name": "testauth" + } + } + } + ], + "pinData": {}, + "connections": { + "Postgres": { + "main": [ + [ + { + "node": "Summarize", + "type": "main", + "index": 0 + } + ] + ] + }, + "Summarize": { + "main": [ + [ + { + "node": "Respond to Webhook", + "type": "main", + "index": 0 + } + ] + ] + }, + "Webhook": { + "main": [ + [ + { + "node": "Postgres", + "type": "main", + "index": 0 + } + ] + ] + } + }, + "active": true, + "settings": { + "executionOrder": "v1" + }, + "versionId": "bd8d96b6-53e1-4cb9-8ee7-0c8be9b86dc9", + "meta": { + "templateCredsSetupCompleted": true, + "instanceId": "73cb7a3e883df514bb47e8d1b34526d30e2abb8f56cd99f10d5948a1e11b25aa" + }, + "id": "t15NIcuhUMXOE8DM", + "tags": [] +} \ No newline at end of file diff --git a/local-ai-packaged/n8n-tool-workflows/Post_Message_to_Slack.json b/local-ai-packaged/n8n-tool-workflows/Post_Message_to_Slack.json new file mode 100644 index 0000000..910db9a --- /dev/null +++ b/local-ai-packaged/n8n-tool-workflows/Post_Message_to_Slack.json @@ -0,0 +1,108 @@ +{ + "name": "Post Message to Slack", + "nodes": [ + { + "parameters": { + "authentication": "oAuth2", + "select": "channel", + "channelId": { + "__rl": true, + "value": "C083QQBQTAM", + "mode": "list", + "cachedResultName": "flowise-n8n" + }, + "text": "={{ $json.body.message }}", + "otherOptions": { + "includeLinkToWorkflow": false + } + }, + "id": "4882859e-bf35-475b-9fd2-a068ac4fc602", + "name": "Slack", + "type": "n8n-nodes-base.slack", + "typeVersion": 2.2, + "position": [ + 1040, + 360 + ], + "credentials": { + "slackOAuth2Api": { + "id": "XtpcBxLD5axuhMm8", + "name": "Slack account" + } + } + }, + { + "parameters": { + "options": {} + }, + "id": "a9e7aa8a-04ba-4dc6-8a14-f2c435df4ad8", + "name": "Respond to Webhook", + "type": "n8n-nodes-base.respondToWebhook", + "typeVersion": 1.1, + "position": [ + 1260, + 360 + ] + }, + { + "parameters": { + "httpMethod": "POST", + "path": "f17f77e5-51dc-4589-8b51-4c8adc23c3c0", + "authentication": "headerAuth", + "responseMode": "responseNode", + "options": {} + }, + "id": "db4d3557-e423-43e9-8f0c-b4309f304567", + "name": "Webhook", + "type": "n8n-nodes-base.webhook", + "typeVersion": 2, + "position": [ + 820, + 360 + ], + "webhookId": "f17f77e5-51dc-4589-8b51-4c8adc23c3c0", + "credentials": { + "httpHeaderAuth": { + "id": "PGr0hc0kn43Di1sz", + "name": "testauth" + } + } + } + ], + "pinData": {}, + "connections": { + "Slack": { + "main": [ + [ + { + "node": "Respond to Webhook", + "type": "main", + "index": 0 + } + ] + ] + }, + "Webhook": { + "main": [ + [ + { + "node": "Slack", + "type": "main", + "index": 0 + } + ] + ] + } + }, + "active": true, + "settings": { + "executionOrder": "v1" + }, + "versionId": "74bd88b9-5eb7-4a1a-8bc3-be2636a3639c", + "meta": { + "templateCredsSetupCompleted": true, + "instanceId": "620f0d7e3114cb344761d7d45a21ef2a32096f91d8696e7057756042e1999e2c" + }, + "id": "dBTFcNDVqjuQ619T", + "tags": [] +} \ No newline at end of file diff --git a/local-ai-packaged/n8n-tool-workflows/Summarize_Slack_Conversation.json b/local-ai-packaged/n8n-tool-workflows/Summarize_Slack_Conversation.json new file mode 100644 index 0000000..2ae9704 --- /dev/null +++ b/local-ai-packaged/n8n-tool-workflows/Summarize_Slack_Conversation.json @@ -0,0 +1,193 @@ +{ + "name": "Summarize Slack Conversation", + "nodes": [ + { + "parameters": { + "authentication": "oAuth2", + "resource": "channel", + "operation": "history", + "channelId": { + "__rl": true, + "value": "C083QQBQTAM", + "mode": "list", + "cachedResultName": "flowise-n8n" + }, + "limit": 10, + "filters": {} + }, + "id": "d572a7b3-311e-4864-a604-11d679ecc855", + "name": "Slack", + "type": "n8n-nodes-base.slack", + "typeVersion": 2.2, + "position": [ + 1040, + 360 + ], + "credentials": { + "slackOAuth2Api": { + "id": "XtpcBxLD5axuhMm8", + "name": "Slack account" + } + } + }, + { + "parameters": { + "options": {} + }, + "id": "c30ce367-cb19-4ef9-b8e7-0e03982960f6", + "name": "Respond to Webhook", + "type": "n8n-nodes-base.respondToWebhook", + "typeVersion": 1.1, + "position": [ + 1840, + 360 + ] + }, + { + "parameters": { + "fieldsToAggregate": { + "fieldToAggregate": [ + { + "fieldToAggregate": "text" + } + ] + }, + "options": {} + }, + "id": "fdb91643-0f0f-4ad5-b333-2294029f0ae7", + "name": "Aggregate", + "type": "n8n-nodes-base.aggregate", + "typeVersion": 1, + "position": [ + 1260, + 360 + ] + }, + { + "parameters": { + "model": "gpt-4o-mini", + "options": {} + }, + "id": "bf2c887e-bb34-44fc-89ea-fad409db0317", + "name": "OpenAI Chat Model", + "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi", + "typeVersion": 1, + "position": [ + 1480, + 580 + ], + "credentials": { + "openAiApi": { + "id": "JJjD91oisPv9cs01", + "name": "OpenAi account" + } + } + }, + { + "parameters": { + "promptType": "define", + "text": "=Summarize the following conversation, outputting just the summary and nothing else - no preabmle or explanation. \n\nKeep in mind the latest message is the first in the list: \n\n{{ $json.text }}" + }, + "id": "90984360-3628-494d-a077-08ec69fd88e5", + "name": "Basic LLM Chain", + "type": "@n8n/n8n-nodes-langchain.chainLlm", + "typeVersion": 1.4, + "position": [ + 1480, + 360 + ] + }, + { + "parameters": { + "path": "66a5755c-5030-4a6b-9b5e-2e09a21456d6", + "authentication": "headerAuth", + "responseMode": "responseNode", + "options": {} + }, + "id": "930860ba-d339-4668-a13c-4e2277308161", + "name": "Webhook", + "type": "n8n-nodes-base.webhook", + "typeVersion": 2, + "position": [ + 820, + 360 + ], + "webhookId": "66a5755c-5030-4a6b-9b5e-2e09a21456d6", + "credentials": { + "httpHeaderAuth": { + "id": "PGr0hc0kn43Di1sz", + "name": "testauth" + } + } + } + ], + "pinData": {}, + "connections": { + "Slack": { + "main": [ + [ + { + "node": "Aggregate", + "type": "main", + "index": 0 + } + ] + ] + }, + "Aggregate": { + "main": [ + [ + { + "node": "Basic LLM Chain", + "type": "main", + "index": 0 + } + ] + ] + }, + "OpenAI Chat Model": { + "ai_languageModel": [ + [ + { + "node": "Basic LLM Chain", + "type": "ai_languageModel", + "index": 0 + } + ] + ] + }, + "Basic LLM Chain": { + "main": [ + [ + { + "node": "Respond to Webhook", + "type": "main", + "index": 0 + } + ] + ] + }, + "Webhook": { + "main": [ + [ + { + "node": "Slack", + "type": "main", + "index": 0 + } + ] + ] + } + }, + "active": true, + "settings": { + "executionOrder": "v1" + }, + "versionId": "039f8fd7-6a15-4070-81ae-1f2851183c86", + "meta": { + "templateCredsSetupCompleted": true, + "instanceId": "620f0d7e3114cb344761d7d45a21ef2a32096f91d8696e7057756042e1999e2c" + }, + "id": "vchuPxsgQU32o0v1", + "tags": [] +} \ No newline at end of file