gcli2api / src /format_detector.py
lightspeed's picture
Upload 22 files
5868187 verified
"""
Format detection utilities for supporting both OpenAI and Gemini request formats
"""
from typing import Dict, Any
from log import log
def detect_request_format(data: Dict[str, Any]) -> str:
"""
Detect whether the request is in OpenAI or Gemini format.
Returns:
"openai" or "gemini"
"""
# OpenAI format indicators:
# - Has "messages" field with array of {role, content} objects
# - Role values are "system", "user", "assistant"
if "messages" in data and isinstance(data["messages"], list):
if data["messages"] and isinstance(data["messages"][0], dict):
# Check for OpenAI role values
first_role = data["messages"][0].get("role", "")
if first_role in ["system", "user", "assistant"]:
return "openai"
# Gemini format indicators:
# - Has "contents" field with array of {role, parts} objects
# - Role values are "user", "model"
# - May have "systemInstruction" field
if "contents" in data and isinstance(data["contents"], list):
if data["contents"] and isinstance(data["contents"][0], dict):
# Check for Gemini structure
if "parts" in data["contents"][0]:
return "gemini"
# Additional Gemini indicators
if "systemInstruction" in data or "generationConfig" in data:
return "gemini"
# Default to OpenAI if unclear (for backwards compatibility)
log.debug(f"Unable to definitively detect format, defaulting to OpenAI. Keys present: {list(data.keys())}")
return "openai"
def gemini_request_to_openai(gemini_request: Dict[str, Any]) -> Dict[str, Any]:
"""
Convert a Gemini format request to OpenAI format.
Args:
gemini_request: Request in Gemini API format
Returns:
Dictionary in OpenAI API format
"""
openai_request = {
"model": gemini_request.get("model", "gemini-2.5-pro"),
"messages": []
}
# Convert system instruction if present
if "systemInstruction" in gemini_request:
system_content = ""
if isinstance(gemini_request["systemInstruction"], dict):
parts = gemini_request["systemInstruction"].get("parts", [])
for part in parts:
if "text" in part:
system_content += part["text"]
elif isinstance(gemini_request["systemInstruction"], str):
system_content = gemini_request["systemInstruction"]
if system_content:
openai_request["messages"].append({
"role": "system",
"content": system_content
})
# Convert contents to messages
contents = gemini_request.get("contents", [])
for content in contents:
role = content.get("role", "user")
# Map Gemini roles to OpenAI roles
if role == "model":
role = "assistant"
# Convert parts to content
parts = content.get("parts", [])
if len(parts) == 1 and "text" in parts[0]:
# Simple text message
openai_request["messages"].append({
"role": role,
"content": parts[0]["text"]
})
elif len(parts) > 0:
# Multi-part message (could include images)
content_parts = []
for part in parts:
if "text" in part:
content_parts.append({
"type": "text",
"text": part["text"]
})
elif "inlineData" in part:
# Convert Gemini inline data to OpenAI image format
inline_data = part["inlineData"]
mime_type = inline_data.get("mimeType", "image/jpeg")
data = inline_data.get("data", "")
content_parts.append({
"type": "image_url",
"image_url": {
"url": f"data:{mime_type};base64,{data}"
}
})
if content_parts:
# If only one text part, use simple string format
if len(content_parts) == 1 and content_parts[0]["type"] == "text":
openai_request["messages"].append({
"role": role,
"content": content_parts[0]["text"]
})
else:
openai_request["messages"].append({
"role": role,
"content": content_parts
})
# Convert generation config if present
if "generationConfig" in gemini_request:
config = gemini_request["generationConfig"]
if "temperature" in config:
openai_request["temperature"] = config["temperature"]
if "topP" in config:
openai_request["top_p"] = config["topP"]
if "topK" in config:
openai_request["top_k"] = config["topK"]
if "maxOutputTokens" in config:
openai_request["max_tokens"] = config["maxOutputTokens"]
if "stopSequences" in config:
openai_request["stop"] = config["stopSequences"]
if "frequencyPenalty" in config:
openai_request["frequency_penalty"] = config["frequencyPenalty"]
if "presencePenalty" in config:
openai_request["presence_penalty"] = config["presencePenalty"]
if "candidateCount" in config:
openai_request["n"] = config["candidateCount"]
if "seed" in config:
openai_request["seed"] = config["seed"]
# Preserve stream setting if present
if "stream" in gemini_request:
openai_request["stream"] = gemini_request["stream"]
return openai_request
def validate_and_normalize_request(data: Dict[str, Any]) -> Dict[str, Any]:
"""
Validate and normalize the request to OpenAI format.
Automatically detects format and converts if necessary.
Args:
data: Raw request data
Returns:
Normalized request in OpenAI format
"""
format_type = detect_request_format(data)
log.info(f"Detected request format: {format_type}")
if format_type == "gemini":
# Convert Gemini format to OpenAI format
return gemini_request_to_openai(data)
else:
# Already in OpenAI format
return data