voice: усиление умного дома — контекст, промпт, новые tools
All checks were successful
Deploy / deploy (push) Successful in 1m52s
All checks were successful
Deploy / deploy (push) Successful in 1m52s
- промпт: перечислены ВСЕ 15+ tools, разрешено уверенно управлять домом - route.ts: живой контекст (время + снимок устройств из HA) в системный промпт для LLM-пути (defensive, с таймаутом) - новые tools: control_climate (кондей/термостат), control_tv, set_scene (night/movie/morning/away), all_off, ha_service (универсальный HA с whitelist) - env: CLIMATE_ENTITY, TV_ENTITY (см. RUNBOOK)
This commit is contained in:
54
lib/tools/ha-generic.ts
Normal file
54
lib/tools/ha-generic.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import type { VoiceTool } from './_types'
|
||||
import { tabletJson } from './_http'
|
||||
|
||||
/**
|
||||
* Универсальный вызов сервиса Home Assistant — «escape hatch» для устройств,
|
||||
* под которые нет отдельного tool. Модель берёт entity_id из снимка состояния
|
||||
* дома (он инжектится в системный промпт) и вызывает нужный сервис.
|
||||
*
|
||||
* Guardrails: разрешены только безопасные бытовые домены. Никаких automation/
|
||||
* script/shell/notify и т.п. — только прямое управление устройствами.
|
||||
*/
|
||||
const ALLOWED_DOMAINS = new Set([
|
||||
'light', 'switch', 'fan', 'climate', 'media_player', 'cover', 'scene', 'humidifier', 'vacuum',
|
||||
])
|
||||
|
||||
const haService: VoiceTool = {
|
||||
schema: {
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'ha_service',
|
||||
description:
|
||||
'Универсальное управление устройством Home Assistant, если нет специального tool. ' +
|
||||
'Передай domain, service и entity_id (возьми из снимка состояния дома). Примеры: ' +
|
||||
'domain=switch service=turn_on entity_id=switch.kettle; ' +
|
||||
'domain=cover service=open_cover entity_id=cover.blinds. ' +
|
||||
'Разрешённые домены: light, switch, fan, climate, media_player, cover, scene, humidifier, vacuum.',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
domain: { type: 'string', description: 'Домен HA (light, switch, fan, climate, media_player, cover, scene, humidifier, vacuum)' },
|
||||
service: { type: 'string', description: 'Сервис, напр. turn_on, turn_off, toggle, open_cover, set_percentage' },
|
||||
entity_id: { type: 'string', description: 'entity_id устройства из снимка состояния дома' },
|
||||
data: { type: 'object', description: 'Доп. параметры сервиса (напр. {"brightness_pct": 50})' },
|
||||
},
|
||||
required: ['domain', 'service', 'entity_id'],
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
async execute(args) {
|
||||
const domain = String(args?.domain || '').trim()
|
||||
const service = String(args?.service || '').trim()
|
||||
const entity_id = String(args?.entity_id || '').trim()
|
||||
if (!ALLOWED_DOMAINS.has(domain)) return { error: `domain not allowed: ${domain}` }
|
||||
if (!service || !entity_id) return { error: 'domain, service, entity_id required' }
|
||||
if (entity_id.split('.')[0] !== domain) return { error: 'entity_id domain must match domain' }
|
||||
|
||||
const extra = (args?.data && typeof args.data === 'object') ? args.data : {}
|
||||
await tabletJson('POST', '/api/voice/tools/smart-home', { domain, service, entity_id, ...extra })
|
||||
return { success: true, domain, service, entity_id }
|
||||
},
|
||||
}
|
||||
|
||||
export const tools: VoiceTool[] = [haService]
|
||||
Reference in New Issue
Block a user