added cors support and started chat client

This commit is contained in:
Storme-bit
2026-04-06 03:25:25 -07:00
parent 461438e81b
commit 1e2ce7a761
16 changed files with 2610 additions and 0 deletions

View File

@@ -0,0 +1,90 @@
import { useState, useCallback, useRef } from 'react';
import { streamMessage } from '../api/orchestration';
export function useChat({ activeSession, appendMessage, updateLastMessage, refreshSessions }) {
const [streaming, setStreaming] = useState(false);
const [error, setError] = useState(null);
const [lastTokenCount, setLastTokenCount] = useState(0);
const [lastModel, setLastModel] = useState(null);
const cancelRef = useRef(null);
const sendMessage = useCallback(async (text, model) => {
if (!activeSession || !text.trim() || streaming) return;
setError(null);
// 1. Append user bubble immediately
appendMessage({
id: `user-${Date.now()}`,
role: 'user',
text,
});
// 2. Append empty assistant bubble — will be filled by stream
appendMessage({
id: `assistant-${Date.now()}`,
role: 'assistant',
text: '',
streaming: true,
});
setStreaming(true);
// 3. Open stream
cancelRef.current = streamMessage(
activeSession.external_id,
text,
model,
{
onChunk: (token) => {
updateLastMessage(msg => ({
...msg,
text: msg.text + token,
}));
},
onDone: ({ model: resolvedModel, tokenCount }) => {
// Mark bubble as complete
updateLastMessage(msg => ({ ...msg, streaming: false }));
setLastTokenCount(tokenCount);
setLastModel(resolvedModel);
setStreaming(false);
cancelRef.current = null;
// Refresh session list so new sessions appear in sidebar
refreshSessions();
},
onError: (err) => {
updateLastMessage(msg => ({
...msg,
text: msg.text || 'Something went wrong.',
streaming: false,
error: true,
}));
setError(err.message);
setStreaming(false);
cancelRef.current = null;
},
}
);
}, [activeSession, streaming, appendMessage, updateLastMessage, refreshSessions]);
const cancelStream = useCallback(() => {
if (cancelRef.current) {
cancelRef.current();
cancelRef.current = null;
updateLastMessage(msg => ({ ...msg, streaming: false }));
setStreaming(false);
}
}, [updateLastMessage]);
return {
sendMessage,
cancelStream,
streaming,
error,
lastTokenCount,
lastModel,
};
}

View File

@@ -0,0 +1,97 @@
import { useState, useEffect, useCallback } from 'react';
import { fetchSessions, fetchSessionHistory } from '../api/orchestration';
import { v4 as uuidv4 } from 'uuid';
export function useSession() {
const [sessions, setSessions] = useState([]);
const [activeSession, setActiveSession] = useState(null);
const [messages, setMessages] = useState([]);
const [loadingHistory, setLoadingHistory] = useState(false);
const [error, setError] = useState(null);
// Load session list on mount
useEffect(() => {
loadSessions();
}, []);
async function loadSessions() {
try {
const data = await fetchSessions();
setSessions(data);
} catch (err) {
setError(err.message);
}
}
// Switch to an existing session and load its history
const selectSession = useCallback(async (session) => {
setActiveSession(session);
setMessages([]);
setLoadingHistory(true);
try {
const data = await fetchSessionHistory(session.external_id);
// History comes back newest-first — reverse for display
const history = data.episodes.reverse().map(ep => ([
{ id: `${ep.id}-user`, role: 'user', text: ep.user_message },
{ id: `${ep.id}-ai`, role: 'assistant', text: ep.ai_response },
])).flat();
setMessages(history);
} catch (err) {
setError(err.message);
} finally {
setLoadingHistory(false);
}
}, []);
// Create a new session with a generated UUID — no backend call needed yet,
// orchestration auto-creates the session on the first message
const createSession = useCallback(() => {
const newSession = {
external_id: uuidv4(),
metadata: null,
isNew: true, // flag so SessionList can style it differently
};
setSessions(prev => [newSession, ...prev]);
setActiveSession(newSession);
setMessages([]);
}, []);
// Called by useChat after a message completes — keeps session list fresh
const refreshSessions = useCallback(async () => {
try {
const data = await fetchSessions();
setSessions(data);
} catch {
// non-critical — sidebar just won't update
}
}, []);
// Append a message to the current thread (used by useChat)
const appendMessage = useCallback((message) => {
setMessages(prev => [...prev, message]);
}, []);
// Update the last message in the thread (used by useChat during streaming)
const updateLastMessage = useCallback((updater) => {
setMessages(prev => {
const updated = [...prev];
updated[updated.length - 1] = updater(updated[updated.length - 1]);
return updated;
});
}, []);
return {
sessions,
activeSession,
messages,
loadingHistory,
error,
selectSession,
createSession,
refreshSessions,
appendMessage,
updateLastMessage,
};
}