chore: stabilize Zhinian pilot delivery
This commit is contained in:
@@ -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: {
|
||||
|
||||
Reference in New Issue
Block a user