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:
@@ -3,6 +3,8 @@
|
||||
* /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'
|
||||
@@ -10,15 +12,16 @@ import path from 'node:path'
|
||||
|
||||
const PRIMARY_DIR = process.env.VOICE_HISTORY_DIR || '/data/voice-history'
|
||||
const DATA_DIR = (() => {
|
||||
// Проверяем существование родителя (/data) — без него запись упадёт ENOENT.
|
||||
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'
|
||||
role: 'user' | 'assistant' | 'tool'
|
||||
content: any
|
||||
tool_calls?: any[]
|
||||
tool_call_id?: string
|
||||
}
|
||||
|
||||
function todayIso(): string {
|
||||
@@ -60,7 +63,8 @@ export async function resetHistory(agent: string): Promise<void> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Убирает cache_control из блоков (для записи в историю — следующий turn пересчитает границу).
|
||||
* Заглушка для обратной совместимости — убирает cache_control из блоков.
|
||||
* В Groq-режиме cache_control не используется, функция — no-op.
|
||||
*/
|
||||
export function stripCacheControl(content: any): any {
|
||||
if (Array.isArray(content)) {
|
||||
@@ -76,33 +80,9 @@ export function stripCacheControl(content: any): any {
|
||||
}
|
||||
|
||||
/**
|
||||
* Граница prompt-кеша: всё, кроме последних N сообщений, помечаем cache_control
|
||||
* на последнем блоке последнего «старого» сообщения. Даёт cache hit на каждом turn.
|
||||
* Заглушка для обратной совместимости.
|
||||
* В Groq-режиме prompt-кеширование не используется — просто возвращаем историю как есть.
|
||||
*/
|
||||
export function buildMessagesWithCache(history: HistoryMessage[], cacheTailUncached = 2): HistoryMessage[] {
|
||||
if (history.length <= cacheTailUncached) {
|
||||
return history.map((m) => ({ role: m.role, content: m.content }))
|
||||
}
|
||||
const cacheBoundary = history.length - cacheTailUncached
|
||||
return history.map((msg, i) => {
|
||||
if (i === cacheBoundary - 1) {
|
||||
return { role: msg.role, content: wrapLastBlockWithCache(msg.content) }
|
||||
}
|
||||
return { role: msg.role, content: msg.content }
|
||||
})
|
||||
}
|
||||
|
||||
function wrapLastBlockWithCache(content: any): any {
|
||||
if (typeof content === 'string') {
|
||||
return [{ type: 'text', text: content, cache_control: { type: 'ephemeral' } }]
|
||||
}
|
||||
if (Array.isArray(content) && content.length) {
|
||||
const out = [...content]
|
||||
const last = out[out.length - 1]
|
||||
if (last && typeof last === 'object') {
|
||||
out[out.length - 1] = { ...last, cache_control: { type: 'ephemeral' } }
|
||||
}
|
||||
return out
|
||||
}
|
||||
return content
|
||||
export function buildMessagesWithCache(history: HistoryMessage[], _cacheTailUncached = 2): HistoryMessage[] {
|
||||
return history.map((m) => ({ role: m.role, content: m.content }))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user