chore: gptd data typing and tests fixing
This commit is contained in:
@@ -2,10 +2,37 @@ import React from 'react';
|
|||||||
import styles from './MonitoringPage.module.css';
|
import styles from './MonitoringPage.module.css';
|
||||||
import useProgramStore from "../../utils/programStore.ts";
|
import useProgramStore from "../../utils/programStore.ts";
|
||||||
import { GestureControls, SpeechPresets, DirectSpeechInput, StatusList, RobotConnected } from './Components';
|
import { GestureControls, SpeechPresets, DirectSpeechInput, StatusList, RobotConnected } from './Components';
|
||||||
import { nextPhase, useExperimentLogger, pauseExperiment, playExperiment, resetExperiment, resetPhase } from ".//MonitoringPageAPI.ts"
|
import { nextPhase, useExperimentLogger, pauseExperiment, playExperiment, resetExperiment, resetPhase, type ExperimentStreamData, type GoalUpdate, type TriggerUpdate, type CondNormsStateUpdate, type PhaseUpdate } from ".//MonitoringPageAPI.ts"
|
||||||
|
|
||||||
import type { NormNodeData } from '../VisProgPage/visualProgrammingUI/nodes/NormNode.tsx';
|
import type { NormNodeData } from '../VisProgPage/visualProgrammingUI/nodes/NormNode.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 = () => {
|
const MonitoringPage: React.FC = () => {
|
||||||
const getPhaseIds = useProgramStore((s) => s.getPhaseIds);
|
const getPhaseIds = useProgramStore((s) => s.getPhaseIds);
|
||||||
const getNormsInPhase = useProgramStore((s) => s.getNormsInPhase);
|
const getNormsInPhase = useProgramStore((s) => s.getNormsInPhase);
|
||||||
@@ -25,25 +52,27 @@ const MonitoringPage: React.FC = () => {
|
|||||||
//see if we reached end node
|
//see if we reached end node
|
||||||
const [isFinished, setIsFinished] = React.useState(false);
|
const [isFinished, setIsFinished] = React.useState(false);
|
||||||
|
|
||||||
const handleStreamUpdate = React.useCallback((data: any) => {
|
const handleStreamUpdate = React.useCallback((data: ExperimentStreamData) => {
|
||||||
// Check for phase updates
|
// Check for phase updates
|
||||||
if (data.type === 'phase_update' && data.phase_id) {
|
if (data.type === 'phase_update' && data.id) {
|
||||||
if (data.phase_id === "end") {
|
const payload = data as PhaseUpdate;
|
||||||
|
if (payload.id === "end") {
|
||||||
setIsFinished(true);
|
setIsFinished(true);
|
||||||
} else {
|
} else {
|
||||||
setIsFinished(false);
|
setIsFinished(false);
|
||||||
|
|
||||||
const allIds = getPhaseIds();
|
const allIds = getPhaseIds();
|
||||||
const newIndex = allIds.indexOf(data.phase_id);
|
const newIndex = allIds.indexOf(payload.id);
|
||||||
if (newIndex !== -1) {
|
if (newIndex !== -1) {
|
||||||
setPhaseIndex(newIndex);
|
setPhaseIndex(newIndex);
|
||||||
setGoalIndex(0);
|
setGoalIndex(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (data.type === 'goal_update') {
|
else if (data.type === 'goal_update') {
|
||||||
const currentPhaseGoals = getGoalsInPhase(phaseIds[phaseIndex]) as any[];
|
const payload = data as GoalUpdate;
|
||||||
const gIndex = currentPhaseGoals.findIndex((g: any) => g.id === data.id);
|
const currentPhaseGoals = getGoalsInPhase(phaseIds[phaseIndex]) as ReducedGoal[];
|
||||||
|
const gIndex = currentPhaseGoals.findIndex((g: ReducedGoal) => g.id === payload.id);
|
||||||
|
|
||||||
if (gIndex !== -1) {
|
if (gIndex !== -1) {
|
||||||
//set current goal to the goal that is just started
|
//set current goal to the goal that is just started
|
||||||
@@ -62,30 +91,32 @@ const MonitoringPage: React.FC = () => {
|
|||||||
return nextState;
|
return nextState;
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(`Now pursuing goal: ${data.id}. Previous goals marked achieved.`);
|
console.log(`Now pursuing goal: ${payload.id}. Previous goals marked achieved.`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (data.type === 'trigger_update') {
|
else if (data.type === 'trigger_update') {
|
||||||
|
const payload = data as TriggerUpdate;
|
||||||
setActiveIds((prev) => ({
|
setActiveIds((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
[data.id]: data.achieved // data.id is de key, achieved is true/false
|
[payload.id]: payload.achieved
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
else if (data.type === 'cond_norms_state_update') {
|
else if (data.type === 'cond_norms_state_update') {
|
||||||
|
const payload = data as CondNormsStateUpdate;
|
||||||
setActiveIds((prev) => {
|
setActiveIds((prev) => {
|
||||||
const nextState = { ...prev };
|
const nextState = { ...prev };
|
||||||
|
// payload.norms is typed on the union, so safe to use directly
|
||||||
data.norms.forEach((normUpdate: { id: string; active: boolean }) => {
|
payload.norms.forEach((normUpdate) => {
|
||||||
nextState[normUpdate.id] = normUpdate.active;
|
nextState[normUpdate.id] = normUpdate.active;
|
||||||
console.log(`Conditional norm ${normUpdate.id} set to active: ${normUpdate.active}`);
|
console.log(`Conditional norm ${normUpdate.id} set to active: ${normUpdate.active}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
return nextState;
|
return nextState;
|
||||||
});
|
});
|
||||||
console.log("Updated conditional norms state:", data.norms);
|
console.log("Updated conditional norms state:", payload.norms);
|
||||||
}
|
}
|
||||||
}, [getPhaseIds]);
|
}, [getPhaseIds, getGoalsInPhase, phaseIds, phaseIndex]);
|
||||||
|
|
||||||
useExperimentLogger(handleStreamUpdate);
|
useExperimentLogger(handleStreamUpdate);
|
||||||
|
|
||||||
@@ -95,35 +126,36 @@ const MonitoringPage: React.FC = () => {
|
|||||||
|
|
||||||
const phaseId = phaseIds[phaseIndex];
|
const phaseId = phaseIds[phaseIndex];
|
||||||
|
|
||||||
const goals = (getGoalsInPhase(phaseId) as any[]).map(g => ({
|
const goals = (getGoalsInPhase(phaseId) as ReducedGoal[]).map(g => ({
|
||||||
...g,
|
...g,
|
||||||
label: g.name,
|
label: g.name,
|
||||||
achieved: activeIds[g.id] ?? false
|
achieved: activeIds[g.id] ?? false,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
||||||
const triggers = (getTriggersInPhase(phaseId) as any[]).map(t => ({
|
const triggers = (getTriggersInPhase(phaseId) as ReducedTrigger[]).map(t => ({
|
||||||
...t,
|
...t,
|
||||||
label: (() => {
|
label: (() => {
|
||||||
|
|
||||||
let prefix = "";
|
let prefix = "";
|
||||||
if (t.condition?.keyword) {
|
if (t.condition && typeof t.condition !== "string" && "keyword" in t.condition && typeof t.condition.keyword === "string") {
|
||||||
prefix = `if keywords said: "${t.condition.keyword}"`;
|
prefix = `if keywords said: "${t.condition.keyword}"`;
|
||||||
} else if (t.condition?.name) {
|
} else if (t.condition && typeof t.condition !== "string" && "name" in t.condition && typeof t.condition.name === "string") {
|
||||||
prefix = `if LLM belief: ${t.condition.name}`;
|
prefix = `if LLM belief: ${t.condition.name}`;
|
||||||
} else { //fallback
|
} else { //fallback
|
||||||
prefix = t.label || "Trigger";
|
prefix = t.name || "Trigger"; // use typed `name` as a reliable fallback
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const stepLabels = t.plan?.steps?.map((step: any) => {
|
const stepLabels = (t.plan && typeof t.plan !== "string" ? t.plan.steps : []).map((step: ReducedPlanStep) => {
|
||||||
if (step.text) {
|
if ("text" in step && typeof step.text === "string") {
|
||||||
return `say: "${step.text}"`;
|
return `say: "${step.text}"`;
|
||||||
}
|
}
|
||||||
if (step.gesture) {
|
if ("gesture" in step && step.gesture) {
|
||||||
return `perform gesture: ${step.gesture.name || step.gesture.type}`;
|
const g = step.gesture;
|
||||||
}
|
return `perform gesture: ${g.name || g.type}`;
|
||||||
if (step.goal) {
|
}
|
||||||
|
if ("goal" in step && typeof step.goal === "string") {
|
||||||
return `perform LLM: ${step.goal}`;
|
return `perform LLM: ${step.goal}`;
|
||||||
}
|
}
|
||||||
return "Action"; // Fallback
|
return "Action"; // Fallback
|
||||||
@@ -144,15 +176,15 @@ const MonitoringPage: React.FC = () => {
|
|||||||
...n,
|
...n,
|
||||||
label: n.norm,
|
label: n.norm,
|
||||||
}));
|
}));
|
||||||
const conditionalNorms = (getNormsInPhase(phaseId) as any[])
|
const conditionalNorms = (getNormsInPhase(phaseId) as ReducedNorm[])
|
||||||
.filter(n => !!n.condition) // Only items with a condition
|
.filter(n => !!n.condition) // Only items with a condition
|
||||||
.map(n => ({
|
.map(n => ({
|
||||||
...n,
|
...n,
|
||||||
label: (() => {
|
label: (() => {
|
||||||
let prefix = "";
|
let prefix = "";
|
||||||
if (n.condition?.keyword) {
|
if (n.condition && typeof n.condition !== "string" && "keyword" in n.condition && typeof n.condition.keyword === "string") {
|
||||||
prefix = `if keywords said: "${n.condition.keyword}"`;
|
prefix = `if keywords said: "${n.condition.keyword}"`;
|
||||||
} else if (n.condition?.name) {
|
} else if (n.condition && typeof n.condition !== "string" && "name" in n.condition && typeof n.condition.name === "string") {
|
||||||
prefix = `if LLM belief: ${n.condition.name}`;
|
prefix = `if LLM belief: ${n.condition.name}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -66,17 +66,26 @@ export async function playExperiment(): Promise<void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Types for the experiment stream messages
|
||||||
|
*/
|
||||||
|
export type PhaseUpdate = { type: 'phase_update'; id: string };
|
||||||
|
export type GoalUpdate = { type: 'goal_update'; id: string };
|
||||||
|
export type TriggerUpdate = { type: 'trigger_update'; id: string; achieved: boolean };
|
||||||
|
export type CondNormsStateUpdate = { type: 'cond_norms_state_update'; norms: { id: string; active: boolean }[] };
|
||||||
|
export type ExperimentStreamData = PhaseUpdate | GoalUpdate | TriggerUpdate | CondNormsStateUpdate | Record<string, unknown>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A hook that listens to the experiment stream and logs data to the console.
|
* A hook that listens to the experiment stream and logs data to the console.
|
||||||
* It does not render anything.
|
* It does not render anything.
|
||||||
*/
|
*/
|
||||||
export function useExperimentLogger(onUpdate?: (data: any) => void) {
|
export function useExperimentLogger(onUpdate?: (data: ExperimentStreamData) => void) {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const eventSource = new EventSource(`${API_BASE}/experiment_stream`);
|
const eventSource = new EventSource(`${API_BASE}/experiment_stream`);
|
||||||
|
|
||||||
eventSource.onmessage = (event) => {
|
eventSource.onmessage = (event) => {
|
||||||
try {
|
try {
|
||||||
const parsedData = JSON.parse(event.data);
|
const parsedData = JSON.parse(event.data) as ExperimentStreamData;
|
||||||
if (onUpdate) {
|
if (onUpdate) {
|
||||||
console.log(event.data);
|
console.log(event.data);
|
||||||
onUpdate(parsedData);
|
onUpdate(parsedData);
|
||||||
|
|||||||
Reference in New Issue
Block a user