Если вы делали AI-фичу на Vue, то, скорее всего, сталкивались с одним и тем же: большинство готовых инструментов для LLM-интерфейсов написаны с прицелом на React, а во Vue их приходится адаптировать или писать стриминг, tool calling и состояние чата вручную. Я сделал vue-llm — open-source библиотеку, которая закрывает это для Vue 3, Nuxt и даже Vue 2.7.
Сразу ссылки, чтобы можно было пощупать:
– Песочница без API-ключа (модель скачивается и работает прямо в браузере на WebGPU): https://vue-llm.js.org/ru/playground
– Документация на русском: https://vue-llm.js.org/ru/
– GitHub: https://github.com/dadashi44/vue-llm
– npm: npm i vue-llm
Ниже — что умеет библиотека и как это выглядит в коде.
Чат за десять строк
Основа — композабл useChat. Он стримит ответ модели токен за токеном в реактивное состояние: сообщения, статус, ошибка, отмена, перегенерация.
<script setup lang="ts">
import { useChat } from 'vue-llm'
import { createProxyProvider } from 'vue-llm/server'
const { messages, input, handleSubmit, status, stop } = useChat({
provider: createProxyProvider('/api/chat'),
model: 'gpt-4o',
})
</script>
<template>
<div v-for="m in messages" :key="m.id">
<b>{{ m.role }}:</b>
<template v-for="(part, i) in m.parts" :key="i">
<span v-if="part.type === 'text'">{{ part.text }}</span>
</template>
</div>
<form @submit.prevent="handleSubmit">
<input v-model="input">
<button v-if="status === 'streaming'" type="button" @click="stop">Стоп</button>
</form>
</template>
Сообщения состоят из «частей» (parts): текст, рассуждения модели, вызовы инструментов, файлы, источники. Поэтому один и тот же шаблон показывает и обычный ответ, и reasoning у Claude или Qwen3, и вызов tool.
API-ключ при этом остаётся на сервере. Серверная часть — обычный Web-стандартный обработчик Request → Response, который работает в Nuxt/Nitro, Node, Bun, Deno и edge-рантаймах:
// server/api/chat.post.ts (Nuxt)
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',
rateLimit: { maxRequests: 30, interval: 60_000 },
})
export default defineEventHandler(event => handler(toWebRequest(event)))
Провайдеры из коробки: OpenAI (и любой OpenAI-совместимый API), Anthropic, Google Gemini, Ollama. Если у вас уже есть свой бэкенд со стримингом, createSSEProvider подключает useChat к нему: вы описываете, как ваши SSE-события превращаются в куски ответа, и всё остальное работает как есть.
Generative UI: модель отвечает компонентами
Самая интересная для меня часть. Когда модель вызывает инструмент, вместо текста можно показать ваш Vue-компонент. Аргументы стримятся в компонент по мере генерации, то есть карточка начинает заполняться ещё до того, как модель дописала вызов.
import { defineTool } from 'vue-llm'
export const getWeather = defineTool<{ city: string }, Weather>({
name: 'get_weather',
description: 'Погода в городе',
parameters: { type: 'object', properties: { city: { type: 'string' } }, required: ['city'] },
execute: ({ city }) => fetch(/api/weather?city=${city}).then(r => r.json()),
})
<MessageParts
v-for="m in messages"
:key="m.id"
:message="m"
:components="{ get_weather: WeatherCard }"
@tool-result="addToolResult"
/>
Инструмент с execute выполняется сам. Инструмент без execute ставит чат на паузу и ждёт ответа из интерфейса — так делается human-in-the-loop: модель предлагает забронировать столик, пользователь жмёт «Подтвердить» в карточке, addToolResult передаёт ответ, и разговор продолжается. Живое демо (работает без сети): https://vue-llm.js.org/ru/guide/generative-ui
Под капотом для этого пришлось написать парсер «недописанного» JSON: {"city":"Мос превращается в { city: 'Мос' }, а незаконченные ключи отбрасываются до тех пор, пока не допишутся.
Модели прямо в браузере
createWebLLM запускает открытые модели (Llama 3.2, Qwen, SmolLM, Phi) на WebGPU через WebLLM. Никакого сервера и ключа, данные не покидают устройство.
import { CreateMLCEngine } from '@mlc-ai/web-llm'
import { createWebLLM } from 'vue-llm/providers'
const ai = createWebLLM({
createEngine: CreateMLCEngine,
onProgress: ({ progress }) => console.log(${Math.round(progress * 100)}%),
})
useChat({ provider: ai, model: 'Llama-3.2-1B-Instruct-q4f16_1-MLC' })
Это тот же useChat, что и с OpenAI, — меняется только провайдер. Можно, например, давать пользователям без WebGPU серверную модель, а остальным — локальную. Первая загрузка модели занимает от 400 МБ, дальше она берётся из кеша браузера за секунду-две.
Готовый интерфейс чата в стиле shadcn
Писать окно чата с нуля утомительно: автоскролл, который не дёргает пользователя, листающего вверх; Enter для отправки, который не срабатывает при наборе на китайском или японском; вставка картинок из буфера; drag-and-drop файлов. Поэтому библиотека устроена гибридно:
– в npm лежит headless-логика: usePromptInput и useAutoScroll;
– готовые компоненты копируются в ваш проект как в shadcn — одной командой, после чего код ваш:
npx shadcn-vue@latest add https://vue-llm.js.org/r/chat.json
Компоненты используют токены темы shadcn, поэтому сразу выглядят как остальной ваш интерфейс, включая тёмную тему.
Отладка в Vue DevTools
vue-llm/devtools добавляет в Vue DevTools вкладку LLM: список всех useChat, токены, время до первого токена, скорость генерации, вызовы инструментов и таймлайн каждого запроса. Без плагина библиотека ничего не трассирует и не тратит на это ресурсы.
Nuxt
Модуль подключается одной командой:
npx nuxi module add vue-llm
Он делает auto-import композаблов и провайдеров, регистрирует компоненты и в dev-режиме сам подключает вкладку в DevTools. Проверено на Nuxt 3 и 4.
Почему не взять готовый AI SDK
Хороший вопрос, и я сам с него начинал. vue-llm сделан Vue-first: Nuxt-модуль, Vue-компоненты для Generative UI, интеграция с Vue DevTools, провайдер для моделей в браузере, адаптер под существующий SSE-бэкенд и поддержка Vue 2.7 для композаблов (у многих легаси-админок до сих пор Vue 2). Зависимостей в рантайме нет, кроме самого Vue.
Что дальше
Библиотека под MIT, версия 1.3, покрыта тестами на Vue 3 и Vue 2.7. В планах — MCP-клиент (подключать MCP-серверы как инструменты), ответы с цитатами для RAG и голосовой ввод.
Буду рад обратной связи: что неудобно, чего не хватает, где документация непонятна. Issues и PR приветствуются, а если проект пригодится — поставьте звёздочку на GitHub, это правда помогает его продвигать.
– Песочница: https://vue-llm.js.org/ru/playground
– Документация: https://vue-llm.js.org/ru/
– GitHub: https://github.com/dadashi44/vue-llm
Автор: dadash_i


