123 lines
3.0 KiB
TypeScript
123 lines
3.0 KiB
TypeScript
import { defineStore } from 'pinia'
|
|
import { ref } from 'vue'
|
|
import { api } from '@/services/api'
|
|
|
|
export interface ChatMessage {
|
|
id: string
|
|
role: 'USER' | 'ASSISTANT'
|
|
content: string
|
|
sources: Array<{ bookTitle: string; page: number | null }>
|
|
createdAt: string
|
|
}
|
|
|
|
export interface ChatSession {
|
|
sessionId: string
|
|
topicId: string | null
|
|
createdAt: string
|
|
}
|
|
|
|
export const useChatStore = defineStore('chat', () => {
|
|
const session = ref<ChatSession | null>(null)
|
|
const messages = ref<ChatMessage[]>([])
|
|
const loading = ref(false)
|
|
const sending = ref(false)
|
|
const error = ref<string | null>(null)
|
|
|
|
async function createSession(topicId?: string): Promise<boolean> {
|
|
loading.value = true
|
|
error.value = null
|
|
try {
|
|
const body = topicId ? { topicId } : {}
|
|
const response = await api.post<ChatSession>('/chat/sessions', body)
|
|
session.value = response.data
|
|
messages.value = []
|
|
return true
|
|
} catch (err: any) {
|
|
error.value = err.message
|
|
return false
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
|
|
async function loadMessages(): Promise<void> {
|
|
if (!session.value) return
|
|
loading.value = true
|
|
error.value = null
|
|
try {
|
|
const response = await api.get<ChatMessage[]>(
|
|
`/chat/sessions/${session.value.sessionId}/messages`
|
|
)
|
|
messages.value = response.data
|
|
} catch (err: any) {
|
|
error.value = err.message
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
|
|
async function sendMessage(content: string): Promise<boolean> {
|
|
if (!session.value) return false
|
|
sending.value = true
|
|
error.value = null
|
|
|
|
// Optimistically add user message
|
|
const tempUserMsg: ChatMessage = {
|
|
id: crypto.randomUUID(),
|
|
role: 'USER',
|
|
content,
|
|
sources: [],
|
|
createdAt: new Date().toISOString()
|
|
}
|
|
messages.value.push(tempUserMsg)
|
|
|
|
try {
|
|
const response = await api.post<ChatMessage>(
|
|
`/chat/sessions/${session.value.sessionId}/messages`,
|
|
{ content }
|
|
)
|
|
// Replace temp message & add assistant response
|
|
messages.value = messages.value.filter((m) => m.id !== tempUserMsg.id)
|
|
// Reload full conversation to stay in sync
|
|
await loadMessages()
|
|
return true
|
|
} catch (err: any) {
|
|
// Remove optimistic message on failure
|
|
messages.value = messages.value.filter((m) => m.id !== tempUserMsg.id)
|
|
error.value = err.message
|
|
return false
|
|
} finally {
|
|
sending.value = false
|
|
}
|
|
}
|
|
|
|
async function deleteSession(): Promise<boolean> {
|
|
if (!session.value) return false
|
|
loading.value = true
|
|
error.value = null
|
|
try {
|
|
await api.delete(`/chat/sessions/${session.value.sessionId}`)
|
|
session.value = null
|
|
messages.value = []
|
|
return true
|
|
} catch (err: any) {
|
|
error.value = err.message
|
|
return false
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
|
|
return {
|
|
session,
|
|
messages,
|
|
loading,
|
|
sending,
|
|
error,
|
|
createSession,
|
|
loadMessages,
|
|
sendMessage,
|
|
deleteSession
|
|
}
|
|
})
|