Process a /v1/chat/completions request asynchronously for Gemini models. Returns either dict (non-streaming) or StreamingResponse (streaming).
(self, request_data: dict)
| 101 | logger.info("GeminiAPIHandler registered models: %s", [m["name"] for m in self.models]) |
| 102 | |
| 103 | async def handle_request(self, request_data: dict): |
| 104 | """ |
| 105 | Process a /v1/chat/completions request asynchronously for Gemini models. |
| 106 | Returns either dict (non-streaming) or StreamingResponse (streaming). |
| 107 | """ |
| 108 | if not self.api_key: |
| 109 | raise ConfigError("GEMINI_API_KEY is not configured on the server.") |
| 110 | |
| 111 | # --- Request Data Validation and Processing --- |
| 112 | model_name = request_data.get("model") |
| 113 | if not model_name: |
| 114 | raise ValueError("Request data must include a 'model' field.") |
| 115 | |
| 116 | # Map display name to actual Gemini model ID |
| 117 | if model_name in self.model_map: |
| 118 | target_model = self.model_map[model_name]["model_id"] |
| 119 | else: |
| 120 | target_model = model_name # Fallback to direct name |
| 121 | |
| 122 | messages = request_data.get("messages", []) |
| 123 | if not messages: |
| 124 | raise ValueError("Request body must contain a 'messages' array") |
| 125 | |
| 126 | # Check for streaming |
| 127 | if request_data.get("stream", False): |
| 128 | return StreamingResponse( |
| 129 | self._stream_gemini_response(request_data, target_model), |
| 130 | media_type="text/event-stream" |
| 131 | ) |
| 132 | |
| 133 | # --- Convert OpenAI format messages to Gemini format --- |
| 134 | system_instruction, contents = self._convert_messages_to_gemini_format(messages) |
| 135 | |
| 136 | if not contents: |
| 137 | raise ValueError("No valid content found to send to Gemini.") |
| 138 | |
| 139 | # --- Prepare Gemini API Call --- |
| 140 | gemini_url = f"https://generativelanguage.googleapis.com/v1beta/models/{target_model}:generateContent" |
| 141 | |
| 142 | # Build payload with converted contents |
| 143 | payload = {"contents": contents} |
| 144 | |
| 145 | # Add system_instruction if present |
| 146 | if system_instruction: |
| 147 | payload["system_instruction"] = system_instruction |
| 148 | |
| 149 | # Add generationConfig if needed from request_data (temperature, max_tokens etc.) |
| 150 | generation_config = {} |
| 151 | if "temperature" in request_data: generation_config["temperature"] = request_data["temperature"] |
| 152 | if "max_tokens" in request_data: generation_config["maxOutputTokens"] = request_data["max_tokens"] |
| 153 | if generation_config: payload["generationConfig"] = generation_config |
| 154 | |
| 155 | headers = { |
| 156 | "Content-Type": "application/json", |
| 157 | "x-goog-api-key": self.api_key, |
| 158 | "User-Agent": "ObserverAI-FastAPI-Client/1.0" |
| 159 | } |
| 160 |
nothing calls this directly
no test coverage detected