RYP / src /lib /aiTeacherClient.ts
Soumya79's picture
Upload 1361 files
f91a684 verified
import { AUTH_TOKEN_KEY } from './authClient';
const getAuthHeaders = () => {
const token = localStorage.getItem(AUTH_TOKEN_KEY);
return {
'Content-Type': 'application/json',
...(token ? { Authorization: `Bearer ${token}` } : {})
};
};
function apiUrl(path: string): string {
const base = (
import.meta.env.VITE_API_BASE as string | undefined
)
?.trim()
.replace(/\/$/, '') ?? '';
const p = path.startsWith('/') ? path : `/${path}`;
return `${base}${p}`;
}
export interface AIThread {
id: string;
userId: string;
title: string;
createdAt: string;
updatedAt: string;
}
export interface AIMessage {
id: string;
threadId: string;
role: 'user' | 'assistant' | 'system';
content: string;
createdAt: string;
}
export type AITeacherMode = 'learning' | 'placement' | 'files';
export async function fetchThreads(): Promise<AIThread[]> {
const res = await fetch(apiUrl('/api/ai/threads'), {
headers: getAuthHeaders()
});
if (!res.ok) throw new Error('Failed to fetch threads');
const data = await res.json();
return data.threads;
}
export async function createThread(title: string): Promise<AIThread> {
const res = await fetch(apiUrl('/api/ai/threads/new'), {
method: 'POST',
headers: getAuthHeaders(),
body: JSON.stringify({ title })
});
if (!res.ok) throw new Error('Failed to create thread');
const data = await res.json();
return data.thread;
}
export async function fetchThreadMessages(threadId: string): Promise<AIMessage[]> {
const res = await fetch(apiUrl(`/api/ai/threads/messages?threadId=${threadId}`), {
headers: getAuthHeaders()
});
if (!res.ok) throw new Error('Failed to fetch messages');
const data = await res.json();
return data.messages;
}
export async function deleteThread(threadId: string): Promise<void> {
const res = await fetch(apiUrl(`/api/ai/threads/delete?threadId=${threadId}`), {
method: 'DELETE',
headers: getAuthHeaders()
});
if (!res.ok) throw new Error('Failed to delete thread');
}
export interface AIFileAttachment {
name: string;
type: string;
size: number;
kind: string;
textExcerpt: string;
}
export interface AIChatResponse {
thread?: AIThread;
userMessage: AIMessage;
assistantMessage: AIMessage;
message: AIMessage;
}
export async function sendMessageToThread(
threadId: string,
message: string,
attachments?: AIFileAttachment[],
mode?: AITeacherMode,
): Promise<AIChatResponse> {
const res = await fetch(apiUrl('/api/ai/teacher'), {
method: 'POST',
headers: getAuthHeaders(),
body: JSON.stringify({
threadId,
message,
attachments: attachments || [],
mode: mode || 'learning',
})
});
if (!res.ok) {
let errorMessage = 'Failed to send message';
try {
const data = await res.json();
if (typeof data?.error === 'string' && data.error.trim()) {
errorMessage = data.error.trim();
}
} catch {
// Ignore JSON parsing errors and use fallback text.
}
throw new Error(errorMessage);
}
const data = await res.json();
return {
thread: data.thread,
userMessage: data.userMessage,
assistantMessage: data.assistantMessage || data.message,
message: data.message,
};
}