Add support of reply and do not get borred if prev message is borred. Manage memory.
This commit is contained in:
parent
d6942943b5
commit
53ed068519
@ -150,13 +150,19 @@ class AIResponder(AIResponderBase):
|
|||||||
def __init__(self, config: Dict[str, Any], channel: Optional[str] = None) -> None:
|
def __init__(self, config: Dict[str, Any], channel: Optional[str] = None) -> None:
|
||||||
super().__init__(config, channel)
|
super().__init__(config, channel)
|
||||||
self.history: List[Dict[str, Any]] = []
|
self.history: List[Dict[str, Any]] = []
|
||||||
|
self.memory: str = 'I am an assistant.'
|
||||||
self.rate_limit_backoff = exponential_backoff()
|
self.rate_limit_backoff = exponential_backoff()
|
||||||
self.history_file: Optional[Path] = None
|
self.history_file: Optional[Path] = None
|
||||||
|
self.memory_file: Optional[Path] = None
|
||||||
if 'history-directory' in self.config:
|
if 'history-directory' in self.config:
|
||||||
self.history_file = Path(self.config['history-directory']).expanduser() / f'{self.channel}.dat'
|
self.history_file = Path(self.config['history-directory']).expanduser() / f'{self.channel}.dat'
|
||||||
if self.history_file.exists():
|
if self.history_file.exists():
|
||||||
with open(self.history_file, 'rb') as fd:
|
with open(self.history_file, 'rb') as fd:
|
||||||
self.history = pickle.load(fd)
|
self.history = pickle.load(fd)
|
||||||
|
self.memory_file = Path(self.config['history-directory']).expanduser() / f'{self.channel}.memory'
|
||||||
|
if self.memory_file.exists():
|
||||||
|
with open(self.memory_file, 'rb') as fd:
|
||||||
|
self.memory = pickle.load(fd)
|
||||||
|
|
||||||
def message(self, message: AIMessage, limit: Optional[int] = None) -> List[Dict[str, Any]]:
|
def message(self, message: AIMessage, limit: Optional[int] = None) -> List[Dict[str, Any]]:
|
||||||
messages = []
|
messages = []
|
||||||
@ -168,6 +174,7 @@ class AIResponder(AIResponderBase):
|
|||||||
with open(news_feed) as fd:
|
with open(news_feed) as fd:
|
||||||
news_feed = fd.read().strip()
|
news_feed = fd.read().strip()
|
||||||
system = system.replace('{news}', news_feed)
|
system = system.replace('{news}', news_feed)
|
||||||
|
system = system.replace('{memory}', self.memory)
|
||||||
messages.append({"role": "system", "content": system})
|
messages.append({"role": "system", "content": system})
|
||||||
if limit is not None:
|
if limit is not None:
|
||||||
while len(self.history) > limit:
|
while len(self.history) > limit:
|
||||||
@ -240,6 +247,9 @@ class AIResponder(AIResponderBase):
|
|||||||
async def fix(self, answer: str) -> str:
|
async def fix(self, answer: str) -> str:
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
async def memory_rewrite(self, memory: str, user: str, question: str, answer: str) -> str:
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
async def translate(self, text: str, language: str = "english") -> str:
|
async def translate(self, text: str, language: str = "english") -> str:
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
@ -268,6 +278,19 @@ class AIResponder(AIResponderBase):
|
|||||||
with open(self.history_file, 'wb') as fd:
|
with open(self.history_file, 'wb') as fd:
|
||||||
pickle.dump(self.history, fd)
|
pickle.dump(self.history, fd)
|
||||||
|
|
||||||
|
def update_memory(self, memory) -> None:
|
||||||
|
if self.memory_file is not None:
|
||||||
|
with open(self.memory_file, 'wb') as fd:
|
||||||
|
pickle.dump(self.memory, fd)
|
||||||
|
|
||||||
|
async def handle_picture(self, response: Dict) -> bool:
|
||||||
|
if not isinstance(response.get("picture"), (type(None), str)):
|
||||||
|
logging.warning(f"picture key is wrong in response: {pp(response)}")
|
||||||
|
return False
|
||||||
|
if response.get("picture") is not None:
|
||||||
|
response["picture"] = await self.translate(response["picture"])
|
||||||
|
return True
|
||||||
|
|
||||||
async def send(self, message: AIMessage) -> AIResponse:
|
async def send(self, message: AIMessage) -> AIResponse:
|
||||||
# Get the history limit from the configuration
|
# Get the history limit from the configuration
|
||||||
limit = self.config["history-limit"]
|
limit = self.config["history-limit"]
|
||||||
@ -305,15 +328,10 @@ class AIResponder(AIResponderBase):
|
|||||||
retries -= 1
|
retries -= 1
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Check if the response has the correct picture format
|
if not await self.handle_picture(response):
|
||||||
if not isinstance(response.get("picture"), (type(None), str)):
|
|
||||||
logging.warning(f"picture key is wrong in response: {pp(response)}")
|
|
||||||
retries -= 1
|
retries -= 1
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if response.get("picture") is not None:
|
|
||||||
response["picture"] = await self.translate(response["picture"])
|
|
||||||
|
|
||||||
# Post-process the message and update the answer's content
|
# Post-process the message and update the answer's content
|
||||||
answer_message = await self.post_process(message, response)
|
answer_message = await self.post_process(message, response)
|
||||||
answer['content'] = str(answer_message)
|
answer['content'] = str(answer_message)
|
||||||
@ -322,6 +340,11 @@ class AIResponder(AIResponderBase):
|
|||||||
self.update_history(messages[-1], answer, limit, message.historise_question)
|
self.update_history(messages[-1], answer, limit, message.historise_question)
|
||||||
logging.info(f"got this answer:\n{str(answer_message)}")
|
logging.info(f"got this answer:\n{str(answer_message)}")
|
||||||
|
|
||||||
|
# Update memory
|
||||||
|
if answer_message.answer is not None:
|
||||||
|
self.memory = await self.memory_rewrite(self.memory, message.user, message.message, answer_message.answer)
|
||||||
|
self.update_memory(self.memory)
|
||||||
|
|
||||||
# Return the updated answer message
|
# Return the updated answer message
|
||||||
return answer_message
|
return answer_message
|
||||||
|
|
||||||
|
|||||||
@ -73,7 +73,9 @@ class FjerkroaBot(commands.Bot):
|
|||||||
boreness_interval = float(self.config.get('boreness-interval', 12.0))
|
boreness_interval = float(self.config.get('boreness-interval', 12.0))
|
||||||
elapsed_time = (time.monotonic() - self.last_activity_time) / 3600.0
|
elapsed_time = (time.monotonic() - self.last_activity_time) / 3600.0
|
||||||
probability = 1 / (1 + math.exp(-1 * (elapsed_time - (boreness_interval / 2.0)) + math.log(1 / 0.2 - 1)))
|
probability = 1 / (1 + math.exp(-1 * (elapsed_time - (boreness_interval / 2.0)) + math.log(1 / 0.2 - 1)))
|
||||||
if random.random() < probability:
|
prev_messages = await self.chat_channel.history(limit=2).flatten()
|
||||||
|
last_author = prev_messages[1].author.id if len(prev_messages) > 1 else None
|
||||||
|
if random.random() < probability and last_author and last_author != self.user.id:
|
||||||
logging.info(f'Borred with {probability} probability after {elapsed_time}')
|
logging.info(f'Borred with {probability} probability after {elapsed_time}')
|
||||||
boreness_prompt = self.config.get('boreness-prompt', 'Pretend that you just now thought of something, be creative.')
|
boreness_prompt = self.config.get('boreness-prompt', 'Pretend that you just now thought of something, be creative.')
|
||||||
message = AIMessage('system', boreness_prompt, self.config.get('chat-channel', 'chat'), True, False)
|
message = AIMessage('system', boreness_prompt, self.config.get('chat-channel', 'chat'), True, False)
|
||||||
@ -155,6 +157,9 @@ class FjerkroaBot(commands.Bot):
|
|||||||
async def handle_message_through_responder(self, message):
|
async def handle_message_through_responder(self, message):
|
||||||
"""Handle a message through the AI responder"""
|
"""Handle a message through the AI responder"""
|
||||||
message_content = str(message.content).strip()
|
message_content = str(message.content).strip()
|
||||||
|
if message.reference and message.reference.resolved and type(message.reference.resolved.content) == str:
|
||||||
|
reference_content = str(message.reference.resolved.content).replace("\n", "> \n")
|
||||||
|
message_content = f'> {reference_content}\n\n{message_content}'
|
||||||
if len(message_content) < 1:
|
if len(message_content) < 1:
|
||||||
return
|
return
|
||||||
for ma_user in self._re_user.finditer(message_content):
|
for ma_user in self._re_user.finditer(message_content):
|
||||||
|
|||||||
@ -110,3 +110,26 @@ class OpenAIResponder(AIResponder, LeonardoAIDrawMixIn):
|
|||||||
except Exception as err:
|
except Exception as err:
|
||||||
logging.warning(f"failed to translate the text: {repr(err)}")
|
logging.warning(f"failed to translate the text: {repr(err)}")
|
||||||
return text
|
return text
|
||||||
|
|
||||||
|
async def memory_rewrite(self, memory: str, user: str, question: str, answer: str) -> str:
|
||||||
|
if 'memory-model' not in self.config:
|
||||||
|
return memory
|
||||||
|
messages = [{'role': 'system', 'content': self.config.get('memory-system', 'You are an memory assistant.')},
|
||||||
|
{'role': 'user', 'content': f'Here is my previous memory:\n```\n{memory}\n```\n\n'
|
||||||
|
f'Here is my conversanion:\n```\n{user}: {question}\n\nassistant: {answer}\n```\n\n'
|
||||||
|
f'Please rewrite the memory in a way, that it contain the content mentioned in conversation. '
|
||||||
|
f'The whole memory should not be too long, summarize if required. '
|
||||||
|
f'Write just new memory data without any comments.'}]
|
||||||
|
try:
|
||||||
|
logging.info(f'send this memory request:\n{pp(messages)}')
|
||||||
|
result = await openai_chat(self.client,
|
||||||
|
model=self.config['memory-model'],
|
||||||
|
messages=messages,
|
||||||
|
temperature=0.6,
|
||||||
|
max_tokens=4096)
|
||||||
|
new_memory = result.choices[0].message.content
|
||||||
|
logging.info(f'new memory:\n{pp(new_memory)}')
|
||||||
|
return new_memory
|
||||||
|
except Exception as err:
|
||||||
|
logging.warning(f"failed to create new memory: {repr(err)}")
|
||||||
|
return memory
|
||||||
|
|||||||
BIN
openai_chat.dat
BIN
openai_chat.dat
Binary file not shown.
Loading…
Reference in New Issue
Block a user