All files / src/pages/VisProgPage/visualProgrammingUI/components Plan.tsx

0% Statements 0/37
0% Branches 0/27
0% Functions 0/14
0% Lines 0/29

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124                                                                                                                                                                                                                                                       
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:
    }
}