diff --git a/README.md b/README.md index d30dba8..fc495e5 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,7 @@ chat = coze.chat.create( ], ) start = int(time.time()) -while chat.status == ChatStatus.in_progress: +while chat.status == ChatStatus.IN_PROGRESS: if int(time.time()) - start > 120: # too long, cancel chat coze.chat.cancel(conversation_id=chat.conversation_id, chat_id=chat.chat_id) @@ -87,7 +87,7 @@ chat_iterator = coze.chat.stream( ], ) for event in chat_iterator: - if event.event == ChatEventType.conversation_message_delta: + if event.event == ChatEventType.CONVERSATION_MESSAGE_DELTA: print('got message delta:', event.message.content) ``` @@ -145,7 +145,7 @@ conversation = coze.conversations.retrieve(conversation_id=conversation.id) message = coze.conversations.messages.create( conversation_id=conversation.id, content='how are you?', - content_type=MessageContentType.text, + content_type=MessageContentType.TEXT, ) # retrieve message @@ -156,7 +156,7 @@ coze.conversations.messages.update( conversation_id=conversation.id, message_id=message.id, content='hey, how are you?', - content_type=MessageContentType.text, + content_type=MessageContentType.TEXT, ) # delete message @@ -199,11 +199,11 @@ result = coze.workflows.runs.create( # stream workflow run def handle_workflow_iterator(iterator: WorkflowEventIterator): for event in iterator: - if event.event == WorkflowEventType.message: + if event.event == WorkflowEventType.MESSAGE: print('got message', event.message) - elif event.event == WorkflowEventType.error: + elif event.event == WorkflowEventType.ERROR: print('got error', event.error) - elif event.event == WorkflowEventType.interrupt: + elif event.event == WorkflowEventType.INTERRUPT: handle_workflow_iterator(coze.workflows.runs.resume( workflow_id='workflow id', event_id=event.interrupt.interrupt_data.event_id, diff --git a/cozepy/__init__.py b/cozepy/__init__.py index d326783..637fffb 100644 --- a/cozepy/__init__.py +++ b/cozepy/__init__.py @@ -1,55 +1,54 @@ -from .auth import OAuthToken, DeviceAuthCode, ApplicationOAuth, Auth, TokenAuth, JWTAuth +from .auth import ApplicationOAuth, Auth, DeviceAuthCode, JWTAuth, OAuthToken, TokenAuth from .bots import ( - BotPromptInfo, - BotOnboardingInfo, + Bot, BotModelInfo, + BotOnboardingInfo, BotPluginAPIInfo, BotPluginInfo, - Bot, + BotPromptInfo, SimpleBot, ) from .chat import ( - MessageRole, - MessageType, - MessageContentType, - MessageObjectStringType, - ChatStatus, - MessageObjectString, - Message, Chat, - ChatEventType, - ChatEvent, ChatChatIterator, + ChatEvent, + ChatEventType, + ChatStatus, + Message, + MessageContentType, + MessageObjectString, + MessageObjectStringType, + MessageRole, + MessageType, ) from .config import ( - COZE_COM_BASE_URL, COZE_CN_BASE_URL, - DEFAULT_TIMEOUT, + COZE_COM_BASE_URL, DEFAULT_CONNECTION_LIMITS, + DEFAULT_TIMEOUT, ) from .conversations import Conversation from .coze import Coze -from .exception import CozeError, CozeAPIError, CozeEventError +from .exception import CozeAPIError, CozeError, CozeEventError from .files import File from .model import ( - TokenPaged, - NumberPaged, LastIDPaged, + NumberPaged, + TokenPaged, ) +from .request import HTTPClient +from .version import VERSION from .workflows.runs import ( - WorkflowRunResult, - WorkflowEventType, - WorkflowEventMessage, - WorkflowEventInterruptData, - WorkflowEventInterrupt, - WorkflowEventError, WorkflowEvent, + WorkflowEventError, + WorkflowEventInterrupt, + WorkflowEventInterruptData, WorkflowEventIterator, + WorkflowEventMessage, + WorkflowEventType, + WorkflowRunResult, ) -from .workspaces import WorkspaceRoleType, WorkspaceType, Workspace -from .request import HTTPClient - -from .version import VERSION +from .workspaces import Workspace, WorkspaceRoleType, WorkspaceType __all__ = [ "VERSION", diff --git a/cozepy/auth/__init__.py b/cozepy/auth/__init__.py index 3eff501..68db71b 100644 --- a/cozepy/auth/__init__.py +++ b/cozepy/auth/__init__.py @@ -5,9 +5,9 @@ from authlib.jose import jwt +from cozepy.config import COZE_COM_BASE_URL from cozepy.model import CozeModel from cozepy.request import Requester -from cozepy.config import COZE_COM_BASE_URL def _random_hex(length): @@ -48,9 +48,7 @@ class ApplicationOAuth(object): App OAuth process to support obtaining token and refreshing token. """ - def __init__( - self, client_id: str, client_secret: str = "", base_url: str = COZE_COM_BASE_URL - ): + def __init__(self, client_id: str, client_secret: str = "", base_url: str = COZE_COM_BASE_URL): self._client_id = client_id self._client_secret = client_secret self._base_url = base_url @@ -62,22 +60,16 @@ def jwt_auth(self, private_key: str, kid: str, ttl: int) -> OAuthToken: """ Get the token by jwt with jwt auth flow. """ - jwt_token = self._gen_jwt( - self._api_endpoint, private_key, self._client_id, kid, 3600 - ) + jwt_token = self._gen_jwt(self._api_endpoint, private_key, self._client_id, kid, 3600) url = f"{self._base_url}/api/permission/oauth2/token" headers = {"Authorization": f"Bearer {jwt_token}"} body = { "duration_seconds": ttl, "grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer", } - return self._requester.request( - "post", url, OAuthToken, headers=headers, body=body - ) + return self._requester.request("post", url, OAuthToken, headers=headers, body=body) - def _gen_jwt( - self, api_endpoint: str, private_key: str, client_id: str, kid: str, ttl: int - ): + def _gen_jwt(self, api_endpoint: str, private_key: str, client_id: str, kid: str, ttl: int): now = int(time.time()) header = {"alg": "RS256", "typ": "JWT", "kid": kid} payload = { diff --git a/cozepy/bots/__init__.py b/cozepy/bots/__init__.py index e5f26aa..60a62bd 100644 --- a/cozepy/bots/__init__.py +++ b/cozepy/bots/__init__.py @@ -27,9 +27,9 @@ class BotModelInfo(CozeModel): class BotMode(IntEnum): - SingleAgent = 0 - MultiAgent = 1 - SingleAgentWorkflow = 2 + SINGL_EAGENT = 0 + MULTI_AGENT = 1 + SINGLE_AGENT_WORKFLOW = 2 class BotPluginAPIInfo(CozeModel): @@ -122,9 +122,7 @@ def create( "description": description, "icon_file_id": icon_file_id, "prompt_info": prompt_info.model_dump() if prompt_info else None, - "onboarding_info": onboarding_info.model_dump() - if onboarding_info - else None, + "onboarding_info": onboarding_info.model_dump() if onboarding_info else None, } return self._requester.request("post", url, Bot, body=body) @@ -165,9 +163,7 @@ def update( "description": description, "icon_file_id": icon_file_id, "prompt_info": prompt_info.model_dump() if prompt_info else None, - "onboarding_info": onboarding_info.model_dump() - if onboarding_info - else None, + "onboarding_info": onboarding_info.model_dump() if onboarding_info else None, } return self._requester.request("post", url, None, body=body) @@ -207,9 +203,7 @@ def retrieve(self, *, bot_id: str) -> Bot: return self._requester.request("get", url, Bot, params=params) - def list( - self, *, space_id: str, page_num: int = 1, page_size: int = 20 - ) -> NumberPaged[SimpleBot]: + def list(self, *, space_id: str, page_num: int = 1, page_size: int = 20) -> NumberPaged[SimpleBot]: """ Get the bots published as API service. 查看指定空间发布到 Bot as API 渠道的 Bot 列表。 @@ -233,9 +227,7 @@ def list( "page_size": page_size, "page_index": page_num, } - data = self._requester.request( - "get", url, self._PrivateListPublishedBotsV1Data, params=params - ) + data = self._requester.request("get", url, self._PrivateListPublishedBotsV1Data, params=params) return NumberPaged( items=data.space_bots, page_num=page_num, diff --git a/cozepy/chat/__init__.py b/cozepy/chat/__init__.py index d306bd0..b1ac159 100644 --- a/cozepy/chat/__init__.py +++ b/cozepy/chat/__init__.py @@ -1,5 +1,5 @@ from enum import Enum -from typing import Dict, List, Iterator, Union, TYPE_CHECKING, Optional +from typing import TYPE_CHECKING, Dict, Iterator, List, Optional, Union from cozepy.auth import Auth from cozepy.exception import CozeEventError @@ -12,47 +12,56 @@ class MessageRole(str, Enum): # Indicates that the content of the message is sent by the user. - user = "user" + USER = "user" + # Indicates that the content of the message is sent by the bot. - assistant = "assistant" + ASSISTANT = "assistant" class MessageType(str, Enum): # User input content. # 用户输入内容。 - question = "question" + 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" + ANSWER = "answer" + # Intermediate results of the function (function call) called during the Bot conversation process. # Bot 对话过程中调用函数(function call)的中间结果。 - function_call = "function_call" + FUNCTION_CALL = "function_call" + # Results returned after calling the tool (function call). # 调用工具 (function call)后返回的结果。 - tool_output = "tool_output" + TOOL_OUTPUT = "tool_output" + # Results returned after calling the tool (function call). # 调用工具 (function call)后返回的结果。 - tool_response = "tool_response" + 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" + 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" + VERBOSE = "verbose" - unknown = "" + UNKNOWN = "" class MessageContentType(str, Enum): # Text. # 文本。 - text = "text" + TEXT = "text" + # Multimodal content, that is, a combination of text and files, or a combination of text and images. # 多模态内容,即文本和文件的组合、文本和图片的组合。 - object_string = "object_string" + 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" + CARD = "card" class MessageObjectStringType(str, Enum): @@ -60,9 +69,9 @@ class MessageObjectStringType(str, Enum): The content type of the multimodal message. """ - text = "text" - file = "file" - image = "image" + TEXT = "text" + FILE = "file" + IMAGE = "image" class MessageObjectString(CozeModel): @@ -107,26 +116,22 @@ class Message(CozeModel): updated_at: int = None @staticmethod - def user_text_message( - content: str, meta_data: Optional[Dict[str, str]] = None - ) -> "Message": + def user_text_message(content: str, meta_data: Optional[Dict[str, str]] = None) -> "Message": return Message( - role=MessageRole.user, - type=MessageType.question, + role=MessageRole.USER, + type=MessageType.QUESTION, content=content, - content_type=MessageContentType.text, + content_type=MessageContentType.TEXT, meta_data=meta_data, ) @staticmethod - def assistant_text_message( - content: str, meta_data: Optional[Dict[str, str]] = None - ) -> "Message": + def assistant_text_message(content: str, meta_data: Optional[Dict[str, str]] = None) -> "Message": return Message( - role=MessageRole.assistant, - type=MessageType.answer, + role=MessageRole.ASSISTANT, + type=MessageType.ANSWER, content=content, - content_type=MessageContentType.text, + content_type=MessageContentType.TEXT, meta_data=meta_data, ) @@ -137,19 +142,19 @@ class ChatStatus(str, Enum): """ # The session has been created. - created = "created" + CREATED = "created" # The Bot is processing. - in_progress = "in_progress" + IN_PROGRESS = "in_progress" # The Bot has finished processing, and the session has ended. - completed = "completed" + COMPLETED = "completed" # The session has failed. - failed = "failed" + FAILED = "failed" # The session is interrupted and requires further processing. - requires_action = "requires_action" + REQUIRES_ACTION = "requires_action" class Chat(CozeModel): @@ -183,45 +188,42 @@ class Chat(CozeModel): # Details of the information needed for execution. -# TODO: 枚举值是否需要大写 - - class ChatEventType(str, Enum): # Event for creating a conversation, indicating the start of the conversation. # 创建对话的事件,表示对话开始。 - conversation_chat_created = "conversation.chat.created" + CONVERSATION_CHAT_CREATED = "conversation.chat.created" # The server is processing the conversation. # 服务端正在处理对话。 - conversation_chat_in_progress = "conversation.chat.in_progress" + CONVERSATION_CHAT_IN_PROGRESS = "conversation.chat.in_progress" # Incremental message, usually an incremental message when type=answer. # 增量消息,通常是 type=answer 时的增量消息。 - conversation_message_delta = "conversation.message.delta" + CONVERSATION_MESSAGE_DELTA = "conversation.message.delta" # The message has been completely replied to. At this point, the streaming package contains the spliced results of all message.delta, and each message is in a completed state. # message 已回复完成。此时流式包中带有所有 message.delta 的拼接结果,且每个消息均为 completed 状态。 - conversation_message_completed = "conversation.message.completed" + CONVERSATION_MESSAGE_COMPLETED = "conversation.message.completed" # The conversation is completed. # 对话完成。 - conversation_chat_completed = "conversation.chat.completed" + CONVERSATION_CHAT_COMPLETED = "conversation.chat.completed" # This event is used to mark a failed conversation. # 此事件用于标识对话失败。 - conversation_chat_failed = "conversation.chat.failed" + CONVERSATION_CHAT_FAILED = "conversation.chat.failed" # The conversation is interrupted and requires the user to report the execution results of the tool. # 对话中断,需要使用方上报工具的执行结果。 - conversation_chat_requires_action = "conversation.chat.requires_action" + CONVERSATION_CHAT_REQUIRES_ACTION = "conversation.chat.requires_action" # Error events during the streaming response process. For detailed explanations of code and msg, please refer to Error codes. # 流式响应过程中的错误事件。关于 code 和 msg 的详细说明,可参考错误码。 - error = "error" + ERROR = "error" # The streaming response for this session ended normally. # 本次会话的流式返回正常结束。 - done = "done" + DONE = "done" class ChatEvent(CozeModel): @@ -262,21 +264,21 @@ def __next__(self) -> ChatEvent: times += 1 - if event == ChatEventType.done: + if event == ChatEventType.DONE: raise StopIteration - elif event == ChatEventType.error: + elif event == ChatEventType.ERROR: raise Exception(f"error event: {line}") # TODO: error struct format elif event in [ - ChatEventType.conversation_message_delta, - ChatEventType.conversation_message_completed, + ChatEventType.CONVERSATION_MESSAGE_DELTA, + ChatEventType.CONVERSATION_MESSAGE_COMPLETED, ]: return ChatEvent(event=event, message=Message.model_validate_json(data)) elif event in [ - ChatEventType.conversation_chat_created, - ChatEventType.conversation_chat_in_progress, - ChatEventType.conversation_chat_completed, - ChatEventType.conversation_chat_failed, - ChatEventType.conversation_chat_requires_action, + ChatEventType.CONVERSATION_CHAT_CREATED, + ChatEventType.CONVERSATION_CHAT_IN_PROGRESS, + ChatEventType.CONVERSATION_CHAT_COMPLETED, + ChatEventType.CONVERSATION_CHAT_FAILED, + ChatEventType.CONVERSATION_CHAT_REQUIRES_ACTION, ]: return ChatEvent(event=event, chat=Chat.model_validate_json(data)) else: @@ -388,9 +390,7 @@ def _create( body = { "bot_id": bot_id, "user_id": user_id, - "additional_messages": [i.model_dump() for i in additional_messages] - if additional_messages - else [], + "additional_messages": [i.model_dump() for i in additional_messages] if additional_messages else [], "stream": stream, "custom_variables": custom_variables, "auto_save_history": auto_save_history, @@ -400,9 +400,7 @@ def _create( if not stream: return self._requester.request("post", url, Chat, body=body, stream=stream) - return ChatChatIterator( - self._requester.request("post", url, Chat, body=body, stream=stream) - ) + return ChatChatIterator(self._requester.request("post", url, Chat, body=body, stream=stream)) def retrieve( self, diff --git a/cozepy/config.py b/cozepy/config.py index af9ce93..354e274 100644 --- a/cozepy/config.py +++ b/cozepy/config.py @@ -7,6 +7,4 @@ # default timeout is 10 minutes, with 5 seconds connect timeout DEFAULT_TIMEOUT = httpx.Timeout(timeout=600.0, connect=5.0) -DEFAULT_CONNECTION_LIMITS = httpx.Limits( - max_connections=1000, max_keepalive_connections=100 -) +DEFAULT_CONNECTION_LIMITS = httpx.Limits(max_connections=1000, max_keepalive_connections=100) diff --git a/cozepy/conversations/__init__.py b/cozepy/conversations/__init__.py index 267593a..5332c34 100644 --- a/cozepy/conversations/__init__.py +++ b/cozepy/conversations/__init__.py @@ -19,9 +19,7 @@ def __init__(self, base_url: str, auth: Auth, requester: Requester): self._requester = requester self._messages = None - def create( - self, *, messages: List[Message] = None, meta_data: Dict[str, str] = None - ) -> Conversation: + def create(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. diff --git a/cozepy/conversations/message/__init__.py b/cozepy/conversations/message/__init__.py index c981717..b72bb16 100644 --- a/cozepy/conversations/message/__init__.py +++ b/cozepy/conversations/message/__init__.py @@ -1,7 +1,7 @@ from typing import Dict, List from cozepy.auth import Auth -from cozepy.chat import MessageRole, MessageContentType, Message +from cozepy.chat import Message, MessageContentType, MessageRole from cozepy.model import CozeModel, LastIDPaged from cozepy.request import Requester @@ -89,9 +89,7 @@ def list( "limit": limit, } - res = self._requester.request( - "post", url, self._PrivateListMessageResp, params=params, body=body - ) + res = self._requester.request("post", url, self._PrivateListMessageResp, params=params, body=body) return LastIDPaged(res.items, res.first_id, res.last_id, res.has_more) def retrieve( @@ -152,9 +150,7 @@ def update( "meta_data": meta_data, } - return self._requester.request( - "post", url, Message, params=params, body=body, data_field="message" - ) + return self._requester.request("post", url, Message, params=params, body=body, data_field="message") def delete( self, diff --git a/cozepy/coze.py b/cozepy/coze.py index 6f2d907..38c5d86 100644 --- a/cozepy/coze.py +++ b/cozepy/coze.py @@ -6,12 +6,12 @@ if TYPE_CHECKING: from .bots import BotsClient - from .workspaces import WorkspacesClient - from .conversations import ConversationsClient from .chat import ChatClient + from .conversations import ConversationsClient from .files import FilesClient - from .workflows import WorkflowsClient from .knowledge import KnowledgeClient + from .workflows import WorkflowsClient + from .workspaces import WorkspacesClient class Coze(object): @@ -47,9 +47,7 @@ def workspaces(self) -> "WorkspacesClient": if not self._workspaces: from .workspaces import WorkspacesClient - self._workspaces = WorkspacesClient( - self._base_url, self._auth, self._requester - ) + self._workspaces = WorkspacesClient(self._base_url, self._auth, self._requester) return self._workspaces @property @@ -57,9 +55,7 @@ def conversations(self) -> "ConversationsClient": if not self._conversations: from .conversations import ConversationsClient - self._conversations = ConversationsClient( - self._base_url, self._auth, self._requester - ) + self._conversations = ConversationsClient(self._base_url, self._auth, self._requester) return self._conversations @property @@ -83,9 +79,7 @@ def workflows(self) -> "WorkflowsClient": if not self._workflows: from .workflows import WorkflowsClient - self._workflows = WorkflowsClient( - self._base_url, self._auth, self._requester - ) + self._workflows = WorkflowsClient(self._base_url, self._auth, self._requester) return self._workflows @property @@ -93,7 +87,5 @@ def knowledge(self) -> "KnowledgeClient": if not self._knowledge: from .knowledge import KnowledgeClient - self._knowledge = KnowledgeClient( - self._base_url, self._auth, self._requester - ) + self._knowledge = KnowledgeClient(self._base_url, self._auth, self._requester) return self._knowledge diff --git a/cozepy/knowledge/__init__.py b/cozepy/knowledge/__init__.py index 4594bb5..896aa99 100644 --- a/cozepy/knowledge/__init__.py +++ b/cozepy/knowledge/__init__.py @@ -1,5 +1,6 @@ from cozepy.auth import Auth from cozepy.request import Requester + from .documents import DocumentsClient @@ -13,7 +14,5 @@ def __init__(self, base_url: str, auth: Auth, requester: Requester): @property def documents(self) -> DocumentsClient: if self._documents is None: - self._documents = DocumentsClient( - base_url=self._base_url, auth=self._auth, requester=self._requester - ) + self._documents = DocumentsClient(base_url=self._base_url, auth=self._auth, requester=self._requester) return self._documents diff --git a/cozepy/knowledge/documents/__init__.py b/cozepy/knowledge/documents/__init__.py index 91a1d6f..ada47f1 100644 --- a/cozepy/knowledge/documents/__init__.py +++ b/cozepy/knowledge/documents/__init__.py @@ -36,39 +36,39 @@ class DocumentChunkStrategy(CozeModel): class DocumentFormatType(IntEnum): # Document type, such as txt, pdf, online web pages, etc. # 文档类型,例如 txt 、pdf 、在线网页等格式均属于文档类型。 - document = 0 + DOCUMENT = 0 # 表格类型,例如 xls 表格等格式属于表格类型。 # Spreadsheet type, such as xls spreadsheets, etc. - spreadsheet = 1 + SPREADSHEET = 1 # 照片类型,例如 png 图片等格式属于照片类型。 # Photo type, such as png images, etc. - image = 2 + IMAGE = 2 class DocumentSourceType(IntEnum): # Upload local files. # 上传本地文件。 - local_file = 0 + LOCAL_FILE = 0 # Upload online web pages. # 上传在线网页。 - online_web = 1 + ONLINE_WEB = 1 class DocumentStatus(IntEnum): # Processing # 处理中 - processing = 0 + PROCESSING = 0 # Completed # 处理完毕 - completed = 1 + COMPLETED = 1 # Processing failed, it is recommended to re-upload # 处理失败,建议重新上传 - failed = 9 + FAILED = 9 class DocumentUpdateType(IntEnum): @@ -196,9 +196,7 @@ def list( "page": page_num, "size": page_size, } - res = self._requester.request( - "get", url, self._PrivateListDocumentsV1Data, params=params - ) + res = self._requester.request("get", url, self._PrivateListDocumentsV1Data, params=params) return NumberPaged( items=res.document_infos, page_num=page_num, diff --git a/cozepy/model.py b/cozepy/model.py index 7afc513..6ebc93e 100644 --- a/cozepy/model.py +++ b/cozepy/model.py @@ -1,4 +1,4 @@ -from typing import TypeVar, Generic, List +from typing import Generic, List, TypeVar from pydantic import BaseModel, ConfigDict @@ -28,9 +28,7 @@ class TokenPaged(PagedBase[T]): return is next_page_token + has_more. """ - def __init__( - self, items: List[T], next_page_token: str = "", has_more: bool = None - ): + def __init__(self, items: List[T], next_page_token: str = "", has_more: bool = None): has_more = has_more if has_more is not None else next_page_token != "" super().__init__(items, has_more) self.next_page_token = next_page_token @@ -40,9 +38,7 @@ def __repr__(self): class NumberPaged(PagedBase[T]): - def __init__( - self, items: List[T], page_num: int, page_size: int, total: int = None - ): + def __init__(self, items: List[T], page_num: int, page_size: int, total: int = None): has_more = len(items) >= page_size super().__init__(items, has_more) self.page_num = page_num @@ -50,7 +46,9 @@ def __init__( self.total = total def __repr__(self): - return f"NumberPaged(items={self.items}, page_num={self.page_num}, page_size={self.page_size}, total={self.total})" + return ( + f"NumberPaged(items={self.items}, page_num={self.page_num}, page_size={self.page_size}, total={self.total})" + ) class LastIDPaged(PagedBase[T]): diff --git a/cozepy/request.py b/cozepy/request.py index 2fcc136..cbcdb95 100644 --- a/cozepy/request.py +++ b/cozepy/request.py @@ -1,13 +1,13 @@ from typing import ( TYPE_CHECKING, - Tuple, - Optional, - Union, - List, + Any, Iterator, + List, + Optional, + Tuple, Type, TypeVar, - Any, + Union, ) import httpx @@ -15,7 +15,7 @@ from pydantic import BaseModel from typing_extensions import get_args, get_origin # compatibility with python 3.7 -from cozepy.config import DEFAULT_TIMEOUT, DEFAULT_CONNECTION_LIMITS +from cozepy.config import DEFAULT_CONNECTION_LIMITS, DEFAULT_TIMEOUT from cozepy.exception import CozeAPIError from cozepy.version import user_agent @@ -48,7 +48,7 @@ def request( self, method: str, url: str, - model: Union[Type[T], List[Type[T]]], + model: Union[Type[T], List[Type[T]], None], params: dict = None, headers: dict = None, body: dict = None, @@ -115,9 +115,7 @@ def _make_request( files=files, ) - def _parse_requests_code_msg( - self, response: Response, data_field: str = "data" - ) -> Tuple[Optional[int], str, Any]: + def _parse_requests_code_msg(self, response: Response, data_field: str = "data") -> Tuple[Optional[int], str, Any]: try: json = response.json() except Exception as e: # noqa: E722 diff --git a/cozepy/workflows/runs/__init__.py b/cozepy/workflows/runs/__init__.py index 95890ad..8c43e63 100644 --- a/cozepy/workflows/runs/__init__.py +++ b/cozepy/workflows/runs/__init__.py @@ -1,8 +1,8 @@ from enum import Enum -from typing import Dict, Any, Iterator +from typing import Any, Dict, Iterator -from cozepy.exception import CozeEventError from cozepy.auth import Auth +from cozepy.exception import CozeEventError from cozepy.model import CozeModel from cozepy.request import Requester @@ -16,21 +16,21 @@ class WorkflowEventType(str, Enum): # The output message from the workflow node, such as the output message from # the message node or end node. You can view the specific message content in data. # 工作流节点输出消息,例如消息节点、结束节点的输出消息。可以在 data 中查看具体的消息内容。 - message = "Message" + MESSAGE = "Message" # An error has occurred. You can view the error_code and error_message in data to # troubleshoot the issue. # 报错。可以在 data 中查看 error_code 和 error_message,排查问题。 - error = "Error" + ERROR = "Error" # End. Indicates the end of the workflow execution, where data is empty. # 结束。表示工作流执行结束,此时 data 为空。 - done = "Done" + DONE = "Done" # Interruption. Indicates the workflow has been interrupted, where the data field # contains specific interruption information. # 中断。表示工作流中断,此时 data 字段中包含具体的中断信息。 - interrupt = "Interrupt" + INTERRUPT = "Interrupt" class WorkflowEventMessage(CozeModel): @@ -76,7 +76,8 @@ class WorkflowEventInterrupt(CozeModel): class WorkflowEventError(CozeModel): - # Status code. 0 represents a successful API call. Other values indicate that the call has failed. You can determine the detailed reason for the error through the error_message field. + # Status code. 0 represents a successful API call. Other values indicate that the call has failed. You can + # determine the detailed reason for the error through the error_message field. # 调用状态码。0 表示调用成功。其他值表示调用失败。你可以通过 error_message 字段判断详细的错误原因。 error_code: int @@ -110,7 +111,6 @@ def __next__(self) -> WorkflowEvent: id = "" event = "" data = "" - line = "" times = 0 while times < 3: @@ -119,7 +119,7 @@ def __next__(self) -> WorkflowEvent: continue elif line.startswith("id:"): if event == "": - id = line[3:] + id = line[3:].strip() else: raise CozeEventError("id", line) elif line.startswith("event:"): @@ -129,7 +129,7 @@ def __next__(self) -> WorkflowEvent: raise CozeEventError("event", line) elif line.startswith("data:"): if data == "": - data = line[5:] + data = line[5:].strip() else: raise CozeEventError("data", line) else: @@ -137,19 +137,17 @@ def __next__(self) -> WorkflowEvent: times += 1 - if event == WorkflowEventType.done: + if event == WorkflowEventType.DONE: raise StopIteration - elif event == WorkflowEventType.message: + elif event == WorkflowEventType.MESSAGE: return WorkflowEvent( id=id, event=event, message=WorkflowEventMessage.model_validate_json(data), ) - elif event == WorkflowEventType.error: - return WorkflowEvent( - id=id, event=event, error=WorkflowEventError.model_validate_json(data) - ) - elif event == WorkflowEventType.interrupt: + elif event == WorkflowEventType.ERROR: + return WorkflowEvent(id=id, event=event, error=WorkflowEventError.model_validate_json(data)) + elif event == WorkflowEventType.INTERRUPT: return WorkflowEvent( id=id, event=event, @@ -227,9 +225,7 @@ def stream( "bot_id": bot_id, "ext": ext, } - return WorkflowEventIterator( - self._requester.request("post", url, None, body=body, stream=True) - ) + return WorkflowEventIterator(self._requester.request("post", url, None, body=body, stream=True)) def resume( self, @@ -255,6 +251,4 @@ def resume( "resume_data": resume_data, "interrupt_type": interrupt_type, } - return WorkflowEventIterator( - self._requester.request("post", url, None, body=body, stream=True) - ) + return WorkflowEventIterator(self._requester.request("post", url, None, body=body, stream=True)) diff --git a/cozepy/workspaces/__init__.py b/cozepy/workspaces/__init__.py index bb78598..24bd289 100644 --- a/cozepy/workspaces/__init__.py +++ b/cozepy/workspaces/__init__.py @@ -8,14 +8,14 @@ class WorkspaceRoleType(str, Enum): - owner = "owner" - admin = "admin" - member = "member" + OWNER = "owner" + ADMIN = "admin" + MEMBER = "member" class WorkspaceType(str, Enum): - personal = "personal" - team = "team" + PERSONAL = "personal" + TEAM = "team" class Workspace(CozeModel): @@ -41,9 +41,7 @@ def __init__(self, base_url: str, auth: Auth, requester: Requester): self._auth = auth self._requester = requester - def list( - self, *, page_num: int = 1, page_size: int = 20, headers=None - ) -> NumberPaged[Workspace]: + def list(self, *, page_num: int = 1, page_size: int = 20, headers=None) -> NumberPaged[Workspace]: url = f"{self._base_url}/v1/workspaces" params = { "page_size": page_size, diff --git a/pyproject.toml b/pyproject.toml index 2695549..7185e9f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,6 +19,13 @@ pytest-cov = "^4.0.0" ruff = "^0.6.0" pre-commit = "^2.9.0" +[tool.ruff] +line-length = 120 + +[tool.ruff.lint] +select = ["E", "F", "I"] +ignore = ["E501"] + [build-system] requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" diff --git a/tests/config.py b/tests/config.py index 2c5b593..8148982 100644 --- a/tests/config.py +++ b/tests/config.py @@ -1,14 +1,11 @@ import os -from cozepy import ApplicationOAuth, COZE_CN_BASE_URL, JWTAuth, TokenAuth +from cozepy import COZE_CN_BASE_URL, ApplicationOAuth, JWTAuth, TokenAuth COZE_JWT_AUTH_CLIENT_ID = os.getenv("COZE_JWT_AUTH_CLIENT_ID").strip() COZE_JWT_AUTH_PRIVATE_KEY = os.getenv("COZE_JWT_AUTH_PRIVATE_KEY").strip() COZE_JWT_AUTH_KEY_ID = os.getenv("COZE_JWT_AUTH_KEY_ID").strip() -if ( - COZE_JWT_AUTH_PRIVATE_KEY == "" - and os.getenv("COZE_JWT_AUTH_PRIVATE_KEY_FILE").strip() != "" -): +if COZE_JWT_AUTH_PRIVATE_KEY == "" and os.getenv("COZE_JWT_AUTH_PRIVATE_KEY_FILE").strip() != "": with open(os.getenv("COZE_JWT_AUTH_PRIVATE_KEY_FILE").strip(), "r") as f: COZE_JWT_AUTH_PRIVATE_KEY = f.read() diff --git a/tests/test_chat.py b/tests/test_chat.py index 74a0fd3..c6b62e8 100644 --- a/tests/test_chat.py +++ b/tests/test_chat.py @@ -1,6 +1,6 @@ import os -from cozepy import Coze, COZE_CN_BASE_URL, Message, ChatChatIterator, ChatEventType +from cozepy import COZE_CN_BASE_URL, ChatChatIterator, ChatEventType, Coze, Message from cozepy.auth import _random_hex from tests.config import fixed_token_auth @@ -40,5 +40,5 @@ def test_chat_stream(): for item in chat_iter: assert item is not None assert item.event != "" - if item.event == ChatEventType.conversation_message_delta: + if item.event == ChatEventType.CONVERSATION_MESSAGE_DELTA: assert item.message.content != "" diff --git a/tests/test_conversation_message.py b/tests/test_conversation_message.py index eba3d6a..923fb88 100644 --- a/tests/test_conversation_message.py +++ b/tests/test_conversation_message.py @@ -1,6 +1,7 @@ import time import unittest -from cozepy import Coze, COZE_CN_BASE_URL, Message + +from cozepy import COZE_CN_BASE_URL, Coze, Message from tests.config import fixed_token_auth @@ -35,16 +36,12 @@ def test_conversation_message(): time.sleep(1) # retrieve message - message_retrieve = cli.conversations.messages.retrieve( - conversation_id=conversation.id, message_id=message.id - ) + message_retrieve = cli.conversations.messages.retrieve(conversation_id=conversation.id, message_id=message.id) assert message_retrieve is not None assert message.id == message_retrieve.id # list message - message_list = cli.conversations.messages.list( - conversation_id=conversation.id, message_id=message.id - ) + message_list = cli.conversations.messages.list(conversation_id=conversation.id, message_id=message.id) assert len(message_list) > 2 # update message