Spaces:
Sleeping
Sleeping
| """ | |
| 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 |