mirror of
https://github.com/soconnor0919/hristudio.git
synced 2026-03-23 19:27:51 -04:00
feat: introduce conditional steps and branching logic to the experiment wizard and designer, along with new core and WoZ plugins.
This commit is contained in:
58
scripts/debug-experiment-structure.ts
Normal file
58
scripts/debug-experiment-structure.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
|
||||
import { db } from "~/server/db";
|
||||
import { steps, experiments, actions } from "~/server/db/schema";
|
||||
import { eq, asc } from "drizzle-orm";
|
||||
|
||||
async function debugExperimentStructure() {
|
||||
console.log("Debugging Experiment Structure for Interactive Storyteller...");
|
||||
|
||||
// Find the experiment
|
||||
const experiment = await db.query.experiments.findFirst({
|
||||
where: eq(experiments.name, "The Interactive Storyteller"),
|
||||
with: {
|
||||
steps: {
|
||||
orderBy: [asc(steps.orderIndex)],
|
||||
with: {
|
||||
actions: {
|
||||
orderBy: [asc(actions.orderIndex)],
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (!experiment) {
|
||||
console.error("Experiment not found!");
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`Experiment: ${experiment.name} (${experiment.id})`);
|
||||
console.log(`Plugin Dependencies:`, experiment.pluginDependencies);
|
||||
console.log("---------------------------------------------------");
|
||||
|
||||
experiment.steps.forEach((step, index) => {
|
||||
console.log(`Step ${index + 1}: ${step.name}`);
|
||||
console.log(` ID: ${step.id}`);
|
||||
console.log(` Type: ${step.type}`);
|
||||
console.log(` Order: ${step.orderIndex}`);
|
||||
console.log(` Conditions:`, JSON.stringify(step.conditions, null, 2));
|
||||
|
||||
if (step.actions && step.actions.length > 0) {
|
||||
console.log(` Actions (${step.actions.length}):`);
|
||||
step.actions.forEach((action, actionIndex) => {
|
||||
console.log(` ${actionIndex + 1}. [${action.type}] ${action.name}`);
|
||||
if (action.type === 'wizard_wait_for_response') {
|
||||
console.log(` Options:`, JSON.stringify((action.parameters as any)?.options, null, 2));
|
||||
}
|
||||
});
|
||||
}
|
||||
console.log("---------------------------------------------------");
|
||||
});
|
||||
}
|
||||
|
||||
debugExperimentStructure()
|
||||
.then(() => process.exit(0))
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
42
scripts/inspect-all-steps.ts
Normal file
42
scripts/inspect-all-steps.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
|
||||
import { db } from "../src/server/db";
|
||||
import { experiments, steps } from "../src/server/db/schema";
|
||||
import { eq } from "drizzle-orm";
|
||||
|
||||
async function inspectAllSteps() {
|
||||
const result = await db.query.experiments.findMany({
|
||||
with: {
|
||||
steps: {
|
||||
orderBy: (steps, { asc }) => [asc(steps.orderIndex)],
|
||||
columns: {
|
||||
id: true,
|
||||
name: true,
|
||||
type: true,
|
||||
orderIndex: true,
|
||||
conditions: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
console.log(`Found ${result.length} experiments.`);
|
||||
|
||||
for (const exp of result) {
|
||||
console.log(`Experiment: ${exp.name} (${exp.id})`);
|
||||
for (const step of exp.steps) {
|
||||
// Only print conditional steps or the first step
|
||||
if (step.type === 'conditional' || step.orderIndex === 0) {
|
||||
console.log(` [${step.orderIndex}] ${step.name} (${step.type})`);
|
||||
console.log(` Conditions: ${JSON.stringify(step.conditions)}`);
|
||||
}
|
||||
}
|
||||
console.log('---');
|
||||
}
|
||||
}
|
||||
|
||||
inspectAllSteps()
|
||||
.then(() => process.exit(0))
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
47
scripts/inspect-branch-action.ts
Normal file
47
scripts/inspect-branch-action.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
|
||||
import { db } from "~/server/db";
|
||||
import { actions, steps } from "~/server/db/schema";
|
||||
import { eq } from "drizzle-orm";
|
||||
|
||||
async function inspectAction() {
|
||||
console.log("Inspecting Action 10851aef-e720-45fc-ba5e-05e1e3425dab...");
|
||||
|
||||
const actionId = "10851aef-e720-45fc-ba5e-05e1e3425dab";
|
||||
|
||||
const action = await db.query.actions.findFirst({
|
||||
where: eq(actions.id, actionId),
|
||||
with: {
|
||||
step: {
|
||||
columns: {
|
||||
id: true,
|
||||
name: true,
|
||||
type: true,
|
||||
conditions: true
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (!action) {
|
||||
console.error("Action not found!");
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("Action Found:");
|
||||
console.log(" Name:", action.name);
|
||||
console.log(" Type:", action.type);
|
||||
console.log(" Parameters:", JSON.stringify(action.parameters, null, 2));
|
||||
|
||||
console.log("Parent Step:");
|
||||
console.log(" ID:", action.step.id);
|
||||
console.log(" Name:", action.step.name);
|
||||
console.log(" Type:", action.step.type);
|
||||
console.log(" Conditions:", JSON.stringify(action.step.conditions, null, 2));
|
||||
}
|
||||
|
||||
inspectAction()
|
||||
.then(() => process.exit(0))
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
30
scripts/inspect-branch-steps.ts
Normal file
30
scripts/inspect-branch-steps.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
|
||||
import { db } from "~/server/db";
|
||||
import { steps } from "~/server/db/schema";
|
||||
import { eq, inArray } from "drizzle-orm";
|
||||
|
||||
async function inspectBranchSteps() {
|
||||
console.log("Inspecting Steps 4 (Branch A) and 5 (Branch B)...");
|
||||
|
||||
const step4Id = "3a2dc0b7-a43e-4236-9b9e-f957abafc1e5";
|
||||
const step5Id = "3ae2fe8a-fc5d-4a04-baa5-699a21f19e30";
|
||||
|
||||
const branchSteps = await db.query.steps.findMany({
|
||||
where: inArray(steps.id, [step4Id, step5Id])
|
||||
});
|
||||
|
||||
branchSteps.forEach(step => {
|
||||
console.log(`Step: ${step.name} (${step.id})`);
|
||||
console.log(` Type: ${step.type}`);
|
||||
console.log(` Order: ${step.orderIndex}`);
|
||||
console.log(` Conditions:`, JSON.stringify(step.conditions, null, 2));
|
||||
console.log("---------------------------------------------------");
|
||||
});
|
||||
}
|
||||
|
||||
inspectBranchSteps()
|
||||
.then(() => process.exit(0))
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
24
scripts/inspect-db.ts
Normal file
24
scripts/inspect-db.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
|
||||
import { db } from "../src/server/db";
|
||||
import { steps } from "../src/server/db/schema";
|
||||
import { eq, like } from "drizzle-orm";
|
||||
|
||||
async function checkSteps() {
|
||||
const allSteps = await db.select().from(steps).where(like(steps.name, "%Comprehension Check%"));
|
||||
|
||||
console.log("Found steps:", allSteps.length);
|
||||
|
||||
for (const step of allSteps) {
|
||||
console.log("Step Name:", step.name);
|
||||
console.log("Type:", step.type);
|
||||
console.log("Conditions (typeof):", typeof step.conditions);
|
||||
console.log("Conditions (value):", JSON.stringify(step.conditions, null, 2));
|
||||
}
|
||||
}
|
||||
|
||||
checkSteps()
|
||||
.then(() => process.exit(0))
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
61
scripts/inspect-step.ts
Normal file
61
scripts/inspect-step.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
|
||||
|
||||
import { db } from "~/server/db";
|
||||
import { steps, experiments } from "~/server/db/schema";
|
||||
import { eq, asc } from "drizzle-orm";
|
||||
|
||||
async function inspectExperimentSteps() {
|
||||
// Find experiment by ID
|
||||
const experiment = await db.query.experiments.findFirst({
|
||||
where: eq(experiments.id, "961d0cb1-256d-4951-8387-6d855a0ae603")
|
||||
});
|
||||
|
||||
if (!experiment) {
|
||||
console.log("Experiment not found!");
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`Inspecting Experiment: ${experiment.name} (${experiment.id})`);
|
||||
|
||||
const experimentSteps = await db.query.steps.findMany({
|
||||
where: eq(steps.experimentId, experiment.id),
|
||||
orderBy: [asc(steps.orderIndex)],
|
||||
with: {
|
||||
actions: {
|
||||
orderBy: (actions, { asc }) => [asc(actions.orderIndex)]
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
console.log(`Found ${experimentSteps.length} steps.`);
|
||||
|
||||
for (const step of experimentSteps) {
|
||||
console.log("--------------------------------------------------");
|
||||
console.log(`Step [${step.orderIndex}] ID: ${step.id}`);
|
||||
console.log(`Name: ${step.name}`);
|
||||
console.log(`Type: ${step.type}`);
|
||||
console.log(`NextStepId: ${step.nextStepId}`);
|
||||
|
||||
if (step.type === 'conditional') {
|
||||
console.log("Conditions:", JSON.stringify(step.conditions, null, 2));
|
||||
}
|
||||
|
||||
if (step.actions.length > 0) {
|
||||
console.log("Actions:");
|
||||
for (const action of step.actions) {
|
||||
console.log(` - [${action.orderIndex}] ${action.name} (${action.type})`);
|
||||
if (action.type === 'wizard_wait_for_response') {
|
||||
console.log(" Parameters:", JSON.stringify(action.parameters, null, 2));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inspectExperimentSteps()
|
||||
.then(() => process.exit(0))
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
33
scripts/inspect-visual-design.ts
Normal file
33
scripts/inspect-visual-design.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
|
||||
import { db } from "../src/server/db";
|
||||
import { experiments } from "../src/server/db/schema";
|
||||
import { eq } from "drizzle-orm";
|
||||
|
||||
async function inspectVisualDesign() {
|
||||
const exps = await db.select().from(experiments);
|
||||
|
||||
for (const exp of exps) {
|
||||
console.log(`Experiment: ${exp.name}`);
|
||||
if (exp.visualDesign) {
|
||||
const vd = exp.visualDesign as any;
|
||||
console.log("Visual Design Steps:");
|
||||
if (vd.steps && Array.isArray(vd.steps)) {
|
||||
vd.steps.forEach((s: any, i: number) => {
|
||||
console.log(` [${i}] ${s.name} (${s.type})`);
|
||||
console.log(` Trigger: ${JSON.stringify(s.trigger)}`);
|
||||
});
|
||||
} else {
|
||||
console.log(" No steps in visualDesign or invalid format.");
|
||||
}
|
||||
} else {
|
||||
console.log(" No visualDesign blob.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inspectVisualDesign()
|
||||
.then(() => process.exit(0))
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
69
scripts/patch-branch-action-params.ts
Normal file
69
scripts/patch-branch-action-params.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
|
||||
import { db } from "~/server/db";
|
||||
import { actions, steps } from "~/server/db/schema";
|
||||
import { eq, sql } from "drizzle-orm";
|
||||
|
||||
async function patchActionParams() {
|
||||
console.log("Patching Action Parameters for Interactive Storyteller...");
|
||||
|
||||
// Target Step IDs
|
||||
const step3CondId = "b9d43f8c-c40c-4f1c-9fdc-9076338d3c85"; // Step 3: Comprehension Check
|
||||
const actionId = "10851aef-e720-45fc-ba5e-05e1e3425dab"; // Action: Wait for Choice
|
||||
|
||||
// 1. Get the authoritative conditions from the Step
|
||||
const step = await db.query.steps.findFirst({
|
||||
where: eq(steps.id, step3CondId)
|
||||
});
|
||||
|
||||
if (!step) {
|
||||
console.error("Step 3 not found!");
|
||||
return;
|
||||
}
|
||||
|
||||
const conditions = step.conditions as any;
|
||||
const richOptions = conditions?.options;
|
||||
|
||||
if (!richOptions || !Array.isArray(richOptions)) {
|
||||
console.error("Step 3 conditions are missing valid options!");
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("Found rich options in Step:", JSON.stringify(richOptions, null, 2));
|
||||
|
||||
// 2. Get the Action
|
||||
const action = await db.query.actions.findFirst({
|
||||
where: eq(actions.id, actionId)
|
||||
});
|
||||
|
||||
if (!action) {
|
||||
console.error("Action not found!");
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("Current Action Parameters:", JSON.stringify(action.parameters, null, 2));
|
||||
|
||||
// 3. Patch the Action Parameters
|
||||
// We replace the simple string options with the rich object options
|
||||
const currentParams = action.parameters as any;
|
||||
const newParams = {
|
||||
...currentParams,
|
||||
options: richOptions // Overwrite with rich options from step
|
||||
};
|
||||
|
||||
console.log("New Action Parameters:", JSON.stringify(newParams, null, 2));
|
||||
|
||||
await db.execute(sql`
|
||||
UPDATE hs_action
|
||||
SET parameters = ${JSON.stringify(newParams)}::jsonb
|
||||
WHERE id = ${actionId}
|
||||
`);
|
||||
|
||||
console.log("Action parameters successfully patched.");
|
||||
}
|
||||
|
||||
patchActionParams()
|
||||
.then(() => process.exit(0))
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
92
scripts/patch-branch-steps.ts
Normal file
92
scripts/patch-branch-steps.ts
Normal file
@@ -0,0 +1,92 @@
|
||||
|
||||
import { db } from "~/server/db";
|
||||
import { steps } from "~/server/db/schema";
|
||||
import { eq, sql } from "drizzle-orm";
|
||||
|
||||
async function patchBranchSteps() {
|
||||
console.log("Patching branch steps for Interactive Storyteller...");
|
||||
|
||||
// Target Step IDs (From debug output)
|
||||
const step3CondId = "b9d43f8c-c40c-4f1c-9fdc-9076338d3c85"; // Step 3: Comprehension Check
|
||||
const stepBranchAId = "3a2dc0b7-a43e-4236-9b9e-f957abafc1e5"; // Step 4: Branch A (Correct)
|
||||
const stepBranchBId = "3ae2fe8a-fc5d-4a04-baa5-699a21f19e30"; // Step 5: Branch B (Incorrect)
|
||||
const stepConclusionId = "cc3fbc7f-29e5-45e0-8d46-e80813c54292"; // Step 6: Conclusion
|
||||
|
||||
// Update Step 3 (The Conditional Step)
|
||||
console.log("Updating Step 3 (Conditional Step)...");
|
||||
const step3Conditional = await db.query.steps.findFirst({
|
||||
where: eq(steps.id, step3CondId)
|
||||
});
|
||||
|
||||
if (step3Conditional) {
|
||||
const currentConditions = (step3Conditional.conditions as any) || {};
|
||||
const options = currentConditions.options || [];
|
||||
|
||||
// Patch options to point to real step IDs
|
||||
const newOptions = options.map((opt: any) => {
|
||||
if (opt.value === "Correct") return { ...opt, nextStepId: stepBranchAId };
|
||||
if (opt.value === "Incorrect") return { ...opt, nextStepId: stepBranchBId };
|
||||
return opt;
|
||||
});
|
||||
|
||||
const newConditions = { ...currentConditions, options: newOptions };
|
||||
|
||||
await db.execute(sql`
|
||||
UPDATE hs_step
|
||||
SET conditions = ${JSON.stringify(newConditions)}::jsonb
|
||||
WHERE id = ${step3CondId}
|
||||
`);
|
||||
console.log("Step 3 (Conditional) updated links.");
|
||||
} else {
|
||||
console.log("Step 3 (Conditional) not found.");
|
||||
}
|
||||
|
||||
// Update Step 4 (Branch A)
|
||||
console.log("Updating Step 4 (Branch A)...");
|
||||
/*
|
||||
Note: We already patched Step 4 in previous run but under wrong assumption?
|
||||
Let's re-patch to be safe.
|
||||
Debug output showed ID: 3a2dc0b7-a43e-4236-9b9e-f957abafc1e5
|
||||
It should jump to Conclusion (cc3fbc7f...)
|
||||
*/
|
||||
const stepBranchA = await db.query.steps.findFirst({
|
||||
where: eq(steps.id, stepBranchAId)
|
||||
});
|
||||
|
||||
if (stepBranchA) {
|
||||
const currentConditions = (stepBranchA.conditions as Record<string, unknown>) || {};
|
||||
const newConditions = { ...currentConditions, nextStepId: stepConclusionId };
|
||||
|
||||
await db.execute(sql`
|
||||
UPDATE hs_step
|
||||
SET conditions = ${JSON.stringify(newConditions)}::jsonb
|
||||
WHERE id = ${stepBranchAId}
|
||||
`);
|
||||
console.log("Step 4 (Branch A) updated jump target.");
|
||||
}
|
||||
|
||||
// Update Step 5 (Branch B)
|
||||
console.log("Updating Step 5 (Branch B)...");
|
||||
const stepBranchB = await db.query.steps.findFirst({
|
||||
where: eq(steps.id, stepBranchBId)
|
||||
});
|
||||
|
||||
if (stepBranchB) {
|
||||
const currentConditions = (stepBranchB.conditions as Record<string, unknown>) || {};
|
||||
const newConditions = { ...currentConditions, nextStepId: stepConclusionId };
|
||||
|
||||
await db.execute(sql`
|
||||
UPDATE hs_step
|
||||
SET conditions = ${JSON.stringify(newConditions)}::jsonb
|
||||
WHERE id = ${stepBranchBId}
|
||||
`);
|
||||
console.log("Step 5 (Branch B) updated jump target.");
|
||||
}
|
||||
}
|
||||
|
||||
patchBranchSteps()
|
||||
.then(() => process.exit(0))
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -35,6 +35,19 @@ async function loadNaoPluginDef() {
|
||||
|
||||
// Global variable to hold the loaded definition
|
||||
let NAO_PLUGIN_DEF: any;
|
||||
let CORE_PLUGIN_DEF: any;
|
||||
let WOZ_PLUGIN_DEF: any;
|
||||
|
||||
function loadSystemPlugin(filename: string) {
|
||||
const LOCAL_PATH = path.join(__dirname, `../src/plugins/definitions/${filename}`);
|
||||
try {
|
||||
const raw = fs.readFileSync(LOCAL_PATH, "utf-8");
|
||||
return JSON.parse(raw);
|
||||
} catch (err) {
|
||||
console.error(`❌ Failed to load system plugin ${filename}:`, err);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
console.log("🌱 Starting realistic seed script...");
|
||||
@@ -43,6 +56,8 @@ async function main() {
|
||||
|
||||
try {
|
||||
NAO_PLUGIN_DEF = await loadNaoPluginDef();
|
||||
CORE_PLUGIN_DEF = loadSystemPlugin("hristudio-core.json");
|
||||
WOZ_PLUGIN_DEF = loadSystemPlugin("hristudio-woz.json");
|
||||
|
||||
// Ensure legacy 'actions' property maps to 'actionDefinitions' if needed, though schema supports both if we map it
|
||||
if (NAO_PLUGIN_DEF.actions && !NAO_PLUGIN_DEF.actionDefinitions) {
|
||||
@@ -61,6 +76,7 @@ async function main() {
|
||||
await db.delete(schema.studyPlugins).where(sql`1=1`);
|
||||
await db.delete(schema.studyMembers).where(sql`1=1`);
|
||||
await db.delete(schema.studies).where(sql`1=1`);
|
||||
await db.delete(schema.studies).where(sql`1=1`);
|
||||
await db.delete(schema.plugins).where(sql`1=1`);
|
||||
await db.delete(schema.pluginRepositories).where(sql`1=1`);
|
||||
await db.delete(schema.userSystemRoles).where(sql`1=1`);
|
||||
@@ -144,12 +160,51 @@ async function main() {
|
||||
{ studyId: study!.id, userId: researcherUser!.id, role: "researcher" }
|
||||
]);
|
||||
|
||||
await db.insert(schema.studyPlugins).values({
|
||||
studyId: study!.id,
|
||||
pluginId: naoPlugin!.id,
|
||||
configuration: { robotIp: "10.0.0.42" },
|
||||
installedBy: adminUser.id
|
||||
});
|
||||
// Insert System Plugins
|
||||
const [corePlugin] = await db.insert(schema.plugins).values({
|
||||
name: CORE_PLUGIN_DEF.name,
|
||||
version: CORE_PLUGIN_DEF.version,
|
||||
description: CORE_PLUGIN_DEF.description,
|
||||
author: CORE_PLUGIN_DEF.author,
|
||||
trustLevel: "official",
|
||||
actionDefinitions: CORE_PLUGIN_DEF.actionDefinitions,
|
||||
robotId: null, // System Plugin
|
||||
metadata: { ...CORE_PLUGIN_DEF, id: CORE_PLUGIN_DEF.id },
|
||||
status: "active"
|
||||
}).returning();
|
||||
|
||||
const [wozPlugin] = await db.insert(schema.plugins).values({
|
||||
name: WOZ_PLUGIN_DEF.name,
|
||||
version: WOZ_PLUGIN_DEF.version,
|
||||
description: WOZ_PLUGIN_DEF.description,
|
||||
author: WOZ_PLUGIN_DEF.author,
|
||||
trustLevel: "official",
|
||||
actionDefinitions: WOZ_PLUGIN_DEF.actionDefinitions,
|
||||
robotId: null, // System Plugin
|
||||
metadata: { ...WOZ_PLUGIN_DEF, id: WOZ_PLUGIN_DEF.id },
|
||||
status: "active"
|
||||
}).returning();
|
||||
|
||||
await db.insert(schema.studyPlugins).values([
|
||||
{
|
||||
studyId: study!.id,
|
||||
pluginId: naoPlugin!.id,
|
||||
configuration: { robotIp: "10.0.0.42" },
|
||||
installedBy: adminUser.id
|
||||
},
|
||||
{
|
||||
studyId: study!.id,
|
||||
pluginId: corePlugin!.id,
|
||||
configuration: {},
|
||||
installedBy: adminUser.id
|
||||
},
|
||||
{
|
||||
studyId: study!.id,
|
||||
pluginId: wozPlugin!.id,
|
||||
configuration: {},
|
||||
installedBy: adminUser.id
|
||||
}
|
||||
]);
|
||||
|
||||
const [experiment] = await db.insert(schema.experiments).values({
|
||||
studyId: study!.id,
|
||||
@@ -256,16 +311,49 @@ async function main() {
|
||||
}
|
||||
]);
|
||||
|
||||
// --- Step 3: Comprehension Check (Wizard Decision Point) ---
|
||||
// Note: Wizard will choose to proceed to Step 4a (Correct) or 4b (Incorrect)
|
||||
// --- Step 3: Comprehension Check (Wizard Decision Point) ---
|
||||
// Note: Wizard will choose to proceed to Step 4a (Correct) or 4b (Incorrect)
|
||||
// --- Step 4a: Correct Response Branch ---
|
||||
const [step4a] = await db.insert(schema.steps).values({
|
||||
experimentId: experiment!.id,
|
||||
name: "Branch A: Correct Response",
|
||||
description: "Response when participant says 'Red'",
|
||||
type: "robot",
|
||||
orderIndex: 3,
|
||||
required: false,
|
||||
durationEstimate: 20
|
||||
}).returning();
|
||||
|
||||
// --- Step 4b: Incorrect Response Branch ---
|
||||
const [step4b] = await db.insert(schema.steps).values({
|
||||
experimentId: experiment!.id,
|
||||
name: "Branch B: Incorrect Response",
|
||||
description: "Response when participant gives wrong answer",
|
||||
type: "robot",
|
||||
orderIndex: 4,
|
||||
required: false,
|
||||
durationEstimate: 20
|
||||
}).returning();
|
||||
|
||||
// --- Step 3: Comprehension Check (Wizard Decision Point) ---
|
||||
// Note: Wizard will choose to proceed to Step 4a (Correct) or 4b (Incorrect)
|
||||
const [step3] = await db.insert(schema.steps).values({
|
||||
experimentId: experiment!.id,
|
||||
name: "Comprehension Check",
|
||||
description: "Ask participant about rock color and wait for wizard input",
|
||||
type: "wizard",
|
||||
type: "conditional",
|
||||
orderIndex: 2,
|
||||
required: true,
|
||||
durationEstimate: 30
|
||||
durationEstimate: 30,
|
||||
conditions: {
|
||||
variable: "last_wizard_response",
|
||||
options: [
|
||||
{ label: "Correct Response (Red)", value: "Correct", nextStepId: step4a!.id, variant: "default" },
|
||||
{ label: "Incorrect Response", value: "Incorrect", nextStepId: step4b!.id, variant: "destructive" }
|
||||
]
|
||||
}
|
||||
}).returning();
|
||||
|
||||
await db.insert(schema.actions).values([
|
||||
@@ -282,30 +370,30 @@ async function main() {
|
||||
},
|
||||
{
|
||||
stepId: step3!.id,
|
||||
name: "Wait for Wizard Input",
|
||||
name: "Wait for Choice",
|
||||
type: "wizard_wait_for_response",
|
||||
orderIndex: 1,
|
||||
// Define the options that will be presented to the Wizard
|
||||
parameters: {
|
||||
prompt_text: "Did participant answer 'Red' correctly?",
|
||||
response_type: "verbal",
|
||||
timeout: 60
|
||||
options: ["Correct", "Incorrect"]
|
||||
},
|
||||
sourceKind: "core",
|
||||
pluginId: "hristudio-woz", // Explicit link
|
||||
category: "wizard"
|
||||
},
|
||||
{
|
||||
stepId: step3!.id,
|
||||
name: "Branch Decision",
|
||||
type: "branch",
|
||||
orderIndex: 2,
|
||||
parameters: {},
|
||||
sourceKind: "core",
|
||||
pluginId: "hristudio-core", // Explicit link
|
||||
category: "control"
|
||||
}
|
||||
]);
|
||||
|
||||
// --- Step 4a: Correct Response Branch ---
|
||||
const [step4a] = await db.insert(schema.steps).values({
|
||||
experimentId: experiment!.id,
|
||||
name: "Branch A: Correct Response",
|
||||
description: "Response when participant says 'Red'",
|
||||
type: "robot",
|
||||
orderIndex: 3,
|
||||
required: false,
|
||||
durationEstimate: 20
|
||||
}).returning();
|
||||
|
||||
await db.insert(schema.actions).values([
|
||||
{
|
||||
stepId: step4a!.id,
|
||||
@@ -342,16 +430,7 @@ async function main() {
|
||||
}
|
||||
]);
|
||||
|
||||
// --- Step 4b: Incorrect Response Branch ---
|
||||
const [step4b] = await db.insert(schema.steps).values({
|
||||
experimentId: experiment!.id,
|
||||
name: "Branch B: Incorrect Response",
|
||||
description: "Response when participant gives wrong answer",
|
||||
type: "robot",
|
||||
orderIndex: 4,
|
||||
required: false,
|
||||
durationEstimate: 20
|
||||
}).returning();
|
||||
|
||||
|
||||
await db.insert(schema.actions).values([
|
||||
{
|
||||
|
||||
78
scripts/simulate-branch-logic.ts
Normal file
78
scripts/simulate-branch-logic.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
|
||||
// Mock of the logic in WizardInterface.tsx handleNextStep
|
||||
const steps = [
|
||||
{
|
||||
id: "b9d43f8c-c40c-4f1c-9fdc-9076338d3c85",
|
||||
name: "Step 3 (Conditional)",
|
||||
order: 2
|
||||
},
|
||||
{
|
||||
id: "3a2dc0b7-a43e-4236-9b9e-f957abafc1e5",
|
||||
name: "Step 4 (Branch A)",
|
||||
order: 3,
|
||||
conditions: {
|
||||
"nextStepId": "cc3fbc7f-29e5-45e0-8d46-e80813c54292"
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "3ae2fe8a-fc5d-4a04-baa5-699a21f19e30",
|
||||
name: "Step 5 (Branch B)",
|
||||
order: 4,
|
||||
conditions: {
|
||||
"nextStepId": "cc3fbc7f-29e5-45e0-8d46-e80813c54292"
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "cc3fbc7f-29e5-45e0-8d46-e80813c54292",
|
||||
name: "Step 6 (Conclusion)",
|
||||
order: 5
|
||||
}
|
||||
];
|
||||
|
||||
function simulateNextStep(currentStepIndex: number) {
|
||||
const currentStep = steps[currentStepIndex];
|
||||
console.log(`\n--- Simulating Next Step from: ${currentStep.name} ---`);
|
||||
console.log("Current Step Data:", JSON.stringify(currentStep, null, 2));
|
||||
|
||||
// Logic from WizardInterface.tsx
|
||||
console.log("[WizardInterface] Checking for nextStepId condition:", currentStep?.conditions);
|
||||
|
||||
if (currentStep?.conditions?.nextStepId) {
|
||||
const nextId = String(currentStep.conditions.nextStepId);
|
||||
const targetIndex = steps.findIndex(s => s.id === nextId);
|
||||
|
||||
console.log(`Target ID: ${nextId}`);
|
||||
console.log(`Target Index Found: ${targetIndex}`);
|
||||
|
||||
if (targetIndex !== -1) {
|
||||
console.log(`[WizardInterface] Condition-based jump to step ${targetIndex} (${nextId})`);
|
||||
return targetIndex;
|
||||
} else {
|
||||
console.warn(`[WizardInterface] Targeted nextStepId ${nextId} not found in steps list.`);
|
||||
}
|
||||
} else {
|
||||
console.log("[WizardInterface] No nextStepId found in conditions, proceeding linearly.");
|
||||
}
|
||||
|
||||
// Default: Linear progression
|
||||
const nextIndex = currentStepIndex + 1;
|
||||
console.log(`Proceeding linearly to index ${nextIndex}`);
|
||||
return nextIndex;
|
||||
}
|
||||
|
||||
// Simulate Branch A (Index 1 in this array, but 3 in real experiment?)
|
||||
// In real exp, Step 3 is index 2. Step 4 (Branch A) is index 3.
|
||||
console.log("Real experiment indices:");
|
||||
// 0: Hook, 1: Narrative, 2: Conditional, 3: Branch A, 4: Branch B, 5: Conclusion
|
||||
const indexStep4 = 1; // logical index in my mock array
|
||||
const indexStep5 = 2; // logical index
|
||||
|
||||
console.log("Testing Branch A Logic:");
|
||||
const resultA = simulateNextStep(indexStep4);
|
||||
if (resultA === 3) console.log("SUCCESS: Branch A jumped to Conclusion");
|
||||
else console.log("FAILURE: Branch A fell through");
|
||||
|
||||
console.log("\nTesting Branch B Logic:");
|
||||
const resultB = simulateNextStep(indexStep5);
|
||||
if (resultB === 3) console.log("SUCCESS: Branch B jumped to Conclusion");
|
||||
else console.log("FAILURE: Branch B fell through");
|
||||
44
scripts/verify-conversion.ts
Normal file
44
scripts/verify-conversion.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
|
||||
import { db } from "../src/server/db";
|
||||
import { experiments } from "../src/server/db/schema";
|
||||
import { eq, asc } from "drizzle-orm";
|
||||
import { convertDatabaseToSteps } from "../src/lib/experiment-designer/block-converter";
|
||||
|
||||
async function verifyConversion() {
|
||||
const experiment = await db.query.experiments.findFirst({
|
||||
with: {
|
||||
steps: {
|
||||
orderBy: (steps, { asc }) => [asc(steps.orderIndex)],
|
||||
with: {
|
||||
actions: {
|
||||
orderBy: (actions, { asc }) => [asc(actions.orderIndex)],
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (!experiment) {
|
||||
console.log("No experiment found");
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("Raw DB Steps Count:", experiment.steps.length);
|
||||
const converted = convertDatabaseToSteps(experiment.steps);
|
||||
|
||||
console.log("Converted Steps:");
|
||||
converted.forEach((s, idx) => {
|
||||
console.log(`[${idx}] ${s.name} (${s.type})`);
|
||||
console.log(` Trigger:`, JSON.stringify(s.trigger));
|
||||
if (s.type === 'conditional') {
|
||||
console.log(` Conditions populated?`, Object.keys(s.trigger.conditions).length > 0);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
verifyConversion()
|
||||
.then(() => process.exit(0))
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
74
scripts/verify-trpc-logic.ts
Normal file
74
scripts/verify-trpc-logic.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
|
||||
import { db } from "~/server/db";
|
||||
import { experiments, steps, actions } from "~/server/db/schema";
|
||||
import { eq, asc, desc } from "drizzle-orm";
|
||||
import { convertDatabaseToSteps } from "~/lib/experiment-designer/block-converter";
|
||||
|
||||
async function verifyTrpcLogic() {
|
||||
console.log("Verifying TRPC Logic for Interactive Storyteller...");
|
||||
|
||||
// 1. Simulate the DB Query from experiments.ts
|
||||
const experiment = await db.query.experiments.findFirst({
|
||||
where: eq(experiments.name, "The Interactive Storyteller"),
|
||||
with: {
|
||||
study: {
|
||||
columns: {
|
||||
id: true,
|
||||
name: true,
|
||||
},
|
||||
},
|
||||
createdBy: {
|
||||
columns: {
|
||||
id: true,
|
||||
name: true,
|
||||
email: true,
|
||||
},
|
||||
},
|
||||
robot: true,
|
||||
steps: {
|
||||
with: {
|
||||
actions: {
|
||||
orderBy: [asc(actions.orderIndex)],
|
||||
},
|
||||
},
|
||||
orderBy: [asc(steps.orderIndex)],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!experiment) {
|
||||
console.error("Experiment not found!");
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. Simulate the Transformation
|
||||
console.log("Transforming DB steps to Designer steps...");
|
||||
const transformedSteps = convertDatabaseToSteps(experiment.steps);
|
||||
|
||||
// 3. Inspect Step 4 (Branch A)
|
||||
// Step index 3 (0-based) is Branch A
|
||||
const branchAStep = transformedSteps[3];
|
||||
console.log("Step 4 (Branch A):", branchAStep.name);
|
||||
console.log(" Type:", branchAStep.type);
|
||||
console.log(" Trigger:", JSON.stringify(branchAStep.trigger, null, 2));
|
||||
|
||||
// Check conditions specifically
|
||||
const conditions = branchAStep.trigger?.conditions as any;
|
||||
if (conditions?.nextStepId) {
|
||||
console.log("SUCCESS: nextStepId found in conditions:", conditions.nextStepId);
|
||||
} else {
|
||||
console.error("FAILURE: nextStepId MISSING in conditions!");
|
||||
}
|
||||
|
||||
// Inspect Step 5 (Branch B) for completeness
|
||||
const branchBStep = transformedSteps[4];
|
||||
console.log("Step 5 (Branch B):", branchBStep.name);
|
||||
console.log(" Trigger:", JSON.stringify(branchBStep.trigger, null, 2));
|
||||
}
|
||||
|
||||
verifyTrpcLogic()
|
||||
.then(() => process.exit(0))
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
Reference in New Issue
Block a user