Skip to content

Commit

Permalink
extend AI usage
Browse files Browse the repository at this point in the history
  • Loading branch information
bastiion committed Aug 16, 2024
1 parent 6ddff62 commit 094eb2a
Show file tree
Hide file tree
Showing 13 changed files with 515 additions and 163 deletions.
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
"@untemps/react-vocal": "^1.7.16",
"ajv": "^8.12.0",
"lodash": "^4.17.21",
"openai": "^3.2.1",
"openai": "^4.55.7",
"react": "^18.2.0",
"react-dnd": "^16.0.1",
"react-dnd-html5-backend": "^16.0.1",
Expand All @@ -37,7 +37,9 @@
"react-redux": "^8.0.5",
"react-router-dom": "^6.9.0",
"react-scripts": "5.0.1",
"redux-persist": "^6.0.0",
"typescript": "^4.9.5",
"uuidv4": "^6.2.13",
"web-vitals": "^2.1.4"
},
"scripts": {
Expand Down
20 changes: 18 additions & 2 deletions src/app/store.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,31 @@
import { configureStore, ThunkAction, Action } from '@reduxjs/toolkit'
import { persistStore, persistReducer } from 'redux-persist'
import counterReducer from '../features/counter/counterSlice'
import jsonFormsEditReducer from '../features/wizard/WizardSlice'
import templateSlice from '../features/wizard/TemplateSlice'

import storage from 'redux-persist/lib/storage'
import { formsDataReducer } from '../features/wizard/FormDataSlice' // defaults to localStorage for web
export const store = configureStore({
reducer: {
counter: counterReducer,
jsonFormsEdit: jsonFormsEditReducer,
template: templateSlice,
template: persistReducer(
{
key: 'template',
storage,
},
templateSlice
),
formsData: persistReducer(
{
key: 'formsData',
storage,
},
formsDataReducer
),
},
})
export const persistor = persistStore(store)

export type AppDispatch = typeof store.dispatch
export type RootState = ReturnType<typeof store.getState>
Expand Down
34 changes: 34 additions & 0 deletions src/features/home/ClickBox.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { Avatar, Button, Card, CardActionArea, CardContent, CardHeader, CardMedia, Typography } from '@mui/material'
import React, { useCallback } from 'react'
import { useAppDispatch } from '../../app/hooks/reduxHooks'
import { loadForm } from '../wizard/FormDataSlice'
import { red } from '@mui/material/colors'

type ClickBoxProps = {
id: string
title: string
avatar?: string
}
export const ClickBox = ({ id, title, avatar }: ClickBoxProps) => {
const dispatch = useAppDispatch()
const handleLoad = useCallback(() => {
dispatch(loadForm({ id }))
}, [id])

return (
<Card>
<CardHeader
avatar={
<Avatar sx={{ bgcolor: red[500] }} aria-label="data">
{title[0]}
</Avatar>
}
title={title}
></CardHeader>
{avatar && <CardMedia component="img" height="194" image={avatar} />}
<CardActionArea>
<Button onClick={handleLoad}>load Form</Button>
</CardActionArea>
</Card>
)
}
13 changes: 11 additions & 2 deletions src/features/home/DragBox.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import React, { useEffect, useLayoutEffect, useState } from 'react'
import React, { useCallback, useEffect, useLayoutEffect, useState } from 'react'
import { useDrag } from 'react-dnd'
import { Button, Card, CardActionArea, CardActions, CardContent, Typography } from '@mui/material'
import { DraggableComponent } from '../wizard/WizardSlice'
import { DraggableComponent, replaceSchema } from '../wizard/WizardSlice'
import { useAppDispatch } from '../../app/hooks/reduxHooks'

type DragBoxProps = {
name: string
img?: string
componentMeta: DraggableComponent
}
const DragBox = ({ name = 'Eingabefeld', img = '', componentMeta }: DragBoxProps) => {
const dispatch = useAppDispatch()
const [, dragRef] = useDrag(
() => ({
type: 'DRAGBOX',
Expand All @@ -25,6 +27,10 @@ const DragBox = ({ name = 'Eingabefeld', img = '', componentMeta }: DragBoxProps
[]
)

const handleReplace = useCallback(() => {
dispatch(replaceSchema(componentMeta.jsonSchemaElement))
}, [dispatch, componentMeta])

return (
<Card ref={dragRef}>
<CardActionArea>
Expand All @@ -33,6 +39,9 @@ const DragBox = ({ name = 'Eingabefeld', img = '', componentMeta }: DragBoxProps
{name}
</Typography>
</CardContent>
<CardActionArea>
<Button onClick={handleReplace}>replace current</Button>
</CardActionArea>
</CardActionArea>
</Card>
)
Expand Down
35 changes: 30 additions & 5 deletions src/features/home/LeftDrawer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,17 @@ import Drawer from '@mui/material/Drawer'
import Toolbar from '@mui/material/Toolbar'

import DragBox from './DragBox'
import { DraggableComponent } from '../wizard/WizardSlice'
import { DraggableComponent, selectJsonSchema, selectUiSchema } from '../wizard/WizardSlice'
import { TabContext, TabList, TabPanel } from '@mui/lab'
import { Tab } from '@mui/material'
import { Button, Tab } from '@mui/material'
import { useCallback } from 'react'
import { updateScopeOfUISchemaElement } from '../../utils/uiSchemaHelpers'
import { ConfirmButton } from '../modals/ChatGptModal'
import { useSelector } from 'react-redux'
import { selectTemplates } from '../wizard/TemplateSlice'
import { useAppDispatch, useAppSelector } from '../../app/hooks/reduxHooks'
import { ClickBox } from './ClickBox'
import { listFormData, newForm } from '../wizard/FormDataSlice'
import { CreateRounded } from '@mui/icons-material'

const drawerWidth = 240

Expand Down Expand Up @@ -116,9 +119,24 @@ export const advancedDraggableComponents: DraggableComponent[] = [
},
]

export const NewEntryButton = () => {
const dispatch = useAppDispatch()
const jsonSchema = useAppSelector(selectJsonSchema)
const uiSchema = useAppSelector(selectUiSchema)
const handleNewEntry = useCallback(() => {
dispatch(newForm({ jsonSchema, uiSchema }))
}, [dispatch, jsonSchema, uiSchema])
return (
<Button startIcon={<CreateRounded />} variant={'contained'} onClick={handleNewEntry}>
New Entry
</Button>
)
}

export default function LeftDrawer() {
const [activeTab, setActiveTab] = React.useState('1')
const templates = useSelector(selectTemplates)
const templates = useAppSelector(selectTemplates)
const formDataList = useAppSelector(listFormData)
const handleChange = useCallback(
(event, newValue) => {
setActiveTab(newValue)
Expand All @@ -145,6 +163,7 @@ export default function LeftDrawer() {
<TabList onChange={handleChange} aria-label="lab API tabs example">
<Tab label="Tools" value="1" />
<Tab label="Templates" value="2" />
<Tab label="Data" value="3" />
</TabList>
</Box>
<TabPanel value="1" sx={{ p: 0 }}>
Expand All @@ -162,14 +181,20 @@ export default function LeftDrawer() {
</Box>
</TabPanel>
<TabPanel value="2" sx={{ p: 0 }}>
<ConfirmButton>KI gestützte Formulargenerierung</ConfirmButton>
{advancedDraggableComponents.map((component, index) => {
return <DragBox name={component.name} key={component.name} componentMeta={component}></DragBox>
})}
<ConfirmButton>Chat GPT</ConfirmButton>
{templates.map((component, index) => {
return <DragBox name={component.name} key={component.name} componentMeta={component}></DragBox>
})}
</TabPanel>
<TabPanel value={'3'} sx={{ p: 0 }}>
<NewEntryButton></NewEntryButton>
{formDataList.map(({ id, title, avatar }) => {
return <ClickBox key={id} title={title} avatar={avatar} id={id}></ClickBox>
})}
</TabPanel>
</TabContext>
</Drawer>
)
Expand Down
88 changes: 88 additions & 0 deletions src/features/input/UploadAnalyzeGPT.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { useState } from 'react'
import openAIInstance, { model } from '../../utils/openai'
import { threadId } from 'worker_threads'

export const UploadAnalyzeGPT = () => {
const [summary, setSummary] = useState('')
const [loading, setLoading] = useState(false)

const handleFileUpload = async (event) => {
const file = event.target.files[0]

if (!file) {
return
}

setLoading(true)

try {
const client = openAIInstance

// Upload the file to OpenAI
/*const response = await client.files.create({
file: new File([file], file.name),
purpose: 'assistants', // Change the purpose according to your need
});*/

const vectorStore = await client.beta.vectorStores.create({ name: 'File Upload' })
console.log('Vector Store ID:', vectorStore.id)

const batch = await client.beta.vectorStores.fileBatches.uploadAndPoll(vectorStore.id, {
files: [new File([file], file.name)],
})
console.log({ batch })

const assistant = await client.beta.assistants.create({
model: model,
instructions: 'You are a knowledgeable assistant that uses the provided files to answer questions.',
tools: [{ type: 'file_search' }],
tool_resources: {
file_search: {
vector_store_ids: [vectorStore.id], // Attach vector store containing your files
},
},
})

console.log('Assistant ID:', assistant.id)
console.log({ assistant })

const thread = await client.beta.threads.create()
console.log('Thread ID:', thread.id)

const message = await client.beta.threads.messages.create(thread.id, {
role: 'user',
content: 'Summarize what can be seen at the image',
})

const run = await client.beta.threads.runs.create(thread.id, {
assistant_id: assistant.id,
})

console.log({ run })

let interval = setInterval(async () => {
const status = await client.beta.threads.runs.retrieve(thread.id, run.id)
if (status.status === 'completed') {
clearInterval(interval)
const messages = await client.beta.threads.messages.list(thread.id)
console.log({ messages })
}
}, 1000)

//setSummary(run.choices[0].message.content.trim());
} catch (error) {
console.error('Error uploading file:', error)
setSummary('An error occurred while summarizing the file.')
} finally {
setLoading(false)
}
}

return (
<div>
<input type="file" onChange={handleFileUpload} accept="image/*" />
{loading && <p>Uploading and summarizing the file...</p>}
{summary && <p>Summary: {summary}</p>}
</div>
)
}
55 changes: 43 additions & 12 deletions src/features/modals/ChatGptModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import Box from '@mui/material/Box'
import { LoadingButton, TabContext, TabList, TabPanel } from '@mui/lab'
import DropTargetFormsPreview from '../dragAndDrop/DropTargetFormsPreview'
import Vocal from '@untemps/react-vocal'
import { AndroidRounded } from '@mui/icons-material'

interface ConfirmModalProps {
onConfirm?: () => void
Expand Down Expand Up @@ -76,20 +77,41 @@ const ChatGptModal = NiceModal.create<ConfirmModalProps>(({ onConfirm = () => nu
const talkToAI = useCallback(
async (text) => {
// Insert message at first element.
const response = await openaiInstance.createCompletion({
const response = await openaiInstance.chat.completions.create({
model: model,
prompt: `For a form generate a JSONSchema that meets the following requirements: ${text}`,
max_tokens: 600,
messages: [
{
role: 'user',
content: `For a form generate a JSONSchema that meets the following requirements: ${text} \n Only output the parsable JSON Schema - even without the typical markdown code block. Keys und properties should always be in english but labels/titles should be in the language of the requirements text. Add additional fields, that can be derived from your domain knowledge, even if not explicitly asked for.`,
},
],
max_tokens: 4000,
})
console.log(response.data)
console.log(response)
// Append AI message.
setResponse(response.data.choices[0].text)
const titleResponse = await openaiInstance.createCompletion({
model: model,
prompt: `The headline or title that summarizes the following form: ${text}`,
max_tokens: 50,
})
setFormTitle(titleResponse.data.choices[0].text)
const res = response.choices[0].message.content
setResponse(res)
let formTitle: string | null = null
try {
const schema = JSON.parse(res)
if (typeof schema.title === 'string') {
formTitle = schema.title
}
} catch (e) {
console.warn(e.message)
}
if (!formTitle) {
const titleResponse = await openaiInstance.chat.completions.create({
model: model,
messages: [
{ role: 'user', content: `A short headline or title that summarizes the following form: ${text}` },
],
max_tokens: 50,
})
formTitle = titleResponse.choices[0].message.content
}

setFormTitle(formTitle)
setLoading(false)
},
[setResponse, setFormTitle, setLoading]
Expand Down Expand Up @@ -143,7 +165,11 @@ const ChatGptModal = NiceModal.create<ConfirmModalProps>(({ onConfirm = () => nu
<Grid container direction={'column'}>
<Grid item>
<TextField multiline fullWidth value={message} onChange={(e) => setMessage(e.target.value)} />
<Vocal onResult={onVocalResult} style={{ width: 16, position: 'absolute', right: 10, top: -2 }} />
<Vocal
lang={'de-DE'}
onResult={onVocalResult}
style={{ width: 16, position: 'absolute', right: 10, top: -2 }}
/>
</Grid>
<LoadingButton loading={loading} onClick={onSubmit}>
Submit
Expand Down Expand Up @@ -171,6 +197,9 @@ const ChatGptModal = NiceModal.create<ConfirmModalProps>(({ onConfirm = () => nu
</Grid>
</DialogContent>
<DialogActions>
<Button onClick={handleAgree} color="primary">
<FormattedMessage description="confirm modal header" defaultMessage="agree" id="agree"></FormattedMessage>
</Button>
<Button onClick={handleDisagree} color="primary">
<FormattedMessage description="confirm modal header" defaultMessage="cancel" id="cancel"></FormattedMessage>
</Button>
Expand All @@ -187,6 +216,8 @@ export function ConfirmButton({
}: ConfirmModalProps & { children: React.ReactNode }) {
return (
<Button
startIcon={<AndroidRounded />}
variant={'contained'}
onClick={() =>
NiceModal.show(ChatGptModal, {
onConfirm,
Expand Down
Loading

0 comments on commit 094eb2a

Please sign in to comment.