Почему нетривиально: самописный aiProviderService размазывает особенности провайдеров (openai/anthropic/google/deepseek/smarty_custom) по ручным адаптерам, а smarty_custom (per-bot endpoint с опциональным токеном) легко сломать при унификации; этот скилл сводит провайдеры к единому интерфейсу AI SDK и сохраняет серверную валидацию tool-call, чтобы стриминг не открыл дыру в изоляции тенантов.
Когда использовать: правки LLM-вызовов в smarty-dialog, адаптеров провайдеров, agent loop, стриминга или tool-calling.
Целевой LLM-слой Smarty (см. docs/MIGRATION_AUDIT.md §9.2, Фаза 6). Заменяет самописный aiProviderService + providers/*.js на единый интерфейс Vercel AI SDK. Главный выигрыш — сквозной стриминг (предпосылка к voice, docs/VOICE_MICROSERVICE.md).
WARN: версия: целиться в AI SDK 6 (стабилен с конца 2025; breaking changes против v5 в useChat/tool-call lifecycle/provider contracts/wire-format). Перед кодом — сверять API с текущими доками AI SDK; описание ниже фиксирует паттерн миграции, а не конкретные сигнатуры.
| Сейчас | Файл | -> AI SDK |
|---|---|---|
aiProviderService диспетчер |
smarty-dialog/services/aiProviderService.js |
generateText / streamText + выбор провайдера |
| Адаптеры провайдеров | smarty-dialog/lib/employeeBots/providers/{openai,anthropic,google,deepseek,smarty,smarty_custom}.js |
официальные provider-пакеты + один кастом-провайдер |
| Agent loop (tool-call) | agentLoop.js (MAX_ITERATIONS=12) |
tool-calling AI SDK; LangGraph — опц., только если граф ветвистый |
| Ручной парсинг tool_calls | executeTools() |
tools + toolChoice, валидация на исполнении остаётся |
tools/executor.js (_wsId-guard, permissionChecker) — остаётся как есть, AI SDK отвечает только за транспорт к модели. инъекция не должна пересечь границу тенанта (R1).baseURL.smarty_custom — ключевой кейс. Это OpenAI-compatible endpoint с per-bot URL/headers и опциональным API-token (см. api-rest/routes/workspace/ai-models.js). Маппится на кастомный инстанс openai-провайдера с baseURL/headers из конфига бота; токен может отсутствовать — не падать на пустом ключе.streamText -> стрим токенов в dialog (и далее в voice TTS). Текущая реализация почти без стриминга — это и есть цель.scripts/seed-buyer-seller-bots.js) до переключения.providers/*.js: маппинг botCfg.provider -> инстанс провайдера AI SDK. openai/anthropic/google — официальные пакеты; deepseek — openai-провайдер с baseURL.smarty_custom — кастомный инстанс openai-совместимого провайдера: baseURL, headers и apiKey берутся из per-bot конфига; при отсутствии токена использовать плейсхолдер, не падать на пустом ключе.streamText (а не generateText) с messages, tools из tools/definitions.js, и политикой остановки (toolChoice/stopWhen), эквивалентной текущему MAX_ITERATIONS.tools/executor.js — AI SDK отвечает только за транспорт к модели; _wsId-guard и permissionChecker не выносить в SDK-слой.smarty_custom работает с пустым API-token и per-bot baseURL/headers._wsId-guard и permissionChecker по-прежнему режут чужой scope (R1).MAX_ITERATIONS/политика остановки сохранена (или заменена осознанно).docs/MIGRATION_AUDIT.md (Фаза 6, §9.2) · docs/VOICE_MICROSERVICE.md (стриминг — блокер №0) · docs/AGENT_SKILLS_ADOPTION.md (этот скилл закрывает гэп «Vercel AI SDK») · CLAUDE.md (инварианты).