feat: memory v2, prompt styles, Dream/GAZE integration, Wyoming TTS fix

SQLite + sqlite-vec replaces JSON memory files with semantic search,
follow-up injection, privacy levels, and lifecycle management.

Six prompt styles (quick/standard/creative/roleplayer/game-master/storyteller)
with per-style Claude model tiering (Haiku/Sonnet/Opus), temperature control,
and section stripping. Characters can set default style and per-style overrides.

Dream character import and GAZE character linking in the dashboard editor
with auto-populated fields, cover image resolution, and preset assignment.

Bridge: session isolation (conversation_id / 12h satellite buckets),
model routing refactor, PUT/DELETE support, memory REST endpoints.

Dashboard: mobile-responsive sidebar, retry button, style picker in chat,
follow-up banner, memory lifecycle/privacy UI, cloud model options in editor.

Wyoming TTS: upgraded to v1.8.0 for HA 1.7.2 compatibility.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Aodhan Collins
2026-03-24 22:31:04 +00:00
parent c3bae6fdc0
commit 56580a2cb2
34 changed files with 2891 additions and 467 deletions

View File

@@ -70,7 +70,8 @@ export function useChat(conversationId, conversationMeta, onConversationUpdate)
}, [conversationMeta, onConversationUpdate])
// send accepts an optional overrideId for when the conversation was just created
const send = useCallback(async (text, overrideId) => {
// and an optional promptStyle to control response style
const send = useCallback(async (text, overrideId, promptStyle) => {
if (!text.trim() || isLoading) return null
const userMsg = { id: Date.now(), role: 'user', content: text.trim(), timestamp: new Date().toISOString() }
@@ -80,13 +81,15 @@ export function useChat(conversationId, conversationMeta, onConversationUpdate)
setIsLoading(true)
try {
const { response, model } = await sendMessage(text.trim(), conversationMeta?.characterId || null)
const activeConvId = overrideId || idRef.current
const { response, model, prompt_style } = await sendMessage(text.trim(), conversationMeta?.characterId || null, promptStyle, activeConvId)
const assistantMsg = {
id: Date.now() + 1,
role: 'assistant',
content: response,
timestamp: new Date().toISOString(),
...(model && { model }),
...(prompt_style && { prompt_style }),
}
const allMessages = [...newMessages, assistantMsg]
setMessages(allMessages)
@@ -114,6 +117,52 @@ export function useChat(conversationId, conversationMeta, onConversationUpdate)
}
}, [isLoading, messages, persist])
// Retry: remove the error message, re-send the preceding user message
const retry = useCallback(async (errorMsgId, promptStyle) => {
const idx = messages.findIndex(m => m.id === errorMsgId)
if (idx < 1) return null
// Find the user message right before the error
const userMsg = messages[idx - 1]
if (!userMsg || userMsg.role !== 'user') return null
// Remove the error message
const cleaned = messages.filter(m => m.id !== errorMsgId)
setMessages(cleaned)
await persist(cleaned)
// Re-send (but we need to temporarily set messages back without the error so send picks up correctly)
// Instead, inline the send logic with the cleaned message list
setIsLoading(true)
try {
const activeConvId = idRef.current
const { response, model, prompt_style } = await sendMessage(userMsg.content, conversationMeta?.characterId || null, promptStyle, activeConvId)
const assistantMsg = {
id: Date.now() + 1,
role: 'assistant',
content: response,
timestamp: new Date().toISOString(),
...(model && { model }),
...(prompt_style && { prompt_style }),
}
const allMessages = [...cleaned, assistantMsg]
setMessages(allMessages)
await persist(allMessages)
return response
} catch (err) {
const newError = {
id: Date.now() + 1,
role: 'assistant',
content: `Error: ${err.message}`,
timestamp: new Date().toISOString(),
isError: true,
}
const allMessages = [...cleaned, newError]
setMessages(allMessages)
await persist(allMessages)
return null
} finally {
setIsLoading(false)
}
}, [messages, persist, conversationMeta])
const clearHistory = useCallback(async () => {
setMessages([])
if (idRef.current) {
@@ -121,5 +170,5 @@ export function useChat(conversationId, conversationMeta, onConversationUpdate)
}
}, [persist])
return { messages, isLoading, isLoadingConv, send, clearHistory }
return { messages, isLoading, isLoadingConv, send, retry, clearHistory }
}