Files
ST-Bionic-Memory-Ecology/runtime/generation-options.js
2026-04-08 01:17:57 +08:00

225 lines
5.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// ST-BME: 任务级生成参数过滤层Phase 1
import { getActiveTaskProfile } from "../prompting/prompt-profiles.js";
const SUPPORTED_FIELDS = [
"max_context_tokens",
"max_completion_tokens",
"reply_count",
"stream",
"temperature",
"top_p",
"top_k",
"top_a",
"min_p",
"seed",
"frequency_penalty",
"presence_penalty",
"repetition_penalty",
"squash_system_messages",
"reasoning_effort",
"request_thoughts",
"enable_function_calling",
"enable_web_search",
"character_name_prefix",
"wrap_user_messages_in_quotes",
];
const CONSERVATIVE_ALLOWLIST = new Set([
"temperature",
"top_p",
"seed",
"max_completion_tokens",
"stream",
"frequency_penalty",
"presence_penalty",
]);
const OPENAI_COMPAT_ALLOWLIST = new Set([
"max_completion_tokens",
"stream",
"temperature",
"top_p",
"seed",
"frequency_penalty",
"presence_penalty",
"reasoning_effort",
"request_thoughts",
"enable_function_calling",
"enable_web_search",
"wrap_user_messages_in_quotes",
]);
const BOOLEAN_FIELDS = new Set([
"stream",
"squash_system_messages",
"request_thoughts",
"enable_function_calling",
"enable_web_search",
"wrap_user_messages_in_quotes",
]);
const INTEGER_FIELDS = new Set([
"max_context_tokens",
"max_completion_tokens",
"reply_count",
"top_k",
"seed",
]);
const FLOAT_FIELDS = new Set([
"temperature",
"top_p",
"top_a",
"min_p",
"frequency_penalty",
"presence_penalty",
"repetition_penalty",
]);
const REASONING_EFFORT_VALUES = new Set(["low", "medium", "high", "minimal"]);
function resolveCapabilityMode(context = {}) {
const normalizedMode = String(context.mode || "").trim().toLowerCase();
if (normalizedMode === "dedicated-openai-compatible") {
return "openai-compatible";
}
const normalizedSource = String(context.source || "").trim().toLowerCase();
if (
normalizedSource &&
["openai", "openrouter", "mistral", "cohere", "custom", "vllm"].includes(
normalizedSource,
)
) {
return "openai-compatible";
}
return "conservative";
}
function getAllowlistForCapability(capabilityMode) {
if (capabilityMode === "openai-compatible") {
return OPENAI_COMPAT_ALLOWLIST;
}
return CONSERVATIVE_ALLOWLIST;
}
function normalizeByField(field, rawValue) {
if (rawValue == null || rawValue === "") {
return { ok: false, reason: "empty_value" };
}
if (BOOLEAN_FIELDS.has(field)) {
return { ok: true, value: Boolean(rawValue) };
}
if (INTEGER_FIELDS.has(field)) {
const parsed = Number.parseInt(rawValue, 10);
if (!Number.isFinite(parsed)) {
return { ok: false, reason: "invalid_number" };
}
if (parsed < 0) {
return { ok: false, reason: "invalid_range" };
}
if (field === "reply_count" && parsed < 1) {
return { ok: false, reason: "invalid_range" };
}
return { ok: true, value: parsed };
}
if (FLOAT_FIELDS.has(field)) {
const parsed = Number.parseFloat(rawValue);
if (!Number.isFinite(parsed)) {
return { ok: false, reason: "invalid_number" };
}
if (field === "temperature" && (parsed < 0 || parsed > 2)) {
return { ok: false, reason: "invalid_range" };
}
if (
["top_p", "top_a", "min_p"].includes(field) &&
(parsed < 0 || parsed > 1)
) {
return { ok: false, reason: "invalid_range" };
}
if (
["frequency_penalty", "presence_penalty", "repetition_penalty"].includes(
field,
) &&
(parsed < -2 || parsed > 2)
) {
return { ok: false, reason: "invalid_range" };
}
return { ok: true, value: parsed };
}
if (field === "reasoning_effort") {
const normalized = String(rawValue || "")
.trim()
.toLowerCase();
if (!normalized) {
return { ok: false, reason: "empty_value" };
}
if (!REASONING_EFFORT_VALUES.has(normalized)) {
return { ok: false, reason: "invalid_value" };
}
return { ok: true, value: normalized };
}
if (field === "character_name_prefix") {
return { ok: true, value: String(rawValue || "").trim() };
}
return { ok: true, value: rawValue };
}
export function resolveTaskGenerationOptions(
settings = {},
taskType,
fallback = {},
capabilityContext = {},
) {
const profile = getActiveTaskProfile(settings, taskType);
const generation = { ...(profile?.generation || {}) };
const filtered = {};
const removed = [];
const capabilityMode = resolveCapabilityMode(capabilityContext);
const allowlist = getAllowlistForCapability(capabilityMode);
for (const field of SUPPORTED_FIELDS) {
if (!Object.prototype.hasOwnProperty.call(generation, field)) continue;
const rawValue = generation[field];
if (rawValue == null || rawValue === "") continue;
if (!allowlist.has(field)) {
removed.push({ field, reason: "capability_filtered", capabilityMode });
continue;
}
const normalized = normalizeByField(field, rawValue);
if (!normalized.ok) {
removed.push({ field, reason: normalized.reason, capabilityMode });
continue;
}
filtered[field] = normalized.value;
}
if (!Number.isFinite(filtered.max_completion_tokens)) {
const fallbackTokens = Number.parseInt(fallback.max_completion_tokens, 10);
if (Number.isFinite(fallbackTokens) && fallbackTokens > 0) {
filtered.max_completion_tokens = fallbackTokens;
}
}
return {
profile,
generation,
filtered,
removed,
capabilityMode,
};
}