diff --git a/client/package.json b/client/package.json new file mode 100644 index 0000000..9188d28 --- /dev/null +++ b/client/package.json @@ -0,0 +1,48 @@ +{ + "name": "autogpt", + "version": "0.1.0", + "private": true, + "proxy": "http://localhost:5000", + "dependencies": { + "@reduxjs/toolkit": "^1.9.0", + "@testing-library/jest-dom": "^5.16.5", + "@testing-library/react": "^13.4.0", + "@testing-library/user-event": "^13.5.0", + "axios": "^1.2.0", + "bootstrap": "^5.2.3", + "react": "^18.2.0", + "react-bootstrap": "^2.6.0", + "react-dom": "^18.2.0", + "react-markdown": "^8.0.5", + "react-redux": "^8.0.5", + "react-router-dom": "^6.4.3", + "react-scripts": "5.0.1", + "react-toastify": "^9.1.1", + "remark-gfm": "^3.0.1", + "web-vitals": "^2.1.4" + }, + "scripts": { + "start": "react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test", + "eject": "react-scripts eject" + }, + "eslintConfig": { + "extends": [ + "react-app", + "react-app/jest" + ] + }, + "browserslist": { + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + } +} diff --git a/client/public/favicon.ico b/client/public/favicon.ico new file mode 100644 index 0000000..91a68aa Binary files /dev/null and b/client/public/favicon.ico differ diff --git a/client/public/index.html b/client/public/index.html new file mode 100644 index 0000000..821c14f --- /dev/null +++ b/client/public/index.html @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + AutoGPT + + + +
+ + + diff --git a/client/public/logo192.png b/client/public/logo192.png new file mode 100644 index 0000000..1d48f8b Binary files /dev/null and b/client/public/logo192.png differ diff --git a/client/public/logo512.png b/client/public/logo512.png new file mode 100644 index 0000000..8b8f295 Binary files /dev/null and b/client/public/logo512.png differ diff --git a/client/public/manifest.json b/client/public/manifest.json new file mode 100644 index 0000000..080d6c7 --- /dev/null +++ b/client/public/manifest.json @@ -0,0 +1,25 @@ +{ + "short_name": "React App", + "name": "Create React App Sample", + "icons": [ + { + "src": "favicon.ico", + "sizes": "64x64 32x32 24x24 16x16", + "type": "image/x-icon" + }, + { + "src": "logo192.png", + "type": "image/png", + "sizes": "192x192" + }, + { + "src": "logo512.png", + "type": "image/png", + "sizes": "512x512" + } + ], + "start_url": ".", + "display": "standalone", + "theme_color": "#000000", + "background_color": "#ffffff" +} diff --git a/client/public/robots.txt b/client/public/robots.txt new file mode 100644 index 0000000..e9e57dc --- /dev/null +++ b/client/public/robots.txt @@ -0,0 +1,3 @@ +# https://www.robotstxt.org/robotstxt.html +User-agent: * +Disallow: diff --git a/client/src/App.js b/client/src/App.js new file mode 100644 index 0000000..d261476 --- /dev/null +++ b/client/src/App.js @@ -0,0 +1,20 @@ +import './styles/home.css'; +import { BrowserRouter, Route, Routes,Navigate } from 'react-router-dom'; +import AgentConvo from './pages/AgentConvo'; +import AgentConvoShare from './pages/AgentConvoShare'; + + +function App() { + return ( +
+ + + }/> + }/> + + +
+ ); +} + +export default App; diff --git a/client/src/App.test.js b/client/src/App.test.js new file mode 100644 index 0000000..1f03afe --- /dev/null +++ b/client/src/App.test.js @@ -0,0 +1,8 @@ +import { render, screen } from '@testing-library/react'; +import App from './App'; + +test('renders learn react link', () => { + render(); + const linkElement = screen.getByText(/learn react/i); + expect(linkElement).toBeInTheDocument(); +}); diff --git a/client/src/assets/camelagi.png b/client/src/assets/camelagi.png new file mode 100644 index 0000000..a9b0c5c Binary files /dev/null and b/client/src/assets/camelagi.png differ diff --git a/client/src/assets/discord.svg b/client/src/assets/discord.svg new file mode 100644 index 0000000..22ee27b --- /dev/null +++ b/client/src/assets/discord.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/client/src/assets/google.svg b/client/src/assets/google.svg new file mode 100644 index 0000000..47889ad --- /dev/null +++ b/client/src/assets/google.svg @@ -0,0 +1,10 @@ + + + + + + + ionicons-v5_logos + + + \ No newline at end of file diff --git a/client/src/assets/key.svg b/client/src/assets/key.svg new file mode 100644 index 0000000..950c1c8 --- /dev/null +++ b/client/src/assets/key.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/client/src/assets/logout-white.svg b/client/src/assets/logout-white.svg new file mode 100644 index 0000000..d52ec8f --- /dev/null +++ b/client/src/assets/logout-white.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/client/src/assets/question-mark.svg b/client/src/assets/question-mark.svg new file mode 100644 index 0000000..30cf4d6 --- /dev/null +++ b/client/src/assets/question-mark.svg @@ -0,0 +1,14 @@ + + + + + + + + \ No newline at end of file diff --git a/client/src/assets/reload.svg b/client/src/assets/reload.svg new file mode 100644 index 0000000..bcd86b9 --- /dev/null +++ b/client/src/assets/reload.svg @@ -0,0 +1,17 @@ + + + + + + + + + + \ No newline at end of file diff --git a/client/src/assets/send.svg b/client/src/assets/send.svg new file mode 100644 index 0000000..fabeea7 --- /dev/null +++ b/client/src/assets/send.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/client/src/assets/share-white.svg b/client/src/assets/share-white.svg new file mode 100644 index 0000000..2e99fc6 --- /dev/null +++ b/client/src/assets/share-white.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/client/src/assets/user-1.svg b/client/src/assets/user-1.svg new file mode 100644 index 0000000..3485a31 --- /dev/null +++ b/client/src/assets/user-1.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/client/src/assets/user-2.svg b/client/src/assets/user-2.svg new file mode 100644 index 0000000..231c05e --- /dev/null +++ b/client/src/assets/user-2.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/client/src/assets/user-3.svg b/client/src/assets/user-3.svg new file mode 100644 index 0000000..5d40e62 --- /dev/null +++ b/client/src/assets/user-3.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/client/src/assets/user-4.svg b/client/src/assets/user-4.svg new file mode 100644 index 0000000..03e5246 --- /dev/null +++ b/client/src/assets/user-4.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/client/src/assets/user.png b/client/src/assets/user.png new file mode 100644 index 0000000..3046f3a Binary files /dev/null and b/client/src/assets/user.png differ diff --git a/client/src/index.css b/client/src/index.css new file mode 100644 index 0000000..ec2585e --- /dev/null +++ b/client/src/index.css @@ -0,0 +1,13 @@ +body { + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', + 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', + sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +code { + font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', + monospace; +} diff --git a/client/src/index.js b/client/src/index.js new file mode 100644 index 0000000..a74730c --- /dev/null +++ b/client/src/index.js @@ -0,0 +1,18 @@ +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import './index.css'; +import App from './App'; +import reportWebVitals from './reportWebVitals'; +import "bootstrap/dist/css/bootstrap.min.css"; + +const root = ReactDOM.createRoot(document.getElementById('root')); +root.render( + + + +); + +// If you want to start measuring performance in your app, pass a function +// to log results (for example: reportWebVitals(console.log)) +// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals +reportWebVitals(); diff --git a/client/src/logo.svg b/client/src/logo.svg new file mode 100644 index 0000000..9dfc1c0 --- /dev/null +++ b/client/src/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/client/src/pages/AgentConvo.js b/client/src/pages/AgentConvo.js new file mode 100644 index 0000000..3ab5795 --- /dev/null +++ b/client/src/pages/AgentConvo.js @@ -0,0 +1,262 @@ +import {useState,useEffect,useRef} from 'react'; +import '../styles/agent-convo.css' +import { Button, Stack, Image, Form, Row, Col, Spinner, InputGroup,FormControl, Modal } from "react-bootstrap"; +import { ReactComponent as ReloadIcon } from "../assets/reload.svg"; +import { ReactComponent as KeyIcon } from "../assets/key.svg"; +import { ReactComponent as LogoutIcon } from "../assets/logout-white.svg"; +import { ReactComponent as Bot1Icon } from "../assets/user-1.svg"; +import { ReactComponent as Bot2Icon } from "../assets/user-2.svg"; +import { ReactComponent as Bot3Icon } from "../assets/user-3.svg"; +import { ReactComponent as Bot4Icon } from "../assets/user-4.svg"; +import logoImage from '../assets/camelagi.png' +import {ReactComponent as SendIcon} from '../assets/send.svg' +import { ToastContainer, toast } from 'react-toastify'; +import {ReactComponent as GoogleIcon} from '../assets/google.svg' +import { ReactComponent as DiscordIcon } from "../assets/discord.svg"; +import axios from "axios" +import ReactMarkdown from 'react-markdown' +import remarkGfm from 'remark-gfm' +import { ReactComponent as ShareIcon } from "../assets/share-white.svg"; + + +function AgentConvo() { + const [isLoggedIn,setIsLoggedIn] = useState(false); + const [authUrl,setAuthUrl] = useState("") + const [ranUser1, setRanUser1] = useState(null); + const [ranUser2, setRanUser2] = useState(null); + const [started, setStarted] = useState(false); + const [key, setKey] = useState(""); + const [task, setTask] = useState(""); + const [finished, setFinished] = useState(false); + const [keyAdded, setKeyAdded] = useState(false); + const [user, setUser] = useState(false); + const [role1, setRole1] = useState(""); + const [role2, setRole2] = useState(""); + const [chat,setChat] = useState([]) + const [sessId,setSessId] = useState(0) + const [showBanner, setShowBanner] = useState(false); + const [turn,setTurn] = useState(0) + const sessionRef = useRef(null) + sessionRef.current = sessId + const chatRef = useRef(null) + chatRef.current = chat + const turnRef = useRef(null) + turnRef.current = turn + + let fetchMessages = () => { + setChat([...chatRef.current,{role:0,msg:null}]) + + axios.post("/rp/start",{role1:role1,role2:role2,task:task,sessId:sessionRef.current}).then((res)=>{ + setSessId(res.data.sessId) + if(res.data.convoEnd==true){ + setFinished((prev)=>true) + }else{ + chatRef.current.at(-1).msg = res.data.userMsg + setChat([...chatRef.current,{role:1,msg:null}]) + setTimeout(() => { + chatRef.current.at(-1).msg = res.data.assistantMsg; + setTurn((prev)=>prev+1) + startDiscussion(false) + }, 3000); + // setUpdate((prev)=>!prev) + + } + + }) + .catch((err)=>{ + toast("Failed to respond " + err.response.data); + }) + } + + let startDiscussion = (newTurn) => { + let getTurn = turnRef.current + if(newTurn==true){ + getTurn = 0 + setTurn((prev)=>0) + setStarted((prev)=>true) + } + + if(getTurn<2){ + fetchMessages() + } + } + + const addKey = () => { + axios.post("/store_key",{key:key}).then((res)=>{ + setKeyAdded(true) + }).catch((err)=>{ + toast("Key cannot be verified, try again"); + }) + } + + const logout = () => { + axios.get("/heybot/logout").then((res)=>{ + window.location.reload(); + }).catch((err)=>{ + console.log(err) + }) + } + + const shareChat = () => { + navigator.clipboard.writeText(window.location.host+'/conversation/share?session='+sessionRef.current) + window.open('/conversation/share?session='+sessionRef.current, "_blank"); + } + + useEffect(() => { + setRanUser1(Math.random()) + setRanUser2(Math.random()) + axios.get("/rp/isLoggedIn").then((res)=>{ + setIsLoggedIn(res.data.isLoggedIn) + if(res.data.isLoggedIn == false){ + setAuthUrl(res.data.auth_url) + }else{ + setUser({id:res.data.userId,image:res.data.image}) + if(res.data.key_added==null){ + setKeyAdded((prev)=>false) + }else{ + setKeyAdded((prev)=>true) + setKey(res.data.key_added) + } + + } + }).catch((err)=>{ + console.log(err) + }) + + window.setTimeout(()=>setShowBanner(true),30000) + },[]) + useEffect(() => { + if(document.querySelector('.end-chat')!=null){document.querySelector('.end-chat').scrollIntoView({ behavior: 'smooth', block: 'end' });} + }, [chat]) + + return ( + <> +
+
+
+

CamelAGI

+ {isLoggedIn&&
+ window.location.reload()} className="icon"/> + {setKeyAdded(false)} className="icon"/>} + +
} +
+ {isLoggedIn? + + {keyAdded? + <> + {started? + <> + + {chatRef.current.length>0&&chatRef.current.map((message)=> + message.role==0? +
+ {ranUser1!=null&&ranUser1>0.5?:} +
+
+ {message.msg==null? + + + + + : +

} +
+ {role1} +
+
: +
+
+
+ {message.msg==null? + + + + + : +

} +
+ {role2} +
+ {ranUser2!=null&&ranUser2>0.5?:} +
+ )} + {sessionRef.current!=0&&} +
+
+ {finished? +
Task Completed!
+ : + <> + {turnRef.current<3? + + :} + } + + : + <> +
Get started with a task to discuss
+ + Enter Role of the Instructor + setRole1(e.target.value)}/> + + + Enter Role of the Assistant + setRole2(e.target.value)}/> + + + Enter topic of discussion + setTask(e.target.value)}/> + + + + + } + : + +
Add your OpenAI Key
+

