344 lines
9.9 KiB
TypeScript
344 lines
9.9 KiB
TypeScript
import { useState, useCallback } from 'react';
|
|
import axios from 'axios';
|
|
import { Memory, Client, Category } from '@/components/types';
|
|
import { useDispatch, useSelector } from 'react-redux';
|
|
import { AppDispatch, RootState } from '@/store/store';
|
|
import { setAccessLogs, setMemoriesSuccess, setSelectedMemory, setRelatedMemories } from '@/store/memoriesSlice';
|
|
|
|
// Define the new simplified memory type
|
|
export interface SimpleMemory {
|
|
id: string;
|
|
text: string;
|
|
created_at: string;
|
|
state: string;
|
|
categories: string[];
|
|
app_name: string;
|
|
}
|
|
|
|
// Define the shape of the API response item
|
|
interface ApiMemoryItem {
|
|
id: string;
|
|
content: string;
|
|
created_at: string;
|
|
state: string;
|
|
app_id: string;
|
|
categories: string[];
|
|
metadata_?: Record<string, any>;
|
|
app_name: string;
|
|
}
|
|
|
|
// Define the shape of the API response
|
|
interface ApiResponse {
|
|
items: ApiMemoryItem[];
|
|
total: number;
|
|
page: number;
|
|
size: number;
|
|
pages: number;
|
|
}
|
|
|
|
interface AccessLogEntry {
|
|
id: string;
|
|
app_name: string;
|
|
accessed_at: string;
|
|
}
|
|
|
|
interface AccessLogResponse {
|
|
total: number;
|
|
page: number;
|
|
page_size: number;
|
|
logs: AccessLogEntry[];
|
|
}
|
|
|
|
interface RelatedMemoryItem {
|
|
id: string;
|
|
content: string;
|
|
created_at: number;
|
|
state: string;
|
|
app_id: string;
|
|
app_name: string;
|
|
categories: string[];
|
|
metadata_: Record<string, any>;
|
|
}
|
|
|
|
interface RelatedMemoriesResponse {
|
|
items: RelatedMemoryItem[];
|
|
total: number;
|
|
page: number;
|
|
size: number;
|
|
pages: number;
|
|
}
|
|
|
|
interface UseMemoriesApiReturn {
|
|
fetchMemories: (
|
|
query?: string,
|
|
page?: number,
|
|
size?: number,
|
|
filters?: {
|
|
apps?: string[];
|
|
categories?: string[];
|
|
sortColumn?: string;
|
|
sortDirection?: 'asc' | 'desc';
|
|
showArchived?: boolean;
|
|
}
|
|
) => Promise<{ memories: Memory[]; total: number; pages: number }>;
|
|
fetchMemoryById: (memoryId: string) => Promise<void>;
|
|
fetchAccessLogs: (memoryId: string, page?: number, pageSize?: number) => Promise<void>;
|
|
fetchRelatedMemories: (memoryId: string) => Promise<void>;
|
|
createMemory: (text: string) => Promise<void>;
|
|
deleteMemories: (memoryIds: string[]) => Promise<void>;
|
|
updateMemory: (memoryId: string, content: string) => Promise<void>;
|
|
updateMemoryState: (memoryIds: string[], state: string) => Promise<void>;
|
|
isLoading: boolean;
|
|
error: string | null;
|
|
hasUpdates: number;
|
|
memories: Memory[];
|
|
selectedMemory: SimpleMemory | null;
|
|
}
|
|
|
|
export const useMemoriesApi = (): UseMemoriesApiReturn => {
|
|
const [isLoading, setIsLoading] = useState<boolean>(false);
|
|
const [error, setError] = useState<string | null>(null);
|
|
const [hasUpdates, setHasUpdates] = useState<number>(0);
|
|
const dispatch = useDispatch<AppDispatch>();
|
|
const user_id = useSelector((state: RootState) => state.profile.userId);
|
|
const memories = useSelector((state: RootState) => state.memories.memories);
|
|
const selectedMemory = useSelector((state: RootState) => state.memories.selectedMemory);
|
|
|
|
const URL = process.env.NEXT_PUBLIC_API_URL || "http://localhost:8765";
|
|
|
|
const fetchMemories = useCallback(async (
|
|
query?: string,
|
|
page: number = 1,
|
|
size: number = 10,
|
|
filters?: {
|
|
apps?: string[];
|
|
categories?: string[];
|
|
sortColumn?: string;
|
|
sortDirection?: 'asc' | 'desc';
|
|
showArchived?: boolean;
|
|
}
|
|
): Promise<{ memories: Memory[], total: number, pages: number }> => {
|
|
setIsLoading(true);
|
|
setError(null);
|
|
try {
|
|
const response = await axios.post<ApiResponse>(
|
|
`${URL}/api/v1/memories/filter`,
|
|
{
|
|
user_id: user_id,
|
|
page: page,
|
|
size: size,
|
|
search_query: query,
|
|
app_ids: filters?.apps,
|
|
category_ids: filters?.categories,
|
|
sort_column: filters?.sortColumn?.toLowerCase(),
|
|
sort_direction: filters?.sortDirection,
|
|
show_archived: filters?.showArchived
|
|
}
|
|
);
|
|
|
|
const adaptedMemories: Memory[] = response.data.items.map((item: ApiMemoryItem) => ({
|
|
id: item.id,
|
|
memory: item.content,
|
|
created_at: new Date(item.created_at).getTime(),
|
|
state: item.state as "active" | "paused" | "archived" | "deleted",
|
|
metadata: item.metadata_,
|
|
categories: item.categories as Category[],
|
|
client: 'api',
|
|
app_name: item.app_name
|
|
}));
|
|
setIsLoading(false);
|
|
dispatch(setMemoriesSuccess(adaptedMemories));
|
|
return {
|
|
memories: adaptedMemories,
|
|
total: response.data.total,
|
|
pages: response.data.pages
|
|
};
|
|
} catch (err: any) {
|
|
const errorMessage = err.message || 'Failed to fetch memories';
|
|
setError(errorMessage);
|
|
setIsLoading(false);
|
|
throw new Error(errorMessage);
|
|
}
|
|
}, [user_id, dispatch]);
|
|
|
|
const createMemory = async (text: string): Promise<void> => {
|
|
try {
|
|
const memoryData = {
|
|
user_id: user_id,
|
|
text: text,
|
|
infer: false,
|
|
app: "openmemory",
|
|
}
|
|
await axios.post<ApiMemoryItem>(`${URL}/api/v1/memories/`, memoryData);
|
|
} catch (err: any) {
|
|
const errorMessage = err.message || 'Failed to create memory';
|
|
setError(errorMessage);
|
|
setIsLoading(false);
|
|
throw new Error(errorMessage);
|
|
}
|
|
};
|
|
|
|
const deleteMemories = async (memory_ids: string[]) => {
|
|
try {
|
|
await axios.delete(`${URL}/api/v1/memories/`, {
|
|
data: { memory_ids, user_id }
|
|
});
|
|
dispatch(setMemoriesSuccess(memories.filter((memory: Memory) => !memory_ids.includes(memory.id))));
|
|
} catch (err: any) {
|
|
const errorMessage = err.message || 'Failed to delete memories';
|
|
setError(errorMessage);
|
|
setIsLoading(false);
|
|
throw new Error(errorMessage);
|
|
}
|
|
};
|
|
|
|
const fetchMemoryById = async (memoryId: string): Promise<void> => {
|
|
if (memoryId === "") {
|
|
return;
|
|
}
|
|
setIsLoading(true);
|
|
setError(null);
|
|
try {
|
|
const response = await axios.get<SimpleMemory>(
|
|
`${URL}/api/v1/memories/${memoryId}?user_id=${user_id}`
|
|
);
|
|
setIsLoading(false);
|
|
dispatch(setSelectedMemory(response.data));
|
|
} catch (err: any) {
|
|
const errorMessage = err.message || 'Failed to fetch memory';
|
|
setError(errorMessage);
|
|
setIsLoading(false);
|
|
throw new Error(errorMessage);
|
|
}
|
|
};
|
|
|
|
const fetchAccessLogs = async (memoryId: string, page: number = 1, pageSize: number = 10): Promise<void> => {
|
|
if (memoryId === "") {
|
|
return;
|
|
}
|
|
setIsLoading(true);
|
|
setError(null);
|
|
try {
|
|
const response = await axios.get<AccessLogResponse>(
|
|
`${URL}/api/v1/memories/${memoryId}/access-log?page=${page}&page_size=${pageSize}`
|
|
);
|
|
setIsLoading(false);
|
|
dispatch(setAccessLogs(response.data.logs));
|
|
} catch (err: any) {
|
|
const errorMessage = err.message || 'Failed to fetch access logs';
|
|
setError(errorMessage);
|
|
setIsLoading(false);
|
|
throw new Error(errorMessage);
|
|
}
|
|
};
|
|
|
|
const fetchRelatedMemories = async (memoryId: string): Promise<void> => {
|
|
if (memoryId === "") {
|
|
return;
|
|
}
|
|
setIsLoading(true);
|
|
setError(null);
|
|
try {
|
|
const response = await axios.get<RelatedMemoriesResponse>(
|
|
`${URL}/api/v1/memories/${memoryId}/related?user_id=${user_id}`
|
|
);
|
|
|
|
const adaptedMemories: Memory[] = response.data.items.map((item: RelatedMemoryItem) => ({
|
|
id: item.id,
|
|
memory: item.content,
|
|
created_at: item.created_at,
|
|
state: item.state as "active" | "paused" | "archived" | "deleted",
|
|
metadata: item.metadata_,
|
|
categories: item.categories as Category[],
|
|
client: 'api',
|
|
app_name: item.app_name
|
|
}));
|
|
|
|
setIsLoading(false);
|
|
dispatch(setRelatedMemories(adaptedMemories));
|
|
} catch (err: any) {
|
|
const errorMessage = err.message || 'Failed to fetch related memories';
|
|
setError(errorMessage);
|
|
setIsLoading(false);
|
|
throw new Error(errorMessage);
|
|
}
|
|
};
|
|
|
|
const updateMemory = async (memoryId: string, content: string): Promise<void> => {
|
|
if (memoryId === "") {
|
|
return;
|
|
}
|
|
setIsLoading(true);
|
|
setError(null);
|
|
try {
|
|
await axios.put(`${URL}/api/v1/memories/${memoryId}`, {
|
|
memory_id: memoryId,
|
|
memory_content: content,
|
|
user_id: user_id
|
|
});
|
|
setIsLoading(false);
|
|
setHasUpdates(hasUpdates + 1);
|
|
} catch (err: any) {
|
|
const errorMessage = err.message || 'Failed to update memory';
|
|
setError(errorMessage);
|
|
setIsLoading(false);
|
|
throw new Error(errorMessage);
|
|
}
|
|
};
|
|
|
|
const updateMemoryState = async (memoryIds: string[], state: string): Promise<void> => {
|
|
if (memoryIds.length === 0) {
|
|
return;
|
|
}
|
|
setIsLoading(true);
|
|
setError(null);
|
|
try {
|
|
await axios.post(`${URL}/api/v1/memories/actions/pause`, {
|
|
memory_ids: memoryIds,
|
|
all_for_app: true,
|
|
state: state,
|
|
user_id: user_id
|
|
});
|
|
dispatch(setMemoriesSuccess(memories.map((memory: Memory) => {
|
|
if (memoryIds.includes(memory.id)) {
|
|
return { ...memory, state: state as "active" | "paused" | "archived" | "deleted" };
|
|
}
|
|
return memory;
|
|
})));
|
|
|
|
// If archive, delete the memory
|
|
if (state === "archived") {
|
|
dispatch(setMemoriesSuccess(memories.filter((memory: Memory) => !memoryIds.includes(memory.id))));
|
|
}
|
|
|
|
// if selected memory, update it
|
|
if (selectedMemory?.id && memoryIds.includes(selectedMemory.id)) {
|
|
dispatch(setSelectedMemory({ ...selectedMemory, state: state as "active" | "paused" | "archived" | "deleted" }));
|
|
}
|
|
|
|
setIsLoading(false);
|
|
setHasUpdates(hasUpdates + 1);
|
|
} catch (err: any) {
|
|
const errorMessage = err.message || 'Failed to update memory state';
|
|
setError(errorMessage);
|
|
setIsLoading(false);
|
|
throw new Error(errorMessage);
|
|
}
|
|
};
|
|
|
|
return {
|
|
fetchMemories,
|
|
fetchMemoryById,
|
|
fetchAccessLogs,
|
|
fetchRelatedMemories,
|
|
createMemory,
|
|
deleteMemories,
|
|
updateMemory,
|
|
updateMemoryState,
|
|
isLoading,
|
|
error,
|
|
hasUpdates,
|
|
memories,
|
|
selectedMemory
|
|
};
|
|
}; |