feat: switch from Anthropic to Groq API (llama-3.3-70b-versatile)
All checks were successful
Deploy / deploy (push) Successful in 2m47s
All checks were successful
Deploy / deploy (push) Successful in 2m47s
- route.ts: replace @anthropic-ai/sdk with groq-sdk, rewrite chat loop - voice-tool-schemas.ts: convert from Anthropic format to OpenAI/Groq function tools - voice-history.ts: extend HistoryMessage type to include tool role, simplify cache stubs No prompt caching (Groq does not support it), tool calling preserved.
This commit is contained in:
@@ -1,203 +1,248 @@
|
||||
/**
|
||||
* Tool schemas для Anthropic API. Порт TOOL_SCHEMAS из satellite/tools.py.
|
||||
* Формат — Anthropic native tools (name + description + input_schema).
|
||||
* Tool schemas для Groq API (OpenAI-compatible format).
|
||||
* Было: Anthropic.Tool[] (input_schema) → Стало: Groq/OpenAI function tools (parameters).
|
||||
*/
|
||||
import type Anthropic from '@anthropic-ai/sdk'
|
||||
|
||||
export const TOOL_SCHEMAS: Anthropic.Tool[] = [
|
||||
export interface GroqTool {
|
||||
type: 'function'
|
||||
function: {
|
||||
name: string
|
||||
description: string
|
||||
parameters: {
|
||||
type: string
|
||||
properties: Record<string, any>
|
||||
required?: string[]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const TOOL_SCHEMAS: GroqTool[] = [
|
||||
{
|
||||
name: 'get_weather',
|
||||
description:
|
||||
'Получить текущую погоду и короткий прогноз для города. ' +
|
||||
'Для вопросов вроде «какая сегодня погода», «холодно ли на улице», «нужен ли зонт». ' +
|
||||
'По умолчанию — Санкт-Петербург.',
|
||||
input_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
city: {
|
||||
type: 'string',
|
||||
description:
|
||||
'Город на русском или шорткод (spb, msk, sochi, ekb, kzn, nsk, krd). ' +
|
||||
'По умолчанию Санкт-Петербург.',
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'get_weather',
|
||||
description:
|
||||
'Получить текущую погоду и короткий прогноз для города. ' +
|
||||
'Для вопросов вроде «какая сегодня погода», «холодно ли на улице», «нужен ли зонт». ' +
|
||||
'По умолчанию — Санкт-Петербург.',
|
||||
parameters: {
|
||||
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». Пусто = все маршруты.',
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'get_transport',
|
||||
description:
|
||||
'Расписание ближайших трамваев на остановке Ул. Антонова-Овсеенко. ' +
|
||||
'Для вопросов «когда следующий 23-й», «что ближайшее в центр», «пора идти на остановку».',
|
||||
parameters: {
|
||||
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 (текущий месяц)',
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'get_today_events',
|
||||
description:
|
||||
'События из календаря (Даниил + Света). Вернёт id события, title, start, end, ' +
|
||||
'owner («daniil» или «sveta»). ВАЖНО: для update_event / delete_event сначала ' +
|
||||
'вызывай этот tool чтобы получить event_id.',
|
||||
parameters: {
|
||||
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: 'Чей это календарь — Даниила или Светы',
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'create_event',
|
||||
description:
|
||||
'Создать событие в Google Calendar. ВАЖНО: параметр owner обязателен. ' +
|
||||
'Если пользователь не сказал чей это календарь — СПРОСИ у него ' +
|
||||
'(«в твой календарь или в Светин?») и только потом вызывай tool. Не угадывай.',
|
||||
parameters: {
|
||||
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'],
|
||||
},
|
||||
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)',
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'update_event',
|
||||
description:
|
||||
'Изменить существующее событие. Сначала обязательно вызови get_today_events ' +
|
||||
'чтобы получить event_id и owner нужного события. Передавай только те поля ' +
|
||||
'которые меняешь.',
|
||||
parameters: {
|
||||
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' },
|
||||
},
|
||||
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'],
|
||||
},
|
||||
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'] },
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'delete_event',
|
||||
description:
|
||||
'Удалить событие из календаря. Сначала вызови get_today_events чтобы найти ' +
|
||||
'event_id и определить owner. Подтверди удаление с пользователем если событие ' +
|
||||
'важное (встреча, врач, работа).',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
event_id: { type: 'string' },
|
||||
owner: { type: 'string', enum: ['daniil', 'sveta'] },
|
||||
},
|
||||
required: ['event_id', 'owner'],
|
||||
},
|
||||
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: 'Короткое название таймера (например «Чайник», «Паста»)',
|
||||
},
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'get_notes',
|
||||
description:
|
||||
'Список заметок и списков покупок с планшета. Для «что мне купить», ' +
|
||||
'«что в списке», «какие записи».',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {},
|
||||
},
|
||||
required: ['seconds', 'label'],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'cancel_timer',
|
||||
description:
|
||||
'Отменить активный таймер по его названию. Для «отмени таймер чайник», ' +
|
||||
'«убери таймер пасты», «останови отсчёт».',
|
||||
input_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
label: {
|
||||
type: 'string',
|
||||
description: 'Название таймера (примерное совпадение — можно частично).',
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'set_timer',
|
||||
description:
|
||||
'Запустить таймер на планшете. Показывает обратный отсчёт с названием и звенит ' +
|
||||
'по окончании. Используй для «поставь таймер на 10 минут», «напомни через час», ' +
|
||||
'«засеки 5 минут для чайника».',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
seconds: {
|
||||
type: 'integer',
|
||||
description: 'Длительность в секундах (1..86400)',
|
||||
minimum: 1,
|
||||
maximum: 86400,
|
||||
},
|
||||
label: {
|
||||
type: 'string',
|
||||
description: 'Короткое название таймера (например «Чайник», «Паста»)',
|
||||
},
|
||||
},
|
||||
required: ['seconds', 'label'],
|
||||
},
|
||||
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 минута.',
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'cancel_timer',
|
||||
description:
|
||||
'Отменить активный таймер по его названию. Для «отмени таймер чайник», ' +
|
||||
'«убери таймер пасты», «останови отсчёт».',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
label: {
|
||||
type: 'string',
|
||||
description: 'Название таймера (примерное совпадение — можно частично).',
|
||||
},
|
||||
},
|
||||
required: ['label'],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'adjust_timer',
|
||||
description:
|
||||
'Изменить оставшееся время таймера. Для «добавь ещё 5 минут», «убавь на минуту», ' +
|
||||
'«накинь времени чайнику». Положительный delta_seconds = добавить, отрицательный = уменьшить.',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
label: {
|
||||
type: 'string',
|
||||
description: 'Название таймера для которого меняем время.',
|
||||
},
|
||||
delta_seconds: {
|
||||
type: 'integer',
|
||||
description: 'Секунды (+ добавить, - уменьшить). Например 300 = +5 минут, -60 = -1 минута.',
|
||||
},
|
||||
},
|
||||
required: ['label', 'delta_seconds'],
|
||||
},
|
||||
required: ['label', 'delta_seconds'],
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user