Hash-verified recorded artifacts. Rejected patches remain visible for inspection, not approval to ship.
Inspect candidate diff
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)