Skip to content

Commit d3c3562

Browse files
committed
perf: reuse gateway provider hooks for turn workspaces
1 parent 1560b34 commit d3c3562

3 files changed

Lines changed: 138 additions & 2 deletions

File tree

src/agents/embedded-agent-runner/run/attempt.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -934,6 +934,7 @@ export async function runEmbeddedAttempt(
934934
config: params.config,
935935
workspaceDir: effectiveWorkspace,
936936
env: process.env,
937+
allowGatewayRegistryWorkspaceReuse: true,
937938
...(pluginMetadataSnapshotCoversProvider(pluginMetadataSnapshot, params.provider)
938939
? { pluginMetadataSnapshot }
939940
: {}),

src/plugins/provider-hook-runtime.ts

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ export type ProviderRuntimePluginLookupParams = {
4444
config?: OpenClawConfig;
4545
workspaceDir?: string;
4646
env?: NodeJS.ProcessEnv;
47+
allowGatewayRegistryWorkspaceReuse?: boolean;
4748
applyAutoEnable?: boolean;
4849
bundledProviderVitestCompat?: boolean;
4950
pluginMetadataSnapshot?: PluginMetadataRegistryView;
@@ -82,6 +83,7 @@ function resolveProviderRuntimePluginCacheKey(
8283
return JSON.stringify({
8384
provider: normalizeLowercaseStringOrEmpty(params.provider),
8485
modelId: resolveProviderRuntimeLookupModelId(params) ?? null,
86+
allowGatewayRegistryWorkspaceReuse: params.allowGatewayRegistryWorkspaceReuse ?? false,
8587
pluginControlPlane: resolvePluginControlPlaneFingerprint({
8688
config: params.config,
8789
env: params.env,
@@ -141,9 +143,19 @@ function findProviderRuntimePluginInLoadedRegistries(params: {
141143
lookup: ProviderRuntimePluginLookupParams;
142144
apiOwnerHint?: string;
143145
}): ProviderPlugin | undefined {
146+
const registryState = getPluginRegistryState();
147+
const activeWorkspaceDir =
148+
params.lookup.allowGatewayRegistryWorkspaceReuse === true &&
149+
registryState?.runtimeSubagentMode === "gateway-bindable"
150+
? getActivePluginRegistryWorkspaceDirFromState()
151+
: undefined;
152+
const lookupWorkspaceDir =
153+
activeWorkspaceDir && activeWorkspaceDir !== params.lookup.workspaceDir
154+
? activeWorkspaceDir
155+
: params.lookup.workspaceDir;
144156
const activeRegistry = getLoadedRuntimePluginRegistry({
145157
env: params.lookup.env,
146-
workspaceDir: params.lookup.workspaceDir,
158+
workspaceDir: lookupWorkspaceDir,
147159
});
148160
const activePlugin = activeRegistry
149161
? findProviderRuntimePluginInRegistry({
@@ -158,7 +170,7 @@ function findProviderRuntimePluginInLoadedRegistries(params: {
158170
for (const surface of PREPARED_PROVIDER_RUNTIME_SURFACES) {
159171
const registry = getLoadedRuntimePluginRegistry({
160172
env: params.lookup.env,
161-
workspaceDir: params.lookup.workspaceDir,
173+
workspaceDir: lookupWorkspaceDir,
162174
surface,
163175
});
164176
const plugin = registry
@@ -371,6 +383,14 @@ export function ensureProviderRuntimePluginHandle(
371383
config: params.config ?? params.runtimeHandle?.config,
372384
workspaceDir: params.workspaceDir ?? params.runtimeHandle?.workspaceDir,
373385
env: params.env ?? params.runtimeHandle?.env,
386+
...((params.allowGatewayRegistryWorkspaceReuse ??
387+
params.runtimeHandle?.allowGatewayRegistryWorkspaceReuse)
388+
? {
389+
allowGatewayRegistryWorkspaceReuse:
390+
params.allowGatewayRegistryWorkspaceReuse ??
391+
params.runtimeHandle?.allowGatewayRegistryWorkspaceReuse,
392+
}
393+
: {}),
374394
applyAutoEnable: params.runtimeHandle?.applyAutoEnable,
375395
bundledProviderVitestCompat: params.runtimeHandle?.bundledProviderVitestCompat,
376396
pluginMetadataSnapshot:
@@ -385,6 +405,7 @@ export function prepareProviderExtraParams(params: {
385405
config?: OpenClawConfig;
386406
workspaceDir?: string;
387407
env?: NodeJS.ProcessEnv;
408+
allowGatewayRegistryWorkspaceReuse?: boolean;
388409
runtimeHandle?: ProviderRuntimePluginHandle;
389410
context: ProviderPrepareExtraParamsContext;
390411
}) {
@@ -399,6 +420,7 @@ export function resolveProviderExtraParamsForTransport(params: {
399420
config?: OpenClawConfig;
400421
workspaceDir?: string;
401422
env?: NodeJS.ProcessEnv;
423+
allowGatewayRegistryWorkspaceReuse?: boolean;
402424
runtimeHandle?: ProviderRuntimePluginHandle;
403425
context: ProviderExtraParamsForTransportContext;
404426
}) {
@@ -413,6 +435,7 @@ export function resolveProviderAuthProfileId(params: {
413435
config?: OpenClawConfig;
414436
workspaceDir?: string;
415437
env?: NodeJS.ProcessEnv;
438+
allowGatewayRegistryWorkspaceReuse?: boolean;
416439
runtimeHandle?: ProviderRuntimePluginHandle;
417440
context: ProviderResolveAuthProfileIdContext;
418441
}): string | undefined {
@@ -427,6 +450,7 @@ export function resolveProviderFollowupFallbackRoute(params: {
427450
config?: OpenClawConfig;
428451
workspaceDir?: string;
429452
env?: NodeJS.ProcessEnv;
453+
allowGatewayRegistryWorkspaceReuse?: boolean;
430454
runtimeHandle?: ProviderRuntimePluginHandle;
431455
context: ProviderFollowupFallbackRouteContext;
432456
}): ProviderFollowupFallbackRouteResult | undefined {
@@ -441,6 +465,7 @@ export function wrapProviderStreamFn(params: {
441465
config?: OpenClawConfig;
442466
workspaceDir?: string;
443467
env?: NodeJS.ProcessEnv;
468+
allowGatewayRegistryWorkspaceReuse?: boolean;
444469
runtimeHandle?: ProviderRuntimePluginHandle;
445470
context: ProviderWrapStreamFnContext;
446471
}) {
@@ -454,6 +479,7 @@ export function wrapProviderSimpleCompletionStreamFn(params: {
454479
config?: OpenClawConfig;
455480
workspaceDir?: string;
456481
env?: NodeJS.ProcessEnv;
482+
allowGatewayRegistryWorkspaceReuse?: boolean;
457483
runtimeHandle?: ProviderRuntimePluginHandle;
458484
context: ProviderWrapStreamFnContext;
459485
}) {

src/plugins/provider-runtime.test.ts

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -538,6 +538,115 @@ describe("provider-runtime", () => {
538538
expect(resolvePluginProvidersMock).not.toHaveBeenCalled();
539539
});
540540

541+
it("uses the active startup registry for provider hook lookup when the turn workspace differs", () => {
542+
const provider: ProviderPlugin = {
543+
id: DEMO_PROVIDER_ID,
544+
label: "Demo",
545+
auth: [],
546+
prepareExtraParams: ({ extraParams }) => ({
547+
...extraParams,
548+
fromActiveRegistry: true,
549+
}),
550+
};
551+
const registry = createEmptyPluginRegistry();
552+
registry.providers.push({
553+
pluginId: DEMO_PROVIDER_ID,
554+
provider,
555+
source: "test",
556+
});
557+
setActivePluginRegistry(registry, "startup-registry", "gateway-bindable", "/tmp/gateway");
558+
559+
expect(
560+
prepareProviderExtraParams({
561+
provider: DEMO_PROVIDER_ID,
562+
workspaceDir: "/tmp/turn-workspace",
563+
allowGatewayRegistryWorkspaceReuse: true,
564+
context: createDemoRuntimeContext({
565+
extraParams: {},
566+
}),
567+
}),
568+
).toEqual({
569+
fromActiveRegistry: true,
570+
});
571+
expect(resolvePluginProvidersMock).not.toHaveBeenCalled();
572+
});
573+
574+
it("does not use a gateway-bindable active registry for workspace mismatches without opt-in", () => {
575+
const activeProvider: ProviderPlugin = {
576+
id: DEMO_PROVIDER_ID,
577+
label: "Active workspace provider",
578+
auth: [],
579+
};
580+
const requestedProvider: ProviderPlugin = {
581+
id: DEMO_PROVIDER_ID,
582+
label: "Requested workspace provider",
583+
auth: [],
584+
prepareExtraParams: ({ extraParams }) => ({
585+
...extraParams,
586+
fromRequestedWorkspace: true,
587+
}),
588+
};
589+
const registry = createEmptyPluginRegistry();
590+
registry.providers.push({
591+
pluginId: DEMO_PROVIDER_ID,
592+
provider: activeProvider,
593+
source: "test",
594+
});
595+
setActivePluginRegistry(registry, "startup-registry", "gateway-bindable", "/tmp/gateway");
596+
resolvePluginProvidersMock.mockReturnValueOnce([requestedProvider]);
597+
598+
expect(
599+
prepareProviderExtraParams({
600+
provider: DEMO_PROVIDER_ID,
601+
workspaceDir: "/tmp/explicit-workspace",
602+
context: createDemoRuntimeContext({
603+
extraParams: {},
604+
}),
605+
}),
606+
).toEqual({
607+
fromRequestedWorkspace: true,
608+
});
609+
expect(resolvePluginProvidersMock).toHaveBeenCalledOnce();
610+
});
611+
612+
it("does not use a default-mode active registry when an explicit lookup workspace differs", () => {
613+
const activeProvider: ProviderPlugin = {
614+
id: DEMO_PROVIDER_ID,
615+
label: "Active workspace provider",
616+
auth: [],
617+
};
618+
const requestedProvider: ProviderPlugin = {
619+
id: DEMO_PROVIDER_ID,
620+
label: "Requested workspace provider",
621+
auth: [],
622+
prepareExtraParams: ({ extraParams }) => ({
623+
...extraParams,
624+
fromRequestedWorkspace: true,
625+
}),
626+
};
627+
const registry = createEmptyPluginRegistry();
628+
registry.providers.push({
629+
pluginId: DEMO_PROVIDER_ID,
630+
provider: activeProvider,
631+
source: "test",
632+
});
633+
setActivePluginRegistry(registry, "workspace-a", "default", "/tmp/workspace-a");
634+
resolvePluginProvidersMock.mockReturnValueOnce([requestedProvider]);
635+
636+
expect(
637+
prepareProviderExtraParams({
638+
provider: DEMO_PROVIDER_ID,
639+
workspaceDir: "/tmp/workspace-b",
640+
context: createDemoRuntimeContext({
641+
extraParams: {},
642+
}),
643+
}),
644+
).toEqual({
645+
fromRequestedWorkspace: true,
646+
});
647+
expect(resolvePluginProvidersMock).toHaveBeenCalledOnce();
648+
});
649+
541650
it("matches active provider hooks through a custom provider's native api owner", () => {
542651
const provider: ProviderPlugin = {
543652
id: "ollama",

0 commit comments

Comments
 (0)