Skip to content

Сервер и прокси ​

vue-llm/server превращает любой провайдер в стриминговый HTTP-эндпоинт и даёт браузеру провайдер, который с ним общается, — API-ключи никогда не покидают сервер.

createLLMHandler ​

Возвращает обработчик (request: Request) => Promise<Response> на веб-стандартах, поэтому он работает в Node (через ваш фреймворк), Bun, Deno, Cloudflare Workers и Nuxt/Nitro.

ts
import { createLLMHandler } from 'vue-llm/server'
import { createAnthropic } from 'vue-llm/providers'

const handler = createLLMHandler({
  provider: createAnthropic({ apiKey: process.env.ANTHROPIC_API_KEY! }),
  model: 'claude-sonnet-4-5',
  system: 'You are a helpful assistant.',
  maxMessages: 50,
  rateLimit: { maxRequests: 30, interval: 60_000 },
  onRequest: body => ({ ...body, model: 'claude-sonnet-4-5' }), // фиксируем модель
})

// Bun
Bun.serve({ port: 3000, fetch: req => new URL(req.url).pathname === '/api/chat' ? handler(req) : new Response('Not found', { status: 404 }) })
ОпцияТипОписание
providerLLMProviderОбязательна
modelstringМодель по умолчанию (тело запроса может её переопределить)
systemstringСистемный промпт по умолчанию (тело запроса может его переопределить)
toolsServerToolDefinition[]Инструменты, доступные модели; инструменты с execute выполняются на сервере
maxToolRoundtripsnumberМаксимум раундов серверных инструментов на запрос (по умолчанию 5)
maxMessagesnumberОставлять только последние N сообщений
rateLimit{ maxRequests, interval?, key? }Лимит на клиента, отвечает 429 + Retry-After
onRequest(body) => bodyПроверить или переписать тело запроса

Тело запроса — { messages: ChatMessage[], model?: string, system?: string }. Ответ приходит как Server-Sent Events, по одному ChatStreamChunk на событие, и завершается data: [DONE]. Ошибки до начала стриминга возвращаются как JSON со статусом 400, 405, 429 или 500.

Серверные инструменты ​

Добавьте инструменту функцию execute — и обработчик выполнит её на сервере, вернёт результат модели и продолжит стриминг. Браузер никогда не увидит вашу базу данных или API-ключи:

ts
const handler = createLLMHandler({
  provider,
  tools: [
    {
      name: 'get_standings',
      description: 'Current standings of a tournament',
      parameters: { type: 'object', properties: { tournament: { type: 'number' } }, required: ['tournament'] },
      async execute(args, { request }) {
        const { tournament } = args as { tournament: number }
        await assertCanRead(request, tournament)
        return db.standings(tournament)
      },
    },
  ],
})

Клиент получает вызов инструмента, а за ним чанк tool-result, поэтому часть tool-call сразу переходит в result без лишнего обращения клиента. Выброшенная ошибка превращается в { type: 'tool-result', error } и тоже передаётся модели, которая может исправиться или объяснить, что произошло.

Инструменты без execute остаются клиенту: поток завершается с finishReason: 'tool-calls', и useChat как обычно вызывает onToolCall. Серверные и клиентские инструменты можно сочетать в одном запросе.

createProxyProvider ​

ts
import { useChat } from 'vue-llm'
import { createProxyProvider } from 'vue-llm/server'

const { messages, sendMessage } = useChat({
  provider: createProxyProvider('/api/chat'),
  model: 'claude-sonnet-4-5',
})

Вложения, вызовы инструментов, результаты серверных инструментов и рассуждения проходят через прокси без изменений. Это тонкая обёртка над createSSEProvider. Ответы со статусом не из 2xx становятся LLMAPIError с provider: 'proxy'.

streamToSSE ​

Низкоуровневый хелпер, если вы формируете ответ сами:

ts
import { streamToSSE } from 'vue-llm/server'

const { stream } = await provider.chat({ model, messages })
return new Response(streamToSSE(stream), { headers: { 'Content-Type': 'text/event-stream' } })

Распространяется по лицензии MIT.