Skip to content

Backends próprios ​

Muitos apps já têm um backend que conversa com o LLM: ele guarda sessões, injeta contexto de negócio e roda ferramentas no seu banco de dados. O createSSEProvider conecta o useChat a esse backend sem mudar o protocolo dele.

createSSEProvider ​

ts
import { useChat } from 'vue-llm'
import { createSSEProvider } from 'vue-llm/providers'

const provider = createSSEProvider({
  url: req => `/api/ai-chat/sessions/${(req.providerOptions as { sessionId: string }).sessionId}/stream`,
  headers: async () => ({ Authorization: `Bearer ${await getToken()}` }),
  body: req => ({ message: req.messages.at(-1)?.content }), // session backends only need the new message
  mapEvent: ({ event, json }) => {
    const d = json as any
    switch (event) {
      case 'thinking': return { type: 'reasoning-delta', text: d.text }
      case 'text_delta': return { type: 'text-delta', text: d.text }
      case 'tool_use': return [
        { type: 'tool-call-start', toolCallId: d.id, toolName: d.name },
        { type: 'tool-call-delta', toolCallId: d.id, args: JSON.stringify(d.input) },
        { type: 'tool-call-end', toolCallId: d.id },
      ]
      case 'tool_result': return { type: 'tool-result', toolCallId: d.tool_use_id, result: d.output }
      case 'error': return { type: 'error', error: new Error(d.message) }
      case 'done': return { type: 'finish', finishReason: 'stop' }
    }
  },
})

const sessionId = ref('s1')
const { messages, sendMessage } = useChat({ provider, model: 'default', body: () => ({ sessionId: sessionId.value }) })
OpçãoTipoDescrição
urlstring | (request) => stringEndpoint; pode depender da requisição (providerOptions carrega o body do useChat)
methodstringPadrão POST
headersHeadersInit | () => HeadersInit | Promise<HeadersInit>Estáticos ou por requisição (por exemplo, tokens de auth renovados)
credentialsRequestCredentialsPor exemplo, 'include' para auth via cookie
body(request) => unknownJSON da requisição. Padrão { messages, model }
mapEvent({ event, data, json, id }) => chunk | chunk[] | nullTraduz um evento SSE. Padrão: os dados já são um chunk do vue-llm
id / namestringUsados em LLMAPIError.provider e nas mensagens de erro
  • json é o event.data parseado como JSON (ou undefined); data: [DONE] é ignorado.
  • Retorne null/undefined para pular um evento, ou um array para emitir vários chunks.
  • Se o stream terminar sem um chunk finish, um é adicionado.
  • Respostas fora da faixa 2xx lançam LLMAPIError, então o retryMiddleware também funciona aqui.

Resultados de ferramentas no servidor ​

Quando o backend executa as ferramentas por conta própria, emita um chunk tool-result depois da chamada:

ts
{ type: 'tool-result', toolCallId: 't1', result: { leader: 'Team A' } }
{ type: 'tool-result', toolCallId: 't1', error: 'Database unavailable' }

A parte tool-call correspondente passa para result (ou error), e o useChat não inicia uma ida e volta pelo cliente para ela. Mostre o progresso a partir do state da parte: pending → (running) → result / error.

TIP

O tool-call-end chega antes do tool-result, então um handler onToolCall também vê as chamadas do servidor. Retorne undefined para as ferramentas que o cliente não trata — o resultado do servidor vai preenchê-las.

Com vue-llm no servidor ​

Se o seu backend é JavaScript, o createLLMHandler pode executar as ferramentas para você — veja Ferramentas no servidor. O stream dele já fala o protocolo do vue-llm, então o cliente só precisa do createProxyProvider.

Distribuído sob a licença MIT.