refactor: 移除所有非ollama引擎,新增OpenAI兼容格式引擎

- 删除: mimo_engine, GLM_http_engine, qwq_engine, xfyun_spark_engine, iflow_api_engine, maas_http_engine, SDK_openAI
- 保留: ollama_engine (本地Ollama引擎)
- 新增: openai_engine (通用OpenAI API兼容引擎,支持DeepSeek/Grok/硅基流动/vLLM等)
- 新增: config.json.example 配置模板
- 更新: README.md / README.en.md 引擎说明
This commit is contained in:
xiaoli-cli committed 2026-04-26 05:27:40 +08:00
1 parent 7c02642982
commit ea534c5b88
11 files changed
+374 -2023

No files matched your search

-210
View File
@@ -1,210 +0,0 @@
import os
import json
import requests
from colorama import Fore, Style
from openai import OpenAI
# 获取项目根目录
current_dir = os.path.dirname(os.path.abspath(__file__))
project_root = os.path.dirname(current_dir)
class Glm_http_engineAI:
"""基于HTTP协议的智谱AI GLM引擎"""
requires_api_key = True # GLM HTTP引擎需要API密钥
def get_help_info(self):
"""获取引擎帮助信息"""
help_text = """
GLM HTTP AI引擎插件帮助信息
==========================
引擎名称: glm_http
描述: 基于HTTP协议的智谱AI GLM(GLM-4.5-Flash)引擎,支持智谱AI服务
可用命令:
无特定命令,直接与AI对话即可
使用说明:
1. 此引擎使用HTTP协议调用智谱AI API
2. 使用/engine.switch glm_http切换到此引擎
3. 直接输入自然语言与AI对话
4. 支持普通对话、流式对话、JSON模式等多种调用方式
"""
return help_text
def __init__(self):
self.name = "glm_http"
self.cli = None
# 直接从 config.json 读取配置
config_file = os.path.join(project_root, "config.json")
try:
with open(config_file, 'r', encoding='utf-8') as f:
config = json.load(f)
engine_config = config.get("api", {}).get("engines", {}).get("GLM_http", {})
self.api_key = engine_config.get("api_key", "")
self.base_url = engine_config.get("base_url", "")
self.model_id = engine_config.get("model", "")
except Exception as e:
# 如果读取失败,使用空值
self.api_key = ""
self.base_url = ""
self.model_id = ""
# 初始化对话历史
self.conversation_history = []
# 共享对话历史引用(由CLI设置)
self.shared_conversation_history = None
# 设置最大历史记录数
try:
with open(config_file, 'r', encoding='utf-8') as f:
config = json.load(f)
self.max_history = config.get("system", {}).get("max_history", 10)
except:
self.max_history = 10
# 初始化 OpenAI 客户端(配置从 config.json 动态读取)
try:
self.client = OpenAI(
api_key=self.api_key,
base_url=self.base_url if self.base_url else None,
timeout=None
)
except Exception as e:
print(f"初始化 OpenAI 客户端失败: {e}")
self.client = None
def generate_response(self, user_input, tool_results=None, system_prompt=None):
"""生成AI響應 - 使用智谱AI HTTP API"""
try:
# 如果有工具結果,將其作為上下文返回給AI處理
if tool_results:
tool_result_text = f"工具執行結果: {tool_results.get('result', '無結果')}"
# 構建包含歷史記錄的消息列表
messages = self._build_messages_with_history(system_prompt, tool_result_text)
# 調用API
response = self._call_api(messages)
if response:
return response
else:
return "智谱AI HTTP API調用失敗"
# 構建包含歷史記錄的消息列表
messages = self._build_messages_with_history(system_prompt, user_input)
# 調用API
response = self._call_api(messages)
if response:
return response
else:
return "智谱AI HTTP API調用失敗"
except Exception as e:
# 如果API調用失敗,返回錯誤信息
return f"智谱AI HTTP API調用失敗: {str(e)}"
def _call_api(self, messages, use_stream=False, extra_body=None):
"""調用API的核心方法"""
if extra_body is None:
extra_body = {}
try:
response = self.client.chat.completions.create(
model=self.model_id,
messages=messages,
stream=use_stream,
temperature=0.7,
max_tokens=4096,
extra_headers={"lora_id": "0"}, # 調用微調大模型時,對應替換為模型服務卡片上的resourceId
stream_options={"include_usage": True},
extra_body=extra_body
)
if use_stream:
# 處理流式響應
full_response = ""
for chunk in response:
if hasattr(chunk.choices[0].delta, 'content') and chunk.choices[0].delta.content:
content = chunk.choices[0].delta.content
full_response += content
return full_response
else:
# 處理非流式響應
return response.choices[0].message.content
except Exception as e:
print(f"智谱AI HTTP API請求出錯: {e}")
return None
def _build_messages_with_history(self, system_prompt, user_input):
"""構建包含歷史記錄的消息列表"""
messages = []
# 添加系統提示詞
if system_prompt:
messages.append({
"role": "system",
"content": system_prompt
})
# 添加歷史對話記錄(使用共享歷史)
if self.shared_conversation_history is not None:
# 確保歷史記錄格式正確
for msg in self.shared_conversation_history[-self.max_history:]:
if isinstance(msg, dict) and "role" in msg and "content" in msg:
messages.append(msg)
else:
# 如果格式不正確,跳過該條記錄
continue
# 添加當前用戶輸入
messages.append({
"role": "user",
"content": user_input
})
return messages
def _call_api_with_history(self, content, system_prompt=None):
"""使用對話歷史調用API"""
# 構建消息列表
messages = []
# 添加系統提示詞(如果提供)
if system_prompt:
messages.append({
"role": "system",
"content": system_prompt
})
# 添加歷史對話記錄(使用共享歷史)
if self.shared_conversation_history is not None:
# 確保歷史記錄格式正確
for msg in self.shared_conversation_history[-self.max_history:]:
if isinstance(msg, dict) and "role" in msg and "content" in msg:
messages.append(msg)
else:
# 如果格式不正確,跳過該條記錄
continue
# 添加當前內容
messages.append({
"role": "tool",
"content": content
})
# 調用API
response = self._call_api(messages)
if response:
return response
else:
return "智谱AI HTTP API調用失敗"
def handle_command(self, command):
"""處理引擎特定命令"""
# GLM HTTP引擎目前不支持特定命令
print(f"{Fore.RED}GLM HTTP引擎不支持此命令{Style.RESET_ALL}")
return False
-266
View File
@@ -1,266 +0,0 @@
"""
小狸 Pro-CLI AI 引擎开发 SDK
提供快速开发 AI 引擎的工具包
"""
import os
import sys
import json
import requests
from colorama import Fore, Style
from openai import OpenAI
import logging
# 设置日志
logger = logging.getLogger(__name__)
# 添加项目根目录到sys.path
current_dir = os.path.dirname(os.path.abspath(__file__))
project_root = os.path.dirname(current_dir)
sys.path.insert(0, project_root)
class _AIEngineSDK:
"""AI 引擎 SDK 内部实现"""
def __init__(self):
self.name = ""
self.api_key = ""
self.base_url = ""
self.model = ""
self.is_online = True
self.max_history = 10
self.conversation_history = []
self.shared_conversation_history = None
self.cli = None
# 深度思考引擎配置
self.is_thinking_engine = False # 是否是深度思考引擎
self.thinking_start_marker = "" # 思考开始标记
self.thinking_end_marker = "" # 思考结束标记
self.show_thinking = True # 是否显示思考过程
# 配置文件路径
self.config_file = os.path.join(project_root, "config.json")
def _load_config(self):
"""从配置文件加载配置"""
try:
with open(self.config_file, 'r', encoding='utf-8') as f:
config = json.load(f)
engine_config = config.get("api", {}).get("engines", {}).get(self.name, {})
if not self.api_key:
self.api_key = engine_config.get("api_key", "")
if not self.base_url:
self.base_url = engine_config.get("base_url", "")
if not self.model:
self.model = engine_config.get("model", "")
self.max_history = config.get("system", {}).get("max_history", 10)
except Exception as e:
logger.error(f"加载配置失败: {e}")
self.max_history = 10
def _call_openai_api(self, messages):
"""调用 OpenAI 格式的 API"""
try:
self._load_config()
# 构建请求体
body = {
"model": self.model,
"messages": messages,
"stream": False
}
# 构建请求头
headers = {
'Content-Type': 'application/json'
}
if self.api_key:
headers['Authorization'] = f'Bearer {self.api_key}'
# 确定 API 地址
api_url = self.base_url
if not api_url.endswith("/chat/completions"):
api_url = api_url.rstrip('/') + "/chat/completions"
# 发送请求
response = requests.post(
url=api_url,
json=body,
headers=headers,
timeout=None
)
# 处理响应
if response.status_code == 200:
response_data = response.json()
if "choices" in response_data and len(response_data["choices"]) > 0:
ai_response = response_data["choices"][0]["message"]["content"]
# 如果是深度思考引擎,处理思考内容
if self.is_thinking_engine:
ai_response = self._process_thinking_content(ai_response)
return ai_response
else:
return f"AI 返回格式错误: {response_data}"
else:
return f"AI 调用失败: {response.status_code} - {response.text}"
except Exception as e:
return f"AI 调用失败: {str(e)}"
def _build_messages(self, system_prompt, user_input):
"""构建消息列表"""
messages = []
# 添加系统提示词
if system_prompt:
messages.append({
"role": "system",
"content": system_prompt
})
# 添加历史对话记录
if self.shared_conversation_history is not None:
for msg in self.shared_conversation_history[-self.max_history:]:
if isinstance(msg, dict) and "role" in msg and "content" in msg:
messages.append(msg)
# 添加当前用户输入
messages.append({
"role": "user",
"content": user_input
})
return messages
def generate_response(self, user_input, tool_results=None, system_prompt=None):
"""生成 AI 响应"""
try:
# 如果有工具结果,将其作为上下文返回给AI处理
if tool_results:
tool_result_text = f"工具执行结果: {tool_results.get('result', '无结果')}"
messages = self._build_messages(system_prompt, tool_result_text)
else:
messages = self._build_messages(system_prompt, user_input)
# 调用 API
return self._call_openai_api(messages)
except Exception as e:
return f"AI 调用失败: {str(e)}"
def _process_thinking_content(self, response):
"""处理思考内容,将思考部分以灰色文本显示,只返回回复部分"""
import re
if not self.thinking_start_marker or not self.thinking_end_marker:
# 如果没有设置标记,直接返回
return response
# 使用正则表达式查找思考内容
pattern = f"{re.escape(self.thinking_start_marker)}(.*?){re.escape(self.thinking_end_marker)}"
matches = re.findall(pattern, response, re.DOTALL)
if matches and self.show_thinking:
for match in matches:
# 用灰色文本显示思考内容
thinking_text = f"{Fore.LIGHTBLACK_EX}[思考: {match}]{Style.RESET_ALL}"
# 替换思考标记之间的内容,保留思考内容但以灰色显示
response = re.sub(pattern, thinking_text, response, 1)
elif matches and not self.show_thinking:
# 如果不显示思考内容,直接删除
for match in matches:
response = re.sub(pattern, "", response, 1)
return response
# 全局 SDK 实例
_sdk_instance = _AIEngineSDK()
def engine_Online(mode):
"""
设置引擎模式
Args:
mode: "on" - 云模式(在线 API)
"off" - 本地模式(如 Ollama)
"""
_sdk_instance.is_online = (mode.lower() == "on")
def ai_name(name):
"""设置引擎名称"""
_sdk_instance.name = name
def ai_api_url(url):
"""设置 API 地址"""
_sdk_instance.base_url = url
def ai_api_key(key):
"""设置 API 密钥"""
_sdk_instance.api_key = key
def ai_model_id(model):
"""设置模型名称"""
_sdk_instance.model = model
def ai_thinking_engine(enable=True, start_marker="<thinking>", end_marker="</thinking>", show_thinking=True):
"""
设置深度思考引擎配置
Args:
enable: 是否启用深度思考引擎模式
start_marker: 思考开始标记,默认为 "<thinking>"
end_marker: 思考结束标记,默认为 "</thinking>"
show_thinking: 是否显示思考过程,默认为 True
"""
_sdk_instance.is_thinking_engine = enable
_sdk_instance.thinking_start_marker = start_marker
_sdk_instance.thinking_end_marker = end_marker
_sdk_instance.show_thinking = show_thinking
def set_cli(cli):
"""设置 CLI 引用"""
_sdk_instance.cli = cli
def set_shared_history(history):
"""设置共享对话历史"""
_sdk_instance.shared_conversation_history = history
def generate_response(user_input, tool_results=None, system_prompt=None):
"""生成 AI 响应"""
return _sdk_instance.generate_response(user_input, tool_results, system_prompt)
def get_sdk_instance():
"""获取 SDK 实例(用于高级用法)"""
return _sdk_instance
# 导出
__all__ = [
'engine_Online',
'ai_name',
'ai_api_url',
'ai_api_key',
'ai_model_id',
'ai_thinking_engine',
'set_cli',
'set_shared_history',
'generate_response',
'get_sdk_instance'
]
-405
View File
@@ -1,405 +0,0 @@
import os
import sys
import json
import requests
import threading
import time
from colorama import Fore, Style
import logging
# 设置日志
logger = logging.getLogger(__name__)
# 添加项目根目录到sys.path,以便正确导入
current_dir = os.path.dirname(os.path.abspath(__file__))
project_root = os.path.dirname(current_dir) # 获取项目根目录
sys.path.insert(0, project_root)
class IflowAPIAI:
"""基于Qwen3-Coder-Plus模型的AI引擎"""
def get_help_info(self):
"""获取引擎帮助信息"""
help_text = """
iflow API AI引擎插件帮助信息
==========================
引擎名称: iflow-api
描述: 基于Qwen3-Coder-Plus模型的AI引擎,支持复杂推理和代码生成
可用命令:
无特定命令,直接与AI对话即可
使用说明:
1. 此引擎使用Qwen3-Coder-Plus云API
2. 使用/engine.switch iflow-api切换到此引擎
3. 直接输入自然语言与AI对话
4. 特别适合处理代码生成、技术问题等复杂任务
"""
return help_text
def __init__(self):
self.name = "iflow-api"
self.cli = None # 引用CLI实例以访问插件
# 直接从 config.json 读取配置(一次性读取)
project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
config_file = os.path.join(project_root, "config.json")
# 默认值
self.api_key = ""
self.url = ""
self.model = ""
self.max_history = 10
try:
with open(config_file, 'r', encoding='utf-8') as f:
config = json.load(f)
engine_config = config.get("api", {}).get("engines", {}).get("iflow_api", {})
self.api_key = engine_config.get("api_key", "")
self.url = engine_config.get("base_url", "")
self.model = engine_config.get("model", "")
self.max_history = config.get("system", {}).get("max_history", 10)
except Exception as e:
# 如果读取失败,使用默认值
logger.warning(f"警告: 读取配置文件失败: {e}")
print(f"{Fore.YELLOW}警告: 读取配置文件失败: {e}{Style.RESET_ALL}")
# 验证API密钥
if not self.api_key:
logger.warning("警告: iflow-api引擎的API密钥未设置")
print(f"{Fore.YELLOW}警告: iflow-api引擎的API密钥未设置{Style.RESET_ALL}")
self.headers = {
'Authorization': f'Bearer {self.api_key}',
'Content-Type': 'application/json'
}
# 初始化对话历史
self.conversation_history = []
# 设置思考标记
self.thinking_start_marker = ""
self.thinking_end_marker = ""
# 共享对话历史引用(由CLI设置)
self.shared_conversation_history = None
def generate_response(self, user_input, tool_results=None, system_prompt=None):
"""生成AI响应 - 使用 qwen3-coder-plus"""
try:
# 如果有工具结果,将其作为上下文返回给AI处理
if tool_results:
tool_result_text = f"工具执行结果: {tool_results.get('result', '无结果')}"
# 构建消息列表
messages = self._build_messages_with_history(system_prompt, tool_result_text)
# 构建请求体
body = {
"model": self.model or "qwen3-coder-plus",
"messages": messages,
"stream": False # 使用非流式响应以便于处理
}
# 发送请求到qwen3-coder-plus API
response = requests.post(url=self.url, json=body, headers=self.headers, timeout=None)
if response.status_code == 200:
response_data = response.json()
# 检查是否是API错误响应(例如认证失败)
if isinstance(response_data, dict) and "status" in response_data:
# 检查是否是认证错误
if response_data.get("status") in ["434", "401", "403"]:
error_msg = response_data.get("msg", "API认证失败")
return f"qwen3-coder-plus API错误: {error_msg}"
elif "body" in response_data and response_data["body"] is None:
error_msg = response_data.get("msg", "API请求失败")
return f"qwen3-coder-plus API错误: {error_msg}"
if "choices" in response_data and len(response_data["choices"]) > 0:
# 改进错误处理以适应不同的API响应格式
choice = response_data["choices"][0]
if "message" in choice and "content" in choice["message"]:
ai_response = choice["message"]["content"]
# 处理思考标记
processed_response = self._process_thinking_content(ai_response)
# 返回处理后的AI回复
return processed_response
else:
# 尝试其他可能的字段名
if "text" in choice:
return choice["text"]
elif "delta" in choice and "content" in choice["delta"]:
return choice["delta"]["content"]
else:
return f" qwen3-coder-plus返回格式错误: 缺少预期的响应字段 - {response_data}"
else:
return f" qwen3-coder-plus返回格式错误: {response_data}"
else:
return f" qwen3-coder-plus调用失败: {response.status_code} - {response.text}"
# 构建消息列表
messages = self._build_messages_with_history(system_prompt, user_input)
# 构建请求体
body = {
"model": self.model or "qwen3-coder-plus",
"messages": messages,
"stream": False # 使用非流式响应以便于处理
}
# 发送请求到qwen3-coder-plus
response = requests.post(url=self.url, json=body, headers=self.headers, timeout=None)
if response.status_code == 200:
response_data = response.json()
# 检查是否是API错误响应(例如认证失败)
if isinstance(response_data, dict) and "status" in response_data:
# 检查是否是认证错误
if response_data.get("status") in ["434", "401", "403"]:
error_msg = response_data.get("msg", "API认证失败")
return f"qwen3-coder-plus API错误: {error_msg}"
elif "body" in response_data and response_data["body"] is None:
error_msg = response_data.get("msg", "API请求失败")
return f"qwen3-coder-plus API错误: {error_msg}"
if "choices" in response_data and len(response_data["choices"]) > 0:
# 改进错误处理以适应不同的API响应格式
choice = response_data["choices"][0]
if "message" in choice and "content" in choice["message"]:
ai_response = choice["message"]["content"]
# 处理思考标记
processed_response = self._process_thinking_content(ai_response)
# 返回处理后的AI回复
return processed_response
else:
# 尝试其他可能的字段名
if "text" in choice:
return choice["text"]
elif "delta" in choice and "content" in choice["delta"]:
return choice["delta"]["content"]
else:
return f"qwen3-coder-plus返回格式错误: 缺少预期的响应字段 - {response_data}"
else:
return f"qwen3-coder-plus返回格式错误: {response_data}"
else:
return f"qwen3-coder-plus调用失败: {response.status_code} - {response.text}"
except Exception as e:
# 如果API调用失败,返回错误信息
return f"qwen3-coder-plus调用失败: {str(e)}"
def _build_messages_with_history(self, system_prompt, user_input):
"""构建包含历史记录的消息列表"""
messages = []
# 添加系统提示词
if system_prompt:
messages.append({
"role": "system",
"content": system_prompt
})
# 添加历史对话记录(使用共享历史)
if self.shared_conversation_history is not None:
# 确保历史记录格式正确
for msg in self.shared_conversation_history[-self.max_history:]:
if isinstance(msg, dict) and "role" in msg and "content" in msg:
messages.append(msg)
else:
# 如果格式不正确,跳过该条记录
continue
# 添加当前用户输入
messages.append({
"role": "user",
"content": user_input
})
return messages
def _call_api_with_history(self, content, system_prompt=None):
"""使用对话历史调用API"""
# 构建消息列表
messages = []
# 添加系统提示词(如果提供)
if system_prompt:
messages.append({
"role": "system",
"content": system_prompt
})
# 添加历史对话记录(使用共享历史)
if self.shared_conversation_history is not None:
# 确保历史记录格式正确
for msg in self.shared_conversation_history[-self.max_history:]:
if isinstance(msg, dict) and "role" in msg and "content" in msg:
messages.append(msg)
else:
# 如果格式不正确,跳过该条记录
continue
# 添加当前内容
messages.append({
"role": "tool",
"content": content
})
# 构建请求体
body = {
"model": self.model or "qwen3-coder-plus",
"messages": messages,
"stream": False # 使用非流式响应以便于处理
}
# 发送请求到qwen3-coder-plus
response = requests.post(url=self.url, json=body, headers=self.headers, timeout=None)
if response.status_code == 200:
response_data = response.json()
# 检查是否是API错误响应(例如认证失败)
if isinstance(response_data, dict) and "status" in response_data:
# 检查是否是认证错误
if response_data.get("status") in ["434", "401", "403"]:
error_msg = response_data.get("msg", "API认证失败")
return f"qwen3-coder-plus API错误: {error_msg}"
elif "body" in response_data and response_data["body"] is None:
error_msg = response_data.get("msg", "API请求失败")
return f"qwen3-coder-plus API错误: {error_msg}"
if "choices" in response_data and len(response_data["choices"]) > 0:
# 改进错误处理以适应不同的API响应格式
choice = response_data["choices"][0]
if "message" in choice and "content" in choice["message"]:
ai_response = choice["message"]["content"]
# 处理思考标记
processed_response = self._process_thinking_content(ai_response)
return processed_response
else:
# 尝试其他可能的字段名
if "text" in choice:
return choice["text"]
elif "delta" in choice and "content" in choice["delta"]:
return choice["delta"]["content"]
else:
return f"qwen3-coder-plus返回格式错误: 缺少预期的响应字段 - {response_data}"
else:
return f"qwen3-coder-plus返回格式错误: {response_data}"
else:
return f"qwen3-coder-plus调用失败: {response.status_code} - {response.text}"
def handle_command(self, command):
"""处理引擎特定命令"""
# qwen3-coder-plus引擎目前不支持特定命令
print(f"{Fore.RED}qwen3-coder-plus引擎不支持此命令{Style.RESET_ALL}")
return False
def _process_thinking_content(self, response):
"""处理思考内容,将思考部分以灰色文本显示,只返回回复部分"""
from colorama import Fore, Style
import re
# 这里可以设置思考标记,由用户自己定义
thinking_start_marker = ""
thinking_end_marker = ""
if self.thinking_start_marker and self.thinking_end_marker:
# 使用正则表达式查找思考内容
pattern = f"{re.escape(self.thinking_start_marker)}(.*?){re.escape(self.thinking_end_marker)}"
matches = re.findall(pattern, response, re.DOTALL)
if matches:
for match in matches:
# 用灰色文本显示思考内容
thinking_text = f"{Fore.LIGHTBLACK_EX}[思考: {match}]{Style.RESET_ALL}"
# 替换思考标记之间的内容,保留思考内容但以灰色显示
response = re.sub(pattern, thinking_text, response, 1)
return response
-205
View File
@@ -1,205 +0,0 @@
import os
import json
import requests
from colorama import Fore, Style
from openai import OpenAI
# 获取项目根目录
current_dir = os.path.dirname(os.path.abspath(__file__))
project_root = os.path.dirname(current_dir)
class Maas_http_engineAI:
"""基于HTTP协议的MaaS(Model as a Service)AI引擎"""
requires_api_key = True # MaaS HTTP引擎需要API密钥
def get_help_info(self):
"""获取引擎帮助信息"""
help_text = """
MaaS HTTP AI引擎插件帮助信息
==========================
引擎名称: maas_http
描述: 基于HTTP协议的MaaS(Model as a Service)AI引擎,支持多种大模型推理服务
可用命令:
无特定命令,直接与AI对话即可
使用说明:
1. 此引擎使用HTTP协议调用MaaS API
2. 使用/engine.switch maas_http切换到此引擎
3. 直接输入自然语言与AI对话
4. 支持普通对话、流式对话、JSON模式等多种调用方式
"""
return help_text
def __init__(self):
self.name = "maas_http"
self.cli = None # 引用CLI实例以訪問插件
# 直接从 config.json 读取配置
config_file = os.path.join(project_root, "config.json")
try:
with open(config_file, 'r', encoding='utf-8') as f:
config = json.load(f)
engine_config = config.get("api", {}).get("engines", {}).get("maas_http", {})
self.api_key = engine_config.get("api_key", "")
self.base_url = engine_config.get("base_url", "")
self.model_id = engine_config.get("model", "")
except Exception as e:
# 如果读取失败,使用空值
self.api_key = ""
self.base_url = ""
self.model_id = ""
# 初始化客户端
self.client = OpenAI(
api_key=self.api_key,
base_url=self.base_url
)
# 初始化对话历史
self.conversation_history = []
# 设置最大历史记录数
try:
with open(config_file, 'r', encoding='utf-8') as f:
config = json.load(f)
self.max_history = config.get("system", {}).get("max_history", 10)
except:
self.max_history = 10
# 共享對話歷史引用(由CLI設置)
self.shared_conversation_history = None
def generate_response(self, user_input, tool_results=None, system_prompt=None):
"""生成AI響應 - 使用MaaS HTTP API"""
try:
# 如果有工具結果,將其作為上下文返回給AI處理
if tool_results:
tool_result_text = f"工具執行結果: {tool_results.get('result', '無結果')}"
# 構建包含歷史記錄的消息列表
messages = self._build_messages_with_history(system_prompt, tool_result_text)
# 調用API
response = self._call_api(messages)
if response:
return response
else:
return "MaaS HTTP API調用失敗"
# 構建包含歷史記錄的消息列表
messages = self._build_messages_with_history(system_prompt, user_input)
# 調用API
response = self._call_api(messages)
if response:
return response
else:
return "MaaS HTTP API調用失敗"
except Exception as e:
# 如果API調用失敗,返回錯誤信息
return f"MaaS HTTP API調用失敗: {str(e)}"
def _call_api(self, messages, use_stream=False, extra_body=None):
"""調用API的核心方法"""
if extra_body is None:
extra_body = {}
try:
response = self.client.chat.completions.create(
model=self.model_id or self.default_model,
messages=messages,
stream=use_stream,
temperature=0.7,
max_tokens=4096,
extra_headers={"lora_id": "0"},
stream_options={"include_usage": True},
extra_body=extra_body
)
if use_stream:
# 處理流式響應
full_response = ""
for chunk in response:
if hasattr(chunk.choices[0].delta, 'content') and chunk.choices[0].delta.content:
content = chunk.choices[0].delta.content
full_response += content
return full_response
else:
# 處理非流式響應
return response.choices[0].message.content
except Exception as e:
print(f"MaaS HTTP API請求出錯: {e}")
return None
def _build_messages_with_history(self, system_prompt, user_input):
"""構建包含歷史記錄的消息列表"""
messages = []
# 添加系統提示詞
if system_prompt:
messages.append({
"role": "system",
"content": system_prompt
})
# 添加歷史對話記錄(使用共享歷史)
if self.shared_conversation_history is not None:
# 確保歷史記錄格式正確
for msg in self.shared_conversation_history[-self.max_history:]:
if isinstance(msg, dict) and "role" in msg and "content" in msg:
messages.append(msg)
else:
# 如果格式不正確,跳過該條記錄
continue
# 添加當前用戶輸入
messages.append({
"role": "user",
"content": user_input
})
return messages
def _call_api_with_history(self, content, system_prompt=None):
"""使用對話歷史調用API"""
# 構建消息列表
messages = []
# 添加系統提示詞(如果提供)
if system_prompt:
messages.append({
"role": "system",
"content": system_prompt
})
# 添加歷史對話記錄(使用共享歷史)
if self.shared_conversation_history is not None:
# 確保歷史記錄格式正確
for msg in self.shared_conversation_history[-self.max_history:]:
if isinstance(msg, dict) and "role" in msg and "content" in msg:
messages.append(msg)
else:
# 如果格式不正確,跳過該條記錄
continue
# 添加當前內容
messages.append({
"role": "tool",
"content": content
})
# 調用API
response = self._call_api(messages)
if response:
return response
else:
return "MaaS HTTP API調用失敗"
def handle_command(self, command):
"""處理引擎特定命令"""
# MaaS HTTP引擎目前不支持特定命令
print(f"{Fore.RED}MaaS HTTP引擎不支持此命令{Style.RESET_ALL}")
return False
-355
View File
@@ -1,355 +0,0 @@
import os
import sys
import json
import requests
from colorama import Fore, Style
from openai import OpenAI
import logging
from datetime import datetime
# 设置日志
logger = logging.getLogger(__name__)
# 添加项目根目录到sys.path
current_dir = os.path.dirname(os.path.abspath(__file__))
project_root = os.path.dirname(current_dir)
sys.path.insert(0, project_root)
class MimoAI:
"""小米MiMoAI引擎 - 使用MiMo-v2-flash模型 - OpenAI格式"""
requires_api_key = True # MiMo引擎需要API密钥
def get_help_info(self):
"""获取引擎帮助信息"""
help_text = """
MiMo AI引擎插件帮助信息
==========================
引擎名称: MiMo
描述: 基于MiMo模型的AI引擎,由小米公司开发
可用命令:
无特定命令,直接与AI对话即可
使用说明:
1. 此引擎使用小米MiMo云API
2. 使用/engine.switch mimo切换到此引擎
3. 直接输入自然语言与AI对话
4. 支持思考模式和多轮对话
"""
return help_text
def __init__(self):
self.name = "mimo"
self.cli = None
# 直接从 config.json 读取配置
config_file = os.path.join(project_root, "config.json")
try:
with open(config_file, 'r', encoding='utf-8') as f:
config = json.load(f)
engine_config = config.get("api", {}).get("engines", {}).get("mimo", {})
self.api_key = engine_config.get("api_key", "")
base_url_raw = engine_config.get("base_url", "")
self.base_url = base_url_raw.rstrip('/') + '/chat/completions' if base_url_raw else ""
self.model = engine_config.get("model", "")
except Exception as e:
# 如果读取失败,使用空值
self.api_key = ""
self.base_url = ""
self.model = ""
# 初始化对话历史
self.conversation_history = []
# 设置最大历史记录数
try:
with open(config_file, 'r', encoding='utf-8') as f:
config = json.load(f)
self.max_history = config.get("system", {}).get("max_history", 10)
except:
self.max_history = 10
# 初始化 headers(用于 API 请求)
self.headers = {
'Authorization': f'Bearer {self.api_key}',
'Content-Type': 'application/json'
}
# 初始化 OpenAI 客户端
try:
if self.api_key and self.base_url:
self.client = OpenAI(
api_key=self.api_key,
base_url=self.base_url.replace('/chat/completions', ''),
timeout=None
)
else:
self.client = None
except Exception as e:
logger.warning(f"初始化 MiMo OpenAI 客户端失败: {e}")
self.client = None
def generate_response(self, user_input, tool_results=None, system_prompt=None):
"""生成AI响应 - 使用MiMo-v2-flash - OpenAI格式"""
if not self.client:
return "MiMo引擎未正确初始化,无法调用API"
try:
# 获取当前日期和星期
now = datetime.now()
date_str = now.strftime("%Y年%m月%d日")
week_str = ["星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日"][now.weekday()]
current_datetime = f"{date_str} {week_str}"
# 设置默认系统提示词(如果未提供)
if not system_prompt:
system_prompt = f"You are MiMo, an AI assistant developed by Xiaomi. Today is date: {current_datetime}. Your knowledge cutoff date is December 2024."
# 如果有工具结果,将其作为上下文返回给AI处理
if tool_results:
tool_result_text = f"工具执行结果: {tool_results.get('result', '无结果')}"
# 构建包含历史记录的消息列表
messages = self._build_messages_with_history(system_prompt, tool_result_text)
# 构建请求体,使用MiMo特定参数
body = {
"model": self.model,
"messages": messages,
"temperature": 0.7,
"max_tokens": 2048,
"top_p": 0.95,
"frequency_penalty": 0,
"presence_penalty": 0,
# MiMo特定参数
"extra_body": {
"thinking": {"type": "disabled"} # 禁用思考模式
}
}
# 直接使用requests调用MiMo API,因为需要特定的header格式
import requests
response = requests.post(
url=self.base_url,
json=body,
headers=self.headers,
timeout=None
)
if response.status_code == 200:
response_data = response.json()
if "choices" in response_data and len(response_data["choices"]) > 0:
ai_response = response_data["choices"][0]["message"]["content"]
# 处理思考标记
processed_response = self._process_thinking_content(ai_response)
# 更新对话历史
self._update_conversation_history(user_input if not tool_results else tool_result_text, ai_response)
# 返回处理后的AI回复,前面加上✦
return processed_response
else:
return "MiMo-v2-flash API返回格式错误"
elif response.status_code == 401:
return "MiMo-v2-flash API认证失败,请检查API密钥是否正确配置"
else:
return f"MiMo-v2-flash API调用失败: {response.status_code} - {response.text}"
# 构建包含历史记录的消息列表
messages = self._build_messages_with_history(system_prompt, user_input)
# 构建请求体,使用MiMo特定参数
body = {
"model": self.model,
"messages": messages,
"temperature": 0.7,
"max_tokens": 2048,
"top_p": 0.95,
"frequency_penalty": 0,
"presence_penalty": 0,
# MiMo特定参数
"extra_body": {
"thinking": {"type": "disabled"} # 禁用思考模式
}
}
# 直接使用requests调用MiMo API,因为需要特定的header格式
import requests
response = requests.post(
url=self.base_url,
json=body,
headers=self.headers,
timeout=None
)
if response.status_code == 200:
response_data = response.json()
if "choices" in response_data and len(response_data["choices"]) > 0:
ai_response = response_data["choices"][0]["message"]["content"]
# 处理思考标记
processed_response = self._process_thinking_content(ai_response)
# 更新对话历史
self._update_conversation_history(user_input, ai_response)
# 返回处理后的AI回复
return processed_response
else:
return "MiMo-v2-flash API返回格式错误"
elif response.status_code == 401:
return "MiMo-v2-flash API认证失败,请检查API密钥是否正确配置"
else:
return f"MiMo-v2-flash API调用失败: {response.status_code} - {response.text}"
except Exception as e:
# 如果API调用失败,返回错误信息
return f"MiMo-v2-flash API调用失败: {str(e)}"
def _build_messages_with_history(self, system_prompt, user_input):
"""构建包含历史记录的消息列表"""
messages = []
# 添加系统提示词
if system_prompt:
messages.append({
"role": "system",
"content": system_prompt
})
# 添加历史对话记录(使用共享历史)
if self.shared_conversation_history is not None:
# 确保历史记录格式正确
for msg in self.shared_conversation_history[-self.max_history:]:
if isinstance(msg, dict) and "role" in msg and "content" in msg:
messages.append(msg)
else:
# 如果格式不正确,跳过该条记录
continue
# 添加当前用户输入
messages.append({
"role": "user",
"content": user_input
})
return messages
def _update_conversation_history(self, user_input, ai_response):
"""更新对话历史"""
# 添加用户输入到历史记录
self.conversation_history.append({
"role": "user",
"content": user_input
})
# 添加AI回复到历史记录
self.conversation_history.append({
"role": "assistant",
"content": ai_response
})
# 限制历史记录数量
if len(self.conversation_history) > self.max_history * 2:
self.conversation_history = self.conversation_history[-self.max_history * 2:]
def _call_api_with_history(self, content, system_prompt=None):
"""使用对话历史调用API"""
# 获取当前日期和星期
now = datetime.now()
date_str = now.strftime("%Y年%m月%d日")
week_str = ["星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日"][now.weekday()]
current_datetime = f"{date_str} {week_str}"
# 设置默认系统提示词(如果未提供)
if not system_prompt:
system_prompt = f"You are MiMo, an AI assistant developed by Xiaomi. Today is date: {current_datetime}. Your knowledge cutoff date is December 2024."
# 构建消息列表
messages = []
# 添加系统提示词(如果提供)
if system_prompt:
messages.append({
"role": "system",
"content": system_prompt
})
# 添加历史对话记录(使用共享历史)
if self.shared_conversation_history is not None:
# 确保历史记录格式正确
for msg in self.shared_conversation_history[-self.max_history:]:
if isinstance(msg, dict) and "role" in msg and "content" in msg:
messages.append(msg)
else:
# 如果格式不正确,跳过该条记录
continue
# 添加当前内容
messages.append({
"role": "user",
"content": content
})
# 构建请求体
body = {
"model": self.model,
"messages": messages,
"max_completion_tokens": 1024,
"temperature": 0.3,
"top_p": 0.95,
"stream": False,
"frequency_penalty": 0,
"presence_penalty": 0,
"extra_body": {
"thinking": {"type": "disabled"}
}
}
# 发送请求到MiMo-v2-flash API
response = requests.post(url=self.base_url, json=body, headers=self.headers, timeout=None)
if response.status_code == 200:
response_data = response.json()
if "choices" in response_data and len(response_data["choices"]) > 0:
ai_response = response_data["choices"][0]["message"]["content"]
# 处理思考标记
processed_response = self._process_thinking_content(ai_response)
return processed_response
else:
return "MiMo-v2-flash API返回格式错误"
else:
return f"MiMo-v2-flash API调用失败: {response.status_code} - {response.text}"
def handle_command(self, command):
"""处理引擎特定命令"""
# MiMo引擎目前不支持特定命令
print(f"{Fore.RED}MiMo引擎不支持此命令{Style.RESET_ALL}")
return False
def _process_thinking_content(self, response):
"""处理思考内容,将思考部分以灰色文本显示,只返回回复部分"""
from colorama import Fore, Style
import re
# 这里可以设置思考标记,由用户自己定义
thinking_start_marker = "<thinking>"
thinking_end_marker = "</thinking>"
# 如果响应中包含思考标记,则提取思考内容和回复内容
if thinking_start_marker in response and thinking_end_marker in response:
# 提取思考内容
thinking_match = re.search(f'{thinking_start_marker}(.*?){thinking_end_marker}', response, re.DOTALL)
if thinking_match:
thinking_content = thinking_match.group(1).strip()
# 打印思考内容(灰色)
print(f"{Fore.LIGHTBLACK_EX}思考: {thinking_content}{Style.RESET_ALL}")
# 提取回复内容
response_content = re.split(f'{thinking_end_marker}', response, maxsplit=1)[-1].strip()
return response_content
# 如果没有思考标记,直接返回响应
return response
+332
View File
@@ -0,0 +1,332 @@
"""
OpenAI 兼容格式引擎
支持所有兼容 OpenAI API 格式的服务(如 DeepSeek、Grok、硅基流动、本地 vLLM 等)
"""
import os
import sys
import json
import requests
from colorama import Fore, Style
import logging
# 设置日志
logger = logging.getLogger(__name__)
# 添加项目根目录到sys.path
current_dir = os.path.dirname(os.path.abspath(__file__))
project_root = os.path.dirname(current_dir)
sys.path.insert(0, project_root)
class OpenaiAI:
"""OpenAI 兼容格式 AI 引擎 - 支持所有兼容 OpenAI API 的服务"""
requires_api_key = True
def get_help_info(self):
"""获取引擎帮助信息"""
help_text = """
OpenAI 兼容格式引擎帮助信息
============================
引擎名称: openai
描述: 通用 OpenAI API 兼容引擎,支持所有兼容 OpenAI 格式的服务
支持的服务:
- OpenAI 官方 API
- DeepSeek API
- Grok (xAI) API
- 硅基流动 (SiliconFlow) API
- 本地 vLLM / LiteLLM / OneAPI 等转发服务
- 任何兼容 /chat/completions 接口的服务
配置方式 (config.json):
{
"api": {
"engines": {
"openai": {
"api_key": "你的API密钥",
"base_url": "https://api.openai.com/v1",
"model": "gpt-4o"
}
}
}
}
可用命令:
/engine.openai models - 列出可用模型(需要服务端支持)
/engine.openai set <模型名> - 切换模型
/engine.openai info - 显示当前配置信息
使用说明:
1. 在 config.json 中配置 api_key、base_url、model
2. 使用 /engine switch openai 切换到此引擎
3. 直接输入自然语言与 AI 对话
注意:
- base_url 填写 API 的基础地址,无需包含 /chat/completions
- 例如 OpenAI 官方填 https://api.openai.com/v1
- 本地服务填 http://localhost:8000/v1
"""
return help_text
def __init__(self):
self.name = "openai"
self.cli = None
# 从 config.json 读取配置
config_file = os.path.join(project_root, "config.json")
try:
with open(config_file, 'r', encoding='utf-8') as f:
config = json.load(f)
engine_config = config.get("api", {}).get("engines", {}).get("openai", {})
self.api_key = engine_config.get("api_key", "")
self.base_url = engine_config.get("base_url", "").rstrip('/')
self.model = engine_config.get("model", "")
self.max_history = config.get("system", {}).get("max_history", 10)
except Exception as e:
logger.warning(f"读取配置文件失败: {e}")
self.api_key = ""
self.base_url = ""
self.model = ""
self.max_history = 10
# 初始化对话历史
self.conversation_history = []
self.shared_conversation_history = None
# 思考标记(兼容深度思考模型)
self.thinking_start_marker = "<think>"
self.thinking_end_marker = "</think>"
# 检查配置
if not self.api_key:
print(f"{Fore.YELLOW}警告: openai 引擎的 API 密钥未设置{Style.RESET_ALL}")
if not self.base_url:
print(f"{Fore.YELLOW}警告: openai 引擎的 base_url 未设置{Style.RESET_ALL}")
else:
print(f"{Fore.GREEN}OpenAI 兼容引擎已启用 (base_url: {self.base_url}){Style.RESET_ALL}")
def _get_headers(self):
"""构建请求头"""
headers = {
'Content-Type': 'application/json'
}
if self.api_key:
headers['Authorization'] = f'Bearer {self.api_key}'
return headers
def _get_api_url(self):
"""获取完整的 API 地址"""
url = self.base_url
if not url:
return ""
if not url.endswith('/chat/completions'):
url = url.rstrip('/') + '/chat/completions'
return url
def generate_response(self, user_input, tool_results=None, system_prompt=None):
"""生成 AI 响应"""
try:
if not self.base_url:
return "错误: openai 引擎的 base_url 未配置,请在 config.json 中设置"
# 处理工具结果
if tool_results:
tool_result_text = f"工具执行结果: {tool_results.get('result', '无结果')}"
messages = self._build_messages_with_history(system_prompt, tool_result_text)
else:
messages = self._build_messages_with_history(system_prompt, user_input)
# 构建请求体
body = {
"model": self.model,
"messages": messages,
"stream": False
}
# 发送请求
api_url = self._get_api_url()
response = requests.post(
url=api_url,
json=body,
headers=self._get_headers(),
timeout=None
)
if response.status_code == 200:
response_data = response.json()
if "choices" in response_data and len(response_data["choices"]) > 0:
ai_response = response_data["choices"][0]["message"]["content"]
# 清理无效的 UTF-8 代理字符
ai_response = ai_response.encode('utf-8', 'replace').decode('utf-8')
# 处理思考内容
ai_response = self._process_thinking_content(ai_response)
return ai_response
else:
return f"API 返回格式错误: {response_data}"
elif response.status_code == 401:
return "API 认证失败,请检查 api_key 是否正确"
elif response.status_code == 404:
return f"API 端点不存在,请检查 base_url 是否正确: {api_url}"
else:
return f"API 调用失败: {response.status_code} - {response.text}"
except requests.exceptions.ConnectionError:
return f"无法连接到 API 服务: {self.base_url},请检查服务是否运行"
except Exception as e:
return f"API 调用失败: {str(e)}"
def _build_messages_with_history(self, system_prompt, user_input):
"""构建包含历史记录的消息列表"""
messages = []
# 添加系统提示词
if system_prompt:
clean_prompt = system_prompt.encode('utf-8', 'replace').decode('utf-8')
messages.append({
"role": "system",
"content": clean_prompt
})
# 添加历史对话记录(使用共享历史)
if self.shared_conversation_history is not None:
for msg in self.shared_conversation_history[-self.max_history:]:
if isinstance(msg, dict) and "role" in msg and "content" in msg:
clean_content = msg["content"].encode('utf-8', 'replace').decode('utf-8')
messages.append({"role": msg["role"], "content": clean_content})
else:
continue
# 添加当前用户输入
if user_input:
clean_input = user_input.encode('utf-8', 'replace').decode('utf-8')
messages.append({
"role": "user",
"content": clean_input
})
return messages
def _call_api_with_history(self, content, system_prompt=None):
"""使用对话历史调用 API"""
if not self.base_url:
return "错误: openai 引擎的 base_url 未配置"
messages = []
if system_prompt:
messages.append({"role": "system", "content": system_prompt})
if self.shared_conversation_history is not None:
for msg in self.shared_conversation_history[-self.max_history:]:
if isinstance(msg, dict) and "role" in msg and "content" in msg:
messages.append(msg)
messages.append({"role": "user", "content": content})
body = {
"model": self.model,
"messages": messages,
"stream": False
}
try:
response = requests.post(
url=self._get_api_url(),
json=body,
headers=self._get_headers(),
timeout=None
)
if response.status_code == 200:
response_data = response.json()
if "choices" in response_data and len(response_data["choices"]) > 0:
ai_response = response_data["choices"][0]["message"]["content"]
ai_response = ai_response.encode('utf-8', 'replace').decode('utf-8')
return self._process_thinking_content(ai_response)
return f"API 调用失败: {response.status_code} - {response.text}"
except Exception as e:
return f"API 调用失败: {str(e)}"
def _process_thinking_content(self, response):
"""处理思考内容,将思考部分以灰色文本显示"""
import re
if not self.thinking_start_marker or not self.thinking_end_marker:
return response
if self.thinking_start_marker in response and self.thinking_end_marker in response:
parts = response.split(self.thinking_start_marker)
if len(parts) > 1:
remaining = parts[1]
if self.thinking_end_marker in remaining:
thinking_part, reply_part = remaining.split(self.thinking_end_marker, 1)
thinking_content = thinking_part.strip()
reply_content = reply_part.strip()
if thinking_content:
print(f"{Fore.LIGHTBLACK_EX}[思考: {thinking_content}]{Style.RESET_ALL}")
return reply_content if reply_content else ""
return response
def set_model(self, model_name):
"""设置模型"""
self.model = model_name
print(f"{Fore.GREEN}已切换模型为: {model_name}{Style.RESET_ALL}")
def list_models(self):
"""列出可用模型(调用 API 的 /models 端点)"""
if not self.base_url:
return ["错误: base_url 未配置"]
try:
models_url = self.base_url.rstrip('/')
if models_url.endswith('/chat/completions'):
models_url = models_url[:-len('/chat/completions')]
models_url = models_url.rstrip('/') + '/models'
response = requests.get(
url=models_url,
headers=self._get_headers(),
timeout=10
)
if response.status_code == 200:
data = response.json()
if "data" in data:
models = [m.get("id", "unknown") for m in data["data"]]
return models
else:
return ["API 返回格式不标准,无法解析模型列表"]
else:
return [f"获取模型列表失败: {response.status_code}"]
except Exception as e:
return [f"获取模型列表失败: {str(e)}"]
def handle_command(self, command):
"""处理引擎特定命令"""
if command == "models":
models = self.list_models()
print(f"{Fore.GREEN}可用模型列表:{Style.RESET_ALL}")
for model in models:
status = " (当前使用)" if model == self.model else ""
print(f"{Fore.GREEN} - {model}{status}{Style.RESET_ALL}")
return True
elif command.startswith("set "):
model_name = command[4:].strip()
if model_name:
self.set_model(model_name)
return True
else:
print(f"{Fore.RED}请提供模型名称. 用法: /engine.openai set <模型名>{Style.RESET_ALL}")
return False
elif command == "info":
print(f"{Fore.GREEN}OpenAI 兼容引擎配置:{Style.RESET_ALL}")
print(f" base_url: {self.base_url}")
print(f" model: {self.model}")
print(f" api_key: {'已设置' if self.api_key else '未设置'}")
return True
else:
print(f"{Fore.RED}未知命令. 可用命令: models, set, info{Style.RESET_ALL}")
return False
-291
View File
@@ -1,291 +0,0 @@
import os
import json
import requests
import threading
import time
from colorama import Fore, Style
class QwqAI:
"""基于QwQ-32B模型的AI引擎"""
def get_help_info(self):
"""获取引擎帮助信息"""
help_text = """
QwQ-32B AI引擎插件帮助信息
==========================
引擎名称: QwQ
描述: 基于QwQ-32B模型的AI引擎,支持复杂推理和数学计算
可用命令:
无特定命令,直接与AI对话即可
使用说明:
1. 此引擎使用QwQ-32B云API
2. 使用/engine.switch qwq切换到此引擎
3. 直接输入自然语言与AI对话
4. 特别适合处理数学、逻辑推理等复杂任务
"""
return help_text
def __init__(self):
self.name = "qwq"
self.cli = None # 引用CLI实例以访问插件
# 直接从 config.json 读取配置
import json
import os
project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
config_file = os.path.join(project_root, "config.json")
try:
with open(config_file, 'r', encoding='utf-8') as f:
config = json.load(f)
engine_config = config.get("api", {}).get("engines", {}).get("qwq", {})
self.api_key = engine_config.get("api_key", "")
self.url = engine_config.get("base_url", "")
self.model = engine_config.get("model", "")
except Exception as e:
# 如果读取失败,使用空值
print(f"{Fore.YELLOW}警告: 读取配置文件失败: {e}{Style.RESET_ALL}")
self.api_key = ""
self.url = ""
self.model = ""
self.headers = {
'Authorization': f'Bearer {self.api_key}',
'Content-Type': 'application/json'
}
# 初始化对话历史
self.conversation_history = []
# 设置最大历史记录数
try:
with open(config_file, 'r', encoding='utf-8') as f:
config = json.load(f)
self.max_history = config.get("system", {}).get("max_history", 10)
except:
self.max_history = 10
# 设置思考标记
self.thinking_start_marker = ""
self.thinking_end_marker = ""
# 共享对话历史引用(由CLI设置)
self.shared_conversation_history = None
def generate_response(self, user_input, tool_results=None, system_prompt=None):
"""生成AI响应 - 使用QwQ API"""
try:
# 如果有工具结果,将其作为上下文返回给AI处理
if tool_results:
tool_result_text = f"工具执行结果: {tool_results.get('result', '无结果')}"
# 构建消息列表
messages = self._build_messages_with_history(system_prompt, tool_result_text)
# 构建请求体
body = {
"model": self.model,
"messages": messages,
"stream": False # 使用非流式响应以便于处理
}
# 发送请求到QwQ API
response = requests.post(url=self.url, json=body, headers=self.headers, timeout=None)
if response.status_code == 200:
response_data = response.json()
if "choices" in response_data and len(response_data["choices"]) > 0:
ai_response = response_data["choices"][0]["message"]["content"]
# 处理思考标记
processed_response = self._process_thinking_content(ai_response)
# 返回处理后的AI回复
return processed_response
else:
return "QwQ API返回格式错误"
else:
return f"QwQ API调用失败: {response.status_code} - {response.text}"
# 构建消息列表
messages = self._build_messages_with_history(system_prompt, user_input)
# 构建请求体
body = {
"model": self.model,
"messages": messages,
"stream": False # 使用非流式响应以便于处理
}
# 发送请求到QwQ API
response = requests.post(url=self.url, json=body, headers=self.headers, timeout=None)
if response.status_code == 200:
response_data = response.json()
if "choices" in response_data and len(response_data["choices"]) > 0:
ai_response = response_data["choices"][0]["message"]["content"]
# 处理思考标记
processed_response = self._process_thinking_content(ai_response)
# 返回处理后的AI回复
return processed_response
else:
return "QwQ API返回格式错误"
else:
return f"QwQ API调用失败: {response.status_code} - {response.text}"
except Exception as e:
# 如果API调用失败,返回错误信息
return f"QwQ API调用失败: {str(e)}"
def _build_messages_with_history(self, system_prompt, user_input):
"""构建包含历史记录的消息列表"""
messages = []
# 添加系统提示词
if system_prompt:
messages.append({
"role": "system",
"content": system_prompt
})
# 添加历史对话记录(使用共享历史)
if self.shared_conversation_history is not None:
# 确保历史记录格式正确
for msg in self.shared_conversation_history[-self.max_history:]:
if isinstance(msg, dict) and "role" in msg and "content" in msg:
messages.append(msg)
else:
# 如果格式不正确,跳过该条记录
continue
# 添加当前用户输入
messages.append({
"role": "user",
"content": user_input
})
return messages
def _call_api_with_history(self, content, system_prompt=None):
"""使用对话历史调用API"""
# 构建消息列表
messages = []
# 添加系统提示词(如果提供)
if system_prompt:
messages.append({
"role": "system",
"content": system_prompt
})
# 添加历史对话记录(使用共享历史)
if self.shared_conversation_history is not None:
# 确保历史记录格式正确
for msg in self.shared_conversation_history[-self.max_history:]:
if isinstance(msg, dict) and "role" in msg and "content" in msg:
messages.append(msg)
else:
# 如果格式不正确,跳过该条记录
continue
# 添加当前内容
messages.append({
"role": "tool",
"content": content
})
# 构建请求体
body = {
"model": self.model,
"messages": messages,
"stream": False # 使用非流式响应以便于处理
}
# 发送请求到QwQ API
response = requests.post(url=self.url, json=body, headers=self.headers, timeout=None)
if response.status_code == 200:
response_data = response.json()
if "choices" in response_data and len(response_data["choices"]) > 0:
ai_response = response_data["choices"][0]["message"]["content"]
# 处理思考标记
processed_response = self._process_thinking_content(ai_response)
return processed_response
else:
return "QwQ API返回格式错误"
else:
return f"QwQ API调用失败: {response.status_code} - {response.text}"
def handle_command(self, command):
"""处理引擎特定命令"""
# QwQ引擎目前不支持特定命令
print(f"{Fore.RED}QwQ引擎不支持此命令{Style.RESET_ALL}")
return False
def _process_thinking_content(self, response):
"""处理思考内容,将思考部分以灰色文本显示,只返回回复部分"""
from colorama import Fore, Style
import re
# 这里可以设置思考标记,由用户自己定义
thinking_start_marker = "<think>" # 用户可以设置开始标记
thinking_end_marker = "</think>" # 用户可以设置结束标记
# 如果用户设置了思考标记,则处理思考内容
if thinking_start_marker and thinking_end_marker and thinking_start_marker in response and thinking_end_marker in response:
# 分离思考内容和回复内容
parts = response.split(thinking_start_marker)
if len(parts) > 1:
# 获取包含结束标记的部分
remaining = parts[1]
thinking_content = ""
reply_content = ""
# 检查结束标记
if thinking_end_marker in remaining:
thinking_part, reply_part = remaining.split(thinking_end_marker, 1)
thinking_content = thinking_part.strip()
reply_content = reply_part.strip()
else:
# 如果没有找到结束标记,将剩余部分都视为思考内容
thinking_content = remaining.strip()
reply_content = ""
# 显示思考内容为灰色文本
if thinking_content:
print(f"{Fore.LIGHTBLACK_EX}[思考: {thinking_content}]{Style.RESET_ALL}")
# 返回回复内容
if reply_content:
return reply_content
else:
return "" # 如果没有回复内容,返回空字符串
else:
# 如果没有设置思考标记或没有找到标记,直接返回原始响应
return response
-275
View File
@@ -1,275 +0,0 @@
import os
import sys
import json
import hashlib
import hmac
import base64
import websocket
import time
import threading
from datetime import datetime
from time import mktime
from urllib.parse import urlparse, urlencode
from wsgiref.handlers import format_date_time
from colorama import Fore, Style
# 获取项目根目录
current_dir = os.path.dirname(os.path.abspath(__file__))
project_root = os.path.dirname(current_dir)
sys.path.insert(0, project_root)
class XfyunSparkAI:
"""基于讯飞星火深度推理X2模型的AI引擎(WebSocket接口)"""
requires_api_key = True # 讯飞引擎需要API密钥
# X2深度推理版本固定配置
URL = "wss://spark-api.xf-yun.com/x2"
DOMAIN = "spark-x"
MAX_TOKENS = 131072 # X2输出上限128K
def __init__(self):
self.name = "xfyun-spark"
self.cli = None
# 直接从 config.json 读取配置
config_file = os.path.join(project_root, "config.json")
try:
with open(config_file, 'r', encoding='utf-8') as f:
config = json.load(f)
engine_config = config.get("api", {}).get("engines", {}).get("xfyun_spark", {})
self.app_id = engine_config.get("app_id", "")
self.api_key = engine_config.get("api_key", "")
self.api_secret = engine_config.get("api_secret", "")
self.max_history = config.get("system", {}).get("max_history", 10)
except Exception as e:
self.app_id = ""
self.api_key = ""
self.api_secret = ""
self.max_history = 10
# 初始化对话历史
self.conversation_history = []
self.shared_conversation_history = None
self.chat_id = f"chat_{self.app_id}_{int(time.time())}" # 固定会话ID,确保上下文连续性
# 验证API密钥
if not self.app_id or not self.api_key or not self.api_secret:
print(f"{Fore.YELLOW}警告: xfyun-spark引擎的API配置未完整设置(需要app_id、api_key、api_secret){Style.RESET_ALL}")
def get_help_info(self):
"""获取引擎帮助信息"""
help_text = f"""
讯飞星火深度推理X2引擎帮助信息
================================
引擎名称: xfyun-spark
描述: 基于讯飞星火深度推理X2模型的AI引擎,支持WebSocket流式输出
版本: X2深度推理 (输入64K, 输出128K)
配置要求:
在config.json中配置:
{{
"api": {{
"engines": {{
"xfyun_spark": {{
"app_id": "你的应用ID",
"api_key": "你的API Key",
"api_secret": "你的API Secret"
}}
}}
}}
}}
使用说明:
1. 使用/engine.switch xfyun-spark切换到此引擎
2. 直接输入自然语言与AI对话
3. 本引擎强制使用流式输出,AI回复会实时显示
4. 支持深度思考模式,思考过程会自动显示
注意: 本引擎使用WebSocket协议,需要app_id、api_key、api_secret三个参数
"""
return help_text
def _generate_url(self):
"""生成鉴权URL(使用官方示例的方式)"""
# 生成RFC1123格式的时间戳
now = datetime.now()
date = format_date_time(mktime(now.timetuple()))
# 解析URL获取host和path
parsed_url = urlparse(self.URL)
host = parsed_url.netloc
path = parsed_url.path
# 拼接签名字符串
signature_origin = "host: " + host + "\n"
signature_origin += "date: " + date + "\n"
signature_origin += "GET " + path + " HTTP/1.1"
# 进行hmac-sha256进行加密
signature_sha = hmac.new(
self.api_secret.encode('utf-8'),
signature_origin.encode('utf-8'),
digestmod=hashlib.sha256
).digest()
signature_sha_base64 = base64.b64encode(signature_sha).decode(encoding='utf-8')
# 拼接authorization_origin
authorization_origin = f'api_key="{self.api_key}", algorithm="hmac-sha256", headers="host date request-line", signature="{signature_sha_base64}"'
authorization = base64.b64encode(authorization_origin.encode('utf-8')).decode(encoding='utf-8')
# 将请求的鉴权参数组合为字典
v = {
"authorization": authorization,
"date": date,
"host": host
}
# 拼接鉴权参数,生成url
url = self.URL + '?' + urlencode(v)
return url
def _build_messages_with_history(self, system_prompt, user_input):
"""构建包含历史记录的消息列表(传递完整历史对话)"""
messages = []
# 添加系统提示词
if system_prompt:
messages.append({
"role": "system",
"content": system_prompt
})
# 添加完整的历史对话(不限制数量,让API自己处理)
if self.shared_conversation_history:
for msg in self.shared_conversation_history:
if isinstance(msg, dict) and "role" in msg and "content" in msg:
role = msg["role"]
content = msg["content"]
# 转换角色名称
if role == "user":
messages.append({"role": "user", "content": content})
elif role == "assistant":
messages.append({"role": "assistant", "content": content})
# 添加当前用户输入
messages.append({
"role": "user",
"content": user_input
})
return messages
def generate_response(self, user_input, tool_results=None, system_prompt=None):
"""生成AI响应(非流式)"""
try:
# 处理工具结果
if tool_results:
tool_result_text = f"工具执行结果: {tool_results.get('result', '无结果')}"
messages = self._build_messages_with_history(system_prompt, tool_result_text)
else:
messages = self._build_messages_with_history(system_prompt, user_input)
# 构建请求数据
request_data = {
"header": {
"app_id": self.app_id,
"uid": str(time.time()).replace('.', '')[:32]
},
"parameter": {
"chat": {
"domain": self.DOMAIN,
"temperature": 0,
"max_tokens": 4096,
"presence_penalty": 1,
"frequency_penalty": 0.02,
"top_k": 5,
"chat_id": self.chat_id
}
},
"payload": {
"message": {
"text": messages
}
}
}
# 生成鉴权URL
auth_url = self._generate_url()
# 收集完整响应
full_response = ""
thinking_content = ""
ws = None
result_queue = []
done_event = threading.Event()
def on_message(ws, message):
nonlocal full_response, thinking_content
try:
data = json.loads(message)
code = data.get("header", {}).get("code", -1)
if code != 0:
result_queue.append(("error", data.get('header', {}).get('message', '未知错误')))
done_event.set()
return
payload = data.get("payload", {})
choices = payload.get("choices", {})
# 处理文本响应
text_list = choices.get("text", [])
if text_list:
reasoning = text_list[0].get("reasoning_content", "")
if reasoning:
thinking_content += reasoning
content = text_list[0].get("content", "")
if content:
full_response += content
if choices.get("status") == 2:
done_event.set()
except Exception as e:
result_queue.append(("error", str(e)))
done_event.set()
def on_error(ws, error):
result_queue.append(("error", str(error)))
done_event.set()
def on_close(ws, close_status_code, close_msg):
pass
def on_open(ws):
ws.send(json.dumps(request_data))
# 创建WebSocket连接
ws = websocket.WebSocketApp(
auth_url,
on_open=on_open,
on_message=on_message,
on_error=on_error,
on_close=on_close
)
# 在后台线程运行WebSocket
ws_thread = threading.Thread(target=ws.run_forever)
ws_thread.daemon = True
ws_thread.start()
# 等待完成
done_event.wait(timeout=60)
# 返回完整响应
return full_response
except Exception as e:
print(f"\n{Fore.RED}讯飞星火API调用失败: {str(e)}{Style.RESET_ALL}")
return f"讯飞星火API调用失败: {str(e)}"