simplify copilot response, adjust error responses, other minor changes

This commit is contained in:
Cyberes 2024-04-11 14:32:38 -06:00
parent 72640ae35f
commit 0d52be41db
8 changed files with 50 additions and 54 deletions

View File

@ -35,7 +35,7 @@ I included a sample Systemd service (`matrixgpt.service`).
## Use
First, invite your bot to a room. Then you can start a chat by prefixing your message with your trigger (for
example, `!c hello!`). The bot will create a thread when it replies. You don't need to use the trigger in the thread.
example, `!c hello`). The bot will create a thread when it replies. You don't need to use the trigger in the thread.
Use `!matrixgpt` to view the bot's help. The bot also responds to `!bots`.

0
main.py Normal file → Executable file
View File

View File

@ -50,7 +50,7 @@ async def generate_ai_response(
context = [{'role': api_client.HUMAN_NAME, 'content': context}]
# Build the context and do the things that need to be done for our specific API type.
api_client.assemble_context(context, system_prompt=command_info.system_prompt, injected_system_prompt=command_info.injected_system_prompt)
api_client.prepare_context(context, system_prompt=command_info.system_prompt, injected_system_prompt=command_info.injected_system_prompt)
if api_client.check_ignore_request():
logger.debug(f'Reply to {event.event_id} was ignored by the model "{command_info.model}".')
@ -82,7 +82,7 @@ async def generate_ai_response(
room.room_id,
event.event_id,
'',
extra_error='Exception' if global_config['send_extra_messages'] else None
extra_error='Exception while generating AI response' if global_config['send_extra_messages'] else None
)
await client.room_typing(room.room_id, typing_state=False, timeout=1000)
return
@ -93,7 +93,7 @@ async def generate_ai_response(
room.room_id,
event.event_id,
'',
extra_error='Response was null.' if global_config['send_extra_messages'] else None
extra_error='AI response was empty' if global_config['send_extra_messages'] else None
)
await client.room_typing(room.room_id, typing_state=False, timeout=1000)
return
@ -135,7 +135,7 @@ async def generate_ai_response(
await client.room_typing(room.room_id, typing_state=False, timeout=1000)
if not isinstance(resp, RoomSendResponse):
logger.critical(f'Failed to respond to event {event.event_id} in room {room.room_id}:\n{vars(resp)}')
await client_helper.react_to_event(room.room_id, event.event_id, '', extra_error='Exception' if global_config['send_extra_messages'] else None)
except Exception:
await client_helper.react_to_event(room.room_id, event.event_id, '', extra_error='Exception' if global_config['send_extra_messages'] else None)
await client_helper.react_to_event(room.room_id, event.event_id, '', extra_error='Exception while responding to event' if global_config['send_extra_messages'] else None)
except Exception as e:
await client_helper.react_to_event(room.room_id, event.event_id, '', extra_error=f'Exception during response process: {e}' if global_config['send_extra_messages'] else None)
raise

View File

@ -16,7 +16,7 @@ class AnthropicApiClient(ApiClient):
api_key=self._api_key
)
def assemble_context(self, context: list, system_prompt: str = None, injected_system_prompt: str = None):
def prepare_context(self, context: list, system_prompt: str = None, injected_system_prompt: str = None):
assert not len(self._context)
self._context = context
self.verify_context()
@ -28,19 +28,19 @@ class AnthropicApiClient(ApiClient):
i = 0
while i < len(self._context) - 1:
if self._context[i]['role'] == self._context[i + 1]['role']:
dummy = self.generate_text_msg(f'<{self._BOT_NAME} did not respond>', self._BOT_NAME) if self._context[i]['role'] == self._HUMAN_NAME else self.generate_text_msg(f'<{self._HUMAN_NAME} did not respond>', self._HUMAN_NAME)
dummy = self.text_msg(f'<{self._BOT_NAME} did not respond>', self._BOT_NAME) if self._context[i]['role'] == self._HUMAN_NAME else self.text_msg(f'<{self._HUMAN_NAME} did not respond>', self._HUMAN_NAME)
self._context.insert(i + 1, dummy)
i += 1
# if self._context[-1]['role'] == self._HUMAN_NAME:
# self._context.append(self.generate_text_msg(f'<{self._BOT_NAME} did not respond>', self._BOT_NAME))
def generate_text_msg(self, content: str, role: str):
def text_msg(self, content: str, role: str):
assert role in [self._HUMAN_NAME, self._BOT_NAME]
return {"role": role, "content": [{"type": "text", "text": str(content)}]}
def append_msg(self, content: str, role: str):
assert role in [self._HUMAN_NAME, self._BOT_NAME]
self._context.append(self.generate_text_msg(content, role))
self._context.append(self.text_msg(content, role))
async def append_img(self, img_event: RoomMessageImage, role: str):
assert role in [self._HUMAN_NAME, self._BOT_NAME]

View File

@ -20,33 +20,51 @@ class ApiClient:
def _create_client(self, base_url: str = None):
raise NotImplementedError
def check_ignore_request(self):
def check_ignore_request(self) -> bool:
"""
If the bot wants to ignore and not respond to a request.
"""
return False
def assemble_context(self, context: list, system_prompt: str = None, injected_system_prompt: str = None):
def prepare_context(self, context: list, system_prompt: str = None, injected_system_prompt: str = None) -> None:
"""
Prepare and process the context that has already been loaded.
"""
assert not len(self._context)
raise NotImplementedError
def generate_text_msg(self, content: str, role: str):
def text_msg(self, content: str, role: str) -> None:
"""
Create a message of the type text with the given content and role.
"""
raise NotImplementedError
def append_msg(self, content: str, role: str):
def append_msg(self, content: str, role: str) -> None:
"""
Add a message of the type text to the context.
"""
raise NotImplementedError
async def append_img(self, img_event: RoomMessageImage, role: str):
async def append_img(self, img_event: RoomMessageImage, role: str) -> None:
"""
Add a message of the type image to the context.
"""
raise NotImplementedError
async def generate(self, command_info: CommandInfo, matrix_gpt_data: str = None) -> Tuple[str, dict | None]:
"""
Generate a response.
"""
raise NotImplementedError
@property
def context(self):
def context(self) -> list:
return self._context.copy()
@property
def HUMAN_NAME(self):
def HUMAN_NAME(self) -> str:
return self._HUMAN_NAME
@property
def BOT_NAME(self):
def BOT_NAME(self) -> str:
return self._BOT_NAME

View File

@ -1,12 +1,10 @@
import json
import re
import time
from urllib.parse import urlparse
from cryptography.fernet import Fernet
from nio import RoomMessageImage
from sydney import SydneyClient
from sydney.exceptions import ThrottledRequestException
from matrix_gpt.config import global_config
from matrix_gpt.generate_clients.api_client import ApiClient
@ -45,12 +43,7 @@ class CopilotClient(ApiClient):
async def append_img(self, img_event: RoomMessageImage, role: str):
raise NotImplementedError
# def check_ignore_request(self):
# if len(self._context) > 1:
# return True
# return False
def assemble_context(self, context: list, system_prompt: str = None, injected_system_prompt: str = None):
def prepare_context(self, context: list, system_prompt: str = None, injected_system_prompt: str = None):
assert not len(self._context)
self._context = context
for i in range(len(self._context)):
@ -60,8 +53,8 @@ class CopilotClient(ApiClient):
async def generate(self, command_info: CommandInfo, matrix_gpt_data: str = None):
# TODO: config option for style
async with SydneyClient(bing_cookies=self._api_key, style='precise') as sydney:
# Ignore any exceptions doing this since they will be caught by the caller.
if matrix_gpt_data:
# Ignore any exceptions doing this since they will be caught by the caller.
decrypted_metadata = decrypt_string(matrix_gpt_data)
conversation_metadata = json.loads(decrypted_metadata)
sydney.conversation_signature = conversation_metadata["conversation_signature"]
@ -70,26 +63,9 @@ class CopilotClient(ApiClient):
sydney.client_id = conversation_metadata["client_id"]
sydney.invocation_id = conversation_metadata["invocation_id"]
response = None
for i in range(3):
try:
response = dict(await sydney.ask(self._context[-1]['content'], citations=True, raw=True))
break
except ThrottledRequestException:
time.sleep(10)
if not response:
# If this happens you should first try to change your cookies.
# Otherwise, you've used all your credits for today.
raise ThrottledRequestException
bot_response = response['item']['messages'][-1]
text_card = {}
for msg in bot_response['adaptiveCards'][0]['body']:
if msg.get('type') == 'TextBlock':
text_card = msg
break
response_text = text_card.get('text', '')
response_text = await sydney.ask(self._context[-1]['content'], citations=True)
if not len(response_text):
raise Exception('Copilot response was empty')
# Parse the attribution links.
attributions_strs = []
@ -135,9 +111,11 @@ class CopilotClient(ApiClient):
)
if len(self._context) == 1:
# Add this disclaimer because the owner of the Microsoft account that the bot uses can go and view
# his conversation history and view everything the bot has done.
response_text += _COPILOT_WARNING_STR
# Store the conversation metadata in the response. It's encrypted for privacy purposes.
# Store the conversation metadata in the response Matrix event. It's encrypted for privacy purposes.
custom_data = {
'thread_root_event': self._event.event_id,
'data': encrypt_string(event_data)

View File

@ -41,7 +41,7 @@ class OpenAIClient(ApiClient):
}]
})
def assemble_context(self, context: list, system_prompt: str = None, injected_system_prompt: str = None):
def prepare_context(self, context: list, system_prompt: str = None, injected_system_prompt: str = None):
assert not len(self._context)
self._context = context
if isinstance(system_prompt, str) and len(system_prompt):

View File

@ -25,9 +25,9 @@ async def do_reply_msg(client_helper: MatrixClientHelper, room: MatrixRoom, requ
context=msg,
command_info=command_info,
)
except Exception:
except Exception as e:
logger.critical(traceback.format_exc())
await client_helper.react_to_event(room.room_id, requestor_event.event_id, '')
await client_helper.react_to_event(room.room_id, requestor_event.event_id, '', extra_error=f'Exception during response process: {e}' if global_config['send_extra_messages'] else None)
raise
@ -86,9 +86,9 @@ async def do_reply_threaded_msg(client_helper: MatrixClientHelper, room: MatrixR
thread_root_id=thread_content[0].event_id,
matrix_gpt_data=matrix_gpt_data
)
except:
except Exception as e:
logger.error(traceback.format_exc())
await client_helper.react_to_event(room.room_id, event.event_id, '')
await client_helper.react_to_event(room.room_id, requestor_event.event_id, '', extra_error=f'Exception during response process: {e}' if global_config['send_extra_messages'] else None)
raise