How to Convert OpenAI Message Payloads to Gemini Contents

Converting from OpenAI to Gemini requires three structural changes: moving the system prompt, renaming a role, and wrapping content in a parts array. The conversation content itself transfers unchanged — only the container format changes.

Side-by-side format comparison

The same two-turn conversation in both formats:

// OpenAI format
{
  "model": "gpt-4o",
  "messages": [
    {"role": "system", "content": "You summarise text concisely."},
    {"role": "user", "content": "Summarise: The quick brown fox..."},
    {"role": "assistant", "content": "A fox jumps over a lazy dog."},
    {"role": "user", "content": "Make it shorter."}
  ]
}

// Gemini format
{
  "system_instruction": {
    "parts": [{"text": "You summarise text concisely."}]
  },
  "contents": [
    {"role": "user", "parts": [{"text": "Summarise: The quick brown fox..."}]},
    {"role": "model", "parts": [{"text": "A fox jumps over a lazy dog."}]},
    {"role": "user", "parts": [{"text": "Make it shorter."}]}
  ]
}

The three changes

1. System prompt moves out of messages. Any message with "role": "system" must be removed from the messages array and its content placed in system_instruction.parts[0].text. Gemini ignores system messages left in the contents array.

2. "assistant" becomes "model". Every message with "role": "assistant" becomes "role": "model". User messages stay as "role": "user".

3. Content becomes parts array. Every "content": "text" becomes "parts": [{"text": "text"}]. This wrapping is required even for simple text messages.

The messages array key becomes contents.

Step-by-step in code

# Python — convert OpenAI payload to Gemini
def openai_to_gemini(payload):
    system_texts = []
    contents = []

    for msg in payload.get("messages", []):
        role = msg["role"]
        content = msg["content"]

        if role == "system":
            system_texts.append(content)
        else:
            gemini_role = "model" if role == "assistant" else "user"
            contents.append({
                "role": gemini_role,
                "parts": [{"text": content}]
            })

    result = {"contents": contents}
    if system_texts:
        result["system_instruction"] = {
            "parts": [{"text": " ".join(system_texts)}]
        }
    return result

Multi-modal and multi-part content

For text-only messages, each part is a single text object. For messages with images, each part is either a text object or an inline data object. The parts array structure is Gemini's way of supporting mixed content in a single message — text plus image, for example. For text-only prompts, the parts array always has exactly one text element.

Model name: Gemini model names follow a different convention from OpenAI. Use gemini-1.5-pro, gemini-1.5-flash, or the current recommended model name from Google's documentation. OpenAI model names like gpt-4o are not valid Gemini model identifiers.