feat: add elapsed ms in debugs
This commit is contained in:
@@ -136,6 +136,8 @@ describe("runDebugBrainInit", () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
expect(result.extractedFacts).toEqual(EXTRACTED_FACTS);
|
expect(result.extractedFacts).toEqual(EXTRACTED_FACTS);
|
||||||
|
expect(typeof result.elapsedMs).toBe("number");
|
||||||
|
expect(result.elapsedMs).toBeGreaterThanOrEqual(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("B2: invokes the LLM exactly 3 times — PERSONA_INIT, PERSONA_BASE_SYSTEM_PROMPT, fact-extractor", async () => {
|
test("B2: invokes the LLM exactly 3 times — PERSONA_INIT, PERSONA_BASE_SYSTEM_PROMPT, fact-extractor", async () => {
|
||||||
@@ -194,6 +196,8 @@ describe("runDebugBrainInit", () => {
|
|||||||
expect(result.ok).toBe(false);
|
expect(result.ok).toBe(false);
|
||||||
if (result.ok) throw new Error("expected !ok");
|
if (result.ok) throw new Error("expected !ok");
|
||||||
expect(result.error).toMatch(/Brain initialization failed/);
|
expect(result.error).toMatch(/Brain initialization failed/);
|
||||||
|
expect(typeof result.elapsedMs).toBe("number");
|
||||||
|
expect(result.elapsedMs).toBeGreaterThanOrEqual(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("B5: with no DB_PATH / BRAINDB_PATH env, runDebugBrainInit still works (no env dependency)", async () => {
|
test("B5: with no DB_PATH / BRAINDB_PATH env, runDebugBrainInit still works (no env dependency)", async () => {
|
||||||
@@ -202,6 +206,9 @@ describe("runDebugBrainInit", () => {
|
|||||||
seed: "no env",
|
seed: "no env",
|
||||||
});
|
});
|
||||||
expect(result.ok).toBe(true);
|
expect(result.ok).toBe(true);
|
||||||
|
if (!result.ok) throw new Error("expected ok");
|
||||||
|
expect(typeof result.elapsedMs).toBe("number");
|
||||||
|
expect(result.elapsedMs).toBeGreaterThanOrEqual(0);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import type { Command } from "commander";
|
|||||||
import ora from "ora";
|
import ora from "ora";
|
||||||
import type { ExtractedFact } from "identitydb";
|
import type { ExtractedFact } from "identitydb";
|
||||||
import { Brain } from "@/brain";
|
import { Brain } from "@/brain";
|
||||||
|
import { formatDuration } from "@/utils/duration";
|
||||||
import { logger } from "@/utils/logger";
|
import { logger } from "@/utils/logger";
|
||||||
|
|
||||||
export interface BrainInitOptions {
|
export interface BrainInitOptions {
|
||||||
@@ -23,8 +24,9 @@ export type BrainInitResult =
|
|||||||
description: string;
|
description: string;
|
||||||
baseSystemPrompt: string;
|
baseSystemPrompt: string;
|
||||||
extractedFacts: ExtractedFact[];
|
extractedFacts: ExtractedFact[];
|
||||||
|
elapsedMs: number;
|
||||||
}
|
}
|
||||||
| { ok: false; error: string };
|
| { ok: false; error: string; elapsedMs: number };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exercise the full `Brain.create` flow (PERSONA_INIT → PERSONA_BASE_SYSTEM_PROMPT
|
* Exercise the full `Brain.create` flow (PERSONA_INIT → PERSONA_BASE_SYSTEM_PROMPT
|
||||||
@@ -44,6 +46,7 @@ export type BrainInitResult =
|
|||||||
export async function runDebugBrainInit(
|
export async function runDebugBrainInit(
|
||||||
opts: BrainInitOptions,
|
opts: BrainInitOptions,
|
||||||
): Promise<BrainInitResult> {
|
): Promise<BrainInitResult> {
|
||||||
|
const startTime = Date.now();
|
||||||
const braindbPath = join(
|
const braindbPath = join(
|
||||||
tmpdir(),
|
tmpdir(),
|
||||||
`brainbox-debug-brain-${randomUUID()}.json`,
|
`brainbox-debug-brain-${randomUUID()}.json`,
|
||||||
@@ -60,7 +63,8 @@ export async function runDebugBrainInit(
|
|||||||
});
|
});
|
||||||
if (!result) {
|
if (!result) {
|
||||||
spinner.fail("Brain initialization failed");
|
spinner.fail("Brain initialization failed");
|
||||||
return { ok: false, error: "Brain initialization failed" };
|
const elapsedMs = Date.now() - startTime;
|
||||||
|
return { ok: false, error: "Brain initialization failed", elapsedMs };
|
||||||
}
|
}
|
||||||
const {
|
const {
|
||||||
brain,
|
brain,
|
||||||
@@ -101,7 +105,10 @@ export async function runDebugBrainInit(
|
|||||||
}
|
}
|
||||||
console.log();
|
console.log();
|
||||||
|
|
||||||
logger.info("Debug run complete. Nothing was written to real disk.");
|
const elapsedMs = Date.now() - startTime;
|
||||||
|
logger.info(
|
||||||
|
`Debug run complete in ${formatDuration(elapsedMs)}. Nothing was written to real disk.`,
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
ok: true,
|
ok: true,
|
||||||
@@ -112,11 +119,13 @@ export async function runDebugBrainInit(
|
|||||||
description,
|
description,
|
||||||
baseSystemPrompt,
|
baseSystemPrompt,
|
||||||
extractedFacts: extractedFacts ?? [],
|
extractedFacts: extractedFacts ?? [],
|
||||||
|
elapsedMs,
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const reason = error instanceof Error ? error.message : String(error);
|
const reason = error instanceof Error ? error.message : String(error);
|
||||||
spinner.fail("Brain initialization failed");
|
spinner.fail("Brain initialization failed");
|
||||||
return { ok: false, error: reason };
|
const elapsedMs = Date.now() - startTime;
|
||||||
|
return { ok: false, error: reason, elapsedMs };
|
||||||
} finally {
|
} finally {
|
||||||
try {
|
try {
|
||||||
await unlink(braindbPath);
|
await unlink(braindbPath);
|
||||||
|
|||||||
@@ -110,6 +110,8 @@ describe("runDebugScheduleDaily", () => {
|
|||||||
expect(result.schedule.items).toHaveLength(48);
|
expect(result.schedule.items).toHaveLength(48);
|
||||||
expect(result.availability.items.length).toBeGreaterThan(0);
|
expect(result.availability.items.length).toBeGreaterThan(0);
|
||||||
expect(result.dateKey).toMatch(/^\d{4}-\d{2}-\d{2}$/);
|
expect(result.dateKey).toMatch(/^\d{4}-\d{2}-\d{2}$/);
|
||||||
|
expect(typeof result.elapsedMs).toBe("number");
|
||||||
|
expect(result.elapsedMs).toBeGreaterThanOrEqual(0);
|
||||||
|
|
||||||
const dailyCall = llmCalls.find(
|
const dailyCall = llmCalls.find(
|
||||||
(c) => c.options.jsonSchemaName === "daily-schedule",
|
(c) => c.options.jsonSchemaName === "daily-schedule",
|
||||||
@@ -133,6 +135,8 @@ describe("runDebugScheduleDaily", () => {
|
|||||||
expect(result.ok).toBe(false);
|
expect(result.ok).toBe(false);
|
||||||
if (result.ok) throw new Error("expected !ok");
|
if (result.ok) throw new Error("expected !ok");
|
||||||
expect(result.error).toMatch(/Daily schedule generation failed/);
|
expect(result.error).toMatch(/Daily schedule generation failed/);
|
||||||
|
expect(typeof result.elapsedMs).toBe("number");
|
||||||
|
expect(result.elapsedMs).toBeGreaterThanOrEqual(0);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -149,6 +153,8 @@ describe("runDebugScheduleMonthly", () => {
|
|||||||
expect(result.kind).toBe("monthly");
|
expect(result.kind).toBe("monthly");
|
||||||
expect(result.schedule.items).toHaveLength(result.daysInMonth);
|
expect(result.schedule.items).toHaveLength(result.daysInMonth);
|
||||||
expect(result.monthKey).toMatch(/^\d{4}-\d{2}$/);
|
expect(result.monthKey).toMatch(/^\d{4}-\d{2}$/);
|
||||||
|
expect(typeof result.elapsedMs).toBe("number");
|
||||||
|
expect(result.elapsedMs).toBeGreaterThanOrEqual(0);
|
||||||
|
|
||||||
const call = llmCalls.find(
|
const call = llmCalls.find(
|
||||||
(c) => c.options.jsonSchemaName === "monthly-schedule",
|
(c) => c.options.jsonSchemaName === "monthly-schedule",
|
||||||
@@ -167,6 +173,8 @@ describe("runDebugScheduleMonthly", () => {
|
|||||||
expect(result.ok).toBe(false);
|
expect(result.ok).toBe(false);
|
||||||
if (result.ok) throw new Error("expected !ok");
|
if (result.ok) throw new Error("expected !ok");
|
||||||
expect(result.error).toMatch(/Monthly schedule generation failed/);
|
expect(result.error).toMatch(/Monthly schedule generation failed/);
|
||||||
|
expect(typeof result.elapsedMs).toBe("number");
|
||||||
|
expect(result.elapsedMs).toBeGreaterThanOrEqual(0);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
type DailySchedule,
|
type DailySchedule,
|
||||||
type MonthlySchedule,
|
type MonthlySchedule,
|
||||||
} from "@/openrouter/schema";
|
} from "@/openrouter/schema";
|
||||||
|
import { formatDuration } from "@/utils/duration";
|
||||||
import { logger } from "@/utils/logger";
|
import { logger } from "@/utils/logger";
|
||||||
import { formatDateKey, nextMonth, pad2 } from "@/brain/schedule";
|
import { formatDateKey, nextMonth, pad2 } from "@/brain/schedule";
|
||||||
|
|
||||||
@@ -22,8 +23,9 @@ export type DailyRunResult =
|
|||||||
tomorrow: Date;
|
tomorrow: Date;
|
||||||
schedule: DailySchedule;
|
schedule: DailySchedule;
|
||||||
availability: AvailabilityWindows;
|
availability: AvailabilityWindows;
|
||||||
|
elapsedMs: number;
|
||||||
}
|
}
|
||||||
| { ok: false; error: string };
|
| { ok: false; error: string; elapsedMs: number };
|
||||||
|
|
||||||
export type MonthlyRunResult =
|
export type MonthlyRunResult =
|
||||||
| {
|
| {
|
||||||
@@ -32,12 +34,14 @@ export type MonthlyRunResult =
|
|||||||
monthKey: string;
|
monthKey: string;
|
||||||
daysInMonth: number;
|
daysInMonth: number;
|
||||||
schedule: MonthlySchedule;
|
schedule: MonthlySchedule;
|
||||||
|
elapsedMs: number;
|
||||||
}
|
}
|
||||||
| { ok: false; error: string };
|
| { ok: false; error: string; elapsedMs: number };
|
||||||
|
|
||||||
export async function runDebugScheduleDaily(
|
export async function runDebugScheduleDaily(
|
||||||
opts: ScheduleOptions,
|
opts: ScheduleOptions,
|
||||||
): Promise<DailyRunResult> {
|
): Promise<DailyRunResult> {
|
||||||
|
const startTime = Date.now();
|
||||||
const today = new Date();
|
const today = new Date();
|
||||||
const tomorrow = new Date(
|
const tomorrow = new Date(
|
||||||
today.getFullYear(),
|
today.getFullYear(),
|
||||||
@@ -54,7 +58,12 @@ export async function runDebugScheduleDaily(
|
|||||||
const schedule = await brain.createDailySchedule(today, opts.message);
|
const schedule = await brain.createDailySchedule(today, opts.message);
|
||||||
if (!schedule) {
|
if (!schedule) {
|
||||||
scheduleSpinner.fail("Daily schedule generation failed");
|
scheduleSpinner.fail("Daily schedule generation failed");
|
||||||
return { ok: false, error: "Daily schedule generation failed" };
|
const elapsedMs = Date.now() - startTime;
|
||||||
|
return {
|
||||||
|
ok: false,
|
||||||
|
error: "Daily schedule generation failed",
|
||||||
|
elapsedMs,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
scheduleSpinner.succeed(
|
scheduleSpinner.succeed(
|
||||||
`Daily schedule generated (${schedule.items.length} slots)`,
|
`Daily schedule generated (${schedule.items.length} slots)`,
|
||||||
@@ -69,7 +78,12 @@ export async function runDebugScheduleDaily(
|
|||||||
const availability = await brain.deriveAvailabilityFromSchedule(schedule);
|
const availability = await brain.deriveAvailabilityFromSchedule(schedule);
|
||||||
if (!availability) {
|
if (!availability) {
|
||||||
availSpinner.fail("Availability derivation failed");
|
availSpinner.fail("Availability derivation failed");
|
||||||
return { ok: false, error: "Availability derivation failed" };
|
const elapsedMs = Date.now() - startTime;
|
||||||
|
return {
|
||||||
|
ok: false,
|
||||||
|
error: "Availability derivation failed",
|
||||||
|
elapsedMs,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
availSpinner.succeed(
|
availSpinner.succeed(
|
||||||
`Availability derived (${availability.items.length} windows)`,
|
`Availability derived (${availability.items.length} windows)`,
|
||||||
@@ -78,7 +92,10 @@ export async function runDebugScheduleDaily(
|
|||||||
printSection(`Availability — ${dateKey}`);
|
printSection(`Availability — ${dateKey}`);
|
||||||
console.log(JSON.stringify(availability, null, 2));
|
console.log(JSON.stringify(availability, null, 2));
|
||||||
|
|
||||||
logger.info("Debug run complete. Nothing was written to disk.");
|
const elapsedMs = Date.now() - startTime;
|
||||||
|
logger.info(
|
||||||
|
`Debug run complete in ${formatDuration(elapsedMs)}. Nothing was written to disk.`,
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
ok: true,
|
ok: true,
|
||||||
@@ -87,12 +104,14 @@ export async function runDebugScheduleDaily(
|
|||||||
tomorrow,
|
tomorrow,
|
||||||
schedule,
|
schedule,
|
||||||
availability,
|
availability,
|
||||||
|
elapsedMs,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function runDebugScheduleMonthly(
|
export async function runDebugScheduleMonthly(
|
||||||
opts: ScheduleOptions,
|
opts: ScheduleOptions,
|
||||||
): Promise<MonthlyRunResult> {
|
): Promise<MonthlyRunResult> {
|
||||||
|
const startTime = Date.now();
|
||||||
const today = new Date();
|
const today = new Date();
|
||||||
const next = nextMonth(today);
|
const next = nextMonth(today);
|
||||||
const monthKey = `${next.year}-${pad2(next.month + 1)}`;
|
const monthKey = `${next.year}-${pad2(next.month + 1)}`;
|
||||||
@@ -105,7 +124,12 @@ export async function runDebugScheduleMonthly(
|
|||||||
const schedule = await brain.createMonthlySchedule(today, opts.message);
|
const schedule = await brain.createMonthlySchedule(today, opts.message);
|
||||||
if (!schedule) {
|
if (!schedule) {
|
||||||
scheduleSpinner.fail("Monthly schedule generation failed");
|
scheduleSpinner.fail("Monthly schedule generation failed");
|
||||||
return { ok: false, error: "Monthly schedule generation failed" };
|
const elapsedMs = Date.now() - startTime;
|
||||||
|
return {
|
||||||
|
ok: false,
|
||||||
|
error: "Monthly schedule generation failed",
|
||||||
|
elapsedMs,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
scheduleSpinner.succeed(
|
scheduleSpinner.succeed(
|
||||||
`Monthly schedule generated (${schedule.items.length} day summaries)`,
|
`Monthly schedule generated (${schedule.items.length} day summaries)`,
|
||||||
@@ -114,8 +138,9 @@ export async function runDebugScheduleMonthly(
|
|||||||
printSection(`Monthly Schedule — ${monthKey} (${next.daysInMonth} days)`);
|
printSection(`Monthly Schedule — ${monthKey} (${next.daysInMonth} days)`);
|
||||||
console.log(JSON.stringify(schedule, null, 2));
|
console.log(JSON.stringify(schedule, null, 2));
|
||||||
|
|
||||||
|
const elapsedMs = Date.now() - startTime;
|
||||||
logger.info(
|
logger.info(
|
||||||
"Debug run complete. Nothing was written to disk. (Availability applies per-day and is not generated for the monthly view.)",
|
`Debug run complete in ${formatDuration(elapsedMs)}. Nothing was written to disk. (Availability applies per-day and is not generated for the monthly view.)`,
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -124,6 +149,7 @@ export async function runDebugScheduleMonthly(
|
|||||||
monthKey,
|
monthKey,
|
||||||
daysInMonth: next.daysInMonth,
|
daysInMonth: next.daysInMonth,
|
||||||
schedule,
|
schedule,
|
||||||
|
elapsedMs,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
15
src/utils/duration.ts
Normal file
15
src/utils/duration.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
/**
|
||||||
|
* Format a millisecond duration as a human-readable string.
|
||||||
|
* - < 1s: "450ms"
|
||||||
|
* - < 60s: "1.23s"
|
||||||
|
* - >= 60s: "2m 5s"
|
||||||
|
*/
|
||||||
|
export function formatDuration(ms: number): string {
|
||||||
|
if (ms < 0 || !Number.isFinite(ms)) return "0ms";
|
||||||
|
if (ms < 1000) return `${Math.round(ms)}ms`;
|
||||||
|
const seconds = ms / 1000;
|
||||||
|
if (seconds < 60) return `${seconds.toFixed(2)}s`;
|
||||||
|
const minutes = Math.floor(seconds / 60);
|
||||||
|
const remainingSeconds = Math.floor(seconds % 60);
|
||||||
|
return `${minutes}m ${remainingSeconds}s`;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user