log number of retries left, add error message to msg

This commit is contained in:
Cyberes 2023-05-24 13:52:40 -06:00
parent c4ffd82698
commit d53517e3fb
1 changed files with 72 additions and 22 deletions

View File

@ -7,12 +7,16 @@ from typing import List, Optional, Union
import stopit import stopit
from markdown import markdown from markdown import markdown
from nio import (AsyncClient, ErrorResponse, Event, MatrixRoom, MegolmEvent, Response, RoomGetEventResponse, RoomMessageText, RoomSendResponse, SendRetryError, ) from nio import AsyncClient, ErrorResponse, Event, MatrixRoom, MegolmEvent, Response, RoomGetEventResponse, \
RoomMessageText, RoomSendResponse, SendRetryError
logger = logging.getLogger('MatrixGPT') logger = logging.getLogger('MatrixGPT')
async def send_text_to_room(client: AsyncClient, room_id: str, message: str, notice: bool = False, markdown_convert: bool = True, reply_to_event_id: Optional[str] = None, thread: bool = False, thread_root_id: str = None) -> Union[RoomSendResponse, ErrorResponse]: async def send_text_to_room(client: AsyncClient, room_id: str, message: str, notice: bool = False,
markdown_convert: bool = True, reply_to_event_id: Optional[str] = None,
thread: bool = False, thread_root_id: str = None, extra_error: str = False,
extra_msg: str = False) -> Union[RoomSendResponse, ErrorResponse]:
"""Send text to a matrix room. """Send text to a matrix room.
Args: Args:
@ -42,10 +46,24 @@ async def send_text_to_room(client: AsyncClient, room_id: str, message: str, not
if reply_to_event_id: if reply_to_event_id:
if thread: if thread:
content["m.relates_to"] = {'event_id': thread_root_id, 'is_falling_back': True, "m.in_reply_to": {"event_id": reply_to_event_id}, 'rel_type': "m.thread"} content["m.relates_to"] = {
'event_id': thread_root_id,
'is_falling_back': True,
"m.in_reply_to": {
"event_id": reply_to_event_id
},
'rel_type': "m.thread"
}
else: else:
content["m.relates_to"] = {"m.in_reply_to": {"event_id": reply_to_event_id}} content["m.relates_to"] = {
"m.in_reply_to": {
"event_id": reply_to_event_id
}
}
content["m.matrixgpt"] = {
"error": str(extra_error),
"msg": str(extra_msg),
}
try: try:
return await client.room_send(room_id, "m.room.message", content, ignore_unverified_devices=True) return await client.room_send(room_id, "m.room.message", content, ignore_unverified_devices=True)
except SendRetryError: except SendRetryError:
@ -70,7 +88,9 @@ def make_pill(user_id: str, displayname: str = None) -> str:
return f'<a href="https://matrix.to/#/{user_id}">{displayname}</a>' return f'<a href="https://matrix.to/#/{user_id}">{displayname}</a>'
async def react_to_event(client: AsyncClient, room_id: str, event_id: str, reaction_text: str, ) -> Union[Response, ErrorResponse]: async def react_to_event(client: AsyncClient, room_id: str, event_id: str, reaction_text: str, extra_error: str = False,
extra_msg: str = False) -> Union[
Response, ErrorResponse]:
"""Reacts to a given event in a room with the given reaction text """Reacts to a given event in a room with the given reaction text
Args: Args:
@ -88,7 +108,17 @@ async def react_to_event(client: AsyncClient, room_id: str, event_id: str, react
Raises: Raises:
SendRetryError: If the reaction was unable to be sent. SendRetryError: If the reaction was unable to be sent.
""" """
content = {"m.relates_to": {"rel_type": "m.annotation", "event_id": event_id, "key": reaction_text}} content = {
"m.relates_to": {
"rel_type": "m.annotation",
"event_id": event_id,
"key": reaction_text
},
"m.matrixgpt": {
"error": str(extra_error),
"msg": str(extra_msg),
}
}
return await client.room_send(room_id, "m.reaction", content, ignore_unverified_devices=True, ) return await client.room_send(room_id, "m.reaction", content, ignore_unverified_devices=True, )
@ -119,7 +149,8 @@ def check_command_prefix(string: str, prefixes: dict):
return False, None, None return False, None, None
async def is_this_our_thread(client: AsyncClient, room: MatrixRoom, event: RoomMessageText, command_prefixes: dict) -> tuple[bool, any, any]: async def is_this_our_thread(client: AsyncClient, room: MatrixRoom, event: RoomMessageText, command_prefixes: dict) -> \
tuple[bool, any, any]:
base_event_id = event.source['content'].get('m.relates_to', {}).get('event_id') base_event_id = event.source['content'].get('m.relates_to', {}).get('event_id')
if base_event_id: if base_event_id:
e = await client.room_get_event(room.room_id, base_event_id) e = await client.room_get_event(room.room_id, base_event_id)
@ -140,8 +171,11 @@ async def get_thread_content(client: AsyncClient, room: MatrixRoom, base_event:
messages.append(new_event) messages.append(new_event)
else: else:
break break
new_event = (await client.room_get_event(room.room_id, new_event.source['content']['m.relates_to']['m.in_reply_to']['event_id'])).event new_event = (await client.room_get_event(room.room_id,
messages.append((await client.room_get_event(room.room_id, base_event.source['content']['m.relates_to']['event_id'])).event) # put the root event in the array new_event.source['content']['m.relates_to']['m.in_reply_to'][
'event_id'])).event
messages.append((await client.room_get_event(room.room_id, base_event.source['content']['m.relates_to'][
'event_id'])).event) # put the root event in the array
messages.reverse() messages.reverse()
return messages return messages
@ -193,16 +227,24 @@ async def process_chat(
@stopit.threading_timeoutable(default=(None, None)) @stopit.threading_timeoutable(default=(None, None))
async def generate(): async def generate():
if openai_model.startswith('gpt-3') or openai_model.startswith('gpt-4'): if openai_model.startswith('gpt-3') or openai_model.startswith('gpt-4'):
r = await loop.run_in_executor(None, functools.partial(openai_obj.ChatCompletion.create, model=openai_model, messages=messages, temperature=openai_temperature, timeout=20)) r = await loop.run_in_executor(None, functools.partial(openai_obj.ChatCompletion.create,
model=openai_model, messages=messages,
temperature=openai_temperature, timeout=20))
return r.choices[0].message.content return r.choices[0].message.content
elif openai_model in ['text-davinci-003', 'davinci-instruct-beta', 'text-davinci-001', 'text-davinci-002', 'text-curie-001', 'text-babbage-001']: elif openai_model in ['text-davinci-003', 'davinci-instruct-beta', 'text-davinci-001',
r = await loop.run_in_executor(None, functools.partial(openai_obj.Completion.create, model=openai_model, temperature=openai_temperature, timeout=20, max_tokens=4096)) 'text-davinci-002', 'text-curie-001', 'text-babbage-001']:
r = await loop.run_in_executor(None,
functools.partial(openai_obj.Completion.create, model=openai_model,
temperature=openai_temperature, timeout=20,
max_tokens=4096))
return r.choices[0].text return r.choices[0].text
else: else:
raise Exception(f'Model {openai_model} not found!') raise Exception(f'Model {openai_model} not found!')
response = None response = None
openai_gen_error = None
for i in range(openai_retries): for i in range(openai_retries):
sleep_time = i * 5
try: try:
task = asyncio.create_task(generate(timeout=20)) task = asyncio.create_task(generate(timeout=20))
asyncio.as_completed(task) asyncio.as_completed(task)
@ -210,24 +252,29 @@ async def process_chat(
if response is not None: if response is not None:
break break
else: else:
logger.warning(f'Response to event {event.event_id} was null, retrying.') openai_gen_error = 'response was null'
time.sleep(2) logger.warning(
f'Response to event {event.event_id} was null, retrying {i}/{openai_retries} after {sleep_time}s.')
# time.sleep(2)
except Exception as e: # (stopit.utils.TimeoutException, openai.error.APIConnectionError) except Exception as e: # (stopit.utils.TimeoutException, openai.error.APIConnectionError)
logger.warning(f'Got exception when generating response to event {event.event_id}, retrying: {e}') openai_gen_error = e
logger.warning(
f'Got exception when generating response to event {event.event_id}, retrying {i}/{openai_retries} after {sleep_time}s. Error: {e}')
await client.room_typing(room.room_id, typing_state=True, timeout=15000) await client.room_typing(room.room_id, typing_state=True, timeout=15000)
time.sleep(2) time.sleep(sleep_time)
continue continue
if response is None: if response is None:
logger.critical(f'Response to event {event.event_id} in room {room.room_id} was null.') logger.critical(f'Response to event {event.event_id} in room {room.room_id} was null.')
await client.room_typing(room.room_id, typing_state=False, timeout=15000) await client.room_typing(room.room_id, typing_state=False, timeout=15000)
await react_to_event(client, room.room_id, event.event_id, '') await react_to_event(client, room.room_id, event.event_id, '', extra_error=openai_gen_error)
return return
text_response = response.strip().strip('\n') text_response = response.strip().strip('\n')
# Logging stuff # Logging stuff
if log_full_response: if log_full_response:
logger.debug({'event_id': event.event_id, 'room': room.room_id, 'messages': messages, 'response': response}) logger.debug(
{'event_id': event.event_id, 'room': room.room_id, 'messages': messages, 'response': response})
z = text_response.replace("\n", "\\n") z = text_response.replace("\n", "\\n")
if isinstance(command, str): if isinstance(command, str):
x = command.replace("\n", "\\n") x = command.replace("\n", "\\n")
@ -237,7 +284,9 @@ async def process_chat(
x = command x = command
logger.info(f'Reply to {event.event_id} --> "{x}" and bot ({openai_model}) responded with "{z}"') logger.info(f'Reply to {event.event_id} --> "{x}" and bot ({openai_model}) responded with "{z}"')
resp = await send_text_to_room(client, room.room_id, text_response, reply_to_event_id=event.event_id, thread=True, thread_root_id=thread_root_id if thread_root_id else event.event_id) resp = await send_text_to_room(client, room.room_id, text_response, reply_to_event_id=event.event_id,
thread=True,
thread_root_id=thread_root_id if thread_root_id else event.event_id)
await client.room_typing(room.room_id, typing_state=False, timeout=3000) await client.room_typing(room.room_id, typing_state=False, timeout=3000)
store.add_event_id(event.event_id) store.add_event_id(event.event_id)
@ -250,6 +299,7 @@ async def process_chat(
await react_to_event(client, room.room_id, event.event_id, '') await react_to_event(client, room.room_id, event.event_id, '')
raise raise
def check_authorized(string, to_check): def check_authorized(string, to_check):
def check_str(s, c): def check_str(s, c):
if c != 'all': if c != 'all':