Merge remote-tracking branch 'origin/feat/add-inferred-belief-node' into feat/monitoringpage

This commit is contained in:
Pim Hutting
2026-01-16 15:39:20 +01:00
17 changed files with 805 additions and 102 deletions

View File

@@ -2,36 +2,13 @@ import React from 'react';
import styles from './MonitoringPage.module.css';
import useProgramStore from "../../utils/programStore.ts";
import { GestureControls, SpeechPresets, DirectSpeechInput, StatusList, RobotConnected } from './MonitoringPageComponents.tsx';
import { nextPhase, useExperimentLogger, useStatusLogger, pauseExperiment, playExperiment, resetPhase, type ExperimentStreamData, type GoalUpdate, type TriggerUpdate, type CondNormsStateUpdate, type PhaseUpdate } from ".//MonitoringPageAPI.ts"
import { nextPhase, useExperimentLogger, useStatusLogger, pauseExperiment, playExperiment, type ExperimentStreamData, type GoalUpdate, type TriggerUpdate, type CondNormsStateUpdate, type PhaseUpdate } from ".//MonitoringPageAPI.ts"
import { graphReducer, runProgramm } from '../VisProgPage/VisProg.tsx';
import type { NormNodeData } from '../VisProgPage/visualProgrammingUI/nodes/NormNode.tsx';
import type { NormNodeData, NormNode } from '../VisProgPage/visualProgrammingUI/nodes/NormNode.tsx';
import type { GoalNode } from '../VisProgPage/visualProgrammingUI/nodes/GoalNode.tsx';
import type { TriggerNode } from '../VisProgPage/visualProgrammingUI/nodes/TriggerNode.tsx';
// Stream message types are defined in MonitoringPageAPI as `ExperimentStreamData`.
// Types for reduced program items (output from node reducers):
export type ReducedPlanStep = {
id: string;
text?: string;
gesture?: { type: string; name?: string };
goal?: string;
} & Record<string, unknown>;
export type ReducedPlan = { id: string; steps: ReducedPlanStep[] } | "";
export type ReducedGoal = { id: string; name: string; description?: string; can_fail?: boolean; plan?: ReducedPlan };
export type ReducedCondition = {
id: string;
keyword?: string;
emotion?: string;
object?: string;
name?: string;
description?: string;
} & Record<string, unknown>;
export type ReducedTrigger = { id: string; name: string; condition?: ReducedCondition | ""; plan?: ReducedPlan };
export type ReducedNorm = { id: string; label?: string; norm?: string; condition?: ReducedCondition | "" };
const MonitoringPage: React.FC = () => {
@@ -77,8 +54,8 @@ const MonitoringPage: React.FC = () => {
}
else if (data.type === 'goal_update') {
const payload = data as GoalUpdate;
const currentPhaseGoals = getGoalsInPhase(phaseIds[phaseIndex]) as ReducedGoal[];
const gIndex = currentPhaseGoals.findIndex((g: ReducedGoal) => g.id === payload.id);
const currentPhaseGoals = getGoalsInPhase(phaseIds[phaseIndex]) as GoalNode[];
const gIndex = currentPhaseGoals.findIndex((g: GoalNode) => g.id === payload.id);
console.log(`${data.type} received, id : ${data.id}`)
if (gIndex == -1)
{console.log(`goal to update with id ${payload.id} not found in current phase ${phaseNames[phaseIndex]}`)}
@@ -168,48 +145,16 @@ const resetExperiment = React.useCallback(async () => {
const phaseId = phaseIds[phaseIndex];
const goals = (getGoalsInPhase(phaseId) as ReducedGoal[]).map(g => ({
const goals = (getGoalsInPhase(phaseId) as GoalNode[]).map(g => ({
...g,
label: g.name,
achieved: activeIds[g.id] ?? false,
}));
const triggers = (getTriggersInPhase(phaseId) as ReducedTrigger[]).map(t => ({
const triggers = (getTriggersInPhase(phaseId) as TriggerNode[]).map(t => ({
...t,
label: (() => {
let prefix = "";
if (t.condition && typeof t.condition !== "string" && "keyword" in t.condition && typeof t.condition.keyword === "string") {
prefix = `if keyword said: "${t.condition.keyword}"`;
} else if (t.condition && typeof t.condition !== "string" && "name" in t.condition && typeof t.condition.name === "string") {
prefix = `if LLM belief: ${t.condition.name}`;
} else { //fallback
prefix = t.name || "Trigger"; // use typed `name` as a reliable fallback
}
const stepLabels = (t.plan && typeof t.plan !== "string" ? t.plan.steps : []).map((step: ReducedPlanStep) => {
if ("text" in step && typeof step.text === "string") {
return `say: "${step.text}"`;
}
if ("gesture" in step && step.gesture) {
const g = step.gesture;
return `perform gesture: ${g.name || g.type}`;
}
if ("goal" in step && typeof step.goal === "string") {
return `perform LLM: ${step.goal}`;
}
return "Action"; // Fallback
}) || [];
const planText = stepLabels.length > 0
? `➔ Do: ${stepLabels.join(", ")}`
: "➔ (No actions set)";
return `${prefix} ${planText}`;
})(),
isActive: activeIds[t.id] ?? false
achieved: activeIds[t.id] ?? false,
}));
const norms = (getNormsInPhase(phaseId) as NormNodeData[])
@@ -218,21 +163,10 @@ const resetExperiment = React.useCallback(async () => {
...n,
label: n.norm,
}));
const conditionalNorms = (getNormsInPhase(phaseId) as ReducedNorm[])
const conditionalNorms = (getNormsInPhase(phaseId) as (NormNodeData &{id: string})[])
.filter(n => !!n.condition) // Only items with a condition
.map(n => ({
...n,
label: (() => {
let prefix = "";
if (n.condition && typeof n.condition !== "string" && "keyword" in n.condition && typeof n.condition.keyword === "string") {
prefix = `if keyword said: "${n.condition.keyword}"`;
} else if (n.condition && typeof n.condition !== "string" && "name" in n.condition && typeof n.condition.name === "string") {
prefix = `if LLM belief: ${n.condition.name}`;
}
return `${prefix} ➔ Norm: ${n.norm}`;
})(),
achieved: activeIds[n.id] ?? false
}));