mirror of
https://github.com/home-assistant/core.git
synced 2026-08-24 10:13:52 -05:00
Fix llm tool results mutation (#167485)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
co-authored by
Copilot
parent
a061e47bec
commit
81f8319af4
@@ -1434,16 +1434,16 @@ class IntentResponse:
|
||||
def as_dict(self) -> dict[str, Any]:
|
||||
"""Return a dictionary representation of an intent response."""
|
||||
response_dict: dict[str, Any] = {
|
||||
"speech": self.speech,
|
||||
"card": self.card,
|
||||
"speech": {k: dict(v) for k, v in self.speech.items()},
|
||||
"card": {k: dict(v) for k, v in self.card.items()},
|
||||
"language": self.language,
|
||||
"response_type": self.response_type.value,
|
||||
}
|
||||
|
||||
if self.reprompt:
|
||||
response_dict["reprompt"] = self.reprompt
|
||||
response_dict["reprompt"] = {k: dict(v) for k, v in self.reprompt.items()}
|
||||
if self.speech_slots:
|
||||
response_dict["speech_slots"] = self.speech_slots
|
||||
response_dict["speech_slots"] = self.speech_slots.copy()
|
||||
|
||||
response_data: dict[str, Any] = {}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ async def test_async_get_result_from_chat_log(
|
||||
) -> None:
|
||||
"""Test getting result from chat log."""
|
||||
intent_response = intent.IntentResponse(language="en")
|
||||
tool_result = llm.IntentResponseDict(intent_response)
|
||||
with (
|
||||
chat_session.async_get_chat_session(hass) as session,
|
||||
conversation.async_get_chat_log(
|
||||
@@ -23,7 +24,7 @@ async def test_async_get_result_from_chat_log(
|
||||
agent_id="mock-agent-id",
|
||||
tool_call_id="mock-tool-call-id",
|
||||
tool_name="mock-tool-name",
|
||||
tool_result=llm.IntentResponseDict(intent_response),
|
||||
tool_result=tool_result,
|
||||
),
|
||||
conversation.AssistantContent(
|
||||
agent_id="mock-agent-id",
|
||||
@@ -37,3 +38,4 @@ async def test_async_get_result_from_chat_log(
|
||||
# Original intent response is returned with speech set
|
||||
assert result.response is intent_response
|
||||
assert result.response.speech["plain"]["speech"] == "This is a response."
|
||||
assert tool_result["speech"] != result.response.speech
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"""Tests for the intent helpers."""
|
||||
|
||||
import asyncio
|
||||
from copy import deepcopy
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
@@ -983,3 +984,76 @@ async def test_get_all_entity_aliases(
|
||||
|
||||
state = State("light.test", "on", {"friendly_name": friendly_name})
|
||||
assert intent.async_get_entity_aliases(hass, entry, state=state) == expected
|
||||
|
||||
|
||||
async def test_intent_response_dict() -> None:
|
||||
"""Test that IntentResponse.as_dict() copies mutable objects."""
|
||||
response = intent.IntentResponse(
|
||||
language="en",
|
||||
intent=None,
|
||||
)
|
||||
# Prepare the intent response initial state
|
||||
response.async_set_speech(
|
||||
speech="Hello", speech_type="plain", extra_data={"key": "value"}
|
||||
)
|
||||
response.async_set_reprompt(
|
||||
speech="Hi", speech_type="plain", extra_data={"key2": "value2"}
|
||||
)
|
||||
response.async_set_card(title="Title", content="Content", card_type="simple")
|
||||
response.async_set_results(
|
||||
success_results=[
|
||||
intent.IntentResponseTarget(
|
||||
type=intent.IntentResponseTargetType.FLOOR,
|
||||
name="first floor",
|
||||
id="floor-1",
|
||||
)
|
||||
],
|
||||
failed_results=[
|
||||
intent.IntentResponseTarget(
|
||||
type=intent.IntentResponseTargetType.ENTITY,
|
||||
name="kitchen light",
|
||||
id="light.kitchen",
|
||||
)
|
||||
],
|
||||
)
|
||||
response.async_set_states(
|
||||
matched_states=[State("light.kitchen", "on")],
|
||||
unmatched_states=[State("light.bedroom", "off")],
|
||||
)
|
||||
response.async_set_speech_slots({"name": {"value": "kitchen"}})
|
||||
|
||||
response_dict1 = response.as_dict()
|
||||
response_dict2 = deepcopy(response_dict1)
|
||||
|
||||
# Mutate the original object
|
||||
response.async_set_speech(
|
||||
speech="Changed", speech_type="plain", extra_data={"key": "changed"}
|
||||
)
|
||||
response.async_set_reprompt(
|
||||
speech="Changed", speech_type="plain", extra_data={"key2": "changed2"}
|
||||
)
|
||||
response.async_set_card(title="Changed", content="Changed", card_type="simple")
|
||||
response.async_set_results(
|
||||
success_results=[
|
||||
intent.IntentResponseTarget(
|
||||
type=intent.IntentResponseTargetType.FLOOR,
|
||||
name="changed floor",
|
||||
id="floor-changed",
|
||||
)
|
||||
],
|
||||
failed_results=[
|
||||
intent.IntentResponseTarget(
|
||||
type=intent.IntentResponseTargetType.ENTITY,
|
||||
name="changed light",
|
||||
id="light.changed",
|
||||
)
|
||||
],
|
||||
)
|
||||
response.async_set_states(
|
||||
matched_states=[State("light.changed", "on")],
|
||||
unmatched_states=[State("light.changed_bedroom", "off")],
|
||||
)
|
||||
response.async_set_speech_slots({"name": {"value": "changed"}})
|
||||
|
||||
# The original dict should not be affected by the mutations
|
||||
assert response_dict1 == response_dict2
|
||||
|
||||
Reference in New Issue
Block a user