Skip to content

Commit

Permalink
initial commit for connecting to the OpenAI API and sending requests,…
Browse files Browse the repository at this point in the history
… see #94
  • Loading branch information
jessegreenberg committed Jun 1, 2023
1 parent 9efeda9 commit 17e46a2
Show file tree
Hide file tree
Showing 18 changed files with 347 additions and 35 deletions.
7 changes: 7 additions & 0 deletions client/common/SceneryDisplay.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
*/

import React, { useEffect } from 'react';
import ViewConstants from './ViewConstants.js';

const SceneryDisplay = props => {

Expand Down Expand Up @@ -78,6 +79,12 @@ const SceneryDisplay = props => {
phet.utteranceQueue.responseCollector.objectResponsesEnabledProperty.value = true;
phet.utteranceQueue.responseCollector.contextResponsesEnabledProperty.value = true;
phet.utteranceQueue.responseCollector.hintResponsesEnabledProperty.value = true;

// custom focus highlight colors for this display
phet.scenery.HighlightOverlay.setInnerHighlightColor( ViewConstants.focusHighlightColor );
phet.scenery.HighlightOverlay.setOuterHilightColor( ViewConstants.focusHighlightColor );
phet.scenery.HighlightOverlay.setInnerGroupHighlightColor( ViewConstants.focusHighlightColor );
phet.scenery.HighlightOverlay.setOuterGroupHighlightColor( ViewConstants.focusHighlightColor );
}, [] );

return <div id='scenery-display'></div>;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import CustomButtonAppearanceStrategy from './CustomButtonAppearanceStrategy.js';
import CustomButtonAppearanceStrategy from '../creator/view/CustomButtonAppearanceStrategy.js';

const TEXT_FONT = new phet.scenery.Font( { size: 16 } );
const TEXT_FILL_COLOR = new phet.scenery.Color( 189, 203, 218 );
Expand Down
93 changes: 93 additions & 0 deletions client/creator-ai/CreatorAIMain.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
@value baseButtonColor: rgb(89, 93, 94);

@value pressedButtonColor: rgb(54,56,57);

@value buttonBorderColor: transparent;

@value focusHighlightColor: #6C8EAC;

/*background color for the page*/
@value backgroundColor: #2E4152;

@value textColor: #bdcbda;

@value padding: 10px;

:local(.container ) {
display: flex;
flex-direction: column;
row-gap: 20px;
height: 100vh;
}

:local(.row) {
display: flex;
flex-direction: column;
column-gap: 20px;
flex: 1;
}

/*applied to many panels in the board page*/
:local(.panelClass) {
border-color: #6C8EAC;
border-style: solid;
color: textColor;
padding: 10px;
height: 100vh;
overflow: auto;
}

:local(.inputElement ) {
padding-top: padding;
}

/* Container for chat components, aligning them in a column and taking up full space (regardless of contents) */
:local(.chatForm) {
display: flex;
flex-direction: column;
height: 100%; /* Set the height of the parent container */
}

/* So that the chat input is at the bottom of the chat form */
:local(.chatInput) {
margin-top: auto;
}

/* So that the chat output is above with a scrollbar */
:local(.chatOutput) {
height: 85%;
overflow: auto;
}

/* Custom styling for list group items */
:local(.listGroupItem ) {
background-color: backgroundColor;
color: textColor;
}

:local(.meChatItem) {
background-color: backgroundColor;
color: textColor;
}

:local(.botChatItem) {
background-color: #212f3a;
color: textColor;
}

/* Apply colors to the bootstrap icons */
:local(.chatIcon ) {
scale: 1.5;
padding: padding;
}

:local(.chatMessage) {
}

:local(.iconWithMessage ) {
display: flex;
justify-content: flex-start;
/*align-content: flex-end;*/
gap: padding;
}

97 changes: 97 additions & 0 deletions client/creator-ai/CreatorAIMain.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/**
* Main react component for the CreatorAI page.
*/

import React, { useState } from 'react';
import Form from 'react-bootstrap/Form';
import ListGroup from 'react-bootstrap/ListGroup';
import { combineClasses } from '../utils.js';
import styles from './CreatorAIMain.css';

export default function CreatorAIMain( props ) {

// add react state for input an chatLog
const [ input, setInput ] = useState( '' );

// Items of the log, with values { user: 'me' | 'ai', message: String, type: 'chat' | 'error' }
const [ chatLog, setChatLog ] = useState( [] );

const handleSubmit = async event => {

// no page refresh on submit
event.preventDefault();

const newChatLog = [ ...chatLog, { user: 'me', message: input, type: 'chat' } ];
await setChatLog( newChatLog );

setInput( '' );


// for chat, we feed all previous messages for a new response
const allMessages = newChatLog.map( message => message.message ).join( '\n' );

const response = await fetch( 'http://localhost:3000/openai', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify( { prompt: allMessages } )
} );

const data = await response.json();

if ( data.error ) {
await setChatLog( [ ...newChatLog, { user: 'ai', message: data.error.message, type: 'error' } ] );
}
else if ( data.choices.length > 0 ) {
await setChatLog( [ ...newChatLog, { user: 'ai', message: data.choices[ 0 ].text, type: 'chat' } ] );
}
};

