You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

76 lines
3.4KB

  1. from bot.session_manager import Session
  2. from common.log import logger
  3. class ChatGPTSession(Session):
  4. def __init__(self, session_id, system_prompt=None, model= "gpt-3.5-turbo"):
  5. super().__init__(session_id, system_prompt)
  6. self.messages = []
  7. self.model = model
  8. self.reset()
  9. def reset(self):
  10. system_item = {'role': 'system', 'content': self.system_prompt}
  11. self.messages = [system_item]
  12. def add_query(self, query):
  13. user_item = {'role': 'user', 'content': query}
  14. self.messages.append(user_item)
  15. def add_reply(self, reply):
  16. assistant_item = {'role': 'assistant', 'content': reply}
  17. self.messages.append(assistant_item)
  18. def discard_exceeding(self, max_tokens, cur_tokens= None):
  19. if cur_tokens is None:
  20. cur_tokens = num_tokens_from_messages(self.messages, self.model)
  21. while cur_tokens > max_tokens:
  22. if len(self.messages) > 2:
  23. self.messages.pop(1)
  24. elif len(self.messages) == 2 and self.messages[1]["role"] == "assistant":
  25. self.messages.pop(1)
  26. cur_tokens = num_tokens_from_messages(self.messages, self.model)
  27. break
  28. elif len(self.messages) == 2 and self.messages[1]["role"] == "user":
  29. logger.warn("user message exceed max_tokens. total_tokens={}".format(cur_tokens))
  30. break
  31. else:
  32. logger.debug("max_tokens={}, total_tokens={}, len(messages)={}".format(max_tokens, cur_tokens, len(self.messages)))
  33. break
  34. try:
  35. cur_tokens = num_tokens_from_messages(self.messages, self.model)
  36. except Exception as e:
  37. logger.debug("Exception when counting tokens precisely for query: {}".format(e))
  38. cur_tokens = cur_tokens - max_tokens
  39. return cur_tokens
  40. # refer to https://github.com/openai/openai-cookbook/blob/main/examples/How_to_count_tokens_with_tiktoken.ipynb
  41. def num_tokens_from_messages(messages, model):
  42. """Returns the number of tokens used by a list of messages."""
  43. import tiktoken
  44. try:
  45. encoding = tiktoken.encoding_for_model(model)
  46. except KeyError:
  47. logger.debug("Warning: model not found. Using cl100k_base encoding.")
  48. encoding = tiktoken.get_encoding("cl100k_base")
  49. if model == "gpt-3.5-turbo":
  50. return num_tokens_from_messages(messages, model="gpt-3.5-turbo-0301")
  51. elif model == "gpt-4":
  52. return num_tokens_from_messages(messages, model="gpt-4-0314")
  53. elif model == "gpt-3.5-turbo-0301":
  54. tokens_per_message = 4 # every message follows <|start|>{role/name}\n{content}<|end|>\n
  55. tokens_per_name = -1 # if there's a name, the role is omitted
  56. elif model == "gpt-4-0314":
  57. tokens_per_message = 3
  58. tokens_per_name = 1
  59. else:
  60. logger.warn(f"num_tokens_from_messages() is not implemented for model {model}. Returning num tokens assuming gpt-3.5-turbo-0301.")
  61. return num_tokens_from_messages(messages, model="gpt-3.5-turbo-0301")
  62. num_tokens = 0
  63. for message in messages:
  64. num_tokens += tokens_per_message
  65. for key, value in message.items():
  66. num_tokens += len(encoding.encode(value))
  67. if key == "name":
  68. num_tokens += tokens_per_name
  69. num_tokens += 3 # every reply is primed with <|start|>assistant<|message|>
  70. return num_tokens