|
import json
|
|
from datetime import datetime
|
|
|
|
from json_repair import repair_json
|
|
from litellm.types.utils import ModelResponse
|
|
|
|
from openhands.core.exceptions import LLMResponseError
|
|
from openhands.events.event import Event
|
|
from openhands.events.observation import CmdOutputMetadata
|
|
from openhands.events.serialization import event_to_dict
|
|
from openhands.llm.metrics import Metrics
|
|
|
|
|
|
def my_default_encoder(obj):
|
|
"""Custom JSON encoder that handles datetime and event objects"""
|
|
if isinstance(obj, datetime):
|
|
return obj.isoformat()
|
|
if isinstance(obj, Event):
|
|
return event_to_dict(obj)
|
|
if isinstance(obj, Metrics):
|
|
return obj.get()
|
|
if isinstance(obj, ModelResponse):
|
|
return obj.model_dump()
|
|
if isinstance(obj, CmdOutputMetadata):
|
|
return obj.model_dump()
|
|
return json.JSONEncoder().default(obj)
|
|
|
|
|
|
def dumps(obj, **kwargs):
|
|
"""Serialize an object to str format"""
|
|
return json.dumps(obj, default=my_default_encoder, **kwargs)
|
|
|
|
|
|
def loads(json_str, **kwargs):
|
|
"""Create a JSON object from str"""
|
|
try:
|
|
return json.loads(json_str, **kwargs)
|
|
except json.JSONDecodeError:
|
|
pass
|
|
depth = 0
|
|
start = -1
|
|
for i, char in enumerate(json_str):
|
|
if char == '{':
|
|
if depth == 0:
|
|
start = i
|
|
depth += 1
|
|
elif char == '}':
|
|
depth -= 1
|
|
if depth == 0 and start != -1:
|
|
response = json_str[start : i + 1]
|
|
try:
|
|
json_str = repair_json(response)
|
|
return json.loads(json_str, **kwargs)
|
|
except (json.JSONDecodeError, ValueError, TypeError) as e:
|
|
raise LLMResponseError(
|
|
'Invalid JSON in response. Please make sure the response is a valid JSON object.'
|
|
) from e
|
|
raise LLMResponseError('No valid JSON object found in response.')
|
|
|