AI-стартер (OpenAI-совместимый стриминг)
Стартер включает готовый потоковый чат к любому OpenAI-совместимому эндпоинту: сам OpenAI, локальный сервер (vLLM, Ollama в режиме OpenAI), OpenRouter, Together или российский прокси — всё настраивается переменными окружения, код провайдера менять не нужно.
Из чего состоит
| Файл | Назначение |
|---|---|
src/lib/ai/openai.ts | OpenAI-совместимый клиент: сборка запроса, разбор SSE, streamChat (async-генератор токенов). Транспорт — нативный fetch, fetchImpl инъектируется (мок в тестах). |
src/app/api/ai/chat/route.ts | POST-роут, отдаёт text/event-stream (SSE через ReadableStream). |
src/components/ai/chat.tsx | Клиентский UI на DaisyUI, читает поток и дописывает ответ по токенам. |
src/app/[locale]/ai/page.tsx | Демо-страница /ai (и /en/ai). |
Переменные окружения
AI_API_KEY="" # без ключа роут отвечает 503, демо показывает подсказку
AI_BASE_URL="https://api.openai.com/v1" # любой OpenAI-совместимый базовый URL
AI_MODEL="gpt-4o-mini" # id модели у выбранного провайдера
Значения проходят валидацию в src/lib/env.ts (Zod). Ключ опционален, чтобы pnpm build,
тесты и launchkit init работали без него: без AI_API_KEY функция resolveAiConfig бросает
понятную ошибку, а роут возвращает 503.
Формат потока (SSE)
Роут /api/ai/chat принимает { messages: [{ role, content }] } и отдаёт события:
data: {"token":"..."} # инкремент текста
data: {"error":"..."} # ошибка в середине потока
data: [DONE] # конец потока
Ручная проверка с реальным ключом
Тесты замоканы (никакой сети и ключа). Чтобы проверить вживую:
echo 'AI_API_KEY="sk-..."' >> .env # ключ вашего провайдера
# при необходимости: AI_BASE_URL="https://ваш-эндпоинт/v1", AI_MODEL="..."
pnpm dev
# откройте http://localhost:3000/ai и отправьте сообщение — ответ придёт по токенам
Проверить роут напрямую:
curl -N -X POST http://localhost:3000/api/ai/chat \
-H 'content-type: application/json' \
-d '{"messages":[{"role":"user","content":"Привет!"}]}'
Тесты
src/lib/ai/openai.test.ts— сборка запроса, разбор SSE, стриминг на мок-fetch(сборка ответа из потока, разрыв события между чанками, ошибки статуса/пустого тела).src/app/api/ai/chat/route.test.ts— роут на замоканномstreamChat: SSE-ответ собирается в полный ответ,[DONE]в конце,400на некорректном теле,503без ключа, кадр ошибки при сбое.
Дальше
- Локализация — демо-страница доступна и на
/ai, и на/en/ai.