mirror of
https://github.com/moltbot/moltbot.git
synced 2026-05-07 07:58:36 +00:00
perf: speed up telegram channel registration (#69786)
Merged via squash.
Prepared head SHA: ac03f96e0d
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
This commit is contained in:
committed by
GitHub
parent
5e72e39c18
commit
ddc1d9aa54
@@ -11,6 +11,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Ollama/onboard: populate the cloud-only model list from `ollama.com/api/tags` so `openclaw onboard` reflects the live cloud catalog instead of a static three-model seed; cap the discovered list at 500 and fall back to the previous hardcoded suggestions when ollama.com is unreachable or returns no models. (#68463) Thanks @BruceMacD.
|
||||
- Matrix/startup: narrow Matrix runtime registration and defer setup/doctor surfaces so cold plugin registration spends about 1.8s less in `setChannelRuntime`. (#69782) Thanks @gumadeiras.
|
||||
- QQBot: extract a self-contained `engine/` architecture with QR-code onboarding, native approval handling via `/bot-approve`, per-account isolated resource stacks and multi-account logger, credential backup/restore, shared `~/.openclaw/media` payload root, and unified API/bridge/gateway modules. (#67960) Thanks @cxyhhhhh.
|
||||
- Telegram/plugin startup: load Telegram's bundled runtime setter through a narrow sidecar and let built sidecars use native loading before falling back to jiti, cutting the measured setup-runtime registration path by about 14s while preserving runtime API compatibility. (#69786) thanks @gumadeiras.
|
||||
|
||||
### Fixes
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ export default defineBundledChannelEntry({
|
||||
exportName: "channelSecrets",
|
||||
},
|
||||
runtime: {
|
||||
specifier: "./runtime-api.js",
|
||||
specifier: "./runtime-setter-api.js",
|
||||
exportName: "setTelegramRuntime",
|
||||
},
|
||||
accountInspect: {
|
||||
|
||||
3
extensions/telegram/runtime-setter-api.ts
Normal file
3
extensions/telegram/runtime-setter-api.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
// Keep bundled registration fast: the runtime setter is needed during plugin
|
||||
// bootstrap, but the broad runtime-api barrel is only for compatibility callers.
|
||||
export { setTelegramRuntime } from "./src/runtime.js";
|
||||
@@ -15,6 +15,7 @@
|
||||
"dist/extensions/lobster/runtime-api.js",
|
||||
"dist/extensions/matrix/helper-api.js",
|
||||
"dist/extensions/matrix/runtime-api.js",
|
||||
"dist/extensions/matrix/runtime-setter-api.js",
|
||||
"dist/extensions/matrix/thread-bindings-runtime.js",
|
||||
"dist/extensions/mattermost/runtime-api.js",
|
||||
"dist/extensions/memory-core/runtime-api.js",
|
||||
@@ -27,6 +28,7 @@
|
||||
"dist/extensions/signal/runtime-api.js",
|
||||
"dist/extensions/slack/runtime-api.js",
|
||||
"dist/extensions/telegram/runtime-api.js",
|
||||
"dist/extensions/telegram/runtime-setter-api.js",
|
||||
"dist/extensions/tlon/runtime-api.js",
|
||||
"dist/extensions/twitch/runtime-api.js",
|
||||
"dist/extensions/voice-call/runtime-api.js",
|
||||
|
||||
@@ -17,6 +17,50 @@ afterEach(() => {
|
||||
vi.unstubAllEnvs();
|
||||
});
|
||||
|
||||
async function expectBuiltArtifactNodeRequireFastPath(
|
||||
scope: string,
|
||||
artifactRoot = "dist",
|
||||
): Promise<void> {
|
||||
vi.stubEnv("OPENCLAW_PLUGIN_LOAD_PROFILE", "1");
|
||||
const errorSpy = vi.spyOn(console, "error").mockImplementation(() => undefined);
|
||||
|
||||
try {
|
||||
const channelEntryContract = await importFreshModule<
|
||||
typeof import("./channel-entry-contract.js")
|
||||
>(import.meta.url, `./channel-entry-contract.js?scope=${scope}`);
|
||||
|
||||
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-channel-entry-contract-"));
|
||||
tempDirs.push(tempRoot);
|
||||
|
||||
const pluginRoot = path.join(tempRoot, artifactRoot, "extensions", "telegram");
|
||||
fs.mkdirSync(pluginRoot, { recursive: true });
|
||||
|
||||
const importerPath = path.join(pluginRoot, "index.js");
|
||||
const sidecarPath = path.join(pluginRoot, "fast-path-sidecar.js");
|
||||
fs.writeFileSync(importerPath, "export default {};\n", "utf8");
|
||||
// CommonJS so `nodeRequire` succeeds without falling back to jiti.
|
||||
fs.writeFileSync(sidecarPath, "module.exports = { sentinel: 7 };\n", "utf8");
|
||||
|
||||
expect(
|
||||
channelEntryContract.loadBundledEntryExportSync<number>(pathToFileURL(importerPath).href, {
|
||||
specifier: "./fast-path-sidecar.js",
|
||||
exportName: "sentinel",
|
||||
}),
|
||||
).toBe(7);
|
||||
|
||||
const profileLine = errorSpy.mock.calls
|
||||
.map((args) => String(args[0] ?? ""))
|
||||
.find((line) => line.startsWith("[plugin-load-profile] phase=bundled-entry-module-load"));
|
||||
expect(profileLine, "expected a bundled-entry-module-load profile line").toBeDefined();
|
||||
expect(profileLine).toContain("getJitiMs=0.0");
|
||||
expect(profileLine).toContain("jitiCallMs=0.0");
|
||||
expect(profileLine).not.toMatch(/getJitiMs=-/);
|
||||
expect(profileLine).not.toMatch(/jitiCallMs=-/);
|
||||
} finally {
|
||||
errorSpy.mockRestore();
|
||||
}
|
||||
}
|
||||
|
||||
describe("loadBundledEntryExportSync", () => {
|
||||
it("includes importer and resolved path context when a bundled sidecar is missing", () => {
|
||||
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-channel-entry-contract-"));
|
||||
@@ -133,51 +177,16 @@ describe("loadBundledEntryExportSync", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("emits zero jiti sub-step timings on the Win32 nodeRequire fast-path", async () => {
|
||||
// The Win32 fast-path goes through `nodeRequire` directly and never
|
||||
it("emits zero jiti sub-step timings on the built-artifact nodeRequire fast-path", async () => {
|
||||
// The built-artifact fast-path goes through `nodeRequire` directly and never
|
||||
// touches jiti. The plugin-load-profile line must reflect that with
|
||||
// `getJitiMs=0.0 jitiCallMs=0.0` rather than negative or full-elapsed
|
||||
// values that would mis-attribute nodeRequire time to jiti sub-steps.
|
||||
const platformSpy = vi.spyOn(process, "platform", "get").mockReturnValue("win32");
|
||||
vi.stubEnv("OPENCLAW_PLUGIN_LOAD_PROFILE", "1");
|
||||
const errorSpy = vi.spyOn(console, "error").mockImplementation(() => undefined);
|
||||
await expectBuiltArtifactNodeRequireFastPath("built-artifact-profile-fast-path");
|
||||
});
|
||||
|
||||
try {
|
||||
const channelEntryContract = await importFreshModule<
|
||||
typeof import("./channel-entry-contract.js")
|
||||
>(import.meta.url, "./channel-entry-contract.js?scope=win32-profile-fast-path");
|
||||
|
||||
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-channel-entry-contract-"));
|
||||
tempDirs.push(tempRoot);
|
||||
|
||||
const pluginRoot = path.join(tempRoot, "dist", "extensions", "telegram");
|
||||
fs.mkdirSync(pluginRoot, { recursive: true });
|
||||
|
||||
const importerPath = path.join(pluginRoot, "index.js");
|
||||
const sidecarPath = path.join(pluginRoot, "fast-path-sidecar.js");
|
||||
fs.writeFileSync(importerPath, "export default {};\n", "utf8");
|
||||
// CommonJS so `nodeRequire` succeeds without falling back to jiti.
|
||||
fs.writeFileSync(sidecarPath, "module.exports = { sentinel: 7 };\n", "utf8");
|
||||
|
||||
expect(
|
||||
channelEntryContract.loadBundledEntryExportSync<number>(pathToFileURL(importerPath).href, {
|
||||
specifier: "./fast-path-sidecar.js",
|
||||
exportName: "sentinel",
|
||||
}),
|
||||
).toBe(7);
|
||||
|
||||
const profileLine = errorSpy.mock.calls
|
||||
.map((args) => String(args[0] ?? ""))
|
||||
.find((line) => line.startsWith("[plugin-load-profile] phase=bundled-entry-module-load"));
|
||||
expect(profileLine, "expected a bundled-entry-module-load profile line").toBeDefined();
|
||||
expect(profileLine).toContain("getJitiMs=0.0");
|
||||
expect(profileLine).toContain("jitiCallMs=0.0");
|
||||
expect(profileLine).not.toMatch(/getJitiMs=-/);
|
||||
expect(profileLine).not.toMatch(/jitiCallMs=-/);
|
||||
} finally {
|
||||
errorSpy.mockRestore();
|
||||
platformSpy.mockRestore();
|
||||
}
|
||||
it("keeps dist-runtime built sidecar loads on the nodeRequire fast-path", async () => {
|
||||
await expectBuiltArtifactNodeRequireFastPath("dist-runtime-profile-fast-path", "dist-runtime");
|
||||
});
|
||||
|
||||
it("can disable source-tree fallback for dist bundled entry checks", () => {
|
||||
|
||||
@@ -316,6 +316,16 @@ function getJiti(modulePath: string) {
|
||||
});
|
||||
}
|
||||
|
||||
function canTryNodeRequireBuiltModule(modulePath: string): boolean {
|
||||
const isBuiltBundledArtifact =
|
||||
modulePath.includes(`${path.sep}dist${path.sep}`) ||
|
||||
modulePath.includes(`${path.sep}dist-runtime${path.sep}`);
|
||||
return (
|
||||
isBuiltBundledArtifact &&
|
||||
[".js", ".mjs", ".cjs"].includes(normalizeLowercaseStringOrEmpty(path.extname(modulePath)))
|
||||
);
|
||||
}
|
||||
|
||||
function loadBundledEntryModuleSync(importMetaUrl: string, specifier: string): unknown {
|
||||
const modulePath = resolveBundledEntryModulePath(importMetaUrl, specifier);
|
||||
const cached = loadedModuleExports.get(modulePath);
|
||||
@@ -326,11 +336,7 @@ function loadBundledEntryModuleSync(importMetaUrl: string, specifier: string): u
|
||||
const profile = shouldProfilePluginLoader();
|
||||
const loadStartMs = profile ? performance.now() : 0;
|
||||
let getJitiEndMs = 0;
|
||||
if (
|
||||
process.platform === "win32" &&
|
||||
modulePath.includes(`${path.sep}dist${path.sep}`) &&
|
||||
[".js", ".mjs", ".cjs"].includes(normalizeLowercaseStringOrEmpty(path.extname(modulePath)))
|
||||
) {
|
||||
if (canTryNodeRequireBuiltModule(modulePath)) {
|
||||
try {
|
||||
loaded = nodeRequire(modulePath);
|
||||
} catch {
|
||||
@@ -355,7 +361,7 @@ function loadBundledEntryModuleSync(importMetaUrl: string, specifier: string): u
|
||||
pluginId: "(bundled-entry)",
|
||||
source: modulePath,
|
||||
elapsedMs: endMs - loadStartMs,
|
||||
// When the Win32 fast-path resolves the module via `nodeRequire`,
|
||||
// When the built-artifact fast-path resolves the module via `nodeRequire`,
|
||||
// `getJitiEndMs` stays `0` because the `catch` block (the only place
|
||||
// it gets stamped) never runs. Reporting `getJitiMs` /
|
||||
// `jitiCallMs` as `0` for that path keeps the breakdown honest:
|
||||
|
||||
@@ -165,6 +165,16 @@ describe("bundled plugin metadata", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("keeps Telegram's narrow runtime setter on the bundled runtime sidecar surface", () => {
|
||||
const telegram = listRepoBundledPluginMetadata().find((entry) => entry.dirName === "telegram");
|
||||
expectArtifactPresence(telegram?.publicSurfaceArtifacts, {
|
||||
contains: ["runtime-setter-api.js"],
|
||||
});
|
||||
expectArtifactPresence(telegram?.runtimeSidecarArtifacts, {
|
||||
contains: ["runtime-setter-api.js"],
|
||||
});
|
||||
});
|
||||
|
||||
it("loads tlon channel config metadata from the lightweight schema surface", () => {
|
||||
expect(collectRepoBundledChannelConfigsForTest("tlon")?.tlon).toEqual(
|
||||
expect.objectContaining({
|
||||
|
||||
@@ -8,6 +8,7 @@ const RUNTIME_SIDECAR_ARTIFACTS = new Set([
|
||||
"helper-api.js",
|
||||
"light-runtime-api.js",
|
||||
"runtime-api.js",
|
||||
"runtime-setter-api.js",
|
||||
"thread-bindings-runtime.js",
|
||||
]);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user