Files
webapp/app/pages/clientarea/ai/index.vue
2026-01-07 09:10:35 +07:00

121 lines
4.0 KiB
Vue
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<Section variant="plain" paddingY="md">
<Stack gap="6">
<PageHeader :title="t('aiAssistants.header.title')" />
<Card padding="lg" class="w-full">
<Stack gap="4">
<Stack direction="row" gap="3" align="center">
<IconCircle tone="primary" size="lg">🛰</IconCircle>
<div>
<Heading :level="3" weight="semibold">{{ t('aiAssistants.view.agentTitle') }}</Heading>
<Text tone="muted">{{ t('aiAssistants.view.agentSubtitle') }}</Text>
</div>
</Stack>
<div class="bg-base-200 rounded-box p-4 h-[70vh] overflow-y-auto space-y-3">
<div
v-for="(message, idx) in chat"
:key="idx"
class="flex"
:class="message.role === 'user' ? 'justify-end' : 'justify-start'"
>
<div
class="max-w-[80%] rounded-2xl px-4 py-3 shadow-sm"
:class="message.role === 'user' ? 'bg-primary text-primary-content' : 'bg-base-100 text-base-content'"
>
<Text weight="semibold" class="mb-1">
{{ message.role === 'user' ? t('aiAssistants.view.you') : t('aiAssistants.view.agentName') }}
</Text>
<Text :tone="message.role === 'user' ? undefined : 'muted'">
{{ message.content }}
</Text>
</div>
</div>
<div v-if="isStreaming" class="text-sm text-base-content/60">
{{ t('aiAssistants.view.typing') }}
</div>
</div>
<form class="flex flex-col gap-3" @submit.prevent="handleSend">
<Textarea
v-model="input"
:placeholder="t('aiAssistants.view.placeholder')"
rows="3"
/>
<div class="flex items-center gap-3">
<Button type="submit" :loading="isSending" :disabled="!input.trim()">
{{ t('aiAssistants.view.send') }}
</Button>
<Button type="button" variant="ghost" @click="resetChat" :disabled="isSending">
{{ t('aiAssistants.view.reset') }}
</Button>
<div class="text-sm text-base-content/60" v-if="error">
{{ error }}
</div>
</div>
</form>
</Stack>
</Card>
</Stack>
</Section>
</template>
<script setup lang="ts">
const { t } = useI18n()
const runtimeConfig = useRuntimeConfig()
const agentUrl = computed(() => runtimeConfig.public.langAgentUrl || '')
const chat = ref<{ role: 'user' | 'assistant', content: string }[]>([
{ role: 'assistant', content: t('aiAssistants.view.welcome') }
])
const input = ref('')
const isSending = ref(false)
const isStreaming = ref(false)
const error = ref('')
const handleSend = async () => {
if (!input.value.trim()) return
error.value = ''
const userMessage = input.value.trim()
chat.value.push({ role: 'user', content: userMessage })
input.value = ''
isSending.value = true
isStreaming.value = true
try {
const body = {
input: {
messages: chat.value.map((m) => ({
role: m.role === 'assistant' ? 'assistant' : 'user',
content: m.content
}))
}
}
const response = await $fetch(`${agentUrl.value}/invoke`, {
method: 'POST',
body
})
const outputMessages = (response as any)?.output?.messages || []
const last = outputMessages[outputMessages.length - 1]
const content = last?.content?.[0]?.text || last?.content || t('aiAssistants.view.emptyResponse')
chat.value.push({ role: 'assistant', content })
} catch (e: any) {
console.error('Agent error', e)
error.value = e?.message || t('aiAssistants.view.error')
chat.value.push({ role: 'assistant', content: t('aiAssistants.view.error') })
} finally {
isSending.value = false
isStreaming.value = false
}
}
const resetChat = () => {
chat.value = [{ role: 'assistant', content: t('aiAssistants.view.welcome') }]
input.value = ''
error.value = ''
}
</script>