Christian Kniep
new webapp
1fff71f
import { writable, derived } from 'svelte/store';
import type { Session, SessionMetadata } from '$lib/types/api';
import type { SessionListItem } from '$lib/types/client';
import { apiClient } from '$lib/services/api';
import { STORAGE_KEY_ACTIVE_SESSION } from '$lib/utils/constants';
import { getItem, setItem, removeItem } from '$lib/services/storage';
/**
* Session store managing user's chat sessions
* Handles session list, active session, and CRUD operations
*/
interface SessionState {
sessions: SessionListItem[]; // List of user sessions (metadata only)
activeSession: Session | null; // Currently loaded session with full messages
loading: boolean;
error: string | null;
}
function createInitialState(): SessionState {
return {
sessions: [],
activeSession: null,
loading: false,
error: null
};
}
// Create the session store
function createSessionStore() {
const { subscribe, set, update } = writable<SessionState>(createInitialState());
return {
subscribe,
/**
* Load all sessions for current user
*/
async loadSessions() {
update((state) => ({ ...state, loading: true, error: null }));
try {
const sessions = await apiClient.listSessions();
// Convert to SessionListItem format (convert Unix timestamps to ISO strings)
const sessionList: SessionListItem[] = sessions.map((s) => ({
id: s.id,
title: s.title,
last_interaction: new Date(s.last_interaction * 1000).toISOString(),
message_count: s.message_count,
is_reference: s.is_reference,
is_active: false
}));
update((state) => ({
...state,
sessions: sessionList,
loading: false
}));
} catch (err) {
const errorMessage = err instanceof Error ? err.message : 'Failed to load sessions';
update((state) => ({
...state,
loading: false,
error: errorMessage
}));
}
},
/**
* Create a new session
*/
async createSession(title: string): Promise<string | null> {
update((state) => ({ ...state, loading: true, error: null }));
try {
const response = await apiClient.createSession(title);
const newSessionId = response.session_id;
// Add to session list
const newSession: SessionListItem = {
id: newSessionId,
title,
last_interaction: new Date().toISOString(),
message_count: 0,
is_reference: false,
is_active: false
};
update((state) => ({
...state,
sessions: [newSession, ...state.sessions],
loading: false
}));
return newSessionId;
} catch (err) {
const errorMessage = err instanceof Error ? err.message : 'Failed to create session';
update((state) => ({
...state,
loading: false,
error: errorMessage
}));
return null;
}
},
/**
* Load a specific session with messages
*/
async loadSession(sessionId: string) {
update((state) => ({ ...state, loading: true, error: null }));
try {
const session = await apiClient.getSession(sessionId);
// Mark as active in session list
update((state) => ({
...state,
sessions: state.sessions.map((s) => ({
...s,
is_active: s.id === sessionId
})),
activeSession: session,
loading: false
}));
// Persist active session ID
setItem(STORAGE_KEY_ACTIVE_SESSION, sessionId);
} catch (err) {
const errorMessage = err instanceof Error ? err.message : 'Failed to load session';
update((state) => ({
...state,
loading: false,
error: errorMessage
}));
}
},
/**
* Delete a session
*/
async deleteSession(sessionId: string): Promise<boolean> {
update((state) => ({ ...state, loading: true, error: null }));
try {
await apiClient.deleteSession(sessionId);
update((state) => {
const newState = {
...state,
sessions: state.sessions.filter((s) => s.id !== sessionId),
loading: false
};
// Clear active session if it was deleted
if (state.activeSession?.id === sessionId) {
newState.activeSession = null;
removeItem(STORAGE_KEY_ACTIVE_SESSION);
}
return newState;
});
return true;
} catch (err) {
const errorMessage = err instanceof Error ? err.message : 'Failed to delete session';
update((state) => ({
...state,
loading: false,
error: errorMessage
}));
return false;
}
},
/**
* Update session reference flag
*/
async updateSessionReference(sessionId: string, isReference: boolean): Promise<boolean> {
update((state) => ({ ...state, loading: true, error: null }));
try {
await apiClient.updateSession(sessionId, isReference);
update((state) => ({
...state,
sessions: state.sessions.map((s) =>
s.id === sessionId ? { ...s, is_reference: isReference } : s
),
activeSession:
state.activeSession?.id === sessionId
? { ...state.activeSession, is_reference: isReference }
: state.activeSession,
loading: false
}));
return true;
} catch (err) {
const errorMessage = err instanceof Error ? err.message : 'Failed to update session';
update((state) => ({
...state,
loading: false,
error: errorMessage
}));
return false;
}
},
/**
* Clear active session
*/
clearActive() {
update((state) => ({
...state,
activeSession: null
}));
removeItem(STORAGE_KEY_ACTIVE_SESSION);
},
/**
* Send a message to the active session
*/
async sendMessage(
sessionId: string,
content: string,
mode: string
): Promise<boolean> {
update((state) => ({ ...state, loading: true, error: null }));
try {
const response = await apiClient.sendMessage(sessionId, {
mode: mode as any,
message: content,
save_to_memory: mode === 'memorize'
});
// Update active session with new message
update((state) => {
if (state.activeSession?.id === sessionId) {
return {
...state,
activeSession: {
...state.activeSession,
messages: [
...state.activeSession.messages,
{
mode: mode as any,
content,
created_at: new Date().toISOString()
}
],
last_interaction: new Date().toISOString()
},
sessions: state.sessions.map((s) =>
s.id === sessionId
? {
...s,
message_count: s.message_count + 1,
last_interaction: new Date().toISOString()
}
: s
),
loading: false
};
}
return { ...state, loading: false };
});
return true;
} catch (err) {
const errorMessage = err instanceof Error ? err.message : 'Failed to send message';
update((state) => ({
...state,
loading: false,
error: errorMessage
}));
return false;
}
},
/**
* Clear error
*/
clearError() {
update((state) => ({ ...state, error: null }));
}
};
}
export const sessionStore = createSessionStore();
// Derived stores
export const activeSessions = derived(sessionStore, ($session) => $session.sessions);
export const activeSession = derived(sessionStore, ($session) => $session.activeSession);
export const sessionCount = derived(sessionStore, ($session) => $session.sessions.length);
export const isLoading = derived(sessionStore, ($session) => $session.loading);
export const sessionError = derived(sessionStore, ($session) => $session.error);