feat(ai): pre-warm the vector namespace

This commit is contained in:
Philipinho
2026-08-17 20:09:53 +01:00
parent 8c4ecfaaf2
commit 7b8ade0968
7 changed files with 32 additions and 10 deletions
@@ -15,6 +15,14 @@ export interface IAiSearchResponse {
}>; }>;
} }
export async function hintVectorCache(): Promise<void> {
try {
await api.post("/ai/vector-cache-hint");
} catch {
// best-effort cache hint
}
}
export async function aiAnswers( export async function aiAnswers(
params: IPageSearchParams, params: IPageSearchParams,
onChunk?: (chunk: { content?: string; sources?: any[] }) => void, onChunk?: (chunk: { content?: string; sources?: any[] }) => void,
@@ -13,11 +13,16 @@ import { SearchResultItem } from "./search-result-item.tsx";
import { AiSearchResult } from "../../../ee/ai/components/ai-search-result.tsx"; import { AiSearchResult } from "../../../ee/ai/components/ai-search-result.tsx";
import { useHasFeature } from "@/ee/hooks/use-feature"; import { useHasFeature } from "@/ee/hooks/use-feature";
import { Feature } from "@/ee/features"; import { Feature } from "@/ee/features";
import { useAtomValue } from "jotai";
import { workspaceAtom } from "@/features/user/atoms/current-user-atom.ts";
import { hintVectorCache } from "@/ee/ai/services/ai-search-service.ts";
import { getAiVectorDriver } from "@/lib/config.ts";
interface SearchSpotlightProps { interface SearchSpotlightProps {
spaceId?: string; spaceId?: string;
} }
export function SearchSpotlight({ spaceId }: SearchSpotlightProps) { export function SearchSpotlight({ spaceId }: SearchSpotlightProps) {
const workspace = useAtomValue(workspaceAtom);
const { t } = useTranslation(); const { t } = useTranslation();
const hasAiFeature = useHasFeature(Feature.AI); const hasAiFeature = useHasFeature(Feature.AI);
const hasAttachmentIndexing = useHasFeature(Feature.ATTACHMENT_INDEXING); const hasAttachmentIndexing = useHasFeature(Feature.ATTACHMENT_INDEXING);
@@ -96,6 +101,15 @@ export function SearchSpotlight({ spaceId }: SearchSpotlightProps) {
/> />
)); ));
const handleSpotlightOpen = () => {
if (
workspace?.settings?.ai?.search === true &&
getAiVectorDriver() === "turbopuffer"
) {
hintVectorCache();
}
};
const handleFiltersChange = (newFilters: any) => { const handleFiltersChange = (newFilters: any) => {
setFilters(newFilters); setFilters(newFilters);
}; };
@@ -115,6 +129,7 @@ export function SearchSpotlight({ spaceId }: SearchSpotlightProps) {
<Spotlight.Root <Spotlight.Root
size="xl" size="xl"
maxHeight={600} maxHeight={600}
onSpotlightOpen={handleSpotlightOpen}
store={searchSpotlightStore} store={searchSpotlightStore}
query={query} query={query}
onQueryChange={setQuery} onQueryChange={setQuery}
+4
View File
@@ -43,6 +43,10 @@ export function isCloud(): boolean {
return castToBoolean(getConfigValue("CLOUD")); return castToBoolean(getConfigValue("CLOUD"));
} }
export function getAiVectorDriver(): string {
return getConfigValue("AI_VECTOR_DRIVER");
}
export function getAvatarUrl( export function getAvatarUrl(
avatarUrl: string, avatarUrl: string,
type: AvatarIconType = AvatarIconType.AVATAR, type: AvatarIconType = AvatarIconType.AVATAR,
@@ -15,7 +15,6 @@ export enum EventName {
WORKSPACE_CREATED = 'workspace.created', WORKSPACE_CREATED = 'workspace.created',
WORKSPACE_UPDATED = 'workspace.updated', WORKSPACE_UPDATED = 'workspace.updated',
WORKSPACE_DELETED = 'workspace.deleted', WORKSPACE_DELETED = 'workspace.deleted',
USER_SESSION_STARTED = 'user.session.started',
BASE_CREATED = 'base.created', BASE_CREATED = 'base.created',
BASE_UPDATED = 'base.updated', BASE_UPDATED = 'base.updated',
@@ -6,7 +6,6 @@ import {
Post, Post,
UseGuards, UseGuards,
} from '@nestjs/common'; } from '@nestjs/common';
import { EventEmitter2 } from '@nestjs/event-emitter';
import { UserService } from './user.service'; import { UserService } from './user.service';
import { UpdateUserDto } from './dto/update-user.dto'; import { UpdateUserDto } from './dto/update-user.dto';
import { AuthUser } from '../../common/decorators/auth-user.decorator'; import { AuthUser } from '../../common/decorators/auth-user.decorator';
@@ -14,7 +13,6 @@ import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard';
import { AuthWorkspace } from '../../common/decorators/auth-workspace.decorator'; import { AuthWorkspace } from '../../common/decorators/auth-workspace.decorator';
import { User, Workspace } from '@docmost/db/types/entity.types'; import { User, Workspace } from '@docmost/db/types/entity.types';
import { WorkspaceRepo } from '@docmost/db/repos/workspace/workspace.repo'; import { WorkspaceRepo } from '@docmost/db/repos/workspace/workspace.repo';
import { EventName } from '../../common/events/event.contants';
@UseGuards(JwtAuthGuard) @UseGuards(JwtAuthGuard)
@Controller('users') @Controller('users')
@@ -22,7 +20,6 @@ export class UserController {
constructor( constructor(
private readonly userService: UserService, private readonly userService: UserService,
private readonly workspaceRepo: WorkspaceRepo, private readonly workspaceRepo: WorkspaceRepo,
private readonly eventEmitter: EventEmitter2,
) {} ) {}
@HttpCode(HttpStatus.OK) @HttpCode(HttpStatus.OK)
@@ -42,11 +39,6 @@ export class UserController {
memberCount, memberCount,
}; };
this.eventEmitter.emit(EventName.USER_SESSION_STARTED, {
userId: authUser.id,
workspaceId: workspace.id,
});
return { user: authUser, workspace: workspaceInfo }; return { user: authUser, workspace: workspaceInfo };
} }
@@ -49,6 +49,10 @@ export class StaticModule implements OnModuleInit {
: undefined, : undefined,
POSTHOG_HOST: this.environmentService.getPostHogHost(), POSTHOG_HOST: this.environmentService.getPostHogHost(),
POSTHOG_KEY: this.environmentService.getPostHogKey(), POSTHOG_KEY: this.environmentService.getPostHogKey(),
AI_VECTOR_DRIVER:
this.environmentService.getAiVectorDriver() === 'turbopuffer'
? 'turbopuffer'
: undefined,
}; };
const windowScriptContent = `<script>window.CONFIG=${JSON.stringify(configString)};</script>`; const windowScriptContent = `<script>window.CONFIG=${JSON.stringify(configString)};</script>`;