chore: replace plan editor functionality into own folder with own files.
This commit is contained in:
@@ -0,0 +1,129 @@
|
||||
// This program has been developed by students from the bachelor Computer Science at Utrecht
|
||||
// University within the Software Project course.
|
||||
// © Copyright Utrecht University (Department of Information and Computing Sciences)
|
||||
import { type Node } from "@xyflow/react"
|
||||
import { GoalReduce } from "../../nodes/GoalNode"
|
||||
|
||||
|
||||
export type Plan = {
|
||||
name: string,
|
||||
id: string,
|
||||
steps: PlanElement[],
|
||||
}
|
||||
|
||||
export type PlanElement = Goal | Action
|
||||
|
||||
export type Goal = {
|
||||
id: string // we let the reducer figure out the rest dynamically
|
||||
type: "goal"
|
||||
}
|
||||
|
||||
// Actions
|
||||
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";
|
||||
|
||||
|
||||
// Extract the wanted information from a plan within the reducing of nodes
|
||||
export function PlanReduce(_nodes: Node[], plan?: Plan, ) {
|
||||
if (!plan) return ""
|
||||
return {
|
||||
id: plan.id,
|
||||
steps: plan.steps.map((x) => StepReduce(x, _nodes))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Extract the wanted information from a plan element.
|
||||
function StepReduce(planElement: PlanElement, _nodes: Node[]) : Record<string, unknown> {
|
||||
// We have different types of plan elements, requiring differnt types of output
|
||||
const nodes = _nodes
|
||||
const thisNode = _nodes.find((x) => x.id === planElement.id)
|
||||
switch (planElement.type) {
|
||||
case ("speech"):
|
||||
return {
|
||||
id: planElement.id,
|
||||
text: planElement.text,
|
||||
}
|
||||
case ("gesture"):
|
||||
return {
|
||||
id: planElement.id,
|
||||
gesture: {
|
||||
type: planElement.isTag ? "tag" : "single",
|
||||
name: planElement.gesture
|
||||
},
|
||||
}
|
||||
case ("llm"):
|
||||
return {
|
||||
id: planElement.id,
|
||||
goal: planElement.goal,
|
||||
}
|
||||
case ("goal"):
|
||||
return thisNode ? GoalReduce(thisNode, nodes) : {}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds out whether the plan can iterate multiple times, or always stops after one action.
|
||||
* This comes down to checking if the plan only has speech/ gesture actions, or others as well.
|
||||
* @param plan: the plan to check
|
||||
* @returns: a boolean
|
||||
*/
|
||||
export function DoesPlanIterate( _nodes: Node[], 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 ||
|
||||
(
|
||||
// Find the goal node of this step
|
||||
plan.steps.filter((step) => step.type == "goal").map((goalStep) => {
|
||||
const goalId = goalStep.id;
|
||||
const goalNode = _nodes.find((x) => x.id === goalId);
|
||||
// In case we don't find any valid plan, this node doesn't iterate
|
||||
if (!goalNode || !goalNode.data.plan) return false;
|
||||
// Otherwise, check if this node can fail - if so, we should have the option to iterate
|
||||
return (goalNode && goalNode.data.plan && goalNode.data.can_fail)
|
||||
})
|
||||
).includes(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if any of the plan's goal steps has its can_fail value set to true.
|
||||
* @param plan: plan to check
|
||||
* @param _nodes: nodes in flow store.
|
||||
*/
|
||||
export function HasCheckingSubGoal(plan: Plan, _nodes: Node[]) {
|
||||
const goalSteps = plan.steps.filter((x) => x.type == "goal");
|
||||
return goalSteps.map((goalStep) => {
|
||||
// Find the goal node and check its can_fail data boolean.
|
||||
const goalId = goalStep.id;
|
||||
const goalNode = _nodes.find((x) => x.id === goalId);
|
||||
return (goalNode && goalNode.data.can_fail)
|
||||
}).includes(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of the action.
|
||||
* Since typescript can't polymorphicly access the value field,
|
||||
* we need to switch over the types and return the correct field.
|
||||
* @param action: action to retrieve the value from
|
||||
* @returns string | undefined
|
||||
*/
|
||||
export function GetActionValue(action: Action) {
|
||||
let returnAction;
|
||||
switch (action.type) {
|
||||
case "gesture":
|
||||
returnAction = action as GestureAction
|
||||
return returnAction.gesture;
|
||||
case "speech":
|
||||
returnAction = action as SpeechAction
|
||||
return returnAction.text;
|
||||
case "llm":
|
||||
returnAction = action as LLMAction
|
||||
return returnAction.goal;
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user