Files
smart-home-tablet/lib/voice-history.ts
Cosmo 04b7d1f104
All checks were successful
Deploy / deploy (push) Successful in 2m47s
feat: switch from Anthropic to Groq API (llama-3.3-70b-versatile)
- 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.
2026-04-30 20:43:30 +00:00

89 lines
3.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.
/**
* История диалога per-agent per-day. Файлы в /data/voice-history/{agent}-{date}.json.
* /data — это volume контейнера (на хосте /opt/digital-home/smart-home-tablet-data/).
*
* Fallback: если /data не существует (локальная разработка) — пишем в /tmp/voice-history.
*
* Формат обновлён под Groq/OpenAI: role может быть 'user' | 'assistant' | 'tool'.
*/
import { promises as fs } from 'node:fs'
import { existsSync } from 'node:fs'
import path from 'node:path'
const PRIMARY_DIR = process.env.VOICE_HISTORY_DIR || '/data/voice-history'
const DATA_DIR = (() => {
const parent = path.dirname(PRIMARY_DIR)
return existsSync(parent) ? PRIMARY_DIR : '/tmp/voice-history'
})()
const MAX_HISTORY = parseInt(process.env.VOICE_MAX_HISTORY || '40', 10)
export type HistoryMessage = {
role: 'user' | 'assistant' | 'tool'
content: any
tool_calls?: any[]
tool_call_id?: string
}
function todayIso(): string {
return new Date().toISOString().slice(0, 10)
}
function historyPath(agent: string): string {
return path.join(DATA_DIR, `${agent}-${todayIso()}.json`)
}
export async function loadHistory(agent: string): Promise<HistoryMessage[]> {
try {
const raw = await fs.readFile(historyPath(agent), 'utf-8')
const parsed = JSON.parse(raw)
return Array.isArray(parsed) ? parsed : []
} catch (e: any) {
if (e?.code === 'ENOENT') return []
console.warn('[voice/history] read failed:', e?.message || e)
return []
}
}
export async function saveHistory(agent: string, history: HistoryMessage[]): Promise<void> {
try {
await fs.mkdir(DATA_DIR, { recursive: true })
const trimmed = history.slice(-MAX_HISTORY)
await fs.writeFile(historyPath(agent), JSON.stringify(trimmed, null, 2), 'utf-8')
} catch (e: any) {
console.warn('[voice/history] write failed:', e?.message || e)
}
}
export async function resetHistory(agent: string): Promise<void> {
try {
await fs.unlink(historyPath(agent))
} catch (e: any) {
if (e?.code !== 'ENOENT') console.warn('[voice/history] reset failed:', e?.message || e)
}
}
/**
* Заглушка для обратной совместимости — убирает cache_control из блоков.
* В Groq-режиме cache_control не используется, функция — no-op.
*/
export function stripCacheControl(content: any): any {
if (Array.isArray(content)) {
return content.map((b) => {
if (b && typeof b === 'object' && 'cache_control' in b) {
const { cache_control: _ignore, ...rest } = b
return rest
}
return b
})
}
return content
}
/**
* Заглушка для обратной совместимости.
* В Groq-режиме prompt-кеширование не используется — просто возвращаем историю как есть.
*/
export function buildMessagesWithCache(history: HistoryMessage[], _cacheTailUncached = 2): HistoryMessage[] {
return history.map((m) => ({ role: m.role, content: m.content }))
}