├── .env.example ├── .eslintrc.json ├── .github ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── pull_request_template.md └── workflows │ └── ci.yml ├── .gitignore ├── LICENSE ├── README.md ├── app ├── api │ └── v1 │ │ └── chatbots │ │ └── [chatbotId] │ │ └── route.js ├── app │ ├── chatbots │ │ ├── [chatbotId] │ │ │ ├── client-page.js │ │ │ ├── loading.js │ │ │ └── page.js │ │ ├── client-page.js │ │ ├── loading.js │ │ └── page.js │ ├── client-page.js │ ├── container.js │ ├── layout.js │ ├── loading.js │ ├── page.js │ ├── prompt-templates │ │ ├── client-page.js │ │ ├── loading.js │ │ └── page.js │ └── sidebar.js ├── favicon.ico ├── landing-page.js ├── layout.js ├── loading.js ├── page.js ├── providers.js └── user │ └── login │ ├── login.js │ └── page.js ├── components ├── chat │ ├── index.js │ ├── input.js │ ├── output.js │ └── prompt-templates.js ├── code-block.js ├── page-header.js └── user-menu.js ├── jsconfig.json ├── lib ├── api-docs.js ├── api.js ├── chain.js ├── prisma.js ├── prompt-template.js ├── sidebar.js └── theme.js ├── next.config.js ├── package-lock.json ├── package.json ├── pages └── api │ ├── auth │ └── [...nextauth].js │ ├── chatbots │ ├── [chatbotId] │ │ ├── index.js │ │ └── messages │ │ │ └── index.js │ └── index.js │ └── prompt-templates │ ├── [promptId] │ └── index.js │ └── index.js ├── prisma ├── migrations │ ├── 20230327164641_user_defaults │ │ └── migration.sql │ ├── 20230327201911_prompt_templates │ │ └── migration.sql │ ├── 20230329205257_chatbots │ │ └── migration.sql │ ├── 20230406080854_chatbot_message │ │ └── migration.sql │ ├── 20230409152256_chatbot_prompt_template │ │ └── migration.sql │ └── migration_lock.toml └── schema.prisma └── public ├── chatbot.png ├── next.svg ├── thirteen.svg ├── user.png └── vercel.svg /.env.example: -------------------------------------------------------------------------------- 1 | DATABASE_URL="" 2 | NEXTAUTH_SECRET="" 3 | NEXTAUTH_URL="" 4 | NEXT_PUBLIC_GITHUB_ID="" 5 | NEXT_PUBLIC_GITHUB_SECRET="" -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to LangChain UI 2 | 3 | Thanks for being interested in contributing to LangChain UI ❤️. 4 | We are extremely open to any and all contributions you might be intersted in making. 5 | To contribute to this project, please follow a ["fork and pull request"](https://docs.github.com/en/get-started/quickstart/contributing-to-projects) workflow. 6 | Please do not try to push directly to this repo unless you are maintainer. 7 | 8 | ## Guidelines 9 | 10 | ### Issues 11 | 12 | The [issues](https://github.com/homanp/langchain-ui/issues) contains all current bugs, improvements, and feature requests. 13 | Please use the correspondnig label when creating an issue. 14 | 15 | The current set of labels are: 16 | 17 | - `api` for LangChain related API integrations 18 | - `infrastructure` for general issues regarding the infra of LangChain UI 19 | - `user interface` for UI/UX related issues. 20 | 21 | ### Getting Help 22 | 23 | Contact a maintainer of LangChain UI with any questions or help you might need. 24 | 25 | ### Release process 26 | 27 | LangChain UI tries to follow the same ad hoc realease proccess as [Langchain](https://github.com/hwchase17/langchain). 28 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🐞 Bug Report 3 | about: Create a report to help us improve 4 | title: "" 5 | labels: bug 6 | assignees: "" 7 | --- 8 | 9 | **Describe the bug** 10 | A clear and concise description of what the bug is. 11 | 12 | **To Reproduce** 13 | Steps to reproduce the behavior: 14 | 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🚀 Feature request 3 | about: Suggest an idea for improving Langchain UI 4 | title: 5 | labels: enhancement 6 | assignees: 7 | --- 8 | 9 | ### Is your proposal related to a problem? 10 | 11 | 15 | 16 | (Write your answer here.) 17 | 18 | ### Describe the solution you'd like 19 | 20 | 23 | 24 | (Describe your proposed solution here.) 25 | 26 | ### Describe alternatives you've considered 27 | 28 | 31 | 32 | (Write your answer here.) 33 | 34 | ### Additional context 35 | 36 | 40 | 41 | (Write your answer here.) 42 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Summary 2 | 3 | 4 | 5 | Fixes 6 | 7 | Depends on 8 | 9 | ## Test plan 10 | 11 | 12 | 13 | - 14 | 15 | ## Screenshots 16 | 17 | 18 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | - push 4 | - pull_request 5 | jobs: 6 | test: 7 | name: Node.js ${{ matrix.node-version }} 8 | runs-on: ubuntu-latest 9 | strategy: 10 | fail-fast: false 11 | matrix: 12 | node-version: 13 | - 18 14 | - 16 15 | steps: 16 | - uses: actions/checkout@v3 17 | - uses: actions/setup-node@v3 18 | with: 19 | node-version: ${{ matrix.node-version }} 20 | - run: npm install 21 | - run: npm test -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | .pnpm-debug.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | .env 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) Ismail Pelaseyed 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # 🧬 LangChain UI 4 | 5 | The no-code open source chat-ai toolkit built on top of [LangChain](https://github.com/hwchase17/langchain). 6 | 7 |

8 | GitHub Contributors 9 | GitHub Last Commit 10 | 11 | GitHub Issues 12 | GitHub Pull Requests 13 | Github License 14 |

15 | 16 | ## About the project 17 | 18 | LangChain UI enables anyone to create and host chatbots using a no-code type of inteface. 19 | 20 | Features: 21 | 22 | 👉 Create custom chatGPT like Chatbot. 23 | 24 | 👉 Give context to the chatbot using external datasources, chatGPT plugins and prompts. 25 | 26 | 👉 Dedicated API endpoint for each Chatbot. 27 | 28 | 👉 Bring your own DB 29 | 30 | 👉 Bring your own Auth provider (defaults to Github) 31 | 32 | 👉 Usage quoutas 33 | 34 | 👉 Embed Chatbots to any site or application 35 | 36 | 👉 Chatbot themes 37 | 38 | ... and more 39 | 40 | ## Roadmap 41 | 42 | - [x] Bring your own db 43 | - [x] Bring your own Auth provider 44 | - [x] Chatbots 45 | - [x] Prompt templates 46 | - [ ] API endpoints to chatbot 47 | - [ ] External datasources 48 | - [ ] chatGPT plugins 49 | - [ ] Chatbots themes 50 | - [ ] Chatbot embedding 51 | 52 | ## Stack 53 | 54 | - [Next.js](https://nextjs.org/?ref=langchain-ui) 55 | - [Chakra UI](https://chakra-ui.com/?ref=langchain-ui) 56 | - [Prisma](https://prisma.io/?ref=langchain-ui) 57 | - [NextAuth](https://next-auth.js.org/?ref=langchain-ui) 58 | 59 | LangChain UI utilizes NextJS 13 `appDir`. Read more about it [here](https://nextjs.org/blog/next-13#new-app-directory-beta) 60 | 61 | ## Getting started 62 | 63 | 1. Clone the repo into a public GitHub repository (or fork https://github.com/homanp/langchain-ui/fork). If you plan to distribute the code, keep the source code public. 64 | 65 | ```sh 66 | git clone https://github.com/homanp/langchain-ui.git 67 | ``` 68 | 69 | 1. Go to the project folder 70 | 71 | ```sh 72 | cd langchain-ui 73 | ``` 74 | 75 | 1. Install packages with npm 76 | 77 | ```sh 78 | npm install 79 | ``` 80 | 81 | 1. Set up your .env file 82 | 83 | - Duplicate `.env.example` to `.env` 84 | 85 | 1. Run the project 86 | 87 | ```sh 88 | npm run dev 89 | ``` 90 | 91 | 1. Run the linter 92 | 93 | ```sh 94 | npm run lint 95 | ``` 96 | 97 | 1. Build the project 98 | 99 | ```sh 100 | npm run build 101 | ``` 102 | 103 | ## Contributions 104 | 105 | Our mission is to make it easy for anyone to create and run LLM apps in the cloud. We are super happy for any contributions you would like to make. Create new features, fix bugs or improve on infra. 106 | 107 | You can read more on how to contribute [here](https://github.com/homanp/langchain-ui/blob/main/.github/CONTRIBUTING.md). 108 | -------------------------------------------------------------------------------- /app/api/v1/chatbots/[chatbotId]/route.js: -------------------------------------------------------------------------------- 1 | import { NextResponse } from "next/server"; 2 | import { ChatOpenAI } from "langchain/chat_models/openai"; 3 | import { ConversationChain } from "langchain/chains"; 4 | import { 5 | ChatPromptTemplate, 6 | HumanMessagePromptTemplate, 7 | SystemMessagePromptTemplate, 8 | } from "langchain/prompts"; 9 | import { MessagesPlaceholder } from "langchain/prompts"; 10 | import { BufferMemory, ChatMessageHistory } from "langchain/memory"; 11 | import { HumanChatMessage, AIChatMessage } from "langchain/schema"; 12 | import { DEFAULT_PROMPT_TEMPLATE } from "@/lib/prompt-template"; 13 | import { prismaClient } from "@/lib/prisma"; 14 | 15 | export const runtime = "nodejs"; 16 | 17 | export const dynamic = "force-dynamic"; 18 | 19 | export async function POST(request, { params }) { 20 | const { chatbotId } = params; 21 | const { message } = await request.json(); 22 | 23 | let responseStream = new TransformStream(); 24 | const writer = responseStream.writable.getWriter(); 25 | const encoder = new TextEncoder(); 26 | 27 | const [{ promptTemplateId }, messages] = await Promise.all([ 28 | prismaClient.chatbot.findUnique({ 29 | where: { id: parseInt(chatbotId) }, 30 | }), 31 | prismaClient.chatbotMessage.findMany({ 32 | where: { chatbotId: parseInt(chatbotId) }, 33 | orderBy: { 34 | createdAt: "desc", 35 | }, 36 | take: 5, 37 | }), 38 | ]); 39 | 40 | const promptTemplate = promptTemplateId 41 | ? await prismaClient.promptTemplate.findUnique({ 42 | where: { id: promptTemplateId }, 43 | }) 44 | : undefined; 45 | 46 | const history = messages.map(({ agent, message }) => 47 | agent === "ai" ? new AIChatMessage(message) : new HumanChatMessage(message) 48 | ); 49 | 50 | const memory = new BufferMemory({ 51 | memoryKey: "history", 52 | chatHistory: new ChatMessageHistory(history), 53 | returnMessages: true, 54 | }); 55 | 56 | const llm = new ChatOpenAI({ 57 | temperature: 0, 58 | streaming: true, 59 | }); 60 | 61 | const prompt = ChatPromptTemplate.fromPromptMessages([ 62 | SystemMessagePromptTemplate.fromTemplate( 63 | promptTemplate?.prompt || DEFAULT_PROMPT_TEMPLATE 64 | ), 65 | new MessagesPlaceholder("history"), 66 | HumanMessagePromptTemplate.fromTemplate("{message}"), 67 | ]); 68 | 69 | const chain = new ConversationChain({ 70 | memory, 71 | prompt, 72 | llm, 73 | }); 74 | 75 | return NextResponse.json({ 76 | success: true, 77 | data: await chain.call({ message }), 78 | }); 79 | } 80 | -------------------------------------------------------------------------------- /app/app/chatbots/[chatbotId]/client-page.js: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import React, { useState } from "react"; 3 | import PropTypes from "prop-types"; 4 | import { 5 | Center, 6 | GridItem, 7 | SimpleGrid, 8 | Spinner, 9 | Stack, 10 | StackDivider, 11 | Text, 12 | } from "@chakra-ui/react"; 13 | import { useAsync } from "react-use"; 14 | import { getChatbotById } from "@/lib/api"; 15 | import Chat from "@/components/chat"; 16 | import CodeBlock from "@/components/code-block"; 17 | 18 | import { API_DOCS } from "@/lib/api-docs"; 19 | 20 | export default function ChatbotClientPage({ chatbotId }) { 21 | const [chatbot, setChatbot] = useState(); 22 | const { loading: isLoading } = useAsync(async () => { 23 | const { data } = await getChatbotById(chatbotId); 24 | 25 | setChatbot(data); 26 | }, [chatbotId, getChatbotById]); 27 | 28 | return ( 29 | 30 | {isLoading && ( 31 |
32 | 33 |
34 | )} 35 | {!isLoading && ( 36 | 37 | 38 | 39 | 40 | 41 | } spacing={0}> 42 | 43 | {chatbot.name} 44 | 45 | 46 | 47 | 48 | Datasources 49 | 50 | Coming soon... 51 | 52 | 53 | 54 | Plugins 55 | 56 | Coming soon... 57 | 58 | 59 | 60 | API 61 | 62 | 63 | Interact with your chatbot using the following API call 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | )} 72 |
73 | ); 74 | } 75 | 76 | ChatbotClientPage.propTypes = { 77 | chatbotId: PropTypes.string, 78 | }; 79 | -------------------------------------------------------------------------------- /app/app/chatbots/[chatbotId]/loading.js: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { Center, Spinner } from "@chakra-ui/react"; 3 | 4 | export default function Loading() { 5 | return ( 6 |
7 | 8 |
9 | ); 10 | } 11 | -------------------------------------------------------------------------------- /app/app/chatbots/[chatbotId]/page.js: -------------------------------------------------------------------------------- 1 | import ChatbotClientPage from "./client-page"; 2 | 3 | export const metadata = { 4 | title: "Chatbot", 5 | description: "Manage your chatbot", 6 | }; 7 | 8 | export default async function ChatbotsPage({ params }) { 9 | const { chatbotId } = params; 10 | 11 | return ; 12 | } 13 | -------------------------------------------------------------------------------- /app/app/chatbots/client-page.js: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import React, { useCallback, useState } from "react"; 3 | import { useRouter } from "next/navigation"; 4 | import Link from "next/link"; 5 | import { 6 | Button, 7 | Center, 8 | Container, 9 | FormControl, 10 | HStack, 11 | Icon, 12 | IconButton, 13 | Input, 14 | Select, 15 | Spinner, 16 | Stack, 17 | Table, 18 | TableContainer, 19 | Tbody, 20 | Text, 21 | Tr, 22 | Td, 23 | useColorModeValue, 24 | } from "@chakra-ui/react"; 25 | import { TbPlus, TbTrashX } from "react-icons/tb"; 26 | import { useAsync } from "react-use"; 27 | import { useForm } from "react-hook-form"; 28 | import PageHeader from "@/components/page-header"; 29 | import { useSidebar } from "@/lib/sidebar"; 30 | import { 31 | createChatbot, 32 | getChatbots, 33 | getPrompTemplates, 34 | removeChatbotById, 35 | } from "@/lib/api"; 36 | 37 | export default function ChatbotsClientPage() { 38 | const [showForm, setShowForm] = useState(); 39 | const [chatbots, setChatbots] = useState([]); 40 | const [promptTemplates, setPromptTemplates] = useState([]); 41 | const buttonColorScheme = useColorModeValue("blackAlpha", "whiteAlpha"); 42 | const buttonBackgroundColor = useColorModeValue("black", "white"); 43 | const borderBottomColor = useColorModeValue("gray.50", "#333"); 44 | const router = useRouter(); 45 | const menu = useSidebar(); 46 | 47 | const { 48 | formState: { errors, isSubmitting }, 49 | handleSubmit, 50 | register, 51 | } = useForm(); 52 | 53 | const { loading: isLoading } = useAsync(async () => { 54 | const [{ data: chatbots }, { data: promptTemplates }] = await Promise.all([ 55 | getChatbots(), 56 | getPrompTemplates(), 57 | ]); 58 | 59 | setChatbots(chatbots); 60 | setPromptTemplates(promptTemplates); 61 | 62 | return; 63 | }, [getChatbots, getPrompTemplates, setChatbots]); 64 | 65 | const handleRemoveChatbot = useCallback(async (chatbotId) => { 66 | await removeChatbotById(chatbotId); 67 | 68 | setChatbots((prev) => prev.filter(({ id }) => id !== chatbotId)); 69 | }, []); 70 | 71 | const onSubmit = useCallback( 72 | async (values) => { 73 | const { name, promptTemplateId } = values; 74 | const { data: chatbot } = await createChatbot({ 75 | name, 76 | promptTemplateId: parseInt(promptTemplateId), 77 | }); 78 | 79 | router.push(`/app/chatbots/${chatbot.id}`); 80 | }, 81 | [router] 82 | ); 83 | 84 | return ( 85 | 86 | id === "chatbots").icon} 88 | title="Chatbots" 89 | > 90 | 91 | 102 | 103 | 104 | {isLoading && ( 105 |
106 | 107 |
108 | )} 109 | {!isLoading && !showForm && ( 110 | 111 | 112 | 113 | {chatbots.map(({ id, name }) => ( 114 | 115 | 122 | 130 | 131 | ))} 132 | 133 |
120 | {name} 121 | 123 | } 126 | variant="ghost" 127 | onClick={() => handleRemoveChatbot(id)} 128 | /> 129 |
134 |
135 | )} 136 | {showForm && ( 137 |
138 | 139 | 140 | 141 | id === "chatbots").icon} 144 | /> 145 | New chatbot 146 | 147 | Create a new chatbot 148 | 149 | 150 | 151 | 152 | 157 | 158 | 159 | 170 | 171 | 172 | 173 | 176 | 185 | 186 | 187 | 188 |
189 | )} 190 |
191 | ); 192 | } 193 | -------------------------------------------------------------------------------- /app/app/chatbots/loading.js: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { Center, Spinner } from "@chakra-ui/react"; 3 | 4 | export default function Loading() { 5 | return ( 6 |
7 | 8 |
9 | ); 10 | } 11 | -------------------------------------------------------------------------------- /app/app/chatbots/page.js: -------------------------------------------------------------------------------- 1 | import ChatbotsClientPage from "./client-page"; 2 | 3 | export const metadata = { 4 | title: "Chatbots", 5 | description: "Manage your chatbots", 6 | }; 7 | 8 | export default function ChatbotsPage() { 9 | return ; 10 | } 11 | -------------------------------------------------------------------------------- /app/app/client-page.js: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { 3 | Card, 4 | CardBody, 5 | CardHeader, 6 | SimpleGrid, 7 | Stack, 8 | Text, 9 | } from "@chakra-ui/react"; 10 | import PageHeader from "@/components/page-header"; 11 | import { useSidebar } from "@/lib/sidebar"; 12 | 13 | export default function AppClientPage() { 14 | const menu = useSidebar(); 15 | 16 | return ( 17 | 18 | id === "home").icon} 20 | title="Home" 21 | /> 22 | 23 | 24 | Connected datasources 25 | 26 | - 27 | 28 | 29 | 30 | Active chatbots 31 | 32 | - 33 | 34 | 35 | 36 | Used tokens 37 | 38 | - 39 | 40 | 41 | 42 | Estimated costs 43 | 44 | - 45 | 46 | 47 | 48 | 49 | ); 50 | } 51 | -------------------------------------------------------------------------------- /app/app/container.js: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import React from "react"; 3 | import { Flex } from "@chakra-ui/react"; 4 | import Sidebar from "./sidebar"; 5 | 6 | export default function AppContainer({ children }) { 7 | return ( 8 | 9 | 10 | {children} 11 | 12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /app/app/layout.js: -------------------------------------------------------------------------------- 1 | import { getServerSession } from "next-auth/next"; 2 | import { redirect } from "next/navigation"; 3 | import { authOptions } from "pages/api/auth/[...nextauth]"; 4 | import AppContainer from "./container"; 5 | 6 | export const metadata = { 7 | title: "Home", 8 | description: "LangChain UI app home page", 9 | }; 10 | 11 | export default async function AppLayout({ children }) { 12 | const session = await getServerSession(authOptions); 13 | 14 | if (!session) { 15 | redirect("/"); 16 | } 17 | 18 | return {children}; 19 | } 20 | -------------------------------------------------------------------------------- /app/app/loading.js: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { Center, Spinner } from "@chakra-ui/react"; 3 | 4 | export default function Loading() { 5 | return ( 6 |
7 | 8 |
9 | ); 10 | } 11 | -------------------------------------------------------------------------------- /app/app/page.js: -------------------------------------------------------------------------------- 1 | import { Inter } from "next/font/google"; 2 | import AppClientPage from "./client-page"; 3 | 4 | const inter = Inter({ subsets: ["latin"] }); 5 | 6 | export default function Home() { 7 | return ; 8 | } 9 | -------------------------------------------------------------------------------- /app/app/prompt-templates/client-page.js: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import React, { useCallback, useState } from "react"; 3 | import { 4 | Box, 5 | Button, 6 | Center, 7 | Container, 8 | FormControl, 9 | FormHelperText, 10 | HStack, 11 | Icon, 12 | IconButton, 13 | Input, 14 | Spinner, 15 | Stack, 16 | Table, 17 | TableContainer, 18 | Tag, 19 | Tbody, 20 | Textarea, 21 | Text, 22 | Tr, 23 | Td, 24 | useColorModeValue, 25 | } from "@chakra-ui/react"; 26 | import { TbPlus, TbTrashX } from "react-icons/tb"; 27 | import { useAsync } from "react-use"; 28 | import { useForm } from "react-hook-form"; 29 | import PageHeader from "@/components/page-header"; 30 | import { useSidebar } from "@/lib/sidebar"; 31 | import { 32 | createPromptTemplate, 33 | getPrompTemplates, 34 | getPromptVariables, 35 | removePromptTemplateById, 36 | } from "@/lib/api"; 37 | 38 | export default function PromptTemplatesClientPage() { 39 | const buttonColorScheme = useColorModeValue("blackAlpha", "whiteAlpha"); 40 | const buttonBackgroundColor = useColorModeValue("black", "white"); 41 | const borderBottomColor = useColorModeValue("gray.50", "#333"); 42 | const menu = useSidebar(); 43 | const [promptTemplates, setPromptTemplates] = useState([]); 44 | 45 | const { loading: isLoading } = useAsync(async () => { 46 | const { data } = await getPrompTemplates(); 47 | setPromptTemplates(data); 48 | 49 | return data; 50 | }, [getPrompTemplates, setPromptTemplates]); 51 | const [showForm, setShowForm] = useState( 52 | !isLoading && promptTemplates.length === 0 53 | ); 54 | 55 | const { 56 | formState: { isSubmitting, errors }, 57 | handleSubmit, 58 | register, 59 | reset, 60 | watch, 61 | } = useForm(); 62 | 63 | const prompt = watch("prompt"); 64 | 65 | const onSubmit = useCallback( 66 | async ({ name, prompt }) => { 67 | const payload = { 68 | name, 69 | prompt, 70 | inputs: getPromptVariables(prompt), 71 | }; 72 | 73 | const { data: promptTemplate } = await createPromptTemplate(payload); 74 | 75 | setPromptTemplates((prev) => [promptTemplate, ...prev]); 76 | setShowForm(); 77 | reset(); 78 | }, 79 | [reset, setPromptTemplates] 80 | ); 81 | 82 | const handleRemovePromptTemplate = useCallback(async (promptTemplateId) => { 83 | await removePromptTemplateById(promptTemplateId); 84 | 85 | setPromptTemplates((prev) => 86 | prev.filter(({ id }) => id !== promptTemplateId) 87 | ); 88 | }, []); 89 | 90 | return ( 91 | 92 | id === "prompt_templates").icon} 94 | title="Prompt templates" 95 | > 96 | 97 | 106 | 107 | 108 | {isLoading && ( 109 |
110 | 111 |
112 | )} 113 | {!isLoading && !showForm && ( 114 | 115 | 116 | 117 | {promptTemplates.map(({ id, name }) => ( 118 | 119 | 126 | 134 | 135 | ))} 136 | 137 |
124 | {name} 125 | 127 | } 130 | variant="ghost" 131 | onClick={() => handleRemovePromptTemplate(id)} 132 | /> 133 |
138 |
139 | )} 140 | {showForm && ( 141 |
142 | 143 | 144 | 145 | id === "prompt_templates").icon} 148 | /> 149 | New prompt template 150 | 151 | Create a prompt template to use in your chat apps 152 | 153 | 154 | 155 | 156 | 161 | 162 | 163 | 169 |