Files
xiaoli-cli/ai_engines/xfyun_spark_engine.py
T
Xiaoli Dev e22652fbbb feat: 增强编码能力 - 新增代码编辑器、代码搜索、Git集成
- 新增 plugins/code_editor.py: 精准代码编辑(搜索替换、批量编辑、diff对比)
- 新增 plugins/code_search.py: 代码搜索与理解(跨文件搜索、符号提取、依赖分析)
- 新增 plugins/git_tools.py: Git 版本控制(status/diff/log/commit/branch)
- 新增 core/ 模块: 配置管理、引擎管理、对话处理、显示输出、代码沙箱
- 修复 requirements.txt: 使用正确的 pip 包名(Pillow、opencv-python 等)
- 更新 unified_tool_manager.py: 兼容 Plugin 和 Liugin 两种类名
- 更新系统提示词: 增强编程工作流指导

本次更新由 mimo 自动化执行,与开发者无关。
2026-04-25 10:55:27 +08:00

275 lines
9.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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)}"