feat(thinking): 思考等级扩展五档(Max/XHigh)+ 按默认模型 support_efforts 能力感知;删除过时的 max→high 迁移;Segmented 支持逐选项禁用/tooltip — bump to v0.7.23
Release / Version consistency (push) Canceled after 0s
Release / Build (macos-latest) (push) Canceled after 0s
Release / Build (ubuntu-latest) (push) Canceled after 0s
Release / Build (windows-latest) (push) Canceled after 0s
Release / Attach macOS install script (push) Canceled after 0s
Release / Version consistency (push) Canceled after 0s
Release / Build (macos-latest) (push) Canceled after 0s
Release / Build (ubuntu-latest) (push) Canceled after 0s
Release / Build (windows-latest) (push) Canceled after 0s
Release / Attach macOS install script (push) Canceled after 0s
This commit is contained in:
1 parent
50f59cac79
commit
26cb88ad0e
17 files changed
+453
-53
No files matched your search
@@ -0,0 +1,127 @@
|
||||
/**
|
||||
* Thinking-effort capability resolution.
|
||||
*
|
||||
* kimi-code's `[thinking] effort` is a free-form string (low/medium/high/
|
||||
* max/xhigh in practice) that the CLI forwards verbatim to OpenAI-compatible
|
||||
* upstreams as `reasoning_effort`. Which tiers a given model accepts is
|
||||
* declared per model in config.toml (`[models."<alias>"] support_efforts`);
|
||||
* models.dev only carries a boolean `reasoning` flag with no tier list. The
|
||||
* two sources are merged here so the UI can grey out tiers the current default
|
||||
* model would reject, without ever rejecting a stored value on read.
|
||||
*/
|
||||
|
||||
import type { Model } from "../types";
|
||||
|
||||
export const THINKING_EFFORTS = [
|
||||
"low",
|
||||
"medium",
|
||||
"high",
|
||||
"max",
|
||||
"xhigh",
|
||||
] as const;
|
||||
|
||||
export type ThinkingEffort = (typeof THINKING_EFFORTS)[number];
|
||||
|
||||
/** Why a tier is blocked, or (for undeclared models) merely unverified. */
|
||||
export type EffortNote =
|
||||
| { kind: "thinkingUnsupported" }
|
||||
| { kind: "tierUnsupported"; supported: string[] }
|
||||
| { kind: "tierUndeclared" };
|
||||
|
||||
export interface EffortAvailability {
|
||||
/** Whether the tier can be picked. */
|
||||
enabled: boolean;
|
||||
/** Advisory message — set for blocked tiers and for unverified ones. */
|
||||
note?: EffortNote;
|
||||
}
|
||||
|
||||
export interface ThinkingEffortSupport {
|
||||
/** False only when the model is known not to support thinking at all. */
|
||||
thinkingSupported: boolean;
|
||||
/** Declared tiers, or null when nothing is known about the model. */
|
||||
declared: string[] | null;
|
||||
levels: Record<ThinkingEffort, EffortAvailability>;
|
||||
}
|
||||
|
||||
/** Tiers offered unverified when a model declares nothing at all. */
|
||||
const ALWAYS_OFFERED = new Set<string>(["low", "medium", "high"]);
|
||||
|
||||
/**
|
||||
* Normalize a `support_efforts` value into a comparable list, or null when the
|
||||
* key is absent / malformed (treating it as "unknown", not "none supported").
|
||||
*/
|
||||
export function normalizeSupportEfforts(value: unknown): string[] | null {
|
||||
if (!Array.isArray(value)) return null;
|
||||
const tiers = value
|
||||
.filter((v): v is string => typeof v === "string")
|
||||
.map((v) => v.trim())
|
||||
.filter((v) => v.length > 0);
|
||||
return tiers.length > 0 ? tiers : null;
|
||||
}
|
||||
|
||||
/** Read `support_efforts` from a model entry's preserved config.toml fields. */
|
||||
export function readSupportEfforts(model: Model | undefined): string[] | null {
|
||||
if (!model) return null;
|
||||
const raw = model.raw_other;
|
||||
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
|
||||
return normalizeSupportEfforts(
|
||||
(raw as Record<string, unknown>).support_efforts
|
||||
);
|
||||
}
|
||||
|
||||
function buildLevels(
|
||||
resolve: (effort: ThinkingEffort) => EffortAvailability
|
||||
): Record<ThinkingEffort, EffortAvailability> {
|
||||
const levels = {} as Record<ThinkingEffort, EffortAvailability>;
|
||||
for (const effort of THINKING_EFFORTS) levels[effort] = resolve(effort);
|
||||
return levels;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve per-tier availability from the two capability sources, in priority
|
||||
* order:
|
||||
* 1. `supportEfforts` declared → tiers follow the list exactly.
|
||||
* 2. models.dev `reasoning === false` → the whole thinking area is off.
|
||||
* 3. Nothing known → low/medium/high are offered plain, max/xhigh stay
|
||||
* selectable but carry a "not declared" note (upstream may reject them).
|
||||
*/
|
||||
export function resolveThinkingEffortSupport(input: {
|
||||
supportEfforts?: unknown;
|
||||
reasoning?: boolean;
|
||||
}): ThinkingEffortSupport {
|
||||
const declared = normalizeSupportEfforts(input.supportEfforts);
|
||||
if (declared) {
|
||||
const supported = new Set(declared.map((t) => t.toLowerCase()));
|
||||
return {
|
||||
thinkingSupported: true,
|
||||
declared,
|
||||
levels: buildLevels((effort) =>
|
||||
supported.has(effort)
|
||||
? { enabled: true }
|
||||
: {
|
||||
enabled: false,
|
||||
note: { kind: "tierUnsupported", supported: declared },
|
||||
}
|
||||
),
|
||||
};
|
||||
}
|
||||
if (input.reasoning === false) {
|
||||
return {
|
||||
thinkingSupported: false,
|
||||
declared: null,
|
||||
levels: buildLevels(() => ({
|
||||
enabled: false,
|
||||
note: { kind: "thinkingUnsupported" },
|
||||
})),
|
||||
};
|
||||
}
|
||||
return {
|
||||
thinkingSupported: true,
|
||||
declared: null,
|
||||
levels: buildLevels((effort) =>
|
||||
ALWAYS_OFFERED.has(effort)
|
||||
? { enabled: true }
|
||||
: { enabled: true, note: { kind: "tierUndeclared" } }
|
||||
),
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user