Skip to content

Commit 8283c5d

Browse files
DmitryPogodaevDmitryPogodaev
andauthored
perf(plugins): reuse startup runtime registry
Reuse the startup runtime plugin registry across provider/tool helper paths while preserving standalone CLI/MCP fallback loading. Includes follow-up fixes for migration/provider/tool registry bootstrap and regression coverage for compatible registry reuse. Co-authored-by: DmitryPogodaev <pogodaev.dm@gmail.com>
1 parent 6959609 commit 8283c5d

36 files changed

Lines changed: 1176 additions & 519 deletions

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ Docs: https://docs.openclaw.ai
66

77
### Changes
88

9+
- Agents/runtime: reuse the startup-loaded plugin registry for request-time providers, tools, channel actions, web/capability/memory/migration helpers, and memoized provider extra-params so stable embedded-run inputs no longer repeat plugin registry resolution while model-specific transport hook patches stay isolated. Thanks @DmitryPogodaev.
910
- Agents/runtime: memoize transcript replay-policy resolution for stable config and process-env runs while preserving custom-env provider hook behavior. Thanks @DmitryPogodaev.
1011
- Infra/path-guards: add a fast path for canonical absolute POSIX containment checks, avoiding repeated `path.resolve` and `path.relative` work in hot filesystem walkers. Refs #75895, #75575, and #68782. Thanks @Enderfga.
1112
- Tools: add a platform-level tool descriptor planner for descriptor-first visibility, generic availability checks, and executor references. Thanks @shakkernerd.

src/agents/pi-embedded-runner-extraparams.test.ts

Lines changed: 82 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -686,7 +686,7 @@ describe("applyExtraParamsToAgent", () => {
686686
api: "openai-responses",
687687
provider: "xai",
688688
id: "grok-4.20-beta-latest-reasoning",
689-
} as Model<"openai-responses">,
689+
} as unknown as Model<"openai-responses">,
690690
payload: {
691691
model: "grok-4.20-beta-latest-reasoning",
692692
input: [],
@@ -743,7 +743,7 @@ describe("applyExtraParamsToAgent", () => {
743743
id: "gpt-5",
744744
baseUrl: "http://127.0.0.1:19191/v1",
745745
reasoning: true,
746-
} as Model<"openai-responses">,
746+
} as unknown as Model<"openai-responses">,
747747
payload: {
748748
model: "gpt-5",
749749
input: [],
@@ -811,7 +811,7 @@ describe("applyExtraParamsToAgent", () => {
811811
api: "openai-completions",
812812
provider: "nvidia-nim",
813813
id: "moonshotai/kimi-k2.5",
814-
} as Model<"openai-completions">,
814+
} as unknown as Model<"openai-completions">,
815815
});
816816

817817
expect(payload.parallel_tool_calls).toBe(false);
@@ -838,7 +838,7 @@ describe("applyExtraParamsToAgent", () => {
838838
api: "openai-completions",
839839
provider: "openrouter",
840840
id: "openrouter/auto",
841-
} as Model<"openai-completions">,
841+
} as unknown as Model<"openai-completions">,
842842
});
843843

844844
expect(payload.parallel_tool_calls).toBe(false);
@@ -1908,7 +1908,7 @@ describe("applyExtraParamsToAgent", () => {
19081908
api: "openai-responses",
19091909
provider: "openai",
19101910
id: "gpt-5.4",
1911-
} as Model<"openai-responses">,
1911+
} as unknown as Model<"openai-responses">,
19121912
payload: {},
19131913
});
19141914

@@ -2025,7 +2025,7 @@ describe("applyExtraParamsToAgent", () => {
20252025
api: "openai-responses",
20262026
provider: "openai",
20272027
id: "gpt-5",
2028-
} as Model<"openai-responses">,
2028+
} as unknown as Model<"openai-responses">,
20292029
payload: { tools: [{ type: "function", name: "read" }] },
20302030
});
20312031

@@ -2257,6 +2257,82 @@ describe("applyExtraParamsToAgent", () => {
22572257
);
22582258
});
22592259

