Improve JSON parsing.

This commit is contained in:
OK 2023-03-29 19:22:54 +02:00
parent 7f3cb66043
commit ddc44bb9da
3 changed files with 20 additions and 6 deletions

View File

@ -1,4 +1,5 @@
import json import json
import multiline
import openai import openai
import aiohttp import aiohttp
import logging import logging
@ -17,6 +18,17 @@ def pp(*args, **kw):
return pformat(*args, **kw) return pformat(*args, **kw)
def parse_response(content: str) -> Dict:
content = content.strip()
try:
return json.loads(content)
except Exception:
try:
return multiline.loads(content, multiline=True)
except Exception as err:
raise err
class AIMessageBase(object): class AIMessageBase(object):
def __init__(self) -> None: def __init__(self) -> None:
pass pass
@ -180,15 +192,15 @@ class AIResponder(object):
if answer is None: if answer is None:
continue continue
try: try:
response = json.loads(answer['content']) response = parse_response(answer['content'])
except Exception as err: except Exception as err:
logging.warning(f"failed to parse the answer: {pp(err)}\n{repr(answer['content'])}") logging.warning(f"failed to parse the answer: {pp(err)}\n{repr(answer['content'])}")
answer['content'] = await self.fix(answer['content']) answer['content'] = await self.fix(answer['content'])
try: try:
response = json.loads(answer['content']) response = parse_response(answer['content'])
except Exception as err: except Exception as err:
logging.error(f"failed to parse the answer: {pp(err)}\n{repr(answer['content'])}") logging.error(f"failed to parse the fixed answer: {pp(err)}\n{repr(answer['content'])}")
return AIResponse(None, False, f"ERROR: I could not parse this answer: {repr(answer['content'])}", None, False) continue
if 'hack' not in response or type(response.get('picture', None)) not in (type(None), str): if 'hack' not in response or type(response.get('picture', None)) not in (type(None), str):
continue continue
logging.info(f"got this answer:\n{pp(response)}") logging.info(f"got this answer:\n{pp(response)}")

View File

@ -10,3 +10,4 @@ wheel
watchdog watchdog
toml toml
types-toml types-toml
multiline

View File

@ -5,6 +5,7 @@ import json
import toml import toml
import openai import openai
import logging import logging
import pytest
from unittest.mock import Mock, PropertyMock, MagicMock, AsyncMock, patch, mock_open, ANY from unittest.mock import Mock, PropertyMock, MagicMock, AsyncMock, patch, mock_open, ANY
from fjerkroa_bot import FjerkroaBot from fjerkroa_bot import FjerkroaBot
from discord import User, Message, TextChannel from discord import User, Message, TextChannel
@ -113,8 +114,8 @@ class TestFunctionality(TestBotBase):
return {'choices': [{'message': {'content': '{ "test": 3 ]'}}]} return {'choices': [{'message': {'content': '{ "test": 3 ]'}}]}
message = self.create_message("Hello there! How are you?") message = self.create_message("Hello there! How are you?")
with patch.object(openai.ChatCompletion, 'acreate', new=acreate): with patch.object(openai.ChatCompletion, 'acreate', new=acreate):
await self.bot.on_message(message) with pytest.raises(RuntimeError, match="Failed to generate answer after multiple retries"):
self.bot.staff_channel.send.assert_called_once_with("ERROR: I could not parse this answer: '{ \"test\": 3 ]'", suppress_embeds=True) await self.bot.on_message(message)
async def test_on_message_event4(self) -> None: async def test_on_message_event4(self) -> None:
async def acreate(*a, **kw): async def acreate(*a, **kw):