hermes-agent/web/src/lib/reasoning-effort.test.ts
Teknium ac00e73688
feat(dashboard): add a reasoning-effort picker to the chat sidebar (#49141)
The web dashboard only showed a read-only "Reasoning" capability badge
with no way to set the effort level — unlike the desktop app, which has
an effort radio in its composer model menu. This adds a picker so the two
surfaces reach parity.

- ReasoningPicker: a Select rendered in the chat sidebar, gated on the
  effective model's supports_reasoning capability (from /api/model/info).
  Reads/writes agent.reasoning_effort via the existing config REST
  endpoints (read-modify-write, the dashboard's single-key save pattern),
  so the value lands in the config the agent boots a fresh chat from.
  Options mirror the desktop: Off/Minimal/Low/Medium/High/Max.
- ChatSidebar: capture supports_reasoning from the model-info fetch and
  render the picker; on change, show the same 'apply on /new or reload'
  notice the model switch uses.
- reasoning-effort.ts: DOM-free helpers (normalizeEffort + options) so the
  node-env vitest harness can cover the resolution logic, plus tests.
2026-06-19 11:37:40 -07:00

48 lines
1.6 KiB
TypeScript

import { describe, it, expect } from "vitest";
import {
EFFORT_OPTIONS,
VALID_EFFORTS,
normalizeEffort,
} from "./reasoning-effort";
describe("normalizeEffort", () => {
it("treats empty/unset as the Hermes default (medium)", () => {
expect(normalizeEffort("")).toBe("medium");
expect(normalizeEffort(null)).toBe("medium");
expect(normalizeEffort(undefined)).toBe("medium");
expect(normalizeEffort(" ")).toBe("medium");
});
it("passes through every valid effort level", () => {
for (const level of ["none", "minimal", "low", "medium", "high", "xhigh"]) {
expect(normalizeEffort(level)).toBe(level);
}
});
it("is case- and whitespace-insensitive", () => {
expect(normalizeEffort("HIGH")).toBe("high");
expect(normalizeEffort(" XHigh ")).toBe("xhigh");
});
it("falls back to medium for unknown values", () => {
expect(normalizeEffort("turbo")).toBe("medium");
expect(normalizeEffort("max")).toBe("medium"); // 'max' is a label, not a value
expect(normalizeEffort(42)).toBe("medium");
});
});
describe("EFFORT_OPTIONS", () => {
it("every option value is in VALID_EFFORTS (no orphan labels)", () => {
for (const opt of EFFORT_OPTIONS) {
expect(VALID_EFFORTS.has(opt.value)).toBe(true);
}
});
it("covers the real reasoning levels plus thinking-off", () => {
// Invariant against hermes_constants.VALID_REASONING_EFFORTS + 'none'.
const values = new Set(EFFORT_OPTIONS.map((o) => o.value));
for (const level of ["none", "minimal", "low", "medium", "high", "xhigh"]) {
expect(values.has(level)).toBe(true);
}
});
});