Merge pull request #1678 from vinodkiran/FEATURE/marketplace-revamp

Marketplace: Revamped UI
This commit is contained in:
Vinod Paidimarry
2024-02-08 03:53:46 -05:00
committed by GitHub
57 changed files with 598 additions and 127 deletions
@@ -1,5 +1,7 @@
{ {
"description": "Use OpenAI Function Agent and Chain to automatically decide which API to call, generating url and body request from conversation", "description": "Use OpenAI Function Agent and Chain to automatically decide which API to call, generating url and body request from conversation",
"categories": "Buffer Memory,ChainTool,API Chain,ChatOpenAI,OpenAI Function Agent,Langchain",
"framework": "Langchain",
"nodes": [ "nodes": [
{ {
"width": 300, "width": 300,
@@ -1,5 +1,7 @@
{ {
"description": "Given API docs, agent automatically decide which API to call, generating url and body request from conversation", "description": "Given API docs, agent automatically decide which API to call, generating url and body request from conversation",
"categories": "Buffer Memory,ChainTool,API Chain,ChatOpenAI,Conversational Agent,Langchain",
"framework": "Langchain",
"nodes": [ "nodes": [
{ {
"width": 300, "width": 300,
@@ -1,5 +1,7 @@
{ {
"description": "Output antonym of given user input using few-shot prompt template built with examples", "description": "Output antonym of given user input using few-shot prompt template built with examples",
"categories": "Few Shot Prompt,ChatOpenAI,LLM Chain,Langchain",
"framework": "Langchain",
"nodes": [ "nodes": [
{ {
"width": 300, "width": 300,
@@ -1,5 +1,7 @@
{ {
"description": "Use AutoGPT - Autonomous agent with chain of thoughts for self-guided task completion", "description": "Use AutoGPT - Autonomous agent with chain of thoughts for self-guided task completion",
"categories": "AutoGPT,SERP Tool,File Read/Write,ChatOpenAI,Pinecone,Langchain",
"framework": "Langchain",
"nodes": [ "nodes": [
{ {
"width": 300, "width": 300,
@@ -1,5 +1,7 @@
{ {
"description": "Use BabyAGI to create tasks and reprioritize for a given objective", "description": "Use BabyAGI to create tasks and reprioritize for a given objective",
"categories": "BabyAGI,ChatOpenAI,Pinecone,Langchain",
"framework": "Langchain",
"nodes": [ "nodes": [
{ {
"width": 300, "width": 300,
@@ -1,5 +1,7 @@
{ {
"description": "Analyse and summarize CSV data", "description": "Analyse and summarize CSV data",
"categories": "CSV Agent,ChatOpenAI,Langchain",
"framework": "Langchain",
"nodes": [ "nodes": [
{ {
"width": 300, "width": 300,
@@ -1,5 +1,7 @@
{ {
"description": "Engage with data sources such as YouTube Transcripts, Google, and more through intelligent Q&A interactions", "description": "Engage with data sources such as YouTube Transcripts, Google, and more through intelligent Q&A interactions",
"categories": "Memory Vector Store,SearchAPI,ChatOpenAI,Conversational Retrieval QA Chain,Langchain",
"framework": "Langchain",
"nodes": [ "nodes": [
{ {
"width": 300, "width": 300,
@@ -1,5 +1,7 @@
{ {
"description": "Use ChatGPT Plugins within LangChain abstractions with GET and POST Tools", "description": "Use ChatGPT Plugins within LangChain abstractions with GET and POST Tools",
"categories": "ChatGPT Plugin,HTTP GET/POST,ChatOpenAI,MRKL Agent,Langchain",
"framework": "Langchain",
"nodes": [ "nodes": [
{ {
"width": 300, "width": 300,
@@ -1,5 +1,7 @@
{ {
"description": "Use Anthropic Claude with 200k context window to ingest whole document for QnA", "description": "Use Anthropic Claude with 200k context window to ingest whole document for QnA",
"categories": "Buffer Memory,Prompt Template,Conversation Chain,ChatAnthropic,Langchain",
"framework": "Langchain",
"nodes": [ "nodes": [
{ {
"width": 300, "width": 300,
@@ -1,5 +1,7 @@
{ {
"description": "Answer question based on retrieved documents (context) with built-in memory to remember conversation using LlamaIndex", "description": "Answer question based on retrieved documents (context) with built-in memory to remember conversation using LlamaIndex",
"categories": "Text File,Prompt Template,ChatOpenAI,Conversation Chain,Pinecone,LlamaIndex,Redis",
"framework": "LlamaIndex",
"badge": "NEW", "badge": "NEW",
"nodes": [ "nodes": [
{ {
@@ -1,5 +1,7 @@
{ {
"description": "A conversational agent for a chat model which utilize chat specific prompts", "description": "A conversational agent for a chat model which utilize chat specific prompts",
"categories": "Calculator Tool,Buffer Memory,SerpAPI,ChatOpenAI,Conversational Agent,Langchain",
"framework": "Langchain",
"nodes": [ "nodes": [
{ {
"width": 300, "width": 300,
@@ -1,6 +1,8 @@
{ {
"description": "Agent optimized for vector retrieval during conversation and answering questions based on previous dialogue.", "description": "Agent optimized for vector retrieval during conversation and answering questions based on previous dialogue.",
"categories": "Retriever Tool,Buffer Memory,ChatOpenAI,Conversational Retrieval Agent, Pinecone,Langchain",
"badge": "POPULAR", "badge": "POPULAR",
"framework": "Langchain",
"nodes": [ "nodes": [
{ {
"width": 300, "width": 300,
@@ -1,6 +1,8 @@
{ {
"description": "Text file QnA using conversational retrieval QA chain", "description": "Text file QnA using conversational retrieval QA chain",
"categories": "TextFile,ChatOpenAI,Conversational Retrieval QA Chain,Pinecone,Langchain",
"badge": "POPULAR", "badge": "POPULAR",
"framework": "Langchain",
"nodes": [ "nodes": [
{ {
"width": 300, "width": 300,
@@ -1,6 +1,8 @@
{ {
"description": "Flowise Docs Github QnA using conversational retrieval QA chain", "description": "Flowise Docs Github QnA using conversational retrieval QA chain",
"categories": "Memory Vector Store,Github Loader,ChatOpenAI,Conversational Retrieval QA Chain,Langchain",
"badge": "POPULAR", "badge": "POPULAR",
"framework": "Langchain",
"nodes": [ "nodes": [
{ {
"width": 300, "width": 300,
@@ -1,5 +1,7 @@
{ {
"description": "Simple LLM Chain using HuggingFace Inference API on falcon-7b-instruct model", "description": "Simple LLM Chain using HuggingFace Inference API on falcon-7b-instruct model",
"categories": "HuggingFace,LLM Chain,Langchain",
"framework": "Langchain",
"nodes": [ "nodes": [
{ {
"width": 300, "width": 300,
@@ -1,5 +1,7 @@
{ {
"description": "Split flows based on if else condition", "description": "Split flows based on if else condition",
"categories": "IfElse Function,ChatOpenAI,OpenAI,LLM Chain,Langchain",
"framework": "Langchain",
"badge": "new", "badge": "new",
"nodes": [ "nodes": [
{ {
@@ -1,6 +1,8 @@
{ {
"description": "Generate image using Replicate Stability text-to-image generative AI model", "description": "Generate image using Replicate Stability text-to-image generative AI model",
"badge": "NEW", "badge": "NEW",
"categories": "Replicate,ChatOpenAI,LLM Chain,Langchain",
"framework": "Langchain",
"nodes": [ "nodes": [
{ {
"width": 300, "width": 300,
@@ -1,6 +1,8 @@
{ {
"description": "Detect text that could generate harmful output and prevent it from being sent to the language model", "description": "Detect text that could generate harmful output and prevent it from being sent to the language model",
"badge": "NEW", "badge": "NEW",
"categories": "Moderation,ChatOpenAI,LLM Chain,Langchain",
"framework": "Langchain",
"nodes": [ "nodes": [
{ {
"width": 300, "width": 300,
@@ -1,6 +1,8 @@
{ {
"description": "Return response as a list (array) instead of a string/text", "description": "Return response as a list (array) instead of a string/text",
"badge": "NEW", "badge": "NEW",
"categories": "CSV Output Parser,ChatOpenAI,LLM Chain,Langchain",
"framework": "Langchain",
"nodes": [ "nodes": [
{ {
"width": 300, "width": 300,
@@ -1,6 +1,8 @@
{ {
"description": "QnA chain using Ollama local LLM, LocalAI embedding model, and Faiss local vector store", "description": "QnA chain using Ollama local LLM, LocalAI embedding model, and Faiss local vector store",
"badge": "POPULAR", "badge": "POPULAR",
"categories": "Text File,ChatOllama,Conversational Retrieval QA Chain,Faiss,Langchain",
"framework": "Langchain",
"nodes": [ "nodes": [
{ {
"width": 300, "width": 300,
@@ -1,5 +1,7 @@
{ {
"description": "Use long term memory like Zep to differentiate conversations between users with sessionId", "description": "Use long term memory like Zep to differentiate conversations between users with sessionId",
"categories": "ChatOpenAI,Conversational Retrieval QA Chain,Zep Memory,Qdrant,Langchain",
"framework": "Langchain",
"nodes": [ "nodes": [
{ {
"width": 300, "width": 300,
@@ -1,6 +1,8 @@
{ {
"description": "Upsert multiple files with metadata and filter by it using conversational retrieval QA chain", "description": "Upsert multiple files with metadata and filter by it using conversational retrieval QA chain",
"categories": "Text File,PDF File,ChatOpenAI,Conversational Retrieval QA Chain,Pinecone,Langchain",
"badge": "POPULAR", "badge": "POPULAR",
"framework": "Langchain",
"nodes": [ "nodes": [
{ {
"width": 300, "width": 300,
@@ -1,5 +1,7 @@
{ {
"description": "A chain that automatically picks an appropriate prompt from multiple prompts", "description": "A chain that automatically picks an appropriate prompt from multiple prompts",
"categories": "ChatOpenAI,Multi Prompt Chain,Langchain",
"framework": "Langchain",
"nodes": [ "nodes": [
{ {
"width": 300, "width": 300,
@@ -1,5 +1,7 @@
{ {
"description": "A chain that automatically picks an appropriate retriever from multiple different vector databases", "description": "A chain that automatically picks an appropriate retriever from multiple different vector databases",
"categories": "ChatOpenAI,Multi Retrieval QA Chain,Pinecone,Chroma,Supabase,Langchain",
"framework": "Langchain",
"nodes": [ "nodes": [
{ {
"width": 300, "width": 300,
@@ -1,5 +1,7 @@
{ {
"description": "Use the agent to choose between multiple different vector databases, with the ability to use other tools", "description": "Use the agent to choose between multiple different vector databases, with the ability to use other tools",
"categories": "Buffer Memory,ChatOpenAI,Chain Tool,Retrieval QA Chain,Redis,Faiss,Conversational Agent,Langchain",
"framework": "Langchain",
"nodes": [ "nodes": [
{ {
"width": 300, "width": 300,
@@ -1,5 +1,7 @@
{ {
"description": "An agent that uses OpenAI's Function Calling functionality to pick the tool and args to call", "description": "An agent that uses OpenAI's Function Calling functionality to pick the tool and args to call",
"categories": "Buffer Memory,Custom Tool, SerpAPI,OpenAI Function,Calculator Tool,ChatOpenAI,Langchain",
"framework": "Langchain",
"nodes": [ "nodes": [
{ {
"width": 300, "width": 300,
@@ -1,5 +1,7 @@
{ {
"description": "OpenAI Assistant that has instructions and can leverage models, tools, and knowledge to respond to user queries", "description": "OpenAI Assistant that has instructions and can leverage models, tools, and knowledge to respond to user queries",
"categories": "Custom Tool, SerpAPI,OpenAI Assistant,Calculator Tool,Langchain",
"framework": "Langchain",
"badge": "NEW", "badge": "NEW",
"nodes": [ "nodes": [
{ {
@@ -1,6 +1,8 @@
{ {
"description": "Use chat history to rephrase user question, and answer the rephrased question using retrieved docs from vector store", "description": "Use chat history to rephrase user question, and answer the rephrased question using retrieved docs from vector store",
"categories": "ChatOpenAI,LLM Chain,SingleStore,Langchain",
"badge": "POPULAR", "badge": "POPULAR",
"framework": "Langchain",
"nodes": [ "nodes": [
{ {
"width": 300, "width": 300,
@@ -1,5 +1,7 @@
{ {
"description": "Use output from a chain as prompt for another chain", "description": "Use output from a chain as prompt for another chain",
"categories": "Custom Tool,OpenAI,LLM Chain,Langchain",
"framework": "Langchain",
"nodes": [ "nodes": [
{ {
"width": 300, "width": 300,
@@ -1,6 +1,8 @@
{ {
"description": "Stateless query engine designed to answer question over your data using LlamaIndex", "description": "Stateless query engine designed to answer question over your data using LlamaIndex",
"categories": "ChatAnthropic,Compact and Refine,Pinecone,LlamaIndex",
"badge": "NEW", "badge": "NEW",
"framework": "LlamaIndex",
"nodes": [ "nodes": [
{ {
"width": 300, "width": 300,
@@ -1,5 +1,7 @@
{ {
"description": "An agent that uses ReAct logic to decide what action to take", "description": "An agent that uses ReAct logic to decide what action to take",
"categories": "Calculator Tool,SerpAPI,ChatOpenAI,MRKL Agent,Langchain",
"framework": "Langchain",
"nodes": [ "nodes": [
{ {
"width": 300, "width": 300,
@@ -1,5 +1,7 @@
{ {
"description": "Use Replicate API that runs Llama 13b v2 model with LLMChain", "description": "Use Replicate API that runs Llama 13b v2 model with LLMChain",
"categories": "Replicate,LLM Chain,Langchain",
"framework": "Langchain",
"nodes": [ "nodes": [
{ {
"width": 300, "width": 300,
@@ -1,5 +1,7 @@
{ {
"description": "Answer questions over a SQL database", "description": "Answer questions over a SQL database",
"categories": "ChatOpenAI,Sql Database Chain,Langchain",
"framework": "Langchain",
"nodes": [ "nodes": [
{ {
"width": 300, "width": 300,
@@ -1,5 +1,7 @@
{ {
"description": "Manually construct prompts to query a SQL database", "description": "Manually construct prompts to query a SQL database",
"categories": "IfElse Function,Variable Set/Get,Custom JS Function,ChatOpenAI,LLM Chain,Langchain",
"framework": "Langchain",
"badge": "new", "badge": "new",
"nodes": [ "nodes": [
{ {
@@ -1,5 +1,7 @@
{ {
"description": "Simple chat engine to handle back and forth conversations using LlamaIndex", "description": "Simple chat engine to handle back and forth conversations using LlamaIndex",
"categories": "BufferMemory,AzureChatOpenAI,LlamaIndex",
"framework": "LlamaIndex",
"badge": "NEW", "badge": "NEW",
"nodes": [ "nodes": [
{ {
@@ -1,5 +1,7 @@
{ {
"description": "Basic example of Conversation Chain with built-in memory - works exactly like ChatGPT", "description": "Basic example of Conversation Chain with built-in memory - works exactly like ChatGPT",
"categories": "Buffer Memory,ChatOpenAI,Conversation Chain,Langchain",
"framework": "Langchain",
"badge": "POPULAR", "badge": "POPULAR",
"nodes": [ "nodes": [
{ {
@@ -1,5 +1,7 @@
{ {
"description": "Basic example of stateless (no memory) LLM Chain with a Prompt Template and LLM Model", "description": "Basic example of stateless (no memory) LLM Chain with a Prompt Template and LLM Model",
"categories": "OpenAI,LLM Chain,Langchain",
"framework": "Langchain",
"nodes": [ "nodes": [
{ {
"width": 300, "width": 300,
@@ -1,5 +1,7 @@
{ {
"description": "Return response as a specified JSON structure instead of a string/text", "description": "Return response as a specified JSON structure instead of a string/text",
"categories": "Structured Output Parser,ChatOpenAI,LLM Chain,Langchain",
"framework": "Langchain",
"badge": "NEW", "badge": "NEW",
"nodes": [ "nodes": [
{ {
@@ -1,5 +1,7 @@
{ {
"description": "Breaks down query into sub questions for each relevant data source, then combine into final response", "description": "Breaks down query into sub questions for each relevant data source, then combine into final response",
"categories": "Sub Question Query Engine,Sticky Note,QueryEngine Tool,Compact and Refine,ChatOpenAI,Pinecone,LlamaIndex",
"framework": "LlamaIndex",
"badge": "NEW", "badge": "NEW",
"nodes": [ "nodes": [
{ {
@@ -1,5 +1,7 @@
{ {
"description": "Language translation using LLM Chain with a Chat Prompt Template and Chat Model", "description": "Language translation using LLM Chain with a Chat Prompt Template and Chat Model",
"categories": "Chat Prompt Template,ChatOpenAI,LLM Chain,Langchain",
"framework": "Langchain",
"nodes": [ "nodes": [
{ {
"width": 300, "width": 300,
@@ -1,4 +1,7 @@
{ {
"description": "QA chain for Vectara",
"categories": "Vectara QA Chain,Vectara,Langchain",
"framework": "Langchain",
"nodes": [ "nodes": [
{ {
"width": 300, "width": 300,
@@ -1,5 +1,7 @@
{ {
"description": "Conversational Agent with ability to visit a website and extract information", "description": "Conversational Agent with ability to visit a website and extract information",
"categories": "Buffer Memory,Web Browser,ChatOpenAI,Conversational Agent",
"framework": "Langchain",
"nodes": [ "nodes": [
{ {
"width": 300, "width": 300,
@@ -1,5 +1,7 @@
{ {
"description": "Scrape web pages for QnA with long term memory Motorhead and return source documents", "description": "Scrape web pages for QnA with long term memory Motorhead and return source documents",
"categories": "HtmlToMarkdown,Cheerio Web Scraper,ChatOpenAI,Redis,Pinecone,Langchain",
"framework": "Langchain",
"badge": "POPULAR", "badge": "POPULAR",
"nodes": [ "nodes": [
{ {
@@ -1,5 +1,6 @@
{ {
"name": "add_contact_hubspot", "name": "add_contact_hubspot",
"framework": "Langchain",
"description": "Add new contact to Hubspot", "description": "Add new contact to Hubspot",
"color": "linear-gradient(rgb(85,198,123), rgb(0,230,99))", "color": "linear-gradient(rgb(85,198,123), rgb(0,230,99))",
"iconSrc": "https://cdn.worldvectorlogo.com/logos/hubspot-1.svg", "iconSrc": "https://cdn.worldvectorlogo.com/logos/hubspot-1.svg",
@@ -1,5 +1,6 @@
{ {
"name": "add_airtable", "name": "add_airtable",
"framework": "Langchain",
"description": "Add column1, column2 to Airtable", "description": "Add column1, column2 to Airtable",
"color": "linear-gradient(rgb(125,71,222), rgb(128,102,23))", "color": "linear-gradient(rgb(125,71,222), rgb(128,102,23))",
"iconSrc": "https://raw.githubusercontent.com/gilbarbara/logos/main/logos/airtable.svg", "iconSrc": "https://raw.githubusercontent.com/gilbarbara/logos/main/logos/airtable.svg",
@@ -1,5 +1,6 @@
{ {
"name": "todays_date_time", "name": "todays_date_time",
"framework": "Langchain",
"description": "Useful to get todays day, date and time.", "description": "Useful to get todays day, date and time.",
"color": "linear-gradient(rgb(117,118,129), rgb(230,10,250))", "color": "linear-gradient(rgb(117,118,129), rgb(230,10,250))",
"iconSrc": "https://raw.githubusercontent.com/gilbarbara/logos/main/logos/javascript.svg", "iconSrc": "https://raw.githubusercontent.com/gilbarbara/logos/main/logos/javascript.svg",
@@ -1,5 +1,6 @@
{ {
"name": "get_stock_movers", "name": "get_stock_movers",
"framework": "Langchain",
"description": "Get the stocks that has biggest price/volume moves, e.g. actives, gainers, losers, etc.", "description": "Get the stocks that has biggest price/volume moves, e.g. actives, gainers, losers, etc.",
"iconSrc": "https://rapidapi.com/cdn/images?url=https://rapidapi-prod-apis.s3.amazonaws.com/9c/e743343bdd41edad39a3fdffd5b974/016c33699f51603ae6fe4420c439124b.png", "iconSrc": "https://rapidapi.com/cdn/images?url=https://rapidapi-prod-apis.s3.amazonaws.com/9c/e743343bdd41edad39a3fdffd5b974/016c33699f51603ae6fe4420c439124b.png",
"color": "linear-gradient(rgb(191,202,167), rgb(143,202,246))", "color": "linear-gradient(rgb(191,202,167), rgb(143,202,246))",
@@ -1,5 +1,6 @@
{ {
"name": "make_webhook", "name": "make_webhook",
"framework": "Langchain",
"description": "Useful when you need to send message to Discord", "description": "Useful when you need to send message to Discord",
"color": "linear-gradient(rgb(19,94,2), rgb(19,124,59))", "color": "linear-gradient(rgb(19,94,2), rgb(19,124,59))",
"iconSrc": "https://github.com/FlowiseAI/Flowise/assets/26460777/517fdab2-8a6e-4781-b3c8-fb92cc78aa0b", "iconSrc": "https://github.com/FlowiseAI/Flowise/assets/26460777/517fdab2-8a6e-4781-b3c8-fb92cc78aa0b",
@@ -1,5 +1,6 @@
{ {
"name": "send_message_to_discord_channel", "name": "send_message_to_discord_channel",
"framework": "Langchain",
"description": "Send message to Discord channel", "description": "Send message to Discord channel",
"color": "linear-gradient(rgb(155,190,84), rgb(176,69,245))", "color": "linear-gradient(rgb(155,190,84), rgb(176,69,245))",
"iconSrc": "https://raw.githubusercontent.com/gilbarbara/logos/main/logos/discord-icon.svg", "iconSrc": "https://raw.githubusercontent.com/gilbarbara/logos/main/logos/discord-icon.svg",
@@ -1,5 +1,6 @@
{ {
"name": "send_message_to_slack_channel", "name": "send_message_to_slack_channel",
"framework": "Langchain",
"description": "Send message to Slack channel", "description": "Send message to Slack channel",
"color": "linear-gradient(rgb(155,190,84), rgb(176,69,245))", "color": "linear-gradient(rgb(155,190,84), rgb(176,69,245))",
"iconSrc": "https://raw.githubusercontent.com/gilbarbara/logos/main/logos/slack-icon.svg", "iconSrc": "https://raw.githubusercontent.com/gilbarbara/logos/main/logos/slack-icon.svg",
@@ -1,5 +1,6 @@
{ {
"name": "send_message_to_teams_channel", "name": "send_message_to_teams_channel",
"framework": "Langchain",
"description": "Send message to Teams channel", "description": "Send message to Teams channel",
"color": "linear-gradient(rgb(155,190,84), rgb(176,69,245))", "color": "linear-gradient(rgb(155,190,84), rgb(176,69,245))",
"iconSrc": "https://raw.githubusercontent.com/gilbarbara/logos/main/logos/microsoft-teams.svg", "iconSrc": "https://raw.githubusercontent.com/gilbarbara/logos/main/logos/microsoft-teams.svg",
@@ -1,5 +1,6 @@
{ {
"name": "sendgrid_email", "name": "sendgrid_email",
"framework": "Langchain",
"description": "Send email using SendGrid", "description": "Send email using SendGrid",
"color": "linear-gradient(rgb(230,108,70), rgb(222,4,98))", "color": "linear-gradient(rgb(230,108,70), rgb(222,4,98))",
"iconSrc": "https://raw.githubusercontent.com/gilbarbara/logos/main/logos/sendgrid-icon.svg", "iconSrc": "https://raw.githubusercontent.com/gilbarbara/logos/main/logos/sendgrid-icon.svg",
+28 -26
View File
@@ -1223,50 +1223,52 @@ export class App {
// Marketplaces // Marketplaces
// ---------------------------------------- // ----------------------------------------
// Get all chatflows for marketplaces // Get all templates for marketplaces
this.app.get('/api/v1/marketplaces/chatflows', async (req: Request, res: Response) => { this.app.get('/api/v1/marketplaces/templates', async (req: Request, res: Response) => {
const marketplaceDir = path.join(__dirname, '..', 'marketplaces', 'chatflows') let marketplaceDir = path.join(__dirname, '..', 'marketplaces', 'chatflows')
const jsonsInDir = fs.readdirSync(marketplaceDir).filter((file) => path.extname(file) === '.json') let jsonsInDir = fs.readdirSync(marketplaceDir).filter((file) => path.extname(file) === '.json')
const templates: any[] = [] let templates: any[] = []
jsonsInDir.forEach((file, index) => { jsonsInDir.forEach((file, index) => {
const filePath = path.join(__dirname, '..', 'marketplaces', 'chatflows', file) const filePath = path.join(__dirname, '..', 'marketplaces', 'chatflows', file)
const fileData = fs.readFileSync(filePath) const fileData = fs.readFileSync(filePath)
const fileDataObj = JSON.parse(fileData.toString()) const fileDataObj = JSON.parse(fileData.toString())
const template = { const template = {
id: index, id: index,
name: file.split('.json')[0], templateName: file.split('.json')[0],
flowData: fileData.toString(), flowData: fileData.toString(),
badge: fileDataObj?.badge, badge: fileDataObj?.badge,
framework: fileDataObj?.framework,
categories: fileDataObj?.categories,
type: 'Chatflow',
description: fileDataObj?.description || '' description: fileDataObj?.description || ''
} }
templates.push(template) templates.push(template)
}) })
marketplaceDir = path.join(__dirname, '..', 'marketplaces', 'tools')
jsonsInDir = fs.readdirSync(marketplaceDir).filter((file) => path.extname(file) === '.json')
jsonsInDir.forEach((file, index) => {
const filePath = path.join(__dirname, '..', 'marketplaces', 'tools', file)
const fileData = fs.readFileSync(filePath)
const fileDataObj = JSON.parse(fileData.toString())
const template = {
...fileDataObj,
id: index,
type: 'Tool',
framework: fileDataObj?.framework,
badge: fileDataObj?.badge,
categories: '',
templateName: file.split('.json')[0]
}
templates.push(template)
})
const FlowiseDocsQnA = templates.find((tmp) => tmp.name === 'Flowise Docs QnA') const FlowiseDocsQnA = templates.find((tmp) => tmp.name === 'Flowise Docs QnA')
const FlowiseDocsQnAIndex = templates.findIndex((tmp) => tmp.name === 'Flowise Docs QnA') const FlowiseDocsQnAIndex = templates.findIndex((tmp) => tmp.name === 'Flowise Docs QnA')
if (FlowiseDocsQnA && FlowiseDocsQnAIndex > 0) { if (FlowiseDocsQnA && FlowiseDocsQnAIndex > 0) {
templates.splice(FlowiseDocsQnAIndex, 1) templates.splice(FlowiseDocsQnAIndex, 1)
templates.unshift(FlowiseDocsQnA) templates.unshift(FlowiseDocsQnA)
} }
return res.json(templates) return res.json(templates.sort((a, b) => a.templateName.localeCompare(b.templateName)))
})
// Get all tools for marketplaces
this.app.get('/api/v1/marketplaces/tools', async (req: Request, res: Response) => {
const marketplaceDir = path.join(__dirname, '..', 'marketplaces', 'tools')
const jsonsInDir = fs.readdirSync(marketplaceDir).filter((file) => path.extname(file) === '.json')
const templates: any[] = []
jsonsInDir.forEach((file, index) => {
const filePath = path.join(__dirname, '..', 'marketplaces', 'tools', file)
const fileData = fs.readFileSync(filePath)
const fileDataObj = JSON.parse(fileData.toString())
const template = {
...fileDataObj,
id: index,
templateName: file.split('.json')[0]
}
templates.push(template)
})
return res.json(templates)
}) })
// ---------------------------------------- // ----------------------------------------
+2 -1
View File
@@ -10,7 +10,8 @@ module.exports = {
} }
} }
] ]
} },
ignoreWarnings: [/Failed to parse source map/] // Ignore warnings about source maps
} }
} }
} }
+3 -1
View File
@@ -2,8 +2,10 @@ import client from './client'
const getAllChatflowsMarketplaces = () => client.get('/marketplaces/chatflows') const getAllChatflowsMarketplaces = () => client.get('/marketplaces/chatflows')
const getAllToolsMarketplaces = () => client.get('/marketplaces/tools') const getAllToolsMarketplaces = () => client.get('/marketplaces/tools')
const getAllTemplatesFromMarketplaces = () => client.get('/marketplaces/templates')
export default { export default {
getAllChatflowsMarketplaces, getAllChatflowsMarketplaces,
getAllToolsMarketplaces getAllToolsMarketplaces,
getAllTemplatesFromMarketplaces
} }
@@ -0,0 +1,146 @@
import PropTypes from 'prop-types'
import { styled } from '@mui/material/styles'
import Table from '@mui/material/Table'
import TableBody from '@mui/material/TableBody'
import TableCell, { tableCellClasses } from '@mui/material/TableCell'
import TableContainer from '@mui/material/TableContainer'
import TableHead from '@mui/material/TableHead'
import TableRow from '@mui/material/TableRow'
import Paper from '@mui/material/Paper'
import Chip from '@mui/material/Chip'
import { Button, Typography } from '@mui/material'
const StyledTableCell = styled(TableCell)(({ theme }) => ({
[`&.${tableCellClasses.head}`]: {
backgroundColor: theme.palette.common.black,
color: theme.palette.common.white
},
[`&.${tableCellClasses.body}`]: {
fontSize: 14
}
}))
const StyledTableRow = styled(TableRow)(({ theme }) => ({
'&:nth-of-type(odd)': {
backgroundColor: theme.palette.action.hover
},
// hide last border
'&:last-child td, &:last-child th': {
border: 0
}
}))
export const MarketplaceTable = ({ data, filterFunction, filterByBadge, filterByType, filterByFramework, goToCanvas, goToTool }) => {
const openTemplate = (selectedTemplate) => {
if (selectedTemplate.flowData) {
goToCanvas(selectedTemplate)
} else {
goToTool(selectedTemplate)
}
}
return (
<>
<TableContainer style={{ marginTop: '30', border: 1 }} component={Paper}>
<Table sx={{ minWidth: 650 }} size='small' aria-label='a dense table'>
<TableHead>
<TableRow sx={{ marginTop: '10', backgroundColor: 'primary' }}>
<StyledTableCell component='th' scope='row' style={{ width: '15%' }} key='0'>
Name
</StyledTableCell>
<StyledTableCell component='th' scope='row' style={{ width: '5%' }} key='1'>
Type
</StyledTableCell>
<StyledTableCell style={{ width: '35%' }} key='2'>
Description
</StyledTableCell>
<StyledTableCell style={{ width: '35%' }} key='3'>
Nodes
</StyledTableCell>
<StyledTableCell component='th' scope='row' style={{ width: '5%' }} key='4'>
&nbsp;
</StyledTableCell>
</TableRow>
</TableHead>
<TableBody>
{data
.filter(filterByBadge)
.filter(filterByType)
.filter(filterFunction)
.filter(filterByFramework)
.map((row, index) => (
<StyledTableRow key={index}>
<TableCell key='0'>
<Typography
sx={{ fontSize: '1.2rem', fontWeight: 500, overflowWrap: 'break-word', whiteSpace: 'pre-line' }}
>
<Button onClick={() => openTemplate(row)} sx={{ textAlign: 'left' }}>
{row.templateName || row.name}
</Button>
</Typography>
</TableCell>
<TableCell key='1'>
<Typography>{row.type}</Typography>
</TableCell>
<TableCell key='2'>
<Typography sx={{ overflowWrap: 'break-word', whiteSpace: 'pre-line' }}>
{row.description || ''}
</Typography>
</TableCell>
<TableCell key='3'>
<div
style={{
display: 'flex',
flexDirection: 'row',
flexWrap: 'wrap',
marginTop: 5
}}
>
{row.categories &&
row.categories
.split(',')
.map((tag, index) => (
<Chip
variant='outlined'
key={index}
size='small'
label={tag.toUpperCase()}
style={{ marginRight: 3, marginBottom: 3 }}
/>
))}
</div>
</TableCell>
<TableCell key='4'>
<Typography>
{row.badge &&
row.badge
.split(';')
.map((tag, index) => (
<Chip
color={tag === 'POPULAR' ? 'primary' : 'error'}
key={index}
size='small'
label={tag.toUpperCase()}
style={{ marginRight: 5, marginBottom: 5 }}
/>
))}
</Typography>
</TableCell>
</StyledTableRow>
))}
</TableBody>
</Table>
</TableContainer>
</>
)
}
MarketplaceTable.propTypes = {
data: PropTypes.array,
filterFunction: PropTypes.func,
filterByBadge: PropTypes.func,
filterByType: PropTypes.func,
filterByFramework: PropTypes.func,
goToTool: PropTypes.func,
goToCanvas: PropTypes.func
}
+300 -76
View File
@@ -4,9 +4,26 @@ import { useSelector } from 'react-redux'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
// material-ui // material-ui
import { Grid, Box, Stack, Tabs, Tab, Badge } from '@mui/material' import {
Grid,
Box,
Stack,
Badge,
Toolbar,
TextField,
InputAdornment,
ButtonGroup,
ToggleButton,
InputLabel,
FormControl,
Select,
OutlinedInput,
Checkbox,
ListItemText,
Button
} from '@mui/material'
import { useTheme } from '@mui/material/styles' import { useTheme } from '@mui/material/styles'
import { IconHierarchy, IconTool } from '@tabler/icons' import { IconChevronsDown, IconChevronsUp, IconLayoutGrid, IconList, IconSearch } from '@tabler/icons'
// project imports // project imports
import MainCard from 'ui-component/cards/MainCard' import MainCard from 'ui-component/cards/MainCard'
@@ -23,6 +40,10 @@ import useApi from 'hooks/useApi'
// const // const
import { baseURL } from 'store/constant' import { baseURL } from 'store/constant'
import * as React from 'react'
import ToggleButtonGroup from '@mui/material/ToggleButtonGroup'
import { MarketplaceTable } from '../../ui-component/table/MarketplaceTable'
import MenuItem from '@mui/material/MenuItem'
function TabPanel(props) { function TabPanel(props) {
const { children, value, index, ...other } = props const { children, value, index, ...other } = props
@@ -45,6 +66,19 @@ TabPanel.propTypes = {
value: PropTypes.number.isRequired value: PropTypes.number.isRequired
} }
const ITEM_HEIGHT = 48
const ITEM_PADDING_TOP = 8
const badges = ['POPULAR', 'NEW']
const types = ['Chatflow', 'Tool']
const framework = ['Langchain', 'LlamaIndex']
const MenuProps = {
PaperProps: {
style: {
maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP,
width: 250
}
}
}
// ==============================|| Marketplace ||============================== // // ==============================|| Marketplace ||============================== //
const Marketplace = () => { const Marketplace = () => {
@@ -53,16 +87,77 @@ const Marketplace = () => {
const theme = useTheme() const theme = useTheme()
const customization = useSelector((state) => state.customization) const customization = useSelector((state) => state.customization)
const [isChatflowsLoading, setChatflowsLoading] = useState(true) const [isLoading, setLoading] = useState(true)
const [isToolsLoading, setToolsLoading] = useState(true)
const [images, setImages] = useState({}) const [images, setImages] = useState({})
const tabItems = ['Chatflows', 'Tools']
const [value, setValue] = useState(0)
const [showToolDialog, setShowToolDialog] = useState(false) const [showToolDialog, setShowToolDialog] = useState(false)
const [toolDialogProps, setToolDialogProps] = useState({}) const [toolDialogProps, setToolDialogProps] = useState({})
const getAllChatflowsMarketplacesApi = useApi(marketplacesApi.getAllChatflowsMarketplaces) const getAllTemplatesMarketplacesApi = useApi(marketplacesApi.getAllTemplatesFromMarketplaces)
const getAllToolsMarketplacesApi = useApi(marketplacesApi.getAllToolsMarketplaces)
const [view, setView] = React.useState(localStorage.getItem('mpDisplayStyle') || 'card')
const [search, setSearch] = useState('')
const [badgeFilter, setBadgeFilter] = useState([])
const [typeFilter, setTypeFilter] = useState([])
const [frameworkFilter, setFrameworkFilter] = useState([])
const [open, setOpen] = useState(false)
const handleBadgeFilterChange = (event) => {
const {
target: { value }
} = event
setBadgeFilter(
// On autofill we get a stringified value.
typeof value === 'string' ? value.split(',') : value
)
}
const handleTypeFilterChange = (event) => {
const {
target: { value }
} = event
setTypeFilter(
// On autofill we get a stringified value.
typeof value === 'string' ? value.split(',') : value
)
}
const handleFrameworkFilterChange = (event) => {
const {
target: { value }
} = event
setFrameworkFilter(
// On autofill we get a stringified value.
typeof value === 'string' ? value.split(',') : value
)
}
const handleViewChange = (event, nextView) => {
localStorage.setItem('mpDisplayStyle', nextView)
setView(nextView)
}
const onSearchChange = (event) => {
setSearch(event.target.value)
}
function filterFlows(data) {
return (
data.categories?.toLowerCase().indexOf(search.toLowerCase()) > -1 ||
data.templateName.toLowerCase().indexOf(search.toLowerCase()) > -1 ||
(data.description && data.description.toLowerCase().indexOf(search.toLowerCase()) > -1)
)
}
function filterByBadge(data) {
return badgeFilter.length > 0 ? badgeFilter.includes(data.badge) : true
}
function filterByType(data) {
return typeFilter.length > 0 ? typeFilter.includes(data.type) : true
}
function filterByFramework(data) {
return frameworkFilter.length > 0 ? frameworkFilter.includes(data.framework) : true
}
const onUseTemplate = (selectedTool) => { const onUseTemplate = (selectedTool) => {
const dialogProp = { const dialogProp = {
@@ -90,39 +185,33 @@ const Marketplace = () => {
navigate(`/marketplace/${selectedChatflow.id}`, { state: selectedChatflow }) navigate(`/marketplace/${selectedChatflow.id}`, { state: selectedChatflow })
} }
const handleChange = (event, newValue) => {
setValue(newValue)
}
useEffect(() => { useEffect(() => {
getAllChatflowsMarketplacesApi.request() getAllTemplatesMarketplacesApi.request()
getAllToolsMarketplacesApi.request()
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, []) }, [])
useEffect(() => { useEffect(() => {
setChatflowsLoading(getAllChatflowsMarketplacesApi.loading) setLoading(getAllTemplatesMarketplacesApi.loading)
}, [getAllChatflowsMarketplacesApi.loading]) }, [getAllTemplatesMarketplacesApi.loading])
useEffect(() => { useEffect(() => {
setToolsLoading(getAllToolsMarketplacesApi.loading) if (getAllTemplatesMarketplacesApi.data) {
}, [getAllToolsMarketplacesApi.loading])
useEffect(() => {
if (getAllChatflowsMarketplacesApi.data) {
try { try {
const chatflows = getAllChatflowsMarketplacesApi.data const flows = getAllTemplatesMarketplacesApi.data
const images = {} const images = {}
for (let i = 0; i < chatflows.length; i += 1) { for (let i = 0; i < flows.length; i += 1) {
const flowDataStr = chatflows[i].flowData if (flows[i].flowData) {
const flowDataStr = flows[i].flowData
const flowData = JSON.parse(flowDataStr) const flowData = JSON.parse(flowDataStr)
const nodes = flowData.nodes || [] const nodes = flowData.nodes || []
images[chatflows[i].id] = [] images[flows[i].id] = []
for (let j = 0; j < nodes.length; j += 1) { for (let j = 0; j < nodes.length; j += 1) {
const imageSrc = `${baseURL}/api/v1/node-icon/${nodes[j].data.name}` const imageSrc = `${baseURL}/api/v1/node-icon/${nodes[j].data.name}`
if (!images[chatflows[i].id].includes(imageSrc)) { if (!images[flows[i].id].includes(imageSrc)) {
images[chatflows[i].id].push(imageSrc) images[flows[i].id].push(imageSrc)
}
} }
} }
} }
@@ -131,31 +220,175 @@ const Marketplace = () => {
console.error(e) console.error(e)
} }
} }
}, [getAllChatflowsMarketplacesApi.data]) }, [getAllTemplatesMarketplacesApi.data])
return ( return (
<> <>
<MainCard sx={{ background: customization.isDarkMode ? theme.palette.common.black : '' }}> <MainCard sx={{ background: customization.isDarkMode ? theme.palette.common.black : '' }}>
<Stack flexDirection='row'> <Box sx={{ flexGrow: 1 }}>
<Toolbar
disableGutters={true}
style={{
margin: 1,
padding: 1,
paddingBottom: 10,
display: 'flex',
justifyContent: 'space-between',
width: '100%'
}}
>
<h1>Marketplace</h1> <h1>Marketplace</h1>
</Stack> <TextField
<Tabs sx={{ mb: 2 }} variant='fullWidth' value={value} onChange={handleChange} aria-label='tabs'> size='small'
{tabItems.map((item, index) => ( id='search-filter-textbox'
<Tab sx={{ display: { xs: 'none', sm: 'block' }, ml: 3 }}
key={index} variant='outlined'
icon={index === 0 ? <IconHierarchy /> : <IconTool />} fullWidth='true'
iconPosition='start' placeholder='Search name or description or node name'
label={<span style={{ fontSize: '1.1rem' }}>{item}</span>} onChange={onSearchChange}
InputProps={{
startAdornment: (
<InputAdornment position='start'>
<IconSearch />
</InputAdornment>
)
}}
/> />
<Button
sx={{ width: '220px', ml: 3, mr: 5 }}
variant='outlined'
onClick={() => setOpen(!open)}
startIcon={open ? <IconChevronsUp /> : <IconChevronsDown />}
>
{open ? 'Hide Filters' : 'Show Filters'}
</Button>
<Box sx={{ flexGrow: 1 }} />
<ButtonGroup sx={{ maxHeight: 40 }} disableElevation variant='contained' aria-label='outlined primary button group'>
<ButtonGroup disableElevation variant='contained' aria-label='outlined primary button group'>
<ToggleButtonGroup
sx={{ maxHeight: 40 }}
value={view}
color='primary'
exclusive
onChange={handleViewChange}
>
<ToggleButton
sx={{ color: theme?.customization?.isDarkMode ? 'white' : 'inherit' }}
variant='contained'
value='card'
title='Card View'
>
<IconLayoutGrid />
</ToggleButton>
<ToggleButton
sx={{ color: theme?.customization?.isDarkMode ? 'white' : 'inherit' }}
variant='contained'
value='list'
title='List View'
>
<IconList />
</ToggleButton>
</ToggleButtonGroup>
</ButtonGroup>
</ButtonGroup>
</Toolbar>
</Box>
{open && (
<Box sx={{ flexGrow: 1, mb: 2 }}>
<Toolbar
disableGutters={true}
style={{
margin: 1,
padding: 1,
paddingBottom: 10,
display: 'flex',
justifyContent: 'flex-start',
width: '100%',
borderBottom: '1px solid'
}}
>
<FormControl sx={{ m: 1, width: 250 }}>
<InputLabel size='small' id='filter-badge-label'>
Tag
</InputLabel>
<Select
labelId='filter-badge-label'
id='filter-badge-checkbox'
size='small'
multiple
value={badgeFilter}
onChange={handleBadgeFilterChange}
input={<OutlinedInput label='Badge' />}
renderValue={(selected) => selected.join(', ')}
MenuProps={MenuProps}
>
{badges.map((name) => (
<MenuItem key={name} value={name}>
<Checkbox checked={badgeFilter.indexOf(name) > -1} />
<ListItemText primary={name} />
</MenuItem>
))} ))}
</Tabs> </Select>
{tabItems.map((item, index) => ( </FormControl>
<TabPanel key={index} value={value} index={index}> <FormControl sx={{ m: 1, width: 250 }}>
{item === 'Chatflows' && ( <InputLabel size='small' id='type-badge-label'>
Type
</InputLabel>
<Select
size='small'
labelId='type-badge-label'
id='type-badge-checkbox'
multiple
value={typeFilter}
onChange={handleTypeFilterChange}
input={<OutlinedInput label='Badge' />}
renderValue={(selected) => selected.join(', ')}
MenuProps={MenuProps}
>
{types.map((name) => (
<MenuItem key={name} value={name}>
<Checkbox checked={typeFilter.indexOf(name) > -1} />
<ListItemText primary={name} />
</MenuItem>
))}
</Select>
</FormControl>
<FormControl sx={{ m: 1, width: 250 }}>
<InputLabel size='small' id='type-fw-label'>
Framework
</InputLabel>
<Select
size='small'
labelId='type-fw-label'
id='type-fw-checkbox'
multiple
value={frameworkFilter}
onChange={handleFrameworkFilterChange}
input={<OutlinedInput label='Badge' />}
renderValue={(selected) => selected.join(', ')}
MenuProps={MenuProps}
>
{framework.map((name) => (
<MenuItem key={name} value={name}>
<Checkbox checked={frameworkFilter.indexOf(name) > -1} />
<ListItemText primary={name} />
</MenuItem>
))}
</Select>
</FormControl>
</Toolbar>
</Box>
)}
{!isLoading && (!view || view === 'card') && getAllTemplatesMarketplacesApi.data && (
<>
<Grid container spacing={gridSpacing}> <Grid container spacing={gridSpacing}>
{!isChatflowsLoading && {getAllTemplatesMarketplacesApi.data
getAllChatflowsMarketplacesApi.data && .filter(filterByBadge)
getAllChatflowsMarketplacesApi.data.map((data, index) => ( .filter(filterByType)
.filter(filterFlows)
.filter(filterByFramework)
.map((data, index) => (
<Grid key={index} item lg={3} md={4} sm={6} xs={12}> <Grid key={index} item lg={3} md={4} sm={6} xs={12}>
{data.badge && ( {data.badge && (
<Badge <Badge
@@ -167,44 +400,35 @@ const Marketplace = () => {
badgeContent={data.badge} badgeContent={data.badge}
color={data.badge === 'POPULAR' ? 'primary' : 'error'} color={data.badge === 'POPULAR' ? 'primary' : 'error'}
> >
<ItemCard onClick={() => goToCanvas(data)} data={data} images={images[data.id]} /> {data.type === 'Chatflow' && (
</Badge>
)}
{!data.badge && (
<ItemCard onClick={() => goToCanvas(data)} data={data} images={images[data.id]} /> <ItemCard onClick={() => goToCanvas(data)} data={data} images={images[data.id]} />
)} )}
</Grid> {data.type === 'Tool' && <ItemCard data={data} onClick={() => goToTool(data)} />}
))}
</Grid>
)}
{item === 'Tools' && (
<Grid container spacing={gridSpacing}>
{!isToolsLoading &&
getAllToolsMarketplacesApi.data &&
getAllToolsMarketplacesApi.data.map((data, index) => (
<Grid key={index} item lg={3} md={4} sm={6} xs={12}>
{data.badge && (
<Badge
sx={{
'& .MuiBadge-badge': {
right: 20
}
}}
badgeContent={data.badge}
color={data.badge === 'POPULAR' ? 'primary' : 'error'}
>
<ItemCard data={data} onClick={() => goToTool(data)} />
</Badge> </Badge>
)} )}
{!data.badge && <ItemCard data={data} onClick={() => goToTool(data)} />} {!data.badge && data.type === 'Chatflow' && (
</Grid> <ItemCard onClick={() => goToCanvas(data)} data={data} images={images[data.id]} />
))}
</Grid>
)} )}
</TabPanel> {!data.badge && data.type === 'Tool' && <ItemCard data={data} onClick={() => goToTool(data)} />}
</Grid>
))} ))}
{((!isChatflowsLoading && (!getAllChatflowsMarketplacesApi.data || getAllChatflowsMarketplacesApi.data.length === 0)) || </Grid>
(!isToolsLoading && (!getAllToolsMarketplacesApi.data || getAllToolsMarketplacesApi.data.length === 0))) && ( </>
)}
{!isLoading && view === 'list' && getAllTemplatesMarketplacesApi.data && (
<MarketplaceTable
sx={{ mt: 20 }}
data={getAllTemplatesMarketplacesApi.data}
filterFunction={filterFlows}
filterByType={filterByType}
filterByBadge={filterByBadge}
filterByFramework={filterByFramework}
goToTool={goToTool}
goToCanvas={goToCanvas}
/>
)}
{!isLoading && (!getAllTemplatesMarketplacesApi.data || getAllTemplatesMarketplacesApi.data.length === 0) && (
<Stack sx={{ alignItems: 'center', justifyContent: 'center' }} flexDirection='column'> <Stack sx={{ alignItems: 'center', justifyContent: 'center' }} flexDirection='column'>
<Box sx={{ p: 2, height: 'auto' }}> <Box sx={{ p: 2, height: 'auto' }}>
<img <img