Get your OpenAI Key by signing up/ logging in from the OpenAI Dashboard. Go to Dashboard

+ + setKey(e.target.value)} + onKeyDown={(e)=>{e.code=="Enter"&&addKey()}} + /> + + + Watch this video to get started + +
+ } +
: + + + {/*
Login to agi
*/} +
Accomplish your task with AI agents
+ + + {/* navigate('/agi/faq')}> Know More */} +
+ } +
+ + setShowBanner(false)}> + + setShowBanner(false)} className="position-absolute top-0 end-0 me-4 mt-2">X + + Camel AGI - Communicative Agents on GPT | Product Hunt + Join Our Discord + Star CamelAGI on Github + + + +
+ + ) + +} + +export default AgentConvo; \ No newline at end of file diff --git a/client/src/pages/AgentConvoShare.js b/client/src/pages/AgentConvoShare.js new file mode 100644 index 0000000..8b48db9 --- /dev/null +++ b/client/src/pages/AgentConvoShare.js @@ -0,0 +1,125 @@ +import {useState,useEffect,useRef} from 'react'; +import '../styles/agent-convo.css' +import { Button, Stack, Image, Form,Modal, Spinner, } from "react-bootstrap"; +import { ReactComponent as Bot1Icon } from "../assets/user-1.svg"; +import { ReactComponent as Bot2Icon } from "../assets/user-2.svg"; +import { ReactComponent as Bot3Icon } from "../assets/user-3.svg"; +import { ReactComponent as Bot4Icon } from "../assets/user-4.svg"; +import logoImage from '../assets/camelagi.png' +import { useNavigate } from "react-router-dom"; +import { ToastContainer, toast } from 'react-toastify'; +import axios from "axios" +import ReactMarkdown from 'react-markdown' +import remarkGfm from 'remark-gfm' +import { ReactComponent as ShareIcon } from "../assets/share-white.svg"; +import { useSearchParams, useParams } from "react-router-dom"; + + +function AgentConvoShare() { + + const [ranUser1, setRanUser1] = useState(null); + const [ranUser2, setRanUser2] = useState(null); + const navigate = useNavigate() + const [showBanner, setShowBanner] = useState(false); + const [user, setUser] = useState(false); + const [role1, setRole1] = useState(""); + const [role2, setRole2] = useState(""); + const [task, setTask] = useState(""); + const [chat,setChat] = useState([]) + const [sessId,setSessId] = useState(0) + const sessionRef = useRef(null) + sessionRef.current = sessId + const chatRef = useRef(null) + chatRef.current = chat + let [searchParams, setSearchParams] = useSearchParams(); + + + useEffect(() => { + setRanUser1(Math.random()) + setRanUser2(Math.random()) + axios.get("/rp/get_chat?sessId="+searchParams.get("session")).then((res)=>{ + setChat(res.data.messages) + setRole1(res.data.role1) + setRole2(res.data.role2) + setTask(res.data.task) + + + }).catch((err)=>{ + console.log(err) + }) + window.setTimeout(()=>setShowBanner(true),30000) + },[]) + // useEffect(() => { + // if(document.querySelector('.end-chat')!=null){document.querySelector('.end-chat').scrollIntoView({ behavior: 'smooth', block: 'end' });} + // }, [chat]) + + return ( + <> +
+
+
+

CamelAGI

+ + +
+ + + + + Check out this conversation between {role1} and {role2} to discuss:
{task}
+ {chatRef.current.length>0&&chatRef.current.map((message)=> + message.role==0? +
+ {ranUser1!=null&&ranUser1>0.5?:} +
+
+ {message.msg==null? + + + + + : +

} +
+ {role1} +
+
: +
+
+
+ {message.msg==null? + + + + + : +

} +
+ {role2} +
+ {ranUser2!=null&&ranUser2>0.5?:} +
+ )} +
+
+ +
+ + +
+ + setShowBanner(false)}> + + setShowBanner(false)} className="position-absolute top-0 end-0 me-4 mt-2">X + + Camel AGI - Communicative Agents on GPT | Product Hunt + + + +
+ + ) + +} + +export default AgentConvoShare; \ No newline at end of file diff --git a/client/src/reportWebVitals.js b/client/src/reportWebVitals.js new file mode 100644 index 0000000..5253d3a --- /dev/null +++ b/client/src/reportWebVitals.js @@ -0,0 +1,13 @@ +const reportWebVitals = onPerfEntry => { + if (onPerfEntry && onPerfEntry instanceof Function) { + import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { + getCLS(onPerfEntry); + getFID(onPerfEntry); + getFCP(onPerfEntry); + getLCP(onPerfEntry); + getTTFB(onPerfEntry); + }); + } +}; + +export default reportWebVitals; diff --git a/client/src/setupTests.js b/client/src/setupTests.js new file mode 100644 index 0000000..8f2609b --- /dev/null +++ b/client/src/setupTests.js @@ -0,0 +1,5 @@ +// jest-dom adds custom jest matchers for asserting on DOM nodes. +// allows you to do things like: +// expect(element).toHaveTextContent(/react/i) +// learn more: https://github.com/testing-library/jest-dom +import '@testing-library/jest-dom'; diff --git a/client/src/styles/agent-convo.css b/client/src/styles/agent-convo.css new file mode 100644 index 0000000..fa3f701 --- /dev/null +++ b/client/src/styles/agent-convo.css @@ -0,0 +1,149 @@ +.agent-convo-container{ + background: #eee; +} +.agent-convo-box{ + border-radius: 12px; + background: #2b2d2f; + box-shadow: 10px 10px 40px #2b2d2f; + height: 80%; + width: 60%; +} +.agent-msg-data{ + color: #fff !important; + background-color: transparent !important; + font-family: var(--fontFamily) !important; + white-space: break-spaces; + font-size: 1rem !important; + margin: 0 !important; + overflow: hidden; + padding: 0 !important; +} + +.bubble { + --r: 12px; + --t: 10px; + + padding: calc(2*var(--r)/3); + -webkit-mask: + radial-gradient(var(--t) at var(--_d) 0,#0000 98%,#000 102%) + var(--_d) 100%/calc(100% - var(--r)) var(--t) no-repeat, + conic-gradient(at var(--r) var(--r),#000 75%,#0000 0) + calc(var(--r)/-2) calc(var(--r)/-2) padding-box, + radial-gradient(50% 50%,#000 98%,#0000 101%) + 0 0/var(--r) var(--r) space padding-box; + + color: #fff; +} +.agent-1-chat { + --_d: 0%; + border-left: var(--t) solid #0000; + margin-right: var(--t); + box-shadow: 10px 10px 40px #2b2d2f; + background: var(--themeColor) border-box; +} +.agent-2-chat { + --_d: 100%; + border-right: var(--t) solid #0000; + margin-left: var(--t); + box-shadow: 10px 10px 40px #2b2d2f; + background: #3f65f9 border-box; +} +.agent-chat-icon{ + min-width: 2.5rem; + min-height: 2.5rem; + max-width: 2.5rem; + max-height: 2.5rem; + +} +.continue-button{ + background-color: #eee !important; + color: #000 !important; + border: none !important; + border-radius: 12px !important; + /* font-size: 12px !important; */ + font-weight: 800 !important; +} +.agent-input{ + background-color: transparent !important; + color: #fff !important; + border-top: none !important; + border-left: none !important; + border-right: none !important; +} +.agent-scroll{ + overflow-y: scroll; + scrollbar-width: thin; +} +.agent-scroll::-webkit-scrollbar { + width: 0; +} +.scroll-container{ + height: 90% !important; +} +.agent-msg-container>pre{ + max-width: 30rem; +} +.key-video-agent{ + width: 60%; + min-height: 14rem; + border-radius: 12px; +} +.role-name{ + opacity: 60%; + font-size: 12px; +} +.role1-container{ + animation: role1msg 0.3s ease-out 0s forwards; + +} +.role2-container{ + animation: role2msg 0.3s ease-out 0s forwards; + +} +@keyframes role1msg { + 0% { + margin-left: -2rem; + opacity: 0; + } + 80% { + transform: scale(1.1); + } + 100% { + transform: scale(1); + opacity: 1; + } + } +.agent-share-btn{ + opacity: 0.8; + background: transparent !important; + border: 1px solid white !important; + padding: 0.2rem 0.4rem !important; + font-size: 12px !important; +} +@keyframes role1msg { + 0% { + margin-right: -2rem; + opacity: 0; + } + 80% { + transform: scale(1.1); + } + 100% { + transform: scale(1); + opacity: 1; + } + } +@media screen and (max-width:1000px){ + .agent-convo-box{ + height: 100%; + width: 100%; + border-radius: 0; + } + .agent-msg-container>pre{ + max-width: 20rem; + } +} + +.logo{ + width: 4rem; +} \ No newline at end of file diff --git a/client/src/styles/home.css b/client/src/styles/home.css new file mode 100644 index 0000000..d5bf605 --- /dev/null +++ b/client/src/styles/home.css @@ -0,0 +1,291 @@ +@import url("https://fonts.googleapis.com/css2?family=Nunito:wght@400;500;600;700;800&display=swap"); +:root { + + --hoverColor: rgb(71, 24, 182) !important ; + --fontFamily: "Nunito", sans-serif; + --border: 1px solid #dfdfdf; + --themeColor: #763FF9; + +} + +.powered-by{ + background-color: #F6F3FF; + border-radius: 22px 22px 0 0; + opacity: 0.8; + z-index: 99; + position: fixed; + bottom: 0; + right: 1rem; +} +.banner-modal>.modal-dialog>.modal-content{ + border-radius: 22px !important; +} +.producthunt-banner{ + z-index: 99; + position: fixed; + top: 1.5rem; + right: 1rem; +} + +.powered-by a{ + color: black; + font-size: 0.8rem; +} +.discord-invite{ + background-color: #5865f2; + color: white; + border-radius: 12px; + text-decoration: none; +} + +.powered-by a:hover{ + color: black; + text-decoration: underline !important; +} + +.btn-filled{ + background: #763FF9 !important; +box-shadow: 0px 2px 25px #F0EDF9 !important; +border-radius: 12px !important; +border: 1px solid #763FF9 !important; + color: white !important; +} + +.btn-filled:hover{ + background: var(--hoverColor) !important; + +} +.side-nav{ + width: 13.5rem ; + min-height: 100vh; + background: #FFFFFF; + border-right: 1px solid #DFDFDF; + box-shadow: 0px 0px 15px rgba(0, 0, 0, 0.04); + font-size: 0.75rem; + font-weight: 700; +} +/* .mobile-nav{ + font-size: 0.75rem !important; +} */ +body,html{ + background-color: #ffffff !important; + font-family: var(--fontFamily) !important; + color:#000000 !important; +} +.nav-body, +.nav-header > .accordion-button { + padding:0.5rem 0rem !important; + border:0 !important; + background-color: transparent !important; + color: #000 !important; + font-size: 0.75rem; + font-weight: 700; +} +.os-logo{ + width: 3.75rem; + height: 3.75rem; + margin: 2.5rem 0; +} +.os-logo-mobile{ + width: 2.5rem; + height: 2.5rem; +} +.offcanvas-backdrop{ + width: 100% !important; + height: 100% !important; +} +.nav-icon{ + height: 1rem; + width: 1rem; + margin-right: 1.5rem; +} +.side-accordion{ + --bs-accordion-bg: transparent !important; +} +.nav-width{ + width: 10.5rem; +} +.nav-width>.nav-item{ + width: 100%; +} + +.nav-header > .accordion-button::after { + margin-left: auto; + +} +.accordion-button.collapsed::after{ + background-size: 1rem; + color: #000 !important; + margin-left: auto; +} +.accordion-button:not(.collapsed) { + box-shadow: none !important; + color: #000 !important; +} +.nav-header > .accordion-button:not(.collapsed)::after { + transform: rotate(-180deg) !important; + background-size: 1rem; + color: #000 !important; +} +.nav-header > .accordion-button:focus { + border: none !important; + box-shadow: none !important; +} + +.side-nav-links >.nav-link.active{ + color: #763FF9 !important; + border-radius: 1.875rem; +} + +.side-nav-links >.nav-link{ + color: #5F5B66 !important; + padding: 0.375rem 2.5rem; +} + +.side-nav-links > .active{ + background-color: #F6F3FF !important; + color: #763FF9 !important; +} + + +.rounded-cards{ + box-sizing: border-box; + background: #FFFFFF !important; + border: 1px solid #DFDFDF; + box-shadow: 0px 2px 25px #F0EDF9; + border-radius: 22px; +} + +.top-filters-cards{ + height: 2.81rem; + background: #FFFFFF !important; + border: 1px solid #DFDFDF !important; + box-shadow: 0px 2px 25px #F0EDF9 !important; + border-radius: 0.75rem; + color: #000 !important; + font-size: 0.75rem !important; + padding: 0.5rem 0.75rem !important; +} + +.dropdown>.dropdown-toggle::after{ + margin-left:20px !important; +} + +.small-text{ + font-size: 0.75rem !important ; +} +.content-container{ + max-width: 50rem; +} +.number-text{ + font-size: 1.5rem !important; + font-weight: 600 !important; +} + +.navbar-toggler:focus{ + box-shadow: none; +} +.graph-title{ + font-weight: 400; +} +.stats-table{ + background: #FFFFFF !important; + border: 1px solid #DFDFDF !important; +} +.stats-table>thead{ + background-color: #F2F2F2; + font-size: 0.75rem; +} +.stats-table>thead>tr>th,.stats-table>tbody>tr>td{ + padding: 1.5rem; + +} +/* .stats-table tr:first-child th:first-child { + border-top-left-radius: 22px; +} + +.stats-table tr:first-child th:last-child { + border-top-right-radius: 22px; +} +.stats-table tr:last-child td:first-child { + border-bottom-left-radius: 22px; +} + +.stats-table tr:last-child td:last-child { + border-bottom-right-radius: 22px; +} */ +@media screen and (max-width:992px){ + .main-col{ + margin-top: 7rem !important; + } + .nav-header > .accordion-button { + font-size: 1rem; + } + .graph-title{ + font-size: 0.9rem; + } + .stats-table>thead>tr>th,.stats-table>tbody>tr>td{ + padding: 1rem; + + } + .powered-by{ + background-color: #21cd9c; + border-radius: 0 0 12px 12px ; + top: 0; + right: 0; + bottom: auto; + } +} + +@media screen and (min-width:1600px){ + .content-container{ + max-width: 75rem; + } +} +@media screen and (max-width:500px){ + .stats-table>thead>tr>th,.stats-table>tbody>tr>td{ + padding: 0.5rem; + + } +} + +.options-bg{ +box-sizing: border-box; +background: #FFFFFF !important; +border: 1px solid #DFDFDF !important; +box-shadow: 0px 2px 25px #F0EDF9 !important; +border-radius: 50px !important; +color: #000000 !important; +font-size: 12px !important; +} + +.filter-drop > button{ + background-color: white !important; + color: #000 !important; + border: 0px !important; + font-size: 12px !important; + padding-left: 3px !important; + padding-right: 3px !important; + padding-top: 6px !important; + padding-bottom: 6px !important; +} +.filter-drop >.dropdown-toggle::after{ + content:none !important; +} + +.filter-drop > .dropdown-menu{ + font-size: 12px !important; + min-width: 5rem !important; + border: 1px solid #ececec!important; + box-shadow: 0px 2px 25px #F0EDF9 !important; +} + +.vr{ + background-color: #acacac !important; +} + +.icon{ + width: 1.3rem ; + height: 1.3rem ; + cursor: pointer ; +} diff --git a/server/agent_convo.py b/server/agent_convo.py new file mode 100644 index 0000000..dfb21af --- /dev/null +++ b/server/agent_convo.py @@ -0,0 +1,286 @@ +from database import * +import urllib.parse +from flask import jsonify,request,session,render_template,redirect,url_for,Blueprint +from requests_oauthlib import OAuth2Session +from flask_login import login_required, login_user, current_user, logout_user +import secrets +from datetime import datetime, date, timedelta, timezone +import os, pickle, codecs +from typing import List +from langchain.chat_models import ChatOpenAI +from langchain.prompts.chat import ( + SystemMessagePromptTemplate, + HumanMessagePromptTemplate, +) +from langchain.schema import ( + AIMessage, + HumanMessage, + SystemMessage, + BaseMessage, +) + +authorization_base_url = "https://accounts.google.com/o/oauth2/v2/auth" +scope = [ + "https://www.googleapis.com/auth/userinfo.email", + "https://www.googleapis.com/auth/userinfo.profile", + "openid" +] +google_client_id = os.environ['google_client_id'] +google_client_secret = os.environ['google_client_secret'] +word_limit = 50 # word limit for task brainstorming + +rp = Blueprint('rp', __name__) + + +@rp.route("/rp/isLoggedIn", methods=['GET']) +def rp_isLoggedIn(): + url_host = urllib.parse.urlsplit(request.url).hostname + if "5000" in request.url: + redirect_uri = "http://"+url_host+":5000/rp/google_callback" + else: + redirect_uri = "https://"+url_host+"/rp/google_callback" + google = OAuth2Session( + google_client_id, scope=scope, redirect_uri=redirect_uri) + login_url, state = google.authorization_url(authorization_base_url) + session['oauth_state'] = google_client_id + if current_user.is_authenticated: + if current_user.openai_key == "" or current_user.openai_key == None: + keyAdded = None + else: + keyAdded = current_user.openai_key + return jsonify(isLoggedIn=current_user.is_authenticated,userId=current_user.id,key_added=keyAdded,image=current_user.profile_image) + else: + return jsonify(isLoggedIn=False,auth_url=login_url) + +@rp.route("/rp/google_callback", methods=['GET']) +def rp_google_callback(): + url_host = urllib.parse.urlsplit(request.url).hostname + if "5000" in request.url: + redirect_uri = "http://"+url_host+":5000/rp/google_callback" + else: + redirect_uri = "https://"+url_host+"/rp/google_callback" + google = OAuth2Session( + google_client_id, scope=scope, redirect_uri=redirect_uri) + token_url = "https://www.googleapis.com/oauth2/v4/token" + welcome = False + try: + google.fetch_token(token_url, client_secret=google_client_secret, + authorization_response=request.url) + except: + pass + response = google.get( + 'https://www.googleapis.com/oauth2/v1/userinfo').json() + email = response["email"].lower() + googleId = str(response["id"]) + name = response["name"] + image = response["picture"] + getAdmin = Admin.query.filter_by(email=email).first() + if getAdmin == None: + getAdmin = Admin(id=secrets.token_urlsafe(24), email=email,google_id=googleId, name=name,profile_image=image, created_date=datetime.now()) + db.session.add(getAdmin) + db.session.commit() + else: + getAdmin.google_id = googleId + getAdmin.profile_image = image + db.session.commit() + login_user(getAdmin, remember=True) + return redirect("http://localhost:3000/") + +class CAMELAgent: + + def __init__( + self, + system_message, + model: ChatOpenAI, + store + ) -> None: + self.model = model + if store == None: + self.system_message = system_message + self.init_messages() + # print("NEW") + else: + self.stored_messages = store + self.system_message = store[0] + # print("MESSAGES \n",self.stored_messages,"\n SYSTEM MESSAGE \n",self.system_message) + + def reset(self) -> None: + self.init_messages() + return self.stored_messages + + def init_messages(self) -> None: + self.stored_messages = [self.system_message] + # for msg in self.stored_messages: + # print("INTIALIZED",msg.content,"\n") + + def update_messages(self, message: BaseMessage) -> List[BaseMessage]: + self.stored_messages.append(message) + # for msg in self.stored_messages: + # print("UPDATED",msg.content,"\n") + return self.stored_messages + + def step( + self, + input_message: HumanMessage, + ) -> AIMessage: + messages = self.update_messages(input_message) + output_message = self.model(messages) + self.update_messages(output_message) + + return output_message + + def store_messages(self) -> None: + return self.stored_messages + + + + +def starting_convo(assistant_role_name,user_role_name,task): + task_specifier_sys_msg = SystemMessage(content="You can make a task more specific.") + task_specifier_prompt = ( + """Here is a task that {assistant_role_name} will help {user_role_name} to complete: {task}. + Please make it more specific. Be creative and imaginative. + Please reply with the specified task in {word_limit} words or less. Do not add anything else.""" + ) + task_specifier_template = HumanMessagePromptTemplate.from_template(template=task_specifier_prompt) + task_specify_agent = CAMELAgent(task_specifier_sys_msg, ChatOpenAI(temperature=1.0),None) + task_specifier_msg = task_specifier_template.format_messages(assistant_role_name=assistant_role_name, + user_role_name=user_role_name, + task=task, word_limit=word_limit)[0] + specified_task_msg = task_specify_agent.step(task_specifier_msg) + # print(f"Specified task: {specified_task_msg.content}") + specified_task = specified_task_msg.content + + assistant_inception_prompt = ( + """Never forget you are a {assistant_role_name} and I am a {user_role_name}. Never flip roles! Never instruct me! + We share a common interest in collaborating to successfully complete a task. + You must help me to complete the task. + Here is the task: {task}. Never forget our task! + I must instruct you based on your expertise and my needs to complete the task. + + I must give you one instruction at a time. + You must write a specific solution that appropriately completes the requested instruction. + You must decline my instruction honestly if you cannot perform the instruction due to physical, moral, legal reasons or your capability and explain the reasons. + Do not add anything else other than your solution to my instruction. + You are never supposed to ask me any questions you only answer questions. + You are never supposed to reply with a flake solution. Explain your solutions. + Your solution must be declarative sentences and simple present tense. + Unless I say the task is completed, you should always start with: + + Solution: + + should be specific and provide preferable implementations and examples for task-solving. + Always end with: Next request.""" + ) + + user_inception_prompt = ( + """Never forget you are a {user_role_name} and I am a {assistant_role_name}. Never flip roles! You will always instruct me. + We share a common interest in collaborating to successfully complete a task. + I must help you to complete the task. + Here is the task: {task}. Never forget our task! + You must instruct me based on my expertise and your needs to complete the task ONLY in the following two ways: + + 1. Instruct with a necessary input: + Instruction: + Input: + + 2. Instruct without any input: + Instruction: + Input: None + + The "Instruction" describes a task or question. The paired "Input" provides further context or information for the requested "Instruction". + + You must give me one instruction at a time. + I must write a response that appropriately completes the requested instruction. + I must decline your instruction honestly if I cannot perform the instruction due to physical, moral, legal reasons or my capability and explain the reasons. + You should instruct me not ask me questions. + Now you must start to instruct me using the two ways described above. + Do not add anything else other than your instruction and the optional corresponding input! + Keep giving me instructions and necessary inputs until you think the task is completed. + When the task is completed, you must only reply with a single word . + Never say unless my responses have solved your task.""" + ) + return specified_task,assistant_inception_prompt,user_inception_prompt + +def get_sys_msgs(assistant_role_name: str, user_role_name: str, task: str,assistant_inception_prompt,user_inception_prompt): + + assistant_sys_template = SystemMessagePromptTemplate.from_template(template=assistant_inception_prompt) + assistant_sys_msg = assistant_sys_template.format_messages(assistant_role_name=assistant_role_name, user_role_name=user_role_name, task=task)[0] + + user_sys_template = SystemMessagePromptTemplate.from_template(template=user_inception_prompt) + user_sys_msg = user_sys_template.format_messages(assistant_role_name=assistant_role_name, user_role_name=user_role_name, task=task)[0] + + return assistant_sys_msg, user_sys_msg + +@rp.route("/rp/start", methods=['POST']) +def start_rp(): + if not current_user.is_authenticated: + return redirect("/agent_convo") + os.environ["OPENAI_API_KEY"] = current_user.openai_key + assistant_role_name = request.json["role1"] + user_role_name = request.json["role2"] + task = request.json["task"] + sessId = request.json["sessId"] + if sessId == 0: + getSession = Agent_Session(role_1=assistant_role_name,role_2=user_role_name,task=task,admin_id=current_user.id) + db.session.add(getSession) + db.session.commit() + specified_task,assistant_inception_prompt,user_inception_prompt = starting_convo(assistant_role_name, user_role_name, task) + assistant_sys_msg, user_sys_msg = get_sys_msgs(assistant_role_name, user_role_name, specified_task,assistant_inception_prompt,user_inception_prompt) + assistant_agent = CAMELAgent(assistant_sys_msg, ChatOpenAI(temperature=0.2),None) + user_agent = CAMELAgent(user_sys_msg, ChatOpenAI(temperature=0.2),None) + # Reset agents + assistant_agent.reset() + user_agent.reset() + + # Initialize chats + assistant_msg = HumanMessage( + content=(f"{user_sys_msg.content}. " + "Now start to give me introductions one by one. " + "Only reply with Instruction and Input.")) + + user_msg = HumanMessage(content=f"{assistant_sys_msg.content}") + user_msg = assistant_agent.step(user_msg) + else: + getSession = Agent_Session.query.filter_by(id=sessId).first() + user_store = pickle.loads(codecs.decode((getSession.user_store).encode(), "base64")) + assistant_store = pickle.loads(codecs.decode((getSession.assistant_store).encode(), "base64")) + user_agent = CAMELAgent(None, ChatOpenAI(temperature=0.2),user_store) + assistant_agent = CAMELAgent(None, ChatOpenAI(temperature=0.2),assistant_store) + assistant_msg = HumanMessage( + content=(f"{assistant_store[-1].content}")) + + # chat_turn_limit, n = 10, 0 + # while n < chat_turn_limit: + # n += 1 + user_ai_msg = user_agent.step(assistant_msg) + user_msg = HumanMessage(content=user_ai_msg.content) + userMsg = user_msg.content.replace("Instruction: ","").replace("Input: None","").replace("Input: None.","") + # print(f"AI User ({user_role_name}):\n\n{user_msg.content}\n\n") + assistant_ai_msg = assistant_agent.step(user_msg) + assistant_msg = HumanMessage(content=assistant_ai_msg.content) + assistantMsg = assistant_msg.content.replace("Solution: ","").replace("Next request.","") + # print(f"AI Assistant ({assistant_role_name}):\n\n{assistant_msg.content}\n\n") + convoEnd = False + if "" in user_msg.content: + convoEnd = True + getUserStore = user_agent.store_messages() + getSession.user_store = codecs.encode(pickle.dumps(getUserStore), "base64").decode() + getAssistantStore = assistant_agent.store_messages() + getSession.assistant_store = codecs.encode(pickle.dumps(getAssistantStore), "base64").decode() + db.session.commit() + return jsonify(sessId=getSession.id,userMsg=userMsg,assistantMsg=assistantMsg,convoEnd=convoEnd) + + +@rp.route("/rp/get_chat", methods=['get']) +def rp_get_chat(): + sessId = request.args.get('sessId') + getSession = Agent_Session.query.filter_by(id=sessId).first() + assistant_store = pickle.loads(codecs.decode((getSession.assistant_store).encode(), "base64")) + messages = [] + for store in assistant_store[2:]: + if str(type(store)) == "": + messages.append({"role":0,"msg":store.content.replace("Instruction: ","").replace("Input: None","").replace("Input: None.","")}) + elif str(type(store)) == "": + messages.append({"role":1,"msg":store.content.replace("Solution: ","").replace("Next request.","")}) + return jsonify(role1=getSession.role_1,role2=getSession.role_2,task=getSession.task,messages=messages) diff --git a/server/database.py b/server/database.py new file mode 100644 index 0000000..04599b2 --- /dev/null +++ b/server/database.py @@ -0,0 +1,43 @@ +from flask_sqlalchemy import SQLAlchemy +from sqlalchemy import DateTime, ForeignKey, MetaData +from flask_login import UserMixin + +from flask import Flask + +convention = { + "ix": 'ix_%(column_0_label)s', + "uq": "uq_%(table_name)s_%(column_0_name)s", + "ck": "ck_%(table_name)s_%(constraint_name)s", + "fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s", + "pk": "pk_%(table_name)s" +} + +metadata = MetaData(naming_convention=convention) +db = SQLAlchemy(metadata=metadata) +app = Flask(__name__) + +class Agent_Session(db.Model): + __tablename__ = "agent_session" + id = db.Column(db.Integer, primary_key=True, autoincrement=True) + role_1 = db.Column(db.String(200), default="", server_default = "") + role_2 = db.Column(db.String(200), default="", server_default = "") + task = db.Column(db.String(3000), default="", server_default = "") + user_store = db.Column(db.String, default="", server_default = "") + assistant_store = db.Column(db.String, default="", server_default = "") + admin_id = db.Column(db.String(100), ForeignKey("admin.id"), index=True) + +class Admin(UserMixin,db.Model): + __tablename__ = "admin" + id = db.Column(db.String(100), primary_key=True) + name = db.Column(db.String(100)) + email = db.Column(db.String(100), unique=True, index=True) + google_id = db.Column(db.String(100)) + openai_key = db.Column(db.String(100)) + profile_image = db.Column(db.String(100000)) + password = db.Column(db.String(100)) + created_date = db.Column(DateTime) + gpt_model = db.Column(db.String, default="gpt-3.5-turbo", server_default="gpt-3.5-turbo") + agent_sessions = db.relationship('Agent_Session', backref='admin', + cascade="all,delete", lazy='dynamic') + def get_id(self): + return (self.id) \ No newline at end of file diff --git a/server/requirements.txt b/server/requirements.txt new file mode 100644 index 0000000..e69de29 diff --git a/server/webserver.py b/server/webserver.py new file mode 100644 index 0000000..26370ff --- /dev/null +++ b/server/webserver.py @@ -0,0 +1,66 @@ +from flask import Flask +from flask_migrate import Migrate +from database import * +import os +import urllib.parse +from flask import jsonify,request,session,render_template,redirect,url_for,Blueprint +from requests_oauthlib import OAuth2Session +from flask_login import UserMixin, LoginManager, login_required, login_user, current_user, logout_user +import secrets +from datetime import datetime, date, timedelta, timezone +import os +import json +import random +import requests +from agent_convo import rp + +try: + import config +except ModuleNotFoundError: + pass + + +app = Flask(__name__) +app.register_blueprint(rp) +app.secret_key = 'autogptsamurai@123' + +app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///./test.db' +db.init_app(app) +db.app = app +migrate = Migrate(app, db, compare_type=True, + render_as_batch=True) + +login_manager = LoginManager(app) + + +@login_manager.user_loader +def load_user(user_id): + return Admin.query.filter_by(id=user_id).first() + +os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1' + +@app.route("/change_model", methods=['POST']) +def change_model(): + model = request.values["model"] + current_user.gpt_model = model + db.session.commit() + return "Success" + +@app.route("/logout", methods=['GET']) +def logout(): + logout_user() + return "Success", 200 + +@app.route("/store_key", methods=['POST']) +def store_key(): + key = request.json['key'] + getAdmin = Admin.query.filter_by(id=current_user.id).first() + if len(key) == 51: + getAdmin.openai_key = key + db.session.commit() + return jsonify(True) + else: + return jsonify(False), 400 + +if __name__ == '__main__': + app.run(host="0.0.0.0", debug=True)