Files
khoj/src/interface/web/app/common/auth.ts
sabaimran d6c2d1fa49 Give Khoj Long Term Memories (#1168)
# Motivation
A major component of useful AI systems is adaptation to the user
context. This is a major reason why we'd enabled syncing knowledge
bases. The next steps in this direction is to dynamically update the
evolving state of the user as conversations take place across time and
topics. This allows for more personalized conversations and to maintain
context across conversations.

# Overview
This change introduces medium and long term memories in Khoj. 
- The scope of a conversation can be thought of as short term memory. 
- Medium term memory extends to the past week.
- Long term memory extends to anytime in the past, where a search query
results in a match.

# Details
- Enable user to view and manage agent generated memories from their
settings page
- Fully integrate the memory object into all downstream usage, from
image generation, notes extraction, online search, etc.
- Scope memory per agent. The default agent has access to memories
created by other agents as well.
- Enable users and admins to enable/disable Khoj's memory system

---------

Co-authored-by: Debanjum <debanjum@gmail.com>
2026-01-03 09:07:05 +05:30

125 lines
3.4 KiB
TypeScript

"use client";
import useSWR from "swr";
export interface UserProfile {
email: string;
username: string;
photo: string;
is_active: boolean;
has_documents: boolean;
detail: string;
khoj_version: string;
}
const fetcher = (url: string) =>
window
.fetch(url)
.then((res) => res.json())
.catch((err) => console.warn(err));
export function useAuthenticatedData() {
const { data, error, isLoading } = useSWR<UserProfile>("/api/v1/user", fetcher, {
revalidateOnFocus: false,
});
if (data?.detail === "Forbidden") {
return { data: null, error: "Forbidden", isLoading: false };
}
return { data, error, isLoading };
}
export interface ModelOptions {
id: number;
name: string;
tier: string;
description: string;
strengths: string;
}
export interface SyncedContent {
computer: boolean;
github: boolean;
notion: boolean;
}
export enum SubscriptionStates {
EXPIRED = "expired",
TRIAL = "trial",
SUBSCRIBED = "subscribed",
UNSUBSCRIBED = "unsubscribed",
INVALID = "invalid",
}
export interface UserConfig {
// user info
username: string;
user_photo: string | null;
is_active: boolean;
given_name: string;
phone_number: string;
is_phone_number_verified: boolean;
// user content settings
enabled_content_source: SyncedContent;
has_documents: boolean;
notion_token: string | null;
enable_memory: boolean;
server_memory_mode: "disabled" | "enabled_default_off" | "enabled_default_on";
// user model settings
search_model_options: ModelOptions[];
selected_search_model_config: number;
chat_model_options: ModelOptions[];
selected_chat_model_config: number;
paint_model_options: ModelOptions[];
selected_paint_model_config: number;
voice_model_options: ModelOptions[];
selected_voice_model_config: number;
// user billing info
subscription_state: SubscriptionStates;
subscription_renewal_date: string | undefined;
subscription_enabled_trial_at: string | undefined;
// server settings
khoj_cloud_subscription_url: string | undefined;
billing_enabled: boolean;
is_eleven_labs_enabled: boolean;
is_twilio_enabled: boolean;
khoj_version: string;
anonymous_mode: boolean;
notion_oauth_url: string;
detail: string;
length_of_free_trial: number;
}
export function useUserConfig(detailed: boolean = false) {
const url = `/api/settings?detailed=${detailed}`;
const { data, error, isLoading } = useSWR<UserConfig>(url, fetcher, {
revalidateOnFocus: false,
});
if (error || !data || data?.detail === "Forbidden") {
return { data: null, error, isLoading };
}
return { data, error, isLoading };
}
export function useChatModelOptions() {
const { data, error, isLoading } = useSWR<ModelOptions[]>(`/api/model/chat/options`, fetcher, {
revalidateOnFocus: false,
});
return { models: data, error, isLoading };
}
export function isUserSubscribed(userConfig: UserConfig | null): boolean {
return (
(userConfig?.subscription_state &&
[
SubscriptionStates.SUBSCRIBED.valueOf(),
SubscriptionStates.TRIAL.valueOf(),
SubscriptionStates.UNSUBSCRIBED.valueOf(),
].includes(userConfig.subscription_state)) ||
false
);
}