├── public ├── favicon.ico ├── screen.png ├── favicon-16x16.png ├── favicon-32x32.png ├── apple-touch-icon.png ├── robots.txt ├── android-chrome-192x192.png ├── android-chrome-512x512.png ├── manifest.json ├── site.webmanifest └── index.html ├── src ├── setupTests.ts ├── services │ ├── firebase.ts │ └── GptAPI.ts ├── components │ ├── examples │ │ ├── RunExamplesButton.tsx │ │ ├── ExampleCollection.tsx │ │ ├── ExamplesTab.tsx │ │ └── Example.tsx │ ├── workspaceSelector │ │ ├── CreateButton.tsx │ │ ├── EditButton.tsx │ │ ├── DeleteButton.tsx │ │ ├── DeletePopup.tsx │ │ ├── EditPopup.tsx │ │ └── WorkspaceSelector.tsx │ ├── variations │ │ ├── VariationsCollection.tsx │ │ ├── FetchVariationsButton.tsx │ │ ├── Variation.tsx │ │ └── VariationsTab.tsx │ ├── conversations │ │ ├── ConversationsTab.tsx │ │ ├── CompletionParameters.tsx │ │ ├── Input.tsx │ │ └── Conversation.tsx │ ├── fileExport │ │ ├── UploadButton.tsx │ │ └── DownloadButton.tsx │ ├── WorkspaceForm.tsx │ ├── TemplatesForm.tsx │ ├── dialogs │ │ ├── ApiKeyDialog.tsx │ │ └── TemplateDialog.tsx │ ├── Header.tsx │ ├── basic │ │ └── BasicTab.tsx │ ├── ModeTabs.tsx │ ├── CodeGeneratorButton.tsx │ ├── SharePopup.tsx │ └── PromptEditor.tsx ├── index.tsx ├── resources │ └── SharedPrompt.ts ├── App.tsx ├── store.ts ├── migrations.ts ├── libs │ ├── codeGenerator.ts │ └── templatesLibrary.ts └── slices │ └── editorSlice.ts ├── tsconfig.json ├── LICENSE ├── package.json ├── README.md └── .gitignore /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawwn/gpt4/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /public/screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawwn/gpt4/HEAD/public/screen.png -------------------------------------------------------------------------------- /public/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawwn/gpt4/HEAD/public/favicon-16x16.png -------------------------------------------------------------------------------- /public/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawwn/gpt4/HEAD/public/favicon-32x32.png -------------------------------------------------------------------------------- /public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawwn/gpt4/HEAD/public/apple-touch-icon.png -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /public/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawwn/gpt4/HEAD/public/android-chrome-192x192.png -------------------------------------------------------------------------------- /public/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawwn/gpt4/HEAD/public/android-chrome-512x512.png -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "GPT-4", 3 | "name": "gpt4.org", 4 | "icons": [ 5 | ], 6 | "start_url": ".", 7 | "display": "standalone", 8 | "theme_color": "#000000", 9 | "background_color": "#ffffff" 10 | } 11 | -------------------------------------------------------------------------------- /src/setupTests.ts: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom/extend-expect'; 6 | -------------------------------------------------------------------------------- /public/site.webmanifest: -------------------------------------------------------------------------------- 1 | {"name":"Prompts","short_name":"Prompts","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"} -------------------------------------------------------------------------------- /src/services/firebase.ts: -------------------------------------------------------------------------------- 1 | import firebase from 'firebase'; 2 | 3 | const config = { 4 | apiKey: "AIzaSyAlIO8H8XBwAtN_WmtB2OIRbrlThuafVhg", 5 | authDomain: "prompts-ai.firebaseapp.com", 6 | projectId: "prompts-ai", 7 | }; 8 | 9 | firebase.initializeApp(config); 10 | 11 | export default firebase; -------------------------------------------------------------------------------- /src/components/examples/RunExamplesButton.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {useDispatch} from "react-redux"; 3 | import {fetchExamplesOutputsAsync} from "../../slices/editorSlice"; 4 | import { Button } from '@material-ui/core'; 5 | 6 | export default function RunExamplesButton() { 7 | const dispatch = useDispatch(); 8 | const fetchOutputs = () => { 9 | dispatch(fetchExamplesOutputsAsync()); 10 | }; 11 | return ( 12 | 13 | ); 14 | } -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | import { store, persistor } from './store'; 5 | import { Provider } from 'react-redux'; 6 | import { PersistGate } from 'redux-persist/integration/react'; 7 | 8 | ReactDOM.render( 9 | 10 | 11 | 12 | 13 | 14 | 15 | , 16 | document.getElementById('root') 17 | ); 18 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "strict": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "module": "esnext", 16 | "moduleResolution": "node", 17 | "resolveJsonModule": true, 18 | "isolatedModules": true, 19 | "noEmit": true, 20 | "jsx": "react" 21 | }, 22 | "include": [ 23 | "src" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /src/components/workspaceSelector/CreateButton.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import AddIcon from '@material-ui/icons/Add'; 3 | import {IconButton} from "@material-ui/core"; 4 | import { useDispatch } from 'react-redux'; 5 | import { createWorkspace } from '../../slices/editorSlice'; 6 | import {ActionCreators} from "redux-undo"; 7 | 8 | export default function CreateButton() { 9 | const dispatch = useDispatch(); 10 | const onAdd = () => { 11 | dispatch(createWorkspace()); 12 | dispatch(ActionCreators.clearHistory()) 13 | }; 14 | 15 | return 16 | 17 | ; 18 | } -------------------------------------------------------------------------------- /src/components/workspaceSelector/EditButton.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import EditIcon from '@material-ui/icons/Edit'; 3 | import {IconButton} from "@material-ui/core"; 4 | import EditPopup from "./EditPopup"; 5 | 6 | export default function EditButton() { 7 | const [isPopupOpen, setPopupOpen] = React.useState(false); 8 | const openPopup = () => { 9 | setPopupOpen(true); 10 | }; 11 | 12 | return <> 13 | 14 | 15 | 16 | { 17 | setPopupOpen(false); 18 | }} /> 19 | ; 20 | } -------------------------------------------------------------------------------- /src/components/workspaceSelector/DeleteButton.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import DeleteIcon from '@material-ui/icons/Delete'; 3 | import {IconButton} from "@material-ui/core"; 4 | import DeletePopup from "./DeletePopup"; 5 | 6 | export default function DeleteButton() { 7 | const [isPopupOpen, setPopupOpen] = React.useState(false); 8 | const openPopup = () => { 9 | setPopupOpen(true); 10 | }; 11 | 12 | return <> 13 | 14 | 15 | 16 | { 17 | setPopupOpen(false); 18 | }} /> 19 | ; 20 | } -------------------------------------------------------------------------------- /src/resources/SharedPrompt.ts: -------------------------------------------------------------------------------- 1 | import firebase from '../services/firebase'; 2 | 3 | interface SharedPromptWriteProperties { 4 | engine: string; 5 | maxTokens: number; 6 | stop: string | Array; 7 | prompt: string; 8 | temperature: number; 9 | topP: number; 10 | presencePenalty: number; 11 | frequencyPenalty: number; 12 | examples?: Array; 13 | } 14 | 15 | interface SharedPromptExample { 16 | text: string; 17 | } 18 | 19 | export default class SharedPrompt { 20 | static create (properties: SharedPromptWriteProperties): Promise { 21 | const db = firebase.firestore(); 22 | return db.collection('SharedPrompts').add(properties); 23 | } 24 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020-2021 Seva Zhidkov and others 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /src/components/variations/VariationsCollection.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {useSelector} from "react-redux"; 3 | import {selectVariations} from "../../slices/editorSlice"; 4 | import { Grid, Box } from '@material-ui/core'; 5 | import {makeStyles} from "@material-ui/styles"; 6 | import Variation from "./Variation"; 7 | 8 | const useStyles = makeStyles({ 9 | gridCard: { 10 | width: '100%', 11 | }, 12 | }); 13 | 14 | 15 | export default function VariationsCollection() { 16 | const completions = useSelector(selectVariations); 17 | const styles = useStyles(); 18 | 19 | return ( 20 | 21 | 27 | {completions.slice().reverse().map(completion => ( 28 | 29 | 30 | 31 | ))} 32 | 33 | 34 | ); 35 | } -------------------------------------------------------------------------------- /src/components/examples/ExampleCollection.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Example from './Example'; 3 | import {useSelector} from "react-redux"; 4 | import {selectExamples} from "../../slices/editorSlice"; 5 | import { Grid, Box } from '@material-ui/core'; 6 | import {makeStyles} from "@material-ui/styles"; 7 | 8 | const useStyles = makeStyles({ 9 | gridCard: { 10 | width: '100%', 11 | }, 12 | }); 13 | 14 | 15 | export default function ExampleCollection() { 16 | const examples = useSelector(selectExamples); 17 | const styles = useStyles(); 18 | 19 | return ( 20 | 21 | 27 | {examples.map((example, ind) => ( 28 | 29 | 30 | 31 | ))} 32 | 33 | 34 | ); 35 | } -------------------------------------------------------------------------------- /src/components/conversations/ConversationsTab.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import {Box, Grid} from "@material-ui/core"; 3 | import {makeStyles} from "@material-ui/core/styles"; 4 | import {useSelector} from "react-redux"; 5 | import Conversation from "./Conversation"; 6 | import {RootState} from "../../store"; 7 | 8 | const useStyles = makeStyles({ 9 | gridItem: { 10 | width: '100%', 11 | }, 12 | }); 13 | 14 | export default function ConversationsTab() { 15 | const styles = useStyles(); 16 | const conversationIds = useSelector((state: RootState) => { 17 | const workspace = state.editor.present.workspaces.find(w => w.id === state.editor.present.currentWorkspaceId)!; 18 | return workspace.conversations.map(c => c.id); 19 | }); 20 | 21 | return 22 | 27 | {conversationIds.map((conversationId, ind) => ( 28 | 29 | 30 | 31 | ))} 32 | 33 | ; 34 | } -------------------------------------------------------------------------------- /src/components/variations/FetchVariationsButton.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {useDispatch, useSelector} from "react-redux"; 3 | import {Button, CircularProgress} from '@material-ui/core'; 4 | import { makeStyles, createStyles } from '@material-ui/styles'; 5 | import { green } from '@material-ui/core/colors'; 6 | import {fetchVariationsAsync, selectVariationsLoadingStatus} from "../../slices/editorSlice"; 7 | 8 | const useStyles = makeStyles( 9 | createStyles({ 10 | buttonProgress: { 11 | color: green[500], 12 | position: 'absolute', 13 | top: '50%', 14 | left: '50%', 15 | marginTop: -12, 16 | marginLeft: -12, 17 | }, 18 | }), 19 | ); 20 | 21 | export default function FetchVariationsButton() { 22 | const styles = useStyles(); 23 | const dispatch = useDispatch(); 24 | const isLoading = useSelector(selectVariationsLoadingStatus); 25 | const fetchOutputs = () => { 26 | dispatch(fetchVariationsAsync()); 27 | }; 28 | return ( 29 | 34 | ); 35 | } -------------------------------------------------------------------------------- /src/components/workspaceSelector/DeletePopup.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { 3 | Dialog, 4 | DialogContent, 5 | DialogActions, 6 | Button, 7 | DialogContentText 8 | } from '@material-ui/core'; 9 | import {useDispatch} from "react-redux"; 10 | import { deleteCurrentWorkspace } from '../../slices/editorSlice'; 11 | import {ActionCreators} from "redux-undo"; 12 | 13 | interface Props { 14 | open: boolean; 15 | onClose: () => void; 16 | } 17 | 18 | export default function DeletePopup(props: Props) { 19 | const dispatch = useDispatch(); 20 | const onDelete = () => { 21 | dispatch(deleteCurrentWorkspace()); 22 | dispatch(ActionCreators.clearHistory()) 23 | props.onClose(); 24 | }; 25 | return 26 | 27 | 28 | This action cannot be undone. This will permanently delete the draft with the prompt, parameters, examples, creative generations. 29 | 30 | 31 | 32 | 35 | 38 | 39 | ; 40 | } 41 | -------------------------------------------------------------------------------- /src/services/GptAPI.ts: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import {AxiosPromise} from "axios"; 3 | import {CompletionParameters} from "../slices/editorSlice"; 4 | 5 | export interface ChoiceResult { 6 | finish_reason: string; 7 | index: number; 8 | text: string; 9 | } 10 | 11 | //const HOST = `https://api.openai.com`; 12 | //const HOST = `http://localhost:9000`; 13 | //const HOST = `http://api.gpt4.org:9000`; 14 | const HOST = `https://api.gpt4.org`; 15 | 16 | class GptAPI { 17 | static generateCompletions(prompt: string | Array, completionParams: CompletionParameters, 18 | n: number = 1, echo: boolean = false): AxiosPromise { 19 | return axios({ 20 | method: "POST", 21 | url: `${HOST}/v1/engines/${completionParams.engine}/completions`, 22 | headers: { 23 | "Content-Type": "application/json", 24 | "Authorization": `Bearer ${completionParams.apiKey}`, 25 | }, 26 | data: { 27 | "echo": echo, 28 | "prompt": prompt, 29 | "n": n, 30 | "max_tokens": completionParams.maxTokens, 31 | "temperature": completionParams.temperature, 32 | "stop": completionParams.stop, 33 | "top_p": completionParams.topP, 34 | "presence_penalty": completionParams.presencePenalty, 35 | "frequency_penalty": completionParams.frequencyPenalty 36 | } 37 | }); 38 | } 39 | } 40 | 41 | export default GptAPI; -------------------------------------------------------------------------------- /src/components/fileExport/UploadButton.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | // @ts-ignore 3 | import Files from "react-files"; 4 | import {Button} from "@material-ui/core"; 5 | import {useDispatch} from "react-redux"; 6 | import {loadTemplateFromFileData, LoadTemplateFromFileDataActionPayload} from "../../slices/editorSlice"; 7 | import CloudUploadIcon from "@material-ui/icons/CloudUpload"; 8 | 9 | interface Props { 10 | className: string; 11 | } 12 | 13 | export default function UploadButton(props: Props) { 14 | const dispatch = useDispatch(); 15 | 16 | const fileReader = new FileReader(); 17 | fileReader.onload = (event) => { 18 | if (event.target === undefined) { 19 | return; 20 | } 21 | if (event.target!.result === undefined) { 22 | return; 23 | } 24 | const template: LoadTemplateFromFileDataActionPayload = JSON.parse(event.target!.result as string); 25 | 26 | template.stopSymbols = template.stopSymbols.map(symbol => { 27 | return symbol.split('\n').join('\\n'); 28 | }); 29 | dispatch(loadTemplateFromFileData(template)); 30 | }; 31 | 32 | return { 35 | fileReader.readAsText(file[0]); 36 | }} 37 | onError={(err: any) => console.log(err)} 38 | accepts={['.json']} 39 | maxFileSize={10000000} 40 | minFileSize={0} 41 | clickable 42 | > 43 | 52 | ; 53 | } -------------------------------------------------------------------------------- /src/components/workspaceSelector/EditPopup.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Dialog, DialogTitle, TextField, DialogContent, DialogActions, Button } from '@material-ui/core'; 3 | import {useDispatch, useSelector} from 'react-redux'; 4 | import { 5 | selectEditableWorkspaceName, 6 | updateCurrentWorkspaceName, 7 | updateEditableWorkspaceName 8 | } from '../../slices/editorSlice'; 9 | import {ActionCreators} from "redux-undo"; 10 | 11 | interface Props { 12 | open: boolean; 13 | onClose: () => void; 14 | } 15 | 16 | export default function EditPopup(props: Props) { 17 | const dispatch = useDispatch(); 18 | const editableName = useSelector(selectEditableWorkspaceName); 19 | const onSave = () => { 20 | dispatch(updateCurrentWorkspaceName()) 21 | dispatch(ActionCreators.clearHistory()) 22 | props.onClose(); 23 | }; 24 | 25 | return 26 | Edit 27 | 28 | ) => { 35 | dispatch(updateEditableWorkspaceName(event.currentTarget.value)); 36 | }} 37 | fullWidth 38 | /> 39 | 40 | 41 | 44 | 47 | 48 | ; 49 | } 50 | -------------------------------------------------------------------------------- /src/components/workspaceSelector/WorkspaceSelector.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Select, Grid } from "@material-ui/core"; 3 | import {makeStyles} from "@material-ui/core/styles"; 4 | import CreateButton from './CreateButton'; 5 | import EditButton from "./EditButton"; 6 | import DeleteButton from "./DeleteButton"; 7 | import {selectWorkspacesList, selectCurrentWorkspaceId, updateWorkspaceId} from "../../slices/editorSlice"; 8 | import {useDispatch, useSelector} from 'react-redux'; 9 | import { ActionCreators } from 'redux-undo'; 10 | 11 | const useStyles = makeStyles({ 12 | selectGridItem: { 13 | width: '150px', 14 | }, 15 | }); 16 | 17 | export default function WorkspaceSelector() { 18 | const styles = useStyles(); 19 | const dispatch = useDispatch(); 20 | const workspaceId = useSelector(selectCurrentWorkspaceId); 21 | const workspaces = useSelector(selectWorkspacesList); 22 | 23 | const handleSelectChange = (event: React.ChangeEvent<{ value: unknown }>) => { 24 | dispatch(updateWorkspaceId(event.target.value as string)); 25 | dispatch(ActionCreators.clearHistory()) 26 | } 27 | return 28 | 29 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | ; 51 | } 52 | -------------------------------------------------------------------------------- /src/components/WorkspaceForm.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Grid, Button } from '@material-ui/core'; 3 | import LibraryBooksIcon from '@material-ui/icons/LibraryBooks'; 4 | import WorkspaceSelector from "./workspaceSelector/WorkspaceSelector"; 5 | import {makeStyles} from "@material-ui/styles"; 6 | import {toggleTemplateDialog} from "../slices/editorSlice"; 7 | import {useDispatch} from "react-redux"; 8 | import DownloadButton from "./fileExport/DownloadButton"; 9 | import UploadButton from "./fileExport/UploadButton"; 10 | 11 | const useStyles = makeStyles({ 12 | fileExportButton: { 13 | width: '125px', 14 | }, 15 | templateButton: { 16 | width: '100%', 17 | }, 18 | }); 19 | 20 | 21 | 22 | export default function WorkspaceForm () { 23 | const styles = useStyles(); 24 | const dispatch = useDispatch(); 25 | 26 | const handleTemplateDialogOpen = () => { 27 | dispatch(toggleTemplateDialog(true)); 28 | }; 29 | 30 | return <> 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 52 | 53 | 54 | ; 55 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "prompts-ai-frontend", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@material-ui/core": "^4.11.0", 7 | "@material-ui/icons": "^4.9.1", 8 | "@material-ui/styles": "^4.10.0", 9 | "@reduxjs/toolkit": "^1.2.5", 10 | "@testing-library/jest-dom": "^4.2.4", 11 | "@testing-library/react": "^9.3.2", 12 | "@testing-library/user-event": "^7.1.2", 13 | "@types/jest": "^24.0.0", 14 | "@types/node": "^12.0.0", 15 | "@types/rc-slider": "^8.6.6", 16 | "@types/react": "^16.9.0", 17 | "@types/react-dom": "^16.9.0", 18 | "@types/react-redux": "^7.1.7", 19 | "@types/react-syntax-highlighter": "^11.0.4", 20 | "@types/uniqid": "^5.2.0", 21 | "axios": "^0.19.2", 22 | "firebase": "^7.17.2", 23 | "lodash": "^4.17.19", 24 | "material-ui-chip-input": "^2.0.0-beta.2", 25 | "rc-slider": "9.2.4", 26 | "react": "^16.13.1", 27 | "react-dom": "^16.13.1", 28 | "react-files": "^2.4.8", 29 | "react-hotkeys-hook": "^2.2.1", 30 | "react-redux": "^7.2.0", 31 | "react-scripts": "3.4.1", 32 | "react-syntax-highlighter": "^13.2.1", 33 | "redux-persist": "^6.0.0", 34 | "redux-state-sync": "^3.1.2", 35 | "redux-undo": "^1.0.1", 36 | "typescript": "~3.8.2", 37 | "uniqid": "^5.2.0" 38 | }, 39 | "scripts": { 40 | "start": "react-scripts start", 41 | "build": "react-scripts build", 42 | "test": "react-scripts test", 43 | "eject": "react-scripts eject" 44 | }, 45 | "eslintConfig": { 46 | "extends": "react-app" 47 | }, 48 | "browserslist": { 49 | "production": [ 50 | ">0.2%", 51 | "not dead", 52 | "not op_mini all" 53 | ], 54 | "development": [ 55 | "last 1 chrome version", 56 | "last 1 firefox version", 57 | "last 1 safari version" 58 | ] 59 | }, 60 | "devDependencies": { 61 | "@types/lodash": "^4.14.158", 62 | "@types/redux-state-sync": "^3.1.1" 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/App.tsx: -------------------------------------------------------------------------------- 1 | import React, {useEffect} from 'react'; 2 | import {PromptEditor} from './components/PromptEditor'; 3 | import {Box, Container, createMuiTheme, CssBaseline, ThemeProvider, Typography,} from "@material-ui/core"; 4 | import {useHotkeys} from "react-hotkeys-hook"; 5 | import {useDispatch} from "react-redux"; 6 | import {fetchForCurrentTab, updateTabIndex, normalizeConversations} from "./slices/editorSlice"; 7 | import Header from "./components/Header"; 8 | import TemplateDialog from "./components/dialogs/TemplateDialog"; 9 | import ApiKeyDialog from "./components/dialogs/ApiKeyDialog"; 10 | 11 | function App() { 12 | const dispatch = useDispatch(); 13 | const theme = createMuiTheme({ 14 | palette: { 15 | type: "dark" 16 | } 17 | }); 18 | 19 | useEffect(() => { 20 | dispatch(normalizeConversations()); 21 | }); 22 | 23 | useHotkeys('ctrl+enter,cmd+enter', () => { 24 | dispatch(fetchForCurrentTab()); 25 | }, {filter: () => true}); 26 | useHotkeys('ctrl+1', () => { 27 | dispatch(updateTabIndex(0)); 28 | }); 29 | useHotkeys('ctrl+2', () => { 30 | dispatch(updateTabIndex(1)); 31 | }); 32 | useHotkeys('ctrl+3', () => { 33 | dispatch(updateTabIndex(2)); 34 | }); 35 | useHotkeys('ctrl+4', () => { 36 | dispatch(updateTabIndex(3)); 37 | }); 38 | 39 | return ( 40 | 41 | 42 | 43 | 44 | 45 | 46 |
47 | 48 | 49 | 50 | 51 | 52 | {/* 53 | 54 | */} 55 | 56 | 57 | Not affiliated with OpenAI. Feedback: https://github.com/shawwn/gpt4 58 | 59 | 60 | 61 | ); 62 | } 63 | 64 | export default App; 65 | -------------------------------------------------------------------------------- /src/components/TemplatesForm.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import {useDispatch} from "react-redux"; 3 | import {Button, FormControl, Select, Box, Grid} from "@material-ui/core"; 4 | import getTemplateGroups, {getFlattenedTemplates} from "../libs/templatesLibrary"; 5 | import {loadTemplate, cleanExampleList,} from "../slices/editorSlice"; 6 | 7 | interface FormElements extends HTMLCollection { 8 | templateId: HTMLSelectElement; 9 | } 10 | 11 | export default function TemplatesForm() { 12 | const templateGroups = getTemplateGroups(); 13 | const flattenedTemplates = getFlattenedTemplates(); 14 | 15 | const dispatch = useDispatch(); 16 | 17 | return 18 |
{ 19 | event.preventDefault(); 20 | const elements: FormElements = event.currentTarget.elements as FormElements; 21 | const template = flattenedTemplates.find((template) => template.id === elements.templateId.value); 22 | if (template === undefined) { 23 | return; 24 | } 25 | dispatch(loadTemplate(template.actionPayload)) 26 | dispatch(cleanExampleList()); 27 | }}> 28 | 29 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 |
48 |
; 49 | } -------------------------------------------------------------------------------- /src/components/fileExport/DownloadButton.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import {useSelector} from "react-redux"; 3 | import {Button} from "@material-ui/core"; 4 | import SaveIcon from "@material-ui/icons/Save"; 5 | import { 6 | selectFrequencyPenalty, selectMaxTokens, selectModelName, 7 | selectPresencePenalty, 8 | selectPrompt, selectStopSymbols, 9 | selectTemperature, 10 | selectTopP 11 | } from "../../slices/editorSlice"; 12 | import {useHotkeys} from "react-hotkeys-hook"; 13 | 14 | interface Props { 15 | className: string; 16 | } 17 | 18 | export default function DownloadButton(props: Props) { 19 | const prompt = useSelector(selectPrompt); 20 | const temperature = useSelector(selectTemperature); 21 | const topP = useSelector(selectTopP); 22 | const frequencyPenalty = useSelector(selectFrequencyPenalty); 23 | const presencePenalty = useSelector(selectPresencePenalty); 24 | const maxTokens = useSelector(selectMaxTokens); 25 | const stopSymbols = useSelector(selectStopSymbols); 26 | const modelName = useSelector(selectModelName); 27 | 28 | useHotkeys('ctrl+s,cmd+s', (event) => { 29 | event.preventDefault(); 30 | handleSaveAndDownload(); 31 | }); 32 | 33 | const handleSaveAndDownload = () => { 34 | const element = document.createElement("a"); 35 | const savedStopSymbols = stopSymbols.map(symbol => { 36 | return symbol.split('\\n').join('\n'); 37 | }); 38 | const file = new Blob([ 39 | JSON.stringify({prompt, temperature, topP, 40 | frequencyPenalty, presencePenalty, 41 | maxTokens, stopSymbols: savedStopSymbols, modelName, 42 | }) 43 | ], {type: 'text/plain'}); 44 | element.href = URL.createObjectURL(file); 45 | element.download = `gpt3_workspace_${Math.trunc(Date.now() / 1000)}.json`; 46 | document.body.appendChild(element); 47 | element.click(); 48 | } 49 | 50 | return ; 60 | } 61 | -------------------------------------------------------------------------------- /src/store.ts: -------------------------------------------------------------------------------- 1 | import { configureStore, ThunkAction, Action, combineReducers, getDefaultMiddleware } from '@reduxjs/toolkit'; 2 | import { persistStore, persistReducer, createMigrate } from 'redux-persist' 3 | import storage from 'redux-persist/lib/storage' // defaults to localStorage for web 4 | import { PERSIST, PURGE, REHYDRATE } from 'redux-persist/es/constants'; 5 | import { createStateSyncMiddleware, initMessageListener } from 'redux-state-sync'; 6 | import undoable from 'redux-undo'; 7 | import {migrations, currentVersion} from './migrations'; 8 | import editorReducer from './slices/editorSlice'; 9 | 10 | const filteredActions = ['editor/addStopSymbol', 'editor/deleteStopSymbol', 11 | 'editor/editTopP', 'editor/editFrequencyPenalty', 'editor/editPresencePenalty', 12 | 'editor/loadTemplate', 'editor/editPrompt', 'editor/editApiKey', 'editor/editTemperature', 'editor/editModelName', 13 | 'editor/editMaxTokens', 'editor/loadTemplateFromFileData' // todo: examples????? 14 | ]; 15 | 16 | const reducers = combineReducers( 17 | { 18 | editor: undoable(editorReducer, { 19 | limit: 10, 20 | filter: (action: Action) => { 21 | return filteredActions.includes(action.type); 22 | }, 23 | groupBy: (action) => filteredActions.includes(action.type) ? `${action.type}_${Math.floor(Date.now() / 1000 / 10)}` : null, 24 | }) 25 | } 26 | ); 27 | 28 | const persistConfig = { 29 | key: 'root', 30 | version: currentVersion, 31 | migrate: createMigrate(migrations), 32 | storage, 33 | } 34 | 35 | const persistedReducer = persistReducer(persistConfig, reducers); 36 | const middlewares = [ 37 | ...getDefaultMiddleware({ 38 | serializableCheck: false, 39 | immutableCheck: false}), 40 | createStateSyncMiddleware({blacklist: [PERSIST, PURGE, REHYDRATE]}), 41 | ]; 42 | 43 | export const store = configureStore({ 44 | reducer: persistedReducer, 45 | middleware: middlewares, 46 | }); 47 | 48 | export const persistor = persistStore(store); 49 | initMessageListener(store); 50 | 51 | export type RootState = ReturnType; 52 | export type AppThunk = ThunkAction< 53 | ReturnType, 54 | RootState, 55 | unknown, 56 | Action 57 | >; 58 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 22 | gpt4.org, forked from Prompts.AI 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 |
34 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /src/components/dialogs/ApiKeyDialog.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | Button, 3 | Dialog, 4 | DialogActions, 5 | DialogContent, 6 | DialogContentText, 7 | DialogTitle, 8 | TextField, 9 | } from "@material-ui/core"; 10 | import React from "react"; 11 | import {editApiKey, selectApiKey, selectApiKeyDialogVisible, toggleApiKeyDialog} from "../../slices/editorSlice"; 12 | import {useDispatch, useSelector} from "react-redux"; 13 | import {makeStyles} from "@material-ui/core/styles"; 14 | 15 | const useStyles = makeStyles({ 16 | apiKeyInput: { 17 | minWidth: '400px', 18 | }, 19 | }); 20 | 21 | export default function ApiKeyDialog() { 22 | const dispatch = useDispatch(); 23 | 24 | const apiKey = useSelector(selectApiKey); 25 | const apiKeyDialogOpen = useSelector(selectApiKeyDialogVisible); 26 | const handleApiKeyDialogClose = () => { 27 | dispatch(toggleApiKeyDialog(false)); 28 | }; 29 | 30 | const classes = useStyles(); 31 | 32 | return 33 | API Key 34 | 35 | 36 | Please enter a random GPT-4 API key. You can type anything you want. 37 |
38 |
39 | (I'm far too lazy tonight to make this automatic. In the meantime, I suggest banging on your keyboard like a monkey.) 40 |
41 | ) => { 51 | dispatch(editApiKey(event.currentTarget.value)); 52 | }} 53 | /> 54 |
55 | 56 | 59 | 60 |
; 61 | } -------------------------------------------------------------------------------- /src/components/conversations/CompletionParameters.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import {ConversationCompletionParameters} from "../../slices/editorSlice"; 3 | import {Grid, TextField} from "@material-ui/core"; 4 | import ChipInput from "material-ui-chip-input"; 5 | 6 | interface Props { 7 | parameters: ConversationCompletionParameters; 8 | } 9 | 10 | // engine: string; 11 | // maxTokens: number; 12 | // stop: string | Array; 13 | // prompt: string; 14 | // temperature: number; 15 | // topP: number; 16 | // presencePenalty: number; 17 | // frequencyPenalty: number; 18 | 19 | export default function CompletionParameters(props: Props) { 20 | let stopSymbols: Array; 21 | if (props.parameters.stop === "") { 22 | stopSymbols = []; 23 | } else if (typeof props.parameters.stop === 'string') { 24 | stopSymbols = [props.parameters.stop as string]; 25 | } else { 26 | stopSymbols = props.parameters.stop as Array; 27 | } 28 | return <> 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | { 46 | return symbol.split('\n').join('\\n'); 47 | })}/> 48 | 49 | 50 | ; 51 | } -------------------------------------------------------------------------------- /src/components/variations/Variation.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import {makeStyles, Theme} from "@material-ui/core/styles"; 3 | import {Card, CardContent, Typography, CardActions} from "@material-ui/core"; 4 | import {useSelector} from "react-redux"; 5 | import {selectShowPromptForVariations} from "../../slices/editorSlice"; 6 | 7 | interface Props { 8 | id: string; 9 | prompt: string; 10 | output: string; 11 | temperature: number; 12 | maxTokens: number; 13 | topP: number; 14 | frequencyPenalty: number; 15 | presencePenalty: number; 16 | modelName: string; 17 | } 18 | 19 | const useStyles = makeStyles((theme: Theme) => ({ 20 | card: { 21 | backgroundColor: theme.palette.background.default, 22 | whiteSpace: "pre-line", 23 | }, 24 | 25 | prompt: {}, 26 | 27 | output: { 28 | //textDecoration: "underline", 29 | } 30 | })); 31 | 32 | export default function Variation(props: Props) { 33 | const styles = useStyles(); 34 | const showPromptForVariations = useSelector(selectShowPromptForVariations); 35 | 36 | return 37 | 38 | { showPromptForVariations && ( 39 | <> 40 | {props.prompt} 41 | 🧠️ 42 | {props.output} 43 | 44 | )} 45 | { !showPromptForVariations && ( 46 | <> 47 | 🧠️ 48 | {props.output} 49 | 50 | )} 51 | 52 | { showPromptForVariations && ( 53 | 54 | Temperature: {props.temperature} 55 | Max tokens: {props.maxTokens} 56 | Top P: {props.topP} 57 | Frequency penalty: {props.frequencyPenalty} 58 | Presence penalty: {props.presencePenalty} 59 | Model: {props.modelName} 60 | 61 | ) 62 | } 63 | ; 64 | } -------------------------------------------------------------------------------- /src/components/Header.tsx: -------------------------------------------------------------------------------- 1 | import {AppBar, Container, IconButton, Theme, Toolbar, Typography} from "@material-ui/core"; 2 | import VpnKeyIcon from "@material-ui/icons/VpnKey"; 3 | import UndoIcon from "@material-ui/icons/Undo"; 4 | import RedoIcon from "@material-ui/icons/Redo"; 5 | import GitHubIcon from "@material-ui/icons/GitHub"; 6 | import React from "react"; 7 | import {makeStyles} from "@material-ui/core/styles"; 8 | import {useDispatch, useSelector} from "react-redux"; 9 | import { 10 | selectApiKey, 11 | toggleApiKeyDialog, 12 | } from "../slices/editorSlice"; 13 | import {ActionCreators} from "redux-undo"; 14 | 15 | const useStyles = makeStyles((theme: Theme) => ({ 16 | buttonGroup: { 17 | marginRight: theme.spacing(2), 18 | }, 19 | apiKeyInput: { 20 | minWidth: '400px', 21 | }, 22 | })); 23 | 24 | export default function Header() { 25 | const dispatch = useDispatch(); 26 | const classes = useStyles(); 27 | 28 | const apiKey = useSelector(selectApiKey); 29 | const apiKeyPresent = !!apiKey; 30 | const handleApiKeyDialogOpen = () => { 31 | dispatch(toggleApiKeyDialog(true)); 32 | }; 33 | const handleUndoClick = () => { 34 | dispatch(ActionCreators.undo()); 35 | }; 36 | const handleRedoClick = () => { 37 | dispatch(ActionCreators.redo()); 38 | }; 39 | 40 | return 41 | 42 | 43 |
44 | 45 | GPT-4 API Playground 46 | 47 |
48 |
49 | 50 |
51 |
52 | 53 | 54 |
55 |
56 | window.open('https://github.com/shawwn/gpt4', '_blank')}> 57 | 58 | 59 |
60 |
61 |
62 |
63 | } -------------------------------------------------------------------------------- /src/components/dialogs/TemplateDialog.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | Button, 3 | Dialog, 4 | DialogActions, 5 | DialogContent, 6 | DialogTitle, 7 | List, 8 | ListItem, 9 | ListItemText, 10 | ListSubheader, 11 | } from "@material-ui/core"; 12 | import React from "react"; 13 | import {useDispatch, useSelector} from "react-redux"; 14 | import { 15 | cleanExampleList, 16 | loadTemplate, 17 | selectTemplateDialogVisible, 18 | toggleTemplateDialog 19 | } from "../../slices/editorSlice"; 20 | import getTemplateGroups, {Template} from "../../libs/templatesLibrary"; 21 | import {makeStyles, Theme} from "@material-ui/core/styles"; 22 | 23 | const useStyles = makeStyles((theme: Theme) => ({ 24 | templateDialog: { 25 | minWidth: '50vw', 26 | }, 27 | templateGroupHeader: { 28 | backgroundColor: theme.palette.background.paper, 29 | } 30 | })); 31 | 32 | export default function TemplateDialog() { 33 | const dispatch = useDispatch(); 34 | const classes = useStyles(); 35 | 36 | const templateDialogOpen = useSelector(selectTemplateDialogVisible); 37 | const handleTemplateDialogClose = () => { 38 | dispatch(toggleTemplateDialog(false)); 39 | }; 40 | 41 | const templateGroups = getTemplateGroups(); 42 | const handleLoadTemplate = (template: Template) => () => { 43 | dispatch(loadTemplate(template.actionPayload)) 44 | dispatch(cleanExampleList()); 45 | handleTemplateDialogClose(); 46 | }; 47 | 48 | return 53 | Load Template 54 | 57 | {templateGroups.map((templateGroup, ind) => ( 58 |
59 | {templateGroup.name}}> 60 | {templateGroup.templates.map(template => ( 61 | {template.name} 63 | ))} 64 | 65 |
66 | ))} 67 | 68 |
69 | 70 | 73 | 74 |
; 75 | } -------------------------------------------------------------------------------- /src/components/basic/BasicTab.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {useDispatch, useSelector} from "react-redux"; 3 | import { 4 | fetchBasicOutputAsync, 5 | selectBasicLoading, 6 | selectBasicOutput, 7 | } from "../../slices/editorSlice"; 8 | import {Box, Button, Card, CardContent, TextField, Typography} from "@material-ui/core"; 9 | import {useStyles} from '../ModeTabs'; 10 | 11 | export default function BasicTab() { 12 | const dispatch = useDispatch(); 13 | 14 | const styles = useStyles(); 15 | 16 | const fetchOutputs = () => { 17 | dispatch(fetchBasicOutputAsync()); 18 | }; 19 | 20 | const basicOutput = useSelector(selectBasicOutput); 21 | const basicLoading = useSelector(selectBasicLoading); 22 | 23 | return ( 24 | 25 | 26 | 27 | 28 | Simple 29 | This is a basic tool to explore the general idea of GPT-4. GPT-4 will try to continue text you wrote in the prompt field and will display the result in the field below. 30 | 31 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 59 | 60 | 61 | 62 | 63 | ); 64 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Prompts AI 2 | 3 | [Prompts AI](https://prompts.ai/) is an advanced [GPT-3](https://en.wikipedia.org/wiki/GPT-3) playground. 4 | 5 | It has two main goals: 6 | 1) Help first-time GPT-3 users to discover capabilities, strengths 7 | and weaknesses of the technology. 8 | 2) Help developers to experiment with prompt engineering by optimizing 9 | the product for concrete use cases such as creative writing, classification, 10 | chat bots and others. 11 | 12 | ### Install 13 | 14 | ```shell script 15 | yarn install 16 | yarn start 17 | ``` 18 | 19 | Note that each app user has to provide their own API key from the [OpenAI console](https://beta.openai.com/). 20 | "Bring your own key" is an important concept enforced to prevent API misuse. 21 | 22 | ### Features 23 | 24 | By use case: 25 | * Examples: run the same prompt on multiple examples of data. Designed for 26 | classification and transformation tasks. 27 | * Variations: generate multiple completions for a prompt and store them in one list, 28 | compare variations by parameters and initial prompts. 29 | Designed for creative writing tasks. 30 | * Conversations: chat-like interface to experiment with conversational bots. 31 | It also allows to maintain multiple conversations at the same time. 32 | 33 | Common: 34 | * Workspaces to quickly change between and experiment with multiple prompts. 35 | State in the local storage. Download/upload prompt with parameters and examples 36 | as file. 37 | * Code generators for Python, Node.js, Javascript, shell. 38 | * Undo/redo for parameters and prompts. 39 | * Shortcuts. 40 | * Templates. 41 | 42 | ``` 43 | Shortcuts 44 | === 45 | 46 | Export the workspace to a file: Ctrl+S, Cmd+S 47 | Run: Ctrl+Enter, Cmd+Enter 48 | 49 | Switch between modes: Ctrl+1-4 50 | (1 - simple, 2 - examples, 3 - variations, 4 - conversations) 51 | ``` 52 | 53 | ### Roadmap 54 | 55 | - Minor UX changes (line break symbols instead of "\n", log probs etc) 56 | - Authorization and collaborative features 57 | - Community features to share and discover prompts 58 | 59 | ### Contributing 60 | 61 | The project is in a very early stage and still shaping its form. 62 | It also has some amount of technical debt (for example, `editorSlice.ts`). 63 | However, contributions are welcome. 64 | 65 | Until we have a formed contribution process, if you need any help 66 | to find something to work on and/or make sure your contribution will be merged to the product, 67 | reach out to me at seva@zhidkoff.com. 68 | 69 | ### Contributors 70 | 71 | Sorted by the date of a first contribution. Feel free to add yourselves while making 72 | your first contribution! 73 | 74 | - [Seva Zhidkov](https://github.com/sevazhidkov) 75 | - [Ilya Penyaev](http://github.com/penyaev) 76 | -------------------------------------------------------------------------------- /src/components/examples/ExamplesTab.tsx: -------------------------------------------------------------------------------- 1 | import React, {useEffect} from 'react'; 2 | import {useDispatch, useSelector} from "react-redux"; 3 | import ExampleCollection from './ExampleCollection'; 4 | import { 5 | cleanExampleList, 6 | selectExamplePreviousOutputsStatus, 7 | updateExamplePreviousOutputsStatus 8 | } from "../../slices/editorSlice"; 9 | import {Box, Typography, Card, CardContent, Grid, FormControlLabel, Switch} from "@material-ui/core"; 10 | import RunExamplesButton from "./RunExamplesButton"; 11 | import { useStyles } from '../ModeTabs'; 12 | 13 | // cleanExampleList 14 | 15 | export default function ExamplesTab() { 16 | const dispatch = useDispatch(); 17 | 18 | const showPreviousOutputs = useSelector(selectExamplePreviousOutputsStatus); 19 | const handlePreviousOutputsSwitchChange = (event: React.ChangeEvent<{}>, value: boolean) => { 20 | dispatch(updateExamplePreviousOutputsStatus(value)); 21 | } 22 | 23 | const styles = useStyles(); 24 | useEffect(() => { 25 | dispatch(cleanExampleList()); 26 | }) 27 | 28 | return ( 29 | 30 | 31 | 32 | 33 | Multiple Examples 34 | This is a tool to quickly run your prompt on multiple examples. 35 | You can use it for text transformation and classification tasks. Use "{"{example}"}" key in the prompt and the editor 36 | will replace it with each of the following examples. The tool is useful to understand how changing a prompt and parameters 37 | will impact generated results. 38 | 39 | 40 | 41 | 42 | 43 | 44 | } 48 | label="Show previous outputs" 49 | /> 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | ); 58 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port 105 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 106 | 107 | # dependencies 108 | /node_modules 109 | /.pnp 110 | .pnp.js 111 | 112 | # testing 113 | /coverage 114 | 115 | # production 116 | /build 117 | 118 | # misc 119 | .DS_Store 120 | .env.local 121 | .env.development.local 122 | .env.test.local 123 | .env.production.local 124 | 125 | npm-debug.log* 126 | yarn-debug.log* 127 | yarn-error.log* 128 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 129 | 130 | # dependencies 131 | /node_modules 132 | /.pnp 133 | .pnp.js 134 | 135 | # testing 136 | /coverage 137 | 138 | # production 139 | /build 140 | 141 | # misc 142 | .DS_Store 143 | .env.local 144 | .env.development.local 145 | .env.test.local 146 | .env.production.local 147 | 148 | npm-debug.log* 149 | yarn-debug.log* 150 | yarn-error.log* 151 | 152 | .vercel 153 | -------------------------------------------------------------------------------- /src/components/conversations/Input.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import {IconButton, InputAdornment, TextField} from "@material-ui/core"; 3 | import SendIcon from "@material-ui/icons/Send"; 4 | import {useDispatch, useSelector} from "react-redux"; 5 | import {RootState} from "../../store"; 6 | import { 7 | normalizeConversations, 8 | sendMessageInConversationAsync, 9 | updateConversationInputValue 10 | } from "../../slices/editorSlice"; 11 | 12 | interface Props { 13 | conversationId: string; 14 | afterSend: () => void, 15 | } 16 | 17 | export default function Input(props: Props) { 18 | const dispatch = useDispatch(); 19 | 20 | const inputValue = useSelector( 21 | (state: RootState) => { 22 | const workspace = state.editor.present.workspaces.find(w => w.id === state.editor.present.currentWorkspaceId)!; 23 | return workspace.conversations.find(c => c.id === props.conversationId)!.inputValue; 24 | } 25 | ); 26 | const hasStarted = useSelector( 27 | (state: RootState) => { 28 | const workspace = state.editor.present.workspaces.find(w => w.id === state.editor.present.currentWorkspaceId)!; 29 | return workspace.conversations.find(c => c.id === props.conversationId)!.parts.some(c => c.submitted); 30 | } 31 | ); 32 | const onSend = () => { 33 | dispatch(sendMessageInConversationAsync(props.conversationId)); 34 | props.afterSend(); 35 | dispatch(normalizeConversations()); 36 | } 37 | 38 | const onInputChange = (event: React.FormEvent) => { 39 | dispatch(updateConversationInputValue({conversationId: props.conversationId, inputValue: event.currentTarget.value})); 40 | } 41 | return ) => { 50 | if (event.metaKey && event.key === 'Enter') { 51 | onSend(); 52 | } 53 | }} 54 | onKeyUp={(event: React.KeyboardEvent) => { 55 | if (event.ctrlKey && event.key === 'Enter') { 56 | onSend(); 57 | } 58 | }} 59 | variant={'outlined'} 60 | fullWidth={true} 61 | InputProps={{ 62 | endAdornment: ( 63 | 64 | 65 | 66 | ) 67 | }} 68 | />; 69 | } -------------------------------------------------------------------------------- /src/components/variations/VariationsTab.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { useSelector, useDispatch } from "react-redux"; 3 | import {Box, Card, CardContent, Slider, Typography, Button, Grid, Switch, FormControlLabel} from "@material-ui/core"; 4 | import {useStyles} from "../ModeTabs"; 5 | import FetchVariationsButton from "./FetchVariationsButton"; 6 | import VariationsCollection from "./VariationsCollection"; 7 | import { 8 | editMaxVariations, 9 | selectMaxVariations, 10 | cleanVariations, 11 | selectShowPromptForVariations, 12 | updateShowPromptForVariations, 13 | } from "../../slices/editorSlice"; 14 | 15 | export default function VariationsTab() { 16 | const styles = useStyles(); 17 | const dispatch = useDispatch(); 18 | 19 | const maxCompletions = useSelector(selectMaxVariations); 20 | const handleMaxVariationsChange = (event: React.ChangeEvent<{}>, value: number | number[]) => { 21 | dispatch(editMaxVariations(value as number)); 22 | } 23 | 24 | const showPrompt = useSelector(selectShowPromptForVariations); 25 | const handlePromptSwitchChange = (event: React.ChangeEvent<{}>, value: boolean) => { 26 | dispatch(updateShowPromptForVariations(value)); 27 | } 28 | 29 | return 30 | 31 | 32 | 33 | Variations 34 | 35 | This is a tool to generate multiple completions from the same prompt. Use it to explore the variety 36 | of GPT-4 completions and impact of parameters on them. If you like a completion, add it to the prompt 37 | and GPT-4 will generate more similar completions. 38 | 39 | 40 | 41 | How many samples to generate (impacts processing time): 42 | 43 | 60 | 61 | 62 | 63 | 64 | 67 | 68 | 69 | } 72 | label="Show prompt" 73 | /> 74 | 75 | 76 | 77 | 78 | 79 | ; 80 | } -------------------------------------------------------------------------------- /src/components/ModeTabs.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {Grid, Hidden, AppBar, Tabs, Tab, Typography, Box} from '@material-ui/core'; 3 | import { makeStyles, Theme } from '@material-ui/core/styles'; 4 | import ExamplesTab from "./examples/ExamplesTab"; 5 | import VariationsTab from './variations/VariationsTab'; 6 | import {useDispatch, useSelector} from "react-redux"; 7 | import {selectTabIndex, updateTabIndex, TabIndex} from "../slices/editorSlice"; 8 | import CodeGeneratorButton from './CodeGeneratorButton'; 9 | import ConversationsTab from './conversations/ConversationsTab'; 10 | import BasicTab from "./basic/BasicTab"; 11 | 12 | interface TabPanelProps { 13 | children?: React.ReactNode; 14 | index: any; 15 | value: any; 16 | } 17 | 18 | function TabPanel(props: TabPanelProps) { 19 | const { children, value, index, ...other } = props; 20 | 21 | return ( 22 | 35 | ); 36 | } 37 | 38 | function a11yProps(index: any) { 39 | return { 40 | id: `simple-tab-${index}`, 41 | 'aria-controls': `simple-tabpanel-${index}`, 42 | }; 43 | } 44 | 45 | export const useStyles = makeStyles((theme: Theme) => ({ 46 | root: { 47 | flexGrow: 1, 48 | backgroundColor: theme.palette.background.paper, 49 | }, 50 | instructionCard: { 51 | backgroundColor: theme.palette.background.default 52 | }, 53 | additionalItemsGridItem: { 54 | marginRight: "10px", 55 | } 56 | })); 57 | 58 | export default function ModeTabs() { 59 | const dispatch = useDispatch(); 60 | const classes = useStyles(); 61 | const tabIndex = useSelector(selectTabIndex); 62 | 63 | const handleTabIndexChange = (event: React.ChangeEvent<{}>, newValue: number) => { 64 | dispatch(updateTabIndex(newValue)); 65 | }; 66 | 67 | return ( 68 |
69 | 70 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 |
104 | ); 105 | } -------------------------------------------------------------------------------- /src/components/examples/Example.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {useDispatch, useSelector} from "react-redux"; 3 | import { 4 | editExample, 5 | deleteExample, 6 | cleanExampleList, 7 | selectExamplePreviousOutputsStatus 8 | } from "../../slices/editorSlice"; 9 | import {TextField, Card, CardContent, Box, CircularProgress, Grid, IconButton} from "@material-ui/core"; 10 | import {Delete} from "@material-ui/icons"; 11 | import {makeStyles, Theme} from "@material-ui/core/styles"; 12 | 13 | interface Props { 14 | ind: number; 15 | isLast: boolean; 16 | id: string; 17 | text: string; 18 | isLoading: boolean; 19 | output?: string; 20 | previousOutput?: string; 21 | } 22 | 23 | const useStyles = makeStyles((theme: Theme) => ({ 24 | card: { 25 | backgroundColor: theme.palette.background.default, 26 | } 27 | })); 28 | 29 | function Example (props: Props) { 30 | const dispatch = useDispatch(); 31 | const styles = useStyles(); 32 | const showPreviousOutput = useSelector(selectExamplePreviousOutputsStatus); 33 | const handleInputChange = (event: React.FormEvent) => { 34 | dispatch(editExample({id: props.id, text: event.currentTarget.value})); 35 | dispatch(cleanExampleList()); 36 | } 37 | 38 | return ( 39 | 40 | 41 | 48 | 49 | 50 | 59 | 60 | 61 | 71 | 72 | {(showPreviousOutput && ( 73 | 74 | 84 | 85 | ))} 86 | 87 | 88 | 89 | {props.isLoading && } 90 | 91 | {!props.isLoading && !props.isLast && { 92 | dispatch(deleteExample(props.id)); 93 | dispatch(cleanExampleList()); 94 | }}> 95 | 96 | } 97 | 98 | 99 | 100 | 101 | 102 | 103 | ); 104 | } 105 | 106 | export default Example; -------------------------------------------------------------------------------- /src/components/CodeGeneratorButton.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Button from '@material-ui/core/Button'; 3 | import Dialog, { DialogProps } from '@material-ui/core/Dialog'; 4 | import DialogContent from '@material-ui/core/DialogContent'; 5 | import DialogContentText from '@material-ui/core/DialogContentText'; 6 | import DialogTitle from '@material-ui/core/DialogTitle'; 7 | import generateCodeExamples from '../libs/codeGenerator'; 8 | import {useSelector} from "react-redux"; 9 | import {selectExamples, selectTabIndex, selectCompletionParameters} from "../slices/editorSlice"; 10 | import { Box, Theme, IconButton, FormControl, Select } from '@material-ui/core'; 11 | import CloseIcon from '@material-ui/icons/Close'; 12 | import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; 13 | import {makeStyles} from "@material-ui/core/styles"; 14 | 15 | const useStyles = makeStyles((theme: Theme) => ({ 16 | closeButton: { 17 | position: 'absolute', 18 | right: theme.spacing(1), 19 | top: theme.spacing(1), 20 | color: theme.palette.grey[500], 21 | }, 22 | fullWidth: { 23 | width: '100%', 24 | } 25 | })); 26 | 27 | 28 | export default function CodeGeneratorButton() { 29 | const classes = useStyles(); 30 | 31 | const completionParameters = useSelector(selectCompletionParameters); 32 | const tabIndex = useSelector(selectTabIndex); 33 | const examples = useSelector(selectExamples); 34 | 35 | const [open, setOpen] = React.useState(false); 36 | const [scroll, setScroll] = React.useState('paper'); 37 | 38 | let defaultExampleId; 39 | const codeExamples = generateCodeExamples(completionParameters, tabIndex, examples); 40 | if (codeExamples.length === 0) { 41 | defaultExampleId = ''; 42 | } else { 43 | defaultExampleId = codeExamples[0].id; 44 | } 45 | 46 | const [selectedExample, setExample] = React.useState(defaultExampleId); 47 | const handleSelectChange = (event: React.ChangeEvent<{ value: unknown }>) => { 48 | setExample(event.target.value as string); 49 | } 50 | 51 | const handleClickOpen = (scrollType: DialogProps['scroll']) => () => { 52 | setOpen(true); 53 | setScroll(scrollType); 54 | }; 55 | 56 | const handleClose = () => { 57 | setOpen(false); 58 | }; 59 | 60 | const descriptionElementRef = React.useRef(null); 61 | React.useEffect(() => { 62 | if (open) { 63 | const { current: descriptionElement } = descriptionElementRef; 64 | if (descriptionElement !== null) { 65 | descriptionElement.focus(); 66 | } 67 | } 68 | }, [open]); 69 | 70 | return ( 71 |
72 | 73 | 80 | 81 | Code Generator 82 | 83 | 84 | 85 | 86 | 87 | 92 | 93 | 103 | 104 | 105 | {generateCodeExamples(completionParameters, tabIndex, examples) 106 | .filter((codeExample) => codeExample.id === selectedExample) 107 | .map((codeExample) => ( 108 | 109 | 110 | {codeExample.text} 111 | 112 | 113 | ))} 114 | 115 | 116 | 117 |
118 | ); 119 | } -------------------------------------------------------------------------------- /src/components/SharePopup.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import DialogTitle from "@material-ui/core/DialogTitle"; 3 | import { 4 | Box, 5 | FormControlLabel, 6 | IconButton, 7 | Theme, 8 | Typography, 9 | FormGroup, 10 | Switch, 11 | Button, CircularProgress, TextField 12 | } from "@material-ui/core"; 13 | import CloseIcon from "@material-ui/icons/Close"; 14 | import DialogContent from "@material-ui/core/DialogContent"; 15 | import Dialog from "@material-ui/core/Dialog"; 16 | import {makeStyles} from "@material-ui/core/styles"; 17 | import { 18 | selectCompletionParameters, selectExamples, 19 | } from "../slices/editorSlice"; 20 | import SharedPrompt from '../resources/SharedPrompt'; 21 | import {useSelector} from "react-redux"; 22 | import {green} from "@material-ui/core/colors"; 23 | 24 | const useStyles = makeStyles((theme: Theme) => ({ 25 | closeButton: { 26 | position: 'absolute', 27 | right: theme.spacing(1), 28 | top: theme.spacing(1), 29 | color: theme.palette.grey[500], 30 | }, 31 | buttonProgress: { 32 | color: green[500], 33 | position: 'absolute', 34 | top: '50%', 35 | left: '50%', 36 | marginTop: -12, 37 | marginLeft: -12, 38 | }, 39 | })); 40 | 41 | 42 | export default function SharePopup() { 43 | const classes = useStyles(); 44 | 45 | const [open, setOpen] = React.useState(true); 46 | const [loading, setLoading] = React.useState(false); 47 | const [link, setLink] = React.useState(undefined); 48 | const [includeExamples, setIncludeExamples] = React.useState(true); 49 | 50 | const completionParameters = useSelector(selectCompletionParameters); 51 | const examples = useSelector(selectExamples); 52 | 53 | const handleClose = () => { 54 | setLink(undefined); 55 | setOpen(false); 56 | }; 57 | 58 | return 64 | 65 | Share Prompt 66 | 67 | 68 | 69 | 70 | 71 | 75 |
{ 76 | event.preventDefault(); 77 | setLoading(true); 78 | SharedPrompt.create({ 79 | engine: completionParameters.engine, 80 | maxTokens: completionParameters.maxTokens, 81 | stop: completionParameters.stop, 82 | prompt: completionParameters.prompt, 83 | temperature: completionParameters.temperature, 84 | topP: completionParameters.topP, 85 | presencePenalty: completionParameters.presencePenalty, 86 | frequencyPenalty: completionParameters.frequencyPenalty, 87 | examples: (includeExamples ? examples : undefined), 88 | }).then(onFulfilled => { 89 | setLoading(false); 90 | setLink(onFulfilled.id) 91 | }); 92 | }}> 93 | If you edit your prompt after sharing a link, the shared prompt won't change. 94 | 95 | { link && ( 96 | 97 | )} 98 | { !link && ( 99 | 100 | 101 | , value: boolean) => { 103 | setIncludeExamples(value); 104 | }} name="includeExamples" />} 105 | label="Include Examples" 106 | /> 107 | 108 | 114 | 115 | )} 116 | 117 |
118 | 119 |
120 |
121 |
; 122 | } -------------------------------------------------------------------------------- /src/migrations.ts: -------------------------------------------------------------------------------- 1 | import uniqid from "uniqid"; 2 | 3 | export const migrations = { 4 | 0: (state: any) => { 5 | return { 6 | ...state, 7 | editor: { 8 | ...state.editor, 9 | maxCreativeCompletions: 10, 10 | creativeCompletions: [] 11 | } 12 | }; 13 | }, 14 | 1: (state: any) => { 15 | return { 16 | ...state, 17 | editor: { 18 | ...state.editor, 19 | showPromptForCreativeCompletions: true, 20 | } 21 | }; 22 | }, 23 | 2: (state: any) => { 24 | return { 25 | ...state, 26 | editor: { 27 | ...state.editor, 28 | tabIndex: 0 29 | } 30 | }; 31 | }, 32 | 3: (state: any) => { 33 | return { 34 | ...state, 35 | editor: { 36 | ...state.editor, 37 | stopSymbols: [] 38 | } 39 | }; 40 | }, 41 | 4: (state: any) => { 42 | return { 43 | ...state, 44 | editor: { 45 | ...state.editor, 46 | topP: 1, 47 | frequencyPenalty: 0, 48 | presencePenalty: 0, 49 | } 50 | }; 51 | }, 52 | 5: (state: any) => { 53 | return { 54 | ...state, 55 | editor: { 56 | ...state.editor, 57 | creativeCompletions: state.creativeCompletions.map((completion: any) => ({ 58 | ...completion, 59 | topP: 1, 60 | frequencyPenalty: 0, 61 | presencePenalty: 0, 62 | })) 63 | } 64 | }; 65 | }, 66 | 6: (state: any) => { 67 | return { 68 | ...state, 69 | editor: { 70 | ...state.editor, 71 | showExamplePreviousOutputs: false 72 | } 73 | }; 74 | }, 75 | 7: (state: any) => { 76 | return { 77 | ...state, 78 | editor: { 79 | past: [], 80 | future: [], 81 | present: {...state.editor} 82 | } 83 | }; 84 | }, 85 | 8: (state: any) => { 86 | return { 87 | ...state, 88 | editor: { 89 | ...state.editor, 90 | present: { 91 | ...state.editor.present, 92 | modelName: 'davinci' 93 | } 94 | } 95 | } 96 | }, 97 | 9: (state: any) => { 98 | return { 99 | ...state, 100 | editor: { 101 | ...state.editor, 102 | present: { 103 | ...state.editor.present, 104 | conversations: [] 105 | } 106 | } 107 | } 108 | }, 109 | 110 | 10: (state: any) => { 111 | return { 112 | ...state, 113 | editor: { 114 | ...state.editor, 115 | present: { 116 | ...state.editor.present, 117 | loadingVariations: state.editor.present.loadingCreativeCompletions, 118 | variations: state.editor.present.creativeCompletions, 119 | maxVariations: state.editor.present.maxCreativeCompletions, 120 | showPromptForVariations: state.editor.present.showPromptForCreativeCompletions 121 | } 122 | } 123 | } 124 | }, 125 | 126 | 12: (state: any) => { 127 | return { 128 | ...state, 129 | editor: { 130 | ...state.editor, 131 | present: { 132 | ...state.editor.present, 133 | currentWorkspaceId: 'first_workspace', 134 | workspaces: [{ 135 | id: 'first_workspace', 136 | prompt: "Click Explore Templates -> Song Generation for a fun example!", 137 | // prompt: "Input: Anna and Mike is going skiing.\n" + 138 | // "Output: Anna and Mike are going skiing.\n" + 139 | // "Input: Anna and Pat are married; he has been together for 20 years.\n" + 140 | // "Output: Anna and Pat are married; they have been together for 20 years.\n" + 141 | // "Input: I walk to the store and I bought milk.\n" + 142 | // "Output: I walked to the store and I bought milk.\n" + 143 | // "Input: {example}\n" + 144 | // "Output:", 145 | modelName: 'davinci', 146 | temperature: 0.8, 147 | topP: 1, 148 | frequencyPenalty: 0, 149 | presencePenalty: 0, 150 | // stopSymbols: ["\\n"], 151 | stopSymbols: [], 152 | maxTokens: 80, 153 | tabIndex: 0, 154 | 155 | showExamplePreviousOutputs: false, 156 | examples: [ 157 | {id: uniqid("input_"), text: "We all eat the fish and then made dessert.", output: "We all ate the fish and then made dessert.", isLoading: false}, 158 | {id: uniqid("input_"), text: "I like ski every day.", output: "I like skiing every day.", isLoading: false}, 159 | ], 160 | 161 | loadingVariations: false, 162 | variations: [], 163 | maxVariations: 2, 164 | showPromptForVariations: true, 165 | 166 | conversations: [], 167 | }], 168 | } 169 | } 170 | } 171 | }, 172 | 173 | 13: (state: any) => { 174 | return { 175 | ...state, 176 | editor: { 177 | ...state.editor, 178 | present: { 179 | ...state.editor.present, 180 | workspaces: state.editor.present.workspaces.map((workspace: any) => ({ 181 | ...workspace, 182 | name: 'Draft #1' 183 | })), 184 | } 185 | } 186 | } 187 | }, 188 | 189 | 14: (state: any) => { 190 | return { 191 | ...state, 192 | editor: { 193 | ...state.editor, 194 | present: { 195 | ...state.editor.present, 196 | workspaces: state.editor.present.workspaces.map((workspace: any) => ({ 197 | ...workspace, 198 | basic: { 199 | output: '', 200 | loading: false, 201 | } 202 | })), 203 | } 204 | } 205 | } 206 | }, 207 | 208 | 15: (state: any) => { 209 | return { 210 | ...state, 211 | editor: { 212 | ...state.editor, 213 | present: { 214 | ...state.editor.present, 215 | editableWorkspaceName: state.editor.present.workspaces.find((w: any) => w.id === state.editor.present.currentWorkspaceId).name 216 | } 217 | } 218 | } 219 | }, 220 | }; 221 | 222 | export const currentVersion = 15; 223 | -------------------------------------------------------------------------------- /src/components/conversations/Conversation.tsx: -------------------------------------------------------------------------------- 1 | import React, {createRef, useEffect} from "react"; 2 | import {Card, CardActions, CardContent, Typography, TextField, Grid, Box, Paper, AccordionDetails, AccordionSummary, Accordion, IconButton} from "@material-ui/core"; 3 | import {makeStyles, Theme} from "@material-ui/core/styles"; 4 | import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; 5 | import { RootState } from "../../store"; 6 | import {useDispatch, useSelector} from "react-redux"; 7 | import { 8 | ConversationPartSource, 9 | selectPrompt, 10 | deleteConversation, selectCompletionParameters, updateConversationRestartSequence, updateConversationStartSequence, 11 | } from "../../slices/editorSlice"; 12 | import {Delete} from "@material-ui/icons"; 13 | import CompletionParameters from "./CompletionParameters"; 14 | import Input from "./Input"; 15 | 16 | interface Props { 17 | id: string; 18 | ind: number; 19 | } 20 | 21 | const useStyles = makeStyles((theme: Theme) => ({ 22 | card: { 23 | backgroundColor: theme.palette.background.default, 24 | width: '100%', 25 | }, 26 | settingField: { 27 | minWidth: '250px', 28 | }, 29 | generatedText: { 30 | whiteSpace: 'pre-line', 31 | display: 'inline', 32 | fontWeight: 800, 33 | }, 34 | promptedText: { 35 | whiteSpace: 'pre-line', 36 | display: 'inline', 37 | fontWeight: 400, 38 | }, 39 | conversationBox: { 40 | minHeight: '400px', 41 | maxHeight: '400px', 42 | overflowY: 'scroll' 43 | }, 44 | responseInput: { 45 | width: '100%' 46 | } 47 | })); 48 | 49 | export default function Conversation(props: Props) { 50 | const styles = useStyles(); 51 | const dispatch = useDispatch(); 52 | const prompt = useSelector(selectPrompt); 53 | const globalCompletionParameters = useSelector(selectCompletionParameters); 54 | const conversation = useSelector((state: RootState) => { 55 | const workspace = state.editor.present.workspaces.find(w => w.id === state.editor.present.currentWorkspaceId)!; 56 | return workspace.conversations.find(c => c.id === props.id)!; 57 | }); 58 | 59 | const hasStarted = conversation.parts.some(c => c.submitted); 60 | 61 | useEffect(() => { 62 | conversationBottom.current!.scrollTop = conversationBottom.current!.scrollHeight; 63 | }); 64 | 65 | const conversationBottom = createRef(); 66 | 67 | return 68 | 69 | 70 | 71 | {!hasStarted && ( 72 | "New Conversation" 73 | )} 74 | {hasStarted && ( 75 | 76 | Conversation #{props.ind}
77 | The prompt and parameters are locked. 78 |
79 | )} 80 |
81 | 82 | {hasStarted && ( 83 | { 84 | dispatch(deleteConversation(props.id)); 85 | }}> 86 | 87 | 88 | )} 89 | 90 |
91 | 92 | 93 | 94 | {hasStarted && (<> 95 | {conversation.initialPrompt} 96 | {conversation.parts.map(part => (<> 97 | {part.source === ConversationPartSource.gpt && {part.text}} 98 | {part.source === ConversationPartSource.user && {part.text}} 99 | ))} 100 | )} 101 | {!hasStarted && (<> 102 | {prompt} 103 | {conversation.restartSequence} 104 | )} 105 |
106 | 107 | 108 | 109 | 110 | { 111 | conversationBottom.current!.scrollTop = conversationBottom.current!.scrollHeight; 112 | }}/> 113 | 114 | 115 | 116 | } 118 | aria-controls="panel1a-content" 119 | id="panel1a-header" 120 | > 121 | Parameters 122 | 123 | 124 | 125 | 126 | 127 | ) => { 130 | dispatch(updateConversationRestartSequence({ 131 | conversationId: props.id, 132 | restartSequence: event.currentTarget.value.split('\\n').join('\n') 133 | })); 134 | }} 135 | className={styles.settingField} 136 | label={'Before User Input'} 137 | variant={'outlined'} 138 | /> 139 | 140 | 141 | ) => { 144 | dispatch(updateConversationStartSequence({ 145 | conversationId: props.id, 146 | startSequence: event.currentTarget.value.split('\\n').join('\n') 147 | })); 148 | }} 149 | className={styles.settingField} 150 | label={'Before GPT-4 Completion'} 151 | variant={'outlined'} 152 | /> 153 | 154 | 155 | 156 | {conversation.completionParams === undefined && ( 157 | 158 | )} 159 | {conversation.completionParams !== undefined && ( 160 | 161 | )} 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | ; 172 | } 173 | -------------------------------------------------------------------------------- /src/libs/codeGenerator.ts: -------------------------------------------------------------------------------- 1 | import {TabIndex, Example, CompletionParameters} from "../slices/editorSlice"; 2 | 3 | //const HOST = `https://api.openai.com`; 4 | //const HOST = `http://localhost:9000`; 5 | //const HOST = `http://api.gpt4.org:9000`; 6 | const HOST = `https://api.gpt4.org`; 7 | 8 | interface CodeExample { 9 | id: string; 10 | name: string; 11 | text: string; 12 | language: string; 13 | } 14 | 15 | enum PythonOutputType { 16 | plain, 17 | stream, 18 | toxicity 19 | } 20 | 21 | export default function generateCodeExamples(completionParameters: CompletionParameters, tabIndex: TabIndex, 22 | examples: Array): Array { 23 | const exampleText = getFirstExampleOrPlaceholder(examples); 24 | return [ 25 | {id: '1', name: "Python", language: "python", text: generatePythonExample( 26 | completionParameters, tabIndex, exampleText, PythonOutputType.plain 27 | )}, 28 | {id: '2', name: "Python: Streaming", language: "python", text: generatePythonExample( 29 | completionParameters, tabIndex, exampleText, PythonOutputType.stream 30 | )}, 31 | {id: '3', name: "Node.js: Axios", language: "javascript", text: generateNodeJsExample( 32 | completionParameters, tabIndex, exampleText 33 | )}, 34 | {id: '4', name: "Typescript: Axios", language: "typescript", text: generateTypescriptExample( 35 | completionParameters, tabIndex, exampleText 36 | )}, 37 | {id: '5', name: "Bash", language: "bash", text: generateShellExample( 38 | completionParameters, tabIndex, exampleText 39 | )}, 40 | 41 | 42 | //{name: "Python: With Toxicity Check", text: generatePythonExample( 43 | // completionParameters, tabIndex, exampleText, PythonOutputType.toxicity 44 | // )}, 45 | 46 | // {name: "Javascript", text: generatePythonExampleWithStream(completionParameters, tabIndex)}, 47 | // {name: "Javascript: Client-side Streaming", text: generatePythonExampleWithStream(completionParameters, tabIndex)}, 48 | 49 | // {name: "Javascript: With Toxicity Check", text: generatePythonExampleWithStream(completionParameters, tabIndex)}, 50 | ]; 51 | } 52 | 53 | function generateNodeJsExample(parameters: CompletionParameters, tabIndex: TabIndex, exampleText: string) { 54 | switch (tabIndex) { 55 | case TabIndex.multipleExamples: { 56 | return `var axios = require('axios'); 57 | 58 | var example = ${formatJavascriptString(exampleText)}; 59 | var config = { 60 | method: 'post', 61 | url: '${HOST}/v1/engines/${parameters.engine}/completions', 62 | headers: { 63 | 'Content-Type': 'application/json', 64 | 'Authorization': 'Bearer ${parameters.apiKey}' 65 | }, 66 | data: { 67 | 'echo': true, 68 | 'prompt': ${formatJavascriptString(parameters.prompt)}.replace('{example}', example), 69 | 'max_tokens': ${parameters.maxTokens}, 70 | 'temperature': ${parameters.temperature}, 71 | 'top_p': ${parameters.topP}, 72 | 'n': 1, 73 | 'stop': ${formatStopSymbolsJavascriptStringOrStringList(parameters.stop)}, 74 | 'presence_penalty': ${parameters.presencePenalty}, 75 | 'frequency_penalty': ${parameters.frequencyPenalty}} 76 | }; 77 | 78 | axios(config) 79 | .then(function (response) { 80 | console.log(response.data); 81 | console.log(response.data['choices'][0]['text']); 82 | }) 83 | .catch(function (error) { 84 | console.log(error); 85 | }); 86 | `; 87 | } 88 | case TabIndex.basic: 89 | case TabIndex.conversations: 90 | case TabIndex.variations: { 91 | return `var axios = require('axios'); 92 | 93 | var config = { 94 | method: 'post', 95 | url: '${HOST}/v1/engines/${parameters.engine}/completions', 96 | headers: { 97 | 'Content-Type': 'application/json', 98 | 'Authorization': 'Bearer ${parameters.apiKey}' 99 | }, 100 | data: { 101 | 'echo': true, 102 | 'prompt': ${formatJavascriptString(parameters.prompt)}, 103 | 'max_tokens': ${parameters.maxTokens}, 104 | 'temperature': ${parameters.temperature}, 105 | 'top_p': ${parameters.topP}, 106 | 'n': 1, 107 | 'stop': ${formatStopSymbolsJavascriptStringOrStringList(parameters.stop)}, 108 | 'presence_penalty': ${parameters.presencePenalty}, 109 | 'frequency_penalty': ${parameters.frequencyPenalty}} 110 | }; 111 | 112 | axios(config) 113 | .then(function (response) { 114 | console.log(response.data); 115 | console.log(response.data['choices'][0]['text']); 116 | }) 117 | .catch(function (error) { 118 | console.log(error); 119 | }); 120 | `; 121 | } 122 | } 123 | 124 | return ''; 125 | } 126 | 127 | function generateTypescriptExample(parameters: CompletionParameters, tabIndex: TabIndex, exampleText: string) { 128 | switch (tabIndex) { 129 | case TabIndex.multipleExamples: { 130 | return `import axios from 'axios' 131 | 132 | const example = ${formatJavascriptString(exampleText)} 133 | 134 | axios({ 135 | method: 'post', 136 | url: '${HOST}/v1/engines/${parameters.engine}/completions', 137 | headers: { 138 | 'Content-Type': 'application/json', 139 | 'Authorization': 'Bearer ${parameters.apiKey}' 140 | }, 141 | data: { 142 | 'echo': true, 143 | 'prompt': ${formatJavascriptString(parameters.prompt)}.replace('{example}', example), 144 | 'max_tokens': ${parameters.maxTokens}, 145 | 'temperature': ${parameters.temperature}, 146 | 'top_p': ${parameters.topP}, 147 | 'n': 1, 148 | 'stop': ${formatStopSymbolsJavascriptStringOrStringList(parameters.stop)}, 149 | 'presence_penalty': ${parameters.presencePenalty}, 150 | 'frequency_penalty': ${parameters.frequencyPenalty}} 151 | }) 152 | .then(response => { 153 | console.log(response.data) 154 | console.log(response.data['choices'][0]['text']) 155 | }) 156 | .catch(error => { 157 | console.log(error) 158 | }); 159 | `; 160 | } 161 | case TabIndex.basic: 162 | case TabIndex.conversations: 163 | case TabIndex.variations: { 164 | return `import axios from 'axios' 165 | 166 | axios({ 167 | method: 'post', 168 | url: '${HOST}/v1/engines/${parameters.engine}/completions', 169 | headers: { 170 | 'Content-Type': 'application/json', 171 | 'Authorization': 'Bearer ${parameters.apiKey}' 172 | }, 173 | data: { 174 | 'echo': true, 175 | 'prompt': ${formatJavascriptString(parameters.prompt)}, 176 | 'max_tokens': ${parameters.maxTokens}, 177 | 'temperature': ${parameters.temperature}, 178 | 'top_p': ${parameters.topP}, 179 | 'n': 1, 180 | 'stop': ${formatStopSymbolsJavascriptStringOrStringList(parameters.stop)}, 181 | 'presence_penalty': ${parameters.presencePenalty}, 182 | 'frequency_penalty': ${parameters.frequencyPenalty}} 183 | }) 184 | .then(function (response) { 185 | console.log(response.data) 186 | console.log(response.data['choices'][0]['text']) 187 | }) 188 | .catch(function (error) { 189 | console.log(error) 190 | }); 191 | `; 192 | } 193 | } 194 | 195 | return ''; 196 | } 197 | 198 | function generateShellExample(parameters: CompletionParameters, tabIndex: TabIndex, exampleText: string) { 199 | switch (tabIndex) { 200 | case TabIndex.multipleExamples: { 201 | return `curl --location --request POST '${HOST}/v1/engines/davinci/completions' \\ 202 | --header 'Content-Type: application/json' \\ 203 | --header 'Authorization: Bearer ${parameters.apiKey}' \\ 204 | --data-raw '${replaceAllOccurrences(JSON.stringify({ 205 | 'echo': true, 206 | 'prompt': parameters.prompt.replace('{example}', exampleText), 207 | 'max_tokens': parameters.maxTokens, 208 | 'temperature': parameters.temperature, 209 | 'top_p': parameters.topP, 210 | 'n': 1, 211 | 'stop': formatStopSymbolsForShell(parameters.stop), 212 | 'presence_penalty': parameters.presencePenalty, 213 | 'frequency_penalty': parameters.frequencyPenalty 214 | }, null, 1), "'", "\\'")}'`; 215 | } 216 | case TabIndex.basic: 217 | case TabIndex.conversations: 218 | case TabIndex.variations: { 219 | return `curl --location --request POST '${HOST}/v1/engines/davinci/completions' \\ 220 | --header 'Content-Type: application/json' \\ 221 | --header 'Authorization: Bearer ${parameters.apiKey}' \\ 222 | --data-raw '${replaceAllOccurrences(JSON.stringify({ 223 | 'echo': true, 224 | 'prompt': parameters.prompt, 225 | 'max_tokens': parameters.maxTokens, 226 | 'temperature': parameters.temperature, 227 | 'top_p': parameters.topP, 228 | 'n': 1, 229 | 'stop': formatStopSymbolsForShell(parameters.stop), 230 | 'presence_penalty': parameters.presencePenalty, 231 | 'frequency_penalty': parameters.frequencyPenalty 232 | }, null, 1), "'", "\\'")}'`; 233 | } 234 | } 235 | 236 | return ''; 237 | } 238 | 239 | function generatePythonExample(parameters: CompletionParameters, tabIndex: TabIndex, exampleText: string, 240 | outputType: PythonOutputType) { 241 | let completionVariableName, additionalArguments, outputCode; 242 | switch (outputType) { 243 | case PythonOutputType.plain: { 244 | completionVariableName = 'completion'; 245 | additionalArguments = ''; 246 | outputCode = `choice = completion["choices"][0] 247 | print("[choice object]", choice) 248 | print("[choice text]", choice["text"])`; 249 | break; 250 | } 251 | case PythonOutputType.stream: { 252 | completionVariableName = 'parts'; 253 | additionalArguments = ` 254 | stream=True`; 255 | outputCode = `for completion in parts: 256 | choice = completion["choices"][0] 257 | print("[choice object]", choice) 258 | print("[choice text]", choice["text"])`; 259 | break; 260 | } 261 | case PythonOutputType.toxicity: { 262 | completionVariableName = 'completion'; 263 | additionalArguments = ''; 264 | outputCode = `choice = completion["choices"][0] 265 | print("[choice object]", choice) 266 | print("[choice text]", choice["text"]) 267 | 268 | filter_completion = openai.Completion.create( 269 | engine="davinci", 270 | max_tokens=1, 271 | prompt=f"<|endoftext|>{choice['text']}\\n--\\nLabel: ", 272 | temperature=0.0, 273 | top_p=0, 274 | ) 275 | filter_result = completion["choices"][0]["text"] 276 | if filter_result == "0": 277 | print("[content status] safe") 278 | if filter_result == "1": 279 | print("[content status] non-toxic warning") 280 | if filter_result == "2": 281 | print("[content status] toxic")`; 282 | break; 283 | } 284 | 285 | } 286 | switch (tabIndex) { 287 | case TabIndex.multipleExamples: { 288 | return `import openai 289 | openai.api_key = "${parameters.apiKey}" 290 | prompt = ${formatPythonString(parameters.prompt)} 291 | example = ${formatPythonString(exampleText)} 292 | ${completionVariableName} = openai.Completion.create( 293 | engine="${parameters.engine}", 294 | n=1, 295 | max_tokens=${parameters.maxTokens}, 296 | stop=${formatStopSymbolsPythonStringOrStringList(parameters.stop)}, 297 | prompt=prompt.replace("{example}", example), 298 | temperature=${parameters.temperature}, 299 | top_p=${parameters.topP}, 300 | presence_penalty=${parameters.presencePenalty}, 301 | frequency_penalty=${parameters.frequencyPenalty},${additionalArguments} 302 | ) 303 | ${outputCode} 304 | `; 305 | } 306 | case TabIndex.basic: 307 | case TabIndex.conversations: 308 | case TabIndex.variations: { 309 | return `import openai 310 | openai.api_key = "${parameters.apiKey}" 311 | ${completionVariableName} = openai.Completion.create( 312 | engine="${parameters.engine}", 313 | n=1, 314 | max_tokens=${parameters.maxTokens}, 315 | stop=${formatStopSymbolsPythonStringOrStringList(parameters.stop)}, 316 | prompt=${formatPythonString(parameters.prompt)}, 317 | temperature=${parameters.temperature}, 318 | top_p=${parameters.topP}, 319 | presence_penalty=${parameters.presencePenalty}, 320 | frequency_penalty=${parameters.frequencyPenalty}, 321 | echo=True,${additionalArguments} 322 | ) 323 | ${outputCode} 324 | `; 325 | } 326 | } 327 | 328 | return ''; 329 | } 330 | 331 | // Shell helpers 332 | 333 | function formatStopSymbolsForShell(value: Array | string) { 334 | if (value instanceof Array) { 335 | return value.map(formatStopSymbolStringForCode); 336 | } else { 337 | return formatStopSymbolStringForCode(value); 338 | } 339 | } 340 | 341 | // Javascript helpers 342 | 343 | function formatJavascriptString(value: string) { 344 | if (value.includes("\n")) { 345 | const formattedString = replaceAllOccurrences(value,'`', '\\`'); 346 | return `\`${formattedString}\``; 347 | } else { 348 | const formattedString = replaceAllOccurrences(value, "'", "\\'"); 349 | return `'${formattedString}'`; 350 | } 351 | } 352 | 353 | function formatStopSymbolsJavascriptStringOrStringList(value: Array | string) { 354 | if (value instanceof Array) { 355 | return formatJavascriptStringList(value.map(formatStopSymbolStringForCode)); 356 | } else { 357 | return formatJavascriptString(formatStopSymbolStringForCode(value)); 358 | } 359 | } 360 | 361 | function formatJavascriptStringList(value: Array) { 362 | return `[${value.map(value => `'${replaceAllOccurrences(value, "'", "\\'")}'`).join(', ')}]`; 363 | } 364 | 365 | // Python helpers 366 | 367 | function formatStopSymbolsPythonStringOrStringList(value: Array | string) { 368 | if (value instanceof Array) { 369 | return formatPythonStringList(value.map(formatStopSymbolStringForCode)); 370 | } else { 371 | return formatPythonString(formatStopSymbolStringForCode(value)); 372 | } 373 | } 374 | 375 | function formatPythonStringList(value: Array) { 376 | return `[${value.map(value => `"${value}"`).join(', ')}]`; 377 | } 378 | 379 | function formatPythonString(value: string) { 380 | if (value.includes("\n")) { 381 | const formattedString = replaceAllOccurrences(value, '"""', '\\"\\"\\"'); 382 | return `"""${formattedString}"""`; 383 | } else { 384 | const formattedString = replaceAllOccurrences(value, '"', '\\"'); 385 | return `"${formattedString}"`; 386 | } 387 | } 388 | 389 | // Common helpers 390 | 391 | function getFirstExampleOrPlaceholder(examples: Array): string { 392 | if (examples.length > 0 && examples[0].text.length > 0) { 393 | return examples[0].text; 394 | } 395 | return 'example'; 396 | } 397 | 398 | function formatStopSymbolStringForCode(value: string) { 399 | return `${replaceAllOccurrences(value, '\n', '\\n')}`; 400 | } 401 | 402 | function replaceAllOccurrences(value: string, replace_from: string, replace_to: string) { 403 | return value.split(replace_from).join(replace_to); 404 | } 405 | 406 | 407 | -------------------------------------------------------------------------------- /src/components/PromptEditor.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {useDispatch, useSelector} from "react-redux"; 3 | import { 4 | Typography, 5 | Slider, 6 | TextField, 7 | Grid, 8 | Tooltip, 9 | Card, 10 | CardContent, 11 | Select, 12 | Box 13 | } from "@material-ui/core"; 14 | import ChipInput from 'material-ui-chip-input' 15 | import { 16 | selectPrompt, 17 | editPrompt, 18 | selectTemperature, 19 | editTemperature, 20 | selectMaxTokens, 21 | editMaxTokens, 22 | selectStopSymbols, 23 | addStopSymbol, 24 | deleteStopSymbol, 25 | editTopP, 26 | editFrequencyPenalty, 27 | editPresencePenalty, 28 | selectTopP, 29 | selectFrequencyPenalty, selectPresencePenalty, selectModelName, editModelName 30 | } from "../slices/editorSlice"; 31 | import {makeStyles} from "@material-ui/styles"; 32 | import ModeTabs from "./ModeTabs"; 33 | import WorkspaceForm from "./WorkspaceForm"; 34 | 35 | const useStyles = makeStyles({ 36 | fullWidth: { 37 | width: '100%', 38 | }, 39 | }); 40 | 41 | export function PromptEditor() { 42 | const dispatch = useDispatch(); 43 | const styles = useStyles(); 44 | 45 | const prompt = useSelector(selectPrompt); 46 | const temperature = useSelector(selectTemperature); 47 | const topP = useSelector(selectTopP); 48 | const frequencyPenalty = useSelector(selectFrequencyPenalty); 49 | const presencePenalty = useSelector(selectPresencePenalty); 50 | const maxTokens = useSelector(selectMaxTokens); 51 | const stopSymbols = useSelector(selectStopSymbols); 52 | 53 | const availableModelNames = ['davinci', 'davinci-instruct-beta', 54 | 'curie', 'curie-instruct-beta', 55 | 'babbage', 56 | 'ada']; 57 | const modelName = useSelector(selectModelName); 58 | 59 | const handlePromptChange = (event: React.FormEvent) => { 60 | dispatch(editPrompt(event.currentTarget.value)); 61 | } 62 | const handleTemperatureChange = (event: React.ChangeEvent<{}>, value: number | number[]) => { 63 | dispatch(editTemperature(value as number)); 64 | } 65 | const handleTopPChange = (event: React.ChangeEvent<{}>, value: number | number[]) => { 66 | dispatch(editTopP(value as number)); 67 | } 68 | const handleFrequencyPenaltyChange = (event: React.ChangeEvent<{}>, value: number | number[]) => { 69 | dispatch(editFrequencyPenalty(value as number)); 70 | } 71 | const handlePresencePenaltyChange = (event: React.ChangeEvent<{}>, value: number | number[]) => { 72 | dispatch(editPresencePenalty(value as number)); 73 | } 74 | const handleMaxTokensChange = (event: React.ChangeEvent<{}>, value: number | number[]) => { 75 | dispatch(editMaxTokens(value as number)); 76 | } 77 | const handleModelNameChange = (event: any) => { 78 | dispatch(editModelName(event.target.value)); 79 | } 80 | 81 | return ( 82 |
83 | 90 | 91 | {/* 92 | 93 | 94 | 95 | 96 | 101 | 102 | 108 | 109 | 110 | 116 | 117 | 118 | 119 |
120 | 121 | 122 | 124 | ) => { 130 | dispatch(editApiKey(event.currentTarget.value)); 131 | }} 132 | inputProps={{ 133 | autoComplete: 'new-password', 134 | form: { 135 | autoComplete: 'off', 136 | }, 137 | }} 138 | className={styles.fullWidth} 139 | /> 140 | 141 | 142 | 143 |
144 |
145 |
*/} 146 | 147 | 148 | 149 | 150 | Workspace 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | Parameters 160 | 161 | 163 | 164 | Temperature: {temperature} 165 | 166 | 167 | 184 | 185 | Response length: {maxTokens} 186 | 187 | 204 | 205 | 206 | 207 | Stop sequences: 208 | 209 | 210 | dispatch(addStopSymbol(chip))} 213 | onDelete={(deletedChip) => dispatch(deleteStopSymbol(deletedChip))} 214 | onBeforeAdd={() => stopSymbols.length !== 4} 215 | newChipKeys={['Tab']} 216 | className={styles.fullWidth} 217 | /> 218 | 219 | 220 | 221 | 222 | Advanced parameters 223 | 224 | 225 | 226 | Top P: {topP} 227 | 228 | 229 | 246 | 247 | 248 | Frequency Penalty: {frequencyPenalty} 249 | 250 | 251 | 268 | 269 | 270 | Presence Penalty: {presencePenalty} 271 | 272 | 273 | 290 | 291 | Model name: 292 | 293 | 298 | 299 | 300 |
301 | 302 | 313 |
314 |
315 | 316 |
317 | 318 |
319 |
320 | ); 321 | } -------------------------------------------------------------------------------- /src/libs/templatesLibrary.ts: -------------------------------------------------------------------------------- 1 | import {uniqueId} from "lodash"; 2 | import {LoadTemplateActionPayload, TabIndex} from '../slices/editorSlice'; 3 | 4 | export interface Template { 5 | id: string; 6 | name: string; 7 | actionPayload: LoadTemplateActionPayload; 8 | } 9 | 10 | interface TemplateGroup { 11 | name: string; 12 | templates: Array