






















@@ -1,17 +1,29 @@
11import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
22import { createEmptyPluginRegistry } from "../../plugins/registry-empty.js";
33import { setActivePluginRegistry } from "../../plugins/runtime.js";
4-import { clearActiveRuntimeWebToolsMetadata } from "../../secrets/runtime-web-tools-state.js";
4+import {
5+clearActiveRuntimeWebToolsMetadata,
6+setActiveRuntimeWebToolsMetadata,
7+} from "../../secrets/runtime-web-tools-state.js";
58import { createWebFetchTool, createWebSearchTool } from "./web-tools.js";
6910+const runWebSearchCalls = vi.hoisted(
11+() => [] as Array<{ config?: unknown; runtimeWebSearch?: unknown }>,
12+);
13+714vi.mock("../../web-search/runtime.js", async () => {
815const { getActivePluginRegistry } = await import("../../plugins/runtime.js");
16+const { getActiveRuntimeWebToolsMetadata } =
17+await import("../../secrets/runtime-web-tools-state.js");
918const resolveRuntimeDefinition = (options?: {
1019config?: unknown;
1120runtimeWebSearch?: { selectedProvider?: string; providerConfigured?: string };
1221}) => {
1322const providerId =
14-options?.runtimeWebSearch?.selectedProvider ?? options?.runtimeWebSearch?.providerConfigured;
23+options?.runtimeWebSearch?.selectedProvider ??
24+options?.runtimeWebSearch?.providerConfigured ??
25+getActiveRuntimeWebToolsMetadata()?.search?.selectedProvider ??
26+getActiveRuntimeWebToolsMetadata()?.search?.providerConfigured;
1527const registration = getActivePluginRegistry()?.webSearchProviders.find(
1628(entry) => entry.provider.id === providerId,
1729);
@@ -33,9 +45,14 @@ vi.mock("../../web-search/runtime.js", async () => {
3345resolveWebSearchDefinition: resolveRuntimeDefinition,
3446resolveWebSearchProviderId: () => "",
3547runWebSearch: async (options: {
48+config?: unknown;
3649args: Record<string, unknown>;
3750runtimeWebSearch?: unknown;
3851}) => {
52+runWebSearchCalls.push({
53+config: options.config,
54+runtimeWebSearch: options.runtimeWebSearch,
55+});
3956const resolved = resolveRuntimeDefinition(options as never);
4057if (!resolved) {
4158throw new Error("web_search is disabled or no provider is available.");
@@ -51,6 +68,7 @@ vi.mock("../../web-search/runtime.js", async () => {
5168beforeEach(() => {
5269setActivePluginRegistry(createEmptyPluginRegistry());
5370clearActiveRuntimeWebToolsMetadata();
71+runWebSearchCalls.length = 0;
5472});
55735674afterEach(() => {
@@ -114,4 +132,91 @@ describe("web tools defaults", () => {
114132expect(tool?.description).toContain("Search the web");
115133expect(result?.details).toMatchObject({ ok: true });
116134});
135+136+it("late-binds managed web_search execution to the current runtime snapshot", async () => {
137+const registry = createEmptyPluginRegistry();
138+registry.webSearchProviders.push(
139+{
140+pluginId: "stale-search",
141+pluginName: "Stale Search",
142+source: "test",
143+provider: {
144+id: "stale",
145+label: "Stale Search",
146+hint: "Stale runtime provider",
147+envVars: [],
148+placeholder: "stale-...",
149+signupUrl: "https://example.com/stale",
150+autoDetectOrder: 1,
151+credentialPath: "tools.web.search.stale.apiKey",
152+getCredentialValue: () => "configured",
153+setCredentialValue: () => {},
154+createTool: () => ({
155+description: "stale runtime tool",
156+parameters: {},
157+execute: async () => ({ provider: "stale" }),
158+}),
159+},
160+},
161+{
162+pluginId: "fresh-search",
163+pluginName: "Fresh Search",
164+source: "test",
165+provider: {
166+id: "fresh",
167+label: "Fresh Search",
168+hint: "Fresh runtime provider",
169+envVars: [],
170+placeholder: "fresh-...",
171+signupUrl: "https://example.com/fresh",
172+autoDetectOrder: 2,
173+credentialPath: "tools.web.search.fresh.apiKey",
174+getCredentialValue: () => "configured",
175+setCredentialValue: () => {},
176+createTool: () => ({
177+description: "fresh runtime tool",
178+parameters: {},
179+execute: async () => ({ provider: "fresh" }),
180+}),
181+},
182+},
183+);
184+setActivePluginRegistry(registry);
185+setActiveRuntimeWebToolsMetadata({
186+search: {
187+providerConfigured: "fresh",
188+providerSource: "configured",
189+selectedProvider: "fresh",
190+selectedProviderKeySource: "config",
191+diagnostics: [],
192+},
193+fetch: {
194+providerSource: "none",
195+diagnostics: [],
196+},
197+diagnostics: [],
198+});
199+200+const tool = createWebSearchTool({
201+config: { tools: { web: { search: { provider: "stale" } } } },
202+sandboxed: true,
203+runtimeWebSearch: {
204+providerConfigured: "stale",
205+providerSource: "configured",
206+selectedProvider: "stale",
207+selectedProviderKeySource: "config",
208+diagnostics: [],
209+},
210+lateBindRuntimeConfig: true,
211+});
212+213+const result = await tool?.execute?.("call-runtime-provider", {});
214+215+expect(result?.details).toMatchObject({ provider: "fresh" });
216+expect(runWebSearchCalls).toHaveLength(1);
217+expect(runWebSearchCalls[0]?.config).toBeUndefined();
218+expect(runWebSearchCalls[0]?.runtimeWebSearch).toMatchObject({
219+selectedProvider: "fresh",
220+});
221+});
117222});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。