mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-22 07:20:59 +00:00
277 lines
7.7 KiB
TypeScript
277 lines
7.7 KiB
TypeScript
export type ThinkLevel = "off" | "minimal" | "low" | "medium" | "high" | "xhigh" | "adaptive";
|
|
export type VerboseLevel = "off" | "on" | "full";
|
|
export type NoticeLevel = "off" | "on" | "full";
|
|
export type ElevatedLevel = "off" | "on" | "ask" | "full";
|
|
export type ElevatedMode = "off" | "ask" | "full";
|
|
export type ReasoningLevel = "off" | "on" | "stream";
|
|
export type UsageDisplayLevel = "off" | "tokens" | "full";
|
|
export type ThinkingCatalogEntry = {
|
|
provider: string;
|
|
id: string;
|
|
reasoning?: boolean;
|
|
};
|
|
|
|
const BASE_THINKING_LEVELS: ThinkLevel[] = ["off", "minimal", "low", "medium", "high", "adaptive"];
|
|
const ANTHROPIC_CLAUDE_46_MODEL_RE = /^claude-(?:opus|sonnet)-4(?:\.|-)6(?:$|[-.])/i;
|
|
const AMAZON_BEDROCK_CLAUDE_46_MODEL_RE = /claude-(?:opus|sonnet)-4(?:\.|-)6(?:$|[-.])/i;
|
|
const OPENAI_XHIGH_MODEL_IDS = [
|
|
"gpt-5.4",
|
|
"gpt-5.4-pro",
|
|
"gpt-5.4-mini",
|
|
"gpt-5.4-nano",
|
|
"gpt-5.2",
|
|
] as const;
|
|
const OPENAI_CODEX_XHIGH_MODEL_IDS = [
|
|
"gpt-5.4",
|
|
"gpt-5.3-codex",
|
|
"gpt-5.3-codex-spark",
|
|
"gpt-5.2-codex",
|
|
"gpt-5.1-codex",
|
|
] as const;
|
|
const GITHUB_COPILOT_XHIGH_MODEL_IDS = ["gpt-5.2", "gpt-5.2-codex"] as const;
|
|
|
|
function matchesExactOrPrefix(modelId: string, ids: readonly string[]): boolean {
|
|
return ids.some((candidate) => modelId === candidate || modelId.startsWith(`${candidate}-`));
|
|
}
|
|
|
|
export function normalizeProviderId(provider?: string | null): string {
|
|
if (!provider) {
|
|
return "";
|
|
}
|
|
const normalized = provider.trim().toLowerCase();
|
|
if (normalized === "z.ai" || normalized === "z-ai") {
|
|
return "zai";
|
|
}
|
|
if (normalized === "bedrock" || normalized === "aws-bedrock") {
|
|
return "amazon-bedrock";
|
|
}
|
|
return normalized;
|
|
}
|
|
|
|
export function isBinaryThinkingProvider(provider?: string | null): boolean {
|
|
return normalizeProviderId(provider) === "zai";
|
|
}
|
|
|
|
export function supportsBuiltInXHighThinking(
|
|
provider?: string | null,
|
|
model?: string | null,
|
|
): boolean {
|
|
const providerId = normalizeProviderId(provider);
|
|
const modelId = model?.trim().toLowerCase();
|
|
if (!providerId || !modelId) {
|
|
return false;
|
|
}
|
|
if (providerId === "openai") {
|
|
return matchesExactOrPrefix(modelId, OPENAI_XHIGH_MODEL_IDS);
|
|
}
|
|
if (providerId === "openai-codex") {
|
|
return matchesExactOrPrefix(modelId, OPENAI_CODEX_XHIGH_MODEL_IDS);
|
|
}
|
|
if (providerId === "github-copilot") {
|
|
return GITHUB_COPILOT_XHIGH_MODEL_IDS.includes(modelId as never);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Normalize user-provided thinking level strings to the canonical enum.
|
|
export function normalizeThinkLevel(raw?: string | null): ThinkLevel | undefined {
|
|
if (!raw) {
|
|
return undefined;
|
|
}
|
|
const key = raw.trim().toLowerCase();
|
|
const collapsed = key.replace(/[\s_-]+/g, "");
|
|
if (collapsed === "adaptive" || collapsed === "auto") {
|
|
return "adaptive";
|
|
}
|
|
if (collapsed === "xhigh" || collapsed === "extrahigh") {
|
|
return "xhigh";
|
|
}
|
|
if (["off"].includes(key)) {
|
|
return "off";
|
|
}
|
|
if (["on", "enable", "enabled"].includes(key)) {
|
|
return "low";
|
|
}
|
|
if (["min", "minimal"].includes(key)) {
|
|
return "minimal";
|
|
}
|
|
if (["low", "thinkhard", "think-hard", "think_hard"].includes(key)) {
|
|
return "low";
|
|
}
|
|
if (["mid", "med", "medium", "thinkharder", "think-harder", "harder"].includes(key)) {
|
|
return "medium";
|
|
}
|
|
if (
|
|
["high", "ultra", "ultrathink", "think-hard", "thinkhardest", "highest", "max"].includes(key)
|
|
) {
|
|
return "high";
|
|
}
|
|
if (["think"].includes(key)) {
|
|
return "minimal";
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
export function listThinkingLevels(
|
|
_provider?: string | null,
|
|
_model?: string | null,
|
|
): ThinkLevel[] {
|
|
return [...BASE_THINKING_LEVELS];
|
|
}
|
|
|
|
export function listThinkingLevelLabels(provider?: string | null, model?: string | null): string[] {
|
|
if (isBinaryThinkingProvider(provider)) {
|
|
return ["off", "on"];
|
|
}
|
|
return listThinkingLevels(provider, model);
|
|
}
|
|
|
|
export function formatThinkingLevels(
|
|
provider?: string | null,
|
|
model?: string | null,
|
|
separator = ", ",
|
|
): string {
|
|
return listThinkingLevelLabels(provider, model).join(separator);
|
|
}
|
|
|
|
export function formatXHighModelHint(): string {
|
|
return "provider models that advertise xhigh reasoning";
|
|
}
|
|
|
|
export function resolveThinkingDefaultForModel(params: {
|
|
provider: string;
|
|
model: string;
|
|
catalog?: ThinkingCatalogEntry[];
|
|
}): ThinkLevel {
|
|
const normalizedProvider = normalizeProviderId(params.provider);
|
|
const modelId = params.model.trim();
|
|
if (normalizedProvider === "anthropic" && ANTHROPIC_CLAUDE_46_MODEL_RE.test(modelId)) {
|
|
return "adaptive";
|
|
}
|
|
if (normalizedProvider === "amazon-bedrock" && AMAZON_BEDROCK_CLAUDE_46_MODEL_RE.test(modelId)) {
|
|
return "adaptive";
|
|
}
|
|
const candidate = params.catalog?.find(
|
|
(entry) => entry.provider === params.provider && entry.id === params.model,
|
|
);
|
|
if (candidate?.reasoning) {
|
|
return "low";
|
|
}
|
|
return "off";
|
|
}
|
|
|
|
type OnOffFullLevel = "off" | "on" | "full";
|
|
|
|
function normalizeOnOffFullLevel(raw?: string | null): OnOffFullLevel | undefined {
|
|
if (!raw) {
|
|
return undefined;
|
|
}
|
|
const key = raw.toLowerCase();
|
|
if (["off", "false", "no", "0"].includes(key)) {
|
|
return "off";
|
|
}
|
|
if (["full", "all", "everything"].includes(key)) {
|
|
return "full";
|
|
}
|
|
if (["on", "minimal", "true", "yes", "1"].includes(key)) {
|
|
return "on";
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
export function normalizeVerboseLevel(raw?: string | null): VerboseLevel | undefined {
|
|
return normalizeOnOffFullLevel(raw);
|
|
}
|
|
|
|
export function normalizeNoticeLevel(raw?: string | null): NoticeLevel | undefined {
|
|
return normalizeOnOffFullLevel(raw);
|
|
}
|
|
|
|
export function normalizeUsageDisplay(raw?: string | null): UsageDisplayLevel | undefined {
|
|
if (!raw) {
|
|
return undefined;
|
|
}
|
|
const key = raw.toLowerCase();
|
|
if (["off", "false", "no", "0", "disable", "disabled"].includes(key)) {
|
|
return "off";
|
|
}
|
|
if (["on", "true", "yes", "1", "enable", "enabled"].includes(key)) {
|
|
return "tokens";
|
|
}
|
|
if (["tokens", "token", "tok", "minimal", "min"].includes(key)) {
|
|
return "tokens";
|
|
}
|
|
if (["full", "session"].includes(key)) {
|
|
return "full";
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
export function resolveResponseUsageMode(raw?: string | null): UsageDisplayLevel {
|
|
return normalizeUsageDisplay(raw) ?? "off";
|
|
}
|
|
|
|
export function normalizeFastMode(raw?: string | boolean | null): boolean | undefined {
|
|
if (typeof raw === "boolean") {
|
|
return raw;
|
|
}
|
|
if (!raw) {
|
|
return undefined;
|
|
}
|
|
const key = raw.toLowerCase();
|
|
if (["off", "false", "no", "0", "disable", "disabled", "normal"].includes(key)) {
|
|
return false;
|
|
}
|
|
if (["on", "true", "yes", "1", "enable", "enabled", "fast"].includes(key)) {
|
|
return true;
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
export function normalizeElevatedLevel(raw?: string | null): ElevatedLevel | undefined {
|
|
if (!raw) {
|
|
return undefined;
|
|
}
|
|
const key = raw.toLowerCase();
|
|
if (["off", "false", "no", "0"].includes(key)) {
|
|
return "off";
|
|
}
|
|
if (["full", "auto", "auto-approve", "autoapprove"].includes(key)) {
|
|
return "full";
|
|
}
|
|
if (["ask", "prompt", "approval", "approve"].includes(key)) {
|
|
return "ask";
|
|
}
|
|
if (["on", "true", "yes", "1"].includes(key)) {
|
|
return "on";
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
export function resolveElevatedMode(level?: ElevatedLevel | null): ElevatedMode {
|
|
if (!level || level === "off") {
|
|
return "off";
|
|
}
|
|
if (level === "full") {
|
|
return "full";
|
|
}
|
|
return "ask";
|
|
}
|
|
|
|
export function normalizeReasoningLevel(raw?: string | null): ReasoningLevel | undefined {
|
|
if (!raw) {
|
|
return undefined;
|
|
}
|
|
const key = raw.toLowerCase();
|
|
if (["off", "false", "no", "0", "hide", "hidden", "disable", "disabled"].includes(key)) {
|
|
return "off";
|
|
}
|
|
if (["on", "true", "yes", "1", "show", "visible", "enable", "enabled"].includes(key)) {
|
|
return "on";
|
|
}
|
|
if (["stream", "streaming", "draft", "live"].includes(key)) {
|
|
return "stream";
|
|
}
|
|
return undefined;
|
|
}
|