Skip to content

Commit

Permalink
refactor: refactor folder struct (#11)
Browse files Browse the repository at this point in the history
chyroc authored Sep 25, 2024
1 parent c110d5b commit 45e7509
Showing 13 changed files with 293 additions and 25 deletions.
7 changes: 0 additions & 7 deletions cozepy/__init__.py
Original file line number Diff line number Diff line change
@@ -11,8 +11,6 @@
MessageObjectString,
Message,
)
from .conversation import Conversation
from .chat import Chat, ChatEvent, ChatIterator, Event

__all__ = [
"ApplicationOAuth",
@@ -29,9 +27,4 @@
"MessageObjectStringType",
"MessageObjectString",
"Message",
"Conversation",
"Chat",
"ChatEvent",
"ChatIterator",
"Event",
]
27 changes: 27 additions & 0 deletions cozepy/bot/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from typing import TYPE_CHECKING

from cozepy.auth import Auth
from cozepy.request import Requester

if TYPE_CHECKING:
from .v1 import BotClient as BotClientV1


class BotClient(object):
"""
Bot class.
"""

def __init__(self, base_url: str, auth: Auth, requester: Requester):
self._base_url = base_url
self._auth = auth
self._requester = requester
self._v1 = None

@property
def v1(self) -> "BotClientV1":
if not self._v1:
from .v1 import BotClient

self._v1 = BotClient(self._base_url, self._auth, self._requester)
return self._v1
6 changes: 2 additions & 4 deletions cozepy/bot.py → cozepy/bot/v1/__init__.py
Original file line number Diff line number Diff line change
@@ -105,7 +105,7 @@ def __init__(self, base_url: str, auth: Auth, requester: Requester):
self._auth = auth
self._requester = requester