2260+
it("keys prepared extra-param memoization by resolved model transport inputs", () => {
2261+
const resolveProviderExtraParamsForTransport = vi.fn((params) => ({
2262+
patch: {
2263+
transportFamily: params.context.model?.api,
2264+
baseUrl: (params.context.model as Record<string, unknown> | undefined)?.baseUrl,
2265+
headerAuth: (
2266+
(params.context.model as Record<string, unknown> | undefined)?.headers as
2267+
| Record<string, unknown>
2268+
| undefined
2269+
)?.["X-Test"],
2270+
},
2271+
}));
2272+
extraParamsTesting.setProviderRuntimeDepsForTest({
2273+
prepareProviderExtraParams: (params) => params.context.extraParams,
2274+
resolveProviderExtraParamsForTransport,
2275+
wrapProviderStreamFn: (params) => params.context.streamFn,
2276+
});
2277+
const cfg = {};
2278+
2279+
const responsesParams = resolvePreparedExtraParams({
2280+
cfg,
2281+
provider: "openai",
2282+
modelId: "gpt-5",
2283+
model: {
2284+
api: "openai-responses",
2285+
provider: "openai",
2286+
id: "gpt-5",
2287+
baseUrl: "https://api-one.example/v1",
2288+
headers: { "X-Test": "one" },
2289+
} as unknown as Model<"openai-responses">,
2290+
});
2291+
const completionsParams = resolvePreparedExtraParams({
2292+
cfg,
2293+
provider: "openai",
2294+
modelId: "gpt-5",
2295+
model: {
2296+
api: "openai-completions",
2297+
provider: "openai",
2298+
id: "gpt-5",
2299+
baseUrl: "https://api-one.example/v1",
2300+
headers: { "X-Test": "one" },
2301+
} as unknown as Model<"openai-completions">,
2302+
});
2303+
const differentModelHeadersParams = resolvePreparedExtraParams({
2304+
cfg,
2305+
provider: "openai",
2306+
modelId: "gpt-5",
2307+
model: {
2308+
api: "openai-responses",
2309+
provider: "openai",
2310+
id: "gpt-5",
2311+
baseUrl: "https://api-two.example/v1",
2312+
headers: { "X-Test": "two" },
2313+
} as unknown as Model<"openai-responses">,
2314+
});
2315+
const repeatedResponsesParams = resolvePreparedExtraParams({
2316+
cfg,
2317+
provider: "openai",
2318+
modelId: "gpt-5",
2319+
model: {
2320+
api: "openai-responses",
2321+
provider: "openai",
2322+
id: "gpt-5",
2323+
baseUrl: "https://api-one.example/v1",
2324+
headers: { "X-Test": "one" },
2325+
} as unknown as Model<"openai-responses">,
2326+
});
2327+
2328+
expect(responsesParams.transportFamily).toBe("openai-responses");
2329+
expect(completionsParams.transportFamily).toBe("openai-completions");
2330+
expect(differentModelHeadersParams.baseUrl).toBe("https://api-two.example/v1");
2331+
expect(differentModelHeadersParams.headerAuth).toBe("two");
2332+
expect(repeatedResponsesParams.transportFamily).toBe("openai-responses");
2333+
expect(resolveProviderExtraParamsForTransport).toHaveBeenCalledTimes(3);
2334+
});
2335+
22602336
it("passes explicit settings transport to transport extra-param hooks", () => {
22612337
const resolveProviderExtraParamsForTransport = vi.fn((_params) => ({
22622338
patch: {

src/agents/pi-embedded-runner/extra-params.ts

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ const providerRuntimeDeps = {
3838
...defaultProviderRuntimeDeps,
3939
};
4040

41+
let preparedExtraParamsCache = new WeakMap<OpenClawConfig, Map<string, Record<string, unknown>>>();
42+
4143
export const __testing = {
4244
setProviderRuntimeDepsForTest(
4345
deps: Partial<typeof defaultProviderRuntimeDeps> | undefined,
@@ -51,6 +53,7 @@ export const __testing = {
5153
deps?.wrapProviderStreamFn ?? defaultProviderRuntimeDeps.wrapProviderStreamFn;
5254
},
5355
resetProviderRuntimeDepsForTest(): void {
56+
clearPreparedExtraParamsCache();
5457
providerRuntimeDeps.prepareProviderExtraParams =
5558
defaultProviderRuntimeDeps.prepareProviderExtraParams;
5659
providerRuntimeDeps.resolveProviderExtraParamsForTransport =
@@ -134,6 +137,60 @@ function hasExplicitTransportSetting(settings: { transport?: unknown }): boolean
134137
return Object.hasOwn(settings, "transport");
135138
}
136139

140+
function clearPreparedExtraParamsCache(): void {
141+
preparedExtraParamsCache = new WeakMap();
142+
}
143+
144+
function fingerprintPreparedExtraParamsModel(model?: ProviderRuntimeModel): unknown {
145+
if (!model) {
146+
return null;
147+
}
148+
const record = model as unknown as Record<string, unknown>;
149+
return {
150+
api: model.api,
151+
provider: model.provider,
152+
id: model.id,
153+
name: model.name,
154+
baseUrl: model.baseUrl,
155+
reasoning: model.reasoning,
156+
input: model.input,
157+
cost: model.cost,
158+
compat: record.compat ?? null,
159+
contextWindow: model.contextWindow,
160+
contextTokens: model.contextTokens ?? null,
161+
headers: record.headers ?? null,
162+
maxTokens: model.maxTokens,
163+
params: model.params ?? null,
164+
requestTimeoutMs: model.requestTimeoutMs ?? null,
165+
};
166+
}
167+
168+
function resolvePreparedExtraParamsCacheKey(params: {
169+
provider: string;
170+
modelId: string;
171+
agentDir?: string;
172+
workspaceDir?: string;
173+
extraParamsOverride?: Record<string, unknown>;
174+
thinkingLevel?: ThinkLevel;
175+
agentId?: string;
176+
resolvedExtraParams?: Record<string, unknown>;
177+
model?: ProviderRuntimeModel;
178+
resolvedTransport?: SupportedTransport;
179+
}): string {
180+
return JSON.stringify({
181+
provider: params.provider,
182+
modelId: params.modelId,
183+
agentId: params.agentId ?? "",
184+
agentDir: params.agentDir ?? "",
185+
workspaceDir: params.workspaceDir ?? "",
186+
thinkingLevel: params.thinkingLevel ?? "",
187+
resolvedTransport: params.resolvedTransport ?? "",
188+
extraParamsOverride: params.extraParamsOverride ?? null,
189+
resolvedExtraParams: params.resolvedExtraParams ?? null,
190+
model: fingerprintPreparedExtraParamsModel(params.model),
191+
});
192+
}
193+
137194
export function resolvePreparedExtraParams(params: {
138195
cfg: OpenClawConfig | undefined;
139196
provider: string;
@@ -176,6 +233,14 @@ export function resolvePreparedExtraParams(params: {
176233
merged.cachedContent = resolvedCachedContent;
177234
delete merged.cached_content;
178235
}
236+
const cfg = params.cfg;
237+
const cacheKey = cfg ? resolvePreparedExtraParamsCacheKey(params) : undefined;
238+
if (cacheKey) {
239+
const cached = preparedExtraParamsCache.get(cfg!)?.get(cacheKey);
240+
if (cached) {
241+
return cached;
242+
}
243+
}
179244
const prepared =
180245
providerRuntimeDeps.prepareProviderExtraParams({
181246
provider: params.provider,
@@ -207,7 +272,16 @@ export function resolvePreparedExtraParams(params: {
207272
transport: params.resolvedTransport ?? resolveSupportedTransport(prepared.transport),
208273
},
209274
})?.patch;
210-
return transportPatch ? { ...prepared, ...transportPatch } : prepared;
275+
const result = transportPatch ? { ...prepared, ...transportPatch } : prepared;
276+
if (cacheKey) {
277+
let bucket = preparedExtraParamsCache.get(cfg!);
278+
if (!bucket) {
279+
bucket = new Map();
280+
preparedExtraParamsCache.set(cfg!, bucket);
281+
}
282+
bucket.set(cacheKey, result);
283+
}
284+
return result;
211285
}
212286

213287
function sanitizeExtraParamsRecord(

0 commit comments

Comments
 (0)