fix async issues
This commit is contained in:
parent
d68262bfee
commit
c4ffd82698
2
main.py
2
main.py
|
@ -63,6 +63,8 @@ check_config_value_exists(config_data['openai'], 'api_key')
|
||||||
|
|
||||||
command_prefixes = {}
|
command_prefixes = {}
|
||||||
for k, v in config_data['command'].items():
|
for k, v in config_data['command'].items():
|
||||||
|
check_config_value_exists(v, 'model')
|
||||||
|
check_config_value_exists(v, 'mode', default='default')
|
||||||
if 'allowed_to_chat' not in v.keys():
|
if 'allowed_to_chat' not in v.keys():
|
||||||
# Set default value
|
# Set default value
|
||||||
v['allowed_to_chat'] = 'all'
|
v['allowed_to_chat'] = 'all'
|
||||||
|
|
|
@ -161,87 +161,94 @@ async def process_chat(
|
||||||
log_full_response: bool = False,
|
log_full_response: bool = False,
|
||||||
injected_system_prompt: str = False
|
injected_system_prompt: str = False
|
||||||
):
|
):
|
||||||
if not store.check_seen_event(event.event_id):
|
try:
|
||||||
await client.room_typing(room.room_id, typing_state=True, timeout=90000)
|
if not store.check_seen_event(event.event_id):
|
||||||
# if self.reply_in_thread:
|
await client.room_typing(room.room_id, typing_state=True, timeout=90000)
|
||||||
# thread_content = await get_thread_content(self.client, self.room, self.event)
|
# if self.reply_in_thread:
|
||||||
|
# thread_content = await get_thread_content(self.client, self.room, self.event)
|
||||||
|
|
||||||
if isinstance(command, list):
|
if isinstance(command, list):
|
||||||
messages = command
|
messages = command
|
||||||
else:
|
else:
|
||||||
messages = [{'role': 'user', 'content': command}]
|
messages = [{'role': 'user', 'content': command}]
|
||||||
|
|
||||||
if system_prompt:
|
if system_prompt:
|
||||||
messages.insert(0, {"role": "system", "content": system_prompt})
|
messages.insert(0, {"role": "system", "content": system_prompt})
|
||||||
if injected_system_prompt:
|
if injected_system_prompt:
|
||||||
if messages[-1]['role'] == 'system':
|
if messages[-1]['role'] == 'system':
|
||||||
del messages[-1]
|
del messages[-1]
|
||||||
index = -9999
|
index = -9999
|
||||||
if len(messages) >= 3: # only inject the system prompt if this isn't the first reply
|
if len(messages) >= 3: # only inject the system prompt if this isn't the first reply
|
||||||
index = -1
|
index = -1
|
||||||
elif not system_prompt:
|
elif not system_prompt:
|
||||||
index = 0
|
index = 0
|
||||||
if index != -9999:
|
if index != -9999:
|
||||||
messages.insert(index, {"role": "system", "content": injected_system_prompt})
|
messages.insert(index, {"role": "system", "content": injected_system_prompt})
|
||||||
|
|
||||||
logger.debug(f'Generating reply to event {event.event_id}')
|
logger.debug(f'Generating reply to event {event.event_id}')
|
||||||
|
|
||||||
loop = asyncio.get_running_loop()
|
loop = asyncio.get_running_loop()
|
||||||
|
|
||||||
# I don't think the OpenAI py api has a built-in timeout
|
# I don't think the OpenAI py api has a built-in timeout
|
||||||
@stopit.threading_timeoutable(default=(None, None))
|
@stopit.threading_timeoutable(default=(None, None))
|
||||||
async def generate():
|
async def generate():
|
||||||
if openai_model in ['gpt-3', 'gpt-4']:
|
if openai_model.startswith('gpt-3') or openai_model.startswith('gpt-4'):
|
||||||
return 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))
|
||||||
elif openai_model in ['text-davinci-003', 'davinci-instruct-beta', 'text-davinci-001', 'text-davinci-002', 'text-curie-001', 'text-babbage-001']:
|
return r.choices[0].message.content
|
||||||
return await loop.run_in_executor(None, functools.partial(openai_obj.Completion.create, model=openai_model, temperature=openai_temperature, timeout=20))
|
elif openai_model in ['text-davinci-003', 'davinci-instruct-beta', 'text-davinci-001', '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
|
||||||
|
else:
|
||||||
|
raise Exception(f'Model {openai_model} not found!')
|
||||||
|
|
||||||
response = None
|
response = None
|
||||||
for i in range(openai_retries):
|
for i in range(openai_retries):
|
||||||
try:
|
try:
|
||||||
task = asyncio.create_task(generate(timeout=20))
|
task = asyncio.create_task(generate(timeout=20))
|
||||||
asyncio.as_completed(task)
|
asyncio.as_completed(task)
|
||||||
response = await task
|
response = await task
|
||||||
if response is not None:
|
if response is not None:
|
||||||
break
|
break
|
||||||
except Exception as e: # (stopit.utils.TimeoutException, openai.error.APIConnectionError)
|
else:
|
||||||
logger.warning(f'Got exception when generating response to event {event.event_id}, retrying: {e}')
|
logger.warning(f'Response to event {event.event_id} was null, retrying.')
|
||||||
await client.room_typing(room.room_id, typing_state=True, timeout=15000)
|
time.sleep(2)
|
||||||
time.sleep(2)
|
except Exception as e: # (stopit.utils.TimeoutException, openai.error.APIConnectionError)
|
||||||
continue
|
logger.warning(f'Got exception when generating response to event {event.event_id}, retrying: {e}')
|
||||||
|
await client.room_typing(room.room_id, typing_state=True, timeout=15000)
|
||||||
|
time.sleep(2)
|
||||||
|
continue
|
||||||
|
|
||||||
if response is None:
|
if response is None:
|
||||||
logger.critical(f'Could not generate response to event {event.event_id} in room {room.room_id}.')
|
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, '❌')
|
||||||
return
|
return
|
||||||
if openai_model in ['gpt-3', 'gpt-4']:
|
text_response = response.strip().strip('\n')
|
||||||
text_response = response["choices"][0]["message"]["content"].strip().strip('\n')
|
|
||||||
elif openai_model in ['text-davinci-003', 'davinci-instruct-beta', 'text-davinci-001', 'text-davinci-002', 'text-curie-001', 'text-babbage-001']:
|
|
||||||
text_response = response["choices"][0]["text"].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")
|
||||||
elif isinstance(command, list):
|
elif isinstance(command, list):
|
||||||
x = command[-1]['content'].replace("\n", "\\n")
|
x = command[-1]['content'].replace("\n", "\\n")
|
||||||
else:
|
else:
|
||||||
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)
|
|
||||||
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 react_to_event(client, room.room_id, event.event_id, '❌')
|
|
||||||
else:
|
|
||||||
store.add_event_id(resp.event_id)
|
|
||||||
|
|
||||||
|
store.add_event_id(event.event_id)
|
||||||
|
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 react_to_event(client, room.room_id, event.event_id, '❌')
|
||||||
|
else:
|
||||||
|
store.add_event_id(resp.event_id)
|
||||||
|
except Exception:
|
||||||
|
await react_to_event(client, room.room_id, event.event_id, '❌')
|
||||||
|
raise
|
||||||
|
|
||||||
def check_authorized(string, to_check):
|
def check_authorized(string, to_check):
|
||||||
def check_str(s, c):
|
def check_str(s, c):
|
||||||
|
|
|
@ -1,14 +1,20 @@
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
|
||||||
def check_config_value_exists(config_part, key, check_type=None, allow_empty=False) -> bool:
|
def check_config_value_exists(config_part, key, check_type=None, allow_empty=False, choices: list = None, default=None) -> bool:
|
||||||
if key not in config_part.keys():
|
if default and key not in config_part.keys():
|
||||||
print(f'Config key not found: "{key}"')
|
return default
|
||||||
sys.exit(1)
|
else:
|
||||||
if not allow_empty and config_part[key] is None or config_part[key] == '':
|
if key not in config_part.keys():
|
||||||
print(f'Config key "{key}" must not be empty.')
|
print(f'Config key not found: "{key}"')
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
if check_type and not isinstance(config_part[key], check_type):
|
if not allow_empty and config_part[key] is None or config_part[key] == '':
|
||||||
print(f'Config key "{key}" must be type "{check_type}", not "{type(config_part[key])}".')
|
print(f'Config key "{key}" must not be empty.')
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
return True
|
if check_type and not isinstance(config_part[key], check_type):
|
||||||
|
print(f'Config key "{key}" must be type "{check_type}", not "{type(config_part[key])}".')
|
||||||
|
sys.exit(1)
|
||||||
|
if choices and config_part[key] not in choices:
|
||||||
|
print(f'Invalid choice for config key "{key}". Choices: {choices}')
|
||||||
|
sys.exit(1)
|
||||||
|
return True
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import json
|
import json
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Union
|
from typing import Union
|
||||||
|
@ -6,6 +7,7 @@ from typing import Union
|
||||||
from nio import AsyncClient, AsyncClientConfig, LoginError
|
from nio import AsyncClient, AsyncClientConfig, LoginError
|
||||||
from nio import LoginResponse
|
from nio import LoginResponse
|
||||||
|
|
||||||
|
logger = logging.getLogger('MatrixGPT')
|
||||||
|
|
||||||
class MatrixNioGPTHelper:
|
class MatrixNioGPTHelper:
|
||||||
"""
|
"""
|
||||||
|
@ -34,6 +36,7 @@ class MatrixNioGPTHelper:
|
||||||
try:
|
try:
|
||||||
# If there are no previously-saved credentials, we'll use the password
|
# If there are no previously-saved credentials, we'll use the password
|
||||||
if not os.path.exists(self.auth_file):
|
if not os.path.exists(self.auth_file):
|
||||||
|
logger.info('Using username/password.')
|
||||||
resp = await self.client.login(self.passwd, device_name=self.device_name)
|
resp = await self.client.login(self.passwd, device_name=self.device_name)
|
||||||
|
|
||||||
# check that we logged in succesfully
|
# check that we logged in succesfully
|
||||||
|
@ -44,6 +47,7 @@ class MatrixNioGPTHelper:
|
||||||
return False, resp
|
return False, resp
|
||||||
else:
|
else:
|
||||||
# Otherwise the config file exists, so we'll use the stored credentials
|
# Otherwise the config file exists, so we'll use the stored credentials
|
||||||
|
logger.info('Using cached credentials.')
|
||||||
with open(self.auth_file, "r") as f:
|
with open(self.auth_file, "r") as f:
|
||||||
config = json.load(f)
|
config = json.load(f)
|
||||||
client = AsyncClient(config["homeserver"])
|
client = AsyncClient(config["homeserver"])
|
||||||
|
|
Loading…
Reference in New Issue