Skip to content

Commit

Permalink
Chore: Input data formatting refactor (#34)
Browse files Browse the repository at this point in the history
* Reworking input data that we post to api

* Minor readme and story updates

* minor cleanup and readme updates
  • Loading branch information
walkingtowork authored Jul 22, 2024
1 parent cadfc06 commit ce0c659
Show file tree
Hide file tree
Showing 10 changed files with 99 additions and 31 deletions.
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,12 +99,16 @@ The project is structured as follows:
- Open `DefaultModels.js` and copy/paste one of the existing models (editing where appropriate)
- `getSampleOutput`
- Return the `Test[Task Name]Output` that you created in `testData` above
- Return the `Test[Task Name]Output` that you created in `testData` above
- `getStaticTasks`
- Create a new story for the task in `QuickInput.stories.js`
- If the user is able to upload files for this task, add the task to `UppyFileTypeCheckerPlugin`
- Add the new task to `ModelDetailPage` in `getSampleInputs` and `getInputType`
- If you need to test the upload dashboard, open `useUploadInputControl` and find the text `UNCOMMENT THIS BEFORE COMMITTING` and comment it out.
- If you need to check the array of inputs that you are submitting, add an `onRunModelClicked` function to your task in `QuickInput.stories.js` (it takes the inputs as a param)
- Additional Notes:
- To test the upload dashboard, open `useUploadInputControl` and find the text `UNCOMMENT THIS BEFORE COMMITTING` and comment it out.
- To see what your currently-selected inputs, and the current state of the data that will be sent to the API (prior to clicking the "Run Model" button), go to `useQuickInputControl.js` and uncomment the useEffect with `console.log`s in it.
- To check the array of inputs that you are submitting to the API, add an `onRunModelClicked` function to your task in `QuickInput.stories.js` (it takes the inputs as a param). Because of how Storybook works, the component you're building won't be passed the real method, but you can make a mock in the stories file to test behavior.
- See `TextConversationOutput.stories.js` for an example of how to test api requests in this way
## Adding new Task icons
- Go to svgrepo.com, search for a suitable icon, download it, and drag the file into the `src/resources/icons` directory
Expand Down
17 changes: 11 additions & 6 deletions src/components/Experiment/QuickInput/QuickInput.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ import {
SampleAudioToAudioInputs,
SampleAudioClassificationInputs,
} from "../../../helpers/sampleImages";
import { QuickInputType } from "./quickInputType";
import { TaskInputTypes } from "../../../helpers/TaskInputTypes";

export default {
title: "Experiments/Quick Input",
Expand Down Expand Up @@ -144,16 +146,19 @@ export const AudioToText = Template.bind({});
AudioToText.args = {
sampleInputs: [
{
title: "automatic-speech-recognition-input.flac",
src: "https://xlab1.netlify.app/automatic-speech-recognition-input.flac"
src: "https://xlab1.netlify.app/automatic-speech-recognition-input.flac",
filename: "automatic-speech-recognition-input.flac",
type: TaskInputTypes.Audio
},
{
title: "automatic-speech-recognition-input(2).flac",
src: "https://xlab1.netlify.app/automatic-speech-recognition-input.flac"
src: "https://xlab1.netlify.app/automatic-speech-recognition-input.flac",
filename: "automatic-speech-recognition-input.flac",
type: TaskInputTypes.Audio
},
{
title: "automatic-speech-recognition-input(3).flac",
src: "https://xlab1.netlify.app/automatic-speech-recognition-input.flac"
src: "https://xlab1.netlify.app/automatic-speech-recognition-input.flac",
filename: "automatic-speech-recognition-input.flac",
type: TaskInputTypes.Audio
},
],
model: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export default function SampleInputsTab(props) {
function makeSampleAudioInput(url, index) {
return (
<button onClick={() => selectInput(index)} key={index} className={getElement(getInputClassName(url))}>
<div>{url.title}</div>
<div>{url.filename}</div>
<audio controls src={url.src} />
</button>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,8 @@ export default function useSampleInputControl(props) {

const isSelected = (input) => sampleInputType === QuickInputType.Image ? selectedIndex.indexOf(input.src) > -1 : selectedIndex.indexOf(input) > -1;
const isUnselected = (input) => selectedIndex.length >= 0 && sampleInputType === QuickInputType.Image ? selectedIndex.indexOf(input.src) === -1 : selectedIndex.indexOf(input) === -1;

const selectMultiInput = (selectedValueIndex) => {


// Note: Currently using both new and old way of handling inputs but should refactor in the future
let input = sampleInputType === QuickInputType.Image ?
props.sampleInputs[props.inputIndex][selectedValueIndex].src :
Expand All @@ -21,7 +20,6 @@ export default function useSampleInputControl(props) {
if (props.multiple) {
// TODO: This block was directly copied from selectInput
// and may need to be updated for useMultiInput

const selected = Array.from(selectedIndex);
let storedIndex = selected.indexOf(input);
if (storedIndex === -1) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export const useUploadInputControl = (props) => {
// Note: Uncomment in order to test w/o server, adding a fake uploadURL:
// result.successful.map(x => x.uploadURL = `test_${props.inputIndex}.com`)

const urls = result.successful.map(x => x.uploadURL);
const urls = result.successful.map(x => x.uploadURL); // Response on staging seems to be { location: "https...." }, not uploadUrl?

if (!task.useMultiInput) {
if (typeof (props.inputSelected) === 'function') {
Expand Down
77 changes: 66 additions & 11 deletions src/components/Experiment/QuickInput/useQuickInputControl.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,17 @@ import Task from "../../../helpers/Task";

export default function useQuickInputControl(props) {
const task = Task.getStaticTask(props.model.output.type);

const [selectedInputs, setSelectedInputs] = useState([""]);
const [selectedInputData, setSelectedInputData] = useState([{src: "", inputType: ""}]);
const [selectedTab, setSelectedTab] = useState(0);

// Note: Uncomment for debugging
// useEffect(() => {
// Because of how hooks/timing works with react, if you print these
// variables out below, such as in `selectInput`, you may see incorrect values
// console.log('selectedInputs', selectedInputs)
// }, [selectedInputs])
// console.log('selectedInputData', selectedInputData)
// }, [selectedInputs, selectedInputData]);

const getTabs = (type = QuickInputType.Image) => { // TODO: Remove this default
if(task.useMultiInput) return getMultiInputTabs(task.inputs);
Expand Down Expand Up @@ -52,7 +55,6 @@ export default function useQuickInputControl(props) {
types.forEach(type => {
if (!(type?.inputUpload === false)) upload.push(getUploadTabType(type.inputType.toLowerCase()));
if (!(type?.inputUrl === false)) input.push(...getInputTabType(type.inputType.toLowerCase()));

});

if (!props.hideSample) tabs.push(sample);
Expand Down Expand Up @@ -93,27 +95,80 @@ export default function useQuickInputControl(props) {
}
}
const runModel = () => {
if (typeof (props.onRunModelClicked) === 'function')
props.onRunModelClicked(selectedInputs.filter(url => url));
if (typeof (props.onRunModelClicked) === 'function') {
props.onRunModelClicked(selectedInputData.filter(input => input));
}
}
const selectInput = (url, index) => {
let selected = selectedInputs;
let selectedData = selectedInputs;

if (index)
if (index) {
// Note: This doesn't get selected in audioToText Sample inputs
// - does it ever happen? Or do we always go to the else?

// Display as selected
selected[index] = url;
else
// Data to be sent to API
if (typeof url !== 'object') {
selectedData[index] = { src: url, inputType: task.inputType }
} else {
selectedData[index] = { inputType: task.inputType, ...url }
}
} else {
// Display as selected
selected = Array.isArray(url) ? url : [url];
// Data to be sent to API
if (typeof url !== 'object') {
selectedData = [{ src: url, inputType: task.inputType }];
} else {
selectedData = [{ inputType: task.inputType, ...url }];
}
}

setSelectedInputs(selected);
setSelectedInputData(selectedData);
}
const selectMultiInput = (url, inputIndex) => {
let selected = [...selectedInputs];
let selectedData = [...selectedInputData];

if (inputIndex >= 0)
if (inputIndex >= 0) {
// Display as selected
selected[inputIndex] = url;
else
// Data to be sent to API
if (typeof url !== 'object') {
selectedData[inputIndex] = {
src: url,
inputType: task.inputs[inputIndex].inputType
}
} else {
selectedData[inputIndex] = {
inputType: task.inputs[inputIndex].inputType,
...url
}
}
}
else {
// Display as selected
selected = Array.isArray(url) ? url : [url];
// Note: Need to use a useEffect to accurately see what selectedInputs is
setSelectedInputs(selected);
// Data to be sent to API
if (typeof url !== 'object') {
selectedData[inputIndex] = [{
src: url,
inputType: task.inputs[inputIndex].inputType
}]
} else {
selectedData[inputIndex] = [{
inputType: task.inputs[inputIndex].inputType,
...url
}]
}
}

// Note: You need to uncomment the useEffect above to accurately see what selectedInputs/Data is
setSelectedInputs(selected);
setSelectedInputData(selectedData);
}

const addInput = (url = "") => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,13 @@ export default function TextConversationOutput(props) {

useEffect(() => {
if (!isSending) {
setNewInput('');

// NOTE: Currently not working
if (inputField.current) {
console.log('focus on input field')
inputField.current.focus();
}

}
}, [isSending]);

Expand All @@ -75,8 +76,7 @@ export default function TextConversationOutput(props) {

const sendMessage = () => {
setMessage({ role: ROLE.USER, content: newInput });
setIsSending(true);
setNewInput('');
setIsSending(true);
}


Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import React from "react";
import TextConversationOutput from "./TextConversationOutput";
import { TestTextConversationOutput, TestTextConversationOutput2 } from "./testData/testTextConversationOutput";
// import api from "../../../../../helpers/api"
// import GetApiHelper from "../../../../../helpers/api";
// import { DefaultTextConversationModel } from "../../../../../helpers/DefaultModels";

export default {
title: "Experiments/Quick Output/Text Conversation",
Expand All @@ -16,9 +19,9 @@ const fakeOnSubmit = async (input, context) => {
// This is basically what "should" happen, uncomment to confirm in Storybook
// see what values would be sent to the api
// const api = GetApiHelper();
// await api.runTrial(DefaultTextConversationModel, input, null, context);
// await api.runTrial(DefaultTextConversationModel, {src: input, inputType: "TEXT"}, null, context);

// Dummy response instead of actually calling api.runTrial above
// Dummy response instead of actually calling api.runTrial above, uncomment when finished testing
return TestTextConversationOutput2;
}

Expand Down
5 changes: 3 additions & 2 deletions src/helpers/DefaultModels.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ import {
imageToText,
textClassification,
audioToAudio,
audioClassification,
audioClassification,
textConversation,
} from "./TaskIDs";

export const DefaultImageClassificationModel = {
Expand Down Expand Up @@ -546,7 +547,7 @@ export const DefaultTextConversationModel = {
name: "DeepLabv3_MobileNet_v2_DM_05_PASCAL_VOC_Train_Aug",
output: {
description: "the chatbot's response to the inputted text",
type: audioToText,
type: textConversation,
},
url: {
github:
Expand Down
2 changes: 2 additions & 0 deletions src/helpers/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,8 @@ class Api {
requestBody['context'] = context;
}

// UNCOMMENT BEFORE COMMITTING
// Comment this out to test fake api requests via storybook
const response = await fetch(`${this.apiUrl}/predict`, {
method: 'POST',
headers: {
Expand Down

0 comments on commit ce0c659

Please sign in to comment.