Describe the bug
prepare_request() methods in Cohere, Mistral, and xAI providers mutate the caller's messages list on the first call (no retry needed).
To reproduce
import instructor
from openai import OpenAI
client = instructor.from_openai(OpenAI())
messages = [{"role": "user", "content": "hello"}]
try:
client.chat.completions.create(
model="gpt-4o",
messages=messages,
response_model=str,
)
except Exception:
pass
print(len(messages)) # Expected: 1, Actual: 2+ (system message prepended)
Expected behavior
messages should remain unchanged after prepare_request() runs.
Root cause
prepare_request() uses kwargs.copy() (shallow copy) or _convert_messages(kwargs) which returns a dict with a shared messages list. Then .insert(), .append(), or direct dict key assignment mutates the caller's data.
Affected providers:
- Cohere:
_convert_messages_to_cohere_v2 (shallow copy)
- Mistral: tools, json_schema, json_instructions modes (shallow copy +
.insert())
- xAI: tools, parallel_tools, json_schema, json_instructions modes (shallow copy +
.insert())
Fix
new_kwargs = {**kwargs, 'messages': [dict(m) for m in kwargs.get('messages', [])]}
Describe the bug
prepare_request()methods in Cohere, Mistral, and xAI providers mutate the caller'smessageslist on the first call (no retry needed).To reproduce
Expected behavior
messagesshould remain unchanged afterprepare_request()runs.Root cause
prepare_request()useskwargs.copy()(shallow copy) or_convert_messages(kwargs)which returns a dict with a shared messages list. Then.insert(),.append(), or direct dict key assignment mutates the caller's data.Affected providers:
_convert_messages_to_cohere_v2(shallow copy).insert()).insert())Fix