All checks were successful
Deploy / deploy (push) Successful in 5m44s
Шаг 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) — отдельно.
204 lines
8.1 KiB
TypeScript
204 lines
8.1 KiB
TypeScript
/**
|
||
* 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'],
|
||
},
|
||
},
|
||
]
|