Hi, welcome to the community!
Your understanding is correct. Conversations hold the history, and the Responses API generates responses within those conversations. You’re seeing the lock message because each conversation object can only handle one active request at a time.
A good workaround is to retrieve your existing conversation, create a copy (a new conversation object) with the same items, and then run your two API calls simultaneously—each using its own separate conversation.
I’ve put together a small Python script as an example how this can be done.
Hope this helps!
Sample Scipt to branch a conversation
import os
from getpass import getpass
from openai import OpenAI
def get_client() -> OpenAI:
api_key = os.getenv("OPENAI_API_KEY")
if not api_key:
api_key = getpass("OPENAI_API_KEY: ").strip()
return OpenAI(api_key=api_key)
def get_all_conversation_items(client: OpenAI, conversation_id: str):
all_items = []
after = None
while True:
page = client.conversations.items.list(
conversation_id,
limit=100,
order="asc",
after=after,
)
all_items.extend(page.data)
if not page.has_more:
break
after = page.last_id
return all_items
def fork_conversation(client: OpenAI, conversation_id: str, metadata=None):
items = get_all_conversation_items(client, conversation_id)
cloned_items = [
{
"type": "message",
"role": item.role,
"content": item.content,
}
for item in items
if item.type == "message"
]
return client.conversations.create(
metadata=metadata or {},
items=cloned_items,
)
def ask_followup(client: OpenAI, conv_id: str, user_message: str, model: str) -> str:
response = client.responses.create(
model=model,
conversation=conv_id,
input=[{"role": "user", "content": user_message}],
max_output_tokens=256,
)
return response.output_text
def main():
MODEL = "gpt-4.1-mini"
client = get_client()
# Base conversation
base_conv = client.conversations.create(
metadata={"topic": "branching-demo"},
items=[
{
"type": "message",
"role": "user",
"content": [
{
"type": "input_text",
"text": (
"We are planning a weekend trip to Paris. "
"We like museums, good food, and walking around the city."
),
}
],
}
],
)
print(f"Base conversation id: {base_conv.id}")
first_resp = client.responses.create(
model=MODEL,
conversation=base_conv.id,
input=[
{
"role": "user",
"content": "Give me a quick 3-bullet itinerary.",
}
],
max_output_tokens=256,
)
print("\nFirst answer:")
print(first_resp.output_text)
# Fork into two branches
conv_a = fork_conversation(
client,
base_conv.id,
metadata={"branch": "A", "source_conversation": base_conv.id},
)
conv_b = fork_conversation(
client,
base_conv.id,
metadata={"branch": "B", "source_conversation": base_conv.id},
)
# Two different follow-ups
answer_a = ask_followup(
client,
conv_a.id,
"Continue this conversation, but respond like a very formal travel agent.",
MODEL,
)
answer_b = ask_followup(
client,
conv_b.id,
"Continue this conversation, but respond like a funny travel buddy.",
MODEL,
)
print("\nBranch A (formal agent):")
print(answer_a)
print("\nBranch B (funny buddy):")
print(answer_b)
if __name__ == "__main__":
main()