chat_service.py 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. #! /usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. # vim:fenc=utf-8
  4. #
  5. import os
  6. import threading
  7. from typing import List, Dict, Optional
  8. from enum import Enum, auto
  9. import logging
  10. import cozepy
  11. from cozepy import Coze, TokenAuth, Message, ChatStatus, MessageType, JWTOAuthApp, JWTAuth
  12. import time
  13. COZE_API_TOKEN = os.getenv("COZE_API_TOKEN")
  14. COZE_CN_BASE_URL = 'https://api.coze.cn'
  15. VOLCENGINE_API_TOKEN = '5e275c38-44fd-415f-abcf-4b59f6377f72'
  16. VOLCENGINE_BASE_URL = "https://ark.cn-beijing.volces.com/api/v3"
  17. VOLCENGINE_MODEL_DEEPSEEK_V3 = "ep-20250213194558-rrmr2"
  18. VOLCENGINE_MODEL_DOUBAO_PRO_1_5 = 'ep-20250307150409-4blz9'
  19. class ChatServiceType(Enum):
  20. OPENAI_COMPATIBLE = auto
  21. COZE_CHAT = auto()
  22. class CrossAccountJWTOAuthApp(JWTOAuthApp):
  23. def __init__(self, account_id: str, client_id: str, private_key: str, public_key_id: str, base_url):
  24. self.account_id = account_id
  25. super().__init__(client_id, private_key, public_key_id, base_url)
  26. def get_access_token(
  27. self, ttl: int = 900, scope: Optional[cozepy.Scope] = None, session_name: Optional[str] = None
  28. ) -> cozepy.OAuthToken:
  29. jwt_token = self._gen_jwt(self._public_key_id, self._private_key, 3600, session_name)
  30. url = f"{self._base_url}/api/permission/oauth2/account/{self.account_id}/token"
  31. headers = {"Authorization": f"Bearer {jwt_token}"}
  32. body = {
  33. "duration_seconds": ttl,
  34. "grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
  35. "scope": scope.model_dump() if scope else None,
  36. }
  37. return self._requester.request("post", url, False, cozepy.OAuthToken, headers=headers, body=body)
  38. class CozeChat:
  39. def __init__(self, base_url: str, auth_token: Optional[str] = None, auth_app: Optional[JWTOAuthApp] = None):
  40. if not auth_token and not auth_app:
  41. raise ValueError("Either auth_token or auth_app must be provided.")
  42. if auth_token:
  43. self.coze = Coze(auth=TokenAuth(auth_token), base_url=base_url)
  44. else:
  45. self.auth_app = auth_app
  46. oauth_token = auth_app.get_access_token(ttl=12*3600)
  47. self.coze = Coze(auth=JWTAuth(oauth_app=auth_app), base_url=base_url)
  48. self.setup_token_refresh()
  49. def create(self, bot_id: str, user_id: str, messages: List, custom_variables: Dict):
  50. response = self.coze.chat.create_and_poll(
  51. bot_id=bot_id, user_id=user_id, additional_messages=messages,
  52. custom_variables=custom_variables)
  53. logging.debug("Coze response size: {}".format(len(response.messages)))
  54. if response.chat.status != ChatStatus.COMPLETED:
  55. logging.error("Coze chat not completed: {}".format(response.chat.status))
  56. return None
  57. final_response = None
  58. for message in response.messages:
  59. if message.type == MessageType.ANSWER:
  60. final_response = message.content
  61. return final_response
  62. def setup_token_refresh(self):
  63. thread = threading.Thread(target=self.refresh_token_loop)
  64. thread.start()
  65. def refresh_token_loop(self):
  66. while True:
  67. time.sleep(11*3600)
  68. if self.auth_app:
  69. self.auth_app.get_access_token(ttl=12*3600)
  70. @staticmethod
  71. def get_oauth_app(client_id, private_key_path, public_key_id, base_url=None, account_id=None) -> JWTOAuthApp:
  72. if not base_url:
  73. base_url = COZE_CN_BASE_URL
  74. with open(private_key_path, "r") as f:
  75. private_key = f.read()
  76. if not account_id:
  77. jwt_oauth_app = JWTOAuthApp(
  78. client_id=str(client_id),
  79. private_key=private_key,
  80. public_key_id=public_key_id,
  81. base_url=base_url,
  82. )
  83. else:
  84. jwt_oauth_app = CrossAccountJWTOAuthApp(
  85. account_id=account_id,
  86. client_id=str(client_id),
  87. private_key=private_key,
  88. public_key_id=public_key_id,
  89. base_url=base_url,
  90. )
  91. return jwt_oauth_app
  92. if __name__ == '__main__':
  93. # Init the Coze client through the access_token.
  94. coze = Coze(auth=TokenAuth(token=COZE_API_TOKEN), base_url=COZE_CN_BASE_URL)
  95. # Create a bot instance in Coze, copy the last number from the web link as the bot's ID.
  96. bot_id = "7491250992952999973"
  97. # The user id identifies the identity of a user. Developers can use a custom business ID
  98. # or a random string.
  99. user_id = "dev_user"
  100. chat = coze.chat.create_and_poll(
  101. bot_id=bot_id,
  102. user_id=user_id,
  103. additional_messages=[Message.build_user_question_text("钱塘江边 樱花开得不错,推荐一个视频吧")],
  104. custom_variables={
  105. 'agent_name': '芳华',
  106. 'agent_age': '25',
  107. 'agent_region': '北京',
  108. 'name': '李明',
  109. 'preferred_nickname': '李叔',
  110. 'age': '70',
  111. 'last_interaction_interval': '12',
  112. 'current_time_period': '上午',
  113. 'if_first_interaction': 'False',
  114. 'if_active_greeting': 'False'
  115. }
  116. )
  117. for message in chat.messages:
  118. print(message, flush=True)
  119. if chat.chat.status == ChatStatus.COMPLETED:
  120. print("token usage:", chat.chat.usage.token_count)