diff --git a/.vscode/settings.json b/.vscode/settings.json index a2a812015..cf15556f4 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,5 +2,7 @@ "editor.formatOnSave": true, "editor.defaultFormatter": "esbenp.prettier-vscode", "prettier.configPath": "./.prettierrc.json", - "eslint.workingDirectories": ["./client", "./core"] + "eslint.workingDirectories": ["./client", "./core"], + "typescript.tsserver.experimental.enableProjectDiagnostics": true, + "cSpell.enabled": false } diff --git a/client/src/database/models/moduleModel.ts b/client/src/database/models/moduleModel.ts index f5f599d37..a85f38814 100644 --- a/client/src/database/models/moduleModel.ts +++ b/client/src/database/models/moduleModel.ts @@ -88,7 +88,10 @@ const loadModuleModel = db => { } const getSpellModules = async spell => { - const moduleNames = Object.values(spell.chain.nodes) + const nodes = spell?.chain?.nodes || spell?.graph?.nodes + if (!nodes) return + + const moduleNames = Object.values(nodes) .filter((n: any) => n.data.module) .map((n: any) => n.data.module) diff --git a/client/src/features/HomeScreen/HomeScreen.js b/client/src/features/HomeScreen/HomeScreen.js index ebcb0157e..e4debf842 100644 --- a/client/src/features/HomeScreen/HomeScreen.js +++ b/client/src/features/HomeScreen/HomeScreen.js @@ -27,13 +27,13 @@ const StartScreen = ({ createNew, allProjects }) => { const onReaderLoad = async event => { const spellData = JSON.parse(event.target.result) - // TODO check for proper values here and throw errors - let spell - try { - spell = await models.spells.getSpell(spellData.name) - } catch (error) { - spell = await saveSpell(spellData) + if (spellData.graph) { + spellData.chain = spellData.graph + delete spellData.graph } + const spell = await saveSpell(spellData) + // TODO check for proper values here and throw errors + // Load modules from the spell if (spellData?.modules && spellData.modules.length > 0) await Promise.all( @@ -47,6 +47,8 @@ const StartScreen = ({ createNew, allProjects }) => { spellId: spellData.name, type: 'spell', }) + + navigate('/thoth') } const loadFile = selectedFile => { diff --git a/client/src/features/HomeScreen/screens/CreateNew.jsx b/client/src/features/HomeScreen/screens/CreateNew.jsx index 9da18be85..6f7791f9f 100644 --- a/client/src/features/HomeScreen/screens/CreateNew.jsx +++ b/client/src/features/HomeScreen/screens/CreateNew.jsx @@ -69,7 +69,7 @@ const CreateNew = () => { type: 'spell', }) - setTimeout(() => navigate('/thoth'), 0) + setTimeout(() => navigate('/thoth'), 500) }) return ( diff --git a/client/src/features/HomeScreen/screens/chains/default.ts b/client/src/features/HomeScreen/screens/chains/default.ts index e9291688b..58b78715b 100644 --- a/client/src/features/HomeScreen/screens/chains/default.ts +++ b/client/src/features/HomeScreen/screens/chains/default.ts @@ -1,36 +1,16 @@ const chain = { id: 'demo@0.1.0', nodes: { - '123': { - id: 123, - data: { - socketKey: 'a2baf53a-d604-40cb-a102-609448c3f67c', - dataControls: { - name: { - expanded: true, - }, - }, - name: 'text', - }, - inputs: {}, - outputs: { - output: { - connections: [], - }, - }, - position: [-1555.0656080696986, -286.76976427120616], - name: 'Module Input', - }, '124': { id: 124, data: { + name: 'default', socketKey: '20c0d2db-1916-433f-88c6-69d3ae123217', dataControls: { name: { expanded: true, }, }, - name: 'default', }, inputs: {}, outputs: { @@ -41,28 +21,53 @@ const chain = { position: [-1555.4724883179474, -132.7648214211178], name: 'Module Trigger In', }, - '125': { - id: 125, + '232': { + id: 232, data: { - socketKey: '048b7f6e-c155-4958-975a-c7698fc7e84d', + playtestToggle: { + receivePlaytest: false, + outputs: [], + }, + socketKey: '9d61118c-3c5a-4379-9dae-41965e56207f', + text: 'Input text here', dataControls: { name: { expanded: true, }, + playtestToggle: { + expanded: true, + }, }, - name: 'result', + name: 'Input', + outputs: [], }, + inputs: {}, + outputs: { + output: { + connections: [], + }, + }, + position: [-1554.8394720686588, -362.87500885530955], + name: 'Universal Input', + }, + '233': { + id: 233, + data: {}, inputs: { - input: { + text: { connections: [], }, trigger: { connections: [], }, }, - outputs: {}, - position: [-601.1864065822252, -230.90176968690835], - name: 'Module Output', + outputs: { + trigger: { + connections: [], + }, + }, + position: [-828.9994593860473, -299.2588216155752], + name: 'Output', }, }, } diff --git a/client/src/features/Thoth/contexts/LayoutProvider.jsx b/client/src/features/Thoth/contexts/LayoutProvider.jsx index 4fc9abe9c..1514c0cc5 100644 --- a/client/src/features/Thoth/contexts/LayoutProvider.jsx +++ b/client/src/features/Thoth/contexts/LayoutProvider.jsx @@ -11,7 +11,7 @@ import { useContext, createContext, useEffect, useState, useRef } from 'react' import LoadingScreen from '@common/LoadingScreen/LoadingScreen' import { usePubSub } from '@/contexts/PubSubProvider' import { useTabManager } from '@/contexts/TabManagerProvider' - +import { useGetSpellQuery, useSaveSpellMutation } from '@/state/api/spells' // Component types are listed here which are used to load components from the data sent by rete const windowTypes = { TEXT_EDITOR: 'textEditor', @@ -46,6 +46,10 @@ export const useLayout = () => useContext(Context) const LayoutProvider = ({ children, tab }) => { const { subscribe, publish, events } = usePubSub() + const [saveSpell] = useSaveSpellMutation() + const { data: spell } = useGetSpellQuery(tab.spell, { + skip: !tab.spell, + }) const currentModelRef = useRef({}) const [currentModel, setCurrentModel] = useState(null) @@ -120,11 +124,13 @@ const LayoutProvider = ({ children, tab }) => { if (inspectorData) { setInspectorData(update) } + saveSpell(spell) } const saveInspector = inspectorData => { setInspectorData(inspectorData) publish(events.$NODE_SET(tab.id, inspectorData.nodeId), inspectorData) + saveSpell(spell) } const createModel = json => { diff --git a/client/src/features/Thoth/windows/InspectorWindow/DataControls/DataControls.jsx b/client/src/features/Thoth/windows/InspectorWindow/DataControls/DataControls.jsx index b8d96f00a..cac6b7c7e 100644 --- a/client/src/features/Thoth/windows/InspectorWindow/DataControls/DataControls.jsx +++ b/client/src/features/Thoth/windows/InspectorWindow/DataControls/DataControls.jsx @@ -9,6 +9,8 @@ import LongText from './LongTextControl' import ModuleSelect from './ModuleSelect' import OutputGenerator from './OutputGenerator' import SocketGenerator from './SocketGenerator' +import PlaytestControl from './PlaytestControl' +import SwitchControl from './SwitchControl' const StubComponent = props =>
{props.name}
@@ -24,6 +26,8 @@ const controlMap = { outputGenerator: OutputGenerator, slider: StubComponent, socketGenerator: SocketGenerator, + playtest: PlaytestControl, + switch: SwitchControl, } const DataControls = ({ @@ -57,7 +61,9 @@ const DataControls = ({ updateData, } - const Component = controlMap[control.component] || StubComponent + const Component = controlMap[control.component] + + if (!Component) return null const setExpanded = state => { control.expanded = state diff --git a/client/src/features/Thoth/windows/InspectorWindow/DataControls/PlaytestControl.tsx b/client/src/features/Thoth/windows/InspectorWindow/DataControls/PlaytestControl.tsx new file mode 100644 index 000000000..85e47f7bb --- /dev/null +++ b/client/src/features/Thoth/windows/InspectorWindow/DataControls/PlaytestControl.tsx @@ -0,0 +1,51 @@ +import { useState } from 'react' + +import Switch from '../../../../common/Switch/Switch' + +type SocketType = { + name: string + socketKey: string + socketType: string + taskType: string +} + +const SwitchControl = ({ control, updateData, initialValue }) => { + const { dataKey, data } = control + const { receivePlaytest } = initialValue + const initial = + typeof receivePlaytest === 'boolean' + ? receivePlaytest + : receivePlaytest === 'true' + const [checked, setChecked] = useState(initial) + + const onChange = e => { + const playtest = e.target.checked + setChecked(playtest) + + const outputs = [] as SocketType[] + + if (playtest) { + outputs.push({ + name: 'Playtest trigger', + socketKey: 'trigger', + socketType: 'triggerSocket', + taskType: 'option', + }) + } + + updateData({ + [dataKey]: { + receivePlaytest: playtest, + outputs, + }, + }) + } + + return ( +
+ +
+ ) +} + +export default SwitchControl diff --git a/client/src/features/Thoth/windows/InspectorWindow/DataControls/SwitchControl.tsx b/client/src/features/Thoth/windows/InspectorWindow/DataControls/SwitchControl.tsx new file mode 100644 index 000000000..4a972008a --- /dev/null +++ b/client/src/features/Thoth/windows/InspectorWindow/DataControls/SwitchControl.tsx @@ -0,0 +1,23 @@ +import { useState } from 'react' + +import Switch from '../../../../common/Switch/Switch' + +const SwitchControl = ({ control, updateData, initialValue }) => { + const { dataKey, data } = control + const [checked, setChecked] = useState(initialValue) + + const onChange = e => { + setChecked(e.target.checked) + updateData({ + [dataKey]: e.target.checked, + }) + } + + return ( +
+ +
+ ) +} + +export default SwitchControl diff --git a/client/src/features/Thoth/windows/InspectorWindow/index.jsx b/client/src/features/Thoth/windows/InspectorWindow/index.jsx index 548a79ad9..2014b4622 100644 --- a/client/src/features/Thoth/windows/InspectorWindow/index.jsx +++ b/client/src/features/Thoth/windows/InspectorWindow/index.jsx @@ -83,6 +83,12 @@ const Inspector = props => { return ( + {inspectorData.deprecated && ( +
+

WARNING

+

{inspectorData.deprecationMessage}

+
+ )} { store.getState(), filteredTab.spell ) - if (spell?.modules.some(module => module.name === tab.module)) + if ( + spell?.modules && + spell?.modules.some(module => module.name === tab.module) + ) saveSpell({ ...spell }) } }) diff --git a/client/src/features/common/Input/Input.tsx b/client/src/features/common/Input/Input.tsx index 899ef6a97..5576b79a4 100644 --- a/client/src/features/common/Input/Input.tsx +++ b/client/src/features/common/Input/Input.tsx @@ -1,4 +1,4 @@ -const Input = ({ value, onChange = () => {}, style = {}, ...props }) => { +const Input = ({ value, onChange = e => {}, style = {}, ...props }) => { return ( { $CREATE_PLAYTEST, $CREATE_INSPECTOR, $CREATE_TEXT_EDITOR, - // $SERIALIZE, + $SERIALIZE, $EXPORT, } = events @@ -68,9 +68,9 @@ const MenuBar = () => { navigate('/home/all-projects') } - // const onSerialize = () => { - // publish($SERIALIZE(activeTabRef.current.id)); - // }; + const onSerialize = () => { + publish($SERIALIZE(activeTabRef.current.id)) + } const onStateManagerCreate = () => { publish($CREATE_STATE_MANAGER(activeTabRef.current.id)) @@ -153,6 +153,13 @@ const MenuBar = () => { paste: {}, }, }, + dev: { + items: { + serialize: { + onClick: onSerialize, + }, + }, + }, studio: { items: { tools: { diff --git a/client/src/features/common/Node/Node.js b/client/src/features/common/Node/Node.js index bc751994c..4d7c53229 100644 --- a/client/src/features/common/Node/Node.js +++ b/client/src/features/common/Node/Node.js @@ -9,14 +9,21 @@ export class MyNode extends Node { const { node, bindSocket, bindControl } = this.props const { outputs, controls, inputs, selected } = this.state + const name = node.displayName ? node.displayName : node.name + const fullName = node.data.name ? `${name} - ${node.data.name}` : name + return (
+ {node.deprecated &&
}
- {node.data.name ? `${node.name} - ${node.data.name}` : node.name} + {fullName} + {node.deprecated && ( +
DEPRECATED
+ )}
{inputs.length > 0 && ( diff --git a/client/src/features/common/Node/Node.module.css b/client/src/features/common/Node/Node.module.css index ca0f8d7e6..4010c2a4f 100644 --- a/client/src/features/common/Node/Node.module.css +++ b/client/src/features/common/Node/Node.module.css @@ -1,103 +1,124 @@ @keyframes nodeGenesis { - from {opacity: 0; transform: scale(1.2);} - to {opacity: 1; transform: scale(1);} + from { + opacity: 0; + transform: scale(1.2); + } + to { + opacity: 1; + transform: scale(1); + } } .node { - animation: nodeGenesis .2s ease; - transition: background-color .05s ease; - min-width: var(--c40); - width: var(--c40); - border-radius: var(--extraSmall); - border: 1px solid transparent; - box-shadow: 0 4px 5px rgba(0,0,0,0.2); - background-color: rgba(70,70,70,0.7); - color: #aaa; - resize: both; - /* backdrop-filter: blur(3px); */ + animation: nodeGenesis 0.2s ease; + transition: background-color 0.05s ease; + min-width: var(--c40); + width: var(--c40); + border-radius: var(--extraSmall); + border: 1px solid transparent; + box-shadow: 0 4px 5px rgba(0, 0, 0, 0.2); + background-color: rgba(70, 70, 70, 0.7); + color: #aaa; + resize: both; + /* backdrop-filter: blur(3px); */ } .node:hover { - border: 1px solid var(--dark-4); + border: 1px solid var(--dark-4); +} +.node-depricated { + position: absolute; + right: 10px; + color: var(--red); +} +.deprecated-overlay { + background-color: var(--red); + position: absolute; + top: 0; + right: 0; + left: 0; + bottom: 0; + opacity: 0.2; } .node-title { - /* font-family: 'IBM Plex Mono', monospace !important; */ - /* text-transform: uppercase; */ - letter-spacing: .1em; - font-weight: 900; - padding: var(--extraSmall); - padding-bottom: var(--small); - padding-top: var(--small); - border-bottom: 1px solid var(--dark-2); - color: #fff; - background: none; - border-radius: var(--extraSmall) var(--extraSmall) 0px 0px; - display: flex; - opacity: .5; -} -.input, .output { - display: flex; + /* font-family: 'IBM Plex Mono', monospace !important; */ + /* text-transform: uppercase; */ + letter-spacing: 0.1em; + font-weight: 900; + padding: var(--extraSmall); + padding-bottom: var(--small); + padding-top: var(--small); + border-bottom: 1px solid var(--dark-2); + color: #fff; + background: none; + border-radius: var(--extraSmall) var(--extraSmall) 0px 0px; + display: flex; + opacity: 0.5; +} +.input, +.output { + display: flex; } + .connections-container { - display: flex; + display: flex; } .bottom-container { - font-family: 'IBM Plex Mono', monospace !important; - border-top: 1px solid var(--dark-2); - padding: var(--extraSmall); - display: flex; - flex-direction: row; - gap: var(--extraSmall); + font-family: 'IBM Plex Mono', monospace !important; + border-top: 1px solid var(--dark-2); + padding: var(--extraSmall); + display: flex; + flex-direction: column; + gap: var(--extraSmall); } .bottom-container .control { - font-family: 'IBM Plex Mono', monospace !important; - flex: 1; + font-family: 'IBM Plex Mono', monospace !important; + flex: 1; } .bottom-container .control:last-child { - font-family: 'IBM Plex Mono', monospace !important; - flex: .5; + font-family: 'IBM Plex Mono', monospace !important; + flex: 0.5; } .bottom-container .control:first-child { - font-family: 'IBM Plex Mono', monospace !important; - flex: 1; + font-family: 'IBM Plex Mono', monospace !important; + flex: 1; } .control button { - background-color: var(--primary); - width: 100%; - font-family: 'IBM Plex Mono', monospace !important; + background-color: var(--primary); + width: 100%; + font-family: 'IBM Plex Mono', monospace !important; } .control input { - box-sizing: border-box; - width: 100%; + box-sizing: border-box; + width: 100%; } .node.selected { - color: var(--dark-5); - background-color: rgba(70,70,70,0.95); - border-color: var(--primary); - /* backdrop-filter: blur(15px); */ + color: var(--dark-5); + background-color: rgba(70, 70, 70, 0.95); + border-color: var(--primary); + /* backdrop-filter: blur(15px); */ } .node.selected .node-title { - opacity: 1; - border-bottom: 1px solid var(--dark-1); - /* background: linear-gradient(to top left, rgba(80,144,206,0), rgba(57, 159, 228, 0.2)); */ + opacity: 1; + border-bottom: 1px solid var(--dark-1); + /* background: linear-gradient(to top left, rgba(80,144,206,0), rgba(57, 159, 228, 0.2)); */ } .node.selected .bottom-container { - border-top: 1px solid var(--dark-1); + border-top: 1px solid var(--dark-1); } .connection-container { - padding: var(--extraSmall); - flex: 1; + padding: var(--extraSmall); + flex: 1; } .connection-container.out { - background-color: rgba(0,0,0,0.3); + background-color: rgba(0, 0, 0, 0.3); } .input { - justify-content: flex-start; - align-items: center; - display: flex; - + justify-content: flex-start; + align-items: center; + display: flex; } .output { - justify-content: flex-end; - align-items: center; - display: flex; -} \ No newline at end of file + justify-content: flex-end; + align-items: center; + display: flex; +} diff --git a/client/src/features/common/Node/Node.module.css.d.ts b/client/src/features/common/Node/Node.module.css.d.ts index a827d9645..c22f5b09c 100644 --- a/client/src/features/common/Node/Node.module.css.d.ts +++ b/client/src/features/common/Node/Node.module.css.d.ts @@ -5,8 +5,10 @@ interface CssExports { 'connection-container': string; 'connections-container': string; 'control': string; + 'deprecated-overlay': string; 'input': string; 'node': string; + 'node-depricated': string; 'node-title': string; 'nodeGenesis': string; 'out': string; diff --git a/client/src/features/common/Switch/Switch.tsx b/client/src/features/common/Switch/Switch.tsx new file mode 100644 index 000000000..478035c03 --- /dev/null +++ b/client/src/features/common/Switch/Switch.tsx @@ -0,0 +1,15 @@ +import Switch from '@material-ui/core/Switch' +import FormControlLabel from '@material-ui/core/FormControlLabel' + +const SwitchComponent = ({ label: _label, checked, onChange }) => { + const label = { inputProps: { 'aria-label': _label } } + + return ( + } + /> + ) +} + +export default SwitchComponent diff --git a/core/src/components/ActionType.ts b/core/src/components/ActionType.ts index d4e06b6d0..2edb69a7c 100644 --- a/core/src/components/ActionType.ts +++ b/core/src/components/ActionType.ts @@ -34,7 +34,11 @@ const info = `The Action type component will take in an action as text, and atte look, get, use, craft, dialog, movement, travel, combat, consume, other.` -export class ActionTypeComponent extends ThothComponent { +type WorkerReturn = { + actionType: string +} + +export class ActionTypeComponent extends ThothComponent> { constructor() { // Name of the component super('Action Type Classifier') @@ -54,7 +58,7 @@ export class ActionTypeComponent extends ThothComponent { // create inputs here. First argument is the name, second is the type (matched to other components sockets), and third is the socket the i/o will use const inp = new Rete.Input('action', 'Action', stringSocket) const out = new Rete.Output('actionType', 'ActionType', stringSocket) - const dataInput = new Rete.Input('trigger', 'Trigger', triggerSocket) + const dataInput = new Rete.Input('trigger', 'Trigger', triggerSocket, true) const dataOutput = new Rete.Output('trigger', 'Trigger', triggerSocket) const fewshotControl = new FewshotControl({}) diff --git a/core/src/components/AlertMessage.ts b/core/src/components/AlertMessage.ts index 6d6667048..3dacd1195 100644 --- a/core/src/components/AlertMessage.ts +++ b/core/src/components/AlertMessage.ts @@ -8,7 +8,7 @@ import { ThothComponent } from '../thoth-component' const info = `When the alert component is triggered, it will fire an alert with the message in the input box.` -export class Alert extends ThothComponent { +export class Alert extends ThothComponent { constructor() { // Name of the component super('Alert') @@ -26,12 +26,12 @@ export class Alert extends ThothComponent { // to generate the appropriate inputs and ouputs for the fewshot at build time builder(node: ThothNode): ThothNode { // create inputs here. First argument is the name, second is the type (matched to other components sockets), and third is the socket the i/o will use - const dataInput = new Rete.Input('trigger', 'Trigger', triggerSocket) + const dataInput = new Rete.Input('trigger', 'Trigger', triggerSocket, true) const value = node.data.text ? node.data.text : 'Input text here' const input = new TextInputControl({ - emitter: this.editor, + editor: this.editor, key: 'text', value, }) diff --git a/core/src/components/BooleanGate.ts b/core/src/components/BooleanGate.ts index 193fe24e7..d7c896a22 100644 --- a/core/src/components/BooleanGate.ts +++ b/core/src/components/BooleanGate.ts @@ -6,7 +6,7 @@ import { ThothComponent } from '../thoth-component' const info = `The boolean gate takes a boolean input, and depending on whether the value is true or false will only trigger one output or the other.` -export class BooleanGate extends ThothComponent { +export class BooleanGate extends ThothComponent { constructor() { // Name of the component super('Boolean Gate') @@ -23,7 +23,7 @@ export class BooleanGate extends ThothComponent { // to generate the appropriate inputs and ouputs for the fewshot at build time builder(node: ThothNode) { const bool = new Rete.Input('boolean', 'Boolean', booleanSocket) - const dataInput = new Rete.Input('trigger', 'Trigger', triggerSocket) + const dataInput = new Rete.Input('trigger', 'Trigger', triggerSocket, true) const isTrue = new Rete.Output('true', 'True', triggerSocket) const isFalse = new Rete.Output('false', 'False', triggerSocket) diff --git a/core/src/components/Code.ts b/core/src/components/Code.ts index 0a52d3310..15b9f080e 100644 --- a/core/src/components/Code.ts +++ b/core/src/components/Code.ts @@ -29,8 +29,7 @@ const info = `The code component is your swiss army knife when other components Please note that the return of your function must be an object whose keys are the same value as the names given to your output sockets. The incoming inputs argument is an object whose keys are the names you defined, aand each is an array. ` - -export class Code extends ThothComponent { +export class Code extends ThothComponent { constructor() { // Name of the component super('Code') @@ -76,7 +75,7 @@ export class Code extends ThothComponent { .add(outputGenerator) .add(codeControl) - const dataInput = new Rete.Input('trigger', 'Trigger', triggerSocket) + const dataInput = new Rete.Input('trigger', 'Trigger', triggerSocket, true) const dataOutput = new Rete.Output('trigger', 'Trigger', triggerSocket) return node.addOutput(dataOutput).addInput(dataInput) diff --git a/core/src/components/DifficultyDetector.ts b/core/src/components/DifficultyDetector.ts index 4ee771e77..18e1e6e3b 100644 --- a/core/src/components/DifficultyDetector.ts +++ b/core/src/components/DifficultyDetector.ts @@ -47,7 +47,14 @@ strength, dexterity, endurance, intelligence, charisma You can also view and edit the fewshot in the text editor. Note however that you must keep the same data format for the component to properly format the completion response. ` -export class DifficultyDetectorComponent extends ThothComponent { +type WorkerReturn = { + difficulty: string | undefined + category: string | undefined +} + +export class DifficultyDetectorComponent extends ThothComponent< + Promise +> { constructor() { // Name of the component super('Difficulty Detector') @@ -73,7 +80,12 @@ export class DifficultyDetectorComponent extends ThothComponent { const categoryOut = new Rete.Output('category', 'Category', stringSocket) - const triggerInput = new Rete.Input('trigger', 'Trigger', triggerSocket) + const triggerInput = new Rete.Input( + 'trigger', + 'Trigger', + triggerSocket, + true + ) const triggerOutput = new Rete.Output('trigger', 'Trigger', triggerSocket) const fewshotControl = new FewshotControl({}) diff --git a/core/src/components/EnkiTask.ts b/core/src/components/EnkiTask.ts index c807a12dc..8584e7c81 100644 --- a/core/src/components/EnkiTask.ts +++ b/core/src/components/EnkiTask.ts @@ -10,7 +10,10 @@ import { ThothComponent } from '../thoth-component' const info = `Enki is a tool for building both fewshots, as well as entire data sets. The enki component allows you to select an enki which you or someone else has made in the Enki tool and utilize it in your spell chains. Due to current limitations in data structure, the enki inputs and outputs are unnamed, so you will have to know the order of them and what to use them for by referencing their usage in Enki.` -export class EnkiTask extends ThothComponent { + +export class EnkiTask extends ThothComponent< + Promise | null> +> { constructor() { // Name of the component super('Enki Task') diff --git a/core/src/components/EntityDetector.ts b/core/src/components/EntityDetector.ts index 6ce7af87d..add63c7c2 100644 --- a/core/src/components/EntityDetector.ts +++ b/core/src/components/EntityDetector.ts @@ -121,7 +121,18 @@ const info = `The entity detector takes in an action as a string, and attempts t The fewshot can be edited in the text edior, though note that the data structure must remian the same for proper processing.` -export class EntityDetector extends ThothComponent { +type Entity = { + name: string + type: string +} + +type WorkerReturn = { + entities: Entity[] +} + +export class EntityDetector extends ThothComponent< + Promise +> { constructor() { // Name of the component super('Entity Detector') @@ -147,7 +158,7 @@ export class EntityDetector extends ThothComponent { // create inputs here. First argument is the name, second is the type (matched to other components sockets), and third is the socket the i/o will use const inp = new Rete.Input('action', 'Action', stringSocket) const out = new Rete.Output('entities', 'Entities', arraySocket) - const dataInput = new Rete.Input('trigger', 'Trigger', triggerSocket) + const dataInput = new Rete.Input('trigger', 'Trigger', triggerSocket, true) const dataOutput = new Rete.Output('trigger', 'Trigger', triggerSocket) const fewshotControl = new FewshotControl({}) diff --git a/core/src/components/ForEach.ts b/core/src/components/ForEach.ts index d7d1e8549..f55bb66db 100644 --- a/core/src/components/ForEach.ts +++ b/core/src/components/ForEach.ts @@ -10,7 +10,10 @@ import { arraySocket, triggerSocket, anySocket } from '../sockets' import { ThothComponent, ThothTask } from '../thoth-component' const info = `The forEach component takes in an array, and will iterate over each item in the array, firing a new trigger signal with the appropriate value,until all items in the array have beeb processed.` -export class ForEach extends ThothComponent { +type WorkerReturn = { + element: string | string[] | unknown +} +export class ForEach extends ThothComponent> { constructor() { super('ForEach') this.task = { @@ -54,7 +57,7 @@ export class ForEach extends ThothComponent { this._task.closed = ['act'] } else { this._task.closed = ['done'] - return { element: outputs.element } + return { element } } } } diff --git a/core/src/components/Generator.ts b/core/src/components/Generator.ts index 7c85ff49e..766c28198 100644 --- a/core/src/components/Generator.ts +++ b/core/src/components/Generator.ts @@ -19,7 +19,12 @@ Controls have also been added which give you control of some of the fundamental The componet has two returns. The composed will output your entire fewshot plus the completion, whereas the result output will only be the result of the completion. ` -export class Generator extends ThothComponent { +type WorkerReturn = { + result: string + composed: string +} + +export class Generator extends ThothComponent> { constructor() { super('Generator') this.task = { @@ -34,7 +39,7 @@ export class Generator extends ThothComponent { } builder(node: ThothNode) { - const dataIn = new Rete.Input('trigger', 'Trigger', triggerSocket) + const dataIn = new Rete.Input('trigger', 'Trigger', triggerSocket, true) const dataOut = new Rete.Output('trigger', 'Trigger', triggerSocket) const resultOut = new Rete.Output('result', 'Result', stringSocket) const composedOut = new Rete.Output('composed', 'Composed', stringSocket) diff --git a/core/src/components/Huggingface.ts b/core/src/components/Huggingface.ts index 692dc3339..84d37eed5 100644 --- a/core/src/components/Huggingface.ts +++ b/core/src/components/Huggingface.ts @@ -19,7 +19,18 @@ NOTE: Hugginface models are on demand, and sometimes require time to "boot up". Also note that you will likely need to parse the return from huggingface yourself inside a code component, or similar.` -export class HuggingfaceComponent extends ThothComponent { +type WorkerReturn = { + result?: + | { + [key: string]: unknown + error: unknown + } + | undefined +} + +export class HuggingfaceComponent extends ThothComponent< + Promise +> { constructor() { super('Huggingface') this.task = { @@ -34,7 +45,7 @@ export class HuggingfaceComponent extends ThothComponent { } builder(node: ThothNode) { - const triggerIn = new Rete.Input('trigger', 'Trigger', triggerSocket) + const triggerIn = new Rete.Input('trigger', 'Trigger', triggerSocket, true) const triggerOut = new Rete.Output('trigger', 'Trigger', triggerSocket) const errorOut = new Rete.Output('error', 'Error', triggerSocket) const resultOut = new Rete.Output('result', 'Result', stringSocket) diff --git a/core/src/components/Input.ts b/core/src/components/Input.ts index caf4b5702..f0da1bb56 100644 --- a/core/src/components/Input.ts +++ b/core/src/components/Input.ts @@ -1,52 +1,185 @@ +import { isEmpty } from 'lodash' import Rete from 'rete' +import { v4 as uuidv4 } from 'uuid' -import { NodeData, ThothNode } from '../../types' +import { + NodeData, + ThothNode, + ThothWorkerInputs, + ThothWorkerOutputs, +} from '../../types' import { TextInputControl } from '../controls/TextInputControl' -import { stringSocket } from '../sockets' -import { ThothComponent } from '../thoth-component' -const info = `The info component has a single control, an input field. Whatever value you put into this input field will be sent out along the compoonents output socket.` +import { InputControl } from '../dataControls/InputControl' +import { PlaytestControl } from '../dataControls/PlaytestControl' +import { SwitchControl } from '../dataControls/SwitchControl' +import { EngineContext } from '../engine' +import { Task } from '../plugins/taskPlugin/task' + +import { anySocket } from '../sockets' +import { ThothComponent, ThothTask } from '../thoth-component' +const info = `The input component allows you to pass a single value to your chain. You can set a default value to fall back to if no value is provided at runtim. You can also turn the input on to receive data from the playtest input.` + +type InputReturn = { + output: unknown +} + +export class InputComponent extends ThothComponent { + nodeTaskMap: Record = {} -export class InputComponent extends ThothComponent { constructor() { // Name of the component - super('Input') + super('Universal Input') this.task = { outputs: { - text: 'output', + output: 'output', + trigger: 'option', + }, + init: (task = {} as Task, node: ThothNode) => { + this.nodeTaskMap[node.id] = task }, } + this.module = { + nodeType: 'input', + socket: anySocket, + } + this.category = 'I/O' this.info = info + this.display = true + this.contextMenuName = 'Input' + this.displayName = 'Input' + } + + subscriptionMap: Record = {} + + unsubscribe?: () => void + + subscribeToPlaytest(node: ThothNode) { + const { onPlaytest } = this.editor?.thoth as EngineContext + + // check node for the right data attribute + if (onPlaytest) { + // store the unsubscribe function in our node map + this.subscriptionMap[node.id] = onPlaytest((text: string) => { + // if the node doesnt have playtest toggled on, do nothing + const playtestToggle = node.data.playtestToggle as unknown as { + receivePlaytest: boolean + } + if (!playtestToggle.receivePlaytest) return + + // attach the text to the nodes data for access in worker + node.data.text = text + + const task = this.nodeTaskMap[node.id] + + // will need to run this here with the stater rather than the text + task?.run(text) + task?.reset() + this.editor?.trigger('process') + }) + } + } + + destroyed(node: ThothNode) { + if (this.subscriptionMap[node.id]) this.subscriptionMap[node.id]() + delete this.subscriptionMap[node.id] } - // the builder is used to "assemble" the node component. - // when we have enki hooked up and have grabbed all few shots, we would use the builder - // to generate the appropriate inputs and ouputs for the fewshot at build time builder(node: ThothNode) { - // create inputs here. First argument is the name, second is the type (matched to other components sockets), and third is the socket the i/o will use - const out = new Rete.Output('text', 'String', stringSocket) + if (this.subscriptionMap[node.id]) this.subscriptionMap[node.id]() + delete this.subscriptionMap[node.id] - // Handle default value if data is present - const value = node.data.text ? node.data.text : 'Input text here' + // subscribe the node to the playtest input data stream + this.subscribeToPlaytest(node) - // controls are the internals of the node itself - // This default control sample has a text field. + const out = new Rete.Output('output', 'output', anySocket) + + const nameInput = new InputControl({ + dataKey: 'name', + name: 'Input name', + }) + + const data = node?.data?.playtestToggle as + | { + receivePlaytest: boolean + outputs: [] + } + | undefined + + const togglePlaytest = new PlaytestControl({ + dataKey: 'playtestToggle', + name: 'Receive from playtest input', + defaultValue: { + receivePlaytest: data?.receivePlaytest || false, + outputs: data?.outputs || [], + }, + ignored: ['output'], + label: 'Toggle playtest', + }) + + const toggleDefault = new SwitchControl({ + dataKey: 'useDefault', + name: 'Use Default', + label: 'Use Default', + defaultValue: false, + }) + + node.inspector.add(nameInput).add(togglePlaytest).add(toggleDefault) + + const value = node.data.text ? node.data.text : 'Input text here' const input = new TextInputControl({ - emitter: this.editor, + editor: this.editor, key: 'text', value, + label: 'Default value', }) + // module components need to have a socket key. + // todo add this somewhere automated? Maybe wrap the modules builder in the plugin + node.data.socketKey = node?.data?.socketKey || uuidv4() + return node.addOutput(out).addControl(input) } - // the worker contains the main business logic of the node. It will pass those results - // to the outputs to be consumed by any connected components - worker(node: NodeData) { + worker( + node: NodeData, + inputs: ThothWorkerInputs, + outputs: ThothWorkerOutputs, + { silent, data }: { silent: boolean; data: string | undefined } + ) { + this._task.closed = ['trigger'] + + const nodeData = node.data as { + playtestToggle: { receivePlaytest: boolean } + } + + // handle data subscription. If there is data, this is from playtest + if (data && !isEmpty(data) && nodeData.playtestToggle.receivePlaytest) { + this._task.closed = [] + + if (!silent) node.display(data) + return { + output: data, + } + } + + // send default value if use default is explicity toggled on + if (node.data.useDefault) { + return { + output: node.data.text as string, + } + } + + // If there are outputs, we are running as a module input and we use that value + if (outputs.output && !outputs?.output.task) { + return outputs as { output: unknown } + } + + // fallback to default value at the end return { - text: node.data.text as string, + output: node.data.text as string, } } } diff --git a/core/src/components/ItemDetector.ts b/core/src/components/ItemDetector.ts index c67fc8ccf..02a9b882c 100644 --- a/core/src/components/ItemDetector.ts +++ b/core/src/components/ItemDetector.ts @@ -22,7 +22,11 @@ Action, Item: ` const info = `The item detector attempts to recognize what item in a give text string is being mentioned or used. The input is a text string the output is a string of the object` -export class ItemTypeComponent extends ThothComponent { +type WorkerReturn = { + detectedItem: string +} + +export class ItemTypeComponent extends ThothComponent> { constructor() { // Name of the component super('Item Detector') @@ -40,7 +44,7 @@ export class ItemTypeComponent extends ThothComponent { node.data.fewshot = fewshot const inp = new Rete.Input('string', 'Text', stringSocket) const out = new Rete.Output('detectedItem', 'Item Detected', stringSocket) - const dataInput = new Rete.Input('trigger', 'Trigger', triggerSocket) + const dataInput = new Rete.Input('trigger', 'Trigger', triggerSocket, true) const dataOutput = new Rete.Output('trigger', 'Trigger', triggerSocket) const fewshotControl = new FewshotControl({}) diff --git a/core/src/components/JoinList.ts b/core/src/components/JoinList.ts index 374ef2e7f..9d6485850 100644 --- a/core/src/components/JoinList.ts +++ b/core/src/components/JoinList.ts @@ -6,7 +6,11 @@ import { stringSocket, arraySocket } from '../sockets' import { ThothComponent } from '../thoth-component' const info = `The Join List component takes in an array, and will join each item in the array together with a seperator, defined in the components input field.` -export class JoinListComponent extends ThothComponent { +type WorkerReturn = { + text: string +} + +export class JoinListComponent extends ThothComponent { constructor() { // Name of the component super('Join List') @@ -40,7 +44,7 @@ export class JoinListComponent extends ThothComponent { // controls are the internals of the node itself // This default control sample has a text field. const input = new TextInputControl({ - emitter: this.editor, + editor: this.editor, key: 'separator', value: separator, }) diff --git a/core/src/components/Module.ts b/core/src/components/Module.ts index 92144e181..39cb0ba25 100644 --- a/core/src/components/Module.ts +++ b/core/src/components/Module.ts @@ -2,18 +2,18 @@ import isEqual from 'lodash/isEqual' import { ModuleType, + ModuleWorkerOutput, NodeData, ThothNode, ThothWorkerInputs, - ThothWorkerOutputs, } from '../../types' import { ModuleControl } from '../dataControls/ModuleControl' import { Task } from '../plugins/taskPlugin/task' import { ThothComponent } from '../thoth-component' const info = `The Module component allows you to add modules into your chain. A module is a bundled self contained chain that defines inputs, outputs, and triggers using components.` -export class ModuleComponent extends ThothComponent { - module + +export class ModuleComponent extends ThothComponent { _task: Task updateModuleSockets: Function task @@ -98,13 +98,13 @@ export class ModuleComponent extends ThothComponent { node: NodeData, inputs: ThothWorkerInputs, outputs: { [key: string]: string }, - { module }: { module: { outputs: ThothWorkerOutputs[] } } + { module }: { module: { outputs: ModuleWorkerOutput[] } } ) { const open = Object.entries(module.outputs) .filter(([, value]) => typeof value === 'boolean' && value) .map(([key]) => key) // close all triggers first - const dataOutputs = node.data.outputs as ThothWorkerOutputs[] + const dataOutputs = node.data.outputs as ModuleWorkerOutput[] this._task.closed = dataOutputs .map((out: { name: string }) => out.name) .filter((out: string) => !open.includes(out)) diff --git a/core/src/components/Output.ts b/core/src/components/Output.ts new file mode 100644 index 000000000..62f860003 --- /dev/null +++ b/core/src/components/Output.ts @@ -0,0 +1,93 @@ +import Rete from 'rete' +import { v4 as uuidv4 } from 'uuid' + +import { + NodeData, + ThothNode, + ThothWorkerInputs, + ThothWorkerOutputs, +} from '../../types' +import { InputControl } from '../dataControls/InputControl' +import { SwitchControl } from '../dataControls/SwitchControl' +import { EngineContext } from '../engine' +import { triggerSocket, anySocket } from '../sockets' +import { ThothComponent } from '../thoth-component' +const info = `The output component will pass values out from your spell. You can have multiple outputs in a spell and all output values willbe collected. It also has an option to send the output to the playtest area for easy testing.` + +export class Output extends ThothComponent { + constructor() { + super('Output') + + this.task = { + runOneInput: true, + outputs: { + text: 'output', + trigger: 'option', + }, + } + + this.module = { + nodeType: 'output', + socket: anySocket, + } + + this.category = 'I/O' + this.display = true + this.info = info + } + + builder(node: ThothNode) { + const triggerInput = new Rete.Input( + 'trigger', + 'Trigger', + triggerSocket, + true + ) + const triggerOutput = new Rete.Output('trigger', 'Trigger', triggerSocket) + const textInput = new Rete.Input('input', 'Outputs', anySocket, true) + + const nameInput = new InputControl({ + dataKey: 'name', + name: 'Output name', + }) + + const switchControl = new SwitchControl({ + dataKey: 'sendToPlaytest', + name: 'Send to Playtest', + label: 'Playtest', + defaultValue: node.data.sendToPlaytest || false, + }) + + node.inspector.add(switchControl).add(nameInput) + // need to automate this part! Wont workw without a socket key + node.data.socketKey = node?.data?.socketKey || uuidv4() + + return node + .addInput(textInput) + .addInput(triggerInput) + .addOutput(triggerOutput) + } + + worker( + node: NodeData, + inputs: ThothWorkerInputs, + outputs: ThothWorkerOutputs, + { silent, thoth }: { silent: boolean; thoth: EngineContext } + ) { + console.log('INPUTS', inputs) + + const text = inputs.input.filter(Boolean)[0] + + //just need a new check here for playtest send boolean + const { sendToPlaytest } = thoth + + if (node.data.sendToPlaytest && sendToPlaytest) { + sendToPlaytest(text) + } + if (!silent) node.display(text as string) + + return { + text, + } + } +} diff --git a/core/src/components/ProseToScript.ts b/core/src/components/ProseToScript.ts index ef5a292c2..8c6ef0299 100644 --- a/core/src/components/ProseToScript.ts +++ b/core/src/components/ProseToScript.ts @@ -66,7 +66,11 @@ Rewritten as a script: const info = `The prose to script converter transforms narrative prose into a screenplay-style script, attributing dialogue to characters in the scene, and discarding all text that is not speech. The input is a text string the output is a string of the script` -export class ProseToScript extends ThothComponent { +type WorkerReturn = { + detectedItem: string +} + +export class ProseToScript extends ThothComponent> { constructor() { // Name of the component super('Prose to Script') @@ -85,7 +89,7 @@ export class ProseToScript extends ThothComponent { node.data.fewshot = fewshot const inp = new Rete.Input('string', 'Text', stringSocket) const out = new Rete.Output('script', 'Script', stringSocket) - const dataInput = new Rete.Input('trigger', 'Trigger', triggerSocket) + const dataInput = new Rete.Input('trigger', 'Trigger', triggerSocket, true) const dataOutput = new Rete.Output('trigger', 'Trigger', triggerSocket) //const fewshotControl = new FewshotControl(); diff --git a/core/src/components/SafetyVerifier.ts b/core/src/components/SafetyVerifier.ts index e21555058..c46af35ee 100644 --- a/core/src/components/SafetyVerifier.ts +++ b/core/src/components/SafetyVerifier.ts @@ -80,7 +80,11 @@ const info = `The Safety Verifier component takes a string and attempts to class The fewshot can be edited in the text editor, however it contains content which may be triggering to some individuals. If you modify the fewshot, note that it must remian in the format for the processing to work.` -export class SafetyVerifier extends ThothComponent { +type WorkerReturn = { + boolean: boolean +} + +export class SafetyVerifier extends ThothComponent> { constructor() { // Name of the component super('Safety Verifier') @@ -100,7 +104,7 @@ export class SafetyVerifier extends ThothComponent { node.data.fewshot = fewshot const inp = new Rete.Input('string', 'Text', stringSocket) - const dataInput = new Rete.Input('trigger', 'Trigger', triggerSocket) + const dataInput = new Rete.Input('trigger', 'Trigger', triggerSocket, true) const dataOutput = new Rete.Output('trigger', 'Trigger', triggerSocket) const out = new Rete.Output('boolean', 'Boolean', booleanSocket) diff --git a/core/src/components/StateRead.ts b/core/src/components/StateRead.ts index 3e24852ed..6bf05169d 100644 --- a/core/src/components/StateRead.ts +++ b/core/src/components/StateRead.ts @@ -8,7 +8,9 @@ import { SocketGeneratorControl } from '../dataControls/SocketGenerator' import { EngineContext } from '../engine' import { ThothComponent } from '../thoth-component' const info = `The State Read component allows you to read values from the state. These can be found in and are managed by the State Manager window. This window consists of a JSON object. You can define any number ouf outputs where an outputs name corresponds to a key in the state manager. Whatever value is assigned to that key will be read ans passed into your chain.` -export class StateRead extends ThothComponent { +export class StateRead extends ThothComponent< + Promise> +> { constructor() { // Name of the component super('State Read') diff --git a/core/src/components/StateWrite.js b/core/src/components/StateWrite.js index 407d61427..5e0b1e727 100644 --- a/core/src/components/StateWrite.js +++ b/core/src/components/StateWrite.js @@ -23,7 +23,7 @@ export class StateWrite extends ThothComponent { } builder(node) { - const dataInput = new Rete.Input('trigger', 'Trigger', triggerSocket) + const dataInput = new Rete.Input('trigger', 'Trigger', triggerSocket, true) const inputGenerator = new SocketGeneratorControl({ connectionType: 'input', diff --git a/core/src/components/StringProcessor.ts b/core/src/components/StringProcessor.ts index 08982764b..03cf50472 100644 --- a/core/src/components/StringProcessor.ts +++ b/core/src/components/StringProcessor.ts @@ -9,7 +9,7 @@ const info = `The String Processor component take s astring as an input and allo Note that the return value of your function must be an objetc whose keys match the names of your generated output sockets.` -export class StringProcessor extends ThothComponent { +export class StringProcessor extends ThothComponent> { constructor() { // Name of the component super('String Processor') @@ -32,7 +32,7 @@ export class StringProcessor extends ThothComponent { // Rete controls const input = new Rete.Input('input', 'Input', stringSocket) - const triggerIn = new Rete.Input('trigger', 'Trigger', triggerSocket) + const triggerIn = new Rete.Input('trigger', 'Trigger', triggerSocket, true) const triggerOut = new Rete.Output('trigger', 'Trigger', triggerSocket) // Inspector controls diff --git a/core/src/components/SwitchGate.ts b/core/src/components/SwitchGate.ts index c8bad2eb3..47653f9cd 100644 --- a/core/src/components/SwitchGate.ts +++ b/core/src/components/SwitchGate.ts @@ -15,7 +15,7 @@ import { ThothComponent } from '../thoth-component' const info = `The Switch Gate component takes a single input, and allows you to define any number of outputs. Its works the same as the javascript switch. The component will try to match the value of the input to one of the output socketnames you have created. It will route the trigger signal through that socket.` -export class SwitchGate extends ThothComponent { +export class SwitchGate extends ThothComponent { constructor() { // Name of the component super('Switch') @@ -41,7 +41,7 @@ export class SwitchGate extends ThothComponent { node.inspector.add(outputGenerator) const input = new Rete.Input('input', 'Input', anySocket) - const dataInput = new Rete.Input('trigger', 'Trigger', triggerSocket) + const dataInput = new Rete.Input('trigger', 'Trigger', triggerSocket, true) const defaultOutput = new Rete.Output('default', 'Default', triggerSocket) node.addInput(input).addInput(dataInput).addOutput(defaultOutput) diff --git a/core/src/components/TenseTransformer.ts b/core/src/components/TenseTransformer.ts index f815da72a..12ac7cebc 100644 --- a/core/src/components/TenseTransformer.ts +++ b/core/src/components/TenseTransformer.ts @@ -74,7 +74,11 @@ Third Person: Fred commands the mercenaries to attack the dragon while he rescue const info = `The Tense Transformer will take any string and attempt to turn it into the first person present tense. It requires a name and text as an input, and will output the result. You can edit the fewshot in the text editor, but be aware that you must retain the fewshots data structure so processing will work.` -export class TenseTransformer extends ThothComponent { + +type WorkerReturn = { + action: string +} +export class TenseTransformer extends ThothComponent> { constructor() { // Name of the component super('Tense Transformer') @@ -100,7 +104,7 @@ export class TenseTransformer extends ThothComponent { // create inputs here. First argument is the name, second is the type (matched to other components sockets), and third is the socket the i/o will use const textInput = new Rete.Input('text', 'Text', stringSocket) const nameInput = new Rete.Input('name', 'Name', stringSocket) - const dataInput = new Rete.Input('trigger', 'Trigger', triggerSocket) + const dataInput = new Rete.Input('trigger', 'Trigger', triggerSocket, true) const dataOutput = new Rete.Output('trigger', 'Trigger', triggerSocket) const out = new Rete.Output('action', 'Action', stringSocket) @@ -141,7 +145,7 @@ export class TenseTransformer extends ThothComponent { if (!silent) node.display(result) return { - action: result, + action: result } } } diff --git a/core/src/components/TimeDetector.ts b/core/src/components/TimeDetector.ts index 1d4283735..af157aedc 100644 --- a/core/src/components/TimeDetector.ts +++ b/core/src/components/TimeDetector.ts @@ -38,7 +38,13 @@ seconds, minutes, hours, days, weeks, years. You can edit the fewshot in the text editor, but be aware that you must retain the fewshots data structure so processing will work.` -export class TimeDetectorComponent extends ThothComponent { +type WorkerReturn = { + detectedTime: string +} + +export class TimeDetectorComponent extends ThothComponent< + Promise +> { constructor() { super('Time Detector') @@ -55,7 +61,7 @@ export class TimeDetectorComponent extends ThothComponent { node.data.fewshot = fewshot const inp = new Rete.Input('string', 'Text', stringSocket) const out = new Rete.Output('detectedTime', 'Time Detected', stringSocket) - const dataInput = new Rete.Input('trigger', 'Trigger', triggerSocket) + const dataInput = new Rete.Input('trigger', 'Trigger', triggerSocket, true) const dataOutput = new Rete.Output('trigger', 'Trigger', triggerSocket) const fewshotControl = new FewshotControl({}) diff --git a/core/src/components/ModuleTriggerIn.ts b/core/src/components/TriggerIn.ts similarity index 85% rename from core/src/components/ModuleTriggerIn.ts rename to core/src/components/TriggerIn.ts index 4950ff066..8ff4c8993 100644 --- a/core/src/components/ModuleTriggerIn.ts +++ b/core/src/components/TriggerIn.ts @@ -9,11 +9,10 @@ import { InputControl } from '../dataControls/InputControl' import { TaskOptions } from '../plugins/taskPlugin/task' import { triggerSocket } from '../sockets' import { ThothComponent, ThothTask } from '../thoth-component' -const info = `The module trigger in adds a trigger input socket to the parent module. It can be given a name, which is displayed on the parent.` +const info = `The trigger in allows you to pass values into your spell either from a higher level component or fromthe server. Theremust be one single dtrigger in to a spell for now as the server does not support multiple triggers. Yet.` -export class ModuleTriggerIn extends ThothComponent { +export class TriggerIn extends ThothComponent { task: TaskOptions - module: object category: string info: string contextMenuName: string @@ -23,6 +22,7 @@ export class ModuleTriggerIn extends ThothComponent { // Name of the component // If name of component changes please update module-manager workerModule code super('Module Trigger In') + this.displayName = 'Trigger In' this.contextMenuName = 'Trigger In' this.task = { @@ -40,7 +40,8 @@ export class ModuleTriggerIn extends ThothComponent { socket: triggerSocket, } - this.category = 'Module' + this.category = 'I/O' + this.info = info } diff --git a/core/src/components/ModuleTriggerOut.ts b/core/src/components/TriggerOut.ts similarity index 79% rename from core/src/components/ModuleTriggerOut.ts rename to core/src/components/TriggerOut.ts index 37a5e9482..91d92da08 100644 --- a/core/src/components/ModuleTriggerOut.ts +++ b/core/src/components/TriggerOut.ts @@ -9,11 +9,14 @@ import { InputControl } from '../dataControls/InputControl' import { TaskOptions } from '../plugins/taskPlugin/task' import { triggerSocket } from '../sockets' import { ThothComponent } from '../thoth-component' -const info = `The module trigger out component adds a trigger out socket to the parent module. It can be given a name, which is displayed on the parent.` +const info = `The trigger out component is mainly used to add an output to a spell when it is being run as a module, ie inside a component of another spell. It will pass the trigger signal out of the spell to the higher level spell.` -export class ModuleTriggerOut extends ThothComponent { +type WorkerReturn = { + trigger: boolean +} + +export class TriggerOut extends ThothComponent { task: TaskOptions - module: object category: string info: string contextMenuName: string @@ -34,7 +37,8 @@ export class ModuleTriggerOut extends ThothComponent { socket: triggerSocket, } - this.category = 'Module' + this.category = 'I/O' + this.displayName = 'Trigger Out' this.info = info } @@ -43,7 +47,7 @@ export class ModuleTriggerOut extends ThothComponent { // to generate the appropriate inputs and outputs for the fewshot at build time builder(node: ThothNode) { // create inputs here. First argument is the name, second is the type (matched to other components sockets), and third is the socket the i/o will use - const input = new Rete.Input('trigger', 'Trigger', triggerSocket) + const input = new Rete.Input('trigger', 'Trigger', triggerSocket, true) // Handle default value if data is present const nameInput = new InputControl({ diff --git a/core/src/components/components.ts b/core/src/components/components.ts index 59b2fb1a2..fab2cf41c 100644 --- a/core/src/components/components.ts +++ b/core/src/components/components.ts @@ -9,17 +9,19 @@ import { ForEach } from './ForEach' import { Generator } from './Generator' import { HuggingfaceComponent } from './Huggingface' import { InputComponent } from './Input' +import { InputFieldComponent } from './deprecated/InputField' import { ItemTypeComponent } from './ItemDetector' import { JoinListComponent } from './JoinList' import { ModuleComponent } from './Module' -import { ModuleInput } from './ModuleInput' -import { ModuleOutput } from './ModuleOutput' -import { ModuleTriggerIn } from './ModuleTriggerIn' -import { ModuleTriggerOut } from './ModuleTriggerOut' -import { PlaytestInput } from './PlaytestInput' -import { PlaytestPrint } from './PlaytestPrint' +import { ModuleInput } from './deprecated/ModuleInput' +import { ModuleOutput } from './deprecated/ModuleOutput' +import { TriggerIn } from './TriggerIn' +import { TriggerOut } from './TriggerOut' +import { Output } from './Output' +import { PlaytestInput } from './deprecated/PlaytestInput' +import { PlaytestPrint } from './deprecated/PlaytestPrint' import { ProseToScript } from './ProseToScript' -import { RunInputComponent } from './RunInput' +import { RunInputComponent } from './deprecated/RunInput' import { SafetyVerifier } from './SafetyVerifier' import { StateRead } from './StateRead' import { StateWrite } from './StateWrite' @@ -43,15 +45,16 @@ export const components = { generator: () => new Generator(), huggingfaceComponent: () => new HuggingfaceComponent(), inputComponent: () => new InputComponent(), + inputFieldComponent: () => new InputFieldComponent(), itemTypeComponent: () => new ItemTypeComponent(), joinListComponent: () => new JoinListComponent(), moduleComponent: () => new ModuleComponent(), moduleInput: () => new ModuleInput(), moduleOutput: () => new ModuleOutput(), - moduleTriggerOut: () => new ModuleTriggerOut(), - moduleTriggerIn: () => new ModuleTriggerIn(), + output: () => new Output(), playtestPrint: () => new PlaytestPrint(), playtestInput: () => new PlaytestInput(), + proseToScript: () => new ProseToScript(), runInputCompnent: () => new RunInputComponent(), safetyVerifier: () => new SafetyVerifier(), stateWrite: () => new StateWrite(), @@ -60,7 +63,8 @@ export const components = { switchGate: () => new SwitchGate(), tenseTransformer: () => new TenseTransformer(), timeDetectorComponent: () => new TimeDetectorComponent(), - proseToScript: () => new ProseToScript(), + TriggerIn: () => new TriggerIn(), + triggerOut: () => new TriggerOut(), } export const getComponents = () => { diff --git a/core/src/components/deprecated/InputField.ts b/core/src/components/deprecated/InputField.ts new file mode 100644 index 000000000..1f407e129 --- /dev/null +++ b/core/src/components/deprecated/InputField.ts @@ -0,0 +1,59 @@ +import Rete from 'rete' + +import { NodeData, ThothNode } from '../../../types' +import { TextInputControl } from '../../controls/TextInputControl' +import { stringSocket } from '../../sockets' +import { ThothComponent } from '../../thoth-component' +const info = `The info component has a single control, an input field. Whatever value you put into this input field will be sent out along the compoonents output socket.` + +type WorkerReturn = { + text: string +} + +export class InputFieldComponent extends ThothComponent { + constructor() { + // Name of the component + super('Input') + + this.task = { + outputs: { + text: 'output', + }, + } + + this.category = 'I/O' + this.info = info + this.deprecated = true + this.deprecationMessage = + 'This component has been deprecated. Please switch all your spells to use the new universal input component found under the name "Input" under the IO category. It allows you to add a default value, which was the previous purpose of this component.' + } + + // the builder is used to "assemble" the node component. + // when we have enki hooked up and have grabbed all few shots, we would use the builder + // to generate the appropriate inputs and ouputs for the fewshot at build time + builder(node: ThothNode) { + // create inputs here. First argument is the name, second is the type (matched to other components sockets), and third is the socket the i/o will use + const out = new Rete.Output('text', 'String', stringSocket) + + // Handle default value if data is present + const value = node.data.text ? node.data.text : 'Input text here' + + // controls are the internals of the node itself + // This default control sample has a text field. + const input = new TextInputControl({ + editor: this.editor, + key: 'text', + value, + }) + + return node.addOutput(out).addControl(input) + } + + // the worker contains the main business logic of the node. It will pass those results + // to the outputs to be consumed by any connected components + worker(node: NodeData) { + return { + text: node.data.text as string, + } + } +} diff --git a/core/src/components/ModuleInput.ts b/core/src/components/deprecated/ModuleInput.ts similarity index 73% rename from core/src/components/ModuleInput.ts rename to core/src/components/deprecated/ModuleInput.ts index 1a200e5c6..d2b4a218a 100644 --- a/core/src/components/ModuleInput.ts +++ b/core/src/components/deprecated/ModuleInput.ts @@ -9,16 +9,15 @@ import { ThothNode, ThothWorkerInputs, ThothWorkerOutputs, -} from '../../types' -import { InputControl } from '../dataControls/InputControl' -import { TaskOptions } from '../plugins/taskPlugin/task' -import { anySocket } from '../sockets' -import { ThothComponent } from '../thoth-component' +} from '../../../types' +import { InputControl } from '../../dataControls/InputControl' +import { TaskOptions } from '../../plugins/taskPlugin/task' +import { anySocket } from '../../sockets' +import { ThothComponent } from '../../thoth-component' const info = `The module input component adds an input socket to the parent module. It can be given a name, which is displayed on the parent.` -export class ModuleInput extends ThothComponent { +export class ModuleInput extends ThothComponent> { task: TaskOptions - module: object category: string info: string contextMenuName: string @@ -39,6 +38,9 @@ export class ModuleInput extends ThothComponent { } this.category = 'Module' + this.deprecated = true + this.deprecationMessage = + 'This component has been deprecated. Please replace it in all of your spells with the new universal input component. This can be found under the name "Input" in the IO category. It will act as a module input for running your spells on the server and inside other component chains.' this.info = info } diff --git a/core/src/components/ModuleOutput.ts b/core/src/components/deprecated/ModuleOutput.ts similarity index 72% rename from core/src/components/ModuleOutput.ts rename to core/src/components/deprecated/ModuleOutput.ts index edad48127..9d4c0365b 100644 --- a/core/src/components/ModuleOutput.ts +++ b/core/src/components/deprecated/ModuleOutput.ts @@ -1,19 +1,22 @@ import Rete from 'rete' import { v4 as uuidv4 } from 'uuid' -import { NodeData, ThothNode, ThothWorkerInputs } from '../../types' -import { InputControl } from '../dataControls/InputControl' -import { TaskOptions } from '../plugins/taskPlugin/task' +import { NodeData, ThothNode, ThothWorkerInputs } from '../../../types' +import { InputControl } from '../../dataControls/InputControl' +import { TaskOptions } from '../../plugins/taskPlugin/task' // @seang todo: convert data controls to typescript to remove this // eslint-disable-next-line @typescript-eslint/ban-ts-comment //@ts-ignore -import { anySocket, triggerSocket } from '../sockets' -import { ThothComponent } from '../thoth-component' +import { anySocket, triggerSocket } from '../../sockets' +import { ThothComponent } from '../../thoth-component' const info = `The module output component adds an output socket to the parent module. It can be given a name, which is displayed on the parent.` -export class ModuleOutput extends ThothComponent { +type WorkerReturn = { + text: string +} + +export class ModuleOutput extends ThothComponent { task: TaskOptions - module: object category: string info: string contextMenuName: string @@ -36,6 +39,9 @@ export class ModuleOutput extends ThothComponent { } this.category = 'Module' + this.deprecated = true + this.deprecationMessage = + 'This component has been deprecated. Please remove it from your spells. You can use in its place the general output component which act aas an output for both server deployments and running spells inside coother components.' this.info = info } diff --git a/core/src/components/PlaytestInput.ts b/core/src/components/deprecated/PlaytestInput.ts similarity index 81% rename from core/src/components/PlaytestInput.ts rename to core/src/components/deprecated/PlaytestInput.ts index 56da16882..5f87f087a 100644 --- a/core/src/components/PlaytestInput.ts +++ b/core/src/components/deprecated/PlaytestInput.ts @@ -5,13 +5,18 @@ import { ThothNode, ThothWorkerInputs, ThothWorkerOutputs, -} from '../../types' -import { EngineContext } from '../engine' -import { Task } from '../plugins/taskPlugin/task' -import { triggerSocket, stringSocket } from '../sockets' -import { ThothComponent, ThothTask } from '../thoth-component' +} from '../../../types' +import { EngineContext } from '../../engine' +import { Task } from '../../plugins/taskPlugin/task' +import { triggerSocket, stringSocket } from '../../sockets' +import { ThothComponent, ThothTask } from '../../thoth-component' const info = `The Playtest Input component is connected to the playtest window. It received anything which is type dinto the playtest areavia the input and will trigger the running of your spell chain.` -export class PlaytestInput extends ThothComponent { + +type WorkerReturn = { + text: string +} + +export class PlaytestInput extends ThothComponent { initialTask?: Task nodeTaskMap: Record = {} @@ -31,6 +36,9 @@ export class PlaytestInput extends ThothComponent { this.category = 'I/O' this.display = true + this.deprecated = true + this.deprecationMessage = + 'This component has been deprecated in favor of a universal input component. You can find as under the name "Input" in the IO category. It has a toggle which will allow you to receive signals from the playtest.' this.info = info } diff --git a/core/src/components/PlaytestPrint.ts b/core/src/components/deprecated/PlaytestPrint.ts similarity index 78% rename from core/src/components/PlaytestPrint.ts rename to core/src/components/deprecated/PlaytestPrint.ts index d948b9bfd..b77de0ad0 100644 --- a/core/src/components/PlaytestPrint.ts +++ b/core/src/components/deprecated/PlaytestPrint.ts @@ -5,12 +5,12 @@ import { ThothNode, ThothWorkerInputs, ThothWorkerOutputs, -} from '../../types' -import { EngineContext } from '../engine' -import { triggerSocket, anySocket } from '../sockets' -import { ThothComponent } from '../thoth-component' +} from '../../../types' +import { EngineContext } from '../../engine' +import { triggerSocket, anySocket } from '../../sockets' +import { ThothComponent } from '../../thoth-component' const info = `The Playtest Print component will print whatever value is attached to its input and print that valyue back to the playtest window.` -export class PlaytestPrint extends ThothComponent { +export class PlaytestPrint extends ThothComponent { constructor() { // Name of the component super('Playtest Print') @@ -24,6 +24,9 @@ export class PlaytestPrint extends ThothComponent { this.category = 'I/O' this.display = true + this.deprecated = true + this.deprecationMessage = + 'This component has been deprecated. Please remove it from your spells. You can replace it with the general "output" component, which has an opion in the inspector to send the outputs value to the playtest window.' this.info = info } diff --git a/core/src/components/RunInput.ts b/core/src/components/deprecated/RunInput.ts similarity index 76% rename from core/src/components/RunInput.ts rename to core/src/components/deprecated/RunInput.ts index 0c16f0e50..a4c399579 100644 --- a/core/src/components/RunInput.ts +++ b/core/src/components/deprecated/RunInput.ts @@ -1,14 +1,18 @@ import Rete, { Control } from 'rete' -import { NodeData, ThothNode } from '../../types' -import { RunButtonControl } from '../controls/RunButtonControl' -import { TextInputControl } from '../controls/TextInputControl' -import { Task } from '../plugins/taskPlugin/task' -import { stringSocket, triggerSocket } from '../sockets' -import { ThothComponent, ThothTask } from '../thoth-component' +import { NodeData, ThothNode } from '../../../types' +import { RunButtonControl } from '../../controls/RunButtonControl' +import { TextInputControl } from '../../controls/TextInputControl' +import { Task } from '../../plugins/taskPlugin/task' +import { stringSocket, triggerSocket } from '../../sockets' +import { ThothComponent, ThothTask } from '../../thoth-component' const info = `The Input With Run component lets you input a value into the provided input field, and trigger off your spell chain to run with that value passed out its output. May be depricated in favor of using the playtest input component.` -export class RunInputComponent extends ThothComponent { +type WorkerReturn = { + text: string +} + +export class RunInputComponent extends ThothComponent { initialTask?: Task subscriptionMap: any @@ -43,6 +47,9 @@ export class RunInputComponent extends ThothComponent { }, } this.category = 'I/O' + this.deprecated = true + this.deprecationMessage = + 'This component has been deprecated. Please remove it fromall spells. It can be replaced generally with the universal input component found under the name "Input" in the IO category. You can use the playtest toggle to receive a value from the playtest which will trigger a run command.' this.info = info } @@ -66,7 +73,7 @@ export class RunInputComponent extends ThothComponent { // controls are the internals of the node itself // This default control sample has a text field. const input = new TextInputControl({ - emitter: this.editor, + editor: this.editor, key: 'text', value: node.data.text || 'Input text', }) diff --git a/core/src/controls/TextInputControl.js b/core/src/controls/TextInputControl.js index decdfd88f..13e31a85a 100644 --- a/core/src/controls/TextInputControl.js +++ b/core/src/controls/TextInputControl.js @@ -12,21 +12,35 @@ const ReactTextInputControl = props => { const onChange = e => { props.putData(props.name, e.target.value) setValue(e.target.value) + props.editor.trigger('save') } - return + return ( + <> + {props.label && } + + + ) } export class TextInputControl extends Control { - constructor({ emitter, key, value }) { + constructor({ editor, key, value, ...rest }) { super(key) this.render = 'react' this.component = ReactTextInputControl + const label = rest.label || null + // we define the props that are passed into the rendered react component here this.props = { - emitter, + editor, name: key, + label, value, putData: (...args) => this.putData.apply(this, args), } diff --git a/core/src/dataControls/EmptyControl.js b/core/src/dataControls/EmptyControl.js new file mode 100644 index 000000000..c64b98576 --- /dev/null +++ b/core/src/dataControls/EmptyControl.js @@ -0,0 +1,20 @@ +import { DataControl } from '../plugins/inspectorPlugin' + +export class EmptyControl extends DataControl { + constructor({ dataKey, ignored = [] }) { + const options = { + dataKey: dataKey, + name: 'empty', + component: 'none', + data: { + ignored, + }, + } + + super(options) + } + + onData() { + return + } +} diff --git a/core/src/dataControls/PlaytestControl.js b/core/src/dataControls/PlaytestControl.js new file mode 100644 index 000000000..7806f2c0f --- /dev/null +++ b/core/src/dataControls/PlaytestControl.js @@ -0,0 +1,30 @@ +import { DataControl } from '../plugins/inspectorPlugin' + +export class PlaytestControl extends DataControl { + constructor({ + dataKey, + name, + icon = 'hand', + label = 'Toggle', + defaultValue = {}, + ignored = [], + }) { + const options = { + dataKey: dataKey, + defaultValue, + name, + component: 'playtest', + icon, + data: { + label, + ignored, + }, + } + + super(options) + } + + onData(playtestToggle) { + return + } +} diff --git a/core/src/dataControls/SwitchControl.ts b/core/src/dataControls/SwitchControl.ts new file mode 100644 index 000000000..acb94b9d9 --- /dev/null +++ b/core/src/dataControls/SwitchControl.ts @@ -0,0 +1,32 @@ +import { DataControl } from '../plugins/inspectorPlugin' + +export class SwitchControl extends DataControl { + constructor({ + dataKey, + name, + icon = 'hand', + label = 'Toggle', + defaultValue = {}, + }: { + dataKey: string + name: string + icon?: string + label: string + defaultValue?: unknown + }) { + super({ + dataKey: dataKey, + defaultValue, + name, + component: 'switch', + icon, + data: { + label, + }, + }) + } + + onData = () => { + return + } +} diff --git a/core/src/editor.ts b/core/src/editor.ts index 1942a542a..44fd5a440 100644 --- a/core/src/editor.ts +++ b/core/src/editor.ts @@ -17,7 +17,7 @@ import ModulePlugin from './plugins/modulePlugin' import { ModuleManager } from './plugins/modulePlugin/module-manager' import SocketGenerator from './plugins/socketGenerator' import TaskPlugin from './plugins/taskPlugin' -import { PubSubContext } from './thoth-component' +import { PubSubContext, ThothComponent } from './thoth-component' export class ThothEditor extends NodeEditor { pubSub: PubSubContext thoth: EngineContext @@ -89,15 +89,12 @@ export const initEditor = async function ({ rename(component: { contextMenuName: any; name: any }) { return component.contextMenuName || component.name }, - allocate: (component: { - editor: ThothEditor - workspaceType: unknown - category: string - }) => { + allocate: (component: ThothComponent) => { //@seang: disabling component filtering in anticipation of needing to treat spells as "top level modules" in the publishing workflow const tabType = editor.tab.type const { workspaceType } = component + if (component.deprecated) return null if (workspaceType && workspaceType !== tabType) return null return [component.category] }, diff --git a/core/src/engine.ts b/core/src/engine.ts index 4b737cfbe..57aa98262 100644 --- a/core/src/engine.ts +++ b/core/src/engine.ts @@ -8,7 +8,6 @@ import { OpenAIResultChoice, Spell, ThothWorkerInputs, - WorkerReturn, } from '../types' import ModulePlugin from './plugins/modulePlugin' import TaskPlugin from './plugins/taskPlugin' @@ -16,7 +15,7 @@ import TaskPlugin from './plugins/taskPlugin' interface WorkerOutputs { [key: string]: unknown } -export abstract class ThothEngineComponent { +export abstract class ThothEngineComponent { // Original Class: https://github.com/latitudegames/rete/blob/master/src/engine/component.ts name: string data: unknown = {} @@ -31,7 +30,7 @@ export abstract class ThothEngineComponent { inputs: ThothWorkerInputs, outputs: WorkerOutputs, ...args: unknown[] - ): WorkerReturn + ): WorkerReturnType } export type EngineContext = { completion: ( diff --git a/core/src/plugins/inspectorPlugin/DataControl.ts b/core/src/plugins/inspectorPlugin/DataControl.ts index 6ec786b83..645ed4496 100644 --- a/core/src/plugins/inspectorPlugin/DataControl.ts +++ b/core/src/plugins/inspectorPlugin/DataControl.ts @@ -10,6 +10,7 @@ export abstract class DataControl { id: string | null = null dataKey: string name: string + defaultValue: unknown componentData: object componentKey: string options: object @@ -24,6 +25,7 @@ export abstract class DataControl { options = {}, write = true, icon = 'ankh', + defaultValue = null, }: { dataKey: string name: string @@ -32,6 +34,7 @@ export abstract class DataControl { options?: Record write?: boolean icon?: string + defaultValue?: unknown }) { if (!dataKey) throw new Error(`Data key is required`) if (!name) throw new Error(`Name is required`) @@ -44,6 +47,7 @@ export abstract class DataControl { this.options = options this.icon = icon this.write = write + this.defaultValue = defaultValue } //Serializer to easily extract the data controls information for publishing diff --git a/core/src/plugins/inspectorPlugin/Inspector.js b/core/src/plugins/inspectorPlugin/Inspector.js index 25b127890..1ff535d13 100644 --- a/core/src/plugins/inspectorPlugin/Inspector.js +++ b/core/src/plugins/inspectorPlugin/Inspector.js @@ -33,6 +33,9 @@ export class Inspector { control.component = this.component control.id = uuidv4() + if (control.defaultValue) + this.node.data[control.dataKey] = control.defaultValue + list.set(control.dataKey, control) } @@ -55,7 +58,7 @@ export class Inspector { existingSockets.push(out.key) }) - const ignored = control.data.ignored || [] + const ignored = (control && control?.data?.ignored) || [] // outputs that are on the node but not in the incoming sockets is removed existingSockets @@ -115,7 +118,8 @@ export class Inspector { const newSocket = new SocketConstructor( socket.socketKey || socket.name.toLowerCase(), socket.name, - socketMap[socket.socketType] + socketMap[socket.socketType], + socket.socketType === 'triggerSocket' ) if (isOutput) { @@ -216,12 +220,16 @@ export class Inspector { ) return { - name: this.node.name, + name: this.node.displayName || this.node.name, nodeId: this.node.id, dataControls, data: this.node.data, category: this.node.category, info: this.node.info, + deprecated: this.component.deprecated, + deprecationMessage: + this.component.deprecationMessage || + 'This component has been deprecated. Please use an alternative component, and remove any instances from your spells.', } } diff --git a/core/src/plugins/inspectorPlugin/index.js b/core/src/plugins/inspectorPlugin/index.js index 6fe28863a..03efd8b19 100644 --- a/core/src/plugins/inspectorPlugin/index.js +++ b/core/src/plugins/inspectorPlugin/index.js @@ -24,6 +24,11 @@ function install(editor) { // Adding category to node for display on node` node.category = component.category + // todo this should likely go somewhere better. Maybe a thoth plugin for all general thoth related things? + node.deprecated = component.deprecated + + node.displayName = component.displayName + node.info = component.info // here we attach the default info control to the component which will show up in the inspector diff --git a/core/src/plugins/modulePlugin/module-manager.ts b/core/src/plugins/modulePlugin/module-manager.ts index 01eded3b4..abec20d17 100644 --- a/core/src/plugins/modulePlugin/module-manager.ts +++ b/core/src/plugins/modulePlugin/module-manager.ts @@ -3,6 +3,7 @@ import { Socket as SocketType } from 'rete/types' import { ModuleType, + ModuleWorkerOutput, ThothNode, ThothWorkerInputs, ThothWorkerOutputs, @@ -175,7 +176,7 @@ export class ModuleManager { workerInputs( node: ThothNode, inputs: ThothWorkerInputs, - outputs: ThothWorkerOutputs, + outputs: ModuleWorkerOutput, { module }: { module: Module } ) { if (!module) return diff --git a/core/src/plugins/modulePlugin/utils.ts b/core/src/plugins/modulePlugin/utils.ts index 05360de30..909136e1a 100644 --- a/core/src/plugins/modulePlugin/utils.ts +++ b/core/src/plugins/modulePlugin/utils.ts @@ -139,7 +139,10 @@ const addSockets = ( socketType: socketNameMap[socket.name as SocketNameType], }) - node[addMethod](new Socket(socketKey, name, socket) as Input & Output) + node[addMethod]( + new Socket(socketKey, name, socket, taskType === 'option') as Input & + Output + ) if (connectionType === 'output') node.inspector.component.task.outputs[socketKey] = taskType }) diff --git a/core/src/plugins/socketGenerator/index.js b/core/src/plugins/socketGenerator/index.js index bee58b3dd..54e3b419c 100644 --- a/core/src/plugins/socketGenerator/index.js +++ b/core/src/plugins/socketGenerator/index.js @@ -50,7 +50,8 @@ function install(editor) { const input = new Rete.Input( socket.socketKey ? socket.socketKey : socket.name, socket.name, - sockets[socket.socketType] + sockets[socket.socketType], + socket.socketType === 'triggerSocket' ) node.addInput(input) }) diff --git a/core/src/plugins/taskPlugin/index.ts b/core/src/plugins/taskPlugin/index.ts index ea81e3a7c..f7f9483ad 100644 --- a/core/src/plugins/taskPlugin/index.ts +++ b/core/src/plugins/taskPlugin/index.ts @@ -6,7 +6,7 @@ import { ThothComponent } from '../../thoth-component' import { Task } from './task' function install(editor: NodeEditor) { - editor.on('componentregister', (component: ThothComponent) => { + editor.on('componentregister', (component: ThothComponent) => { if (!component.task) throw new Error('Task plugin requires a task property in component') if (component.task.outputs.constructor !== Object) diff --git a/core/src/plugins/taskPlugin/task.ts b/core/src/plugins/taskPlugin/task.ts index 6e2bfcd15..e31124154 100644 --- a/core/src/plugins/taskPlugin/task.ts +++ b/core/src/plugins/taskPlugin/task.ts @@ -30,7 +30,7 @@ export type TaskOutputTypes = 'option' | 'output' export class Task { node: NodeData inputs: ThothWorkerInputs - component: ThothComponent + component: ThothComponent worker: Function next: TaskRef[] outputData: Record | null @@ -38,7 +38,7 @@ export class Task { constructor( inputs: ThothWorkerInputs, - component: ThothComponent, + component: ThothComponent, node: NodeData, worker: Function ) { diff --git a/core/src/thoth-component.ts b/core/src/thoth-component.ts index c74f76641..442563815 100644 --- a/core/src/thoth-component.ts +++ b/core/src/thoth-component.ts @@ -1,4 +1,4 @@ -import { Node, NodeEditor } from 'rete' +import { Node, NodeEditor, Socket } from 'rete' import { PubSubBase, ThothNode } from '../types' import { EngineContext, ThothEngineComponent } from './engine' @@ -26,7 +26,14 @@ export interface ThothTask extends Task { onRun?: Function } -export abstract class ThothComponent extends ThothEngineComponent { +export interface ModuleOptions { + nodeType: 'input' | 'output' | 'triggerIn' | 'triggerOut' | 'module' + socket?: Socket +} + +export abstract class ThothComponent< + WorkerReturnType +> extends ThothEngineComponent { // Original interface for task and _task: IComponentWithTask from the Rete Task Plugin task: TaskOptions _task: ThothTask @@ -36,7 +43,12 @@ export abstract class ThothComponent extends ThothEngineComponent { category: string info: string display: boolean + deprecated: boolean = false + deprecationMessage: string | undefined + module: ModuleOptions + contextMenuName: string | undefined workspaceType: 'module' | 'spell' | null | undefined + displayName: string | undefined constructor(name: string) { super(name) diff --git a/core/types.ts b/core/types.ts index bd160f66d..b8e18a6cb 100644 --- a/core/types.ts +++ b/core/types.ts @@ -136,11 +136,22 @@ export type ThothReteInput = { task: ThothTask key: string } + +export type TaskOutput = { + type: TaskOutputTypes + task: ThothTask + key: string +} + +export type ModuleWorkerOutput = WorkerOutputs & { + [key: string]: string | string[] +} export type ThothWorkerInput = string | unknown | unknown[] export type ThothWorkerInputs = { [key: string]: ThothWorkerInput[] } export type ThothWorkerOutputs = WorkerOutputs & { - [key: string]: string[] | string + [key: string]: TaskOutput } + export type WorkerReturn = | Node | ThothWorkerOutputs diff --git a/netlify.toml b/netlify.toml index 5a8a51dae..13e64e2fb 100644 --- a/netlify.toml +++ b/netlify.toml @@ -7,9 +7,12 @@ # directory located at the absolute path "root/project/build-output" [[redirects]] -from = "./*" -to = "/index.html" -status = 200 + from = "./*" + to = "/index.html" + status = 200 [[context.deploy-preview.plugins]] -package = "/netlify/plugins/conditional-canary" \ No newline at end of file + package = "/netlify/plugins/conditional-canary" + +[context.deploy-preview.environment] + REACT_APP_API_URL="https://latitude-api-staging.herokuapp.com" \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index 812ccf245..68ff11207 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -21,14 +21,6 @@ "rootDir": ".", "noEmit": true }, - "references": [ - { - "path": "core" - }, - { - "path": "client" - } - ], "exclude": ["node_modules", "dist", "*.d.ts"], "typeRoots": ["@types/@thoth"] }