@dbx-tools/appkit-web-search 0.9.4 → 0.9.6
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/README.md +7 -9
- package/index.ts +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.js +2 -2
- package/lib/src/allowlist.js +3 -3
- package/lib/src/config.js +17 -17
- package/lib/src/plugin.d.ts +11 -18
- package/lib/src/plugin.js +16 -27
- package/lib/src/runtime.d.ts +1 -19
- package/lib/src/runtime.js +8 -37
- package/lib/src/schema.js +6 -6
- package/lib/src/search.d.ts +4 -2
- package/lib/src/search.js +44 -13
- package/package.json +7 -6
- package/src/allowlist.ts +2 -2
- package/src/config.ts +23 -23
- package/src/plugin.ts +14 -35
- package/src/runtime.ts +7 -49
- package/src/schema.ts +5 -5
- package/src/search.ts +40 -13
package/src/search.ts
CHANGED
|
@@ -28,9 +28,9 @@ import {
|
|
|
28
28
|
getExecutionContext,
|
|
29
29
|
ValidationError,
|
|
30
30
|
} from "@databricks/appkit";
|
|
31
|
-
import { invoke, resolve,
|
|
32
|
-
import { log, object,
|
|
33
|
-
import { openaiChat
|
|
31
|
+
import { invoke, resolve, modelCatalog } from "@dbx-tools/model";
|
|
32
|
+
import { log, object, stringUtils } from "@dbx-tools/shared-core";
|
|
33
|
+
import { openaiChat } from "@dbx-tools/shared-model";
|
|
34
34
|
import { MODEL_ENV, SERVING_ENDPOINT_ENV, type ResolvedWebSearchConfig } from "./config.ts";
|
|
35
35
|
import { toCallSettings, webSearchExecuteDefaults } from "./defaults.ts";
|
|
36
36
|
import { detectWebSearchProvider, supportsWebSearch, webSearchToolSpec } from "./provider.ts";
|
|
@@ -43,10 +43,10 @@ import {
|
|
|
43
43
|
import type { WebSearchCitation, WebSearchRequest, WebSearchResult } from "./schema.ts";
|
|
44
44
|
import { runScrapeSearch } from "./scrape.ts";
|
|
45
45
|
|
|
46
|
-
type WorkspaceClientLike =
|
|
46
|
+
type WorkspaceClientLike = modelCatalog.WorkspaceClientLike & invoke.AuthenticatingClientLike;
|
|
47
47
|
const logger = log.logger("web-search/search");
|
|
48
48
|
const { resolveModel } = resolve;
|
|
49
|
-
const { listServingEndpoints } =
|
|
49
|
+
const { listServingEndpoints } = modelCatalog;
|
|
50
50
|
|
|
51
51
|
/**
|
|
52
52
|
* How deep the grounding-metadata walk descends. Gemini nests its sources a
|
|
@@ -66,6 +66,8 @@ const RATE_LIMITED_STATUS = 429;
|
|
|
66
66
|
export interface WebSearchContext {
|
|
67
67
|
client: WorkspaceClientLike;
|
|
68
68
|
host: string;
|
|
69
|
+
/** Trusted identity of the credential carried by `client`. */
|
|
70
|
+
cacheIdentity?: string;
|
|
69
71
|
}
|
|
70
72
|
|
|
71
73
|
/**
|
|
@@ -77,7 +79,8 @@ export interface WebSearchContext {
|
|
|
77
79
|
export async function resolveWebSearchContext(): Promise<WebSearchContext> {
|
|
78
80
|
const ctx = getExecutionContext();
|
|
79
81
|
const host = (await ctx.client.config.getHost()).toString();
|
|
80
|
-
|
|
82
|
+
const cacheIdentity = "userId" in ctx ? ctx.userId : ctx.serviceUserId;
|
|
83
|
+
return { client: ctx.client, host, cacheIdentity };
|
|
81
84
|
}
|
|
82
85
|
|
|
83
86
|
/**
|
|
@@ -104,7 +107,9 @@ async function resolveWebSearchModel(
|
|
|
104
107
|
config: ResolvedWebSearchConfig,
|
|
105
108
|
requested: string | undefined,
|
|
106
109
|
): Promise<string | null> {
|
|
107
|
-
const endpoints = await listServingEndpoints(ctx.client, ctx.host
|
|
110
|
+
const endpoints = await listServingEndpoints(ctx.client, ctx.host, {
|
|
111
|
+
...(ctx.cacheIdentity ? { cacheIdentity: ctx.cacheIdentity } : {}),
|
|
112
|
+
});
|
|
108
113
|
// Only deployed, web-search-capable endpoints are candidates.
|
|
109
114
|
const capable = endpoints.filter((e) => supportsWebSearch(e.name));
|
|
110
115
|
const pinned = requested ?? config.model;
|
|
@@ -213,15 +218,37 @@ async function postServing(
|
|
|
213
218
|
/* --------------------------- response extraction --------------------------- */
|
|
214
219
|
|
|
215
220
|
/**
|
|
216
|
-
* Extract answer text and citations from an OpenAI Responses API payload
|
|
217
|
-
* the shared reader in `@dbx-tools/shared-model`.
|
|
221
|
+
* Extract answer text and citations from an OpenAI Responses API payload.
|
|
218
222
|
*/
|
|
219
223
|
function fromResponsesPayload(payload: Record<string, unknown>): {
|
|
220
224
|
answer: string;
|
|
221
225
|
citations: WebSearchCitation[];
|
|
222
226
|
} {
|
|
223
|
-
const
|
|
224
|
-
|
|
227
|
+
const output = Array.isArray(payload.output) ? payload.output : [];
|
|
228
|
+
const texts: string[] = [];
|
|
229
|
+
const citations: WebSearchCitation[] = [];
|
|
230
|
+
const seen = new Set<string>();
|
|
231
|
+
for (const item of output) {
|
|
232
|
+
if (!object.isRecord(item)) continue;
|
|
233
|
+
const parts = openaiChat.chatContentParts(item.content);
|
|
234
|
+
if (!parts) continue;
|
|
235
|
+
for (const part of parts) {
|
|
236
|
+
if (!object.isRecord(part)) continue;
|
|
237
|
+
const text = stringUtils.trimToEmpty(part.text);
|
|
238
|
+
if (text) texts.push(text);
|
|
239
|
+
const annotations = Array.isArray(part.annotations) ? part.annotations : [];
|
|
240
|
+
for (const annotation of annotations) {
|
|
241
|
+
if (!object.isRecord(annotation)) continue;
|
|
242
|
+
const url = stringUtils.trimToEmpty(annotation.url);
|
|
243
|
+
if (!url || seen.has(url)) continue;
|
|
244
|
+
seen.add(url);
|
|
245
|
+
const title = stringUtils.trimToEmpty(annotation.title);
|
|
246
|
+
citations.push({ url, ...(title ? { title } : {}) });
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
const answer = stringUtils.trimToEmpty(payload.output_text) || texts.join("\n").trim();
|
|
251
|
+
return { answer, citations };
|
|
225
252
|
}
|
|
226
253
|
|
|
227
254
|
/**
|
|
@@ -243,10 +270,10 @@ function fromChatPayload(payload: Record<string, unknown>): {
|
|
|
243
270
|
const seen = new Set<string>();
|
|
244
271
|
const visit = (v: unknown, depth: number): void => {
|
|
245
272
|
if (depth > MAX_GROUNDING_WALK_DEPTH || !object.isRecord(v)) return;
|
|
246
|
-
const url =
|
|
273
|
+
const url = stringUtils.trimToEmpty(v.url) || stringUtils.trimToEmpty(v.uri);
|
|
247
274
|
if (url && !seen.has(url)) {
|
|
248
275
|
seen.add(url);
|
|
249
|
-
const title =
|
|
276
|
+
const title = stringUtils.trimToEmpty(v.title);
|
|
250
277
|
citations.push({ url, ...(title ? { title } : {}) });
|
|
251
278
|
}
|
|
252
279
|
for (const val of Object.values(v)) {
|