来源

一个允许用户查看用于生成响应的来源或引用的组件。

Sources 组件允许用户查看用于生成响应的来源或引用。

使用 CLI 安装

AI Elements Vue
shadcn-vue CLI
npx ai-elements-vue@latest add sources

手动安装

将以下代码复制并粘贴到同一文件夹中。

Source.vue
Sources.vue
SourcesTrigger.vue
SourcesContent.vue
index.ts
<script setup lang="ts">
import type { HTMLAttributes } from 'vue'
import { cn } from '@repo/shadcn-vue/lib/utils'
import { BookIcon } from 'lucide-vue-next'

const props = defineProps<{
  href: string
  title: string
  class?: HTMLAttributes['class']
}>()
</script>

<template>
  <a
    :class="cn('flex items-center gap-2', props.class)"
    :href="props.href"
    rel="noreferrer"
    target="_blank"
  >
    <slot>
      <BookIcon class="h-4 w-4" />
      <span class="block font-medium">{{ props.title }}</span>
    </slot>
  </a>
</template>

与 AI SDK 一起使用

使用 Perplexity Sonar 构建简单的网络搜索代理。

将以下组件添加到你的前端:

pages/index.vue
<script setup lang="ts">
import { useChat } from '@ai-sdk/vue'
import { DefaultChatTransport } from 'ai'
import { ref } from 'vue'

import {
  Conversation,
  ConversationContent,
  ConversationScrollButton,
} from '@/components/ai-elements/conversation'
import { Message, MessageContent } from '@/components/ai-elements/message'
import {
  PromptInput,
  PromptInputSubmit,
  PromptInputTextarea,
} from '@/components/ai-elements/prompt-input'
import { Response } from '@/components/ai-elements/response'
import {
  Source,
  Sources,
  SourcesContent,
  SourcesTrigger,
} from '@/components/ai-elements/sources'

const input = ref('')

const { messages, sendMessage, status } = useChat({
  transport: new DefaultChatTransport({
    api: '/api/sources',
  }),
})

function handleSubmit() {
  if (input.value.trim()) {
    sendMessage({ text: input.value })
    input.value = ''
  }
}

function getSourceParts(message: any) {
  return message.parts.filter((part: any) => part.type === 'source-url')
}

function getTextParts(message: any) {
  return message.parts.filter((part: any) => part.type === 'text')
}
</script>

<template>
  <div
    class="max-w-4xl mx-auto p-6 relative size-full rounded-lg border h-[600px]"
  >
    <div class="flex flex-col h-full">
      <div class="flex-1 overflow-auto mb-4">
        <Conversation>
          <ConversationContent>
            <div v-for="message in messages" :key="message.id">
              <Sources v-if="message.role === 'assistant'">
                <SourcesTrigger :count="getSourceParts(message).length" />
                <SourcesContent>
                  <template
                    v-for="(part, i) in getSourceParts(message)"
                    :key="`${message.id}-${i}`"
                  >
                    <Source :href="part.url" :title="part.url" />
                  </template>
                </SourcesContent>
              </Sources>

              <Message :from="message.role">
                <MessageContent>
                  <template
                    v-for="(part, i) in getTextParts(message)"
                    :key="`${message.id}-${i}`"
                  >
                    <Response>{{ part.text }}</Response>
                  </template>
                </MessageContent>
              </Message>
            </div>
          </ConversationContent>
          <ConversationScrollButton />
        </Conversation>
      </div>

      <PromptInput
        class="mt-4 w-full max-w-2xl mx-auto relative"
        @submit.prevent="handleSubmit"
      >
        <PromptInputTextarea
          v-model="input"
          placeholder="Ask a question and search the..."
          class="pr-12"
        />
        <PromptInputSubmit
          :status="status === 'streaming' ? 'streaming' : 'ready'"
          :disabled="!input.trim()"
          class="absolute bottom-1 right-1"
        />
      </PromptInput>
    </div>
  </div>
</template>

将以下路由添加到你的后端:

server/api/chat/route.ts
import type { UIMessage } from 'ai'
import { perplexity } from '@ai-sdk/perplexity'
import { convertToModelMessages, streamText } from 'ai'

export const maxDuration = 30

export default defineEventHandler(async (event) => {
  const body = await readBody<{ messages: UIMessage[] }>(event)

  const result = streamText({
    model: perplexity('sonar'),
    system:
      'You are a helpful assistant. Keep your responses short (< 100 words) unless you are asked for more details. ALWAYS USE SEARCH.',
    messages: convertToModelMessages(body.messages),
  })

  return result.toUIMessageStreamResponse({
    sendSources: true,
  })
})

特性

  • 可折叠组件,允许用户查看用于生成响应的来源或引用
  • 可自定义的触发器和内容组件
  • 支持自定义来源或引用
  • 具有移动友好控件的响应式设计
  • 具有可自定义主题的简洁现代样式

示例

自定义渲染

Props

<Source />

hrefstring
''
来源的 URL。
titlestring
''
来源的标题。
classstring
''
应用于组件的额外 CSS 类。

<Sources />

classstring
''
应用于组件的额外 CSS 类。

<SourcesTrigger />

countnumber
0
来源或引用的数量。
classstring
''
应用于组件的额外 CSS 类。

<SourcesContent />

classstring
''
应用于组件的额外 CSS 类。