diff --git a/adapter.py b/adapter.py
--- a/adapter.py
+++ b/adapter.py
@@ -1,30 +1,49 @@
-import json
-
-def normalize_event(event):
-    if event.get("type") == "chat.completion.chunk":
-        return event
-    if event.get("type") == "response.output_text.delta":
-        return {"choices": [{"delta": {"content": event.get("delta", "")}}]}
-    if event.get("type") == "response.output_item.added":
-        call = normalize_tool_call(event.get("item", {}))
-        return {"choices": [{"delta": {"tool_calls": [call]}}]} if call else {"choices": []}
-    if event.get("type") == "response.completed":
-        usage = event.get("response", {}).get("usage", {})
-        return {"choices": [], "usage": {"prompt_tokens": usage.get("input_tokens", 0), "completion_tokens": usage.get("output_tokens", 0), "total_tokens": usage.get("total_tokens", 0)}}
-    return {"choices": []}
-
-def parse_structured(text):
-    return json.loads(text)
-
-def normalize_tool_call(item):
-    if item.get("type") != "function_call":
-        return None
-    arguments = item.get("arguments")
-    if not isinstance(arguments, str) or not isinstance(json.loads(arguments), dict):
-        raise ValueError("function arguments must be a JSON object string")
-    return {"id": item.get("id"), "type": "function", "function": {"name": item.get("name"), "arguments": arguments}}
-
-def retry_delay(status, attempt):
-    if status != 429 and not 500 <= status <= 599:
-        return None
-    return min(250 * (2 ** attempt), 2000)
+import json
+
+def normalize_event(event):
+    if event.get("type") == "chat.completion.chunk":
+        return event
+    if event.get("type") == "response.output_text.delta":
+        return {"choices": [{"delta": {"content": event.get("delta", "")}}]}
+    if event.get("type") == "response.output_item.added":
+        call = normalize_tool_call(event.get("item", {}))
+        return {"choices": [{"delta": {"tool_calls": [call]}}]} if call else {"choices": []}
+    if event.get("type") == "response.completed":
+        usage = event.get("response", {}).get("usage", {})
+        return {"choices": [], "usage": {"prompt_tokens": usage.get("input_tokens", 0), "completion_tokens": usage.get("output_tokens", 0), "total_tokens": usage.get("total_tokens", 0)}}
+    return {"choices": []}
+
+def parse_structured(text):
+    import json
+    import re
+    match text:
+        case _ if not isinstance(text, str):
+            raise ValueError("Input must be a string")
+        case _ if not text.strip():
+            raise ValueError("Input string is empty")
+        case _ if re.fullmatch(r'.*```(?:json)?\s*([\s\S]+?)\s*```.*', text, re.DOTALL):
+            json_str = re.search(r'```(?:json)?\s*([\s\S]+?)\s*```', text, re.DOTALL).group(1).strip()
+        case _:
+            json_str = text.strip()
+    try:
+        data = json.loads(json_str)
+    except json.JSONDecodeError as e:
+        raise ValueError(f"Invalid JSON: {e}")
+    match data:
+        case {"answer": answer} if isinstance(answer, str) and answer.strip() and len(data) == 1:
+            return {"answer": answer}
+        case _:
+            raise ValueError("Must be a dict with exactly one non-empty string 'answer' key")
+
+def normalize_tool_call(item):
+    if item.get("type") != "function_call":
+        return None
+    arguments = item.get("arguments")
+    if not isinstance(arguments, str) or not isinstance(json.loads(arguments), dict):
+        raise ValueError("function arguments must be a JSON object string")
+    return {"id": item.get("id"), "type": "function", "function": {"name": item.get("name"), "arguments": arguments}}
+
+def retry_delay(status, attempt):
+    if status != 429 and not 500 <= status <= 599:
+        return None
+    return min(250 * (2 ** attempt), 2000)
