init
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
import logging
|
||||
import os
|
||||
from typing import Optional
|
||||
|
||||
import httpx
|
||||
|
||||
from app.main import settings
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
HERMES_PROFILES_DIR = os.path.expanduser("~/.hermes/profiles")
|
||||
HERMES_TIMEOUT = 30
|
||||
|
||||
DEGRADED_RESPONSE = "(系统提示:AI引擎暂时不可用,请稍后再试)"
|
||||
DEGRADED_PROFILE_LIST: list[str] = []
|
||||
|
||||
|
||||
class HermesClient:
|
||||
def __init__(self):
|
||||
self.api_url = settings.hermes_api_url.rstrip("/")
|
||||
self.model = settings.hermes_model
|
||||
self.profiles_dir = HERMES_PROFILES_DIR
|
||||
self.timeout = HERMES_TIMEOUT
|
||||
|
||||
async def chat(self, profile_name: str, message: str) -> str:
|
||||
soul = await self.read_soul(profile_name)
|
||||
|
||||
messages = []
|
||||
if soul:
|
||||
messages.append({"role": "system", "content": soul})
|
||||
messages.append({"role": "user", "content": message})
|
||||
|
||||
try:
|
||||
async with httpx.AsyncClient(timeout=self.timeout) as client:
|
||||
response = await client.post(
|
||||
f"{self.api_url}/chat/completions",
|
||||
json={
|
||||
"model": self.model,
|
||||
"messages": messages,
|
||||
"temperature": 0.85,
|
||||
"max_tokens": 1024,
|
||||
},
|
||||
)
|
||||
response.raise_for_status()
|
||||
data = response.json()
|
||||
choice = data.get("choices", [{}])[0]
|
||||
return choice.get("message", {}).get("content", "").strip() or DEGRADED_RESPONSE
|
||||
|
||||
except httpx.TimeoutException:
|
||||
logger.warning(f"Hermes API timeout after {self.timeout}s for profile '{profile_name}'")
|
||||
return DEGRADED_RESPONSE
|
||||
except httpx.ConnectError:
|
||||
logger.error(f"Hermes API unreachable at '{self.api_url}'")
|
||||
return DEGRADED_RESPONSE
|
||||
except Exception as e:
|
||||
logger.error(f"Hermes API error for '{profile_name}': {e}")
|
||||
return DEGRADED_RESPONSE
|
||||
|
||||
async def create_profile(self, name: str, soul_content: str) -> bool:
|
||||
profile_dir = os.path.join(self.profiles_dir, name)
|
||||
os.makedirs(profile_dir, exist_ok=True)
|
||||
|
||||
soul_path = os.path.join(profile_dir, "SOUL.md")
|
||||
with open(soul_path, "w", encoding="utf-8") as f:
|
||||
f.write(soul_content)
|
||||
|
||||
logger.info(f"Profile created: {name} -> {profile_dir}")
|
||||
return True
|
||||
|
||||
async def delete_profile(self, name: str) -> bool:
|
||||
import shutil
|
||||
|
||||
profile_dir = os.path.join(self.profiles_dir, name)
|
||||
if os.path.exists(profile_dir):
|
||||
shutil.rmtree(profile_dir)
|
||||
logger.info(f"Profile deleted: {name}")
|
||||
return True
|
||||
logger.warning(f"Profile not found for deletion: {name}")
|
||||
return False
|
||||
|
||||
async def get_profile_list(self) -> list[str]:
|
||||
global DEGRADED_PROFILE_LIST
|
||||
if not os.path.exists(self.profiles_dir):
|
||||
return []
|
||||
try:
|
||||
dirs = [
|
||||
d
|
||||
for d in os.listdir(self.profiles_dir)
|
||||
if os.path.isdir(os.path.join(self.profiles_dir, d))
|
||||
and os.path.exists(os.path.join(self.profiles_dir, d, "SOUL.md"))
|
||||
]
|
||||
DEGRADED_PROFILE_LIST = dirs
|
||||
return dirs
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to list profiles: {e}")
|
||||
return DEGRADED_PROFILE_LIST
|
||||
|
||||
async def read_soul(self, profile_name: str) -> str:
|
||||
soul_path = os.path.join(self.profiles_dir, profile_name, "SOUL.md")
|
||||
if os.path.exists(soul_path):
|
||||
with open(soul_path, "r", encoding="utf-8") as f:
|
||||
return f.read()
|
||||
return ""
|
||||
|
||||
|
||||
hermes_client = HermesClient()
|
||||
Reference in New Issue
Block a user