return (
<div className={styles.container}>
<div className={combineClasses( styles.row, styles.panelClass )}>
<div className={styles.chatForm}>
<div className={styles.chatOutput}>
<ListGroup variant='flush'>
{
chatLog.map( ( logItem, index ) => (
<ChatItem key={index} user={logItem.user} message={logItem.message} type={logItem.type}/>
) )
}
</ListGroup>
</div>
<div className={styles.chatInput}>
<form onSubmit={handleSubmit}>
<Form.Control
type='text'
rows={3}
placeholder='What do you want to create?'
value={input}
onChange={event => setInput( event.target.value )}
/>
</form>
</div>
</div>
</div>
<div className={combineClasses( styles.row, styles.panelClass )}> Scenery</div>
</div>
);
}

function ChatItem( props ) {
return (
<ListGroup.Item className={props.user === 'me' ? styles.meChatItem : styles.botChatItem}>
<div className={styles.iconWithMessage}>
<div>
{
props.user === 'me' ?
<img className={styles.chatIcon} src={'./media/images/person-icon.svg'}></img> :
<img className={styles.chatIcon} src={'./media/images/ai-icon.svg'}></img>
}
</div>
<p className={styles.chatMessage}>{props.message}</p>
</div>
</ListGroup.Item>
);
}
23 changes: 23 additions & 0 deletions client/creator-ai/entry.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/**
* Entry-point file for the Sim Design Board, which is a scene graph based on the PhET libraries that uses PhET
* components that can be manipulated using the paper programs.
*
* @author Jesse Greenberg (PhET Interactive Simulations)
*/

import 'bootstrap/dist/css/bootstrap.min.css';
import React from 'react';
import ReactDOM from 'react-dom';
import CreatorAIMain from './CreatorAIMain.js';

// Create the root element for React.
const simDisplayDiv = document.getElementById( 'creator-root-element' );
document.body.appendChild( simDisplayDiv );

// Create the root of the scene graph.
const scene = new phet.scenery.Node();

ReactDOM.render(
<CreatorAIMain scene={scene}></CreatorAIMain>,
simDisplayDiv
);
7 changes: 0 additions & 7 deletions client/creator/CreatorMain.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import styles from './CreatorMain.css';
import CreatorModel from './model/CreatorModel.js';
import CreatorControls from './react/CreatorControls.js';
import CreatorView from './view/CreatorView.js';
import ViewConstants from './view/ViewConstants.js';

export default function CreatorMain( props ) {
const scene = props.scene;
Expand Down Expand Up @@ -87,12 +86,6 @@ export default function CreatorMain( props ) {
}, true );
};

// custom focus highlight colors for this display
phet.scenery.HighlightOverlay.setInnerHighlightColor( ViewConstants.focusHighlightColor );
phet.scenery.HighlightOverlay.setOuterHilightColor( ViewConstants.focusHighlightColor );
phet.scenery.HighlightOverlay.setInnerGroupHighlightColor( ViewConstants.focusHighlightColor );
phet.scenery.HighlightOverlay.setOuterGroupHighlightColor( ViewConstants.focusHighlightColor );

return (
<div>
<div className={styles.rowContainer}>
Expand Down
2 changes: 1 addition & 1 deletion client/creator/view/ComponentListItemNode.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import ImageLoader from './ImageLoader.js';
import ViewConstants from './ViewConstants.js';
import ViewConstants from '../../common/ViewConstants.js';

export default class ComponentListItemNode extends phet.scenery.Node {
constructor( namedProperty, programWidth ) {
Expand Down
2 changes: 1 addition & 1 deletion client/creator/view/CreatorView.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import DeleteProgramAreaNode from './DeleteProgramAreaNode.js';
import ProgramNode from './ProgramNode.js';
import ViewConstants from './ViewConstants.js';
import ViewConstants from '../../common/ViewConstants.js';

export default class CreatorView extends phet.scenery.Node {

Expand Down
2 changes: 1 addition & 1 deletion client/creator/view/DeleteProgramAreaNode.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import ImageLoader from './ImageLoader.js';
import ViewConstants from './ViewConstants.js';
import ViewConstants from '../../common/ViewConstants.js';

export default class DeleteProgramAreaNode extends phet.scenery.Node {
constructor() {
Expand Down
2 changes: 1 addition & 1 deletion client/creator/view/ProgramNode.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import ActiveEdit from '../model/ActiveEdit.js';
import EditType from '../model/EditType.js';
import ComponentListItemNode from './ComponentListItemNode.js';
import ViewConstants from './ViewConstants.js';
import ViewConstants from '../../common/ViewConstants.js';

// default dimensions of a paper, though it may change as components are added
const WIDTH = 70;
Expand Down
5 changes: 5 additions & 0 deletions client/utils.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import Matrix from 'node-matrices';

// A function that takes a list of class name strings and combines them into a single string.
export function combineClasses( ...classes ) {
return classes.join( ' ' );
}

export function norm( vector ) {
if ( vector.x !== undefined ) {
return norm( [ vector.x, vector.y ] );
Expand Down
Loading

0 comments on commit 17e46a2

Please sign in to comment.