def get_online_info_v1(self, *, bot_id: str) -> Bot:
def get_online_info(self, *, bot_id: str) -> Bot:
"""
Get the configuration information of the bot, which must have been published
to the Bot as API channel.
@@ -118,9 +118,7 @@ def get_online_info_v1(self, *, bot_id: str) -> Bot:

return self._requester.request("get", url, Bot, params=params)

def list_published_bots_v1(
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.
20 changes: 20 additions & 0 deletions cozepy/chat/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from enum import Enum

from cozepy.auth import Auth
from cozepy.request import Requester


class ChatClient(object):
def __init__(self, base_url: str, auth: Auth, requester: Requester):
self._base_url = base_url
self._auth = auth
self._requester = requester
self._v3 = None

@property
def v3(self):
if not self._v3:
from .v3 import ChatClient

self._v3 = ChatClient(self._base_url, self._auth, self._requester)
return self._v3
190 changes: 190 additions & 0 deletions cozepy/chat/v3/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
import json
from enum import Enum
from typing import Dict, List, Iterator, Union

from cozepy.auth import Auth
from cozepy.model import Message, Chat, CozeModel
from cozepy.request import Requester


class Event(str, Enum):
# Event for creating a conversation, indicating the start of the conversation.
# 创建对话的事件,表示对话开始。
conversation_chat_created = "conversation.chat.created"

# The server is processing the conversation.
# 服务端正在处理对话。
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"

# 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"

# The conversation is completed.
# 对话完成。
conversation_chat_completed = "conversation.chat.completed"

# This event is used to mark a failed conversation.
# 此事件用于标识对话失败。
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"

# Error events during the streaming response process. For detailed explanations of code and msg, please refer to Error codes.
# 流式响应过程中的错误事件。关于 code 和 msg 的详细说明,可参考错误码。
error = "error"

# The streaming response for this session ended normally.
# 本次会话的流式返回正常结束。
done = "done"


class ChatEvent(CozeModel):
event: Event
chat: Chat = None
message: Message = None


class ChatIterator(object):
def __init__(self, iters: Iterator[bytes]):
self._iters = iters

def __iter__(self):
return self

def __next__(self) -> ChatEvent:
event = ""
data = ""
line = ""
times = 0

while times < 2:
line = next(self._iters).decode("utf-8")
if line == "":
continue
elif line.startswith("event:"):
if event == "":
event = line[6:]
else:
raise Exception(f"invalid event: {line}")
elif line.startswith("data:"):
if data == "":
data = line[5:]
else:
raise Exception(f"invalid event: {line}")
else:
raise Exception(f"invalid event: {line}")

times += 1

if event == Event.done:
raise StopIteration
elif event == Event.error:
raise Exception(f"error event: {line}")
elif event in [Event.conversation_message_delta, Event.conversation_message_completed]:
return ChatEvent(event=event, message=Message.model_validate(json.loads(data)))
elif event in [
Event.conversation_chat_created,
Event.conversation_chat_in_progress,
Event.conversation_chat_completed,
Event.conversation_chat_failed,
Event.conversation_chat_requires_action,
]:
return ChatEvent(event=event, chat=Chat.model_validate(json.loads(data)))
else:
raise Exception(f"unknown event: {line}")


class ChatClient(object):
def __init__(self, base_url: str, auth: Auth, requester: Requester):
self._base_url = base_url
self._auth = auth
self._requester = requester

def create(
self,
*,
bot_id: str,
user_id: str,
additional_messages: List[Message] = None,
stream: bool = False,
custom_variables: Dict[str, str] = None,
auto_save_history: bool = True,
meta_data: Dict[str, str] = None,
conversation_id: str = None,
) -> Union[Chat, ChatIterator]:
"""
Create a conversation.
Conversation is an interaction between a bot and a user, including one or more messages.
"""
url = f"{self._base_url}/v3/chat"
body = {
"bot_id": bot_id,
"user_id": user_id,
"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,
"conversation_id": conversation_id if conversation_id else None,
"meta_data": meta_data,
}
if not stream:
return self._requester.request("post", url, Chat, body=body, stream=stream)

return ChatIterator(self._requester.request("post", url, Chat, body=body, stream=stream))

def get(
self,
*,
conversation_id: str,
chat_id: str,
) -> Chat:
"""
Create a conversation.
Conversation is an interaction between a bot and a user, including one or more messages.
"""
url = f"{self._base_url}/v3/chat/retrieve"
params = {
"conversation_id": conversation_id,
"chat_id": chat_id,
}
return self._requester.request("post", url, Chat, params=params)

def list_message(
self,
*,
conversation_id: str,
chat_id: str,
) -> List[Message]:
"""
Create a conversation.
Conversation is an interaction between a bot and a user, including one or more messages.
"""
url = f"{self._base_url}/v3/chat/message/list"
params = {
"conversation_id": conversation_id,
"chat_id": chat_id,
}
return self._requester.request("post", url, List[Message], params=params)

def cancel_v3(
self,
*,
conversation_id: str,
chat_id: str,
) -> Chat:
"""
Call this API to cancel an ongoing chat.
"""
url = f"{self._base_url}/v3/chat/cancel"
params = {
"conversation_id": conversation_id,
"chat_id": chat_id,
}
return self._requester.request("post", url, Chat, params=params)
18 changes: 18 additions & 0 deletions cozepy/conversation/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from cozepy.auth import Auth
from cozepy.request import Requester


class ConversationClient(object):
def __init__(self, base_url: str, auth: Auth, requester: Requester):
self._base_url = base_url
self._auth = auth
self._requester = requester
self._v1 = None

@property
def v1(self):
if not self._v1:
from .v1 import ConversationClient

self._v1 = ConversationClient(self._base_url, self._auth, self._requester)
return self._v1
10 changes: 5 additions & 5 deletions cozepy/conversation.py → cozepy/conversation/v1/__init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from typing import Dict, List

from .auth import Auth
from .model import CozeModel, Message
from .request import Requester
from cozepy.auth import Auth
from cozepy.model import CozeModel, Message
from cozepy.request import Requester


class Conversation(CozeModel):
@@ -17,7 +17,7 @@ def __init__(self, base_url: str, auth: Auth, requester: Requester):
self._auth = auth
self._requester = requester

def create_v1(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.
@@ -29,7 +29,7 @@ def create_v1(self, *, messages: List[Message] = None, meta_data: Dict[str, str]
}
return self._requester.request("post", url, Conversation, body=body)

def get_v1(self, *, conversation_id: str) -> Conversation:
def get(self, *, conversation_id: str) -> Conversation:
"""
Get the information of specific conversation.
"""
4 changes: 2 additions & 2 deletions cozepy/coze.py
Original file line number Diff line number Diff line change
@@ -38,15 +38,15 @@ def bot(self) -> "BotClient":
@property
def workspace(self) -> "WorkspaceClient":
if not self._workspace:
from cozepy.workspace import WorkspaceClient
from .workspace import 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
from .conversation import ConversationClient

self._conversation = ConversationClient(self._base_url, self._auth, self._requester)
return self._conversation
22 changes: 22 additions & 0 deletions cozepy/workspace/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from cozepy.auth import Auth
from cozepy.request import Requester


class WorkspaceClient(object):
"""
Bot class.
"""

def __init__(self, base_url: str, auth: Auth, requester: Requester):
self._base_url = base_url
self._auth = auth
self._requester = requester
self._v1 = None

@property
def v1(self):
if not self._v1:
from .v1 import WorkspaceClient

self._v1 = WorkspaceClient(self._base_url, self._auth, self._requester)
return self._v1
2 changes: 1 addition & 1 deletion cozepy/workspace.py → cozepy/workspace/v1/__init__.py
Original file line number Diff line number Diff line change
@@ -40,7 +40,7 @@ def __init__(self, base_url: str, auth: Auth, requester: Requester):
self._auth = auth
self._requester = requester

def list_workspace_v1(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,
2 changes: 1 addition & 1 deletion tests/test_auth.py
Original file line number Diff line number Diff line change
@@ -16,5 +16,5 @@ def test_jwt_auth():
token = app.jwt_auth(private_key, key_id, 30)
assert token.access_token != ""
assert token.token_type == "Bearer"
assert token.expires_in - int(time.time()) <= 30
assert token.expires_in - int(time.time()) <= 31
assert token.refresh_token == ""
2 changes: 1 addition & 1 deletion tests/test_bot.py
Original file line number Diff line number Diff line change
@@ -11,7 +11,7 @@ def test_list_published_bots_v1(self):
auth = TokenAuth(token)
cli = Coze(auth=auth, base_url=COZE_CN_BASE_URL)

res = cli.bot.list_published_bots_v1(space_id=space_id, page_size=2)
res = cli.bot.v1.list(space_id=space_id, page_size=2)
assert res.total > 1
assert res.has_more
assert len(res.items) > 1
8 changes: 4 additions & 4 deletions tests/test_chat.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import os

from cozepy import TokenAuth, Coze, COZE_CN_BASE_URL, Message, ChatIterator, Event
from cozepy import TokenAuth, Coze, COZE_CN_BASE_URL, Message
from cozepy.auth import _random_hex
from cozepy.model import ChatStatus
from cozepy.chat.v3 import ChatIterator, Event


def test_chat_v3_not_stream():
@@ -12,7 +12,7 @@ def test_chat_v3_not_stream():
auth = TokenAuth(token)
cli = Coze(auth=auth, base_url=COZE_CN_BASE_URL)

chat = cli.chat.chat_v3(
chat = cli.chat.v3.create(
bot_id=bot_id,
user_id=_random_hex(10),
additional_messages=[Message.user_text_message("Hi, how are you?")],
@@ -37,7 +37,7 @@ def test_chat_v3_stream():
auth = TokenAuth(token)
cli = Coze(auth=auth, base_url=COZE_CN_BASE_URL)

chat_iter: ChatIterator = cli.chat.chat_v3(
chat_iter: ChatIterator = cli.chat.v3.create(
bot_id=bot_id,
user_id=_random_hex(10),
additional_messages=[Message.user_text_message("Hi, how are you?")],

0 comments on commit 45e7509

Please sign in to comment.