@dbx-tools/appkit-web-search 0.9.132 → 0.9.138
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.ts +2 -1
- package/lib/index.d.ts +2 -1
- package/lib/index.js +2 -2
- package/lib/src/fetch.d.ts +31 -0
- package/lib/src/fetch.js +93 -19
- package/lib/src/plugin.d.ts +5 -0
- package/lib/src/plugin.js +6 -2
- package/lib/src/runtime.d.ts +5 -1
- package/lib/src/runtime.js +17 -5
- package/lib/src/search.js +79 -32
- package/package.json +7 -7
- package/src/fetch.ts +151 -22
- package/src/plugin.ts +18 -1
- package/src/runtime.ts +20 -3
- package/src/search.ts +97 -33
package/src/plugin.ts
CHANGED
|
@@ -31,7 +31,14 @@ import {
|
|
|
31
31
|
WEB_SEARCH_CONFIG_SCHEMA,
|
|
32
32
|
type WebSearchPluginConfig,
|
|
33
33
|
} from "./config.ts";
|
|
34
|
-
import {
|
|
34
|
+
import {
|
|
35
|
+
runWebAssetPageFetch,
|
|
36
|
+
runWebFetch,
|
|
37
|
+
runWebImageFetch,
|
|
38
|
+
type WebAssetPageFetchRequest,
|
|
39
|
+
type WebImageFetchRequest,
|
|
40
|
+
type WebImageFetchResult,
|
|
41
|
+
} from "./fetch.ts";
|
|
35
42
|
import { createWebSearchRuntime, type WebSearchRuntime } from "./runtime.ts";
|
|
36
43
|
import {
|
|
37
44
|
webFetchRequestSchema,
|
|
@@ -230,6 +237,16 @@ export class WebSearchPlugin extends ToolRegistryPlugin<WebSearchPluginConfig> {
|
|
|
230
237
|
*/
|
|
231
238
|
fetch: (request: WebFetchRequest, signal?: AbortSignal): Promise<WebFetchResult> =>
|
|
232
239
|
this.fetch(request, signal),
|
|
240
|
+
/** Fetch expanded HTML for in-process asset discovery. */
|
|
241
|
+
fetchAssetPage: (
|
|
242
|
+
request: WebAssetPageFetchRequest,
|
|
243
|
+
signal?: AbortSignal,
|
|
244
|
+
): Promise<WebFetchResult> => runWebAssetPageFetch(request, this.runtime, signal),
|
|
245
|
+
/** Fetch one image for in-process visual research. */
|
|
246
|
+
fetchImage: (
|
|
247
|
+
request: WebImageFetchRequest,
|
|
248
|
+
signal?: AbortSignal,
|
|
249
|
+
): Promise<WebImageFetchResult> => runWebImageFetch(request, this.runtime, signal),
|
|
233
250
|
};
|
|
234
251
|
}
|
|
235
252
|
|
package/src/runtime.ts
CHANGED
|
@@ -37,6 +37,10 @@ export interface WebSearchRuntime {
|
|
|
37
37
|
execute: WebSearchExecutor;
|
|
38
38
|
/** Provider-family cooldown deadlines learned from workspace failures. */
|
|
39
39
|
familyCooldowns: Map<policy.ModelFamily, number>;
|
|
40
|
+
/** Provider families that completed a native search successfully. */
|
|
41
|
+
availableFamilies: Set<policy.ModelFamily>;
|
|
42
|
+
/** First native request currently establishing one family's availability. */
|
|
43
|
+
familyProbes: Map<policy.ModelFamily, Promise<void>>;
|
|
40
44
|
}
|
|
41
45
|
|
|
42
46
|
/** Runtime-aware operation input, retaining resolved-config compatibility. */
|
|
@@ -54,7 +58,13 @@ export function createWebSearchRuntime(
|
|
|
54
58
|
overrides?: WebSearchPluginConfig,
|
|
55
59
|
execute: WebSearchExecutor = directExecute,
|
|
56
60
|
): WebSearchRuntime {
|
|
57
|
-
return {
|
|
61
|
+
return {
|
|
62
|
+
config: resolveWebSearchConfig(overrides),
|
|
63
|
+
execute,
|
|
64
|
+
familyCooldowns: new Map(),
|
|
65
|
+
availableFamilies: new Set(),
|
|
66
|
+
familyProbes: new Map(),
|
|
67
|
+
};
|
|
58
68
|
}
|
|
59
69
|
|
|
60
70
|
/** Build an isolated runtime from config that has already been resolved. */
|
|
@@ -62,7 +72,13 @@ export function createResolvedWebSearchRuntime(
|
|
|
62
72
|
config: ResolvedWebSearchConfig,
|
|
63
73
|
execute: WebSearchExecutor = directExecute,
|
|
64
74
|
): WebSearchRuntime {
|
|
65
|
-
return {
|
|
75
|
+
return {
|
|
76
|
+
config,
|
|
77
|
+
execute,
|
|
78
|
+
familyCooldowns: new Map(),
|
|
79
|
+
availableFamilies: new Set(),
|
|
80
|
+
familyProbes: new Map(),
|
|
81
|
+
};
|
|
66
82
|
}
|
|
67
83
|
|
|
68
84
|
/** Normalize an explicit runtime or legacy resolved config for one operation. */
|
|
@@ -91,10 +107,11 @@ export async function executeRead<T>(
|
|
|
91
107
|
settings: WebSearchExecutionSettings,
|
|
92
108
|
fn: (signal?: AbortSignal) => Promise<T>,
|
|
93
109
|
signal?: AbortSignal,
|
|
110
|
+
logFailure = true,
|
|
94
111
|
): Promise<T> {
|
|
95
112
|
return pluginExecution.runPluginExecution({
|
|
96
113
|
plugin: "web-search",
|
|
97
|
-
logger,
|
|
114
|
+
logger: logFailure ? logger : { warn() {} },
|
|
98
115
|
operation,
|
|
99
116
|
settings,
|
|
100
117
|
execute: runtime.execute,
|
package/src/search.ts
CHANGED
|
@@ -231,25 +231,22 @@ async function postServing(
|
|
|
231
231
|
const responseBody = await response.text();
|
|
232
232
|
const model =
|
|
233
233
|
object.isRecord(body) && typeof body.model === "string" ? body.model : undefined;
|
|
234
|
-
|
|
235
|
-
model,
|
|
236
|
-
api: url.endsWith("/responses") ? "responses" : "chat",
|
|
237
|
-
url,
|
|
238
|
-
status: response.status,
|
|
239
|
-
responseBody,
|
|
240
|
-
});
|
|
241
|
-
if (
|
|
234
|
+
const crossRegionDisabled =
|
|
242
235
|
response.status === 400 &&
|
|
243
236
|
model &&
|
|
244
237
|
policy.modelFamily(model) === policy.ModelFamily.Gemini &&
|
|
245
|
-
GEMINI_CROSS_REGION_DISABLED.test(responseBody)
|
|
246
|
-
) {
|
|
238
|
+
GEMINI_CROSS_REGION_DISABLED.test(responseBody);
|
|
239
|
+
if (crossRegionDisabled) {
|
|
247
240
|
const expiresAt = Date.now() + GEMINI_CROSS_REGION_COOLDOWN_MS;
|
|
248
241
|
runtime.familyCooldowns.set(policy.ModelFamily.Gemini, expiresAt);
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
242
|
+
runtime.availableFamilies.delete(policy.ModelFamily.Gemini);
|
|
243
|
+
} else {
|
|
244
|
+
logger.error("serving-request-rejected", {
|
|
245
|
+
model,
|
|
246
|
+
api: url.endsWith("/responses") ? "responses" : "chat",
|
|
247
|
+
url,
|
|
248
|
+
status: response.status,
|
|
249
|
+
responseBody,
|
|
253
250
|
});
|
|
254
251
|
}
|
|
255
252
|
// Load-shedding and server faults are worth another attempt; a 4xx is
|
|
@@ -268,6 +265,7 @@ async function postServing(
|
|
|
268
265
|
: response.json();
|
|
269
266
|
},
|
|
270
267
|
signal,
|
|
268
|
+
false,
|
|
271
269
|
);
|
|
272
270
|
return object.isRecord(payload) ? payload : {};
|
|
273
271
|
}
|
|
@@ -456,29 +454,95 @@ export async function runWebSearch(
|
|
|
456
454
|
);
|
|
457
455
|
}
|
|
458
456
|
|
|
457
|
+
return runSelectedWebSearch(request, runtime, ctx, endpoint, signal, writeProgress);
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
/** Run one selected family after its first request establishes availability. */
|
|
461
|
+
async function runSelectedWebSearch(
|
|
462
|
+
request: WebSearchRequest,
|
|
463
|
+
runtime: WebSearchRuntime,
|
|
464
|
+
ctx: WebSearchContext,
|
|
465
|
+
endpoint: ServingEndpointSummary,
|
|
466
|
+
signal?: AbortSignal,
|
|
467
|
+
writeProgress?: ProgressWriter,
|
|
468
|
+
): Promise<WebSearchResult> {
|
|
469
|
+
const family = endpointFamily(endpoint);
|
|
470
|
+
if (!family || runtime.availableFamilies.has(family)) {
|
|
471
|
+
try {
|
|
472
|
+
return await runNativeWebSearch(request, runtime, ctx, endpoint, signal, writeProgress);
|
|
473
|
+
} catch (error) {
|
|
474
|
+
return runSearchFallback(request, runtime, ctx, endpoint, error, signal, writeProgress);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
const activeProbe = runtime.familyProbes.get(family);
|
|
479
|
+
if (activeProbe) {
|
|
480
|
+
signal?.throwIfAborted();
|
|
481
|
+
await activeProbe;
|
|
482
|
+
signal?.throwIfAborted();
|
|
483
|
+
return runWebSearch(request, runtime, ctx, signal, writeProgress);
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
let releaseProbe!: () => void;
|
|
487
|
+
const probe = new Promise<void>((resolve) => {
|
|
488
|
+
releaseProbe = resolve;
|
|
489
|
+
});
|
|
490
|
+
runtime.familyProbes.set(family, probe);
|
|
491
|
+
|
|
492
|
+
if (runtime.availableFamilies.has(family) || activeFamilyCooldowns(runtime).has(family)) {
|
|
493
|
+
runtime.familyProbes.delete(family);
|
|
494
|
+
releaseProbe();
|
|
495
|
+
return runWebSearch(request, runtime, ctx, signal, writeProgress);
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
let failure: unknown;
|
|
459
499
|
try {
|
|
460
|
-
|
|
500
|
+
const result = await runNativeWebSearch(request, runtime, ctx, endpoint, signal, writeProgress);
|
|
501
|
+
runtime.availableFamilies.add(family);
|
|
502
|
+
return result;
|
|
461
503
|
} catch (error) {
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
)
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
504
|
+
failure = error;
|
|
505
|
+
} finally {
|
|
506
|
+
if (runtime.familyProbes.get(family) === probe) runtime.familyProbes.delete(family);
|
|
507
|
+
releaseProbe();
|
|
508
|
+
}
|
|
509
|
+
return runSearchFallback(request, runtime, ctx, endpoint, failure, signal, writeProgress);
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
/** Resolve a cached family failure to another model or the scrape fallback. */
|
|
513
|
+
async function runSearchFallback(
|
|
514
|
+
request: WebSearchRequest,
|
|
515
|
+
runtime: WebSearchRuntime,
|
|
516
|
+
ctx: WebSearchContext,
|
|
517
|
+
endpoint: ServingEndpointSummary,
|
|
518
|
+
error: unknown,
|
|
519
|
+
signal?: AbortSignal,
|
|
520
|
+
writeProgress?: ProgressWriter,
|
|
521
|
+
): Promise<WebSearchResult> {
|
|
522
|
+
if (
|
|
523
|
+
endpointFamily(endpoint) !== policy.ModelFamily.Gemini ||
|
|
524
|
+
!activeFamilyCooldowns(runtime).has(policy.ModelFamily.Gemini)
|
|
525
|
+
) {
|
|
526
|
+
throw error;
|
|
527
|
+
}
|
|
528
|
+
const fallback = await resolveWebSearchModel(ctx, runtime, request.model);
|
|
529
|
+
if (!fallback) {
|
|
530
|
+
if (runtime.config.scrapeFallback) {
|
|
531
|
+
logger.warn("web-search-model-fallback", {
|
|
532
|
+
failedModel: endpoint.name,
|
|
533
|
+
fallback: "scrape",
|
|
534
|
+
reason: "cross-region-disabled",
|
|
535
|
+
});
|
|
536
|
+
return runScrapeSearch(request, runtime, signal);
|
|
475
537
|
}
|
|
476
|
-
|
|
477
|
-
failedModel: endpoint.name,
|
|
478
|
-
fallbackModel: fallback.name,
|
|
479
|
-
});
|
|
480
|
-
return runNativeWebSearch(request, runtime, ctx, fallback, signal, writeProgress);
|
|
538
|
+
throw error;
|
|
481
539
|
}
|
|
540
|
+
logger.warn("web-search-model-fallback", {
|
|
541
|
+
failedModel: endpoint.name,
|
|
542
|
+
fallbackModel: fallback.name,
|
|
543
|
+
reason: "cross-region-disabled",
|
|
544
|
+
});
|
|
545
|
+
return runSelectedWebSearch(request, runtime, ctx, fallback, signal, writeProgress);
|
|
482
546
|
}
|
|
483
547
|
|
|
484
548
|
/** Execute one native provider request against an already-selected endpoint. */
|