diff --git a/cozepy/__init__.py b/cozepy/__init__.py index 925b5c6..fe3a6d2 100644 --- a/cozepy/__init__.py +++ b/cozepy/__init__.py @@ -2,6 +2,15 @@ from .config import COZE_COM_BASE_URL, COZE_CN_BASE_URL from .coze import Coze from .model import TokenPaged, NumberPaged +from .conversation import ( + MessageRole, + MessageType, + MessageContentType, + MessageObjectStringType, + MessageObjectString, + Message, + Conversation, +) __all__ = [ "ApplicationOAuth", @@ -12,4 +21,11 @@ "Coze", "TokenPaged", "NumberPaged", + "MessageRole", + "MessageType", + "MessageContentType", + "MessageObjectStringType", + "MessageObjectString", + "Message", + "Conversation", ] diff --git a/cozepy/conversation.py b/cozepy/conversation.py new file mode 100644 index 0000000..a682d75 --- /dev/null +++ b/cozepy/conversation.py @@ -0,0 +1,139 @@ +from enum import Enum +from typing import Dict, List, Optional + +from .auth import Auth +from .model import CozeModel +from .request import Requester + + +class MessageRole(str, Enum): + # Indicates that the content of the message is sent by the user. + user = "user" + # Indicates that the content of the message is sent by the bot. + assistant = "assistant" + + +class MessageType(str, Enum): + # User input content. + # 用户输入内容。 + question = "question" + # The message content returned by the Bot to the user, supporting incremental return. If the workflow is bound to a message node, there may be multiple answer scenarios, and the end flag of the streaming return can be used to determine that all answers are completed. + # Bot 返回给用户的消息内容,支持增量返回。如果工作流绑定了消息节点,可能会存在多 answer 场景,此时可以用流式返回的结束标志来判断所有 answer 完成。 + answer = "answer" + # Intermediate results of the function (function call) called during the Bot conversation process. + # Bot 对话过程中调用函数(function call)的中间结果。 + function_call = "function_call" + # Results returned after calling the tool (function call). + # 调用工具 (function call)后返回的结果。 + tool_output = "tool_output" + # Results returned after calling the tool (function call). + # 调用工具 (function call)后返回的结果。 + tool_response = "tool_response" + # If the user question suggestion switch is turned on in the Bot configuration, the reply content related to the recommended questions will be returned. + # 如果在 Bot 上配置打开了用户问题建议开关,则会返回推荐问题相关的回复内容。不支持在请求中作为入参。 + follow_up = "follow_up" + # In the scenario of multiple answers, the server will return a verbose package, and the corresponding content is in JSON format. content.msg_type = generate_answer_finish represents that all answers have been replied to. + # 多 answer 场景下,服务端会返回一个 verbose 包,对应的 content 为 JSON 格式,content.msg_type =generate_answer_finish 代表全部 answer 回复完成。不支持在请求中作为入参。 + verbose = "verbose" + + +class MessageContentType(str, Enum): + # Text. + # 文本。 + text = "text" + # Multimodal content, that is, a combination of text and files, or a combination of text and images. + # 多模态内容,即文本和文件的组合、文本和图片的组合。 + object_string = "object_string" + # message card. This enum value only appears in the interface response and is not supported as an input parameter. + # 卡片。此枚举值仅在接口响应中出现,不支持作为入参。 + card = "card" + + +class MessageObjectStringType(str, Enum): + """ + The content type of the multimodal message. + """ + + text = "text" + file = "file" + image = "image" + + +class MessageObjectString(CozeModel): + # The content type of the multimodal message. + # 多模态消息内容类型 + type: MessageObjectStringType + # Text content. Required when type is text. + # 文本内容。 + text: str + # The ID of the file or image content. + # 在 type 为 file 或 image 时,file_id 和 file_url 应至少指定一个。 + file_id: str + # The online address of the file or image content.
Must be a valid address that is publicly accessible. + # file_id or file_url must be specified when type is file or image. + # 文件或图片内容的在线地址。必须是可公共访问的有效地址。 + # 在 type 为 file 或 image 时,file_id 和 file_url 应至少指定一个。 + file_url: str + + +class Message(CozeModel): + # The entity that sent this message. + role: MessageRole + # The type of message. + # type: # MessageType + # The content of the message. It supports various types of content, including plain text, multimodal (a mix of text, images, and files), message cards, and more. + # 消息的内容,支持纯文本、多模态(文本、图片、文件混合输入)、卡片等多种类型的内容。 + content: str + # The type of message content. + # 消息内容的类型 + content_type: MessageContentType + # Additional information when creating a message, and this additional information will also be returned when retrieving messages. + # Custom key-value pairs should be specified in Map object format, with a length of 16 key-value pairs. The length of the key should be between 1 and 64 characters, and the length of the value should be between 1 and 512 characters. + # 创建消息时的附加消息,获取消息时也会返回此附加消息。 + # 自定义键值对,应指定为 Map 对象格式。长度为 16 对键值对,其中键(key)的长度范围为 1~64 个字符,值(value)的长度范围为 1~512 个字符。 + meta_data: Optional[Dict[str, str]] = None + + @staticmethod + def user_text_message(content: str, meta_data: Optional[Dict[str, str]] = None) -> "Message": + return Message( + role=MessageRole.user, + type=MessageType.question, + content=content, + content_type=MessageContentType.text, + meta_data=meta_data, + ) + + @staticmethod + def assistant_text_message(content: str, meta_data: Optional[Dict[str, str]] = None) -> "Message": + return Message( + role=MessageRole.assistant, + type=MessageType.answer, + content=content, + content_type=MessageContentType.text, + meta_data=meta_data, + ) + + +class Conversation(CozeModel): + id: str + created_at: int + meta_data: Dict[str, str] + + +class ConversationClient(object): + def __init__(self, base_url: str, auth: Auth, requester: Requester): + self._base_url = base_url + self._auth = auth + self._requester = requester + + def create_v1(self, *, messages: List[Message] = None, meta_data: Dict[str, str] = None) -> Conversation: + """ + Create a conversation. + Conversation is an interaction between a bot and a user, including one or more messages. + """ + url = f"{self._base_url}/v1/conversation/create" + body = { + "messages": [i.model_dump() for i in messages] if len(messages) > 0 else [], + "meta_data": meta_data, + } + return self._requester.request("get", url, Conversation, body=body) diff --git a/cozepy/coze.py b/cozepy/coze.py index aa52892..a4cbe44 100644 --- a/cozepy/coze.py +++ b/cozepy/coze.py @@ -7,6 +7,7 @@ if TYPE_CHECKING: from .bot import BotClient from .workspace import WorkspaceClient + from .conversation import ConversationClient class Coze(object): @@ -22,6 +23,7 @@ def __init__( # service client self._bot = None self._workspace = None + self._conversation = None @property def bot(self) -> "BotClient": @@ -38,3 +40,11 @@ def workspace(self) -> "WorkspaceClient": self._workspace = WorkspaceClient(self._base_url, self._auth, self._requester) return self._workspace + + @property + def conversation(self) -> "ConversationClient": + if not self._conversation: + from cozepy.conversation import ConversationClient + + self._conversation = ConversationClient(self._base_url, self._auth, self._requester) + return self._conversation diff --git a/pyproject.toml b/pyproject.toml index de5e9e2..1222743 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "cozepy" -version = "0.1.0" +version = "0.1.1" description = "OpenAPI SDK for Coze(coze.com/coze.cn)" authors = ["chyroc "] readme = "README.md" diff --git a/tests/test_bot.py b/tests/test_bot.py index 333ce3a..f6a2e03 100644 --- a/tests/test_bot.py +++ b/tests/test_bot.py @@ -1,7 +1,7 @@ import os from unittest import TestCase -from cozepy import TokenAuth, Coze +from cozepy import TokenAuth, Coze, COZE_CN_BASE_URL class TestBotClient(TestCase): @@ -11,7 +11,7 @@ def test_list_published_bots_v1(self): for i in token: print("token", i) auth = TokenAuth(token) - cli = Coze(auth=auth, base_url="https://api.coze.cn") + cli = Coze(auth=auth, base_url=COZE_CN_BASE_URL) res = cli.bot.list_published_bots_v1(space_id=space_id, page_size=2) assert res.total > 1