├── .gitignore ├── LICENSE ├── README.md ├── app ├── api │ ├── assistant │ │ └── route.js │ ├── route.js │ └── storeID │ │ └── route.js ├── create │ └── [assistantId] │ │ └── page.js ├── embed │ └── [assistantId] │ │ └── page.js ├── favicon.ico ├── globals.css ├── layout.js └── page.js ├── components └── MainComponent.js ├── db.json ├── jsconfig.json ├── next.config.js ├── package-lock.json ├── package.json ├── postcss.config.js ├── public ├── assistant.svg ├── back.svg ├── cancel.svg ├── home.svg ├── link.svg ├── refresh.svg └── send.svg └── tailwind.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .next -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 SamurAIGPT 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 all 13 | 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 THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | Open Custom GPT 3 |
4 |

The no-code platform for building Custom GPT using Assistant api

5 |
6 | 7 | 8 | 9 | **Open Custom GPT** provides a user-friendly solution to quickly setup a custom GPT and add to your site. 10 | 11 | 12 | 13 | ### Youtube Tutorial -> https://www.youtube.com/watch?v=2S38vkMubrg 14 | 15 | 16 | ## Key Features 🎯 17 | 18 | - **Fast and Efficient**: Designed with speed and efficiency at its core. Open Custom GPT ensures rapid speed of building a GPT. 19 | - **Secure**: Your data, your control. Always. Self-hosted and never shared with others 20 | - **Open Source**: Open source and free to use. 21 | - **Share/Embed**: Share/Embed your project with your users directly and give access to your information 22 | - **Monetization**: Gate your Custom GPT behind a paywall and earn money 23 | 24 | ## Convert your existing Custom GPT to host on your site 25 | 26 | To convert your existing Custom GPT to host on your site, 27 | 28 | - Copy the instructions from the Configure Tab 29 | - Paste them in the Open Custom GPT instructions section 30 | - Enable Code Interpreter, Dall E or File retrieval similar to your Custom GPT 31 | - Upload any files you added to Custom GPT 32 | - Setup any custom functions you added in your Custom GPT in Open Custom GPT 33 | 34 | ### Stack 35 | 36 | - Next.js 37 | - OpenAI 38 | - Tailwind 39 | 40 | ### Run the project locally 41 | 42 | Minimum requirements to run the projects locally 43 | 44 | - Node.js v18 45 | - OpenAI API Key 46 | 47 | ```shell 48 | npm install 49 | 50 | npm run build 51 | 52 | npm run dev 53 | 54 | # visit http://localhost:3000 55 | ``` 56 | 57 | ### Hosted version of Open Custom GPT 58 | 59 | If you don't want to setup locally and wish to use a hosted version, you can start from https://customgpt.thesamur.ai/ 60 | 61 | Streaming support now added in hosted version 62 | 63 | ## Contribute 🤝 64 | 65 | Did you get a pull request? Open it, and we'll review it as soon as possible. 66 | 67 | - [Open Issues](https://github.com/SamurAIGPT/Open-Custom-GPT/issues) 68 | - [Open Pull Requests](https://github.com/SamurAIGPT/Open-Custom-GPT/pulls) 69 | 70 | ## License 📄 71 | 72 | This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details 73 | 74 | ## Community 75 | 76 | Join the discord community https://discord.gg/3sbpBxVZyH to get support with setting up your Custom GPT 77 | -------------------------------------------------------------------------------- /app/api/assistant/route.js: -------------------------------------------------------------------------------- 1 | import fsPromises from 'fs/promises'; 2 | import path from 'path'; 3 | import { NextResponse } from 'next/server' 4 | 5 | const dataFilePath = path.join(process.cwd(), 'db.json'); 6 | 7 | export async function POST(request) { 8 | const req = await request.json() 9 | const jsonData = await fsPromises.readFile(dataFilePath); 10 | const objectData = JSON.parse(jsonData); 11 | objectData.assistants[req.id] = req 12 | await fsPromises.writeFile(dataFilePath, JSON.stringify(objectData)); 13 | return NextResponse.json({"msg":"assistant added"}) 14 | 15 | } 16 | 17 | -------------------------------------------------------------------------------- /app/api/route.js: -------------------------------------------------------------------------------- 1 | import fsPromises from 'fs/promises'; 2 | import path from 'path'; 3 | import { NextResponse } from 'next/server' 4 | 5 | const dataFilePath = path.join(process.cwd(), 'db.json'); 6 | 7 | export async function GET(request) { 8 | var url = new URL(request.url) 9 | const assistantId = url.searchParams.get("assistantId") 10 | const jsonData = await fsPromises.readFile(dataFilePath); 11 | const objectData = JSON.parse(jsonData); 12 | let resData 13 | if(assistantId!=null){ 14 | let getAssistant 15 | if(assistantId=="new"){ 16 | getAssistant = null 17 | }else{ 18 | getAssistant = objectData.assistants[assistantId] 19 | } 20 | resData = {openAIKey:objectData.openAIKey,assistant:getAssistant} 21 | }else{ 22 | resData = objectData 23 | } 24 | return NextResponse.json(resData) 25 | 26 | } 27 | 28 | -------------------------------------------------------------------------------- /app/api/storeID/route.js: -------------------------------------------------------------------------------- 1 | import fsPromises from 'fs/promises'; 2 | import path from 'path'; 3 | import { NextResponse } from 'next/server' 4 | 5 | const dataFilePath = path.join(process.cwd(), 'db.json'); 6 | 7 | export async function POST(request) { 8 | const req = await request.json() 9 | const jsonData = await fsPromises.readFile(dataFilePath); 10 | const objectData = JSON.parse(jsonData); 11 | objectData.openAIKey = req.id 12 | await fsPromises.writeFile(dataFilePath, JSON.stringify(objectData)); 13 | return NextResponse.json(objectData) 14 | 15 | } 16 | 17 | -------------------------------------------------------------------------------- /app/create/[assistantId]/page.js: -------------------------------------------------------------------------------- 1 | 'use client' 2 | import Image from 'next/image' 3 | import { useState,useEffect } from "react" 4 | import OpenAI from 'openai'; 5 | import Link from "next/link"; 6 | import { useContext } from "react" 7 | import { KeyContext } from "../../../components/MainComponent" 8 | import {useRouter} from "next/navigation"; 9 | export default function Create({params:{assistantId}}) { 10 | const router = useRouter() 11 | const getKey = useContext(KeyContext); 12 | const [name,setName] = useState("") 13 | const [instructions,setInstructions] = useState("") 14 | const [types,setTypes] = useState([]) 15 | const [functions,setFunctions] = useState([]) 16 | const [update,setUpdate] = useState(false) 17 | const [files,setFiles] = useState([]) 18 | const [openai,setOpenai] = useState(null) 19 | const [assistant,setAssistant] = useState(null) 20 | const [showShare,setShowShare] = useState(false) 21 | 22 | 23 | const createAssistant = async() => { 24 | if(getKey.key!=""){ 25 | if(name!=""&&instructions!=""){ 26 | let fileIds = [] 27 | let fileDetails = [] 28 | if(files.length>0){ 29 | for await (const file of files) { 30 | if(file.id!=null&&file.id!=undefined){ 31 | fileIds.push(file.id) 32 | fileDetails.push(file) 33 | }else{ 34 | let saveFile = await openai.files.create({ 35 | file: file, 36 | purpose: "assistants", 37 | }) 38 | fileIds.push(saveFile.id) 39 | fileDetails.push({id:saveFile.id,name:file.name}) 40 | } 41 | } 42 | } 43 | let tools = [] 44 | types.forEach((tool)=> 45 | tools.push({"type":tool}) 46 | ) 47 | functions.forEach((fn)=> 48 | tools.push({"type":"function","function":JSON.parse(fn)}) 49 | ) 50 | let model 51 | if(types.includes('retrieval')){ 52 | model = "gpt-3.5-turbo-1106" 53 | }else{ 54 | model = "gpt-3.5-turbo" 55 | } 56 | let getAssistant 57 | if(assistant==null){ 58 | getAssistant = await openai.beta.assistants.create({ 59 | name: name, 60 | instructions: instructions, 61 | model:model, 62 | tools: tools, 63 | file_ids: fileIds 64 | }) 65 | }else{ 66 | console.log("updating") 67 | getAssistant = await openai.beta.assistants.update(assistant,{ 68 | name: name, 69 | instructions: instructions, 70 | model:model, 71 | tools: tools, 72 | file_ids: fileIds 73 | }) 74 | } 75 | setAssistant(getAssistant.id) 76 | setFiles(fileDetails) 77 | setShowShare(true) 78 | const response = await fetch('/api/assistant',{ 79 | method: 'POST', 80 | headers: { 81 | 'Content-Type': 'application/json' 82 | }, 83 | body: JSON.stringify({ id: getAssistant.id,name:name,instructions:instructions,model:model,tools:tools,files:fileDetails }) 84 | }) 85 | if(assistantId=="new"){ 86 | router.push('/create/'+getAssistant.id) 87 | } 88 | 89 | 90 | }else{ 91 | alert("Add you assistant's name and instructions!") 92 | } 93 | }else{ 94 | alert('Add your OpenAI Key!') 95 | } 96 | } 97 | 98 | const addType = (type) => { 99 | if(types.includes(type)){ 100 | var filteredArray = types.filter(e => e !== type) 101 | setTypes(filteredArray) 102 | }else{ 103 | setTypes([...types,type]) 104 | } 105 | } 106 | const addFunction = (index,input) => { 107 | functions[index] = input 108 | setUpdate((prev)=>!prev) 109 | } 110 | const removeFunction = (index) => { 111 | if(index==0){ 112 | setFunctions([]) 113 | }else{ 114 | let newFns = functions.splice(index,1) 115 | setFunctions(newFns) 116 | } 117 | } 118 | const removeFile = async(file) => { 119 | var filteredArray = files.filter(e => e.name !== file.name) 120 | setFiles(filteredArray) 121 | if(assistant!=null){ 122 | const deletedAssistantFile = await openai.beta.assistants.files.del( 123 | assistant, 124 | file.id 125 | ); 126 | } 127 | } 128 | const shareEmbed = (type) => { 129 | if(type==0){ 130 | navigator.clipboard.writeText('