@@ -2,21 +2,14 @@ import json
import os
import os
import re
import re
import requests
import requests
from datetime import date
from . config import AGENTS , log
from . config import AGENTS , log
from . text import clean_for_speech , find_sentence_end
from . text import clean_for_speech , find_sentence_end
from . tts import speak , play_error_sound
from . tts import speak , play_error_sound
SYSTEM_PROMPT = (
# Ключ голосовой сессии — Cosmo работает как полноценный агент
" Отвечай кратко, 1-2 предложения, без markdown, без эмодзи. "
VOICE_SESSION_KEY = os . getenv ( " VOICE_SESSION_KEY " , " agent:main:voice:home " )
" Ответ будет озвучен голосом, поэтому: "
" числа пиши прописью (двадцать три, а не 23), "
" единицы измерения пиши полностью (километров в час, а не км/ч), "
" не используй спецсимволы (+, -, /, % , °) — заменяй словами (плюс, минус, из, процентов, градусов). "
" Температуру пиши так: ' плюс девять градусов ' , а не ' +9°C ' . "
)
MAX_HISTORY = int ( os . getenv ( " MAX_HISTORY " , " 20 " ) )
# "stream" — режем по предложениям (быстро, но рваная интонация)
# "stream" — режем по предложениям (быстро, но рваная интонация)
# "full" — собираем весь ответ, потом TTS (естественно, но пауза перед началом)
# "full" — собираем весь ответ, потом TTS (естественно, но пауза перед началом)
TTS_MODE = os . getenv ( " TTS_MODE " , " full " )
TTS_MODE = os . getenv ( " TTS_MODE " , " full " )
@@ -29,60 +22,34 @@ RESET_PATTERNS = re.compile(
)
)
class Conversation :
""" Хранит историю сообщений — одна сессия на день """
def __init__ ( self , agent_id : str = " cosmo " ) :
self . agent_id = agent_id
self . created_date = date . today ( )
self . messages = [ { " role " : " system " , " content " : SYSTEM_PROMPT } ]
def is_expired ( self ) - > bool :
return date . today ( ) != self . created_date
def reset ( self ) :
self . created_date = date . today ( )
self . messages = [ { " role " : " system " , " content " : SYSTEM_PROMPT } ]
def add_user ( self , text : str ) :
self . messages . append ( { " role " : " user " , " content " : text } )
self . _trim ( )
def add_assistant ( self , text : str ) :
self . messages . append ( { " role " : " assistant " , " content " : text } )
self . _trim ( )
def _trim ( self ) :
if len ( self . messages ) > MAX_HISTORY + 1 :
self . messages = [ self . messages [ 0 ] ] + self . messages [ - ( MAX_HISTORY ) : ]
def is_reset_command ( text : str ) - > bool :
def is_reset_command ( text : str ) - > bool :
return bool ( RESET_PATTERNS . search ( text ) )
return bool ( RESET_PATTERNS . search ( text ) )
def ask_agent_stream ( text : str , conv : " Conversation | None " = None , agent_id : str = " cosmo " ) - > str :
def ask_agent_stream ( text : str , conv = None , agent_id : str = " cosmo " ) - > str :
if conv is None :
"""
conv = Conversation ( agent_id )
Отправляет запрос к OpenClaw gateway как полноценный агент.
История хранится на стороне gateway (session_key).
conv. add_user ( text )
conv параметр сохранён для обратной совместимости, не используется.
"""
cfg = AGENTS . get ( agent_id , AGENTS [ " cosmo " ] )
cfg = AGENTS . get ( agent_id , AGENTS [ " cosmo " ] )
gateway_url = cfg [ " gateway_url " ]
gateway_url = cfg [ " gateway_url " ]
session = cfg [ " session " ]
session = cfg [ " session " ]
agent = cfg [ " agent " ]
agent = cfg [ " agent " ]
session_key = cfg . get ( " session_key " , VOICE_SESSION_KEY )
try :
try :
resp = session . post (
resp = session . post (
f " { gateway_url } /v1/chat/completions " ,
f " { gateway_url } /v1/chat/completions " ,
headers = {
headers = {
" x-openclaw -model " : cfg [ " voice_model " ] ,
" x-ocplatform -model " : cfg [ " voice_model " ] ,
" x-openclaw-session-key " : cfg [ " session_key" ] ,
" x-openclaw-session-key " : session_key ,
} ,
} ,
json = {
json = {
" model " : agent ,
" model " : agent ,
" stream " : True ,
" stream " : True ,
" messages " : conv . messages ,
" messages " : [ { " role " : " user " , " content " : text } ] ,
" max_tokens " : 150 ,
" max_tokens " : 150 ,
} ,
} ,
stream = True ,
stream = True ,
@@ -151,16 +118,13 @@ def ask_agent_stream(text: str, conv: "Conversation | None" = None, agent_id: st
result = clean_for_speech ( full_text )
result = clean_for_speech ( full_text )
if TTS_MODE == " full " :
if TTS_MODE == " full " :
# LLM уже до cтр имил — озвучиваем весь ответ одним куском с цельной интонацией
if result . strip ( ) :
if result . strip ( ) :
print ( f " 🔊 Говорю: { result } " )
print ( f " 🔊 Говорю: { result } " )
speak ( result , agent_id )
speak ( result , agent_id )
else :
else :
# остаток буфера в stream-режиме
if buffer . strip ( ) :
if buffer . strip ( ) :
tail = clean_for_speech ( buffer )
tail = clean_for_speech ( buffer )
if tail :
if tail :
speak ( tail , agent_id )
speak ( tail , agent_id )
conv . add_assistant ( full_text )
return result
return result