Files
ds2api/core/messages.py

119 lines
4.5 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.
# -*- coding: utf-8 -*-
"""消息处理模块"""
import re
from .config import CONFIG, logger
# Claude 默认模型
CLAUDE_DEFAULT_MODEL = "claude-sonnet-4-20250514"
# 预编译正则表达式(性能优化)
_MARKDOWN_IMAGE_PATTERN = re.compile(r"!\[(.*?)\]\((.*?)\)")
# ----------------------------------------------------------------------
# 消息预处理函数,将多轮对话合并成最终 prompt
# ----------------------------------------------------------------------
def messages_prepare(messages: list) -> str:
"""处理消息列表,合并连续相同角色的消息,并添加角色标签:
- 对于 assistant 消息,加上 <Assistant> 前缀及 <end▁of▁sentence> 结束标签;
- 对于 user/system 消息(除第一条外)加上 <User> 前缀;
- 如果消息 content 为数组,则提取其中 type 为 "text" 的部分;
- 最后移除 markdown 图片格式的内容。
"""
processed = []
for m in messages:
role = m.get("role", "")
content = m.get("content", "")
if isinstance(content, list):
texts = [
item.get("text", "") for item in content if item.get("type") == "text"
]
text = "\n".join(texts)
else:
text = str(content)
processed.append({"role": role, "text": text})
if not processed:
return ""
# 合并连续同一角色的消息
merged = [processed[0]]
for msg in processed[1:]:
if msg["role"] == merged[-1]["role"]:
merged[-1]["text"] += "\n\n" + msg["text"]
else:
merged.append(msg)
# 添加标签
parts = []
for idx, block in enumerate(merged):
role = block["role"]
text = block["text"]
if role == "assistant":
parts.append(f"<Assistant>{text}<end▁of▁sentence>")
elif role in ("user", "system"):
if idx > 0:
parts.append(f"<User>{text}")
else:
parts.append(text)
else:
parts.append(text)
final_prompt = "".join(parts)
# 仅移除 markdown 图片格式(不全部移除 !- 使用预编译的正则表达式
final_prompt = _MARKDOWN_IMAGE_PATTERN.sub(r"[\1](\2)", final_prompt)
return final_prompt
# ----------------------------------------------------------------------
# OpenAI到Claude格式转换函数
# ----------------------------------------------------------------------
def convert_claude_to_deepseek(claude_request: dict) -> dict:
"""将Claude格式的请求转换为DeepSeek格式基于现有OpenAI接口"""
messages = claude_request.get("messages", [])
model = claude_request.get("model", CLAUDE_DEFAULT_MODEL)
# 从配置文件读取Claude模型映射
claude_mapping = CONFIG.get(
"claude_model_mapping", {"fast": "deepseek-chat", "slow": "deepseek-chat"}
)
# Claude模型映射到DeepSeek模型 - 基于配置和模型特征判断
if (
"opus" in model.lower()
or "reasoner" in model.lower()
or "slow" in model.lower()
):
deepseek_model = claude_mapping.get("slow", "deepseek-chat")
else:
deepseek_model = claude_mapping.get("fast", "deepseek-chat")
deepseek_request = {"model": deepseek_model, "messages": messages.copy()}
# 处理system消息 - 将system参数转换为system role消息
if "system" in claude_request:
system_msg = {"role": "system", "content": claude_request["system"]}
deepseek_request["messages"].insert(0, system_msg)
# 添加可选参数
if "temperature" in claude_request:
deepseek_request["temperature"] = claude_request["temperature"]
if "top_p" in claude_request:
deepseek_request["top_p"] = claude_request["top_p"]
if "stop_sequences" in claude_request:
deepseek_request["stop"] = claude_request["stop_sequences"]
if "stream" in claude_request:
deepseek_request["stream"] = claude_request["stream"]
return deepseek_request
def convert_deepseek_to_claude_format(
deepseek_response: dict, original_claude_model: str = CLAUDE_DEFAULT_MODEL
) -> dict:
"""将DeepSeek响应转换为Claude格式的OpenAI响应"""
# DeepSeek响应已经是OpenAI格式只需要修改模型名称
if isinstance(deepseek_response, dict):
claude_response = deepseek_response.copy()
claude_response["model"] = original_claude_model
return claude_response
return deepseek_response