Files
artagents/app/pages/[agent].vue

175 lines
5.1 KiB
Vue

<script setup lang="ts">
import { PROVIDERS, type Agent } from '~~/types'
import { useChat } from '@ai-sdk/vue'
import { onStartTyping } from '@vueuse/core'
import { AGENTS, MODELS } from '~~/types'
const toast = useToast()
const { agent } = useRoute().params
const currentAgent = ref(AGENTS.find(item => item.slug === agent) as Agent | undefined)
const model = ref<PROVIDERS>(currentAgent.value?.defaultModel || PROVIDERS.MISTRAL)
const selectedModel = computed(() => MODELS.find(item => item.model === model.value))
const { loadChat, deleteChat } = await useChatFile()
const initialMessages = await loadChat(currentAgent.value?.slug || '')
const { messages, input, handleSubmit, status, stop, error, reload } = useChat({
id: currentAgent.value?.slug,
initialMessages,
sendExtraMessageFields: true,
experimental_prepareRequestBody({ messages, id }) {
return { message: messages[messages.length - 1], id }
},
body: {
model: model.value,
agent: currentAgent.value,
},
})
const inputRef = shallowRef<HTMLInputElement | null>(null)
onStartTyping(() => { // TODO: fix focus
if (inputRef.value !== document.activeElement)
inputRef.value!.focus()
})
const isModalOpen = ref(false)
async function deleteConversation() {
await deleteChat(currentAgent.value!.slug)
window.location.reload()
isModalOpen.value = false
toast.add({
title: 'Conversation deleted',
description: `The conversation has been deleted`,
color: 'success',
})
}
</script>
<template>
<main
v-if="currentAgent"
class="flex flex-col justify-between"
>
<ChatHeader :current-agent="currentAgent" />
<ChatMessages
:messages="messages"
:initial-messages="initialMessages"
:current-agent="currentAgent"
/>
<div
v-if="error"
class="flex items-center justify-center gap-2"
>
<div class="text-red-500">
{{ 'An error occurred' }}
</div>
<UButton
color="neutral"
variant="subtle"
size="xs"
@click="reload()"
>
retry
</UButton>
</div>
<ClientOnly>
<form
class="mt-4 shadow-md flex flex-col items-center w-full p-2 gap-2 bg-zinc-100 rounded-2xl dark:bg-neutral-800"
@submit.prevent="handleSubmit"
@keydown.enter.prevent="handleSubmit"
>
<div class="w-full flex gap-2">
<UTextarea
ref="inputRef"
v-model="input"
placeholder="Type what you want to ask..."
class="w-full min-h-8"
:ui="{
base: 'min-h-8',
}"
color="primary"
autoresize
:rows="1"
variant="soft"
:disabled="Boolean(error)"
/>
<div class="flex items-start">
<UButton
v-if="status !== 'ready'"
icon="i-heroicons-stop"
color="primary"
@click="stop"
/>
<UButton
v-else
icon="i-heroicons-arrow-long-up-16-solid"
type="submit"
class="rounded-xl duration-300"
:color="input.length === 0 ? 'neutral' : 'primary'"
:disabled="input.length === 0"
/>
</div>
</div>
<div class="w-full flex items-center gap-2">
<UButton
icon="i-heroicons-paper-clip"
color="neutral"
variant="subtle"
class="rounded-xl"
@click="stop"
/>
<UTooltip text="Select model">
<USelect
v-model="model"
class="rounded-xl w-40"
color="neutral"
variant="subtle"
:items="MODELS"
label-key="name"
value-key="model"
:icon="selectedModel?.icon"
trailing-icon=""
leading-icon=""
/>
</UTooltip>
<UModal
v-model:open="isModalOpen"
title="Are you sure you want to delete this conversation?"
description="This action cannot be undone. The agent will lost all the conversation history and context."
:ui="{ footer: 'justify-end' }"
>
<UTooltip text="Clear conversation">
<UButton
:disabled="messages.length === 0"
icon="i-heroicons-trash"
color="neutral"
variant="subtle"
class="rounded-xl"
/>
</UTooltip>
<template #footer>
<div class="flex gap-2">
<UButton
color="neutral"
label="Dismiss"
@click.prevent="isModalOpen = false"
/>
<UButton
color="error"
label="Delete"
@click.prevent="deleteConversation"
/>
</div>
</template>
</UModal>
</div>
</form>
</ClientOnly>
</main>
<main v-else>
<div class="flex items-center justify-center h-full">
Agent not found
</div>
</main>
</template>