Files
Jyotisha/frontend/tests/answer-effort-switch-20261013.test.ts
T
Jesse_ChenandClaude Opus 5.5 435c8e19f9
Independent Staging Quality Gate / validate (push) Successful in 24m29s
Independent Staging Quality Gate / publish (push) Successful in 4m17s
Answer step defaults to low reasoning effort (issue #134)
Product chose A after the A/B: low cut answer-step reasoning tokens ~45%
and answer time 34s -> 22s with no quality loss. Unset now means "low";
CONSULT_ANSWER_REASONING_EFFORT=off restores the pre-change request body,
and the old "unset keeps the body" guarantee moves to "off".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 11:00:22 +08:00

152 lines
5.8 KiB
TypeScript

/**
* Answer-step reasoning effort (issue #65).
* The unset bodies were captured before this switch read the environment:
* node node_modules/tsx/dist/cli.mjs tests/answer-effort-probe.mts --dry-run --save-baseline
*/
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import test from "node:test";
import {
consultAnswerReasoningEffort,
consultationContinueGenerationSettings,
consultationGeneralPrepareStep,
consultationGenerationSettings,
consultationNatalPrepareStep,
} from "../src/mastra/consultation-tools.ts";
import { captureStreamBodies } from "./answer-effort-probe.mts";
const MODEL = {
providerId: "deepseek",
modelId: "deepseek-flash",
url: "http://127.0.0.1:9/v1",
apiKey: "dummy",
};
const fixture = JSON.parse(readFileSync(new URL("./fixtures/answer-effort-switch-20261013.json", import.meta.url), "utf8")) as {
natal: unknown[];
continue: unknown[];
};
function withoutEffort(body: unknown) {
const copy = { ...(body as Record<string, unknown>) };
delete copy.reasoning_effort;
return copy;
}
test("an illegal effort warns once and stays unset", () => {
const previous = process.env.CONSULT_ANSWER_REASONING_EFFORT;
const seen: unknown[][] = [];
const warn = console.warn;
console.warn = (...args: unknown[]) => { seen.push(args); };
process.env.CONSULT_ANSWER_REASONING_EFFORT = "turbo";
try {
assert.equal(consultAnswerReasoningEffort(), undefined);
assert.equal(consultAnswerReasoningEffort(), undefined);
assert.equal(seen.length, 1);
} finally {
console.warn = warn;
if (previous === undefined) delete process.env.CONSULT_ANSWER_REASONING_EFFORT;
else process.env.CONSULT_ANSWER_REASONING_EFFORT = previous;
}
});
// 原值: 不设环境变量时请求体与改动前逐字节相同
// 新值: 设成 "off" 时请求体与改动前逐字节相同;不设时默认 "low"(见下一条)
// 原因: issue #134 产品选 A,答题步默认低思考量;"off" 是一行回退
test("off keeps the pre-change request bodies", async () => {
const previous = process.env.CONSULT_ANSWER_REASONING_EFFORT;
process.env.CONSULT_ANSWER_REASONING_EFFORT = "off";
try {
const natal = await captureStreamBodies({
settings: consultationGenerationSettings(MODEL),
prepareStep: (input) => consultationNatalPrepareStep({
stepNumber: input.stepNumber,
answerReady: input.stepNumber >= 1,
model: MODEL,
}),
steps: 2,
});
const continued = await captureStreamBodies({
settings: consultationContinueGenerationSettings(MODEL),
steps: 1,
});
assert.deepEqual(natal, fixture.natal);
assert.deepEqual(continued, fixture.continue);
} finally {
if (previous === undefined) delete process.env.CONSULT_ANSWER_REASONING_EFFORT;
else process.env.CONSULT_ANSWER_REASONING_EFFORT = previous;
}
});
test("unset or blank effort defaults to low", () => {
const previous = process.env.CONSULT_ANSWER_REASONING_EFFORT;
try {
delete process.env.CONSULT_ANSWER_REASONING_EFFORT;
assert.equal(consultAnswerReasoningEffort(), "low");
process.env.CONSULT_ANSWER_REASONING_EFFORT = " ";
assert.equal(consultAnswerReasoningEffort(), "low");
process.env.CONSULT_ANSWER_REASONING_EFFORT = "off";
assert.equal(consultAnswerReasoningEffort(), undefined);
} finally {
if (previous === undefined) delete process.env.CONSULT_ANSWER_REASONING_EFFORT;
else process.env.CONSULT_ANSWER_REASONING_EFFORT = previous;
}
});
test("unset effort puts low on the answer step only", async () => {
const previous = process.env.CONSULT_ANSWER_REASONING_EFFORT;
delete process.env.CONSULT_ANSWER_REASONING_EFFORT;
try {
const natal = await captureStreamBodies({
settings: consultationGenerationSettings(MODEL),
prepareStep: (input) => consultationNatalPrepareStep({
stepNumber: input.stepNumber,
answerReady: input.stepNumber >= 1,
model: MODEL,
}),
steps: 2,
}) as Record<string, unknown>[];
assert.deepEqual(natal[0], fixture.natal[0]);
assert.equal(natal[1]?.reasoning_effort, "low");
assert.deepEqual(withoutEffort(natal[1]), fixture.natal[1]);
} finally {
if (previous === undefined) delete process.env.CONSULT_ANSWER_REASONING_EFFORT;
else process.env.CONSULT_ANSWER_REASONING_EFFORT = previous;
}
});
test("low effort is only on the answer step", async () => {
const previous = process.env.CONSULT_ANSWER_REASONING_EFFORT;
process.env.CONSULT_ANSWER_REASONING_EFFORT = "low";
try {
const natal = await captureStreamBodies({
settings: consultationGenerationSettings(MODEL),
prepareStep: (input) => consultationNatalPrepareStep({
stepNumber: input.stepNumber,
answerReady: input.stepNumber >= 1,
model: MODEL,
}),
steps: 2,
}) as Record<string, unknown>[];
assert.equal(natal[0]?.reasoning_effort, undefined);
assert.deepEqual(natal[0], fixture.natal[0]);
assert.equal(natal[1]?.reasoning_effort, "low");
assert.deepEqual(withoutEffort(natal[1]), fixture.natal[1]);
const continued = await captureStreamBodies({
settings: consultationContinueGenerationSettings(MODEL),
steps: 1,
});
assert.deepEqual(continued, fixture.continue);
const daily = await captureStreamBodies({
settings: consultationGenerationSettings(MODEL),
prepareStep: (input) => consultationGeneralPrepareStep({ stepNumber: input.stepNumber, model: MODEL }),
steps: 1,
}) as Record<string, unknown>[];
assert.equal(daily[0]?.reasoning_effort, "low");
} finally {
if (previous === undefined) delete process.env.CONSULT_ANSWER_REASONING_EFFORT;
else process.env.CONSULT_ANSWER_REASONING_EFFORT = previous;
}
});