Merge pull request 'feat: route voice through OpenClaw agent session (full memory + tools)' (#1) from feature/openclaw-agent-session into main
Reviewed-on: #1
This commit was merged in pull request #1.
This commit is contained in:
@@ -34,3 +34,5 @@ FOLLOWUP_TIMEOUT=8
|
||||
|
||||
# Логирование
|
||||
LOG_FILE=errors.log
|
||||
|
||||
VOICE_SESSION_KEY=agent:main:voice:home
|
||||
|
||||
@@ -2,21 +2,14 @@ import json
|
||||
import os
|
||||
import re
|
||||
import requests
|
||||
from datetime import date
|
||||
|
||||
from .config import AGENTS, log
|
||||
from .text import clean_for_speech, find_sentence_end
|
||||
from .tts import speak, play_error_sound
|
||||
|
||||
SYSTEM_PROMPT = (
|
||||
"Отвечай кратко, 1-2 предложения, без markdown, без эмодзи. "
|
||||
"Ответ будет озвучен голосом, поэтому: "
|
||||
"числа пиши прописью (двадцать три, а не 23), "
|
||||
"единицы измерения пиши полностью (километров в час, а не км/ч), "
|
||||
"не используй спецсимволы (+, -, /, %, °) — заменяй словами (плюс, минус, из, процентов, градусов). "
|
||||
"Температуру пиши так: 'плюс девять градусов', а не '+9°C'."
|
||||
)
|
||||
MAX_HISTORY = int(os.getenv("MAX_HISTORY", "20"))
|
||||
# Ключ голосовой сессии — Cosmo работает как полноценный агент
|
||||
VOICE_SESSION_KEY = os.getenv("VOICE_SESSION_KEY", "agent:main:voice:home")
|
||||
|
||||
# "stream" — режем по предложениям (быстро, но рваная интонация)
|
||||
# "full" — собираем весь ответ, потом TTS (естественно, но пауза перед началом)
|
||||
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:
|
||||
return bool(RESET_PATTERNS.search(text))
|
||||
|
||||
|
||||
def ask_agent_stream(text: str, conv: "Conversation | None" = None, agent_id: str = "cosmo") -> str:
|
||||
if conv is None:
|
||||
conv = Conversation(agent_id)
|
||||
|
||||
conv.add_user(text)
|
||||
|
||||
def ask_agent_stream(text: str, conv=None, agent_id: str = "cosmo") -> str:
|
||||
"""
|
||||
Отправляет запрос к OpenClaw gateway как полноценный агент.
|
||||
История хранится на стороне gateway (session_key).
|
||||
conv параметр сохранён для обратной совместимости, не используется.
|
||||
"""
|
||||
cfg = AGENTS.get(agent_id, AGENTS["cosmo"])
|
||||
gateway_url = cfg["gateway_url"]
|
||||
session = cfg["session"]
|
||||
agent = cfg["agent"]
|
||||
|
||||
session_key = cfg.get("session_key", VOICE_SESSION_KEY)
|
||||
|
||||
try:
|
||||
resp = session.post(
|
||||
f"{gateway_url}/v1/chat/completions",
|
||||
headers={
|
||||
"x-openclaw-model": cfg["voice_model"],
|
||||
"x-openclaw-session-key": cfg["session_key"],
|
||||
"x-ocplatform-model": cfg["voice_model"],
|
||||
"x-openclaw-session-key": session_key,
|
||||
},
|
||||
json={
|
||||
"model": agent,
|
||||
"stream": True,
|
||||
"messages": conv.messages,
|
||||
"messages": [{"role": "user", "content": text}],
|
||||
"max_tokens": 150,
|
||||
},
|
||||
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)
|
||||
|
||||
if TTS_MODE == "full":
|
||||
# LLM уже доcтримил — озвучиваем весь ответ одним куском с цельной интонацией
|
||||
if result.strip():
|
||||
print(f"🔊 Говорю: {result}")
|
||||
speak(result, agent_id)
|
||||
else:
|
||||
# остаток буфера в stream-режиме
|
||||
if buffer.strip():
|
||||
tail = clean_for_speech(buffer)
|
||||
if tail:
|
||||
speak(tail, agent_id)
|
||||
|
||||
conv.add_assistant(full_text)
|
||||
return result
|
||||
|
||||
Reference in New Issue
Block a user