feat: automatic addition of goals to a current goal node, adding it to the plan, making sure the data stays correct. Also for the trigger nodes. :)
ref: N25B-434
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import type { Plan } from "./Plan";
|
||||
import type { Plan, PlanElement } from "./Plan";
|
||||
|
||||
export const defaultPlan: Plan = {
|
||||
name: "Default Plan",
|
||||
id: "-1",
|
||||
steps: [],
|
||||
steps: [] as PlanElement[],
|
||||
}
|
||||
@@ -1,3 +1,6 @@
|
||||
import { GoalReduce, type GoalNode } from "../nodes/GoalNode"
|
||||
import { type Node } from "@xyflow/react"
|
||||
|
||||
export type Plan = {
|
||||
name: string,
|
||||
id: string,
|
||||
@@ -5,13 +8,9 @@ export type Plan = {
|
||||
}
|
||||
|
||||
export type PlanElement = Goal | Action
|
||||
export type PlanElementTypes = ActionTypes | "goal"
|
||||
|
||||
export type Goal = {
|
||||
id: string,
|
||||
name: string,
|
||||
plan: Plan,
|
||||
can_fail: boolean,
|
||||
id: string // we let the reducer figure out the rest dynamically
|
||||
type: "goal"
|
||||
}
|
||||
|
||||
@@ -20,22 +19,21 @@ export type Action = SpeechAction | GestureAction | LLMAction
|
||||
export type SpeechAction = { id: string, text: string, type:"speech" }
|
||||
export type GestureAction = { id: string, gesture: string, isTag: boolean, type:"gesture" }
|
||||
export type LLMAction = { id: string, goal: string, type:"llm" }
|
||||
|
||||
export type ActionTypes = "speech" | "gesture" | "llm"
|
||||
export type ActionTypes = "speech" | "gesture" | "llm";
|
||||
|
||||
|
||||
// Extract the wanted information from a plan within the reducing of nodes
|
||||
export function PlanReduce(plan?: Plan) {
|
||||
export function PlanReduce(_nodes: Node[], plan?: Plan, ) {
|
||||
if (!plan) return ""
|
||||
return {
|
||||
id: plan.id,
|
||||
steps: plan.steps.map((x) => StepReduce(x))
|
||||
steps: plan.steps.map((x) => StepReduce(x, _nodes))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Extract the wanted information from a plan element.
|
||||
function StepReduce(planElement: PlanElement) {
|
||||
function StepReduce(planElement: PlanElement, _nodes: Node[]) : Record<string, unknown> {
|
||||
// We have different types of plan elements, requiring differnt types of output
|
||||
switch (planElement.type) {
|
||||
case ("speech"):
|
||||
@@ -57,12 +55,9 @@ function StepReduce(planElement: PlanElement) {
|
||||
goal: planElement.goal,
|
||||
}
|
||||
case ("goal"):
|
||||
return {
|
||||
id: planElement.id,
|
||||
plan: planElement.plan,
|
||||
can_fail: planElement.can_fail,
|
||||
};
|
||||
default:
|
||||
const nodes = _nodes
|
||||
const thisNode = _nodes.find((x) => x.id === planElement.id)
|
||||
return thisNode ? GoalReduce(thisNode, nodes) : {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,7 +70,7 @@ function StepReduce(planElement: PlanElement) {
|
||||
export function DoesPlanIterate(plan?: Plan) : boolean {
|
||||
// TODO: should recursively check plans that have goals (and thus more plans) in them.
|
||||
if (!plan) return false
|
||||
return plan.steps.filter((step) => step.type == "llm").length > 0 || (plan.steps.filter((x) => x.type == "goal").map((x) => DoesPlanIterate(x.plan))).includes(true);
|
||||
return plan.steps.filter((step) => step.type == "llm").length > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -99,4 +94,28 @@ export function GetActionValue(action: Action) {
|
||||
return returnAction.goal;
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a goal into a plan
|
||||
* @param plan: plan to insert goal into
|
||||
* @param goalNode: the goal node to insert into the plan.
|
||||
* @returns: a new plan with the goal inside.
|
||||
*/
|
||||
export function insertGoalInPlan(plan: Plan, goalNode: GoalNode): Plan {
|
||||
const planElement : Goal = {
|
||||
id: goalNode.id,
|
||||
type: "goal",
|
||||
}
|
||||
|
||||
return {
|
||||
...plan,
|
||||
steps: [...plan.steps, planElement],
|
||||
}
|
||||
}
|
||||
|
||||
export function deleteGoalInPlanByID(plan: Plan, goalID: string): Plan {
|
||||
return {...plan,
|
||||
steps: plan.steps.filter((x) => x.id !== goalID)
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import {useRef, useState} from "react";
|
||||
import useFlowStore from "../VisProgStores.tsx";
|
||||
import styles from './PlanEditor.module.css';
|
||||
import { GetActionValue, type Action, type ActionTypes, type Goal, type Plan, type PlanElement, type PlanElementTypes } from "../components/Plan";
|
||||
import { GetActionValue, type Action, type ActionTypes, type Plan } from "../components/Plan";
|
||||
import { defaultPlan } from "../components/Plan.default";
|
||||
import { TextField } from "../../../../components/TextField";
|
||||
import GestureValueEditor from "./GestureValueEditor";
|
||||
@@ -21,10 +21,10 @@ export default function PlanEditorDialog({
|
||||
const dialogRef = useRef<HTMLDialogElement | null>(null);
|
||||
const [draftPlan, setDraftPlan] = useState<Plan | null>(null);
|
||||
const [newActionType, setNewActionType] = useState<ActionTypes>("speech");
|
||||
const [newPlanElementType, setNewPlanElementType] = useState<PlanElementTypes>("speech")
|
||||
const [newActionGestureType, setNewActionGestureType] = useState<boolean>(true);
|
||||
const [newActionValue, setNewActionValue] = useState("");
|
||||
const { setScrollable } = useFlowStore();
|
||||
const nodes = useFlowStore().nodes;
|
||||
|
||||
//Button Actions
|
||||
const openCreate = () => {
|
||||
@@ -58,24 +58,14 @@ export default function PlanEditorDialog({
|
||||
const id = crypto.randomUUID();
|
||||
switch (newActionType) {
|
||||
case "speech":
|
||||
return { id, text: newPlanElementType, type: "speech" };
|
||||
return { id, text: newActionValue, type: "speech" };
|
||||
case "gesture":
|
||||
return { id, gesture: newPlanElementType, isTag: newActionGestureType, type: "gesture" };
|
||||
return { id, gesture: newActionValue, isTag: newActionGestureType, type: "gesture" };
|
||||
case "llm":
|
||||
return { id, goal: newPlanElementType, type: "llm" };
|
||||
return { id, goal: newActionValue, type: "llm" };
|
||||
}
|
||||
};
|
||||
|
||||
const buildGoal = (): Goal => {
|
||||
return {
|
||||
id: "",
|
||||
name: "",
|
||||
plan: defaultPlan,
|
||||
can_fail: false,
|
||||
type: "goal"
|
||||
}
|
||||
}
|
||||
|
||||
return (<>
|
||||
{/* Create and edit buttons */}
|
||||
{!plan && (
|
||||
@@ -121,21 +111,20 @@ export default function PlanEditorDialog({
|
||||
Action Type <wbr />
|
||||
{/* Type selection */}
|
||||
<select
|
||||
value={newPlanElementType}
|
||||
value={newActionType}
|
||||
onChange={(e) => {
|
||||
setNewPlanElementType(e.target.value as PlanElementTypes);
|
||||
setNewActionType(e.target.value as ActionTypes);
|
||||
// Reset value when action type changes
|
||||
setNewActionValue("");
|
||||
}}>
|
||||
<option value="speech">Speech Action</option>
|
||||
<option value="gesture">Gesture Action</option>
|
||||
<option value="llm">LLM Action</option>
|
||||
<option value="goal">Seperate Goal</option>
|
||||
</select>
|
||||
</label>
|
||||
|
||||
{/* Action value editor*/}
|
||||
{newPlanElementType === "gesture" ? (
|
||||
{newActionType === "gesture" ? (
|
||||
// Gesture get their own editor component
|
||||
<GestureValueEditor
|
||||
value={newActionValue}
|
||||
@@ -143,25 +132,16 @@ export default function PlanEditorDialog({
|
||||
setType={setNewActionGestureType}
|
||||
placeholder="Gesture name"
|
||||
/>
|
||||
)
|
||||
: (newPlanElementType === "goal" ? (
|
||||
<div>
|
||||
|
||||
|
||||
</div>
|
||||
)
|
||||
:
|
||||
// Only used for the Speech and LLM elements
|
||||
(
|
||||
) : (
|
||||
<TextField
|
||||
value={newActionValue}
|
||||
setValue={setNewActionValue}
|
||||
placeholder={
|
||||
newPlanElementType === "speech" ? "Speech text"
|
||||
newActionType === "speech" ? "Speech text"
|
||||
: "LLM goal"
|
||||
}
|
||||
/>
|
||||
))}
|
||||
)}
|
||||
|
||||
{/* Adding steps */}
|
||||
<button
|
||||
@@ -217,9 +197,13 @@ export default function PlanEditorDialog({
|
||||
|
||||
<span className={styles.stepIndex}>{index + 1}.</span>
|
||||
<span className={styles.stepType}>{step.type}:</span>
|
||||
<span className={styles.stepName}>{
|
||||
step.type == "goal" ? ""/* TODO: Add support for goals */
|
||||
: GetActionValue(step)}
|
||||
<span className={styles.stepName}>
|
||||
{
|
||||
// This just tries to find the goals name, i know it looks ugly:(
|
||||
step.type === "goal"
|
||||
? ((nodes.find(x => x.id === step.id)?.data.name as string) == "" ?
|
||||
"unnamed goal": (nodes.find(x => x.id === step.id)?.data.name as string))
|
||||
: (GetActionValue(step) ?? "")}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
|
||||
Reference in New Issue
Block a user