mirascope
mirascope copied to clipboard
`ValidationError` when using chat history that contains tool message
Description
Run the following code:
from typing import Literal
from openai.types.chat import ChatCompletionMessageParam
from mirascope.openai import OpenAICall, OpenAICallParams
def get_current_weather(
location: str, unit: Literal["celsius", "fahrenheit"] = "fahrenheit"
):
"""Get the current weather in a given location."""
if "tokyo" in location.lower():
return f"It is 10 degrees {unit} in Tokyo, Japan"
elif "san francisco" in location.lower():
return f"It is 72 degrees {unit} in San Francisco, CA"
elif "paris" in location.lower():
return f"It is 22 degress {unit} in Paris, France"
else:
return f"I'm not sure what the weather is like in {location}"
class Forecast(OpenAICall):
prompt_template = """
MESSAGES: {history}
USER: {question}
"""
question: str
history: list[ChatCompletionMessageParam] = []
call_params = OpenAICallParams(model="gpt-4-turbo", tools=[get_current_weather])
# Make the first call to the LLM
forecast = Forecast(question="What's the weather in Tokyo Japan?")
response = forecast.call()
history = []
history += [
{"role": "user", "content": forecast.question},
response.message.model_dump(),
]
tool = response.tool
if tool:
print("Tool arguments:", tool.args)
# > {'location': 'Tokyo, Japan', 'unit': 'fahrenheit'}
output = tool.fn(**tool.args)
print("Tool output:", output)
# > It is 10 degrees fahrenheit in Tokyo, Japan
# reinsert the tool call into the chat messages through history
# NOTE: this should be more convenient, e.g. `tool.message_param`
history += [
{
"role": "tool",
"content": output,
"tool_call_id": tool.tool_call.id,
"name": tool.__class__.__name__,
},
]
else:
print(response.content) # if no tool, print the content of the response
# Call the LLM again with the history including the tool call
response = Forecast(question="Is that cold or hot?", history=history).call()
print("After Tools Response:", response.content)
And you will get the following error:
---------------------------------------------------------------------------
ValidationError Traceback (most recent call last)
Cell In[27], [line 64](vscode-notebook-cell:?execution_count=27&line=64)
[61](vscode-notebook-cell:?execution_count=27&line=61) print(response.content) # if no tool, print the content of the response
[63](vscode-notebook-cell:?execution_count=27&line=63) # Call the LLM again with the history including the tool call
---> [64](vscode-notebook-cell:?execution_count=27&line=64) response = Forecast(question="Is that cold or hot?", history=history[:-1]).call()
[65](vscode-notebook-cell:?execution_count=27&line=65) print("After Tools Response:", response.content)
File ~/Documents/catalyst-ai/.venv/lib/python3.10/site-packages/pydantic/main.py:176, in BaseModel.__init__(self, **data)
[174](https://file+.vscode-resource.vscode-cdn.net/Users/prithivi/Documents/catalyst-ai/src/v2/~/Documents/catalyst-ai/.venv/lib/python3.10/site-packages/pydantic/main.py:174) # `__tracebackhide__` tells pytest and some other tools to omit this function from tracebacks
[175](https://file+.vscode-resource.vscode-cdn.net/Users/prithivi/Documents/catalyst-ai/src/v2/~/Documents/catalyst-ai/.venv/lib/python3.10/site-packages/pydantic/main.py:175) __tracebackhide__ = True
--> [176](https://file+.vscode-resource.vscode-cdn.net/Users/prithivi/Documents/catalyst-ai/src/v2/~/Documents/catalyst-ai/.venv/lib/python3.10/site-packages/pydantic/main.py:176) self.__pydantic_validator__.validate_python(data, self_instance=self)
ValidationError: 11 validation errors for Forecast
history.1.typed-dict.content
Input should be a valid string [type=string_type, input_value=None, input_type=NoneType]
For further information visit https://errors.pydantic.dev/2.7/v/string_type
history.1.typed-dict.role
Input should be 'system' [type=literal_error, input_value='assistant', input_type=str]
For further information visit https://errors.pydantic.dev/2.7/v/literal_error
history.1.typed-dict.content.str
Input should be a valid string [type=string_type, input_value=None, input_type=NoneType]
For further information visit https://errors.pydantic.dev/2.7/v/string_type
history.1.typed-dict.content.generator[union[typed-dict,typed-dict]]
Input should be iterable [type=iterable_type, input_value=None, input_type=NoneType]
For further information visit https://errors.pydantic.dev/2.7/v/iterable_type
history.1.typed-dict.role
Input should be 'user' [type=literal_error, input_value='assistant', input_type=str]
For further information visit https://errors.pydantic.dev/2.7/v/literal_error
history.1.typed-dict.function_call
Input should be a valid dictionary [type=dict_type, input_value=None, input_type=NoneType]
For further information visit https://errors.pydantic.dev/2.7/v/dict_type
history.1.typed-dict.content
Input should be a valid string [type=string_type, input_value=None, input_type=NoneType]
For further information visit https://errors.pydantic.dev/2.7/v/string_type
history.1.typed-dict.role
Input should be 'tool' [type=literal_error, input_value='assistant', input_type=str]
For further information visit https://errors.pydantic.dev/2.7/v/literal_error
history.1.typed-dict.tool_call_id
Field required [type=missing, input_value={'content': None, 'role':...}, 'type': 'function'}]}, input_type=dict]
For further information visit https://errors.pydantic.dev/2.7/v/missing
history.1.typed-dict.name
Field required [type=missing, input_value={'content': None, 'role':...}, 'type': 'function'}]}, input_type=dict]
For further information visit https://errors.pydantic.dev/2.7/v/missing
history.1.typed-dict.role
Input should be 'function' [type=literal_error, input_value='assistant', input_type=str]
For further information visit https://errors.pydantic.dev/2.7/v/literal_error
The code above is just a slight modification of code in this file https://github.com/Mirascope/mirascope/blob/b6439511288425340726165175165c05589c5436/docs/concepts/tools_(function_calling).md?plain=1#L110-L174
The change I did was about creating a new instance of Forecast
instead of reusing the old one. The reason I created a new instance is because I like to not mutate the state of an object after it's created.
Here's the file diff: https://diffcheck.io/y-m0-U_Zn_mDOt7MjX
Python, Mirascope & OS Versions, related packages (not required)
mirascope==0.13.4
pydantic==2.7.1
pydantic-core==2.18.2