Update changelog for version 0.1.12, improve response parsing for nested OpenAI-compatible payloads, and enhance assistant message extraction in conversation agent.
This commit is contained in:
@@ -2,6 +2,12 @@
|
||||
|
||||
All notable changes to the OpenClaw Home Assistant Integration will be documented in this file.
|
||||
|
||||
## [0.1.12] - 2026-02-20
|
||||
|
||||
### Fixed
|
||||
- Improved response parsing for nested/modern OpenAI-compatible payloads (including `output` / nested `content` shapes), which could previously result in missing UI replies.
|
||||
- Applied the same recursive extraction strategy to both service-based chat responses and Assist conversation fallback parsing.
|
||||
|
||||
## [0.1.11] - 2026-02-20
|
||||
|
||||
### Fixed
|
||||
|
||||
@@ -380,8 +380,11 @@ def _get_first_entry_data(hass: HomeAssistant) -> dict[str, Any] | None:
|
||||
return None
|
||||
|
||||
|
||||
def _coerce_text(value: Any) -> str | None:
|
||||
"""Convert known message content shapes to plain text."""
|
||||
def _extract_text_recursive(value: Any, depth: int = 0) -> str | None:
|
||||
"""Recursively extract assistant text from nested response payloads."""
|
||||
if depth > 8:
|
||||
return None
|
||||
|
||||
if isinstance(value, str):
|
||||
text = value.strip()
|
||||
return text or None
|
||||
@@ -389,38 +392,41 @@ def _coerce_text(value: Any) -> str | None:
|
||||
if isinstance(value, list):
|
||||
parts: list[str] = []
|
||||
for item in value:
|
||||
if isinstance(item, str):
|
||||
if item.strip():
|
||||
parts.append(item.strip())
|
||||
continue
|
||||
if isinstance(item, dict):
|
||||
text_part = item.get("text") or item.get("content")
|
||||
if isinstance(text_part, str) and text_part.strip():
|
||||
parts.append(text_part.strip())
|
||||
extracted = _extract_text_recursive(item, depth + 1)
|
||||
if extracted:
|
||||
parts.append(extracted)
|
||||
if parts:
|
||||
return "\n".join(parts)
|
||||
return None
|
||||
|
||||
if isinstance(value, dict):
|
||||
priority_keys = (
|
||||
"output_text",
|
||||
"text",
|
||||
"content",
|
||||
"message",
|
||||
"response",
|
||||
"answer",
|
||||
"choices",
|
||||
"output",
|
||||
"delta",
|
||||
)
|
||||
|
||||
for key in priority_keys:
|
||||
if key not in value:
|
||||
continue
|
||||
extracted = _extract_text_recursive(value.get(key), depth + 1)
|
||||
if extracted:
|
||||
return extracted
|
||||
|
||||
for nested_value in value.values():
|
||||
extracted = _extract_text_recursive(nested_value, depth + 1)
|
||||
if extracted:
|
||||
return extracted
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def _extract_assistant_message(response: dict[str, Any]) -> str | None:
|
||||
"""Extract assistant text from multiple OpenAI-compatible response shapes."""
|
||||
choices = response.get("choices")
|
||||
if isinstance(choices, list) and choices:
|
||||
first = choices[0]
|
||||
if isinstance(first, dict):
|
||||
message = first.get("message")
|
||||
if isinstance(message, dict):
|
||||
extracted = _coerce_text(message.get("content"))
|
||||
if extracted:
|
||||
return extracted
|
||||
extracted = _coerce_text(first.get("text"))
|
||||
if extracted:
|
||||
return extracted
|
||||
|
||||
for key in ("output_text", "response", "message", "content", "answer"):
|
||||
extracted = _coerce_text(response.get(key))
|
||||
if extracted:
|
||||
return extracted
|
||||
|
||||
return None
|
||||
"""Extract assistant text from modern/legacy OpenAI-compatible responses."""
|
||||
return _extract_text_recursive(response)
|
||||
|
||||
@@ -174,10 +174,54 @@ class OpenClawConversationAgent(conversation.AbstractConversationAgent):
|
||||
message=message,
|
||||
session_id=conversation_id,
|
||||
)
|
||||
choices = response.get("choices", [])
|
||||
if choices:
|
||||
return choices[0].get("message", {}).get("content", "")
|
||||
return ""
|
||||
extracted = self._extract_text_recursive(response)
|
||||
return extracted or ""
|
||||
|
||||
def _extract_text_recursive(self, value: Any, depth: int = 0) -> str | None:
|
||||
"""Recursively extract assistant text from nested response payloads."""
|
||||
if depth > 8:
|
||||
return None
|
||||
|
||||
if isinstance(value, str):
|
||||
text = value.strip()
|
||||
return text or None
|
||||
|
||||
if isinstance(value, list):
|
||||
parts: list[str] = []
|
||||
for item in value:
|
||||
extracted = self._extract_text_recursive(item, depth + 1)
|
||||
if extracted:
|
||||
parts.append(extracted)
|
||||
if parts:
|
||||
return "\n".join(parts)
|
||||
return None
|
||||
|
||||
if isinstance(value, dict):
|
||||
priority_keys = (
|
||||
"output_text",
|
||||
"text",
|
||||
"content",
|
||||
"message",
|
||||
"response",
|
||||
"answer",
|
||||
"choices",
|
||||
"output",
|
||||
"delta",
|
||||
)
|
||||
|
||||
for key in priority_keys:
|
||||
if key not in value:
|
||||
continue
|
||||
extracted = self._extract_text_recursive(value.get(key), depth + 1)
|
||||
if extracted:
|
||||
return extracted
|
||||
|
||||
for nested_value in value.values():
|
||||
extracted = self._extract_text_recursive(nested_value, depth + 1)
|
||||
if extracted:
|
||||
return extracted
|
||||
|
||||
return None
|
||||
|
||||
def _error_result(
|
||||
self,
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
"iot_class": "local_polling",
|
||||
"issue_tracker": "https://github.com/techartdev/OpenClawHomeAssistant/issues",
|
||||
"requirements": [],
|
||||
"version": "0.1.11",
|
||||
"version": "0.1.12",
|
||||
"dependencies": ["conversation"],
|
||||
"after_dependencies": ["hassio", "lovelace"]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user