Files
smart-home-tablet/lib/voice-tool-schemas.ts
Cosmo eeac2eefb3
All checks were successful
Deploy / deploy (push) Successful in 5m44s
feat(voice): server-side LLM/STT — porting Python satellite into tablet
Шаг 1 миграции голосового стека из home-voice-assistant в сам tablet:

- /api/voice/chat — Claude Haiku 4.5 с tool-loop (max 4 раунда), prompt
  caching на system + старой истории, история в /data/voice-history/.
  Эмитит command/response/error в voice-bus → орб моргает как раньше.
- /api/voice/stt — Groq whisper-large-v3-turbo, multipart или raw audio.
- lib/voice-text.ts — порт clean_for_speech (без pymorphy3, время в
  именительном падеже) и strip_fillers + RESET_PATTERNS.
- lib/voice-executors.ts — tool executors через loopback fetch на
  существующие /api/voice/tools/* и /api/voice/timer.
- Поддержка ANTHROPIC_PROXY/GROQ_PROXY (fallback на HTTPS_PROXY).

После деплоя нужны GROQ_API_KEY и ANTHROPIC_API_KEY в tablet.env.
Шаги 2 (push-to-talk в браузере) и 3 (wake-word) — отдельно.
2026-04-27 08:24:19 +00:00

204 lines
8.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Tool schemas для Anthropic API. Порт TOOL_SCHEMAS из satellite/tools.py.
* Формат — Anthropic native tools (name + description + input_schema).
*/
import type Anthropic from '@anthropic-ai/sdk'
export const TOOL_SCHEMAS: Anthropic.Tool[] = [
{
name: 'get_weather',
description:
'Получить текущую погоду и короткий прогноз для города. ' +
'Для вопросов вроде «какая сегодня погода», «холодно ли на улице», «нужен ли зонт». ' +
'По умолчанию — Санкт-Петербург.',
input_schema: {
type: 'object',
properties: {
city: {
type: 'string',
description:
'Город на русском или шорткод (spb, msk, sochi, ekb, kzn, nsk, krd). ' +
'По умолчанию Санкт-Петербург.',
},
},
},
},
{
name: 'get_transport',
description:
'Расписание ближайших трамваев на остановке Ул. Антонова-Овсеенко. ' +
'Для вопросов «когда следующий 23-й», «что ближайшее в центр», «пора идти на остановку».',
input_schema: {
type: 'object',
properties: {
direction: {
type: 'string',
enum: ['to_center', 'from_center', 'all'],
description:
'to_center = в центр (к Новочеркасской), ' +
'from_center = от центра (к Большевиков), all = оба направления',
},
routes: {
type: 'string',
description:
'Фильтр маршрутов через запятую, например «23» или «23,27». Пусто = все маршруты.',
},
},
},
},
{
name: 'get_today_events',
description:
'События из календаря (Даниил + Света). Вернёт id события, title, start, end, ' +
'owner («daniil» или «sveta»). ВАЖНО: для update_event / delete_event сначала ' +
'вызывай этот tool чтобы получить event_id.',
input_schema: {
type: 'object',
properties: {
range: {
type: 'string',
enum: ['today', 'week', 'month'],
description: 'today (по умолчанию), week (7 дней) или month (текущий месяц)',
},
},
},
},
{
name: 'create_event',
description:
'Создать событие в Google Calendar. ВАЖНО: параметр owner обязателен. ' +
'Если пользователь не сказал чей это календарь — СПРОСИ у него ' +
'(«в твой календарь или в Светин?») и только потом вызывай tool. Не угадывай.',
input_schema: {
type: 'object',
properties: {
title: { type: 'string', description: 'Название события' },
date: { type: 'string', description: 'Дата в формате YYYY-MM-DD' },
start_time: {
type: 'string',
description: 'Время начала в формате HH:MM (24-часовой). Обязательно если all_day=false.',
},
end_time: {
type: 'string',
description: 'Время окончания в формате HH:MM. По умолчанию start_time + 1 час.',
},
all_day: {
type: 'boolean',
description: 'Событие на весь день без времени. По умолчанию false.',
},
owner: {
type: 'string',
enum: ['daniil', 'sveta'],
description: 'Чей это календарь — Даниила или Светы',
},
},
required: ['title', 'date', 'owner'],
},
},
{
name: 'update_event',
description:
'Изменить существующее событие. Сначала обязательно вызови get_today_events ' +
'чтобы получить event_id и owner нужного события. Передавай только те поля ' +
'которые меняешь.',
input_schema: {
type: 'object',
properties: {
event_id: { type: 'string' },
owner: {
type: 'string',
enum: ['daniil', 'sveta'],
description: 'Чей календарь (из get_today_events)',
},
title: { type: 'string' },
date: { type: 'string', description: 'YYYY-MM-DD' },
start_time: { type: 'string', description: 'HH:MM' },
end_time: { type: 'string', description: 'HH:MM' },
all_day: { type: 'boolean' },
},
required: ['event_id', 'owner'],
},
},
{
name: 'delete_event',
description:
'Удалить событие из календаря. Сначала вызови get_today_events чтобы найти ' +
'event_id и определить owner. Подтверди удаление с пользователем если событие ' +
'важное (встреча, врач, работа).',
input_schema: {
type: 'object',
properties: {
event_id: { type: 'string' },
owner: { type: 'string', enum: ['daniil', 'sveta'] },
},
required: ['event_id', 'owner'],
},
},
{
name: 'get_notes',
description:
'Список заметок и списков покупок с планшета. Для «что мне купить», ' +
'«что в списке», «какие записи».',
input_schema: { type: 'object', properties: {} },
},
{
name: 'set_timer',
description:
'Запустить таймер на планшете. Показывает обратный отсчёт с названием и звенит ' +
'по окончании. Используй для «поставь таймер на 10 минут», «напомни через час», ' +
'«засеки 5 минут для чайника».',
input_schema: {
type: 'object',
properties: {
seconds: {
type: 'integer',
description: 'Длительность в секундах (1..86400)',
minimum: 1,
maximum: 86400,
},
label: {
type: 'string',
description: 'Короткое название таймера (например «Чайник», «Паста»)',
},
},
required: ['seconds', 'label'],
},
},
{
name: 'cancel_timer',
description:
'Отменить активный таймер по его названию. Для «отмени таймер чайник», ' +
'«убери таймер пасты», «останови отсчёт».',
input_schema: {
type: 'object',
properties: {
label: {
type: 'string',
description: 'Название таймера (примерное совпадение — можно частично).',
},
},
required: ['label'],
},
},
{
name: 'adjust_timer',
description:
'Изменить оставшееся время таймера. Для «добавь ещё 5 минут», «убавь на минуту», ' +
'«накинь времени чайнику». Положительный delta_seconds = добавить, отрицательный = уменьшить.',
input_schema: {
type: 'object',
properties: {
label: {
type: 'string',
description: 'Название таймера для которого меняем время.',
},
delta_seconds: {
type: 'integer',
description: 'Секунды (+ добавить, - уменьшить). Например 300 = +5 минут, -60 = -1 минута.',
},
},
required: ['label', 'delta_seconds'],
},
},
]