add banbanmini backend

This commit is contained in:
HycJack
2026-03-24 15:04:36 +08:00
parent 0d0f995dc2
commit 7510ca6df1
197 changed files with 13008 additions and 0 deletions

View File

@@ -0,0 +1,212 @@
import json
import aiohttp
import asyncio
import time
from typing import List, Dict, AsyncGenerator
from interfaces.llm import LLM
from utils.logger import session_logger
from config import settings
from services.interrupt_handler import interrupt_handler
from implementations.base_service import BaseService
class BaseLLM(LLM, BaseService):
def __init__(self, api_key: str, base_url: str, service_name: str, **kwargs):
BaseService.__init__(self, "llm", kwargs.get("selected_role"))
self.session = None
self.api_key = api_key
self.base_url = base_url
self.connected = False
self.closed = False
self.response = None
self.device_id = None
self.session_id = None
self.service_name = service_name
async def connect(self):
try:
if self.session:
await self.session.close()
self.session = aiohttp.ClientSession()
self.connected = True
except Exception as e:
session_logger.error(
self.device_id,
self.session_id,
f"[{self.service_name}] 连接建立失败: {str(e)}",
)
self.connected = False
async def prepare_request(
self, transcript: str, history: List[Dict[str, str]], selected_role: dict
) -> Dict:
start_time = time.perf_counter()
if (
hasattr(self, "_system_message")
and self._system_message.get("content") == selected_role["content"]
):
messages = [self._system_message] # 复用缓存的系统消息
else:
messages = [{"role": "system", "content": selected_role["content"]}]
self._system_message = messages[0] # 更新缓存
recent_history = history[-settings.max_conversation_history :]
for exchange in recent_history:
messages.append({"role": "user", "content": exchange["user"]})
messages.append({"role": "assistant", "content": exchange["assistant"]})
messages.append({"role": "user", "content": transcript})
data = {
"model": self.model_id,
"messages": messages,
"stream": True,
}
end_time = time.perf_counter()
session_logger.info(
self.device_id,
self.session_id,
f"[{self.service_name}] 请求准备完成: {len(messages)}条消息, 耗时: {end_time - start_time:.4f}",
)
return data
async def generate_response_stream(
self, transcript: str, history: List[Dict[str, str]], selected_role: dict
) -> AsyncGenerator[str, None]:
if not self.connected or not self.session:
await self.connect()
if not self.connected:
yield None
return
self.device_id = getattr(self, "device_id", "unknown")
self.session_id = getattr(self, "session_id", "unknown")
session_key = (
(self.device_id, self.session_id)
if self.device_id != "unknown" and self.session_id != "unknown"
else None
)
try:
if session_key:
self.closed = interrupt_handler.is_interrupted(session_key)
data = await self.prepare_request(transcript, history, selected_role)
if session_key:
await interrupt_handler.set_interrupt_state(session_key, False)
self.closed = False
session_logger.info(
self.device_id, self.session_id, f"[{self.service_name}] 开始生成回复"
)
headers = {
"Authorization": f"Bearer {self.api_key}",
"Content-Type": "application/json",
}
try:
if not self.session or self.session.closed:
self.session = aiohttp.ClientSession()
self.connected = True
response = await self.session.post(
f"{self.base_url}/chat/completions", headers=headers, json=data
)
self.response = response
if response.status == 200:
async for line in response.content:
if session_key and interrupt_handler.is_interrupted(
session_key
):
self.closed = True
if self.closed:
session_logger.info(
self.device_id,
self.session_id,
f"[{self.service_name}] 生成被中断",
)
break
decoded_line = line.decode("utf-8").strip()
if not decoded_line:
continue
if decoded_line.startswith("data:"):
data_str = decoded_line[len("data:") :].strip()
if data_str == "[DONE]":
break
try:
data_json = json.loads(data_str)
except json.JSONDecodeError:
continue
choices = data_json.get("choices", [])
if choices:
delta = choices[0].get("delta", {})
content = delta.get("content")
if content:
if self.closed:
session_logger.info(
self.device_id,
self.session_id,
f"[{self.service_name}] 生成被中断",
)
break
yield content
else:
error_text = await response.text()
session_logger.error(
self.device_id,
self.session_id,
f"[{self.service_name}] 生成回复失败: {error_text}",
)
yield None
except asyncio.CancelledError:
session_logger.info(
self.device_id, self.session_id, f"[{self.service_name}] 请求被取消"
)
yield None
except Exception as e:
session_logger.error(
self.device_id,
self.session_id,
f"[{self.service_name}] API请求错误: {str(e)}",
)
yield None
except Exception as e:
session_logger.error(
self.device_id,
self.session_id,
f"[{self.service_name}] 生成过程发生错误: {str(e)}",
)
yield None
finally:
if self.response:
await self.response.release()
self.response = None
async def close(self):
if (
hasattr(self, "device_id")
and hasattr(self, "session_id")
and self.device_id != "unknown"
and self.session_id != "unknown"
):
session_key = (self.device_id, self.session_id)
await interrupt_handler.set_interrupt_state(session_key, True)
self.closed = True # 本地标志也同步设置
if self.response:
try:
self.response.close() # 立即关闭响应不等待release
await self.response.release()
except Exception as e:
if hasattr(self, "device_id") and hasattr(self, "session_id"):
session_logger.warning(
self.device_id,
self.session_id,
f"[{self.service_name}] 关闭响应时出错: {e}",
)
self.response = None
if self.session:
try:
await self.session.close()
except Exception as e:
if hasattr(self, "device_id") and hasattr(self, "session_id"):
session_logger.warning(
self.device_id,
self.session_id,
f"[{self.service_name}] 关闭会话时出错: {e}",
)
self.session = None
self.connected = False
if hasattr(self, "device_id") and hasattr(self, "session_id"):
session_logger.info(
self.device_id, self.session_id, f"[{self.service_name}] 连接已关闭"
)