chore: initial import of standalone agentscope project
Some checks failed
Pre-commit / run (ubuntu-latest) (push) Has been cancelled
Deploy Sphinx documentation to Pages / build_en (ubuntu-latest, 3.10) (push) Has been cancelled
Deploy Sphinx documentation to Pages / build_zh (ubuntu-latest, 3.10) (push) Has been cancelled
Python Unittest Coverage / test (macos-15, 3.10) (push) Has been cancelled
Python Unittest Coverage / test (macos-15, 3.11) (push) Has been cancelled
Python Unittest Coverage / test (macos-15, 3.12) (push) Has been cancelled
Python Unittest Coverage / test (ubuntu-latest, 3.10) (push) Has been cancelled
Python Unittest Coverage / test (ubuntu-latest, 3.11) (push) Has been cancelled
Python Unittest Coverage / test (ubuntu-latest, 3.12) (push) Has been cancelled
Python Unittest Coverage / test (windows-latest, 3.10) (push) Has been cancelled
Python Unittest Coverage / test (windows-latest, 3.11) (push) Has been cancelled
Python Unittest Coverage / test (windows-latest, 3.12) (push) Has been cancelled
Some checks failed
Pre-commit / run (ubuntu-latest) (push) Has been cancelled
Deploy Sphinx documentation to Pages / build_en (ubuntu-latest, 3.10) (push) Has been cancelled
Deploy Sphinx documentation to Pages / build_zh (ubuntu-latest, 3.10) (push) Has been cancelled
Python Unittest Coverage / test (macos-15, 3.10) (push) Has been cancelled
Python Unittest Coverage / test (macos-15, 3.11) (push) Has been cancelled
Python Unittest Coverage / test (macos-15, 3.12) (push) Has been cancelled
Python Unittest Coverage / test (ubuntu-latest, 3.10) (push) Has been cancelled
Python Unittest Coverage / test (ubuntu-latest, 3.11) (push) Has been cancelled
Python Unittest Coverage / test (ubuntu-latest, 3.12) (push) Has been cancelled
Python Unittest Coverage / test (windows-latest, 3.10) (push) Has been cancelled
Python Unittest Coverage / test (windows-latest, 3.11) (push) Has been cancelled
Python Unittest Coverage / test (windows-latest, 3.12) (push) Has been cancelled
This commit is contained in:
265
src/agentscope/formatter/_deepseek_formatter.py
Normal file
265
src/agentscope/formatter/_deepseek_formatter.py
Normal file
@@ -0,0 +1,265 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# pylint: disable=too-many-branches
|
||||
"""The DeepSeek formatter module."""
|
||||
import json
|
||||
from typing import Any
|
||||
|
||||
from ._truncated_formatter_base import TruncatedFormatterBase
|
||||
from .._logging import logger
|
||||
from ..message import Msg, TextBlock, ToolUseBlock, ToolResultBlock
|
||||
from ..token import TokenCounterBase
|
||||
|
||||
|
||||
class DeepSeekChatFormatter(TruncatedFormatterBase):
|
||||
"""The DeepSeek formatter class for chatbot scenario, where only a user
|
||||
and an agent are involved. We use the `role` field to identify different
|
||||
entities in the conversation.
|
||||
"""
|
||||
|
||||
support_tools_api: bool = True
|
||||
"""Whether support tools API"""
|
||||
|
||||
support_multiagent: bool = False
|
||||
"""Whether support multi-agent conversations"""
|
||||
|
||||
support_vision: bool = False
|
||||
"""Whether support vision data"""
|
||||
|
||||
supported_blocks: list[type] = [
|
||||
TextBlock,
|
||||
# Tool use
|
||||
ToolUseBlock,
|
||||
ToolResultBlock,
|
||||
]
|
||||
"""The list of supported message blocks"""
|
||||
|
||||
async def _format(
|
||||
self,
|
||||
msgs: list[Msg],
|
||||
) -> list[dict[str, Any]]:
|
||||
"""Format message objects into DeepSeek API format.
|
||||
|
||||
Args:
|
||||
msgs (`list[Msg]`):
|
||||
The list of message objects to format.
|
||||
|
||||
Returns:
|
||||
`list[dict[str, Any]]`:
|
||||
The formatted messages as a list of dictionaries.
|
||||
"""
|
||||
self.assert_list_of_msgs(msgs)
|
||||
|
||||
messages: list[dict] = []
|
||||
for msg in msgs:
|
||||
content_blocks: list = []
|
||||
reasoning_content_blocks: list = []
|
||||
tool_calls = []
|
||||
|
||||
for block in msg.get_content_blocks():
|
||||
typ = block.get("type")
|
||||
if typ == "text":
|
||||
content_blocks.append({**block})
|
||||
elif typ == "thinking":
|
||||
reasoning_content_blocks.append({**block})
|
||||
|
||||
elif typ == "tool_use":
|
||||
tool_calls.append(
|
||||
{
|
||||
"id": block.get("id"),
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": block.get("name"),
|
||||
"arguments": json.dumps(
|
||||
block.get("input", {}),
|
||||
ensure_ascii=False,
|
||||
),
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
elif typ == "tool_result":
|
||||
textual_output, _ = self.convert_tool_result_to_string(
|
||||
block.get("output"), # type: ignore[arg-type]
|
||||
)
|
||||
messages.append(
|
||||
{
|
||||
"role": "tool",
|
||||
"tool_call_id": block.get("id"),
|
||||
"content": textual_output,
|
||||
"name": block.get("name"),
|
||||
},
|
||||
)
|
||||
|
||||
else:
|
||||
logger.warning(
|
||||
"Unsupported block type %s in the message, skipped.",
|
||||
typ,
|
||||
)
|
||||
content_msg = "\n".join(
|
||||
content.get("text", "") for content in content_blocks
|
||||
)
|
||||
reasoning_msg = "\n".join(
|
||||
reasoning.get("thinking", "")
|
||||
for reasoning in reasoning_content_blocks
|
||||
)
|
||||
|
||||
msg_deepseek = {
|
||||
"role": msg.role,
|
||||
"content": content_msg or None,
|
||||
}
|
||||
|
||||
if reasoning_msg:
|
||||
msg_deepseek["reasoning_content"] = reasoning_msg
|
||||
|
||||
if tool_calls:
|
||||
msg_deepseek["tool_calls"] = tool_calls
|
||||
|
||||
if msg_deepseek["content"] or msg_deepseek.get("tool_calls"):
|
||||
messages.append(msg_deepseek)
|
||||
|
||||
return messages
|
||||
|
||||
|
||||
class DeepSeekMultiAgentFormatter(TruncatedFormatterBase):
|
||||
"""
|
||||
DeepSeek formatter for multi-agent conversations, where more than
|
||||
a user and an agent are involved.
|
||||
"""
|
||||
|
||||
support_tools_api: bool = True
|
||||
"""Whether support tools API"""
|
||||
|
||||
support_multiagent: bool = True
|
||||
"""Whether support multi-agent conversations"""
|
||||
|
||||
support_vision: bool = False
|
||||
"""Whether support vision data"""
|
||||
|
||||
supported_blocks: list[type] = [
|
||||
TextBlock,
|
||||
# Tool use
|
||||
ToolUseBlock,
|
||||
ToolResultBlock,
|
||||
]
|
||||
"""The list of supported message blocks"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
conversation_history_prompt: str = (
|
||||
"# Conversation History\n"
|
||||
"The content between <history></history> tags contains "
|
||||
"your conversation history\n"
|
||||
),
|
||||
token_counter: TokenCounterBase | None = None,
|
||||
max_tokens: int | None = None,
|
||||
) -> None:
|
||||
"""Initialize the DeepSeek multi-agent formatter.
|
||||
|
||||
Args:
|
||||
conversation_history_prompt (`str`):
|
||||
The prompt to use for the conversation history section.
|
||||
token_counter (`TokenCounterBase | None`, optional):
|
||||
A token counter instance used to count tokens in the messages.
|
||||
If not provided, the formatter will format the messages
|
||||
without considering token limits.
|
||||
max_tokens (`int | None`, optional):
|
||||
The maximum number of tokens allowed in the formatted
|
||||
messages. If not provided, the formatter will not truncate
|
||||
the messages.
|
||||
"""
|
||||
super().__init__(token_counter=token_counter, max_tokens=max_tokens)
|
||||
self.conversation_history_prompt = conversation_history_prompt
|
||||
|
||||
async def _format_tool_sequence(
|
||||
self,
|
||||
msgs: list[Msg],
|
||||
) -> list[dict[str, Any]]:
|
||||
"""Given a sequence of tool call/result messages, format them into
|
||||
the required format for the DeepSeek API.
|
||||
|
||||
Args:
|
||||
msgs (`list[Msg]`):
|
||||
The list of messages containing tool calls/results to format.
|
||||
|
||||
Returns:
|
||||
`list[dict[str, Any]]`:
|
||||
A list of dictionaries formatted for the DeepSeek API.
|
||||
"""
|
||||
return await DeepSeekChatFormatter().format(msgs)
|
||||
|
||||
async def _format_agent_message(
|
||||
self,
|
||||
msgs: list[Msg],
|
||||
is_first: bool = True,
|
||||
) -> list[dict[str, Any]]:
|
||||
"""Given a sequence of messages without tool calls/results, format
|
||||
them into the required format for the DeepSeek API.
|
||||
|
||||
Args:
|
||||
msgs (`list[Msg]`):
|
||||
A list of Msg objects to be formatted.
|
||||
is_first (`bool`, defaults to `True`):
|
||||
Whether this is the first agent message in the conversation.
|
||||
If `True`, the conversation history prompt will be included.
|
||||
|
||||
Returns:
|
||||
`list[dict[str, Any]]`:
|
||||
A list of dictionaries formatted for the DeepSeek API.
|
||||
"""
|
||||
|
||||
if is_first:
|
||||
conversation_history_prompt = self.conversation_history_prompt
|
||||
else:
|
||||
conversation_history_prompt = ""
|
||||
|
||||
# Format into required DeepSeek format
|
||||
formatted_msgs: list[dict] = []
|
||||
|
||||
conversation_blocks: list = []
|
||||
accumulated_text = []
|
||||
for msg in msgs:
|
||||
for block in msg.get_content_blocks():
|
||||
if block["type"] == "text":
|
||||
accumulated_text.append(f"{msg.name}: {block['text']}")
|
||||
|
||||
if accumulated_text:
|
||||
conversation_blocks.append(
|
||||
{"text": "\n".join(accumulated_text)},
|
||||
)
|
||||
|
||||
if conversation_blocks:
|
||||
if conversation_blocks[0].get("text"):
|
||||
conversation_blocks[0]["text"] = (
|
||||
conversation_history_prompt
|
||||
+ "<history>\n"
|
||||
+ conversation_blocks[0]["text"]
|
||||
)
|
||||
|
||||
else:
|
||||
conversation_blocks.insert(
|
||||
0,
|
||||
{
|
||||
"text": conversation_history_prompt + "<history>\n",
|
||||
},
|
||||
)
|
||||
|
||||
if conversation_blocks[-1].get("text"):
|
||||
conversation_blocks[-1]["text"] += "\n</history>"
|
||||
|
||||
else:
|
||||
conversation_blocks.append({"text": "</history>"})
|
||||
|
||||
conversation_blocks_text = "\n".join(
|
||||
conversation_block.get("text", "")
|
||||
for conversation_block in conversation_blocks
|
||||
)
|
||||
|
||||
user_message = {
|
||||
"role": "user",
|
||||
"content": conversation_blocks_text,
|
||||
}
|
||||
|
||||
if conversation_blocks:
|
||||
formatted_msgs.append(user_message)
|
||||
|
||||
return formatted_msgs
|
||||
Reference in New Issue
Block a user