refactor: centralize llm backend configuration
This commit is contained in:
@@ -1,8 +1,6 @@
|
||||
[Dify]
|
||||
enable = true
|
||||
|
||||
api-key = "app-u5EnYq3ill19bm6pWJwGkY4D" # Dify的API Key
|
||||
base-url = "http://192.168.2.240/v1" #Dify API接口base url
|
||||
backend = "dify_workflow_chat"
|
||||
|
||||
commands = ["聊天"]
|
||||
command-tip = """
|
||||
@@ -17,4 +15,4 @@ http-proxy = ""
|
||||
|
||||
# 管理员和白名单用户是否免费使用
|
||||
admin_ignore = true
|
||||
whitelist_ignore = true
|
||||
whitelist_ignore = true
|
||||
|
||||
@@ -22,6 +22,7 @@ from utils.robot_cmd.robot_command import Feature, PermissionStatus, GroupBotMan
|
||||
from utils.decorator.points_decorator import plugin_points_cost
|
||||
from utils.media_downloader import MediaDownloader
|
||||
from utils.string_utils import remove_reasoning_content, remove_trailing_content, remove_grok_render_tags
|
||||
from utils.ai.unified_llm import UnifiedLLMClient
|
||||
from wechat_ipad import WechatAPIClient
|
||||
from wechat_ipad.models.message import MessageType
|
||||
import aiohttp
|
||||
@@ -97,12 +98,25 @@ class DifyPlugin(MessagePluginInterface):
|
||||
self._commands = dify_config.get("commands", ["ai", "dify", "聊天", "AI"])
|
||||
self.command_format = dify_config.get("command-tip", "聊天 请求内容")
|
||||
self.enable = dify_config.get("enable", True)
|
||||
self.api_key = dify_config.get("api-key", "")
|
||||
self.base_url = dify_config.get("base-url", "")
|
||||
self.price = dify_config.get("price", 0)
|
||||
self.admin_ignore = dify_config.get("admin_ignore", False)
|
||||
self.whitelist_ignore = dify_config.get("whitelist_ignore", False)
|
||||
self.http_proxy = dify_config.get("http-proxy", "")
|
||||
llm_config = dify_config.get("llm", {}) or {}
|
||||
if not llm_config:
|
||||
llm_config = {
|
||||
"backend": dify_config.get("backend", ""),
|
||||
"provider": "dify",
|
||||
"mode": "workflow",
|
||||
"api-key": self.api_key,
|
||||
"base-url": self.base_url,
|
||||
"endpoint": "workflows/run",
|
||||
"response_mode": "blocking",
|
||||
"request_timeout": 40,
|
||||
}
|
||||
self.llm_client = UnifiedLLMClient(llm_config)
|
||||
self.api_key = self.llm_client.api_key
|
||||
self.base_url = self.llm_client.base_url
|
||||
|
||||
self.LOG.debug(f"[{self.name}] 插件初始化完成,指令:{self._commands}")
|
||||
return True
|
||||
@@ -445,13 +459,6 @@ class DifyPlugin(MessagePluginInterface):
|
||||
if session_id not in self.conversations:
|
||||
self.conversations[session_id] = []
|
||||
|
||||
# 准备请求头
|
||||
headers = {
|
||||
"Authorization": f"Bearer {self.api_key}",
|
||||
"Content-Type": "application/json",
|
||||
"Accept": "text/event-stream" # 指定接受事件流
|
||||
}
|
||||
|
||||
# 准备历史记录
|
||||
history_text = ""
|
||||
if self.conversations[session_id]:
|
||||
@@ -471,122 +478,72 @@ class DifyPlugin(MessagePluginInterface):
|
||||
# 如果有历史记录,添加到inputs_params中
|
||||
if history_text:
|
||||
inputs_params["history"] = history_text
|
||||
if self.conversations[session_id]:
|
||||
inputs_params["conversation_history"] = self.conversations[session_id]
|
||||
|
||||
if files is None:
|
||||
files = []
|
||||
self.LOG.debug(f"Dify请求准备: files={len(files)}")
|
||||
|
||||
# 准备请求数据
|
||||
data = {
|
||||
"files": files,
|
||||
"user": user_id,
|
||||
"inputs": inputs_params,
|
||||
"response_mode": "blocking" # 使用阻塞响应模式
|
||||
}
|
||||
|
||||
# 如果有历史记录,同时添加到conversation_history中
|
||||
if self.conversations[session_id]:
|
||||
data["conversation_history"] = self.conversations[session_id]
|
||||
|
||||
# 设置代理
|
||||
proxy = self.http_proxy if self.http_proxy else None
|
||||
|
||||
# 发送请求
|
||||
url = f"{self.base_url}/workflows/run"
|
||||
|
||||
self.LOG.info(f"发送请求到Dify API: {url}")
|
||||
self.LOG.info(f"请求数据: {json.dumps(data, ensure_ascii=False)}")
|
||||
|
||||
self.LOG.info(f"Dify请求准备: session_id={session_id}, query_len={len(query)}, files={len(files)}")
|
||||
try:
|
||||
async with aiohttp.ClientSession() as session:
|
||||
response = await session.post(url, headers=headers, json=data, proxy=proxy, timeout=40)
|
||||
if response.status != 200:
|
||||
error_text = await response.text()
|
||||
self.LOG.error(f"Dify API请求失败: {response.status} {error_text}")
|
||||
return False, f"请求失败,状态码: {response.status}"
|
||||
response = await asyncio.to_thread(
|
||||
self.llm_client.generate,
|
||||
query,
|
||||
user_id,
|
||||
inputs_params,
|
||||
f"dify:{session_id}",
|
||||
"",
|
||||
"",
|
||||
None,
|
||||
files,
|
||||
)
|
||||
if not response:
|
||||
self.LOG.error(f"Dify API请求失败: {self.llm_client.last_error}")
|
||||
return False, "请求失败"
|
||||
|
||||
# 解析响应
|
||||
response_data = await response.json()
|
||||
self.LOG.info(f"收到Dify API响应: {json.dumps(response_data, ensure_ascii=False)}")
|
||||
answer = response.get("text", "") or ""
|
||||
total_tokens = int((response.get("usage", {}) or {}).get("total_tokens") or 0)
|
||||
raw_data = response.get("raw", {}) or {}
|
||||
outputs = ((raw_data.get("data") or {}).get("outputs") or {}) if isinstance(raw_data, dict) else {}
|
||||
|
||||
# 提取回答内容
|
||||
answer = ""
|
||||
total_tokens = 0
|
||||
if outputs and "result" in outputs and "type" in outputs:
|
||||
if outputs["type"] in {"image", "video"}:
|
||||
downloader = MediaDownloader()
|
||||
media_path = await downloader.download_media(outputs["result"])
|
||||
answer = media_path
|
||||
|
||||
# 获取输出内容
|
||||
outputs = response_data.get("data", {}).get("outputs", {})
|
||||
if outputs:
|
||||
# 处理媒体类型返回
|
||||
if "result" in outputs and "type" in outputs:
|
||||
if outputs["type"] == "image":
|
||||
downloader = MediaDownloader()
|
||||
image_url = outputs["result"]
|
||||
image_path = await downloader.download_media(image_url)
|
||||
answer = image_path
|
||||
if outputs["type"] == "video":
|
||||
downloader = MediaDownloader()
|
||||
image_url = outputs["result"]
|
||||
image_path = await downloader.download_media(image_url)
|
||||
answer = image_path
|
||||
# 处理文本类型返回
|
||||
elif "text" in outputs and isinstance(outputs["text"], str):
|
||||
answer = outputs["text"]
|
||||
# 兼容旧版处理逻辑
|
||||
else:
|
||||
for key, value in outputs.items():
|
||||
if isinstance(value, str) and value.strip():
|
||||
answer += value
|
||||
elif isinstance(value, dict):
|
||||
# 处理嵌套字典的情况
|
||||
for sub_key, sub_value in value.items():
|
||||
if isinstance(sub_value, str) and sub_value.strip():
|
||||
answer += sub_value
|
||||
elif isinstance(value, list):
|
||||
# 处理列表的情况
|
||||
for item in value:
|
||||
if isinstance(item, str) and item.strip():
|
||||
answer += item
|
||||
elif isinstance(item, dict):
|
||||
# 处理列表中的字典
|
||||
for item_key, item_value in item.items():
|
||||
if isinstance(item_value, str) and item_value.strip():
|
||||
answer += item_value
|
||||
if answer and not os.path.isfile(answer):
|
||||
answer = remove_reasoning_content(answer)
|
||||
answer = remove_trailing_content(answer)
|
||||
answer = remove_grok_render_tags(answer)
|
||||
answer = re.sub(r'\n{3,}', '\n\n', answer).strip()
|
||||
|
||||
# 获取token使用情况
|
||||
total_tokens = response_data.get("data", {}).get("total_tokens", 0)
|
||||
# 更新会话历史
|
||||
self.conversations[session_id].append({
|
||||
"role": "user",
|
||||
"content": query
|
||||
})
|
||||
|
||||
if answer and not os.path.isfile(answer):
|
||||
answer = remove_reasoning_content(answer)
|
||||
answer = remove_trailing_content(answer)
|
||||
answer = remove_grok_render_tags(answer)
|
||||
answer = re.sub(r'\n{3,}', '\n\n', answer).strip()
|
||||
self.conversations[session_id].append({
|
||||
"role": "assistant",
|
||||
"content": answer
|
||||
})
|
||||
|
||||
# 更新会话历史
|
||||
self.conversations[session_id].append({
|
||||
"role": "user",
|
||||
"content": query
|
||||
})
|
||||
# 限制会话历史长度
|
||||
if len(self.conversations[session_id]) > self.max_history_length * 2:
|
||||
self.conversations[session_id] = self.conversations[session_id][-self.max_history_length * 2:]
|
||||
|
||||
self.conversations[session_id].append({
|
||||
"role": "assistant",
|
||||
"content": answer
|
||||
})
|
||||
# 统计token使用情况
|
||||
if total_tokens > 0:
|
||||
if user_id in self.token_usage:
|
||||
self.token_usage[user_id] += total_tokens
|
||||
else:
|
||||
self.token_usage[user_id] = total_tokens
|
||||
|
||||
# 限制会话历史长度
|
||||
if len(self.conversations[session_id]) > self.max_history_length * 2:
|
||||
self.conversations[session_id] = self.conversations[session_id][-self.max_history_length * 2:]
|
||||
self.LOG.info(
|
||||
f"用户 {user_id} 本次消耗 {total_tokens} tokens,累计 {self.token_usage[user_id]} tokens")
|
||||
|
||||
# 统计token使用情况
|
||||
if total_tokens > 0:
|
||||
if user_id in self.token_usage:
|
||||
self.token_usage[user_id] += total_tokens
|
||||
else:
|
||||
self.token_usage[user_id] = total_tokens
|
||||
|
||||
self.LOG.info(
|
||||
f"用户 {user_id} 本次消耗 {total_tokens} tokens,累计 {self.token_usage[user_id]} tokens")
|
||||
|
||||
return True, answer
|
||||
return True, answer
|
||||
|
||||
except Exception as e:
|
||||
self.LOG.error(f"处理Dify响应时出错: {str(e)}")
|
||||
|
||||
Reference in New Issue
Block a user