添加更多种类的发送类型
This commit is contained in:
@@ -6,7 +6,8 @@ class BaseDataModel:
|
||||
def deepcopy(self):
|
||||
return copy.deepcopy(self)
|
||||
|
||||
def temporarily_transform_class_to_dict(obj: Any) -> Any:
|
||||
|
||||
def transform_class_to_dict(obj: Any) -> Any:
|
||||
# sourcery skip: assign-if-exp, reintroduce-else
|
||||
"""
|
||||
将对象或容器中的 BaseDataModel 子类(类对象)或 BaseDataModel 实例
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
from dataclasses import dataclass
|
||||
from typing import Optional, List, Tuple, TYPE_CHECKING, Any
|
||||
from typing import Optional, List, TYPE_CHECKING
|
||||
|
||||
from . import BaseDataModel
|
||||
if TYPE_CHECKING:
|
||||
from src.common.data_models.message_data_model import ReplySetModel
|
||||
from src.llm_models.payload_content.tool_option import ToolCall
|
||||
|
||||
@dataclass
|
||||
@@ -13,4 +14,4 @@ class LLMGenerationDataModel(BaseDataModel):
|
||||
tool_calls: Optional[List["ToolCall"]] = None
|
||||
prompt: Optional[str] = None
|
||||
selected_expressions: Optional[List[int]] = None
|
||||
reply_set: Optional[List[Tuple[str, Any]]] = None
|
||||
reply_set: Optional["ReplySetModel"] = None
|
||||
@@ -1,4 +1,4 @@
|
||||
from typing import Optional, TYPE_CHECKING, List, Tuple, Union
|
||||
from typing import Optional, TYPE_CHECKING, List, Tuple, Union, Dict
|
||||
from dataclasses import dataclass, field
|
||||
from enum import Enum
|
||||
|
||||
@@ -40,9 +40,35 @@ class MessageAndActionModel(BaseDataModel):
|
||||
class ReplyContentType(Enum):
|
||||
TEXT = "text"
|
||||
IMAGE = "image"
|
||||
EMOJI = "emoji"
|
||||
COMMAND = "command"
|
||||
VOICE = "voice"
|
||||
FORWARD = "forward"
|
||||
HYBRID = "hybrid" # 混合类型,包含多种内容
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return self.value
|
||||
|
||||
|
||||
@dataclass
|
||||
class ReplyContent(BaseDataModel):
|
||||
content_type: ReplyContentType | str
|
||||
content: Union[str, Dict, List["ReplyContent"]] # 支持嵌套的 ReplyContent
|
||||
|
||||
def __post_init__(self):
|
||||
if isinstance(self.content_type, ReplyContentType):
|
||||
if self.content_type not in [ReplyContentType.HYBRID, ReplyContentType.FORWARD] and isinstance(
|
||||
self.content, List
|
||||
):
|
||||
raise ValueError(
|
||||
f"非混合类型/转发类型的内容不能是列表,content_type: {self.content_type}, content: {self.content}"
|
||||
)
|
||||
elif self.content_type in [ReplyContentType.HYBRID, ReplyContentType.FORWARD]:
|
||||
if not isinstance(self.content, List):
|
||||
raise ValueError(
|
||||
f"混合类型/转发类型的内容必须是列表,content_type: {self.content_type}, content: {self.content}"
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class ReplySetModel(BaseDataModel):
|
||||
@@ -50,28 +76,42 @@ class ReplySetModel(BaseDataModel):
|
||||
回复集数据模型,用于多种回复类型的返回
|
||||
"""
|
||||
|
||||
reply_set_data: List[Tuple[ReplyContentType | str, Union[str, "ReplySetModel"]]] = field(default_factory=list)
|
||||
reply_data: List[ReplyContent] = field(default_factory=list)
|
||||
|
||||
def __len__(self):
|
||||
return len(self.reply_data)
|
||||
|
||||
def add_text_content(self, text: str):
|
||||
"""添加文本内容"""
|
||||
self.reply_set_data.append((ReplyContentType.TEXT, text))
|
||||
self.reply_data.append(ReplyContent(content_type=ReplyContentType.TEXT, content=text))
|
||||
|
||||
def add_image_content(self, image_base64: str):
|
||||
"""添加图片内容,base64编码的图片数据"""
|
||||
self.reply_set_data.append((ReplyContentType.IMAGE, image_base64))
|
||||
self.reply_data.append(ReplyContent(content_type=ReplyContentType.IMAGE, content=image_base64))
|
||||
|
||||
def add_voice_content(self, voice_base64: str):
|
||||
"""添加语音内容,base64编码的音频数据"""
|
||||
self.reply_set_data.append((ReplyContentType.VOICE, voice_base64))
|
||||
self.reply_data.append(ReplyContent(content_type=ReplyContentType.VOICE, content=voice_base64))
|
||||
|
||||
def add_hybrid_content(self, hybrid_content: "ReplySetModel"):
|
||||
def add_hybrid_content(self, hybrid_content: List[Tuple[ReplyContentType, str]]):
|
||||
"""
|
||||
添加混合型内容,可以包含多种类型的内容
|
||||
|
||||
实际解析时只关注最外层,没有递归嵌套处理
|
||||
"""
|
||||
self.reply_set_data.append((ReplyContentType.HYBRID, hybrid_content))
|
||||
for content_type, content in hybrid_content:
|
||||
assert isinstance(content, str), "混合内容的每个项必须是字符串"
|
||||
self.reply_data.append(ReplyContent(content_type=content_type, content=content))
|
||||
|
||||
def add_custom_content(self, content_type: str, content: str):
|
||||
"""添加自定义类型的内容"""
|
||||
self.reply_set_data.append((content_type, content))
|
||||
self.reply_data.append(ReplyContent(content_type=content_type, content=content))
|
||||
|
||||
def add_forward_content(self, forward_content: List[Tuple[ReplyContentType, Union[str, ReplyContent]]]):
|
||||
"""添加转发内容,可以是字符串或ReplyContent,嵌套的转发内容需要自己构造放入"""
|
||||
for content_type, content in forward_content:
|
||||
if isinstance(content, ReplyContent):
|
||||
self.reply_data.append(content)
|
||||
else:
|
||||
assert isinstance(content, str), "转发内容的每个data必须是字符串或ReplyContent"
|
||||
self.reply_data.append(ReplyContent(content_type=content_type, content=content))
|
||||
|
||||
36
src/common/data_models/message_data_model_ref.md
Normal file
36
src/common/data_models/message_data_model_ref.md
Normal file
@@ -0,0 +1,36 @@
|
||||
# 对于`message_data_model.py`中`class ReplyContent`的规划解读
|
||||
|
||||
分类讨论如下:
|
||||
- `ReplyContent.TEXT`: 单独的文本,`_level = 0`,`content`为`str`类型。
|
||||
- `ReplyContent.IMAGE`: 单独的图片,`_level = 0`,`content`为`str`类型(图片base64)。
|
||||
- `ReplyContent.EMOJI`: 单独的表情包,`_level = 0`,`content`为`str`类型(图片base64)。
|
||||
- `ReplyContent.VOICE`: 单独的语音,`_level = 0`,`content`为`str`类型(语音base64)。
|
||||
- `ReplyContent.HYBRID`: 混合内容,`_level = 0`
|
||||
- 其应该是一个列表,列表内应该只接受`str`类型的内容(图片和文本混合体)
|
||||
- `ReplyContent.FORWARD`: 转发消息,`_level = n`
|
||||
- 其应该是一个列表,列表接受`str`类型(图片/文本),`ReplyContent`类型(嵌套转发,嵌套有最高层数限制)
|
||||
- `ReplyContent.COMMAND`: 指令消息,`_level = 0`
|
||||
- 其应该是一个列表,列表内应该只接受`Dict`类型的内容
|
||||
|
||||
未来规划:
|
||||
- `ReplyContent.AT`: 单独的艾特,`_level = 0`,`content`为`str`类型(用户ID)。
|
||||
|
||||
内容构造方式:
|
||||
- 对于`TEXT`, `IMAGE`, `EMOJI`, `VOICE`,直接传入对应类型的内容,且`content`应该为`str`。
|
||||
- 对于`COMMAND`,传入一个字典,字典内的内容类型应符合上述规定。
|
||||
- 对于`HYBRID`, `FORWARD`,传入一个列表,列表内的内容类型应符合上述规定。
|
||||
|
||||
因此,我们的类型注解应该是:
|
||||
```python
|
||||
from typing import Union, List, Dict
|
||||
|
||||
ReplyContentType = Union[
|
||||
str, # TEXT, IMAGE, EMOJI, VOICE
|
||||
List[Union[str, 'ReplyContent']], # HYBRID, FORWARD
|
||||
Dict # COMMAND
|
||||
]
|
||||
```
|
||||
|
||||
现在`_level`被移除了,在解析的时候显式地检查内容的类型和结构即可。
|
||||
|
||||
`send_api`的custom_reply_set_to_stream仅在特定的类型下提供reply)message
|
||||
Reference in New Issue
Block a user