- adapters/websearch: Brave client (X-Subscription-Token, error classes), result normalization with redaction/truncation, SSRF guard, HTML→text fetch (10s, 512KB, text/html|text/plain only) - modules/websearch: 10-min PG cache (region websearch), cross-process per-minute limit, web.fetch allowlist = URLs from the caller's latest web.search; POST /websearch/test - settings.agent.webSearch (key write-only → secret_ref); settings card under Agent; web.* offered to the model only when enabled, plus a system prompt rule; outputs marked untrusted - capability count 27 → 29; e2e E13; runbook docs/runbook/websearch.md Co-Authored-By: Claude Opus 5.5 <[email protected]>
46 lines
1.5 KiB
TypeScript
46 lines
1.5 KiB
TypeScript
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
|
import {
|
|
type GlobalSettings,
|
|
type GlobalSettingsUpdate,
|
|
mergeSettings,
|
|
type WebSearchTestResult,
|
|
} from "@umt/contracts";
|
|
import { apiData } from "~/lib/api.ts";
|
|
import { qk } from "~/lib/query-keys.ts";
|
|
|
|
export function useSettings() {
|
|
return useQuery({
|
|
queryKey: qk.settings,
|
|
queryFn: ({ signal }) => apiData<GlobalSettings>("/settings", { signal }),
|
|
});
|
|
}
|
|
|
|
/** PATCH /settings,乐观更新(失败回滚) */
|
|
export function useUpdateSettings() {
|
|
const qc = useQueryClient();
|
|
return useMutation({
|
|
mutationFn: (patch: GlobalSettingsUpdate) => apiData<GlobalSettings>("/settings", { method: "PATCH", body: patch }),
|
|
onMutate: async (patch) => {
|
|
await qc.cancelQueries({ queryKey: qk.settings });
|
|
const prev = qc.getQueryData<GlobalSettings>(qk.settings);
|
|
if (prev) qc.setQueryData(qk.settings, mergeSettings(prev, patch));
|
|
return { prev };
|
|
},
|
|
onError: (_e, _p, ctx) => {
|
|
if (ctx?.prev) qc.setQueryData(qk.settings, ctx.prev);
|
|
},
|
|
onSuccess: (data) => {
|
|
qc.setQueryData(qk.settings, data);
|
|
qc.invalidateQueries({ queryKey: qk.dashboard });
|
|
},
|
|
});
|
|
}
|
|
|
|
/** POST /websearch/test:用已保存的搜索服务 Key 实际查一次 */
|
|
export function useTestWebSearch() {
|
|
return useMutation({
|
|
mutationFn: (body: { query?: string } = {}) =>
|
|
apiData<WebSearchTestResult>("/websearch/test", { method: "POST", body }),
|
|
});
|
|
}
|