mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-25 20:05:46 -06:00
refactor(config): retire redundant settings (#113174)
* refactor(config): retire redundant settings * style: apply current formatter * chore: update plugin sdk baseline * fix: keep Codex tool caps context-aware * chore: remove stale imports * test: align WhatsApp QA debounce config * fix(config): clean up retired config checks * fix(ci): align config cleanup checks
This commit is contained in:
committed by
GitHub
parent
1603781bb0
commit
bb657eec93
@@ -9,7 +9,6 @@ import { LEGACY_SECRETREF_ENV_MARKER_PREFIX } from "../config/types.secrets.js";
|
||||
import { migrateLegacySecretRefEnvMarkers } from "../secrets/legacy-secretref-env-marker.js";
|
||||
import { readConfigMachineState } from "../state/config-machine-state.js";
|
||||
import { CORE_HEALTH_CHECKS } from "./doctor-core-checks.js";
|
||||
import "./doctor-tool-result-cap-advice.js";
|
||||
import { resolveDoctorContributionHealthChecks } from "./doctor-health-contributions.js";
|
||||
import {
|
||||
createDoctorHealthContribution,
|
||||
@@ -1865,7 +1864,6 @@ describe("doctor health contributions", () => {
|
||||
expect(contributionIds).toContain("core/doctor/device-pairing");
|
||||
expect(contributionIds).toContain("core/doctor/channel-plugin-blockers");
|
||||
expect(contributionIds).toContain("core/doctor/channel-preview-warnings");
|
||||
expect(contributionIds).toContain("core/doctor/tool-result-cap");
|
||||
expect(contributionIds).toContain("core/doctor/systemd-linger");
|
||||
expect(contributionChecks.map((check) => check.id)).toEqual(contributionIds);
|
||||
});
|
||||
@@ -1935,60 +1933,6 @@ describe("doctor health contributions", () => {
|
||||
expect(mocks.readSystemdUserLingerStatus).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("keeps tool result cap opt-in for default lint selection", async () => {
|
||||
const contributionChecks = await resolveDoctorContributionHealthChecks();
|
||||
const toolResultCapCheck = contributionChecks.find(
|
||||
(check) => check.id === "core/doctor/tool-result-cap",
|
||||
);
|
||||
expect(toolResultCapCheck).toMatchObject({ defaultEnabled: false });
|
||||
expect(toolResultCapCheck).toBeDefined();
|
||||
|
||||
const ctx = {
|
||||
cfg: {
|
||||
agents: {
|
||||
defaults: { contextLimits: { toolResultMaxChars: 16_000 } },
|
||||
},
|
||||
},
|
||||
mode: "lint",
|
||||
runtime: { log: vi.fn(), error: vi.fn(), exit: vi.fn() },
|
||||
} as const;
|
||||
const detect = vi.fn(async () => [
|
||||
{
|
||||
checkId: "core/doctor/tool-result-cap",
|
||||
severity: "warning" as const,
|
||||
message: "Configured tool result cap overrides the model-window default.",
|
||||
path: "agents.defaults.contextLimits.toolResultMaxChars",
|
||||
},
|
||||
]);
|
||||
// This case owns lint selection; the real cap detector is exercised below.
|
||||
const checks = [{ ...toolResultCapCheck!, detect }];
|
||||
|
||||
await expect(runDoctorLintChecks(ctx, { checks })).resolves.toMatchObject({
|
||||
checksRun: 0,
|
||||
checksSkipped: 1,
|
||||
});
|
||||
expect(detect).not.toHaveBeenCalled();
|
||||
await expect(
|
||||
runDoctorLintChecks(ctx, { checks, includeAllChecks: true }),
|
||||
).resolves.toMatchObject({
|
||||
checksRun: 1,
|
||||
checksSkipped: 0,
|
||||
findings: [
|
||||
expect.objectContaining({
|
||||
checkId: "core/doctor/tool-result-cap",
|
||||
path: "agents.defaults.contextLimits.toolResultMaxChars",
|
||||
}),
|
||||
],
|
||||
});
|
||||
await expect(
|
||||
runDoctorLintChecks(ctx, { checks, onlyIds: ["core/doctor/tool-result-cap"] }),
|
||||
).resolves.toMatchObject({
|
||||
checksRun: 1,
|
||||
checksSkipped: 0,
|
||||
});
|
||||
expect(detect).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it("keeps stale plugin-runtime symlinks opt-in for structured lint selection", async () => {
|
||||
const contributionChecks = await resolveDoctorContributionHealthChecks();
|
||||
const check = contributionChecks.find(
|
||||
@@ -2036,56 +1980,6 @@ describe("doctor health contributions", () => {
|
||||
expect(mocks.collectStalePluginRuntimeSymlinkHealthFindings).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("reports agent findings for inherited default tool result caps", async () => {
|
||||
const contributionChecks = await resolveDoctorContributionHealthChecks();
|
||||
const toolResultCapCheck = contributionChecks.find(
|
||||
(check) => check.id === "core/doctor/tool-result-cap",
|
||||
);
|
||||
expect(toolResultCapCheck).toBeDefined();
|
||||
|
||||
mocks.resolveAgentContextLimits.mockImplementation(
|
||||
(cfg: { agents?: { defaults?: { contextLimits?: unknown } } }) =>
|
||||
cfg.agents?.defaults?.contextLimits ?? {},
|
||||
);
|
||||
mocks.resolveDefaultModelForAgent.mockImplementation((...args: unknown[]) => {
|
||||
const params = args[0] as { agentId?: string };
|
||||
return params.agentId === "writer"
|
||||
? { provider: "openai", model: "gpt-5.5" }
|
||||
: { provider: "local", model: "tiny" };
|
||||
});
|
||||
mocks.findModelCatalogEntry.mockImplementation((...args: unknown[]) => {
|
||||
const params = args[1] as { modelId?: string };
|
||||
return params.modelId === "gpt-5.5" ? { contextTokens: 200_000 } : { contextTokens: 8_000 };
|
||||
});
|
||||
|
||||
const ctx = {
|
||||
cfg: {
|
||||
agents: {
|
||||
defaults: { contextLimits: { toolResultMaxChars: 16_000 } },
|
||||
list: [{ id: "writer" }],
|
||||
},
|
||||
},
|
||||
mode: "lint" as const,
|
||||
runtime: { log: vi.fn(), error: vi.fn(), exit: vi.fn() },
|
||||
};
|
||||
|
||||
await expect(
|
||||
runDoctorLintChecks(ctx, {
|
||||
checks: [toolResultCapCheck!],
|
||||
onlyIds: ["core/doctor/tool-result-cap"],
|
||||
}),
|
||||
).resolves.toMatchObject({
|
||||
checksRun: 1,
|
||||
findings: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
checkId: "core/doctor/tool-result-cap",
|
||||
path: "agents.defaults.contextLimits.toolResultMaxChars",
|
||||
target: "agents.list.writer",
|
||||
}),
|
||||
]),
|
||||
});
|
||||
});
|
||||
|
||||
it("keeps legacy plugin dependency lint opt-in and read-only", async () => {
|
||||
const previousStateDir = process.env.OPENCLAW_STATE_DIR;
|
||||
const tempDir = fs.mkdtempSync(nodePath.join(os.tmpdir(), "openclaw-legacy-plugin-deps-lint-"));
|
||||
|
||||
@@ -86,7 +86,6 @@ const loadDoctorCoreChecksModule = async () => await import("./doctor-core-check
|
||||
const loadDoctorStateIntegrityModule = async () =>
|
||||
await import("../commands/doctor-state-integrity.js");
|
||||
const loadHealthCheckRegistryModule = async () => await import("./health-check-registry.js");
|
||||
const loadCatalogLookupModule = async () => await import("../agents/model-catalog.js");
|
||||
const loadPreparedModelCatalogModule = async () =>
|
||||
await import("../agents/prepared-model-catalog.js");
|
||||
const loadModelSelectionModule = async () => await import("../agents/model-selection.js");
|
||||
@@ -842,167 +841,6 @@ async function runHooksModelHealth(ctx: DoctorHealthFlowContext): Promise<void>
|
||||
}
|
||||
}
|
||||
|
||||
type ToolResultCapTarget = {
|
||||
agentId?: string;
|
||||
configuredCap?: number;
|
||||
path?: string;
|
||||
scopeLabel: string;
|
||||
target?: string;
|
||||
};
|
||||
|
||||
async function collectToolResultCapFindings(
|
||||
cfg: OpenClawConfig,
|
||||
): Promise<readonly HealthFinding[]> {
|
||||
const { resolveAgentContextLimits } = await loadAgentScopeModule();
|
||||
const { normalizeAgentId } = await import("../routing/session-key.js");
|
||||
const targets: ToolResultCapTarget[] = [];
|
||||
const defaultsConfiguredCap = cfg.agents?.defaults?.contextLimits?.toolResultMaxChars;
|
||||
if (defaultsConfiguredCap !== undefined) {
|
||||
targets.push({
|
||||
configuredCap: defaultsConfiguredCap,
|
||||
path: "agents.defaults.contextLimits.toolResultMaxChars",
|
||||
scopeLabel: "defaults",
|
||||
target: "agents.defaults",
|
||||
});
|
||||
}
|
||||
for (const entry of cfg.agents?.list ?? []) {
|
||||
const normalizedAgentId = normalizeAgentId(entry.id);
|
||||
if (
|
||||
!normalizedAgentId ||
|
||||
(defaultsConfiguredCap === undefined && entry.contextLimits?.toolResultMaxChars === undefined)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
targets.push({
|
||||
agentId: normalizedAgentId,
|
||||
configuredCap: resolveAgentContextLimits(cfg, normalizedAgentId)?.toolResultMaxChars,
|
||||
path:
|
||||
entry.contextLimits?.toolResultMaxChars === undefined
|
||||
? "agents.defaults.contextLimits.toolResultMaxChars"
|
||||
: `agents.list.${normalizedAgentId}.contextLimits.toolResultMaxChars`,
|
||||
scopeLabel: `agent "${normalizedAgentId}"`,
|
||||
target: `agents.list.${normalizedAgentId}`,
|
||||
});
|
||||
}
|
||||
if (targets.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const { collectToolResultCapDoctorIssues, toolResultCapDoctorIssueToHealthFinding } =
|
||||
await import("./doctor-tool-result-cap-advice.js");
|
||||
|
||||
return collectToolResultCapTargetAdvice({
|
||||
cfg,
|
||||
readOnlyCatalog: true,
|
||||
targets,
|
||||
}).then((entries) =>
|
||||
entries.flatMap((entry) =>
|
||||
collectToolResultCapDoctorIssues(entry).map(toolResultCapDoctorIssueToHealthFinding),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
async function collectToolResultCapTargetAdvice(params: {
|
||||
cfg: OpenClawConfig;
|
||||
readOnlyCatalog?: boolean;
|
||||
targets: readonly ToolResultCapTarget[];
|
||||
}): Promise<
|
||||
Array<{
|
||||
contextWindowTokens: number;
|
||||
modelKey: string;
|
||||
configuredCap?: number;
|
||||
deep?: boolean;
|
||||
path?: string;
|
||||
scopeLabel?: string;
|
||||
target?: string;
|
||||
}>
|
||||
> {
|
||||
const { DEFAULT_CONTEXT_TOKENS } = await loadAgentDefaultsModule();
|
||||
const { findModelCatalogEntry } = await loadCatalogLookupModule();
|
||||
const { loadPreparedModelCatalog } = await loadPreparedModelCatalogModule();
|
||||
const { resolveContextWindowInfo } = await import("../agents/context-window-guard.js");
|
||||
const { resolveDefaultModelForAgent, modelKey } = await loadModelSelectionModule();
|
||||
const catalog = await loadPreparedModelCatalog({
|
||||
config: params.cfg,
|
||||
...(params.readOnlyCatalog ? { readOnly: true } : {}),
|
||||
});
|
||||
|
||||
return params.targets.map((target) => {
|
||||
const modelRef = resolveDefaultModelForAgent({
|
||||
cfg: params.cfg,
|
||||
agentId: target.agentId,
|
||||
});
|
||||
const entry = findModelCatalogEntry(catalog, {
|
||||
provider: modelRef.provider,
|
||||
modelId: modelRef.model,
|
||||
});
|
||||
const contextWindow = resolveContextWindowInfo({
|
||||
cfg: params.cfg,
|
||||
provider: modelRef.provider,
|
||||
modelId: modelRef.model,
|
||||
modelContextTokens: entry?.contextTokens,
|
||||
modelContextWindow: entry?.contextWindow,
|
||||
defaultTokens: DEFAULT_CONTEXT_TOKENS,
|
||||
});
|
||||
return {
|
||||
contextWindowTokens: contextWindow.tokens,
|
||||
modelKey: modelKey(modelRef.provider, modelRef.model),
|
||||
configuredCap: target.configuredCap,
|
||||
path: target.path,
|
||||
scopeLabel: target.scopeLabel,
|
||||
target: target.target,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
async function runToolResultCapHealth(ctx: DoctorHealthFlowContext): Promise<void> {
|
||||
const { resolveAgentContextLimits } = await loadAgentScopeModule();
|
||||
const { normalizeAgentId } = await import("../routing/session-key.js");
|
||||
const targets: ToolResultCapTarget[] = [];
|
||||
const defaultsConfiguredCap = ctx.cfg.agents?.defaults?.contextLimits?.toolResultMaxChars;
|
||||
if (ctx.options.deep === true || defaultsConfiguredCap !== undefined) {
|
||||
targets.push({
|
||||
configuredCap: defaultsConfiguredCap,
|
||||
scopeLabel: "defaults",
|
||||
});
|
||||
}
|
||||
for (const entry of ctx.cfg.agents?.list ?? []) {
|
||||
const normalizedAgentId = normalizeAgentId(entry.id);
|
||||
if (
|
||||
!normalizedAgentId ||
|
||||
(ctx.options.deep !== true &&
|
||||
defaultsConfiguredCap === undefined &&
|
||||
entry.contextLimits?.toolResultMaxChars === undefined)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
targets.push({
|
||||
agentId: normalizedAgentId,
|
||||
configuredCap: resolveAgentContextLimits(ctx.cfg, normalizedAgentId)?.toolResultMaxChars,
|
||||
scopeLabel: `agent "${normalizedAgentId}"`,
|
||||
});
|
||||
}
|
||||
if (targets.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { buildToolResultCapDoctorAdvice } = await import("./doctor-tool-result-cap-advice.js");
|
||||
const { note } = await loadNoteModule();
|
||||
const entries = await collectToolResultCapTargetAdvice({
|
||||
cfg: ctx.cfg,
|
||||
targets,
|
||||
});
|
||||
const lines = entries.flatMap((entry) =>
|
||||
buildToolResultCapDoctorAdvice({
|
||||
...entry,
|
||||
deep: ctx.options.deep === true,
|
||||
}),
|
||||
);
|
||||
if (lines.length > 0) {
|
||||
note(lines.join("\n"), "Tool result cap");
|
||||
}
|
||||
}
|
||||
|
||||
async function runSystemdLingerHealth(ctx: DoctorHealthFlowContext): Promise<void> {
|
||||
if (
|
||||
ctx.options.nonInteractive === true ||
|
||||
@@ -2095,18 +1933,6 @@ function resolveDoctorHealthContributions(): DoctorHealthContribution[] {
|
||||
healthCheckIds: ["core/doctor/hooks-model"],
|
||||
run: runHooksModelHealth,
|
||||
}),
|
||||
createDoctorHealthContribution({
|
||||
id: "doctor:tool-result-cap",
|
||||
label: "Tool result cap",
|
||||
healthChecks: {
|
||||
id: "core/doctor/tool-result-cap",
|
||||
description:
|
||||
"Detect explicit toolResultMaxChars settings that fight model-window defaults.",
|
||||
defaultEnabled: false,
|
||||
detect: async (ctx) => collectToolResultCapFindings(ctx.cfg),
|
||||
},
|
||||
run: runToolResultCapHealth,
|
||||
}),
|
||||
createDoctorHealthContribution({
|
||||
id: "doctor:provider-catalog-projection",
|
||||
label: "Provider catalog projection",
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
// Tool result cap advice tests cover doctor guidance for capped tool output.
|
||||
import { expectDefined } from "@openclaw/normalization-core";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
buildToolResultCapDoctorAdvice,
|
||||
collectToolResultCapDoctorIssues,
|
||||
toolResultCapDoctorIssueToHealthFinding,
|
||||
} from "./doctor-tool-result-cap-advice.js";
|
||||
|
||||
describe("buildToolResultCapDoctorAdvice", () => {
|
||||
it("stays quiet for unset config outside deep doctor output", () => {
|
||||
expect(
|
||||
buildToolResultCapDoctorAdvice({
|
||||
contextWindowTokens: 200_000,
|
||||
modelKey: "openai/gpt-5.5",
|
||||
}),
|
||||
).toEqual([]);
|
||||
});
|
||||
|
||||
it("shows the effective auto cap in deep doctor output", () => {
|
||||
expect(
|
||||
buildToolResultCapDoctorAdvice({
|
||||
contextWindowTokens: 200_000,
|
||||
modelKey: "openai/gpt-5.5",
|
||||
deep: true,
|
||||
}),
|
||||
).toEqual([
|
||||
'- primary model "openai/gpt-5.5" context window 200,000 tokens; live tool-result cap 64,000 chars (auto)',
|
||||
]);
|
||||
});
|
||||
|
||||
it("warns when an explicit cap keeps a large model on the old 16K ceiling", () => {
|
||||
expect(
|
||||
buildToolResultCapDoctorAdvice({
|
||||
contextWindowTokens: 200_000,
|
||||
modelKey: "openai/gpt-5.5",
|
||||
configuredCap: 16_000,
|
||||
scopeLabel: 'agent "writer"',
|
||||
}),
|
||||
).toEqual([
|
||||
'- agent "writer": configured toolResultMaxChars is 16,000 chars; unset it to use the 64,000 char auto cap for "openai/gpt-5.5".',
|
||||
]);
|
||||
});
|
||||
|
||||
it("warns when an explicit cap is above the runtime context-share ceiling", () => {
|
||||
expect(
|
||||
buildToolResultCapDoctorAdvice({
|
||||
contextWindowTokens: 8_000,
|
||||
modelKey: "local/tiny",
|
||||
configuredCap: 20_000,
|
||||
}),
|
||||
).toEqual([
|
||||
"- configured toolResultMaxChars is 20,000 chars, but this model can use at most 9,600 chars per live tool result; lower it or unset it.",
|
||||
]);
|
||||
});
|
||||
|
||||
it("maps cap advice issues to structured health findings", () => {
|
||||
const [issue] = collectToolResultCapDoctorIssues({
|
||||
contextWindowTokens: 200_000,
|
||||
modelKey: "openai/gpt-5.5",
|
||||
configuredCap: 16_000,
|
||||
path: "agents.writer.contextLimits.toolResultMaxChars",
|
||||
scopeLabel: 'agent "writer"',
|
||||
target: "agents.writer",
|
||||
});
|
||||
|
||||
expect(
|
||||
toolResultCapDoctorIssueToHealthFinding(expectDefined(issue, "issue test invariant")),
|
||||
).toEqual({
|
||||
checkId: "core/doctor/tool-result-cap",
|
||||
severity: "warning",
|
||||
message:
|
||||
'agent "writer": configured toolResultMaxChars is 16,000 chars; unset it to use the 64,000 char auto cap for "openai/gpt-5.5".',
|
||||
path: "agents.writer.contextLimits.toolResultMaxChars",
|
||||
target: "agents.writer",
|
||||
requirement: "configured-below-auto-cap",
|
||||
fixHint: "Lower or unset agents.writer.contextLimits.toolResultMaxChars.",
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,162 +0,0 @@
|
||||
// Tool result cap advice helpers format doctor guidance for capped outputs.
|
||||
import {
|
||||
calculateMaxToolResultCharsWithCap,
|
||||
resolveAutoLiveToolResultMaxChars,
|
||||
} from "../agents/embedded-agent-runner/tool-result-truncation.js";
|
||||
import type { HealthFinding } from "./health-checks.js";
|
||||
|
||||
const TOOL_RESULT_CAP_CHECK_ID = "core/doctor/tool-result-cap";
|
||||
|
||||
type ToolResultCapDoctorIssue = {
|
||||
kind: "configured-above-runtime-ceiling" | "configured-below-auto-cap";
|
||||
contextWindowTokens: number;
|
||||
modelKey: string;
|
||||
configuredCap: number;
|
||||
runtimeCeiling?: number;
|
||||
autoEffectiveCap?: number;
|
||||
path?: string;
|
||||
scopeLabel?: string;
|
||||
target?: string;
|
||||
};
|
||||
|
||||
// Doctor advice for explicit live tool-result caps that fight model-window defaults.
|
||||
type ToolResultCapDoctorAdviceParams = {
|
||||
contextWindowTokens: number;
|
||||
modelKey: string;
|
||||
configuredCap?: number;
|
||||
deep?: boolean;
|
||||
path?: string;
|
||||
scopeLabel?: string;
|
||||
target?: string;
|
||||
};
|
||||
|
||||
function formatNumber(value: number): string {
|
||||
return String(Math.max(0, Math.floor(value))).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
||||
}
|
||||
|
||||
function formatIssueMessage(issue: ToolResultCapDoctorIssue): string {
|
||||
const prefix = issue.scopeLabel ? `${issue.scopeLabel}: ` : "";
|
||||
if (issue.kind === "configured-above-runtime-ceiling") {
|
||||
return `${prefix}configured toolResultMaxChars is ${formatNumber(
|
||||
issue.configuredCap,
|
||||
)} chars, but this model can use at most ${formatNumber(
|
||||
issue.runtimeCeiling ?? 0,
|
||||
)} chars per live tool result; lower it or unset it.`;
|
||||
}
|
||||
return `${prefix}configured toolResultMaxChars is ${formatNumber(
|
||||
issue.configuredCap,
|
||||
)} chars; unset it to use the ${formatNumber(issue.autoEffectiveCap ?? 0)} char auto cap for "${
|
||||
issue.modelKey
|
||||
}".`;
|
||||
}
|
||||
|
||||
export function collectToolResultCapDoctorIssues(
|
||||
params: ToolResultCapDoctorAdviceParams,
|
||||
): ToolResultCapDoctorIssue[] {
|
||||
if (!Number.isFinite(params.contextWindowTokens) || params.contextWindowTokens <= 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const configuredCap =
|
||||
typeof params.configuredCap === "number" && Number.isFinite(params.configuredCap)
|
||||
? Math.floor(params.configuredCap)
|
||||
: undefined;
|
||||
if (configuredCap === undefined) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const autoCap = resolveAutoLiveToolResultMaxChars(params.contextWindowTokens);
|
||||
const runtimeCeiling = calculateMaxToolResultCharsWithCap(
|
||||
params.contextWindowTokens,
|
||||
Number.MAX_SAFE_INTEGER,
|
||||
);
|
||||
const effectiveCap = calculateMaxToolResultCharsWithCap(
|
||||
params.contextWindowTokens,
|
||||
configuredCap,
|
||||
);
|
||||
const autoEffectiveCap = calculateMaxToolResultCharsWithCap(params.contextWindowTokens, autoCap);
|
||||
|
||||
if (configuredCap > runtimeCeiling) {
|
||||
return [
|
||||
{
|
||||
kind: "configured-above-runtime-ceiling",
|
||||
contextWindowTokens: params.contextWindowTokens,
|
||||
modelKey: params.modelKey,
|
||||
configuredCap,
|
||||
runtimeCeiling,
|
||||
path: params.path,
|
||||
scopeLabel: params.scopeLabel,
|
||||
target: params.target,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
if (effectiveCap < autoEffectiveCap) {
|
||||
return [
|
||||
{
|
||||
kind: "configured-below-auto-cap",
|
||||
contextWindowTokens: params.contextWindowTokens,
|
||||
modelKey: params.modelKey,
|
||||
configuredCap,
|
||||
autoEffectiveCap,
|
||||
path: params.path,
|
||||
scopeLabel: params.scopeLabel,
|
||||
target: params.target,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
export function toolResultCapDoctorIssueToHealthFinding(
|
||||
issue: ToolResultCapDoctorIssue,
|
||||
): HealthFinding {
|
||||
return {
|
||||
checkId: TOOL_RESULT_CAP_CHECK_ID,
|
||||
severity: "warning",
|
||||
message: formatIssueMessage(issue),
|
||||
...(issue.path ? { path: issue.path } : {}),
|
||||
...(issue.target ? { target: issue.target } : {}),
|
||||
requirement: issue.kind,
|
||||
fixHint: issue.path ? `Lower or unset ${issue.path}.` : "Lower or unset toolResultMaxChars.",
|
||||
};
|
||||
}
|
||||
|
||||
/** Builds human-readable doctor lines for stale or ineffective toolResultMaxChars settings. */
|
||||
export function buildToolResultCapDoctorAdvice(params: ToolResultCapDoctorAdviceParams): string[] {
|
||||
if (!Number.isFinite(params.contextWindowTokens) || params.contextWindowTokens <= 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const autoCap = resolveAutoLiveToolResultMaxChars(params.contextWindowTokens);
|
||||
const configuredCap =
|
||||
typeof params.configuredCap === "number" && Number.isFinite(params.configuredCap)
|
||||
? Math.floor(params.configuredCap)
|
||||
: undefined;
|
||||
const configuredSource = configuredCap !== undefined;
|
||||
const requestedCap = configuredCap ?? autoCap;
|
||||
const effectiveCap = calculateMaxToolResultCharsWithCap(params.contextWindowTokens, requestedCap);
|
||||
const lines: string[] = [];
|
||||
const prefix = params.scopeLabel ? `${params.scopeLabel}: ` : "";
|
||||
// Deep mode always shows the effective cap, even when no warning is needed.
|
||||
if (params.deep) {
|
||||
lines.push(
|
||||
`- ${prefix}primary model "${params.modelKey}" context window ${formatNumber(
|
||||
params.contextWindowTokens,
|
||||
)} tokens; live tool-result cap ${formatNumber(effectiveCap)} chars (${
|
||||
configuredSource ? "explicit" : "auto"
|
||||
})`,
|
||||
);
|
||||
}
|
||||
|
||||
if (configuredCap === undefined) {
|
||||
return lines;
|
||||
}
|
||||
|
||||
lines.push(
|
||||
...collectToolResultCapDoctorIssues(params).map((issue) => `- ${formatIssueMessage(issue)}`),
|
||||
);
|
||||
|
||||
return lines;
|
||||
}
|
||||
Reference in New Issue
Block a user