chore: stabilize Zhinian pilot delivery

This commit is contained in:
inman
2026-05-12 19:44:44 +08:00
parent 45389855e1
commit 20b5aff4ad
174 changed files with 41428 additions and 784 deletions

View File

@@ -6,7 +6,6 @@ import { getOpenClawConfigDir } from './paths';
import { readOpenClawConfig, writeOpenClawConfig } from './channel-config';
import { logger } from './logger';
const YINIAN_MODEL_TIMEOUT_SECONDS = 300;
const YINIAN_MODEL_PROVIDER_KEY = 'minimax';
const YINIAN_MODEL_ID = 'MiniMax-M2.7';
const YINIAN_MODEL_REF = `${YINIAN_MODEL_PROVIDER_KEY}/${YINIAN_MODEL_ID}`;
@@ -19,6 +18,7 @@ const YINIAN_MODEL_AUTH_ALIAS_PROFILE_IDS = [
];
const YINIAN_MODEL_AUTH_TARGET_PROFILE_ID = 'minimax:default';
const YINIAN_MODEL_AUTH_TARGET_PROVIDER = 'minimax';
const YINIAN_FALLBACK_SKILL_IDS = ['docx', 'pdf', 'pptx', 'xlsx', 'design', 'image-search', 'web-search'];
type JsonObject = Record<string, unknown>;
@@ -55,8 +55,8 @@ export interface YinianModelConfigDiagnostics {
modelId: string | null;
};
runtime: {
pricingEnabled: boolean | null;
pricingCatalogFetchDisabled: boolean;
heartbeatEvery: string | null;
heartbeatDisabled: boolean;
};
providers: Array<{
key: string;
@@ -90,6 +90,25 @@ function isPlainRecord(value: unknown): value is JsonObject {
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
}
function arraysEqual(left: string[], right: string[]): boolean {
return left.length === right.length && left.every((value, index) => value === right[index]);
}
function resolveYinianEnabledSkillIds(config: JsonObject): string[] {
const skills = isPlainRecord(config.skills) ? config.skills : {};
const entries = isPlainRecord(skills.entries) ? skills.entries : {};
const enabled = Object.entries(entries)
.filter(([, value]) => !isPlainRecord(value) || value.enabled !== false)
.map(([id]) => id.trim())
.filter(Boolean);
const unique = [...new Set(enabled)];
const ordered = [
...YINIAN_FALLBACK_SKILL_IDS.filter((id) => unique.includes(id)),
...unique.filter((id) => !YINIAN_FALLBACK_SKILL_IDS.includes(id)).sort((left, right) => left.localeCompare(right)),
];
return ordered.length > 0 ? ordered : [...YINIAN_FALLBACK_SKILL_IDS];
}
function getAuthProfilesPath(agentId = 'main'): string {
return join(homedir(), '.openclaw', 'agents', agentId, 'agent', 'auth-profiles.json');
}
@@ -209,22 +228,31 @@ function buildProviderDiagnostics(config: JsonObject, primaryProviderKey: string
});
}
function getPricingEnabled(config: JsonObject): boolean | null {
const models = isPlainRecord(config.models) ? config.models : {};
const pricing = isPlainRecord(models.pricing) ? models.pricing : null;
return typeof pricing?.enabled === 'boolean' ? pricing.enabled : null;
function getHeartbeatEvery(config: JsonObject): string | null {
const agents = isPlainRecord(config.agents) ? config.agents : {};
const defaults = isPlainRecord(agents.defaults) ? agents.defaults : {};
const heartbeat = isPlainRecord(defaults.heartbeat) ? defaults.heartbeat : {};
return typeof heartbeat.every === 'string' ? heartbeat.every : null;
}
export async function ensureYinianModelRuntimeConfigured(): Promise<void> {
const config = await readOpenClawConfig() as JsonObject;
const models = isPlainRecord(config.models) ? { ...config.models } : {};
const providers = isPlainRecord(models.providers) ? { ...models.providers } : {};
const pricing = isPlainRecord(models.pricing) ? { ...models.pricing } : {};
let changed = false;
if ('pricing' in models) {
delete models.pricing;
changed = true;
}
const currentYinianProvider = isPlainRecord(providers[YINIAN_MODEL_PROVIDER_KEY])
? { ...providers[YINIAN_MODEL_PROVIDER_KEY] }
: {};
if ('timeoutSeconds' in currentYinianProvider) {
delete currentYinianProvider.timeoutSeconds;
changed = true;
}
const currentModels = Array.isArray(currentYinianProvider.models)
? currentYinianProvider.models.filter(isPlainRecord)
: [];
@@ -240,7 +268,6 @@ export async function ensureYinianModelRuntimeConfigured(): Promise<void> {
authHeader: typeof currentYinianProvider.authHeader === 'boolean'
? currentYinianProvider.authHeader
: true,
timeoutSeconds: YINIAN_MODEL_TIMEOUT_SECONDS,
models: hasYinianModel
? currentModels
: [
@@ -264,20 +291,14 @@ export async function ensureYinianModelRuntimeConfigured(): Promise<void> {
const currentProvider = providers[providerKey];
if (!isPlainRecord(currentProvider)) continue;
if (currentProvider.timeoutSeconds !== YINIAN_MODEL_TIMEOUT_SECONDS) {
providers[providerKey] = {
...currentProvider,
timeoutSeconds: YINIAN_MODEL_TIMEOUT_SECONDS,
};
if ('timeoutSeconds' in currentProvider) {
const nextProvider = { ...currentProvider };
delete nextProvider.timeoutSeconds;
providers[providerKey] = nextProvider;
changed = true;
}
}
if (pricing.enabled !== false) {
pricing.enabled = false;
changed = true;
}
if (models.mode !== 'merge') {
models.mode = 'merge';
changed = true;
@@ -295,8 +316,12 @@ export async function ensureYinianModelRuntimeConfigured(): Promise<void> {
changed = true;
}
defaults.model = defaultModel;
if (!Array.isArray(defaults.skills)) {
defaults.skills = [];
const enabledSkillIds = resolveYinianEnabledSkillIds(config);
const currentDefaultSkills = Array.isArray(defaults.skills)
? defaults.skills.filter((value): value is string => typeof value === 'string')
: [];
if (!arraysEqual(currentDefaultSkills, enabledSkillIds)) {
defaults.skills = enabledSkillIds;
changed = true;
}
const heartbeat = isPlainRecord(defaults.heartbeat) ? { ...defaults.heartbeat } : {};
@@ -314,8 +339,13 @@ export async function ensureYinianModelRuntimeConfigured(): Promise<void> {
const normalizedList = list.map((item) => {
if (!isPlainRecord(item) || item.id !== 'main') return item;
const tools = isPlainRecord(item.tools) ? item.tools : {};
const currentSkills = Array.isArray(item.skills)
? item.skills.filter((value): value is string => typeof value === 'string')
: [];
const nextSkills = arraysEqual(currentSkills, enabledSkillIds) ? currentSkills : enabledSkillIds;
return {
...item,
skills: nextSkills,
tools: {
...tools,
profile: 'coding',
@@ -337,7 +367,7 @@ export async function ensureYinianModelRuntimeConfigured(): Promise<void> {
default: true,
workspace: join(homedir(), '.openclaw', 'workspace'),
agentDir: '~/.openclaw/agents/main/agent',
skills: [],
skills: enabledSkillIds,
tools: { profile: 'coding' },
},
];
@@ -350,7 +380,6 @@ export async function ensureYinianModelRuntimeConfigured(): Promise<void> {
if (changed) {
models.providers = providers;
models.pricing = pricing;
config.models = models;
await writeOpenClawConfig(config);
logger.info('[provider-sync] Applied Yinian model runtime defaults');
@@ -420,11 +449,10 @@ export async function buildYinianModelConfigDiagnostics(): Promise<YinianModelCo
const primary = getPrimaryModel(config);
const fallbacks = getFallbackModels(config);
const { providerKey, modelId } = splitModelRef(primary);
const pricingEnabled = getPricingEnabled(config);
const heartbeatEvery = getHeartbeatEvery(config);
const providerConfigured = hasConfiguredProvider(config, providerKey);
const authProfileConfigured = hasMatchingAuthProfile(authStore, providerKey);
const providers = buildProviderDiagnostics(config, providerKey);
const primaryProvider = providers.find((provider) => provider.key === providerKey);
const checks: YinianModelConfigCheck[] = [
{
id: 'primary-model',
@@ -449,18 +477,12 @@ export async function buildYinianModelConfigDiagnostics(): Promise<YinianModelCo
: '无法检查调用凭据',
},
{
id: 'timeout',
label: '长任务等待',
status: (primaryProvider?.timeoutSeconds ?? 0) >= YINIAN_MODEL_TIMEOUT_SECONDS ? 'ok' : 'warning',
detail: primaryProvider?.timeoutSeconds
? `${primaryProvider.timeoutSeconds}`
: '未显式配置等待时长',
},
{
id: 'pricing-catalog',
label: '外部价格目录',
status: pricingEnabled === false ? 'ok' : 'warning',
detail: pricingEnabled === false ? '已关闭启动时外部目录拉取' : '未关闭,弱网环境可能拖慢后台启动',
id: 'heartbeat',
label: '后台心跳',
status: heartbeatEvery === '0m' ? 'ok' : 'warning',
detail: heartbeatEvery === '0m'
? '已关闭,不会把心跳任务混入普通会话'
: `当前间隔 ${heartbeatEvery ?? '默认'},可能产生后台心跳会话`,
},
];
@@ -474,8 +496,8 @@ export async function buildYinianModelConfigDiagnostics(): Promise<YinianModelCo
modelId,
},
runtime: {
pricingEnabled,
pricingCatalogFetchDisabled: pricingEnabled === false,
heartbeatEvery,
heartbeatDisabled: heartbeatEvery === '0m',
},
providers,
authProfiles: {