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 useProgramStore from "../../utils/programStore.ts";
|
||||
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';
|
||||
|
||||
// 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 getPhaseIds = useProgramStore((s) => s.getPhaseIds);
|
||||
const getNormsInPhase = useProgramStore((s) => s.getNormsInPhase);
|
||||
@@ -25,25 +52,27 @@ const MonitoringPage: React.FC = () => {
|
||||
//see if we reached end node
|
||||
const [isFinished, setIsFinished] = React.useState(false);
|
||||
|
||||
const handleStreamUpdate = React.useCallback((data: any) => {
|
||||
const handleStreamUpdate = React.useCallback((data: ExperimentStreamData) => {
|
||||
// Check for phase updates
|
||||
if (data.type === 'phase_update' && data.phase_id) {
|
||||
if (data.phase_id === "end") {
|
||||
if (data.type === 'phase_update' && data.id) {
|
||||
const payload = data as PhaseUpdate;
|
||||
if (payload.id === "end") {
|
||||
setIsFinished(true);
|
||||
} else {
|
||||
setIsFinished(false);
|
||||
|
||||
setIsFinished(false);
|
||||
|
||||
const allIds = getPhaseIds();
|
||||
const newIndex = allIds.indexOf(data.phase_id);
|
||||
const newIndex = allIds.indexOf(payload.id);
|
||||
if (newIndex !== -1) {
|
||||
setPhaseIndex(newIndex);
|
||||
setGoalIndex(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (data.type === 'goal_update') {
|
||||
const currentPhaseGoals = getGoalsInPhase(phaseIds[phaseIndex]) as any[];
|
||||
const gIndex = currentPhaseGoals.findIndex((g: any) => g.id === data.id);
|
||||
const payload = data as GoalUpdate;
|
||||
const currentPhaseGoals = getGoalsInPhase(phaseIds[phaseIndex]) as ReducedGoal[];
|
||||
const gIndex = currentPhaseGoals.findIndex((g: ReducedGoal) => g.id === payload.id);
|
||||
|
||||
if (gIndex !== -1) {
|
||||
//set current goal to the goal that is just started
|
||||
@@ -62,30 +91,32 @@ const MonitoringPage: React.FC = () => {
|
||||
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') {
|
||||
const payload = data as TriggerUpdate;
|
||||
setActiveIds((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') {
|
||||
const payload = data as CondNormsStateUpdate;
|
||||
setActiveIds((prev) => {
|
||||
const nextState = { ...prev };
|
||||
|
||||
data.norms.forEach((normUpdate: { id: string; active: boolean }) => {
|
||||
// payload.norms is typed on the union, so safe to use directly
|
||||
payload.norms.forEach((normUpdate) => {
|
||||
nextState[normUpdate.id] = normUpdate.active;
|
||||
console.log(`Conditional norm ${normUpdate.id} set to active: ${normUpdate.active}`);
|
||||
});
|
||||
|
||||
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);
|
||||
|
||||
@@ -95,35 +126,36 @@ const MonitoringPage: React.FC = () => {
|
||||
|
||||
const phaseId = phaseIds[phaseIndex];
|
||||
|
||||
const goals = (getGoalsInPhase(phaseId) as any[]).map(g => ({
|
||||
const goals = (getGoalsInPhase(phaseId) as ReducedGoal[]).map(g => ({
|
||||
...g,
|
||||
label: g.name,
|
||||
achieved: activeIds[g.id] ?? false
|
||||
label: g.name,
|
||||
achieved: activeIds[g.id] ?? false,
|
||||
}));
|
||||
|
||||
|
||||
const triggers = (getTriggersInPhase(phaseId) as any[]).map(t => ({
|
||||
const triggers = (getTriggersInPhase(phaseId) as ReducedTrigger[]).map(t => ({
|
||||
...t,
|
||||
label: (() => {
|
||||
|
||||
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}"`;
|
||||
} 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}`;
|
||||
} 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) => {
|
||||
if (step.text) {
|
||||
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 (step.gesture) {
|
||||
return `perform gesture: ${step.gesture.name || step.gesture.type}`;
|
||||
}
|
||||
if (step.goal) {
|
||||
}
|
||||
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
|
||||
@@ -144,15 +176,15 @@ const MonitoringPage: React.FC = () => {
|
||||
...n,
|
||||
label: n.norm,
|
||||
}));
|
||||
const conditionalNorms = (getNormsInPhase(phaseId) as any[])
|
||||
const conditionalNorms = (getNormsInPhase(phaseId) as ReducedNorm[])
|
||||
.filter(n => !!n.condition) // Only items with a condition
|
||||
.map(n => ({
|
||||
...n,
|
||||
label: (() => {
|
||||
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}"`;
|
||||
} 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}`;
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
* It does not render anything.
|
||||
*/
|
||||
export function useExperimentLogger(onUpdate?: (data: any) => void) {
|
||||
export function useExperimentLogger(onUpdate?: (data: ExperimentStreamData) => void) {
|
||||
useEffect(() => {
|
||||
const eventSource = new EventSource(`${API_BASE}/experiment_stream`);
|
||||
|
||||
eventSource.onmessage = (event) => {
|
||||
try {
|
||||
const parsedData = JSON.parse(event.data);
|
||||
const parsedData = JSON.parse(event.data) as ExperimentStreamData;
|
||||
if (onUpdate) {
|
||||
console.log(event.data);
|
||||
onUpdate(parsedData);
|
||||
|
||||
Reference in New Issue
Block a user