diff --git a/README.md b/README.md index 6def9ad..686aa50 100644 --- a/README.md +++ b/README.md @@ -3,8 +3,7 @@ _Chatbots for Matrix._ This bot supports OpenAI, Anthropic, and locally hosted models that use an OpenAI-compatible endpoint. It can run -multiple different models using -different triggers, such as `!c4` for GPT4 and `!ca` for Anthropic, all through the same bot. +multiple different models using different triggers, such as `!c4` for GPT4 and `!ca` for Anthropic, all through the same bot. **Supported Services** @@ -42,7 +41,7 @@ Use `!matrixgpt` to view the bot's help. The bot also responds to `!bots`.
- Don't try to use two bots in the same thread. -- You can DM a bot for a private chat. +- You can DM the bot for a private chat. - The bot will move its read marker whenever a message is sent in the room.
@@ -57,4 +56,8 @@ The bot can give helpful reactions: ## TODO - [ ] Dalle bot -- [ ] Fix the typing indicator being removed when two responses are generating \ No newline at end of file +- [ ] Add our own context mechanism to Copilot? +- [ ] Improve error messages sent with reactions to narrow down where the issue occurred. +- [ ] Allow replying to an image post which will give a vision model an image + text on the first message. +- [ ] Fix the typing indicator being removed when two responses are generating. +- [ ] ~~Add vision to Copilot~~ (not doing, API to unstable). diff --git a/main.py b/main.py index adaee07..9af45ba 100644 --- a/main.py +++ b/main.py @@ -65,6 +65,10 @@ async def main(args): logger.debug(f'Command Prefixes: {[k for k, v in global_config.command_prefixes.items()]}') + logger.info(f"OpenAI API key: {'yes' if global_config['openai'].get('api_key') else 'no'}") + logger.info(f"Anthropic API key: {'yes' if global_config['anthropic'].get('api_key') else 'no'}") + logger.info(f"Copilot API key: {'yes' if global_config['copilot'].get('api_key') else 'no'}") + client_helper = MatrixClientHelper( user_id=global_config['auth']['username'], passwd=global_config['auth']['password'], diff --git a/matrix_gpt/generate.py b/matrix_gpt/generate.py index 2f304e6..2eb8591 100644 --- a/matrix_gpt/generate.py +++ b/matrix_gpt/generate.py @@ -31,6 +31,17 @@ async def generate_ai_response( await client.room_typing(room.room_id, typing_state=True, timeout=global_config['response_timeout'] * 1000) api_client = api_client_helper.get_client(command_info.api_type, client_helper) + if not api_client: + # If this was None then we were missing an API key for this client type. Error has already been logged. + await client_helper.react_to_event( + room.room_id, + event.event_id, + '❌', + extra_error=f'No API key for model {command_info.model}' if global_config['send_extra_messages'] else None + ) + await client.room_typing(room.room_id, typing_state=False, timeout=1000) + return + messages = api_client.assemble_context(msg, system_prompt=command_info.system_prompt, injected_system_prompt=command_info.injected_system_prompt) if api_client.check_ignore_request(): diff --git a/matrix_gpt/generate_clients/copilot.py b/matrix_gpt/generate_clients/copilot.py index a8da0f9..e166756 100644 --- a/matrix_gpt/generate_clients/copilot.py +++ b/matrix_gpt/generate_clients/copilot.py @@ -9,7 +9,7 @@ from matrix_gpt.generate_clients.api_client import ApiClient from matrix_gpt.generate_clients.command_info import CommandInfo """ -This was written with sydney.py==0.20.4 but requirements.txt has not locked in a version because Bing's API may change. +This was written with sydney.py==0.20.4 but requirements.txt has not locked in a version because Bing's API may change. """ _REGEX_ATTR_RE_STR = r'^\[(\d*)]:\s(https?://(?:www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b[-a-zA-Z0-9()@:%_+.~#?&/=]*)\s*(\"\")*' @@ -18,6 +18,13 @@ _REGEX_ATTR_LINK_RE_STR = [r'\[\^\d*\^]\[', r']'] _REGEX_ATTR_LINK_RE = re.compile(r'\d*'.join(_REGEX_ATTR_LINK_RE_STR)) +""" +To implement context, could we maybe pickle the `sydney` object and track state via requester event ID? +Probably best not to store it in memory, but maybe a sqlite database in /tmp? +But might have to store it in memory because of async issues and not being able to restore the state of an old async loop. +""" + + class CopilotClient(ApiClient): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs)