Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 32 additions & 28 deletions docs/app/components/content/AssistantDemo.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script setup lang="ts">
import { Chat } from '@ai-sdk/vue'
import { useChat } from '@ai-sdk/vue'
import { DefaultChatTransport, isToolUIPart, isTextUIPart } from 'ai'
import { isToolStreaming } from '@nuxt/ui/utils/ai'

Expand All @@ -24,45 +24,49 @@ const suggestedQuestionsMap: Record<string, string[]> = {

const suggestedQuestions = computed(() => suggestedQuestionsMap[locale.value] || suggestedQuestionsMap.en)

const chat = isEnabled.value
? new Chat({
messages: [],
transport: new DefaultChatTransport({
api: config.public.assistant.apiPath,
}),
onError: (error: Error) => {
console.error('AI Chat error:', error)
},
})
: null
const {
messages: chatMessages,
status,
sendMessage,
stop,
regenerate,
} = useChat({
messages: [],
transport: new DefaultChatTransport({
api: config.public.assistant.apiPath,
}),
onError: (error: Error) => {
console.error('AI Chat error:', error)
},
})

function handleSubmit() {
if (!input.value.trim() || !chat) return
if (!input.value.trim() || !isEnabled.value) return

chat.sendMessage({ text: input.value })
sendMessage({ text: input.value })
input.value = ''
}

function askQuestion(question: string) {
if (!chat) return
chat.sendMessage({
if (!isEnabled.value) return
sendMessage({
text: question,
})
}

function resetChat() {
if (!chat) return
chat.stop()
chat.messages.length = 0
if (!isEnabled.value) return
stop()
chatMessages.value = []
}
</script>

<template>
<div class="flex flex-col w-full h-96 rounded-lg overflow-hidden">
<div class="flex-1 overflow-y-auto">
<template v-if="isEnabled && chat">
<template v-if="isEnabled">
<div
v-if="chat.messages.length === 0"
v-if="chatMessages.length === 0"
class="h-full flex flex-col items-center justify-center p-4"
>
<div class="size-10 rounded-full bg-primary/10 flex items-center justify-center mb-3">
Expand All @@ -89,9 +93,9 @@ function resetChat() {
<UChatMessages
v-else
should-auto-scroll
:messages="chat.messages"
:messages="chatMessages"
compact
:status="chat.status"
:status="status"
:user="{ ui: { content: 'text-sm' } }"
:ui="{ indicator: '*:bg-accented', root: 'h-auto!' }"
class="px-4 py-4"
Expand Down Expand Up @@ -155,7 +159,7 @@ function resetChat() {
<template #footer>
<div class="ml-auto flex items-center gap-2">
<UButton
v-if="chat?.messages.length"
v-if="chatMessages.length"
icon="i-lucide-trash-2"
color="neutral"
variant="ghost"
Expand All @@ -166,10 +170,10 @@ function resetChat() {

<UChatPromptSubmit
size="xs"
:status="chat?.status || 'ready'"
:disabled="!isEnabled || (chat?.status === 'ready' && !input.trim())"
@stop="chat?.stop()"
@reload="chat?.regenerate()"
:status="status"
:disabled="!isEnabled || (status === 'ready' && !input.trim())"
@stop="stop()"
@reload="regenerate()"
/>
</div>
</template>
Expand Down
4 changes: 2 additions & 2 deletions docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"tailwindcss": "^4.3.2"
},
"devDependencies": {
"@ai-sdk/vue": "^3.0.208",
"ai": "^6.0.208"
"@ai-sdk/vue": "^4.0.29",
"ai": "^7.0.29"
}
}
44 changes: 26 additions & 18 deletions layer/modules/assistant/runtime/components/AssistantPanel.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script setup lang="ts">
import type { ToolUIPart, DynamicToolUIPart } from 'ai'
import { DefaultChatTransport, isToolUIPart, isReasoningUIPart, isTextUIPart } from 'ai'
import { Chat } from '@ai-sdk/vue'
import { useChat } from '@ai-sdk/vue'
import { isPartStreaming, isToolStreaming } from '@nuxt/ui/utils/ai'
import { useDocusI18n } from '../../../../app/composables/useDocusI18n'
import { getToolText, getToolSuffix, getToolIcon } from '../../../../app/utils/assistantTools'
Expand Down Expand Up @@ -34,7 +34,14 @@ const displayPlaceholder = computed(() => t('assistant.placeholder'))

let _skipSync = false

const chat = new Chat({
const {
messages: chatMessages,
status,
error,
sendMessage,
regenerate,
stop,
} = useChat({
messages: messages.value,
transport: new DefaultChatTransport({
api: (config.app?.baseURL.replace(/\/$/, '') || '') + config.public.assistant.apiPath,
Expand All @@ -59,7 +66,7 @@ const chat = new Chat({
},
onFinish: () => {
_skipSync = true
messages.value = [...chat.messages]
messages.value = [...chatMessages.value]
nextTick(() => {
_skipSync = false
})
Expand All @@ -69,9 +76,10 @@ const chat = new Chat({
watch(messages, (newMessages) => {
if (_skipSync) return

chat.messages = newMessages
if (chat.lastMessage?.role === 'user' && chat.status !== 'streaming') {
chat.regenerate()
chatMessages.value = newMessages
const lastMessage = chatMessages.value[chatMessages.value.length - 1]
if (lastMessage?.role === 'user' && status.value !== 'streaming') {
regenerate()
}
})

Expand Down Expand Up @@ -99,7 +107,7 @@ function getToolOutput(part: ToolPart): string | undefined {
function onSubmit() {
if (!input.value.trim()) return

chat.sendMessage({ text: input.value })
sendMessage({ text: input.value })
input.value = ''
}

Expand All @@ -109,11 +117,11 @@ function askQuestion(question: string) {
}

function clearMessages() {
if (chat.status === 'streaming') {
chat.stop()
if (status.value === 'streaming') {
stop()
}
messages.value = []
chat.messages = []
chatMessages.value = []
}

defineShortcuts({
Expand Down Expand Up @@ -183,10 +191,10 @@ defineShortcuts({
}"
>
<UChatMessages
v-if="chat.messages.length"
v-if="chatMessages.length"
should-auto-scroll
:messages="chat.messages"
:status="chat.status"
:messages="chatMessages"
:status="status"
compact
class="px-0 gap-2"
:user="{ ui: { container: 'max-w-full pb-3' } }"
Expand Down Expand Up @@ -281,7 +289,7 @@ defineShortcuts({
<UChatPrompt
ref="promptRef"
v-model="input"
:error="chat.error"
:error="error"
:placeholder="displayPlaceholder"
variant="naked"
size="sm"
Expand All @@ -305,10 +313,10 @@ defineShortcuts({

<UChatPromptSubmit
size="sm"
:status="chat.status"
:disabled="chat.status === 'ready' && !input.trim()"
@stop="chat.stop()"
@reload="chat.regenerate()"
:status="status"
:disabled="status === 'ready' && !input.trim()"
@stop="stop()"
@reload="regenerate()"
/>
</template>
</UChatPrompt>
Expand Down
16 changes: 10 additions & 6 deletions layer/modules/assistant/runtime/server/api/search.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { streamText, convertToModelMessages, stepCountIs, smoothStream } from 'ai'
import { streamText, convertToModelMessages, isStepCount, smoothStream, toUIMessageStream, createUIMessageStreamResponse } from 'ai'
import type { ToolSet } from 'ai'
import { createMCPClient } from '@ai-sdk/mcp'
import type { H3Event } from 'h3'
Expand Down Expand Up @@ -100,12 +100,12 @@ export default defineEventHandler(async (event) => {

const closeMcp = () => event.waitUntil(httpClient.close())

return streamText({
const result = streamText({
model: config.assistant.model,
maxOutputTokens: 8000,
maxRetries: 2,
abortSignal: abortController.signal,
stopWhen: stepCountIs(MAX_STEPS),
stopWhen: isStepCount(MAX_STEPS),
// On the last allowed step, disable tools so the model is forced to
// produce a final text answer instead of stopping mid tool-calling.
prepareStep: ({ stepNumber }) => {
Expand All @@ -116,12 +116,16 @@ export default defineEventHandler(async (event) => {
caching: 'auto',
},
},
system: getSystemPrompt(siteName),
instructions: getSystemPrompt(siteName),
messages: await convertToModelMessages(messages),
tools: mcpTools as ToolSet,
experimental_transform: smoothStream(),
onFinish: closeMcp,
onEnd: closeMcp,
onAbort: closeMcp,
onError: closeMcp,
}).toUIMessageStreamResponse()
})

return createUIMessageStreamResponse({
stream: toUIMessageStream({ stream: result.stream }),
})
})
8 changes: 4 additions & 4 deletions layer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@
"README.md"
],
"dependencies": {
"@ai-sdk/gateway": "^3.0.133",
"@ai-sdk/mcp": "^1.0.52",
"@ai-sdk/vue": "^3.0.208",
"@ai-sdk/gateway": "^4.0.21",
"@ai-sdk/mcp": "^2.0.14",
"@ai-sdk/vue": "^4.0.29",
"@comark/vue": "^0.5.1",
"@iconify-json/lucide": "^1.2.117",
"@iconify-json/simple-icons": "^1.2.90",
Expand All @@ -45,7 +45,7 @@
"@shikijs/themes": "^4.3.1",
"@takumi-rs/core": "^2.2.0",
"@vueuse/core": "^14.3.0",
"ai": "^6.0.208",
"ai": "^7.0.29",
"defu": "^6.1.7",
"exsolve": "^1.1.0",
"git-url-parse": "^16.1.0",
Expand Down
Loading
Loading