(self, payload: dict)
| 120 | raise HandlerError(f"Unexpected error processing Gemini request: {exc}") from exc |
| 121 | |
| 122 | async def _stream_response(self, payload: dict): |
| 123 | try: |
| 124 | client = get_http_client() |
| 125 | async with client.stream("POST", GEMINI_OPENAI_COMPAT_URL, headers=self._headers(), json=payload) as response: |
| 126 | if response.status_code >= 400: |
| 127 | # Read error body while still inside the context manager (stream open). |
| 128 | try: |
| 129 | body = await response.aread() |
| 130 | detail = body.decode("utf-8", "replace") |
| 131 | except Exception: |
| 132 | detail = "<no body>" |
| 133 | logger.error("Gemini streaming API error %s: %s", response.status_code, detail[:1000]) |
| 134 | yield f"data: {json.dumps({'error': f'API error ({response.status_code}): {detail[:500]}'})}\n\n" |
| 135 | return |
| 136 | async for line in response.aiter_lines(): |
| 137 | if not line: |
| 138 | continue |
| 139 | if line.startswith("data: ") and line != "data: [DONE]": |
| 140 | try: |
| 141 | chunk = json.loads(line[6:]) |
| 142 | for choice in chunk.get("choices", []): |
| 143 | delta = choice.get("delta", {}) |
| 144 | # compat endpoint signals thought chunks via extra_content.google.thought |
| 145 | is_thought = delta.get("extra_content", {}).get("google", {}).get("thought", False) |
| 146 | if is_thought and "content" in delta: |
| 147 | text = delta.pop("content").strip("<thought>").strip("</thought>") |
| 148 | delta["reasoning"] = text |
| 149 | delta.pop("extra_content", None) |
| 150 | # also handle reasoning_content field (future-proofing) |
| 151 | elif "reasoning_content" in delta: |
| 152 | delta["reasoning"] = delta.pop("reasoning_content") |
| 153 | yield f"data: {json.dumps(chunk)}\n\n" |
| 154 | continue |
| 155 | except (json.JSONDecodeError, KeyError): |
| 156 | pass |
| 157 | yield line + "\n\n" |
| 158 | except httpx.RequestError as exc: |
| 159 | logger.error("Gemini streaming request failed: %s", exc) |
| 160 | yield f"data: {json.dumps({'error': f'Connection error: {exc}'})}\n\n" |
| 161 | except Exception as exc: |
| 162 | logger.exception("Unexpected error in Gemini streaming") |
| 163 | yield f"data: {json.dumps({'error': f'Unexpected error: {exc}'})}\n\n" |
no test coverage detected