feat: 重连时 MD5 校验模型列表,有变化才更新 openclaw.json
- 新增 computeModelsMd5():对模型 id 列表排序后取 MD5 - 新增 refreshModelsIfChanged():读现有 provider 配置拉新模型,MD5 不同才写盘 - client.js: 重连(active + 无完整 provider)时调用 refreshModelsIfChanged,而非直接跳过 Made-with: Cursor
This commit is contained in:
@@ -12,7 +12,7 @@ const { getDashboardInfo, resolveOpenclawConfigFile, startTtyd, FrpcManager } =
|
|||||||
const { ProvisionManager } = require('./provisioning');
|
const { ProvisionManager } = require('./provisioning');
|
||||||
const { BtMonitor } = require('./bt-monitor');
|
const { BtMonitor } = require('./bt-monitor');
|
||||||
const { hasInternet, hasWiredInternetProbe, getLocalIps } = require('./network');
|
const { hasInternet, hasWiredInternetProbe, getLocalIps } = require('./network');
|
||||||
const { applyFullProviderFromVps, removeProviderByName, isFullProvider } = require('./openclaw-provider');
|
const { applyFullProviderFromVps, removeProviderByName, refreshModelsIfChanged, isFullProvider } = require('./openclaw-provider');
|
||||||
const led = require('./led');
|
const led = require('./led');
|
||||||
|
|
||||||
const MAX_BACKOFF_MS = 60_000;
|
const MAX_BACKOFF_MS = 60_000;
|
||||||
@@ -469,7 +469,10 @@ class ClawClient {
|
|||||||
this._updateOpenClawOrigin(clawIdStr);
|
this._updateOpenClawOrigin(clawIdStr);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
// 重连场景:检查模型列表是否有变化,有变化才写盘
|
||||||
|
refreshModelsIfChanged(() => {
|
||||||
this._updateOpenClawOrigin(clawIdStr);
|
this._updateOpenClawOrigin(clawIdStr);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ const fs = require('fs');
|
|||||||
const path = require('path');
|
const path = require('path');
|
||||||
const http = require('http');
|
const http = require('http');
|
||||||
const https = require('https');
|
const https = require('https');
|
||||||
|
const crypto = require('crypto');
|
||||||
const log = require('./logger');
|
const log = require('./logger');
|
||||||
const { resolveOpenclawConfigFile } = require('./frpc');
|
const { resolveOpenclawConfigFile } = require('./frpc');
|
||||||
|
|
||||||
@@ -246,9 +247,86 @@ function isFullProvider(p) {
|
|||||||
|| Object.prototype.hasOwnProperty.call(p, 'baseUrl');
|
|| Object.prototype.hasOwnProperty.call(p, 'baseUrl');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对模型列表计算 MD5(按 id 排序后 JSON 序列化),用于变更检测。
|
||||||
|
*/
|
||||||
|
function computeModelsMd5(models) {
|
||||||
|
const ids = (models || []).map((m) => m.id).sort();
|
||||||
|
return crypto.createHash('md5').update(JSON.stringify(ids)).digest('hex');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重连时刷新模型列表:读取现有 openclaw.json 中第一个 provider 的 baseUrl/apiKey,
|
||||||
|
* 拉取最新模型,MD5 与现有模型对比,不一致才写盘(触发 gateway 自动重启)。
|
||||||
|
* 若模型未变则跳过,不写盘,不触发 gateway 重启。
|
||||||
|
* 完成后调用 onDone()(无论是否更新)。
|
||||||
|
*/
|
||||||
|
function refreshModelsIfChanged(onDone) {
|
||||||
|
if (_busy) {
|
||||||
|
log.info('openclaw-provider', 'refreshModels: 有操作进行中,跳过');
|
||||||
|
if (typeof onDone === 'function') onDone();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const configFile = resolveOpenclawConfigFile();
|
||||||
|
if (!configFile) {
|
||||||
|
if (typeof onDone === 'function') onDone();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let config;
|
||||||
|
try {
|
||||||
|
config = readJsonFile(configFile);
|
||||||
|
} catch (e) {
|
||||||
|
log.warn('openclaw-provider', `refreshModels: 读取配置失败: ${e.message}`);
|
||||||
|
if (typeof onDone === 'function') onDone();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const providers = config.models?.providers || {};
|
||||||
|
const providerId = Object.keys(providers)[0];
|
||||||
|
if (!providerId) {
|
||||||
|
log.info('openclaw-provider', 'refreshModels: 未找到已配置的 provider,跳过');
|
||||||
|
if (typeof onDone === 'function') onDone();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const providerCfg = providers[providerId];
|
||||||
|
const baseUrl = providerCfg.baseUrl || '';
|
||||||
|
const apiKey = providerCfg.apiKey || '';
|
||||||
|
const currentModels = providerCfg.models || [];
|
||||||
|
|
||||||
|
_busy = true;
|
||||||
|
fetchModels(baseUrl, apiKey, (err, newModels) => {
|
||||||
|
try {
|
||||||
|
if (err) {
|
||||||
|
log.warn('openclaw-provider', `refreshModels: 拉模型失败: ${err.message}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentMd5 = computeModelsMd5(currentModels);
|
||||||
|
const newMd5 = computeModelsMd5(newModels);
|
||||||
|
|
||||||
|
if (currentMd5 === newMd5) {
|
||||||
|
log.info('openclaw-provider', `模型列表未变化(${newModels.length} 个),跳过更新`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info('openclaw-provider', `模型列表已变化(${currentModels.length} → ${newModels.length} 个),更新 openclaw.json`);
|
||||||
|
addProviderSync(configFile, providerId, baseUrl, apiKey, newModels, null);
|
||||||
|
} catch (e) {
|
||||||
|
log.error('openclaw-provider', `refreshModels: ${e.message}`);
|
||||||
|
} finally {
|
||||||
|
_busy = false;
|
||||||
|
if (typeof onDone === 'function') onDone();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
applyFullProviderFromVps,
|
applyFullProviderFromVps,
|
||||||
removeProviderByName,
|
removeProviderByName,
|
||||||
|
refreshModelsIfChanged,
|
||||||
isFullProvider,
|
isFullProvider,
|
||||||
DEFAULT_BASE_URL,
|
DEFAULT_BASE_URL,
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user