search: cache results

This commit is contained in:
2026-05-30 17:57:56 -07:00
parent 5b7ed25522
commit 600bc3befc
8 changed files with 148 additions and 11 deletions

View File

@@ -185,6 +185,18 @@ type CreateChatRequest = {
messages?: CompletionRequestMessage[];
};
type CreateSearchRequest = {
title?: string;
query?: string;
reuseByQuery?: boolean;
};
type CreateSearchResponse = {
search: SearchSummary;
reused: boolean;
cacheHit: boolean;
};
const API_BASE_URL = import.meta.env.VITE_API_BASE_URL ?? "/api";
const ENV_ADMIN_TOKEN = (import.meta.env.VITE_ADMIN_TOKEN as string | undefined)?.trim() || null;
let authToken: string | null = ENV_ADMIN_TOKEN;
@@ -296,14 +308,22 @@ export async function listSearches() {
return data.searches;
}
export async function createSearch(body?: { title?: string; query?: string }) {
const data = await api<{ search: SearchSummary }>("/v1/searches", {
async function postSearch(body?: CreateSearchRequest) {
return api<CreateSearchResponse>("/v1/searches", {
method: "POST",
body: JSON.stringify(body ?? {}),
});
}
export async function createSearch(body?: CreateSearchRequest) {
const data = await postSearch(body);
return data.search;
}
export async function createReusableSearch(body: Omit<CreateSearchRequest, "reuseByQuery">) {
return postSearch({ ...body, reuseByQuery: true });
}
export async function getSearch(searchId: string) {
const data = await api<{ search: SearchDetail }>(`/v1/searches/${searchId}`);
return data.search;

View File

@@ -4,7 +4,7 @@ import { AuthScreen } from "@/components/auth/auth-screen";
import { SearchResultsPanel } from "@/components/search/search-results-panel";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { createSearch, runSearchStream, type SearchDetail } from "@/lib/api";
import { createReusableSearch, getSearch, runSearchStream, type SearchDetail } from "@/lib/api";
import { useSessionAuth } from "@/hooks/use-session-auth";
function readQueryFromUrl() {
@@ -85,14 +85,16 @@ export default function SearchRoutePage() {
const runQuery = async (query: string) => {
const trimmed = query.trim();
const requestId = ++requestCounterRef.current;
streamAbortRef.current?.abort();
if (!trimmed) {
setSearch(null);
setError(null);
setIsRunning(false);
return;
}
const requestId = ++requestCounterRef.current;
streamAbortRef.current?.abort();
const abortController = new AbortController();
streamAbortRef.current = abortController;
let wasInterrupted = false;
@@ -119,10 +121,11 @@ export default function SearchRoutePage() {
});
try {
const created = await createSearch({
const createdResult = await createReusableSearch({
query: trimmed,
title: trimmed.slice(0, 80),
});
const created = createdResult.search;
if (requestId !== requestCounterRef.current) return;
setSearch((current) =>
@@ -140,6 +143,13 @@ export default function SearchRoutePage() {
: current
);
if (createdResult.cacheHit) {
const cached = await getSearch(created.id);
if (requestId !== requestCounterRef.current) return;
setSearch(cached);
return;
}
await runSearchStream(
created.id,
{