├── src ├── components │ ├── ChatGPTMainPage │ │ ├── index.ts │ │ ├── ChatGPTPage.tsx │ │ └── ChatGPTPage.test.tsx │ ├── ChatGptPlayground │ │ ├── index.ts │ │ ├── types.tsx │ │ ├── SystemPrompt.tsx │ │ ├── ChatGptPlayground.test.tsx │ │ ├── GettingStartedGuide.tsx │ │ ├── SettingsPannel.tsx │ │ ├── ChatGptPlayground.tsx │ │ ├── ChatPannel.tsx │ │ └── PlaygroundContext.tsx │ └── common │ │ ├── ButtonPanel.tsx │ │ └── styles.css ├── index.ts ├── setupTests.ts ├── routes.ts ├── plugin.test.ts ├── client │ ├── chatgpt.tsx │ └── README.md └── plugin.ts ├── .eslintrc.js ├── docs └── Frontpage.jpeg.png ├── .gitignore ├── tsconfig.json ├── dev └── index.tsx ├── .github └── workflows │ └── stale_issues.yaml ├── README.md └── package.json /src/components/ChatGPTMainPage/index.ts: -------------------------------------------------------------------------------- 1 | export { ChatGPTPage } from './ChatGPTPage'; 2 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export { chatgptFrontendPlugin, ChatGPTFrontendPage } from './plugin'; 2 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); 2 | -------------------------------------------------------------------------------- /src/setupTests.ts: -------------------------------------------------------------------------------- 1 | import '@testing-library/jest-dom'; 2 | import 'cross-fetch/polyfill'; 3 | -------------------------------------------------------------------------------- /src/components/ChatGptPlayground/index.ts: -------------------------------------------------------------------------------- 1 | export { ChatGptPlayground 2 | } from './ChatGptPlayground'; 3 | -------------------------------------------------------------------------------- /docs/Frontpage.jpeg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enfuse/backstage-chatgpt-plugin/HEAD/docs/Frontpage.jpeg.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | node_modules/.bin/backstage-cli 3 | node_modules/.bin/msw 4 | dist-types/ 5 | dist/ 6 | package/ -------------------------------------------------------------------------------- /src/routes.ts: -------------------------------------------------------------------------------- 1 | import { createRouteRef } from '@backstage/core-plugin-api'; 2 | 3 | export const rootRouteRef = createRouteRef({ 4 | id: 'chatgpt-frontend', 5 | }); 6 | -------------------------------------------------------------------------------- /src/plugin.test.ts: -------------------------------------------------------------------------------- 1 | import { chatgptFrontendPlugin } from './plugin'; 2 | 3 | describe('chatgpt-frontend', () => { 4 | it('should export plugin', () => { 5 | expect(chatgptFrontendPlugin).toBeDefined(); 6 | }); 7 | }); 8 | -------------------------------------------------------------------------------- /src/components/common/ButtonPanel.tsx: -------------------------------------------------------------------------------- 1 | 2 | import React from 'react'; 3 | import './styles.css' 4 | import { ButtonPannelProps } from '../ChatGptPlayground/types'; 5 | 6 | 7 | export const ButtonPanel: React.FC = ({ children }) => { 8 | return
{children}
; 9 | }; 10 | 11 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["src/index.ts"], 3 | "exclude":["node_modules"], 4 | "compilerOptions": { 5 | "allowJs": true, 6 | "declaration": true, 7 | "emitDeclarationOnly": true, 8 | "outDir": "dist-types/src", 9 | "declarationMap": true, 10 | "jsx": "react", 11 | "esModuleInterop":true, 12 | } 13 | } -------------------------------------------------------------------------------- /dev/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { createDevApp } from '@backstage/dev-utils'; 3 | import { chatgptFrontendPlugin, ChatGPTFrontendPage } from '../src/plugin'; 4 | 5 | createDevApp() 6 | .registerPlugin(chatgptFrontendPlugin) 7 | .addPage({ 8 | element: , 9 | title: 'Root Page', 10 | path: '/chatgpt-frontend' 11 | }) 12 | .render(); 13 | -------------------------------------------------------------------------------- /src/client/chatgpt.tsx: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import { Message } from "../components/ChatGptPlayground/PlaygroundContext"; 3 | 4 | export const getChatGptCompletion = (baseUrl: string, model: string, messages : Message[], 5 | temperature: number, maxTokens: number) => { 6 | return axios.get(`${baseUrl}/api/chatgpt/completions`,{ 7 | params:{ 8 | model: model, 9 | messages: messages, 10 | temperature: temperature, 11 | maxTokens: maxTokens 12 | }}) 13 | 14 | } -------------------------------------------------------------------------------- /src/plugin.ts: -------------------------------------------------------------------------------- 1 | import { createPlugin, createRoutableExtension } from '@backstage/core-plugin-api'; 2 | 3 | import { rootRouteRef } from './routes'; 4 | 5 | export const chatgptFrontendPlugin = createPlugin({ 6 | id: 'chatgpt-frontend', 7 | routes: { 8 | root: rootRouteRef, 9 | }, 10 | }); 11 | 12 | export const ChatGPTFrontendPage = chatgptFrontendPlugin.provide( 13 | createRoutableExtension({ 14 | name: 'ChatGPTFrontendPage', 15 | component: () => 16 | import('./components/ChatGPTMainPage').then(m => m.ChatGPTPage), 17 | mountPoint: rootRouteRef, 18 | }), 19 | ); 20 | -------------------------------------------------------------------------------- /src/components/ChatGptPlayground/types.tsx: -------------------------------------------------------------------------------- 1 | 2 | export interface ButtonPannelProps { 3 | children : React.ReactNode 4 | } 5 | 6 | export interface CodeBoxProps { 7 | codeSnippet : string 8 | } 9 | 10 | export interface ChatGptPlaygroundProps { 11 | showErrorMessage : () => void 12 | } 13 | 14 | export interface FormProps { 15 | loading:boolean, 16 | isSuccess:boolean, 17 | onSubmit:()=>void, 18 | resetForm: ()=>void, 19 | } 20 | 21 | export interface CustomButtomProps { 22 | selected: string 23 | framework:string, 24 | setFrameworkCallback: (framework: string) => void 25 | } -------------------------------------------------------------------------------- /.github/workflows/stale_issues.yaml: -------------------------------------------------------------------------------- 1 | name: Close inactive issues 2 | on: 3 | schedule: 4 | - cron: "0 0 * * *" 5 | workflow_dispatch: 6 | 7 | 8 | jobs: 9 | close-issues: 10 | runs-on: ubuntu-latest 11 | permissions: 12 | issues: write 13 | steps: 14 | - uses: actions/stale@v5 15 | with: 16 | days-before-issue-stale: 15 17 | days-before-issue-close: 7 18 | stale-issue-label: "stale" 19 | stale-issue-message: "This issue is stale because it has been open for 15 days with no activity." 20 | close-issue-message: "This issue was closed because it has been inactive for 7 days since being marked as stale." 21 | repo-token: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /src/components/ChatGptPlayground/SystemPrompt.tsx: -------------------------------------------------------------------------------- 1 | import React, { useContext } from 'react'; 2 | import PlaygroundContext, { UPDATE_SYSTEM_PROMPT } from './PlaygroundContext'; 3 | 4 | const SystemPrompt = () => { 5 | const { state, dispatch } = useContext(PlaygroundContext); 6 | const PLACEHOLDER = 'Act as a Spring expert assistant' 7 | const handleChange = (event: React.ChangeEvent) => { 8 | dispatch({type: UPDATE_SYSTEM_PROMPT, payload: {systemPrompt: event.target.value}}) 9 | }; 10 | return ( 11 |
12 |

System Prompt

13 | 17 |
18 | ) 19 | } 20 | 21 | export default SystemPrompt; -------------------------------------------------------------------------------- /src/components/ChatGptPlayground/ChatGptPlayground.test.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render } from '@testing-library/react'; 3 | import { ChatGptPlayground } from './ChatGptPlayground'; 4 | import { rest } from 'msw'; 5 | import { setupServer } from 'msw/node'; 6 | import { setupRequestMockHandlers } from '@backstage/test-utils'; 7 | 8 | describe('ExampleFetchComponent', () => { 9 | const server = setupServer(); 10 | // Enable sane handlers for network requests 11 | setupRequestMockHandlers(server); 12 | 13 | // setup mock response 14 | beforeEach(() => { 15 | server.use( 16 | rest.get('https://randomuser.me/*', (_, res, ctx) => 17 | res(ctx.status(200), ctx.delay(2000), ctx.json({})), 18 | ), 19 | ); 20 | }); 21 | it('should render', async () => { 22 | const rendered = render(); 23 | expect(await rendered.findByTestId('progress')).toBeInTheDocument(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/components/ChatGptPlayground/GettingStartedGuide.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const GetStartedGuide = () => { 4 | return ( 5 |
6 |

Get Started

7 |

ChatGPT is an incredibly advanced Large Language Model (LLM) auto-complete engine. You can use this playground to explore it’s functionality. You can try asking it to create components, services, sql queries and more. It can help write user-stories, design architectures, create documentation and tests.

8 |

Try playing with the settings and see how temperature (randomness) and max tokens (max length of input + output) affect the output.

9 |

Keep in mind

10 |

More descriptive prompts will generate better results, i.e. specify the language the you want the model to respond with.

11 |

Larger prompts can take significantly more time.

12 |
13 | ) 14 | } 15 | 16 | export default GetStartedGuide -------------------------------------------------------------------------------- /src/components/ChatGPTMainPage/ChatGPTPage.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Grid } from '@material-ui/core'; 3 | import { 4 | Header, 5 | Page, 6 | Content, 7 | } from '@backstage/core-components'; 8 | import { ChatGptPlayground } from '../ChatGptPlayground'; 9 | import { PlaygroundProvider } from '../ChatGptPlayground/PlaygroundContext'; 10 | 11 | export const ChatGPTPage = () => { 12 | const [error , setError] = React.useState(false) 13 | const showErrorMessage = () => { 14 | setError(true); 15 | setTimeout(() => setError(false), 2000); 16 | } 17 | return ( 18 |
19 |
20 |
An error occurred. Please try again.
21 | 22 | 23 | 24 | 25 |
26 | ) 27 | }; 28 | -------------------------------------------------------------------------------- /src/components/ChatGPTMainPage/ChatGPTPage.test.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { ChatGPTPage } from './ChatGPTPage'; 3 | import { ThemeProvider } from '@material-ui/core'; 4 | import { lightTheme } from '@backstage/theme'; 5 | import { rest } from 'msw'; 6 | import { setupServer } from 'msw/node'; 7 | import { 8 | setupRequestMockHandlers, 9 | renderInTestApp, 10 | } from "@backstage/test-utils"; 11 | 12 | describe('ChatGPT PAge', () => { 13 | const server = setupServer(); 14 | // Enable sane handlers for network requests 15 | setupRequestMockHandlers(server); 16 | 17 | // setup mock response 18 | beforeEach(() => { 19 | server.use( 20 | rest.get('/*', (_, res, ctx) => res(ctx.status(200), ctx.json({}))), 21 | ); 22 | }); 23 | 24 | it('should render', async () => { 25 | const rendered = await renderInTestApp( 26 | 27 | 28 | , 29 | ); 30 | expect(rendered.getByText('Welcome to chatgpt-frontend!')).toBeInTheDocument(); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Enfuse ChatGPT Plugin for Backstage 2 | The Enfuse ChatGPT Plugin for Backstage takes user-inputted description of a desired file of any format and returns the file. 3 | 4 | ![ChatGPT Playground](https://github.com/enfuse/backstage-chatgpt-plugin/blob/main/docs/Frontpage.jpeg.png) 5 | 6 | The more descriptive the prompt, the better the response! Note that the response will require additional time the larger the prompt, but the response's quality will increase. 7 | 8 | # Features 9 | * Temperature: Temperature will determine how creative the response will be, with a lower temperature returning a more focused and conservative output. A higher temperature will return a more creative response, but has the potential to be less coherent. 10 | * Max Tokens: Determines the length of the input + output. 11 | 12 | # Requirements 13 | 1. A [Backstage](https://backstage.io/docs/getting-started/) application instance 14 | 2. The [backend](https://github.com/enfuse/backstage-chatgpt-backend) part of this plugin installed. 15 | 16 | # Installation 17 | Navigate to root of Backstage installation and run 18 | ```sh 19 | # From root directory of your Backstage installation 20 | yarn add --cwd packages/app @enfuse/chatgpt-plugin-frontend 21 | ``` 22 | 23 | 2. Navigate to your packages/app/src/App.tsx and include the following 24 | 25 | ``` javascript 26 | import { ChatGPTFrontendPage } from '@enfuse/chatgpt-plugin-frontend'; 27 | .... 28 | 29 | .... 30 | } /> 31 | 32 | ``` 33 | -------------------------------------------------------------------------------- /src/components/ChatGptPlayground/SettingsPannel.tsx: -------------------------------------------------------------------------------- 1 | import { FormControl, InputLabel, MenuItem, Select, Slider } from '@material-ui/core' 2 | import React, { useContext } from 'react' 3 | import "../common/styles.css" 4 | import PlaygroundContext, { UPDATE_MAX_TOKENS, UPDATE_TEMPERATURE } from './PlaygroundContext' 5 | 6 | export const SettingsPanel = () => { 7 | const { state, dispatch } = useContext(PlaygroundContext); 8 | return ( 9 |
10 |

Settings

11 | 12 |

Temperature: {state.temperature}

13 | dispatch({type: UPDATE_TEMPERATURE, payload: {temperature: (value as number) / 100}})}/> 14 |

Max Tokens: {state.maxTokens}

15 | dispatch({type: UPDATE_MAX_TOKENS, payload: {maxTokens:( value as number)*40}})}/> 16 |
17 | ) 18 | } 19 | 20 | const ModuleSetting = () => { 21 | return ( 22 | 23 | Model 24 | 34 | 35 | ) 36 | } -------------------------------------------------------------------------------- /src/client/README.md: -------------------------------------------------------------------------------- 1 | # Enfuse ChatGPT Plugin for Backstage 2 | The Enfuse ChatGPT Plugin for Backstage takes user-inputted description of a desired file of any format and returns the file. 3 | 4 | [!ChatGPT Playground] (https://raw.githubusercontent.com/enfuse/enfuse-backstage/main/docs/frontpage.png?raw=true) 5 | 6 | The more descriptive the prompt, the better the response! Note that the response will require additional time the larger the prompt, but the response's quality will increase. 7 | 8 | # Features 9 | *Temperature: 10 | *Temperature will determine how creative the response will be, with a lower temperature returning a more focused and conservative output. A higher temperature will return a more creative response, but has the potential to be less coherent. 11 | *Maximum Length: 12 | *The length setting determines how long both the request and response are allowed to be. 13 | 14 | [!Setting Demonstration] (https://raw.githubusercontent.com/enfuse/enfuse-backstage/main/docs/settings.png?raw=true) 15 | 16 | # Releases 17 | ## v0.0.1 18 | - Initial release 19 | 20 | # Requirements 21 | - Yarn 22 | - Node v14 23 | - Postgres (running on Docker) 24 | 25 | # Installation 26 | ## Postgres 27 | Spin up a local postgres db with this: 28 | 29 | ```bash 30 | docker run -itd -e POSTGRES_USER=${USERNAME} -e POSTGRES_PASSWORD=${psw} -p 5432:5432 --name postgresql postgres 31 | ``` 32 | 33 | Note: During the first time you run backstage, it will automatically create the db for itself. 34 | 35 | 36 | ## Environment variabels 37 | 38 | ```bash 39 | export POSTGRES_USER=${username} 40 | export POSTGRES_PASSWORD=${psw} 41 | export POSTGRES_PORT=5432 42 | export POSTGRES_HOST=localhost 43 | export BACKSTAGE_GITHUB_TOKEN=${github_tokenn } 44 | export OPENAI_API_KEY=${open_ai_key} 45 | ``` 46 | 47 | 48 | ## Local Dev 49 | 50 | To start the app locally, run: 51 | 52 | ```sh 53 | yarn install 54 | yarn dev 55 | ``` 56 | This will run both frontend and backend apps. 57 | 58 | # Resources 59 | ### [Backstage](https://backstage.io) 60 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@enfuse/chatgpt-plugin-frontend", 3 | "version": "1.1.1", 4 | "main": "src/index.ts", 5 | "types": "src/index.ts", 6 | "license": "Apache-2.0", 7 | "publishConfig": { 8 | "access": "public", 9 | "main": "dist/index.esm.js", 10 | "types": "dist/index.d.ts" 11 | }, 12 | "backstage": { 13 | "role": "frontend-plugin" 14 | }, 15 | "scripts": { 16 | "start": "backstage-cli package start", 17 | "build": "backstage-cli package build", 18 | "lint": "backstage-cli package lint", 19 | "test": "backstage-cli package test", 20 | "clean": "backstage-cli package clean", 21 | "prepack": "backstage-cli package prepack", 22 | "postpack": "backstage-cli package postpack", 23 | "prepublish": "yarn tsc -p tsconfig.json && yarn run build" 24 | }, 25 | "dependencies": { 26 | "@backstage/core-components": "^0.11.1", 27 | "@backstage/core-plugin-api": "^1.0.6", 28 | "@backstage/theme": "^0.2.16", 29 | "@material-ui/core": "^4.9.13", 30 | "@material-ui/icons": "^4.9.1", 31 | "@material-ui/lab": "4.0.0-alpha.57", 32 | "react-code-blocks": "^0.0.9-0", 33 | "react-syntax-highlighter": "^15.5.0", 34 | "react-use": "^17.2.4", 35 | "typescript": "^5.0.4" 36 | }, 37 | "peerDependencies": { 38 | "react": "^16.13.1 || ^17.0.0", 39 | "react-dom": "^16.13.1 || ^17.0.0", 40 | "react-router": "^6.3.0", 41 | "react-router-dom": "^6.3.0" 42 | }, 43 | "devDependencies": { 44 | "@backstage/cli": "^0.22.6", 45 | "@backstage/core-app-api": "^1.1.0", 46 | "@backstage/dev-utils": "^1.0.6", 47 | "@backstage/test-utils": "^1.2.0", 48 | "@testing-library/react": "^12.1.3", 49 | "@testing-library/user-event": "^14.0.0", 50 | "@types/axios": "^0.14.0", 51 | "@types/history": "^5.0.0", 52 | "@types/node": "*", 53 | "@types/react-syntax-highlighter": "^15.5.6", 54 | "cross-fetch": "^3.1.5", 55 | "msw": "^0.46.0", 56 | "react": "^17.0.2", 57 | "react-router": "^6.3.0", 58 | "react-router-dom": "^6.3.0" 59 | }, 60 | "resolutions": { 61 | "@types/react": "^17.0.0", 62 | "@types/react-dom": "^17.0.0", 63 | "react": "^17.0.2", 64 | "react-dom": "^17.0.0", 65 | "react-router": "^6.3.0", 66 | "react-router-dom": "^6.3.0" 67 | }, 68 | "files": [ 69 | "dist" 70 | ] 71 | } 72 | -------------------------------------------------------------------------------- /src/components/ChatGptPlayground/ChatGptPlayground.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useApi, configApiRef } from '@backstage/core-plugin-api'; 3 | import "../common/styles.css" 4 | import {getChatGptCompletion} from '../../client/chatgpt' 5 | import { ChatPannel } from './ChatPannel'; 6 | import { SettingsPanel } from './SettingsPannel'; 7 | import SystemPrompt from './SystemPrompt' 8 | import PlaygroundContext, {Message, RESET_MESSAGE_CHAT, UPDATE_MESSAGE_CHAT} from './PlaygroundContext'; 9 | import { ChatGptPlaygroundProps } from './types'; 10 | 11 | 12 | export const ChatGptPlayground = ({showErrorMessage}: ChatGptPlaygroundProps) => { 13 | const config = useApi(configApiRef) 14 | const baseUrl = config.getString('backend.baseUrl') 15 | const {state, dispatch} = React.useContext(PlaygroundContext); 16 | 17 | const [loading , setLoading] = React.useState(false) 18 | const [isSuccess , setIsSuccess] = React.useState(false) 19 | 20 | const onSubmit = async () => { 21 | setLoading(true) 22 | 23 | const userMessge : Message = {role: 'user', content : state.userMessage} 24 | const messageHistory = [...state.messages, userMessge] 25 | getChatGptCompletion(baseUrl, state.model, messageHistory, state.temperature, state.maxTokens) 26 | .then(response => { 27 | const systemContent = response.data?.completion[0].message.content; 28 | const assistantMessage: Message = {role: 'Assistant', content : systemContent} 29 | dispatch({ type: UPDATE_MESSAGE_CHAT, payload: {newMessages: [userMessge,assistantMessage]} }); 30 | setLoading(false) 31 | setIsSuccess(true) 32 | }) 33 | .catch( e => { 34 | setLoading(false) 35 | setIsSuccess(false) 36 | showErrorMessage() 37 | }) 38 | } 39 | 40 | const resetPage = () => { 41 | setLoading(false) 42 | setIsSuccess(false) 43 | dispatch({type: RESET_MESSAGE_CHAT}) 44 | } 45 | 46 | return ( 47 |
48 | 49 | resetPage()} 53 | /> 54 | 55 |
56 | ) 57 | }; 58 | 59 | -------------------------------------------------------------------------------- /src/components/ChatGptPlayground/ChatPannel.tsx: -------------------------------------------------------------------------------- 1 | 2 | import React, {useContext} from 'react' 3 | import {Button } from '@material-ui/core'; 4 | import { FormProps } from './types'; 5 | import { ButtonPanel } from '../common/ButtonPanel'; 6 | import PlaygroundContext, { UPDATE_USER_MESSAGE } from './PlaygroundContext'; 7 | 8 | export const ChatPannel = ({onSubmit,resetForm, loading, isSuccess}: FormProps) => { 9 | const { state, dispatch } = useContext(PlaygroundContext); 10 | const PLACEHOLDER = 'Add message...' 11 | 12 | const handleChange = (event: React.ChangeEvent) => { 13 | dispatch({type: UPDATE_USER_MESSAGE, payload: {userMessage: event.target.value}}) 14 | }; 15 | 16 | const onSubmitHandler = () => { 17 | onSubmit() 18 | dispatch({type: UPDATE_USER_MESSAGE, payload: {userMessage: ''}}) 19 | } 20 | 21 | return
22 |
23 |
24 |